Compare commits

..

2 Commits

Author SHA1 Message Date
Sehoon Shon f669a7adc1 Merge branch 'main' into abhi/use-flash-e2e 2026-03-30 15:48:28 -04:00
Abhi defcd5a754 test-utils: update default integration test model to PREVIEW_GEMINI_FLASH_MODEL 2026-03-23 13:22:24 -04:00
11 changed files with 33 additions and 133 deletions
-1
View File
@@ -534,7 +534,6 @@ export const mockAppState: AppState = {
};
const mockUIActions: UIActions = {
toggleAlternateBuffer: vi.fn(),
handleThemeSelect: vi.fn(),
closeThemeDialog: vi.fn(),
handleThemeHighlight: vi.fn(),
+1 -58
View File
@@ -68,10 +68,8 @@ import {
writeToStdout,
disableMouseEvents,
enterAlternateScreen,
exitAlternateScreen,
enableMouseEvents,
disableLineWrapping,
enableLineWrapping,
shouldEnterAlternateScreen,
startupProfiler,
SessionStartSource,
@@ -215,7 +213,7 @@ export const AppContainer = (props: AppContainerProps) => {
});
useMemoryMonitor(historyManager);
const [isAlternateBuffer, setIsAlternateBuffer] = useState(config.getUseAlternateBuffer());
const isAlternateBuffer = config.getUseAlternateBuffer();
const [corgiMode, setCorgiMode] = useState(false);
const [forceRerenderKey, setForceRerenderKey] = useState(0);
const [debugMessage, setDebugMessage] = useState<string>('');
@@ -1552,23 +1550,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
type: TransientMessageType;
}>(WARNING_PROMPT_DURATION_MS);
const [shownBufferToggleHint, setShownBufferToggleHint] = useState(false);
useEffect(() => {
if (isAlternateBuffer) return;
const isLongHistory = historyManager.history.length > 15;
const isComplexPrompt = buffer.text.length > 200 || buffer.text.includes('\n');
if ((isLongHistory || isComplexPrompt) && !shownBufferToggleHint) {
showTransientMessage({
text: 'Tip: Press Alt+T to toggle full-screen mode for better scrolling/editing',
type: TransientMessageType.Hint
});
setShownBufferToggleHint(true);
}
}, [historyManager.history.length, buffer.text, isAlternateBuffer, shownBufferToggleHint, showTransientMessage]);
const {
isFolderTrustDialogOpen,
discoveryResults: folderDiscoveryResults,
@@ -1719,11 +1700,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
return true;
}
if (keyMatchers[Command.TOGGLE_BUFFER_MODE](key)) {
toggleAlternateBuffer();
return true;
}
if (keyMatchers[Command.QUIT](key)) {
// If the user presses Ctrl+C, we want to cancel any ongoing requests.
// This should happen regardless of the count.
@@ -2228,11 +2204,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
};
}, [config, refreshStatic]);
const showIsAlternateBufferHint = (historyManager.history.length > 15 || buffer.text.length > 200 || buffer.text.includes('\n')) && !isAlternateBuffer;
const uiState: UIState = useMemo(
() => ({
isAlternateBuffer,
history: historyManager.history,
historyManager,
isThemeDialogOpen,
@@ -2358,7 +2331,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
adminSettingsChanged,
newAgents,
showIsExpandableHint,
showIsAlternateBufferHint,
hintMode:
config.isModelSteeringEnabled() && isToolExecuting(pendingHistoryItems),
hintBuffer: '',
@@ -2485,8 +2457,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
adminSettingsChanged,
newAgents,
showIsExpandableHint,
showIsAlternateBufferHint,
isAlternateBuffer,
],
);
@@ -2495,31 +2465,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
[setShowPrivacyNotice],
);
const toggleAlternateBuffer = useCallback(() => {
setIsAlternateBuffer(prev => {
const next = !prev;
if (next) {
enterAlternateScreen();
enableMouseEvents();
disableLineWrapping();
} else {
exitAlternateScreen();
disableMouseEvents();
enableLineWrapping();
writeToStdout('\x1b[2J\x1b[H');
}
process.stdout.emit('resize');
// Give a tick for resize to process, then trigger remount to force full redraw
setImmediate(() => {
refreshStatic();
setForceRerenderKey((prev) => prev + 1);
});
return next;
});
}, [setIsAlternateBuffer, refreshStatic, setForceRerenderKey]);
const uiActions: UIActions = useMemo(
() => ({
handleThemeSelect,
@@ -2531,7 +2476,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleEditorSelect,
exitEditorDialog,
exitPrivacyNotice,
toggleAlternateBuffer,
closeSettingsDialog,
closeModelDialog,
openAgentConfigDialog,
@@ -2670,7 +2614,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
config,
historyManager,
getPreferredEditor,
toggleAlternateBuffer,
],
);
@@ -142,38 +142,4 @@ describe('<StatusRow />', () => {
await waitUntilReady();
expect(lastFrame()).toContain('Tip: Test Tip');
});
it('renders buffer toggle hint when showIsAlternateBufferHint is true', async () => {
(useComposerStatus as Mock).mockReturnValue({
isInteractiveShellWaiting: false,
showLoadingIndicator: false,
showTips: false,
showWit: false,
modeContentObj: null,
showMinimalContext: false,
});
const uiState: Partial<UIState> = {
...defaultUiState,
showIsAlternateBufferHint: true,
};
const { lastFrame, waitUntilReady } = await renderWithProviders(
<StatusRow
showUiDetails={false}
isNarrow={false}
terminalWidth={100}
hideContextSummary={false}
hideUiDetailsForSuggestions={false}
hasPendingActionRequired={false}
/>,
{
width: 100,
uiState,
},
);
await waitUntilReady();
expect(lastFrame()).toContain('[Alt+T] Switch to Full Screen');
});
});
+1 -6
View File
@@ -206,12 +206,7 @@ export const StatusRow: React.FC<StatusRowProps> = ({
return uiState.currentTip;
}
// 2. Buffer Toggle Hint
if (uiState.showIsAlternateBufferHint) {
return '[Alt+T] Switch to Full Screen';
}
// 3. Shortcut Hint (Fallback)
// 2. Shortcut Hint (Fallback)
if (
settings.merged.ui.showShortcutsHint &&
!hideUiDetailsForSuggestions &&
@@ -205,8 +205,6 @@ const MAC_ALT_KEY_CHARACTER_MAP: Record<string, string> = {
'\u03A9': 'z', // "Ω" Option+z
'\u00B8': 'Z', // "¸" Option+Shift+z
'\u2202': 'd', // "∂" delete word forward
'\u2020': 't', // "†" toggle full screen buffer
'\u00E5': 'a', // "å" Option+a for alternate buffer
};
function nonKeyboardEventFilter(
@@ -78,7 +78,6 @@ export interface UIActions {
setShortcutsHelpVisible: (visible: boolean) => void;
setCleanUiDetailsVisible: (visible: boolean) => void;
toggleCleanUiDetailsVisible: () => void;
toggleAlternateBuffer: () => void;
revealCleanUiDetailsTemporarily: (durationMs?: number) => void;
handleWarning: (message: string) => void;
setEmbeddedShellFocused: (value: boolean) => void;
@@ -118,8 +118,6 @@ export interface UIState {
isEditorDialogOpen: boolean;
showPrivacyNotice: boolean;
corgiMode: boolean;
isAlternateBuffer: boolean;
showIsAlternateBufferHint: boolean;
debugMessage: string;
quittingMessages: HistoryItem[] | null;
isSettingsDialogOpen: boolean;
@@ -11,47 +11,49 @@ import {
isAlternateBufferEnabled,
} from './useAlternateBuffer.js';
import type { Config } from '@google/gemini-cli-core';
import { useUIState } from '../contexts/UIStateContext.js';
vi.mock('../contexts/UIStateContext.js');
vi.mock('../contexts/ConfigContext.js', () => ({
useConfig: vi.fn(),
}));
const mockUseUIState = vi.mocked(useUIState);
const mockUseConfig = vi.mocked(
await import('../contexts/ConfigContext.js').then((m) => m.useConfig),
);
describe('useAlternateBuffer', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should return false when uiState.isAlternateBuffer is false', async () => {
mockUseUIState.mockReturnValue({
isAlternateBuffer: false,
} as unknown as ReturnType<typeof mockUseUIState>);
it('should return false when config.getUseAlternateBuffer returns false', async () => {
mockUseConfig.mockReturnValue({
getUseAlternateBuffer: () => false,
} as unknown as ReturnType<typeof mockUseConfig>);
const { result } = await renderHook(() => useAlternateBuffer());
expect(result.current).toBe(false);
});
it('should return true when uiState.isAlternateBuffer is true', async () => {
mockUseUIState.mockReturnValue({
isAlternateBuffer: true,
} as unknown as ReturnType<typeof mockUseUIState>);
it('should return true when config.getUseAlternateBuffer returns true', async () => {
mockUseConfig.mockReturnValue({
getUseAlternateBuffer: () => true,
} as unknown as ReturnType<typeof mockUseConfig>);
const { result } = await renderHook(() => useAlternateBuffer());
expect(result.current).toBe(true);
});
it('should react to state changes', async () => {
mockUseUIState.mockReturnValue({
isAlternateBuffer: false,
} as unknown as ReturnType<typeof mockUseUIState>);
it('should return the immutable config value, not react to settings changes', async () => {
const mockConfig = {
getUseAlternateBuffer: () => true,
} as unknown as ReturnType<typeof mockUseConfig>;
mockUseConfig.mockReturnValue(mockConfig);
const { result, rerender } = await renderHook(() => useAlternateBuffer());
expect(result.current).toBe(false);
mockUseUIState.mockReturnValue({
isAlternateBuffer: true,
} as unknown as ReturnType<typeof mockUseUIState>);
// Value should remain true even after rerender
expect(result.current).toBe(true);
rerender();
@@ -4,14 +4,14 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { useUIState } from '../contexts/UIStateContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import type { Config } from '@google/gemini-cli-core';
export const isAlternateBufferEnabled = (config: Config): boolean =>
config.getUseAlternateBuffer();
// This is read from UIState so that the UI can toggle dynamically
// This is read from Config so that the UI reads the same value per application session
export const useAlternateBuffer = (): boolean => {
const uiState = useUIState();
return uiState.isAlternateBuffer;
const config = useConfig();
return isAlternateBufferEnabled(config);
};
-3
View File
@@ -95,7 +95,6 @@ export enum Command {
RESTART_APP = 'app.restart',
SUSPEND_APP = 'app.suspend',
SHOW_SHELL_INPUT_UNFOCUS_WARNING = 'app.showShellUnfocusWarning',
TOGGLE_BUFFER_MODE = 'app.toggleBufferMode',
// Background Shell Controls
BACKGROUND_SHELL_ESCAPE = 'background.escape',
@@ -393,7 +392,6 @@ export const defaultKeyBindingConfig: KeyBindingConfig = new Map([
[Command.RESTART_APP, [new KeyBinding('r'), new KeyBinding('shift+r')]],
[Command.SUSPEND_APP, [new KeyBinding('ctrl+z')]],
[Command.SHOW_SHELL_INPUT_UNFOCUS_WARNING, [new KeyBinding('tab')]],
[Command.TOGGLE_BUFFER_MODE, [new KeyBinding('alt+a')]],
// Background Shell Controls
[Command.BACKGROUND_SHELL_ESCAPE, [new KeyBinding('escape')]],
@@ -611,7 +609,6 @@ export const commandDescriptions: Readonly<Record<Command, string>> = {
[Command.SHOW_IDE_CONTEXT_DETAIL]: 'Show IDE context details.',
[Command.TOGGLE_MARKDOWN]: 'Toggle Markdown rendering.',
[Command.TOGGLE_COPY_MODE]: 'Toggle copy mode when in alternate buffer mode.',
[Command.TOGGLE_BUFFER_MODE]: 'Toggle between regular and full screen (alternate buffer) mode.',
[Command.TOGGLE_YOLO]: 'Toggle YOLO (auto-approval) mode for tool calls.',
[Command.CYCLE_APPROVAL_MODE]:
'Cycle through approval modes: default (prompt), auto_edit (auto-approve edits), and plan (read-only). Plan mode is skipped when the agent is busy.',
+5 -2
View File
@@ -11,7 +11,10 @@ import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { env } from 'node:process';
import { setTimeout as sleep } from 'node:timers/promises';
import { DEFAULT_GEMINI_MODEL, GEMINI_DIR } from '@google/gemini-cli-core';
import {
GEMINI_DIR,
PREVIEW_GEMINI_FLASH_MODEL,
} from '@google/gemini-cli-core';
export { GEMINI_DIR };
import * as pty from '@lydell/node-pty';
import stripAnsi from 'strip-ansi';
@@ -457,7 +460,7 @@ export class TestRig {
...(env['GEMINI_TEST_TYPE'] === 'integration'
? {
model: {
name: DEFAULT_GEMINI_MODEL,
name: PREVIEW_GEMINI_FLASH_MODEL,
},
}
: {}),