diff --git a/packages/cli/src/ui/components/Composer.test.tsx b/packages/cli/src/ui/components/Composer.test.tsx index af53d39c50..b12f4b62bf 100644 --- a/packages/cli/src/ui/components/Composer.test.tsx +++ b/packages/cli/src/ui/components/Composer.test.tsx @@ -89,10 +89,6 @@ vi.mock('./ShellModeIndicator.js', () => ({ ShellModeIndicator: () => ShellModeIndicator, })); -vi.mock('./ShortcutsHint.js', () => ({ - ShortcutsHint: () => ShortcutsHint, -})); - vi.mock('./ShortcutsHelp.js', () => ({ ShortcutsHelp: () => ShortcutsHelp, })); @@ -847,7 +843,9 @@ describe('Composer', () => { await vi.advanceTimersByTimeAsync(250); }); - expect(lastFrame({ allowEmpty: true })).toContain('ShortcutsHint'); + expect(lastFrame({ allowEmpty: true })).toContain( + 'press tab twice for more', + ); }); it('hides shortcuts hint when text is typed in buffer', async () => { @@ -901,7 +899,7 @@ describe('Composer', () => { await vi.advanceTimersByTimeAsync(250); }); - expect(lastFrame()).toContain('ShortcutsHint'); + expect(lastFrame()).toContain('press tab twice for more'); }); it('shows shortcuts hint when full UI details are visible', async () => { @@ -916,7 +914,7 @@ describe('Composer', () => { }); // In Refreshed UX, shortcuts hint is in the top multipurpose status row - expect(lastFrame()).toContain('ShortcutsHint'); + expect(lastFrame()).toContain('? for shortcuts'); }); it('hides shortcuts hint while loading when full UI details are visible', async () => { @@ -1009,7 +1007,7 @@ describe('Composer', () => { }); // In Refreshed UX, shortcuts hint is in the top status row and doesn't collide with suggestions below - expect(lastFrame()).toContain('ShortcutsHint'); + expect(lastFrame()).toContain('press tab twice for more'); }); }); diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 2e25c94575..ba537ee970 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -31,7 +31,6 @@ 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'; @@ -112,27 +111,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { uiState.streamingState === StreamingState.Idle && !hasPendingActionRequired; - const [showShortcutsHintDebounced, setShowShortcutsHintDebounced] = - useState(false); - const canShowShortcutsHint = - uiState.isInputActive && - uiState.streamingState === StreamingState.Idle && - !hasPendingActionRequired && - uiState.buffer.text.length === 0; - - useEffect(() => { - if (!canShowShortcutsHint) { - setShowShortcutsHintDebounced(false); - return; - } - - const timeout = setTimeout(() => { - setShowShortcutsHintDebounced(true); - }, 200); - - return () => clearTimeout(timeout); - }, [canShowShortcutsHint]); - /** * Use the setting if provided, otherwise default to true for the new UX. * This allows tests to override the collapse behavior. @@ -194,9 +172,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { const shouldReserveSpaceForShortcutsHint = settings.merged.ui.showShortcutsHint && !hideUiDetailsForSuggestions; - const showShortcutsHint = - shouldReserveSpaceForShortcutsHint && showShortcutsHintDebounced; - const showMinimalContextBleedThrough = !settings.merged.ui.footer.hideContextPercentage && isContextUsageHigh( @@ -239,7 +214,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { * Determine the ambient text (tip) to display. */ const ambientContentStr = (() => { - // Only show Tips on the right + // 1. Proactive Tip (Priority) if (showTips && uiState.currentTip) { if ( estimatedStatusLength + uiState.currentTip.length + 10 <= @@ -249,6 +224,15 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { } } + // 2. Shortcut Hint (Fallback) + if ( + settings.merged.ui.showShortcutsHint && + !hideUiDetailsForSuggestions && + !hasPendingActionRequired + ) { + return showUiDetails ? '? for shortcuts' : 'press tab twice for more'; + } + return undefined; })(); @@ -257,9 +241,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { estimatedStatusLength + estimatedAmbientLength + 5 > terminalWidth; const showAmbientLine = - uiState.streamingState !== StreamingState.Idle && !hasPendingActionRequired && - (showTips || showWit) && ambientContentStr && !willCollideAmbient && !isNarrow; @@ -296,17 +278,22 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { const renderAmbientNode = () => { if (!ambientContentStr) return null; + const isShortcutHint = + ambientContentStr === '? for shortcuts' || + ambientContentStr === 'press tab twice for more'; + const color = + isShortcutHint && uiState.shortcutsHelpVisible + ? theme.text.accent + : theme.text.secondary; + return ( - + {ambientContentStr === uiState.currentTip ? `Tip: ${ambientContentStr}` @@ -352,7 +339,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { thought={uiState.thought} elapsedTime={uiState.elapsedTime} forceRealStatusOnly={false} - showCancelAndTimer={false} wittyPhrase={uiState.currentWittyPhrase} /> ); @@ -374,7 +360,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { errorVerbosity={settings.merged.ui.errorVerbosity} elapsedTime={uiState.elapsedTime} forceRealStatusOnly={true} - showCancelAndTimer={false} /> )} {hasUserHooks && ( @@ -450,12 +435,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { - {!isNarrow && ( - <> - {showShortcutsHint && } - {!showShortcutsHint && showAmbientLine && renderAmbientNode()} - - )} + {!isNarrow && showAmbientLine && renderAmbientNode()} )} @@ -464,7 +444,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { {showRow1 && showRow2 && (showUiDetails || (showRow1_MiniMode && showRow2_MiniMode)) && ( - + { - const { cleanUiDetailsVisible, shortcutsHelpVisible } = useUIState(); - - if (!cleanUiDetailsVisible) { - return press tab twice for more ; - } - - const highlightColor = shortcutsHelpVisible - ? theme.text.accent - : theme.text.secondary; - - return ? for shortcuts; -}; diff --git a/packages/cli/src/ui/components/__snapshots__/Composer.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/Composer.test.tsx.snap index fa12e847ae..745347bc95 100644 --- a/packages/cli/src/ui/components/__snapshots__/Composer.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/Composer.test.tsx.snap @@ -2,8 +2,8 @@ exports[`Composer > Snapshots > matches snapshot in idle state 1`] = ` " - - ─────────────────────────────────────────────────────────────────────────────────────────────────── + ? for shortcuts +──────────────────────────────────────────────────────────────────────────────────────────────────── ApprovalModeIndicator: default StatusDisplay InputPrompt: Type your message or @path/to/file Footer @@ -11,21 +11,21 @@ Footer `; exports[`Composer > Snapshots > matches snapshot in minimal UI mode 1`] = ` -" +" press tab twice for more InputPrompt: Type your message or @path/to/file " `; exports[`Composer > Snapshots > matches snapshot in minimal UI mode while loading 1`] = ` -"LoadingIndicator +"LoadingIndicator press tab twice for more InputPrompt: Type your message or @path/to/file " `; exports[`Composer > Snapshots > matches snapshot in narrow view 1`] = ` " - - ──────────────────────────────────────── + ? for shortcuts +──────────────────────────────────────── ApprovalModeIndicator: StatusDispl default ay InputPrompt: Type your message or @@ -36,8 +36,8 @@ Footer exports[`Composer > Snapshots > matches snapshot while streaming 1`] = ` " - LoadingIndicator: Thinking - ─────────────────────────────────────────────────────────────────────────────────────────────────── + LoadingIndicator: Thinking ? for shortcuts +──────────────────────────────────────────────────────────────────────────────────────────────────── ApprovalModeIndicator: default StatusDisplay InputPrompt: Type your message or @path/to/file Footer