mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-16 22:36:48 -07:00
feat(ui): automatically dismiss ephemeral /btw display on input typing
By intercepting text input when the `/btw` query results are visible (but not actively streaming), we can dismiss the ephemeral BtwDisplay before the new text wraps to the next line. This fixes the UI jumpiness and "ghost space" scrolling that occurs when the terminal recalculates the tall rendered height of the previous query dynamically. Also, includes test updates to mock the spinner to eliminate `act(...)` asynchronous test warnings.
This commit is contained in:
committed by
Mahima Shanware
parent
ee1bc7e209
commit
55a7a22471
@@ -10,6 +10,12 @@ import { BtwDisplay } from './BtwDisplay.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import type { UIState } from '../contexts/UIStateContext.js';
|
||||
|
||||
import { Text } from 'ink';
|
||||
|
||||
vi.mock('./GeminiRespondingSpinner.js', () => ({
|
||||
GeminiRespondingSpinner: () => <Text>⠋</Text>,
|
||||
}));
|
||||
|
||||
describe('BtwDisplay', () => {
|
||||
const defaultMockUiState = {
|
||||
renderMarkdown: true,
|
||||
|
||||
@@ -5182,6 +5182,64 @@ describe('InputPrompt', () => {
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('dismisses Btw and accepts input on typing when not streaming', async () => {
|
||||
const dismissBtw = vi.fn();
|
||||
const { stdin, stdout, unmount } = await renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
{
|
||||
uiState: {
|
||||
btwState: {
|
||||
isActive: true,
|
||||
query: '',
|
||||
response: '',
|
||||
isStreaming: false,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
uiActions: { dismissBtw },
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write('a');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(dismissBtw).toHaveBeenCalled();
|
||||
expect(clean(stdout.lastFrameRaw())).toContain('a');
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('blocks typing when Btw is streaming', async () => {
|
||||
const dismissBtw = vi.fn();
|
||||
const { stdin, stdout, unmount } = await renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
{
|
||||
uiState: {
|
||||
btwState: {
|
||||
isActive: true,
|
||||
query: '',
|
||||
response: '',
|
||||
isStreaming: true,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
uiActions: { dismissBtw },
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write('Z');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(dismissBtw).not.toHaveBeenCalled();
|
||||
expect(clean(stdout.lastFrameRaw())).not.toContain('Z');
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -694,6 +694,27 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
dismissBtw();
|
||||
return true;
|
||||
}
|
||||
|
||||
const isPrintable =
|
||||
key.sequence &&
|
||||
!key.ctrl &&
|
||||
!key.cmd &&
|
||||
!key.alt &&
|
||||
key.sequence.length === 1;
|
||||
|
||||
const isEditingKey =
|
||||
isPrintable ||
|
||||
key.name === 'backspace' ||
|
||||
key.name === 'delete' ||
|
||||
key.name === 'paste';
|
||||
|
||||
if (isEditingKey) {
|
||||
if (btwState.isStreaming) {
|
||||
return true;
|
||||
} else {
|
||||
dismissBtw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset completion suppression if the user performs any action other than
|
||||
@@ -1386,6 +1407,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
isHelpDismissKey,
|
||||
settings,
|
||||
btwState.isActive,
|
||||
btwState.isStreaming,
|
||||
dismissBtw,
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user