Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
88da17d
Add shortcuts panel toggle and ? key behavior
LyalinDotCom Jan 31, 2026
614cc15
Refine shortcuts UI header and highlighting
LyalinDotCom Jan 31, 2026
9409a80
Polish shortcuts layout and header
LyalinDotCom Jan 31, 2026
b92d9e2
Refine shortcuts header and status alignment
LyalinDotCom Jan 31, 2026
419299b
Remove shortcut hint separator
LyalinDotCom Jan 31, 2026
136efd6
Update shortcuts panel with paste images
LyalinDotCom Jan 31, 2026
8c4a091
Relax indicator tests for wrapped output
LyalinDotCom Feb 1, 2026
014846a
Update shortcuts help behavior and docs
LyalinDotCom Feb 1, 2026
e8a8aab
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 2, 2026
b6b4031
Limit shortcuts panel to empty prompt
LyalinDotCom Feb 2, 2026
d925d08
Wrap shortcuts panel text
LyalinDotCom Feb 2, 2026
18a1ad8
Merge branch 'show_help_questionmark' of https://github.com/LyalinDot…
LyalinDotCom Feb 2, 2026
b856560
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 2, 2026
5c713af
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 3, 2026
937bd3c
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 3, 2026
f65e24e
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 3, 2026
90bc491
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 3, 2026
df462ed
Simplify ShortcutsHint and fix test regex patterns
LyalinDotCom Feb 3, 2026
1d30b17
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 4, 2026
b5f67ce
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 4, 2026
a1c0674
Fix composer indicator assertions
LyalinDotCom Feb 4, 2026
be2d593
Merge branch 'show_help_questionmark' of https://github.com/LyalinDot…
LyalinDotCom Feb 5, 2026
636e600
Move loading indicator inline and hide status
LyalinDotCom Feb 5, 2026
7681b19
Fix failing CI tests for LoadingIndicator and Composer
LyalinDotCom Feb 5, 2026
5aa4620
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 5, 2026
78b2e50
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 5, 2026
c98f815
Fix Escape key closing shortcuts panel and cancelling operation simul…
LyalinDotCom Feb 5, 2026
2049906
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 5, 2026
341502b
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 6, 2026
6b1b8f7
Refine shortcuts panel layout and loading status behavior
LyalinDotCom Feb 6, 2026
b26bb9a
Fix shortcuts panel key behavior documentation
LyalinDotCom Feb 6, 2026
83724b1
Merge remote-tracking branch 'origin/show_help_questionmark' into sho…
LyalinDotCom Feb 6, 2026
9d6fff7
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 6, 2026
a7bc6b1
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 6, 2026
085a68d
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 6, 2026
902f286
Merge branch 'main' into show_help_questionmark
LyalinDotCom Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/cli/src/services/BuiltinCommandLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ vi.mock('../ui/commands/extensionsCommand.js', () => ({
extensionsCommand: () => ({}),
}));
vi.mock('../ui/commands/helpCommand.js', () => ({ helpCommand: {} }));
vi.mock('../ui/commands/shortcutsCommand.js', () => ({
shortcutsCommand: {},
}));
vi.mock('../ui/commands/memoryCommand.js', () => ({ memoryCommand: {} }));
vi.mock('../ui/commands/modelCommand.js', () => ({
modelCommand: { name: 'model' },
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/services/BuiltinCommandLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { directoryCommand } from '../ui/commands/directoryCommand.js';
import { editorCommand } from '../ui/commands/editorCommand.js';
import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
import { helpCommand } from '../ui/commands/helpCommand.js';
import { shortcutsCommand } from '../ui/commands/shortcutsCommand.js';
import { rewindCommand } from '../ui/commands/rewindCommand.js';
import { hooksCommand } from '../ui/commands/hooksCommand.js';
import { ideCommand } from '../ui/commands/ideCommand.js';
Expand Down Expand Up @@ -115,6 +116,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
]
: [extensionsCommand(this.config?.getEnableExtensionReloading())]),
helpCommand,
shortcutsCommand,
...(this.config?.getEnableHooksUI() ? [hooksCommand] : []),
rewindCommand,
await ideCommand(),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/test-utils/mockCommandContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const createMockCommandContext = (
setPendingItem: vi.fn(),
loadHistory: vi.fn(),
toggleCorgiMode: vi.fn(),
toggleShortcutsHelp: vi.fn(),
toggleVimEnabled: vi.fn(),
openAgentConfigDialog: vi.fn(),
closeAgentConfigDialog: vi.fn(),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/test-utils/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const mockUIActions: UIActions = {
handleApiKeySubmit: vi.fn(),
handleApiKeyCancel: vi.fn(),
setBannerVisible: vi.fn(),
setShortcutsHelpVisible: vi.fn(),
setEmbeddedShellFocused: vi.fn(),
dismissBackgroundShell: vi.fn(),
setActiveBackgroundShellPid: vi.fn(),
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
const setIsBackgroundShellListOpenRef = useRef<(open: boolean) => void>(
() => {},
);
const [shortcutsHelpVisible, setShortcutsHelpVisible] = useState(false);

const slashCommandActions = useMemo(
() => ({
Expand Down Expand Up @@ -834,6 +835,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
}
}
},
toggleShortcutsHelp: () => setShortcutsHelpVisible((visible) => !visible),
setText: stableSetText,
}),
[
Expand All @@ -852,6 +854,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
openPermissionsDialog,
addConfirmUpdateExtensionRequest,
toggleDebugProfiler,
setShortcutsHelpVisible,
stableSetText,
],
);
Expand Down Expand Up @@ -1893,6 +1896,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
ctrlCPressedOnce: ctrlCPressCount >= 1,
ctrlDPressedOnce: ctrlDPressCount >= 1,
showEscapePrompt,
shortcutsHelpVisible,
isFocused,
elapsedTime,
currentLoadingPhrase,
Expand Down Expand Up @@ -1998,6 +2002,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
ctrlCPressCount,
ctrlDPressCount,
showEscapePrompt,
shortcutsHelpVisible,
isFocused,
elapsedTime,
currentLoadingPhrase,
Expand Down Expand Up @@ -2097,6 +2102,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleApiKeySubmit,
handleApiKeyCancel,
setBannerVisible,
setShortcutsHelpVisible,
handleWarning,
setEmbeddedShellFocused,
dismissBackgroundShell,
Expand Down Expand Up @@ -2173,6 +2179,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
handleApiKeySubmit,
handleApiKeyCancel,
setBannerVisible,
setShortcutsHelpVisible,
handleWarning,
setEmbeddedShellFocused,
dismissBackgroundShell,
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/ui/commands/helpCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { MessageType, type HistoryItemHelp } from '../types.js';

