Download packages/cli/src/interactiveCli.tsx from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 8.73 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/interactiveCli.tsx
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/interactiveCli.tsx
-
curl -L -o interactiveCli.tsx https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/interactiveCli.tsx
8.73 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import React from 'react'; | |
| import { render } from 'ink'; | |
| import { basename } from 'node:path'; | |
| import { AppContainer } from './ui/AppContainer.js'; | |
| import { ConsolePatcher } from './ui/utils/ConsolePatcher.js'; | |
| import { | |
| registerCleanup, | |
| removeCleanup, | |
| setupTtyCheck, | |
| } from './utils/cleanup.js'; | |
| import { | |
| type StartupWarning, | |
| type Config, | |
| type ResumedSessionData, | |
| coreEvents, | |
| createWorkingStdio, | |
| disableMouseEvents, | |
| enableMouseEvents, | |
| disableLineWrapping, | |
| enableLineWrapping, | |
| shouldEnterAlternateScreen, | |
| recordSlowRender, | |
| writeToStdout, | |
| getVersion, | |
| debugLogger, | |
| } from '@google/gemini-cli-core'; | |
| import type { InitializationResult } from './core/initializer.js'; | |
| import type { LoadedSettings } from './config/settings.js'; | |
| import { checkForUpdates } from './ui/utils/updateCheck.js'; | |
| import { handleAutoUpdate } from './utils/handleAutoUpdate.js'; | |
| import { SettingsContext } from './ui/contexts/SettingsContext.js'; | |
| import { MouseProvider } from './ui/contexts/MouseContext.js'; | |
| import { StreamingState } from './ui/types.js'; | |
| import { computeTerminalTitle } from './utils/windowTitle.js'; | |
| import { SessionStatsProvider } from './ui/contexts/SessionContext.js'; | |
| import { VimModeProvider } from './ui/contexts/VimModeContext.js'; | |
| import { KeyMatchersProvider } from './ui/hooks/useKeyMatchers.js'; | |
| import { loadKeyMatchers } from './ui/key/keyMatchers.js'; | |
| import { KeypressProvider } from './ui/contexts/KeypressContext.js'; | |
| import { useKittyKeyboardProtocol } from './ui/hooks/useKittyKeyboardProtocol.js'; | |
| import { ScrollProvider } from './ui/contexts/ScrollProvider.js'; | |
| import { TerminalProvider } from './ui/contexts/TerminalContext.js'; | |
| import { OverflowProvider } from './ui/contexts/OverflowContext.js'; | |
| import { profiler } from './ui/components/DebugProfiler.js'; | |
| import { initializeConsoleStore } from './ui/hooks/useConsoleMessages.js'; | |
| const SLOW_RENDER_MS = 200; | |
| export async function startInteractiveUI( | |
| config: Config, | |
| settings: LoadedSettings, | |
| startupWarnings: StartupWarning[], | |
| workspaceRoot: string = process.cwd(), | |
| resumedSessionData: ResumedSessionData | undefined, | |
| initializationResult: InitializationResult, | |
| ) { | |
| initializeConsoleStore(); | |
| // Never enter Ink alternate buffer mode when screen reader mode is enabled | |
| // as there is no benefit of alternate buffer mode when using a screen reader | |
| // and the Ink alternate buffer mode requires line wrapping harmful to | |
| // screen readers. | |
| const useAlternateBuffer = shouldEnterAlternateScreen( | |
| config.getUseAlternateBuffer(), | |
| config.getScreenReader(), | |
| ); | |
| const mouseEventsEnabled = useAlternateBuffer; | |
| if (mouseEventsEnabled) { | |
| enableMouseEvents(); | |
| registerCleanup(() => { | |
| disableMouseEvents(); | |
| }); | |
| } | |
| const { matchers, errors } = await loadKeyMatchers(); | |
| errors.forEach((error) => { | |
| coreEvents.emitFeedback('warning', error); | |
| }); | |
| const version = await getVersion(); | |
| setWindowTitle(basename(workspaceRoot), settings); | |
| const consolePatcher = new ConsolePatcher({ | |
| onNewMessage: (msg) => { | |
| coreEvents.emitConsoleLog(msg.type, msg.content); | |
| }, | |
| debugMode: config.getDebugMode(), | |
| }); | |
| consolePatcher.patch(); | |
| const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio(); | |
| const isShpool = !!process.env['SHPOOL_SESSION_NAME']; | |
| // Create wrapper component to use hooks inside render | |
| const AppWrapper = () => { | |
| useKittyKeyboardProtocol(); | |
| return ( | |
| <SettingsContext.Provider value={settings}> | |
| <KeyMatchersProvider value={matchers}> | |
| <KeypressProvider config={config}> | |
| <MouseProvider mouseEventsEnabled={mouseEventsEnabled}> | |
| <TerminalProvider> | |
| <ScrollProvider> | |
| <OverflowProvider> | |
| <SessionStatsProvider sessionId={config.getSessionId()}> | |
| <VimModeProvider> | |
| <AppContainer | |
| config={config} | |
| startupWarnings={startupWarnings} | |
| version={version} | |
| resumedSessionData={resumedSessionData} | |
| initializationResult={initializationResult} | |
| /> | |
| </VimModeProvider> | |
| </SessionStatsProvider> | |
| </OverflowProvider> | |
| </ScrollProvider> | |
| </TerminalProvider> | |
| </MouseProvider> | |
| </KeypressProvider> | |
| </KeyMatchersProvider> | |
| </SettingsContext.Provider> | |
| ); | |
| }; | |
| if (isShpool) { | |
| // Wait a moment for shpool to stabilize terminal size and state. | |
| await new Promise((resolve) => setTimeout(resolve, 100)); | |
| } | |
| const instance = render( | |
| process.env['DEBUG'] ? ( | |
| <React.StrictMode> | |
| <AppWrapper /> | |
| </React.StrictMode> | |
| ) : ( | |
| <AppWrapper /> | |
| ), | |
| { | |
| stdout: inkStdout, | |
| stderr: inkStderr, | |
| stdin: process.stdin, | |
| exitOnCtrlC: false, | |
| isScreenReaderEnabled: config.getScreenReader(), | |
| onRender: ({ renderTime }: { renderTime: number }) => { | |
| if (renderTime > SLOW_RENDER_MS) { | |
| recordSlowRender(config, Math.round(renderTime)); | |
| } | |
| profiler.reportFrameRendered(); | |
| }, | |
| standardReactLayoutTiming: | |
| useAlternateBuffer || config.getUseTerminalBuffer(), | |
| patchConsole: false, | |
| alternateBuffer: useAlternateBuffer, | |
| terminalBuffer: config.getUseTerminalBuffer(), | |
| renderProcess: | |
| config.getUseRenderProcess() && config.getUseTerminalBuffer(), | |
| incrementalRendering: | |
| settings.merged.ui.incrementalRendering !== false && | |
| useAlternateBuffer && | |
| !isShpool, | |
| debugRainbow: settings.merged.ui.debugRainbow === true, | |
| }, | |
| ); | |
| let cleanupLineWrapping: (() => void) | undefined; | |
| if (useAlternateBuffer) { | |
| disableLineWrapping(); | |
| cleanupLineWrapping = () => enableLineWrapping(); | |
| registerCleanup(cleanupLineWrapping); | |
| } | |
| checkForUpdates(settings) | |
| .then((info) => { | |
| handleAutoUpdate( | |
| info, | |
| settings, | |
| config.getProjectRoot(), | |
| config.getSandboxEnabled(), | |
| ); | |
| }) | |
| .catch((err) => { | |
| // Silently ignore update check errors. | |
| if (config.getDebugMode()) { | |
| debugLogger.warn('Update check failed:', err); | |
| } | |
| }); | |
| const cleanupUnmount = () => instance.unmount(); | |
| const cleanupNonResumableCurrentSession = async () => { | |
| try { | |
| await config | |
| .getGeminiClient() | |
| ?.getChatRecordingService() | |
| ?.deleteCurrentSessionIfNotResumableAsync(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error cleaning up non-resumable session:', e); | |
| } | |
| }; | |
| registerCleanup(cleanupNonResumableCurrentSession); | |
| registerCleanup(cleanupUnmount); | |
| const cleanupTtyCheck = setupTtyCheck(); | |
| registerCleanup(cleanupTtyCheck); | |
| const cleanupConsolePatcher = () => consolePatcher.cleanup(); | |
| registerCleanup(cleanupConsolePatcher); | |
| try { | |
| await instance.waitUntilExit(); | |
| } finally { | |
| try { | |
| removeCleanup(cleanupConsolePatcher); | |
| cleanupConsolePatcher(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error cleaning up console patcher:', e); | |
| } | |
| try { | |
| removeCleanup(cleanupNonResumableCurrentSession); | |
| await cleanupNonResumableCurrentSession(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error removing non-resumable session cleanup:', e); | |
| } | |
| try { | |
| removeCleanup(cleanupUnmount); | |
| instance.unmount(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error unmounting Ink instance:', e); | |
| } | |
| try { | |
| removeCleanup(cleanupTtyCheck); | |
| cleanupTtyCheck(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error in TTY cleanup:', e); | |
| } | |
| if (cleanupLineWrapping) { | |
| try { | |
| removeCleanup(cleanupLineWrapping); | |
| cleanupLineWrapping(); | |
| } catch (e: unknown) { | |
| debugLogger.error('Error restoring line wrapping:', e); | |
| } | |
| } | |
| } | |
| } | |
| function setWindowTitle(title: string, settings: LoadedSettings) { | |
| if (!settings.merged.ui.hideWindowTitle) { | |
| // Initial state before React loop starts | |
| const windowTitle = computeTerminalTitle({ | |
| streamingState: StreamingState.Idle, | |
| isConfirming: false, | |
| isSilentWorking: false, | |
| folderName: title, | |
| showThoughts: !!settings.merged.ui.showStatusInTitle, | |
| useDynamicTitle: settings.merged.ui.dynamicWindowTitle, | |
| }); | |
| writeToStdout(`\x1b]0;${windowTitle}\x07`); | |
| process.on('exit', () => { | |
| writeToStdout(`\x1b]0;\x07`); | |
| }); | |
| } | |
| } | |