diff options
Diffstat (limited to 'packages/cli/src/ui/App.tsx')
| -rw-r--r-- | packages/cli/src/ui/App.tsx | 337 |
1 files changed, 168 insertions, 169 deletions
diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 1453487f..5860e36e 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -41,6 +41,10 @@ import { useHistory } from './hooks/useHistoryManager.js'; import process from 'node:process'; import { getErrorMessage, type Config } from '@gemini-code/server'; import { useLogger } from './hooks/useLogger.js'; +import { + StreamingContext, + StreamingContextType, +} from './contexts/StreamingContext.js'; interface AppProps { config: Config; @@ -174,19 +178,15 @@ export const App = ({ handleSlashCommand, shellModeActive, ); - const isPausedForConfirmation = useMemo( - () => - pendingHistoryItems.some( - (item) => - item?.type === 'tool_group' && - item.tools.some((tool) => tool.status === 'Confirming'), - ), - [pendingHistoryItems], - ); - const { elapsedTime, currentLoadingPhrase, shouldShowSpinner } = - useLoadingIndicator(streamingState, isPausedForConfirmation); + const { elapsedTime, currentLoadingPhrase } = + useLoadingIndicator(streamingState); const showAutoAcceptIndicator = useAutoAcceptIndicator({ config }); + const streamingContextValue: StreamingContextType = useMemo( + () => ({ streamingState }), + [streamingState], + ); + const handleFinalSubmit = useCallback( (submittedValue: string) => { const trimmedValue = submittedValue.trim(); @@ -278,177 +278,176 @@ export const App = ({ }, [consoleMessages, config]); return ( - <Box flexDirection="column" marginBottom={1} width="90%"> - {/* - * The Static component is an Ink intrinsic in which there can only be 1 per application. - * Because of this restriction we're hacking it slightly by having a 'header' item here to - * ensure that it's statically rendered. - * - * Background on the Static Item: Anything in the Static component is written a single time - * to the console. Think of it like doing a console.log and then never using ANSI codes to - * clear that content ever again. Effectively it has a moving frame that every time new static - * content is set it'll flush content to the terminal and move the area which it's "clearing" - * down a notch. Without Static the area which gets erased and redrawn continuously grows. - */} - <Static - key={staticKey} - items={[ - <Box flexDirection="column" key="header"> - <Header /> - <Tips /> - </Box>, - ...history.map((h) => ( + <StreamingContext.Provider value={streamingContextValue}> + <Box flexDirection="column" marginBottom={1} width="90%"> + {/* + * The Static component is an Ink intrinsic in which there can only be 1 per application. + * Because of this restriction we're hacking it slightly by having a 'header' item here to + * ensure that it's statically rendered. + * + * Background on the Static Item: Anything in the Static component is written a single time + * to the console. Think of it like doing a console.log and then never using ANSI codes to + * clear that content ever again. Effectively it has a moving frame that every time new static + * content is set it'll flush content to the terminal and move the area which it's "clearing" + * down a notch. Without Static the area which gets erased and redrawn continuously grows. + */} + <Static + key={staticKey} + items={[ + <Box flexDirection="column" key="header"> + <Header /> + <Tips /> + </Box>, + ...history.map((h) => ( + <HistoryItemDisplay + availableTerminalHeight={availableTerminalHeight} + key={h.id} + item={h} + isPending={false} + /> + )), + ]} + > + {(item) => item} + </Static> + <Box ref={pendingHistoryItemRef}> + {pendingHistoryItems.map((item, i) => ( <HistoryItemDisplay + key={i} availableTerminalHeight={availableTerminalHeight} - key={h.id} - item={h} - isPending={false} - streamingState={streamingState} + // TODO(taehykim): It seems like references to ids aren't necessary in + // HistoryItemDisplay. Refactor later. Use a fake id for now. + item={{ ...item, id: 0 }} + isPending={true} /> - )), - ]} - > - {(item) => item} - </Static> - <Box ref={pendingHistoryItemRef}> - {pendingHistoryItems.map((item, i) => ( - <HistoryItemDisplay - key={i} - availableTerminalHeight={availableTerminalHeight} - // TODO(taehykim): It seems like references to ids aren't necessary in - // HistoryItemDisplay. Refactor later. Use a fake id for now. - item={{ ...item, id: 0 }} - isPending={true} - streamingState={streamingState} - /> - ))} - </Box> - {showHelp && <Help commands={slashCommands} />} - - <Box flexDirection="column" ref={mainControlsRef}> - {startupWarnings.length > 0 && ( - <Box - borderStyle="round" - borderColor={Colors.AccentYellow} - paddingX={1} - marginY={1} - flexDirection="column" - > - {startupWarnings.map((warning, index) => ( - <Text key={index} color={Colors.AccentYellow}> - {warning} - </Text> - ))} - </Box> - )} + ))} + </Box> + {showHelp && <Help commands={slashCommands} />} - {isThemeDialogOpen ? ( - <Box flexDirection="column"> - {themeError && ( - <Box marginBottom={1}> - <Text color={Colors.AccentRed}>{themeError}</Text> - </Box> - )} - <ThemeDialog - onSelect={handleThemeSelect} - onHighlight={handleThemeHighlight} - settings={settings} - /> - </Box> - ) : ( - <> - <LoadingIndicator - isLoading={streamingState === StreamingState.Responding} - showSpinner={shouldShowSpinner} - currentLoadingPhrase={currentLoadingPhrase} - elapsedTime={elapsedTime} - /> + <Box flexDirection="column" ref={mainControlsRef}> + {startupWarnings.length > 0 && ( <Box - marginTop={1} - display="flex" - justifyContent="space-between" - width="100%" + borderStyle="round" + borderColor={Colors.AccentYellow} + paddingX={1} + marginY={1} + flexDirection="column" > - <Box> - {process.env.GEMINI_SYSTEM_MD && ( - <Text color={Colors.AccentRed}>|⌐■_■| </Text> - )} - {geminiMdFileCount > 0 && ( - <Text color={Colors.SubtleComment}> - Using {geminiMdFileCount} GEMINI.md file - {geminiMdFileCount > 1 ? 's' : ''} - </Text> - )} - </Box> - <Box> - {showAutoAcceptIndicator && !shellModeActive && ( - <AutoAcceptIndicator /> - )} - {shellModeActive && <ShellModeIndicator />} - </Box> + {startupWarnings.map((warning, index) => ( + <Text key={index} color={Colors.AccentYellow}> + {warning} + </Text> + ))} </Box> + )} - {showErrorDetails && ( - <DetailedMessagesDisplay messages={filteredConsoleMessages} /> - )} - - {isInputActive && ( - <InputPrompt - widthFraction={0.9} - onSubmit={handleFinalSubmit} - userMessages={userMessages} - onClearScreen={handleClearScreen} - config={config} - slashCommands={slashCommands} - shellModeActive={shellModeActive} - setShellModeActive={setShellModeActive} + {isThemeDialogOpen ? ( + <Box flexDirection="column"> + {themeError && ( + <Box marginBottom={1}> + <Text color={Colors.AccentRed}>{themeError}</Text> + </Box> + )} + <ThemeDialog + onSelect={handleThemeSelect} + onHighlight={handleThemeHighlight} + settings={settings} + /> + </Box> + ) : ( + <> + <LoadingIndicator + currentLoadingPhrase={currentLoadingPhrase} + elapsedTime={elapsedTime} /> - )} - </> - )} + <Box + marginTop={1} + display="flex" + justifyContent="space-between" + width="100%" + > + <Box> + {process.env.GEMINI_SYSTEM_MD && ( + <Text color={Colors.AccentRed}>|⌐■_■| </Text> + )} + {geminiMdFileCount > 0 && ( + <Text color={Colors.SubtleComment}> + Using {geminiMdFileCount} GEMINI.md file + {geminiMdFileCount > 1 ? 's' : ''} + </Text> + )} + </Box> + <Box> + {showAutoAcceptIndicator && !shellModeActive && ( + <AutoAcceptIndicator /> + )} + {shellModeActive && <ShellModeIndicator />} + </Box> + </Box> - {initError && streamingState !== StreamingState.Responding && ( - <Box - borderStyle="round" - borderColor={Colors.AccentRed} - paddingX={1} - marginBottom={1} - > - {history.find( - (item) => item.type === 'error' && item.text?.includes(initError), - )?.text ? ( - <Text color={Colors.AccentRed}> - { - history.find( - (item) => - item.type === 'error' && item.text?.includes(initError), - )?.text - } - </Text> - ) : ( - <> - <Text color={Colors.AccentRed}> - Initialization Error: {initError} - </Text> + {showErrorDetails && ( + <DetailedMessagesDisplay messages={filteredConsoleMessages} /> + )} + + {isInputActive && ( + <InputPrompt + widthFraction={0.9} + onSubmit={handleFinalSubmit} + userMessages={userMessages} + onClearScreen={handleClearScreen} + config={config} + slashCommands={slashCommands} + shellModeActive={shellModeActive} + setShellModeActive={setShellModeActive} + /> + )} + </> + )} + + {initError && streamingState !== StreamingState.Responding && ( + <Box + borderStyle="round" + borderColor={Colors.AccentRed} + paddingX={1} + marginBottom={1} + > + {history.find( + (item) => + item.type === 'error' && item.text?.includes(initError), + )?.text ? ( <Text color={Colors.AccentRed}> - {' '} - Please check API key and configuration. + { + history.find( + (item) => + item.type === 'error' && item.text?.includes(initError), + )?.text + } </Text> - </> - )} - </Box> - )} + ) : ( + <> + <Text color={Colors.AccentRed}> + Initialization Error: {initError} + </Text> + <Text color={Colors.AccentRed}> + {' '} + Please check API key and configuration. + </Text> + </> + )} + </Box> + )} - <Footer - config={config} - debugMode={config.getDebugMode()} - debugMessage={debugMessage} - cliVersion={cliVersion} - corgiMode={corgiMode} - errorCount={errorCount} - showErrorDetails={showErrorDetails} - /> + <Footer + config={config} + debugMode={config.getDebugMode()} + debugMessage={debugMessage} + cliVersion={cliVersion} + corgiMode={corgiMode} + errorCount={errorCount} + showErrorDetails={showErrorDetails} + /> + </Box> </Box> - </Box> + </StreamingContext.Provider> ); }; |