export const helpCommand: SlashCommand = {
name: 'help',
altNames: ['?'],
kind: CommandKind.BUILT_IN,
description: 'For help on gemini-cli',
autoExecute: true,
Expand Down
19 changes: 19 additions & 0 deletions packages/cli/src/ui/commands/shortcutsCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import type { SlashCommand } from './types.js';
import { CommandKind } from './types.js';

export const shortcutsCommand: SlashCommand = {
name: 'shortcuts',
altNames: ['keys', '?'],
kind: CommandKind.BUILT_IN,
description: 'Toggle the shortcuts panel above the input',
autoExecute: true,
action: (context) => {
context.ui.toggleShortcutsHelp();
},
};
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export interface CommandContext {
addConfirmUpdateExtensionRequest: (value: ConfirmationRequest) => void;
removeComponent: () => void;
toggleBackgroundShell: () => void;
toggleShortcutsHelp: () => void;
};
// Session-specific data
session: {
Expand Down
13 changes: 11 additions & 2 deletions packages/cli/src/ui/components/Composer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ vi.mock('./ShellModeIndicator.js', () => ({
ShellModeIndicator: () => <Text>ShellModeIndicator</Text>,
}));

vi.mock('./ShortcutsHint.js', () => ({
ShortcutsHint: () => <Text>ShortcutsHint</Text>,
}));

vi.mock('./ShortcutsHelp.js', () => ({
ShortcutsHelp: () => <Text>ShortcutsHelp</Text>,
}));

vi.mock('./DetailedMessagesDisplay.js', () => ({
DetailedMessagesDisplay: () => <Text>DetailedMessagesDisplay</Text>,
}));
Expand Down Expand Up @@ -116,6 +124,7 @@ const createMockUIState = (overrides: Partial<UIState> = {}): UIState =>
ctrlCPressedOnce: false,
ctrlDPressedOnce: false,
showEscapePrompt: false,
shortcutsHelpVisible: false,
ideContextState: null,
geminiMdFileCount: 0,
renderMarkdown: true,
Expand Down Expand Up @@ -457,7 +466,7 @@ describe('Composer', () => {

const { lastFrame } = renderComposer(uiState);

expect(lastFrame()).toContain('ApprovalModeIndicator');
expect(lastFrame()).toMatch(/ApprovalModeIndica\s*tor/);
});

it('shows ShellModeIndicator when shell mode is active', () => {
Expand All @@ -467,7 +476,7 @@ describe('Composer', () => {

const { lastFrame } = renderComposer(uiState);

expect(lastFrame()).toContain('ShellModeIndicator');
expect(lastFrame()).toMatch(/ShellModeIndica\s*tor/);
});

it('shows RawMarkdownIndicator when renderMarkdown is false', () => {
Expand Down
22 changes: 20 additions & 2 deletions packages/cli/src/ui/components/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { ApprovalModeIndicator } from './ApprovalModeIndicator.js';
import { ShellModeIndicator } from './ShellModeIndicator.js';
import { DetailedMessagesDisplay } from './DetailedMessagesDisplay.js';
import { RawMarkdownIndicator } from './RawMarkdownIndicator.js';
import { ShortcutsHint } from './ShortcutsHint.js';
import { ShortcutsHelp } from './ShortcutsHelp.js';
import { InputPrompt } from './InputPrompt.js';
import { Footer } from './Footer.js';
import { ShowMoreLines } from './ShowMoreLines.js';
Expand Down Expand Up @@ -92,8 +94,18 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
flexDirection={isNarrow ? 'column' : 'row'}
alignItems={isNarrow ? 'flex-start' : 'center'}
>
<Box marginRight={1}>
<StatusDisplay hideContextSummary={hideContextSummary} />
<Box
marginRight={1}
flexDirection="row"
alignItems="center"
flexGrow={1}
justifyContent="space-between"
width="100%"
>
<ShortcutsHint />
<Box flexGrow={1} justifyContent="flex-end" flexDirection="row">
<StatusDisplay hideContextSummary={hideContextSummary} />
</Box>
</Box>
<Box paddingTop={isNarrow ? 1 : 0}>
{showApprovalModeIndicator !== ApprovalMode.DEFAULT &&
Expand Down Expand Up @@ -121,6 +133,12 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
</OverflowProvider>
)}

{uiState.shortcutsHelpVisible && (
<Box marginTop={1}>
<ShortcutsHelp />
</Box>
)}

{uiState.isInputActive && (
<InputPrompt
buffer={uiState.buffer}
Expand Down
29 changes: 28 additions & 1 deletion packages/cli/src/ui/components/InputPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,15 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const { merged: settings } = useSettings();
const kittyProtocol = useKittyKeyboardProtocol();
const isShellFocused = useShellFocusState();
const { setEmbeddedShellFocused } = useUIActions();
const { setEmbeddedShellFocused, setShortcutsHelpVisible } = useUIActions();
const {
terminalWidth,
activePtyId,
history,
terminalBackgroundColor,
backgroundShells,
backgroundShellHeight,
shortcutsHelpVisible,
} = useUIState();
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
const escPressCount = useRef(0);
Expand Down Expand Up @@ -511,6 +512,30 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
return true;
}

if (shortcutsHelpVisible) {
if (key.sequence === '?' && key.insertable) {
setShortcutsHelpVisible(false);
buffer.handleInput(key);
return true;
}
if (
keyMatchers[Command.ESCAPE](key) ||
key.name === 'backspace' ||
key.sequence === '\b'
) {
setShortcutsHelpVisible(false);
return true;
}
if (key.insertable) {
setShortcutsHelpVisible(false);
}
}

if (key.sequence === '?' && key.insertable && !shortcutsHelpVisible) {
setShortcutsHelpVisible(true);
return true;
}

if (vimHandleInput && vimHandleInput(key)) {
return true;
}
Expand Down Expand Up @@ -969,6 +994,8 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
commandSearchActive,
commandSearchCompletion,
kittyProtocol.enabled,
shortcutsHelpVisible,
setShortcutsHelpVisible,
tryLoadQueuedMessages,
setBannerVisible,
onSubmit,
Expand Down
Loading
Loading