mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
fix(cli): resolve hook visibility and restore original settings configuration
This commit is contained in:
@@ -259,7 +259,7 @@ const renderComposer = async (
|
|||||||
<SettingsContext.Provider value={settings as unknown as LoadedSettings}>
|
<SettingsContext.Provider value={settings as unknown as LoadedSettings}>
|
||||||
<UIStateContext.Provider value={uiState}>
|
<UIStateContext.Provider value={uiState}>
|
||||||
<UIActionsContext.Provider value={uiActions}>
|
<UIActionsContext.Provider value={uiActions}>
|
||||||
<Composer />
|
<Composer isFocused={true} />
|
||||||
</UIActionsContext.Provider>
|
</UIActionsContext.Provider>
|
||||||
</UIStateContext.Provider>
|
</UIStateContext.Provider>
|
||||||
</SettingsContext.Provider>
|
</SettingsContext.Provider>
|
||||||
@@ -822,12 +822,16 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
describe('Shortcuts Hint', () => {
|
describe('Shortcuts Hint', () => {
|
||||||
it('restores shortcuts hint after 200ms debounce when buffer is empty', async () => {
|
it('restores shortcuts hint after 200ms debounce when buffer is empty', async () => {
|
||||||
const { lastFrame } = await renderComposer(
|
const uiState = createMockUIState({
|
||||||
createMockUIState({
|
buffer: { text: '' } as unknown as TextBuffer,
|
||||||
buffer: { text: '' } as unknown as TextBuffer,
|
cleanUiDetailsVisible: false,
|
||||||
cleanUiDetailsVisible: false,
|
});
|
||||||
}),
|
|
||||||
);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
expect(lastFrame({ allowEmpty: true })).toContain('ShortcutsHint');
|
expect(lastFrame({ allowEmpty: true })).toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
@@ -880,6 +884,10 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
expect(lastFrame()).toContain('ShortcutsHint');
|
expect(lastFrame()).toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -890,6 +898,10 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
expect(lastFrame()).toContain('ShortcutsHint');
|
expect(lastFrame()).toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -901,6 +913,12 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
|
// In experimental layout, status row is visible during loading
|
||||||
|
expect(lastFrame()).toContain('LoadingIndicator');
|
||||||
expect(lastFrame()).not.toContain('ShortcutsHint');
|
expect(lastFrame()).not.toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -911,6 +929,7 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
// In experimental layout, shortcuts hint is hidden when text is present
|
||||||
expect(lastFrame()).not.toContain('ShortcutsHint');
|
expect(lastFrame()).not.toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -923,6 +942,12 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
|
// In experimental layout, status row is visible in clean mode while busy
|
||||||
|
expect(lastFrame()).toContain('LoadingIndicator');
|
||||||
expect(lastFrame()).not.toContain('ShortcutsHint');
|
expect(lastFrame()).not.toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -976,6 +1001,10 @@ describe('Composer', () => {
|
|||||||
|
|
||||||
const { lastFrame } = await renderComposer(uiState);
|
const { lastFrame } = await renderComposer(uiState);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await vi.advanceTimersByTimeAsync(250);
|
||||||
|
});
|
||||||
|
|
||||||
expect(lastFrame()).toContain('ShortcutsHint');
|
expect(lastFrame()).toContain('ShortcutsHint');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2026 Google LLC
|
* Copyright 2025 Google LLC
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
|
||||||
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
|
|
||||||
import {
|
import {
|
||||||
ApprovalMode,
|
ApprovalMode,
|
||||||
checkExhaustive,
|
checkExhaustive,
|
||||||
CoreToolCallStatus,
|
CoreToolCallStatus,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
import { Box, Text, useIsScreenReaderEnabled } from 'ink';
|
||||||
|
import type React from 'react';
|
||||||
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
|
import { useConfig } from '../contexts/ConfigContext.js';
|
||||||
|
import { useSettings } from '../contexts/SettingsContext.js';
|
||||||
|
import { useUIState } from '../contexts/UIStateContext.js';
|
||||||
|
import { useUIActions } from '../contexts/UIActionsContext.js';
|
||||||
|
import { useVimMode } from '../contexts/VimModeContext.js';
|
||||||
|
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
|
||||||
|
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
||||||
|
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
||||||
|
import { getInlineThinkingMode } from '../utils/inlineThinkingMode.js';
|
||||||
|
import { isContextUsageHigh } from '../utils/contextUsage.js';
|
||||||
|
import { theme } from '../semantic-colors.js';
|
||||||
|
import { GENERIC_WORKING_LABEL } from '../textConstants.js';
|
||||||
|
import { INTERACTIVE_SHELL_WAITING_PHRASE } from '../hooks/usePhraseCycler.js';
|
||||||
|
import { StreamingState, type HistoryItemToolGroup } from '../types.js';
|
||||||
import { LoadingIndicator } from './LoadingIndicator.js';
|
import { LoadingIndicator } from './LoadingIndicator.js';
|
||||||
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
|
||||||
import { StatusDisplay } from './StatusDisplay.js';
|
import { StatusDisplay } from './StatusDisplay.js';
|
||||||
import { HookStatusDisplay } from './HookStatusDisplay.js';
|
|
||||||
import { ToastDisplay, shouldShowToast } from './ToastDisplay.js';
|
import { ToastDisplay, shouldShowToast } from './ToastDisplay.js';
|
||||||
import { ApprovalModeIndicator } from './ApprovalModeIndicator.js';
|
import { ApprovalModeIndicator } from './ApprovalModeIndicator.js';
|
||||||
import { ShellModeIndicator } from './ShellModeIndicator.js';
|
import { ShellModeIndicator } from './ShellModeIndicator.js';
|
||||||
@@ -29,31 +42,25 @@ import { QueuedMessageDisplay } from './QueuedMessageDisplay.js';
|
|||||||
import { ContextUsageDisplay } from './ContextUsageDisplay.js';
|
import { ContextUsageDisplay } from './ContextUsageDisplay.js';
|
||||||
import { HorizontalLine } from './shared/HorizontalLine.js';
|
import { HorizontalLine } from './shared/HorizontalLine.js';
|
||||||
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
import { OverflowProvider } from '../contexts/OverflowContext.js';
|
||||||
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
|
import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
|
||||||
import { useUIState } from '../contexts/UIStateContext.js';
|
import { HookStatusDisplay } from './HookStatusDisplay.js';
|
||||||
import { useUIActions } from '../contexts/UIActionsContext.js';
|
import { ConfigInitDisplay } from './ConfigInitDisplay.js';
|
||||||
import { useVimMode } from '../contexts/VimModeContext.js';
|
|
||||||
import { useConfig } from '../contexts/ConfigContext.js';
|
|
||||||
import { useSettings } from '../contexts/SettingsContext.js';
|
|
||||||
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
|
|
||||||
import { StreamingState, type HistoryItemToolGroup } from '../types.js';
|
|
||||||
import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js';
|
|
||||||
import { TodoTray } from './messages/Todo.js';
|
import { TodoTray } from './messages/Todo.js';
|
||||||
import { getInlineThinkingMode } from '../utils/inlineThinkingMode.js';
|
|
||||||
import { isContextUsageHigh } from '../utils/contextUsage.js';
|
|
||||||
import { theme } from '../semantic-colors.js';
|
|
||||||
import { GENERIC_WORKING_LABEL } from '../textConstants.js';
|
|
||||||
|
|
||||||
export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
interface ComposerProps {
|
||||||
const config = useConfig();
|
isFocused: boolean;
|
||||||
const settings = useSettings();
|
}
|
||||||
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
||||||
|
export const Composer: React.FC<ComposerProps> = ({ isFocused }) => {
|
||||||
const uiState = useUIState();
|
const uiState = useUIState();
|
||||||
const uiActions = useUIActions();
|
const uiActions = useUIActions();
|
||||||
|
const settings = useSettings();
|
||||||
|
const config = useConfig();
|
||||||
|
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
||||||
|
const { columns: terminalWidth } = useTerminalSize();
|
||||||
|
const isNarrow = isNarrowWidth(terminalWidth);
|
||||||
const { vimEnabled, vimMode } = useVimMode();
|
const { vimEnabled, vimMode } = useVimMode();
|
||||||
const inlineThinkingMode = getInlineThinkingMode(settings);
|
const inlineThinkingMode = getInlineThinkingMode(settings);
|
||||||
const terminalWidth = uiState.terminalWidth;
|
|
||||||
const isNarrow = isNarrowWidth(terminalWidth);
|
|
||||||
const debugConsoleMaxHeight = Math.floor(Math.max(terminalWidth * 0.2, 5));
|
const debugConsoleMaxHeight = Math.floor(Math.max(terminalWidth * 0.2, 5));
|
||||||
const [suggestionsVisible, setSuggestionsVisible] = useState(false);
|
const [suggestionsVisible, setSuggestionsVisible] = useState(false);
|
||||||
|
|
||||||
@@ -117,18 +124,51 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
uiState.shortcutsHelpVisible &&
|
uiState.shortcutsHelpVisible &&
|
||||||
uiState.streamingState === StreamingState.Idle &&
|
uiState.streamingState === StreamingState.Idle &&
|
||||||
!hasPendingActionRequired;
|
!hasPendingActionRequired;
|
||||||
const isInteractiveShellWaiting =
|
|
||||||
uiState.currentLoadingPhrase?.includes('Tab to focus');
|
const [showShortcutsHintDebounced, setShowShortcutsHintDebounced] =
|
||||||
const hasToast = shouldShowToast(uiState) || isInteractiveShellWaiting;
|
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.
|
||||||
|
const shouldCollapseDuringApproval =
|
||||||
|
(settings.merged.ui as Record<string, unknown>)[
|
||||||
|
'collapseDrawerDuringApproval'
|
||||||
|
] !== false;
|
||||||
|
|
||||||
|
if (hasPendingActionRequired && shouldCollapseDuringApproval) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasToast = shouldShowToast(uiState);
|
||||||
const showLoadingIndicator =
|
const showLoadingIndicator =
|
||||||
(!uiState.embeddedShellFocused || uiState.isBackgroundShellVisible) &&
|
(!uiState.embeddedShellFocused || uiState.isBackgroundShellVisible) &&
|
||||||
uiState.streamingState === StreamingState.Responding &&
|
uiState.streamingState === StreamingState.Responding &&
|
||||||
!hasPendingActionRequired;
|
!hasPendingActionRequired;
|
||||||
|
|
||||||
const hideUiDetailsForSuggestions =
|
const hideUiDetailsForSuggestions =
|
||||||
suggestionsVisible && suggestionsPosition === 'above';
|
suggestionsVisible && suggestionsPosition === 'above';
|
||||||
const showApprovalIndicator =
|
const showApprovalIndicator =
|
||||||
!uiState.shellModeActive && !hideUiDetailsForSuggestions;
|
!uiState.shellModeActive && !hideUiDetailsForSuggestions;
|
||||||
const showRawMarkdownIndicator = !uiState.renderMarkdown;
|
const showRawMarkdownIndicator = !uiState.renderMarkdown;
|
||||||
|
|
||||||
let modeBleedThrough: { text: string; color: string } | null = null;
|
let modeBleedThrough: { text: string; color: string } | null = null;
|
||||||
switch (showApprovalModeIndicator) {
|
switch (showApprovalModeIndicator) {
|
||||||
case ApprovalMode.YOLO:
|
case ApprovalMode.YOLO:
|
||||||
@@ -164,37 +204,8 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
? uiState.currentModel
|
? uiState.currentModel
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const hideShortcutsHintForSuggestions = hideUiDetailsForSuggestions;
|
const hideShortcutsHintForSuggestions = hideUiDetailsForSuggestions;
|
||||||
const isModelIdle = uiState.streamingState === StreamingState.Idle;
|
|
||||||
const isBufferEmpty = uiState.buffer.text.length === 0;
|
|
||||||
const canShowShortcutsHint =
|
|
||||||
isModelIdle && isBufferEmpty && !hasPendingActionRequired;
|
|
||||||
const [showShortcutsHintDebounced, setShowShortcutsHintDebounced] =
|
|
||||||
useState(canShowShortcutsHint);
|
|
||||||
|
|
||||||
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.
|
|
||||||
const shouldCollapseDuringApproval =
|
|
||||||
(settings.merged.ui as Record<string, unknown>)[
|
|
||||||
'collapseDrawerDuringApproval'
|
|
||||||
] !== false;
|
|
||||||
|
|
||||||
if (hasPendingActionRequired && shouldCollapseDuringApproval) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const showShortcutsHint =
|
const showShortcutsHint =
|
||||||
settings.merged.ui.showShortcutsHint &&
|
settings.merged.ui.showShortcutsHint &&
|
||||||
@@ -203,6 +214,8 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
const showMinimalModeBleedThrough =
|
const showMinimalModeBleedThrough =
|
||||||
!hideUiDetailsForSuggestions && Boolean(minimalModeBleedThrough);
|
!hideUiDetailsForSuggestions && Boolean(minimalModeBleedThrough);
|
||||||
const showMinimalInlineLoading = !showUiDetails && showLoadingIndicator;
|
const showMinimalInlineLoading = !showUiDetails && showLoadingIndicator;
|
||||||
|
const hasActiveHooks =
|
||||||
|
uiState.activeHooks.length > 0 && settings.merged.hooksConfig.notifications;
|
||||||
const showMinimalBleedThroughRow =
|
const showMinimalBleedThroughRow =
|
||||||
!showUiDetails &&
|
!showUiDetails &&
|
||||||
(showMinimalModeBleedThrough ||
|
(showMinimalModeBleedThrough ||
|
||||||
@@ -212,7 +225,8 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
!showUiDetails &&
|
!showUiDetails &&
|
||||||
(showMinimalInlineLoading ||
|
(showMinimalInlineLoading ||
|
||||||
showMinimalBleedThroughRow ||
|
showMinimalBleedThroughRow ||
|
||||||
showShortcutsHint);
|
showShortcutsHint ||
|
||||||
|
hasActiveHooks);
|
||||||
|
|
||||||
let estimatedStatusLength = 0;
|
let estimatedStatusLength = 0;
|
||||||
if (
|
if (
|
||||||
@@ -241,6 +255,10 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
estimatedStatusLength = 20; // "↑ Action required"
|
estimatedStatusLength = 20; // "↑ Action required"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isInteractiveShellWaiting = uiState.currentLoadingPhrase?.includes(
|
||||||
|
INTERACTIVE_SHELL_WAITING_PHRASE,
|
||||||
|
);
|
||||||
|
|
||||||
const ambientText = (() => {
|
const ambientText = (() => {
|
||||||
if (isInteractiveShellWaiting) return undefined;
|
if (isInteractiveShellWaiting) return undefined;
|
||||||
|
|
||||||
@@ -317,8 +335,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderStatusNode = () => {
|
const renderStatusNode = () => {
|
||||||
if (!showUiDetails) return null;
|
// In experimental layout, hooks take priority
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isExperimentalLayout &&
|
isExperimentalLayout &&
|
||||||
uiState.activeHooks.length > 0 &&
|
uiState.activeHooks.length > 0 &&
|
||||||
@@ -345,8 +362,8 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
</Text>
|
</Text>
|
||||||
{!hasUserHooks && showWit && uiState.currentWittyPhrase && (
|
{!hasUserHooks && showWit && uiState.currentWittyPhrase && (
|
||||||
<Box marginLeft={1}>
|
<Box marginLeft={1}>
|
||||||
<Text color={theme.text.secondary} italic>
|
<Text color={theme.text.secondary} dimColor italic>
|
||||||
{uiState.currentWittyPhrase}
|
{uiState.currentWittyPhrase} :)
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -397,6 +414,188 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
const statusNode = renderStatusNode();
|
const statusNode = renderStatusNode();
|
||||||
const hasStatusMessage = Boolean(statusNode) || hasToast;
|
const hasStatusMessage = Boolean(statusNode) || hasToast;
|
||||||
|
|
||||||
|
const renderExperimentalStatusNode = () => {
|
||||||
|
if (!showUiDetails && !showMinimalMetaRow) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box width="100%" flexDirection="column">
|
||||||
|
{!showUiDetails && showMinimalMetaRow && (
|
||||||
|
<Box
|
||||||
|
width="100%"
|
||||||
|
flexDirection="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Box flexDirection="row">
|
||||||
|
{showMinimalInlineLoading && (
|
||||||
|
<LoadingIndicator
|
||||||
|
inline
|
||||||
|
loadingPhrases={loadingPhrases}
|
||||||
|
errorVerbosity={settings.merged.ui.errorVerbosity}
|
||||||
|
elapsedTime={uiState.elapsedTime}
|
||||||
|
forceRealStatusOnly={true}
|
||||||
|
showCancelAndTimer={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasActiveHooks && (
|
||||||
|
<Box marginLeft={showMinimalInlineLoading ? 1 : 0}>
|
||||||
|
<Box marginRight={1}>
|
||||||
|
<GeminiRespondingSpinner isHookActive={true} />
|
||||||
|
</Box>
|
||||||
|
<Text color={theme.text.primary} italic>
|
||||||
|
<HookStatusDisplay activeHooks={uiState.activeHooks} />
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{showMinimalBleedThroughRow && (
|
||||||
|
<Box
|
||||||
|
marginLeft={
|
||||||
|
showMinimalInlineLoading || hasActiveHooks ? 1 : 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showMinimalModeBleedThrough && minimalModeBleedThrough && (
|
||||||
|
<Text color={minimalModeBleedThrough.color}>
|
||||||
|
● {minimalModeBleedThrough.text}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{hasMinimalStatusBleedThrough && (
|
||||||
|
<Box
|
||||||
|
marginLeft={
|
||||||
|
showMinimalInlineLoading ||
|
||||||
|
showMinimalModeBleedThrough ||
|
||||||
|
hasActiveHooks
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ToastDisplay />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{showMinimalContextBleedThrough && (
|
||||||
|
<Box
|
||||||
|
marginLeft={
|
||||||
|
showMinimalInlineLoading ||
|
||||||
|
showMinimalModeBleedThrough ||
|
||||||
|
hasMinimalStatusBleedThrough ||
|
||||||
|
hasActiveHooks
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ContextUsageDisplay
|
||||||
|
promptTokenCount={
|
||||||
|
uiState.sessionStats.lastPromptTokenCount
|
||||||
|
}
|
||||||
|
model={uiState.currentModel}
|
||||||
|
terminalWidth={uiState.terminalWidth}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
{showShortcutsHint && (
|
||||||
|
<Box marginLeft={1}>
|
||||||
|
<ShortcutsHint />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showUiDetails && (
|
||||||
|
<Box
|
||||||
|
width="100%"
|
||||||
|
flexDirection="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="flex-start"
|
||||||
|
>
|
||||||
|
<Box flexDirection="row" flexGrow={1} flexShrink={1}>
|
||||||
|
{hasToast ? (
|
||||||
|
<Box width="100%" marginLeft={1}>
|
||||||
|
{isInteractiveShellWaiting && !shouldShowToast(uiState) ? (
|
||||||
|
<Text color={theme.status.warning}>
|
||||||
|
! Shell awaiting input (Tab to focus)
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<ToastDisplay />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
flexDirection="row"
|
||||||
|
alignItems={isNarrow ? 'flex-start' : 'center'}
|
||||||
|
flexGrow={1}
|
||||||
|
flexShrink={0}
|
||||||
|
marginLeft={1}
|
||||||
|
>
|
||||||
|
{statusNode}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{!hasToast && (
|
||||||
|
<Box flexShrink={0} marginLeft={2}>
|
||||||
|
{renderAmbientNode()}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showUiDetails && (
|
||||||
|
<Box
|
||||||
|
width="100%"
|
||||||
|
flexDirection={isNarrow ? 'column' : 'row'}
|
||||||
|
alignItems={isNarrow ? 'flex-start' : 'center'}
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<Box flexDirection="row" alignItems="center" marginLeft={1}>
|
||||||
|
{showApprovalIndicator && (
|
||||||
|
<ApprovalModeIndicator
|
||||||
|
approvalMode={showApprovalModeIndicator}
|
||||||
|
allowPlanMode={uiState.allowPlanMode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{uiState.shellModeActive && (
|
||||||
|
<Box
|
||||||
|
marginLeft={showApprovalIndicator && !isNarrow ? 1 : 0}
|
||||||
|
marginTop={showApprovalIndicator && isNarrow ? 1 : 0}
|
||||||
|
>
|
||||||
|
<ShellModeIndicator />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{showRawMarkdownIndicator && (
|
||||||
|
<Box
|
||||||
|
marginLeft={
|
||||||
|
(showApprovalIndicator || uiState.shellModeActive) &&
|
||||||
|
!isNarrow
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
marginTop={
|
||||||
|
(showApprovalIndicator || uiState.shellModeActive) &&
|
||||||
|
isNarrow
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<RawMarkdownIndicator />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
marginTop={isNarrow ? 1 : 0}
|
||||||
|
flexDirection="row"
|
||||||
|
alignItems="center"
|
||||||
|
marginLeft={isNarrow ? 1 : 0}
|
||||||
|
>
|
||||||
|
<StatusDisplay hideContextSummary={hideContextSummary} />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@@ -420,7 +619,9 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
|
|
||||||
<Box width="100%" flexDirection="column">
|
<Box width="100%" flexDirection="column">
|
||||||
{showUiDetails && hasStatusMessage && <HorizontalLine />}
|
{showUiDetails && hasStatusMessage && <HorizontalLine />}
|
||||||
{!isExperimentalLayout ? (
|
{isExperimentalLayout ? (
|
||||||
|
renderExperimentalStatusNode()
|
||||||
|
) : (
|
||||||
<Box width="100%" flexDirection="column">
|
<Box width="100%" flexDirection="column">
|
||||||
<Box
|
<Box
|
||||||
width="100%"
|
width="100%"
|
||||||
@@ -438,21 +639,14 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
{showUiDetails && showLoadingIndicator && (
|
{showUiDetails && showLoadingIndicator && (
|
||||||
<LoadingIndicator
|
<LoadingIndicator
|
||||||
inline
|
inline
|
||||||
thought={
|
loadingPhrases={loadingPhrases}
|
||||||
uiState.streamingState ===
|
errorVerbosity={settings.merged.ui.errorVerbosity}
|
||||||
StreamingState.WaitingForConfirmation
|
thought={uiState.thought}
|
||||||
? undefined
|
|
||||||
: uiState.thought
|
|
||||||
}
|
|
||||||
currentLoadingPhrase={
|
|
||||||
!showTips && !showWit
|
|
||||||
? undefined
|
|
||||||
: uiState.currentLoadingPhrase
|
|
||||||
}
|
|
||||||
thoughtLabel={
|
thoughtLabel={
|
||||||
inlineThinkingMode === 'full' ? 'Thinking ...' : undefined
|
inlineThinkingMode === 'full' ? 'Thinking ...' : undefined
|
||||||
}
|
}
|
||||||
elapsedTime={uiState.elapsedTime}
|
elapsedTime={uiState.elapsedTime}
|
||||||
|
forceRealStatusOnly={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -481,39 +675,52 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
{showMinimalInlineLoading && (
|
{showMinimalInlineLoading && (
|
||||||
<LoadingIndicator
|
<LoadingIndicator
|
||||||
inline
|
inline
|
||||||
thought={
|
loadingPhrases={loadingPhrases}
|
||||||
uiState.streamingState ===
|
errorVerbosity={settings.merged.ui.errorVerbosity}
|
||||||
StreamingState.WaitingForConfirmation
|
|
||||||
? undefined
|
|
||||||
: uiState.thought
|
|
||||||
}
|
|
||||||
currentLoadingPhrase={
|
|
||||||
!showTips && !showWit
|
|
||||||
? undefined
|
|
||||||
: uiState.currentLoadingPhrase
|
|
||||||
}
|
|
||||||
thoughtLabel={
|
|
||||||
inlineThinkingMode === 'full'
|
|
||||||
? 'Thinking ...'
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
elapsedTime={uiState.elapsedTime}
|
elapsedTime={uiState.elapsedTime}
|
||||||
|
forceRealStatusOnly={true}
|
||||||
|
showCancelAndTimer={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{showMinimalModeBleedThrough && minimalModeBleedThrough && (
|
{hasActiveHooks && (
|
||||||
<Text color={minimalModeBleedThrough.color}>
|
<Box marginLeft={showMinimalInlineLoading ? 1 : 0}>
|
||||||
● {minimalModeBleedThrough.text}
|
<Box marginRight={1}>
|
||||||
</Text>
|
<GeminiRespondingSpinner isHookActive={true} />
|
||||||
|
</Box>
|
||||||
|
<Text color={theme.text.primary} italic>
|
||||||
|
<HookStatusDisplay activeHooks={uiState.activeHooks} />
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
{hasMinimalStatusBleedThrough && (
|
{showMinimalBleedThroughRow && (
|
||||||
<Box
|
<Box
|
||||||
marginLeft={
|
marginLeft={
|
||||||
showMinimalInlineLoading || showMinimalModeBleedThrough
|
showMinimalInlineLoading ||
|
||||||
|
showMinimalModeBleedThrough ||
|
||||||
|
hasActiveHooks
|
||||||
? 1
|
? 1
|
||||||
: 0
|
: 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ToastDisplay />
|
{showMinimalModeBleedThrough &&
|
||||||
|
minimalModeBleedThrough && (
|
||||||
|
<Text color={minimalModeBleedThrough.color}>
|
||||||
|
● {minimalModeBleedThrough.text}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{hasMinimalStatusBleedThrough && (
|
||||||
|
<Box
|
||||||
|
marginLeft={
|
||||||
|
showMinimalInlineLoading ||
|
||||||
|
showMinimalModeBleedThrough ||
|
||||||
|
hasActiveHooks
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ToastDisplay />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -572,7 +779,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
allowPlanMode={uiState.allowPlanMode}
|
allowPlanMode={uiState.allowPlanMode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!showLoadingIndicator && (
|
{!showLoadingIndicator && !hasActiveHooks && (
|
||||||
<>
|
<>
|
||||||
{uiState.shellModeActive && (
|
{uiState.shellModeActive && (
|
||||||
<Box marginLeft={1}>
|
<Box marginLeft={1}>
|
||||||
@@ -587,7 +794,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{!showLoadingIndicator && (
|
{!showLoadingIndicator && !hasActiveHooks && (
|
||||||
<>
|
<>
|
||||||
<Box marginLeft={1}>
|
<Box marginLeft={1}>
|
||||||
<Text color={theme.text.secondary}>·</Text>
|
<Text color={theme.text.secondary}>·</Text>
|
||||||
@@ -602,96 +809,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
|
||||||
<Box width="100%" flexDirection="column">
|
|
||||||
{showUiDetails && (
|
|
||||||
<Box
|
|
||||||
width="100%"
|
|
||||||
flexDirection="row"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="space-between"
|
|
||||||
>
|
|
||||||
{hasToast ? (
|
|
||||||
<Box width="100%" marginLeft={1}>
|
|
||||||
{isInteractiveShellWaiting && !shouldShowToast(uiState) ? (
|
|
||||||
<Text color={theme.status.warning}>
|
|
||||||
! Shell awaiting input (Tab to focus)
|
|
||||||
</Text>
|
|
||||||
) : (
|
|
||||||
<ToastDisplay />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Box
|
|
||||||
flexDirection="row"
|
|
||||||
alignItems={isNarrow ? 'flex-start' : 'center'}
|
|
||||||
flexGrow={1}
|
|
||||||
flexShrink={0}
|
|
||||||
marginLeft={1}
|
|
||||||
>
|
|
||||||
{statusNode}
|
|
||||||
</Box>
|
|
||||||
<Box flexShrink={0} marginLeft={2}>
|
|
||||||
{renderAmbientNode()}
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showUiDetails && (
|
|
||||||
<Box
|
|
||||||
width="100%"
|
|
||||||
flexDirection={isNarrow ? 'column' : 'row'}
|
|
||||||
alignItems={isNarrow ? 'flex-start' : 'center'}
|
|
||||||
justifyContent="space-between"
|
|
||||||
>
|
|
||||||
<Box flexDirection="row" alignItems="center" marginLeft={1}>
|
|
||||||
{showApprovalIndicator && (
|
|
||||||
<ApprovalModeIndicator
|
|
||||||
approvalMode={showApprovalModeIndicator}
|
|
||||||
allowPlanMode={uiState.allowPlanMode}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{uiState.shellModeActive && (
|
|
||||||
<Box
|
|
||||||
marginLeft={showApprovalIndicator && !isNarrow ? 1 : 0}
|
|
||||||
marginTop={showApprovalIndicator && isNarrow ? 1 : 0}
|
|
||||||
>
|
|
||||||
<ShellModeIndicator />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{showRawMarkdownIndicator && (
|
|
||||||
<Box
|
|
||||||
marginLeft={
|
|
||||||
(showApprovalIndicator || uiState.shellModeActive) &&
|
|
||||||
!isNarrow
|
|
||||||
? 1
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
marginTop={
|
|
||||||
(showApprovalIndicator || uiState.shellModeActive) &&
|
|
||||||
isNarrow
|
|
||||||
? 1
|
|
||||||
: 0
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<RawMarkdownIndicator />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
marginTop={isNarrow ? 1 : 0}
|
|
||||||
flexDirection="row"
|
|
||||||
alignItems="center"
|
|
||||||
marginLeft={isNarrow ? 1 : 0}
|
|
||||||
>
|
|
||||||
<StatusDisplay hideContextSummary={hideContextSummary} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -713,7 +830,6 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
|
|
||||||
{uiState.isInputActive && (
|
{uiState.isInputActive && (
|
||||||
<InputPrompt
|
<InputPrompt
|
||||||
disabled={hasPendingActionRequired}
|
|
||||||
buffer={uiState.buffer}
|
buffer={uiState.buffer}
|
||||||
inputWidth={uiState.inputWidth}
|
inputWidth={uiState.inputWidth}
|
||||||
suggestionsWidth={uiState.suggestionsWidth}
|
suggestionsWidth={uiState.suggestionsWidth}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ interface LoadingIndicatorProps {
|
|||||||
wittyPhrase?: string;
|
wittyPhrase?: string;
|
||||||
showWit?: boolean;
|
showWit?: boolean;
|
||||||
showTips?: boolean;
|
showTips?: boolean;
|
||||||
|
loadingPhrases?: 'tips' | 'witty' | 'all' | 'off';
|
||||||
|
errorVerbosity?: 'low' | 'full';
|
||||||
elapsedTime: number;
|
elapsedTime: number;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
rightContent?: React.ReactNode;
|
rightContent?: React.ReactNode;
|
||||||
@@ -34,8 +36,10 @@ interface LoadingIndicatorProps {
|
|||||||
export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
||||||
currentLoadingPhrase,
|
currentLoadingPhrase,
|
||||||
wittyPhrase,
|
wittyPhrase,
|
||||||
showWit = true,
|
showWit: showWitProp,
|
||||||
showTips: _showTips = true,
|
showTips: _showTipsProp,
|
||||||
|
loadingPhrases = 'all',
|
||||||
|
errorVerbosity: _errorVerbosity = 'full',
|
||||||
elapsedTime,
|
elapsedTime,
|
||||||
inline = false,
|
inline = false,
|
||||||
rightContent,
|
rightContent,
|
||||||
@@ -48,6 +52,9 @@ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
|
|||||||
const { columns: terminalWidth } = useTerminalSize();
|
const { columns: terminalWidth } = useTerminalSize();
|
||||||
const isNarrow = isNarrowWidth(terminalWidth);
|
const isNarrow = isNarrowWidth(terminalWidth);
|
||||||
|
|
||||||
|
const showWit =
|
||||||
|
showWitProp ?? (loadingPhrases === 'witty' || loadingPhrases === 'all');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
streamingState === StreamingState.Idle &&
|
streamingState === StreamingState.Idle &&
|
||||||
!currentLoadingPhrase &&
|
!currentLoadingPhrase &&
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useUIState } from '../contexts/UIStateContext.js';
|
|||||||
import { useSettings } from '../contexts/SettingsContext.js';
|
import { useSettings } from '../contexts/SettingsContext.js';
|
||||||
import { useConfig } from '../contexts/ConfigContext.js';
|
import { useConfig } from '../contexts/ConfigContext.js';
|
||||||
import { ContextSummaryDisplay } from './ContextSummaryDisplay.js';
|
import { ContextSummaryDisplay } from './ContextSummaryDisplay.js';
|
||||||
|
import { HookStatusDisplay } from './HookStatusDisplay.js';
|
||||||
|
|
||||||
interface StatusDisplayProps {
|
interface StatusDisplayProps {
|
||||||
hideContextSummary: boolean;
|
hideContextSummary: boolean;
|
||||||
@@ -27,6 +28,20 @@ export const StatusDisplay: React.FC<StatusDisplayProps> = ({
|
|||||||
return <Text color={theme.status.error}>|⌐■_■|</Text>;
|
return <Text color={theme.status.error}>|⌐■_■|</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In legacy layout, we show hooks here.
|
||||||
|
// In experimental layout, hooks are shown in the top row of the composer,
|
||||||
|
// but we still show them here if they are "system" hooks or if notifications are enabled.
|
||||||
|
const isLegacyLayout =
|
||||||
|
(settings.merged.ui as Record<string, unknown>)['useLegacyLayout'] === true;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isLegacyLayout &&
|
||||||
|
uiState.activeHooks.length > 0 &&
|
||||||
|
settings.merged.hooksConfig.notifications
|
||||||
|
) {
|
||||||
|
return <HookStatusDisplay activeHooks={uiState.activeHooks} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!settings.merged.ui.hideContextSummary && !hideContextSummary) {
|
if (!settings.merged.ui.hideContextSummary && !hideContextSummary) {
|
||||||
return (
|
return (
|
||||||
<ContextSummaryDisplay
|
<ContextSummaryDisplay
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`Composer > Snapshots > matches snapshot in idle state 1`] = `
|
exports[`Composer > Snapshots > matches snapshot in idle state 1`] = `
|
||||||
" ShortcutsHint
|
"
|
||||||
ApprovalModeIndicator ·StatusDisplay
|
ApprovalModeIndicator ·StatusDisplay
|
||||||
InputPrompt: Type your message or @path/to/file
|
InputPrompt: Type your message or @path/to/file
|
||||||
Footer
|
Footer
|
||||||
@@ -9,20 +9,20 @@ Footer
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Composer > Snapshots > matches snapshot in minimal UI mode 1`] = `
|
exports[`Composer > Snapshots > matches snapshot in minimal UI mode 1`] = `
|
||||||
" ShortcutsHint
|
"
|
||||||
InputPrompt: Type your message or @path/to/file
|
InputPrompt: Type your message or @path/to/file
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Composer > Snapshots > matches snapshot in minimal UI mode while loading 1`] = `
|
exports[`Composer > Snapshots > matches snapshot in minimal UI mode while loading 1`] = `
|
||||||
" LoadingIndicator
|
"
|
||||||
|
LoadingIndicator
|
||||||
InputPrompt: Type your message or @path/to/file
|
InputPrompt: Type your message or @path/to/file
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Composer > Snapshots > matches snapshot in narrow view 1`] = `
|
exports[`Composer > Snapshots > matches snapshot in narrow view 1`] = `
|
||||||
"
|
"
|
||||||
ShortcutsHint
|
|
||||||
ApprovalModeIndicator ·StatusDisplay
|
ApprovalModeIndicator ·StatusDisplay
|
||||||
InputPrompt: Type your message or
|
InputPrompt: Type your message or
|
||||||
@path/to/file
|
@path/to/file
|
||||||
@@ -33,6 +33,7 @@ Footer
|
|||||||
exports[`Composer > Snapshots > matches snapshot while streaming 1`] = `
|
exports[`Composer > Snapshots > matches snapshot while streaming 1`] = `
|
||||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||||
LoadingIndicator: Thinking
|
LoadingIndicator: Thinking
|
||||||
|
|
||||||
ApprovalModeIndicator
|
ApprovalModeIndicator
|
||||||
InputPrompt: Type your message or @path/to/file
|
InputPrompt: Type your message or @path/to/file
|
||||||
Footer
|
Footer
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export const ScreenReaderAppLayout: React.FC = () => {
|
|||||||
addItem={uiState.historyManager.addItem}
|
addItem={uiState.historyManager.addItem}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Composer />
|
<Composer isFocused={true} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ExitWarning />
|
<ExitWarning />
|
||||||
|
|||||||
Reference in New Issue
Block a user