@vybestack/llxprt-code 0.1.14-nightly.250729.2076f7c6 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +53 -0
  2. package/dist/package.json +4 -5
  3. package/dist/src/config/extension.d.ts +1 -0
  4. package/dist/src/config/extension.js +4 -0
  5. package/dist/src/config/extension.js.map +1 -1
  6. package/dist/src/config/settings.d.ts +2 -1
  7. package/dist/src/config/settings.js.map +1 -1
  8. package/dist/src/gemini.d.ts +1 -0
  9. package/dist/src/gemini.js +35 -15
  10. package/dist/src/gemini.js.map +1 -1
  11. package/dist/src/generated/git-commit.d.ts +1 -1
  12. package/dist/src/generated/git-commit.js +1 -1
  13. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  14. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  15. package/dist/src/services/CommandService.d.ts +8 -4
  16. package/dist/src/services/CommandService.js +24 -8
  17. package/dist/src/services/CommandService.js.map +1 -1
  18. package/dist/src/services/FileCommandLoader.d.ts +15 -3
  19. package/dist/src/services/FileCommandLoader.js +94 -42
  20. package/dist/src/services/FileCommandLoader.js.map +1 -1
  21. package/dist/src/services/McpPromptLoader.d.ts +25 -0
  22. package/dist/src/services/McpPromptLoader.js +192 -0
  23. package/dist/src/services/McpPromptLoader.js.map +1 -0
  24. package/dist/src/services/prompt-processors/shellProcessor.d.ts +32 -0
  25. package/dist/src/services/prompt-processors/shellProcessor.js +77 -0
  26. package/dist/src/services/prompt-processors/shellProcessor.js.map +1 -0
  27. package/dist/src/services/prompt-processors/types.d.ts +4 -0
  28. package/dist/src/services/prompt-processors/types.js +4 -0
  29. package/dist/src/services/prompt-processors/types.js.map +1 -1
  30. package/dist/src/ui/App.js +312 -195
  31. package/dist/src/ui/App.js.map +1 -1
  32. package/dist/src/ui/commands/chatCommand.js +39 -1
  33. package/dist/src/ui/commands/chatCommand.js.map +1 -1
  34. package/dist/src/ui/commands/initCommand.d.ts +7 -0
  35. package/dist/src/ui/commands/initCommand.js +76 -0
  36. package/dist/src/ui/commands/initCommand.js.map +1 -0
  37. package/dist/src/ui/commands/mcpCommand.js +53 -8
  38. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  39. package/dist/src/ui/commands/types.d.ts +22 -3
  40. package/dist/src/ui/commands/types.js +1 -0
  41. package/dist/src/ui/commands/types.js.map +1 -1
  42. package/dist/src/ui/commands/vimCommand.js +0 -7
  43. package/dist/src/ui/commands/vimCommand.js.map +1 -1
  44. package/dist/src/ui/components/ContextSummaryDisplay.d.ts +3 -3
  45. package/dist/src/ui/components/ContextSummaryDisplay.js +8 -8
  46. package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
  47. package/dist/src/ui/components/Footer.d.ts +1 -0
  48. package/dist/src/ui/components/Footer.js +2 -2
  49. package/dist/src/ui/components/Footer.js.map +1 -1
  50. package/dist/src/ui/components/Header.js +1 -1
  51. package/dist/src/ui/components/Header.js.map +1 -1
  52. package/dist/src/ui/components/Help.js +2 -2
  53. package/dist/src/ui/components/Help.js.map +1 -1
  54. package/dist/src/ui/components/IDEContextDetailDisplay.d.ts +4 -4
  55. package/dist/src/ui/components/IDEContextDetailDisplay.js +5 -7
  56. package/dist/src/ui/components/IDEContextDetailDisplay.js.map +1 -1
  57. package/dist/src/ui/components/InputPrompt.d.ts +2 -0
  58. package/dist/src/ui/components/InputPrompt.js +5 -1
  59. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  60. package/dist/src/ui/components/ShellConfirmationDialog.d.ts +15 -0
  61. package/dist/src/ui/components/ShellConfirmationDialog.js +45 -0
  62. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -0
  63. package/dist/src/ui/components/Tips.js +1 -1
  64. package/dist/src/ui/components/Tips.js.map +1 -1
  65. package/dist/src/ui/components/shared/text-buffer.d.ts +270 -2
  66. package/dist/src/ui/components/shared/text-buffer.js +415 -70
  67. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  68. package/dist/src/ui/components/shared/vim-buffer-actions.d.ts +72 -0
  69. package/dist/src/ui/components/shared/vim-buffer-actions.js +565 -0
  70. package/dist/src/ui/components/shared/vim-buffer-actions.js.map +1 -0
  71. package/dist/src/ui/contexts/VimModeContext.js +2 -2
  72. package/dist/src/ui/contexts/VimModeContext.js.map +1 -1
  73. package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -0
  74. package/dist/src/ui/hooks/shellCommandProcessor.js +139 -200
  75. package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
  76. package/dist/src/ui/hooks/slashCommandProcessor.d.ts +7 -3
  77. package/dist/src/ui/hooks/slashCommandProcessor.js +208 -129
  78. package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
  79. package/dist/src/ui/hooks/useCompletion.js +7 -2
  80. package/dist/src/ui/hooks/useCompletion.js.map +1 -1
  81. package/dist/src/ui/hooks/useConsoleMessages.js +53 -37
  82. package/dist/src/ui/hooks/useConsoleMessages.js.map +1 -1
  83. package/dist/src/ui/hooks/useGeminiStream.js +14 -2
  84. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  85. package/dist/src/ui/hooks/useKeypress.js +5 -2
  86. package/dist/src/ui/hooks/useKeypress.js.map +1 -1
  87. package/dist/src/ui/hooks/useReactToolScheduler.js +0 -1
  88. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  89. package/dist/src/ui/hooks/vim.d.ts +28 -0
  90. package/dist/src/ui/hooks/vim.js +630 -0
  91. package/dist/src/ui/hooks/vim.js.map +1 -0
  92. package/dist/src/ui/themes/theme-manager.js +10 -1
  93. package/dist/src/ui/themes/theme-manager.js.map +1 -1
  94. package/dist/src/ui/themes/theme.d.ts +1 -0
  95. package/dist/src/ui/themes/theme.js +19 -4
  96. package/dist/src/ui/themes/theme.js.map +1 -1
  97. package/dist/src/ui/utils/textUtils.d.ts +0 -8
  98. package/dist/src/ui/utils/textUtils.js +0 -22
  99. package/dist/src/ui/utils/textUtils.js.map +1 -1
  100. package/dist/src/ui/utils/updateCheck.d.ts +7 -1
  101. package/dist/src/ui/utils/updateCheck.js +17 -28
  102. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  103. package/dist/src/utils/events.d.ts +11 -0
  104. package/dist/src/utils/events.js +13 -0
  105. package/dist/src/utils/events.js.map +1 -0
  106. package/dist/src/utils/handleAutoUpdate.d.ts +10 -0
  107. package/dist/src/utils/handleAutoUpdate.js +97 -0
  108. package/dist/src/utils/handleAutoUpdate.js.map +1 -0
  109. package/dist/src/utils/installationInfo.d.ts +23 -0
  110. package/dist/src/utils/installationInfo.js +154 -0
  111. package/dist/src/utils/installationInfo.js.map +1 -0
  112. package/dist/src/utils/updateEventEmitter.d.ts +11 -0
  113. package/dist/src/utils/updateEventEmitter.js +12 -0
  114. package/dist/src/utils/updateEventEmitter.js.map +1 -0
  115. package/dist/tsconfig.tsbuildinfo +1 -1
  116. package/package.json +4 -5
@@ -4,18 +4,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  * Copyright 2025 Google LLC
5
5
  * SPDX-License-Identifier: Apache-2.0
6
6
  */
7
- import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
8
- import { Box, Static, Text, useStdin, useStdout, useInput, } from 'ink';
9
- import { StreamingState } from './types.js';
7
+ import { useCallback, useEffect, useMemo, useState, useRef, useReducer, } from 'react';
8
+ import { Box, measureElement, Static, Text, useStdin, useStdout, useInput, } from 'ink';
9
+ import { StreamingState, MessageType } from './types.js';
10
+ import { useTerminalSize } from './hooks/useTerminalSize.js';
10
11
  import { useGeminiStream } from './hooks/useGeminiStream.js';
11
12
  import { useLoadingIndicator } from './hooks/useLoadingIndicator.js';
12
13
  import { useThemeCommand } from './hooks/useThemeCommand.js';
13
14
  import { useAuthCommand } from './hooks/useAuthCommand.js';
14
15
  import { useEditorSettings } from './hooks/useEditorSettings.js';
15
- import { useProviderModelDialog } from './hooks/useProviderModelDialog.js';
16
- import { useProviderDialog } from './hooks/useProviderDialog.js';
17
- import { ProviderModelDialog } from './components/ProviderModelDialog.js';
18
- import { ProviderDialog } from './components/ProviderDialog.js';
19
16
  import { useSlashCommandProcessor } from './hooks/slashCommandProcessor.js';
20
17
  import { useAutoAcceptIndicator } from './hooks/useAutoAcceptIndicator.js';
21
18
  import { useConsoleMessages } from './hooks/useConsoleMessages.js';
@@ -29,8 +26,10 @@ import { ThemeDialog } from './components/ThemeDialog.js';
29
26
  import { AuthDialog } from './components/AuthDialog.js';
30
27
  import { AuthInProgress } from './components/AuthInProgress.js';
31
28
  import { EditorSettingsDialog } from './components/EditorSettingsDialog.js';
29
+ import { ShellConfirmationDialog } from './components/ShellConfirmationDialog.js';
32
30
  import { Colors } from './colors.js';
33
31
  import { Help } from './components/Help.js';
32
+ import { loadHierarchicalLlxprtMemory } from '../config/config.js';
34
33
  import { Tips } from './components/Tips.js';
35
34
  import { ConsolePatcher } from './utils/ConsolePatcher.js';
36
35
  import { registerCleanup } from '../utils/cleanup.js';
@@ -38,9 +37,9 @@ import { DetailedMessagesDisplay } from './components/DetailedMessagesDisplay.js
38
37
  import { HistoryItemDisplay } from './components/HistoryItemDisplay.js';
39
38
  import { ContextSummaryDisplay } from './components/ContextSummaryDisplay.js';
40
39
  import { IDEContextDetailDisplay } from './components/IDEContextDetailDisplay.js';
41
- // useHistory is now managed by SessionController
40
+ import { useHistory } from './hooks/useHistoryManager.js';
42
41
  import process from 'node:process';
43
- import { getAllLlxprtMdFilenames, ApprovalMode, isEditorAvailable, ideContext, } from '@vybestack/llxprt-code-core';
42
+ import { getErrorMessage, getAllLlxprtMdFilenames, ApprovalMode, isEditorAvailable, FlashFallbackEvent, logFlashFallback, AuthType, ideContext, } from '@vybestack/llxprt-code-core';
44
43
  import { validateAuthMethod } from '../config/auth.js';
45
44
  import { useLogger } from './hooks/useLogger.js';
46
45
  import { StreamingContext } from './contexts/StreamingContext.js';
@@ -49,32 +48,43 @@ import { useGitBranchName } from './hooks/useGitBranchName.js';
49
48
  import { useFocus } from './hooks/useFocus.js';
50
49
  import { useBracketedPaste } from './hooks/useBracketedPaste.js';
51
50
  import { useTextBuffer } from './components/shared/text-buffer.js';
51
+ import { useVimMode, VimModeProvider } from './contexts/VimModeContext.js';
52
+ import { useVim } from './hooks/vim.js';
52
53
  import * as fs from 'fs';
54
+ import { appReducer, initialAppState, } from './reducers/appReducer.js';
55
+ import { AppDispatchProvider } from './contexts/AppDispatchContext.js';
53
56
  import { UpdateNotification } from './components/UpdateNotification.js';
54
- // Quota error functions moved to SessionController
55
- import { checkForUpdates } from './utils/updateCheck.js';
57
+ import { isProQuotaExceededError, isGenericQuotaExceededError, UserTierId, } from '@vybestack/llxprt-code-core';
56
58
  import ansiEscapes from 'ansi-escapes';
57
59
  import { OverflowProvider } from './contexts/OverflowContext.js';
58
60
  import { ShowMoreLines } from './components/ShowMoreLines.js';
59
61
  import { PrivacyNotice } from './privacy/PrivacyNotice.js';
62
+ import { setUpdateHandler } from '../utils/handleAutoUpdate.js';
63
+ import { appEvents, AppEvent } from '../utils/events.js';
60
64
  import { getProviderManager } from '../providers/providerManagerInstance.js';
61
- import { UIStateShell } from './containers/UIStateShell.js';
62
- import { useLayout } from './components/LayoutManager.js';
63
- import { SessionController } from './containers/SessionController.js';
64
- import { useSession } from './hooks/useSession.js';
65
+ import { useProviderModelDialog } from './hooks/useProviderModelDialog.js';
66
+ import { useProviderDialog } from './hooks/useProviderDialog.js';
67
+ import { ProviderModelDialog } from './components/ProviderModelDialog.js';
68
+ import { ProviderDialog } from './components/ProviderDialog.js';
65
69
  const CTRL_EXIT_PROMPT_DURATION_MS = 1000;
66
- export const AppWrapper = (props) => (_jsx(SessionStatsProvider, { children: _jsx(App, { ...props }) }));
67
- // Inner component that uses layout context
68
- const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthenticating, }) => {
70
+ export const AppWrapper = (props) => (_jsx(SessionStatsProvider, { children: _jsx(VimModeProvider, { settings: props.settings, children: _jsx(AppWithState, { ...props }) }) }));
71
+ // New intermediate component that manages state and provides context
72
+ const AppWithState = (props) => {
73
+ const [appState, appDispatch] = useReducer(appReducer, initialAppState);
74
+ return (_jsx(AppDispatchProvider, { value: appDispatch, children: _jsx(App, { ...props, appState: appState, appDispatch: appDispatch }) }));
75
+ };
76
+ const App = (props) => {
77
+ const { config, settings, startupWarnings = [], version, appState } = props;
69
78
  const isFocused = useFocus();
70
79
  useBracketedPaste();
71
- const [updateMessage, setUpdateMessage] = useState(null);
80
+ const [updateInfo, setUpdateInfo] = useState(null);
72
81
  const { stdout } = useStdout();
73
82
  const nightly = version.includes('nightly');
83
+ const { history, addItem, clearItems, loadHistory } = useHistory();
74
84
  useEffect(() => {
75
- checkForUpdates().then(setUpdateMessage);
76
- }, []);
77
- const { history, addItem, clearItems, loadHistory, sessionState, dispatch: sessionDispatch, appState, appDispatch, checkPaymentModeChange, performMemoryRefresh, } = useSession();
85
+ const cleanup = setUpdateHandler(addItem, setUpdateInfo);
86
+ return cleanup;
87
+ }, [addItem]);
78
88
  const { consoleMessages, handleNewMessage, clearConsoleMessages: clearConsoleMessagesState, } = useConsoleMessages();
79
89
  useEffect(() => {
80
90
  const consolePatcher = new ConsolePatcher({
@@ -85,37 +95,6 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
85
95
  registerCleanup(consolePatcher.cleanup);
86
96
  }, [handleNewMessage, config]);
87
97
  const { stats: sessionStats } = useSessionStats();
88
- // These are now managed by SessionController
89
- const { currentModel, isPaidMode, transientWarnings: sessionTransientWarnings, modelSwitchedFromQuotaError, } = sessionState;
90
- // Add payment mode warning to startup warnings only at startup
91
- const allStartupWarnings = useMemo(() => {
92
- const warnings = [...startupWarnings];
93
- // Only show payment warnings at startup (when history is empty)
94
- if (history.length === 0) {
95
- try {
96
- const providerManager = getProviderManager();
97
- if (providerManager.hasActiveProvider()) {
98
- const provider = providerManager.getActiveProvider();
99
- const isPaidMode = provider.isPaidMode?.();
100
- // Only show paid/free mode warnings for Gemini provider
101
- if (isPaidMode !== undefined && provider.name === 'gemini') {
102
- if (isPaidMode) {
103
- warnings.push(`! PAID MODE: You are using Gemini with API credentials - usage will be charged to your account`);
104
- }
105
- else {
106
- warnings.push(`FREE MODE: You are using Gemini with OAuth authentication - no charges will apply`);
107
- }
108
- }
109
- }
110
- }
111
- catch (_e) {
112
- // Ignore errors when checking payment mode
113
- }
114
- }
115
- return warnings;
116
- }, [startupWarnings, history]);
117
- // Use transient warnings from session state
118
- const transientWarnings = sessionTransientWarnings;
119
98
  const [staticNeedsRefresh, setStaticNeedsRefresh] = useState(false);
120
99
  const [staticKey, setStaticKey] = useState(0);
121
100
  const refreshStatic = useCallback(() => {
@@ -125,6 +104,12 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
125
104
  const [llxprtMdFileCount, setLlxprtMdFileCount] = useState(0);
126
105
  const [debugMessage, setDebugMessage] = useState('');
127
106
  const [showHelp, setShowHelp] = useState(false);
107
+ const [_themeError, _setThemeError] = useState(null);
108
+ const [authError, setAuthError] = useState(null);
109
+ const [_editorError, _setEditorError] = useState(null);
110
+ const [footerHeight, setFooterHeight] = useState(0);
111
+ const [_corgiMode, setCorgiMode] = useState(false);
112
+ const [currentModel, setCurrentModel] = useState(config.getModel());
128
113
  const [shellModeActive, setShellModeActive] = useState(false);
129
114
  const [showErrorDetails, setShowErrorDetails] = useState(false);
130
115
  const [showToolDescriptions, setShowToolDescriptions] = useState(false);
@@ -134,92 +119,227 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
134
119
  const ctrlCTimerRef = useRef(null);
135
120
  const [ctrlDPressedOnce, setCtrlDPressedOnce] = useState(false);
136
121
  const ctrlDTimerRef = useRef(null);
137
- const showPrivacyNotice = appState.openDialogs.privacy;
138
- // modelSwitchedFromQuotaError and userTier are now in sessionState
139
- const [_activeFile, _setActiveFile] = useState();
140
- const [openFiles, setOpenFiles] = useState();
122
+ const [constrainHeight, setConstrainHeight] = useState(true);
123
+ const [showPrivacyNotice, setShowPrivacyNotice] = useState(false);
124
+ const [modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError] = useState(false);
125
+ const [userTier, setUserTier] = useState(undefined);
126
+ const [ideContextState, setIdeContextState] = useState();
127
+ const [isProcessing, setIsProcessing] = useState(false);
128
+ const [providerModels, setProviderModels] = useState([]);
141
129
  useEffect(() => {
142
- const unsubscribe = ideContext.subscribeToOpenFiles(setOpenFiles);
130
+ const unsubscribe = ideContext.subscribeToIdeContext(setIdeContextState);
143
131
  // Set the initial value
144
- setOpenFiles(ideContext.getOpenFilesContext());
132
+ setIdeContextState(ideContext.getIdeContext());
145
133
  return unsubscribe;
146
134
  }, []);
135
+ useEffect(() => {
136
+ const openDebugConsole = () => {
137
+ setShowErrorDetails(true);
138
+ setConstrainHeight(false); // Make sure the user sees the full message.
139
+ };
140
+ appEvents.on(AppEvent.OpenDebugConsole, openDebugConsole);
141
+ const logErrorHandler = (errorMessage) => {
142
+ handleNewMessage({
143
+ type: 'error',
144
+ content: String(errorMessage),
145
+ count: 1,
146
+ });
147
+ };
148
+ appEvents.on(AppEvent.LogError, logErrorHandler);
149
+ return () => {
150
+ appEvents.off(AppEvent.OpenDebugConsole, openDebugConsole);
151
+ appEvents.off(AppEvent.LogError, logErrorHandler);
152
+ };
153
+ }, [handleNewMessage]);
147
154
  const openPrivacyNotice = useCallback(() => {
148
- appDispatch({ type: 'OPEN_DIALOG', payload: 'privacy' });
149
- }, [appDispatch]);
150
- const closePrivacyNotice = useCallback(() => {
151
- appDispatch({ type: 'CLOSE_DIALOG', payload: 'privacy' });
152
- }, [appDispatch]);
155
+ setShowPrivacyNotice(true);
156
+ }, []);
153
157
  const initialPromptSubmitted = useRef(false);
154
- const errorCount = useMemo(() => consoleMessages.filter((msg) => msg.type === 'error').length, [consoleMessages]);
155
- // Create dispatch-based wrapper for addItem
156
- const addItemViaDispatch = useCallback((itemData, baseTimestamp) => {
157
- appDispatch({
158
- type: 'ADD_ITEM',
159
- payload: { itemData, baseTimestamp },
160
- });
161
- }, [appDispatch]);
162
- const { isThemeDialogOpen, openThemeDialog, handleThemeSelect, handleThemeHighlight, } = useThemeCommand(settings, appState, addItemViaDispatch);
163
- const { isAuthDialogOpen, openAuthDialog, handleAuthSelect, isAuthenticating: authIsAuthenticating, cancelAuthentication, } = useAuthCommand(settings, appState, config);
164
- // Sync auth state with parent
165
- useEffect(() => {
166
- setIsAuthenticating(authIsAuthenticating);
167
- }, [authIsAuthenticating, setIsAuthenticating]);
168
- const onAuthTimeout = useCallback(() => {
169
- appDispatch({
170
- type: 'SET_AUTH_ERROR',
171
- payload: 'Authentication timed out. Please try again.',
172
- });
173
- cancelAuthentication();
174
- openAuthDialog();
175
- }, [cancelAuthentication, openAuthDialog, appDispatch]);
158
+ const errorCount = useMemo(() => consoleMessages
159
+ .filter((msg) => msg.type === 'error')
160
+ .reduce((total, msg) => total + msg.count, 0), [consoleMessages]);
161
+ const { isThemeDialogOpen, openThemeDialog, handleThemeSelect, handleThemeHighlight, } = useThemeCommand(settings, appState, addItem);
162
+ const { isAuthDialogOpen, openAuthDialog, handleAuthSelect, isAuthenticating, cancelAuthentication, } = useAuthCommand(settings, appState, config);
176
163
  useEffect(() => {
177
164
  if (settings.merged.selectedAuthType) {
178
165
  const error = validateAuthMethod(settings.merged.selectedAuthType);
179
166
  if (error) {
180
- appDispatch({ type: 'SET_AUTH_ERROR', payload: error });
167
+ setAuthError(error);
181
168
  openAuthDialog();
182
169
  }
183
170
  }
184
- }, [settings.merged.selectedAuthType, openAuthDialog, appDispatch]);
185
- // User tier sync is now handled by SessionController
186
- const { isEditorDialogOpen, openEditorDialog, handleEditorSelect, exitEditorDialog, } = useEditorSettings(settings, appState, addItemViaDispatch);
187
- const providerModelDialog = useProviderModelDialog({
188
- addMessage: (m) => addItemViaDispatch({ type: m.type, text: m.content }, m.timestamp.getTime()),
189
- onModelChange: () => {
190
- // Model change detection is handled by SessionController's useEffect
191
- // No need to manually update here
192
- },
171
+ }, [settings.merged.selectedAuthType, openAuthDialog, setAuthError]);
172
+ // Sync user tier from config when authentication changes
173
+ useEffect(() => {
174
+ // Only sync when not currently authenticating
175
+ if (!isAuthenticating) {
176
+ setUserTier(config.getGeminiClient()?.getUserTier());
177
+ }
178
+ }, [config, isAuthenticating]);
179
+ const { isEditorDialogOpen, openEditorDialog, handleEditorSelect, exitEditorDialog, } = useEditorSettings(settings, appState, addItem);
180
+ const providerManager = getProviderManager(config);
181
+ const { showDialog: isProviderDialogOpen, openDialog: openProviderDialog, handleSelect: handleProviderSelect, closeDialog: exitProviderDialog, } = useProviderDialog({
182
+ addMessage: (msg) => addItem({ type: msg.type, text: msg.content }, msg.timestamp.getTime()),
193
183
  appState,
184
+ config,
194
185
  });
195
- const handleClearScreen = useCallback(() => {
196
- clearItems();
197
- clearConsoleMessagesState();
198
- console.clear();
199
- refreshStatic();
200
- }, [clearItems, clearConsoleMessagesState, refreshStatic]);
201
- // Provider selection dialog
202
- const providerDialog = useProviderDialog({
203
- addMessage: (m) => addItemViaDispatch({ type: m.type, text: m.content }, m.timestamp.getTime()),
204
- onProviderChange: () => {
205
- // Provider change will be detected by SessionController's useEffect
206
- checkPaymentModeChange?.();
207
- },
186
+ const { showDialog: isProviderModelDialogOpen, openDialog: openProviderModelDialogRaw, handleSelect: handleProviderModelChange, closeDialog: exitProviderModelDialog, } = useProviderModelDialog({
187
+ addMessage: (msg) => addItem({ type: msg.type, text: msg.content }, msg.timestamp.getTime()),
208
188
  appState,
209
- config,
210
- onClear: handleClearScreen,
211
189
  });
212
- // checkPaymentModeChange is now provided by SessionController
213
- // performMemoryRefresh is now provided by SessionController
214
- // Model watching is now handled by SessionController
215
- // Flash fallback handler is now set up by SessionController
216
- const { handleSlashCommand, slashCommands, pendingHistoryItems: pendingSlashCommandHistoryItems, commandContext, } = useSlashCommandProcessor(config, settings, addItem, clearItems, loadHistory, refreshStatic, setShowHelp, setDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, providerDialog.openDialog, providerModelDialog.openDialog, performMemoryRefresh, setQuittingMessages, openPrivacyNotice, checkPaymentModeChange, showToolDescriptions);
217
- // FIX: Initialize as empty array, will be combined with pendingGeminiHistoryItems later
218
- // This prevents mutations during render
219
- let pendingHistoryItems = [...pendingSlashCommandHistoryItems];
220
- const { terminalHeight, terminalWidth, constrainHeight, availableTerminalHeight, setConstrainHeight, footerRef, registerFooterDependency, } = useLayout();
221
- const isInitialMount = useRef(true);
190
+ const openProviderModelDialog = useCallback(async () => {
191
+ try {
192
+ const activeProvider = providerManager.getActiveProvider();
193
+ if (activeProvider) {
194
+ const models = await activeProvider.getModels();
195
+ setProviderModels(models);
196
+ }
197
+ }
198
+ catch (e) {
199
+ console.error('Failed to load models:', e);
200
+ setProviderModels([]);
201
+ }
202
+ await openProviderModelDialogRaw();
203
+ }, [providerManager, openProviderModelDialogRaw]);
204
+ // Update current model when provider or model changes
205
+ useEffect(() => {
206
+ const activeProvider = providerManager.getActiveProvider();
207
+ if (activeProvider) {
208
+ const providerModel = activeProvider.getCurrentModel?.();
209
+ if (providerModel && providerModel !== currentModel) {
210
+ setCurrentModel(providerModel);
211
+ }
212
+ }
213
+ }, [providerManager, currentModel]);
214
+ const toggleCorgiMode = useCallback(() => {
215
+ setCorgiMode((prev) => !prev);
216
+ }, []);
217
+ const performMemoryRefresh = useCallback(async () => {
218
+ addItem({
219
+ type: MessageType.INFO,
220
+ text: 'Refreshing hierarchical memory (LLXPRT.md or other context files)...',
221
+ }, Date.now());
222
+ try {
223
+ const { memoryContent, fileCount } = await loadHierarchicalLlxprtMemory(process.cwd(), config.getDebugMode(), config.getFileService(), settings.merged, config.getExtensionContextFilePaths(), config.getFileFilteringOptions());
224
+ config.setUserMemory(memoryContent);
225
+ config.setLlxprtMdFileCount(fileCount);
226
+ setLlxprtMdFileCount(fileCount);
227
+ addItem({
228
+ type: MessageType.INFO,
229
+ text: `Memory refreshed successfully. ${memoryContent.length > 0 ? `Loaded ${memoryContent.length} characters from ${fileCount} file(s).` : 'No memory content found.'}`,
230
+ }, Date.now());
231
+ if (config.getDebugMode()) {
232
+ console.log(`[DEBUG] Refreshed memory content in config: ${memoryContent.substring(0, 200)}...`);
233
+ }
234
+ }
235
+ catch (error) {
236
+ const errorMessage = getErrorMessage(error);
237
+ addItem({
238
+ type: MessageType.ERROR,
239
+ text: `Error refreshing memory: ${errorMessage}`,
240
+ }, Date.now());
241
+ console.error('Error refreshing memory:', error);
242
+ }
243
+ }, [config, addItem, settings.merged]);
244
+ // Watch for model changes (e.g., from Flash fallback)
245
+ useEffect(() => {
246
+ const checkModelChange = () => {
247
+ const configModel = config.getModel();
248
+ if (configModel !== currentModel) {
249
+ setCurrentModel(configModel);
250
+ }
251
+ };
252
+ // Check immediately and then periodically
253
+ checkModelChange();
254
+ const interval = setInterval(checkModelChange, 1000); // Check every second
255
+ return () => clearInterval(interval);
256
+ }, [config, currentModel]);
257
+ // Set up Flash fallback handler
258
+ useEffect(() => {
259
+ const flashFallbackHandler = async (currentModel, fallbackModel, error) => {
260
+ let message;
261
+ const contentGenConfig = config.getContentGeneratorConfig();
262
+ const authType = contentGenConfig?.authType;
263
+ if (authType === AuthType.LOGIN_WITH_GOOGLE) {
264
+ // Use actual user tier if available; otherwise, default to FREE tier behavior (safe default)
265
+ const isPaidTier = userTier === UserTierId.LEGACY || userTier === UserTierId.STANDARD;
266
+ // Check if this is a Pro quota exceeded error
267
+ if (error && isProQuotaExceededError(error)) {
268
+ if (isPaidTier) {
269
+ message = `⚡ You have reached your daily ${currentModel} quota limit.
270
+ ⚡ Automatically switching from ${currentModel} to ${fallbackModel} for the remainder of this session.
271
+ ⚡ To continue accessing the ${currentModel} model today, consider using /auth to switch to using a paid API key from AI Studio at https://aistudio.google.com/apikey`;
272
+ }
273
+ else {
274
+ message = `⚡ You have reached your daily ${currentModel} quota limit.
275
+ ⚡ Automatically switching from ${currentModel} to ${fallbackModel} for the remainder of this session.
276
+ ⚡ To increase your limits, upgrade to a Gemini Code Assist Standard or Enterprise plan with higher limits at https://goo.gle/set-up-gemini-code-assist
277
+ ⚡ Or you can utilize a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key
278
+ ⚡ You can switch authentication methods by typing /auth`;
279
+ }
280
+ }
281
+ else if (error && isGenericQuotaExceededError(error)) {
282
+ if (isPaidTier) {
283
+ message = `⚡ You have reached your daily quota limit.
284
+ ⚡ Automatically switching from ${currentModel} to ${fallbackModel} for the remainder of this session.
285
+ ⚡ To continue accessing the ${currentModel} model today, consider using /auth to switch to using a paid API key from AI Studio at https://aistudio.google.com/apikey`;
286
+ }
287
+ else {
288
+ message = `⚡ You have reached your daily quota limit.
289
+ ⚡ Automatically switching from ${currentModel} to ${fallbackModel} for the remainder of this session.
290
+ ⚡ To increase your limits, upgrade to a Gemini Code Assist Standard or Enterprise plan with higher limits at https://goo.gle/set-up-gemini-code-assist
291
+ ⚡ Or you can utilize a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key
292
+ ⚡ You can switch authentication methods by typing /auth`;
293
+ }
294
+ }
295
+ else {
296
+ if (isPaidTier) {
297
+ // Default fallback message for other cases (like consecutive 429s)
298
+ message = `⚡ Automatically switching from ${currentModel} to ${fallbackModel} for faster responses for the remainder of this session.
299
+ ⚡ Possible reasons for this are that you have received multiple consecutive capacity errors or you have reached your daily ${currentModel} quota limit
300
+ ⚡ To continue accessing the ${currentModel} model today, consider using /auth to switch to using a paid API key from AI Studio at https://aistudio.google.com/apikey`;
301
+ }
302
+ else {
303
+ // Default fallback message for other cases (like consecutive 429s)
304
+ message = `⚡ Automatically switching from ${currentModel} to ${fallbackModel} for faster responses for the remainder of this session.
305
+ ⚡ Possible reasons for this are that you have received multiple consecutive capacity errors or you have reached your daily ${currentModel} quota limit
306
+ ⚡ To increase your limits, upgrade to a Gemini Code Assist Standard or Enterprise plan with higher limits at https://goo.gle/set-up-gemini-code-assist
307
+ ⚡ Or you can utilize a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key
308
+ ⚡ You can switch authentication methods by typing /auth`;
309
+ }
310
+ }
311
+ // Add message to UI history
312
+ if (message) {
313
+ addItem({
314
+ type: MessageType.INFO,
315
+ text: message,
316
+ }, Date.now());
317
+ }
318
+ // Set the flag to prevent tool continuation
319
+ setModelSwitchedFromQuotaError(true);
320
+ // Set global quota error flag to prevent Flash model calls
321
+ config.setQuotaErrorOccurred(true);
322
+ }
323
+ // Switch model for future use but return false to stop current retry
324
+ if (fallbackModel) {
325
+ config.setModel(fallbackModel);
326
+ }
327
+ config.setFallbackMode(true);
328
+ const contentGenConfigForEvent = config.getContentGeneratorConfig();
329
+ const authTypeForEvent = contentGenConfigForEvent?.authType || AuthType.USE_GEMINI;
330
+ logFlashFallback(config, new FlashFallbackEvent(authTypeForEvent));
331
+ return false; // Don't continue with current prompt
332
+ };
333
+ config.setFlashFallbackHandler(flashFallbackHandler);
334
+ }, [config, addItem, userTier]);
335
+ // Terminal and UI setup
336
+ const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize();
222
337
  const { stdin, setRawMode } = useStdin();
338
+ const isInitialMount = useRef(true);
339
+ const widthFraction = 0.9;
340
+ const inputWidth = Math.max(20, Math.floor(terminalWidth * widthFraction) - 3);
341
+ const suggestionsWidth = Math.max(60, Math.floor(terminalWidth * 0.8));
342
+ // Utility callbacks
223
343
  const isValidPath = useCallback((filePath) => {
224
344
  try {
225
345
  return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
@@ -228,17 +348,51 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
228
348
  return false;
229
349
  }
230
350
  }, []);
231
- const widthFraction = 0.9;
232
- const inputWidth = Math.max(20, Math.floor(terminalWidth * widthFraction) - 3);
233
- const suggestionsWidth = Math.max(60, Math.floor(terminalWidth * 0.8));
351
+ const getPreferredEditor = useCallback(() => {
352
+ const editorType = settings.merged.preferredEditor;
353
+ const isValidEditor = isEditorAvailable(editorType);
354
+ if (!isValidEditor) {
355
+ openEditorDialog();
356
+ return;
357
+ }
358
+ return editorType;
359
+ }, [settings, openEditorDialog]);
360
+ const onAuthError = useCallback(() => {
361
+ setAuthError('reauth required');
362
+ openAuthDialog();
363
+ }, [openAuthDialog, setAuthError]);
364
+ const handleAuthTimeout = useCallback(() => {
365
+ setAuthError('Authentication timed out. Please try again.');
366
+ cancelAuthentication();
367
+ openAuthDialog();
368
+ }, [setAuthError, cancelAuthentication, openAuthDialog]);
369
+ const handlePrivacyNoticeExit = useCallback(() => {
370
+ setShowPrivacyNotice(false);
371
+ }, []);
372
+ // Core hooks and processors
373
+ const { vimEnabled: vimModeEnabled, vimMode, toggleVimEnabled, } = useVimMode();
374
+ const { handleSlashCommand, slashCommands, pendingHistoryItems: pendingSlashCommandHistoryItems, commandContext, shellConfirmationRequest, } = useSlashCommandProcessor(config, settings, addItem, clearItems, loadHistory, refreshStatic, setShowHelp, setDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, openProviderDialog, openProviderModelDialog, toggleCorgiMode, setQuittingMessages, openPrivacyNotice, toggleVimEnabled, setIsProcessing);
375
+ const { streamingState, submitQuery, initError, pendingHistoryItems: pendingGeminiHistoryItems, thought, } = useGeminiStream(config.getGeminiClient(), history, addItem, setShowHelp, config, setDebugMessage, handleSlashCommand, shellModeActive, getPreferredEditor, onAuthError, performMemoryRefresh, modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError);
376
+ // Input handling
377
+ const handleFinalSubmit = useCallback((submittedValue) => {
378
+ const trimmedValue = submittedValue.trim();
379
+ if (trimmedValue.length > 0) {
380
+ submitQuery(trimmedValue);
381
+ }
382
+ }, [submitQuery]);
234
383
  const buffer = useTextBuffer({
235
384
  initialText: '',
236
- viewport: useMemo(() => ({ height: 10, width: inputWidth }), [inputWidth]),
385
+ viewport: { height: 10, width: inputWidth },
237
386
  stdin,
238
387
  setRawMode,
239
388
  isValidPath,
240
389
  shellModeActive,
241
390
  });
391
+ const { handleInput: vimHandleInput } = useVim(buffer, handleFinalSubmit);
392
+ const pendingHistoryItems = [...pendingSlashCommandHistoryItems];
393
+ pendingHistoryItems.push(...pendingGeminiHistoryItems);
394
+ const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState);
395
+ const showAutoAcceptIndicator = useAutoAcceptIndicator({ config });
242
396
  const handleExit = useCallback((pressedOnce, setPressedOnce, timerRef) => {
243
397
  if (pressedOnce) {
244
398
  if (timerRef.current) {
@@ -276,7 +430,7 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
276
430
  handleSlashCommand(newValue ? '/mcp desc' : '/mcp nodesc');
277
431
  }
278
432
  }
279
- else if (key.ctrl && input === 'e' && ideContext) {
433
+ else if (key.ctrl && input === 'e' && ideContextState) {
280
434
  setShowIDEContextDetail((prev) => !prev);
281
435
  }
282
436
  else if (key.ctrl && (input === 'c' || input === 'C')) {
@@ -298,49 +452,6 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
298
452
  setLlxprtMdFileCount(config.getLlxprtMdFileCount());
299
453
  }
300
454
  }, [config]);
301
- const getPreferredEditor = useCallback(() => {
302
- const editorType = settings.merged.preferredEditor;
303
- const isValidEditor = isEditorAvailable(editorType);
304
- if (!isValidEditor) {
305
- openEditorDialog();
306
- return;
307
- }
308
- return editorType;
309
- }, [settings, openEditorDialog]);
310
- const onAuthError = useCallback(() => {
311
- appDispatch({ type: 'SET_AUTH_ERROR', payload: 'reauth required' });
312
- openAuthDialog();
313
- }, [openAuthDialog, appDispatch]);
314
- const geminiClientForStream = useMemo(() => config.getGeminiClient(), [config]);
315
- const { streamingState, submitQuery, initError, pendingHistoryItems: pendingGeminiHistoryItems, thought, } = useGeminiStream(geminiClientForStream, history, addItem, setShowHelp, config, setDebugMessage, handleSlashCommand, shellModeActive, getPreferredEditor, onAuthError, performMemoryRefresh, modelSwitchedFromQuotaError, useCallback((value) => {
316
- if (typeof value === 'function') {
317
- // Handle function form of setState
318
- const currentValue = modelSwitchedFromQuotaError;
319
- sessionDispatch({
320
- type: 'SET_MODEL_SWITCHED_FROM_QUOTA_ERROR',
321
- payload: value(currentValue),
322
- });
323
- }
324
- else {
325
- sessionDispatch({
326
- type: 'SET_MODEL_SWITCHED_FROM_QUOTA_ERROR',
327
- payload: value,
328
- });
329
- }
330
- }, [modelSwitchedFromQuotaError, sessionDispatch]));
331
- // FIX: Create a new array instead of mutating the existing one
332
- // This ensures React can properly track changes and prevents infinite loops
333
- pendingHistoryItems = [...pendingHistoryItems, ...pendingGeminiHistoryItems];
334
- const { elapsedTime, currentLoadingPhrase } = useLoadingIndicator(streamingState);
335
- const showAutoAcceptIndicator = useAutoAcceptIndicator({ config });
336
- const handleFinalSubmit = useCallback((submittedValue) => {
337
- const trimmedValue = submittedValue.trim();
338
- if (trimmedValue.length > 0) {
339
- // Clear transient warnings when user submits a message
340
- sessionDispatch({ type: 'CLEAR_TRANSIENT_WARNINGS' });
341
- submitQuery(trimmedValue);
342
- }
343
- }, [submitQuery, sessionDispatch]);
344
455
  const logger = useLogger();
345
456
  const [userMessages, setUserMessages] = useState([]);
346
457
  useEffect(() => {
@@ -372,12 +483,23 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
372
483
  };
373
484
  fetchUserMessages();
374
485
  }, [history, logger]);
375
- const isInputActive = streamingState === StreamingState.Idle && !initError;
486
+ const isInputActive = streamingState === StreamingState.Idle && !initError && !isProcessing;
487
+ const handleClearScreen = useCallback(() => {
488
+ clearItems();
489
+ clearConsoleMessagesState();
490
+ console.clear();
491
+ refreshStatic();
492
+ }, [clearItems, clearConsoleMessagesState, refreshStatic]);
493
+ const mainControlsRef = useRef(null);
376
494
  const pendingHistoryItemRef = useRef(null);
377
- // Register dependencies that affect footer height with LayoutManager
378
495
  useEffect(() => {
379
- registerFooterDependency();
380
- }, [consoleMessages, showErrorDetails, registerFooterDependency]);
496
+ if (mainControlsRef.current) {
497
+ const fullFooterMeasurement = measureElement(mainControlsRef.current);
498
+ setFooterHeight(fullFooterMeasurement.height);
499
+ }
500
+ }, [terminalHeight, consoleMessages, showErrorDetails]);
501
+ const staticExtraHeight = /* margins and padding */ 3;
502
+ const availableTerminalHeight = useMemo(() => terminalHeight - footerHeight - staticExtraHeight, [terminalHeight, footerHeight]);
381
503
  useEffect(() => {
382
504
  // skip refreshing Static during first mount
383
505
  if (isInitialMount.current) {
@@ -414,26 +536,30 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
414
536
  return getAllLlxprtMdFilenames();
415
537
  }, [settings.merged.contextFileName]);
416
538
  const initialPrompt = useMemo(() => config.getQuestion(), [config]);
417
- const geminiClient = useMemo(() => config.getGeminiClient(), [config]);
539
+ const geminiClient = config.getGeminiClient();
418
540
  useEffect(() => {
419
541
  if (initialPrompt &&
420
542
  !initialPromptSubmitted.current &&
421
- !authIsAuthenticating &&
543
+ !isAuthenticating &&
422
544
  !isAuthDialogOpen &&
423
545
  !isThemeDialogOpen &&
424
546
  !isEditorDialogOpen &&
547
+ !isProviderDialogOpen &&
548
+ !isProviderModelDialogOpen &&
425
549
  !showPrivacyNotice &&
426
- geminiClient) {
550
+ geminiClient?.isInitialized?.()) {
427
551
  submitQuery(initialPrompt);
428
552
  initialPromptSubmitted.current = true;
429
553
  }
430
554
  }, [
431
555
  initialPrompt,
432
556
  submitQuery,
433
- authIsAuthenticating,
557
+ isAuthenticating,
434
558
  isAuthDialogOpen,
435
559
  isThemeDialogOpen,
436
560
  isEditorDialogOpen,
561
+ isProviderDialogOpen,
562
+ isProviderModelDialogOpen,
437
563
  showPrivacyNotice,
438
564
  geminiClient,
439
565
  ]);
@@ -445,32 +571,23 @@ const AppInner = ({ config, settings, startupWarnings = [], version, setIsAuthen
445
571
  // Arbitrary threshold to ensure that items in the static area are large
446
572
  // enough but not too large to make the terminal hard to use.
447
573
  const staticAreaMaxItemHeight = Math.max(terminalHeight * 4, 100);
448
- // Show loading state if geminiClient is not initialized
449
- if (!geminiClientForStream) {
450
- console.log('App: geminiClientForStream is not initialized yet');
451
- return _jsx(Text, { children: "Initializing Gemini client..." });
452
- }
453
- return (_jsx(StreamingContext.Provider, { value: streamingState, children: _jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "90%", children: [updateMessage && _jsx(UpdateNotification, { message: updateMessage }), _jsx(Static, { items: [
574
+ const placeholder = vimModeEnabled
575
+ ? " Press 'i' for INSERT mode and 'Esc' for NORMAL mode."
576
+ : ' Type your message or @path/to/file';
577
+ return (_jsx(StreamingContext.Provider, { value: streamingState, children: _jsxs(Box, { flexDirection: "column", width: "90%", children: [_jsx(Static, { items: [
454
578
  _jsxs(Box, { flexDirection: "column", children: [!settings.merged.hideBanner && (_jsx(Header, { terminalWidth: terminalWidth, version: version, nightly: nightly })), !settings.merged.hideTips && _jsx(Tips, { config: config })] }, "header"),
455
579
  ...history.map((h) => (_jsx(HistoryItemDisplay, { terminalWidth: mainAreaWidth, availableTerminalHeight: staticAreaMaxItemHeight, item: h, isPending: false, config: config }, h.id))),
456
580
  ], children: (item) => item }, staticKey), _jsx(OverflowProvider, { children: _jsxs(Box, { ref: pendingHistoryItemRef, flexDirection: "column", children: [pendingHistoryItems.map((item, i) => (_jsx(HistoryItemDisplay, { availableTerminalHeight: constrainHeight ? availableTerminalHeight : undefined, terminalWidth: mainAreaWidth,
457
581
  // TODO(taehykim): It seems like references to ids aren't necessary in
458
582
  // HistoryItemDisplay. Refactor later. Use a fake id for now.
459
- item: { ...item, id: 0 }, isPending: true, config: config, isFocused: !isEditorDialogOpen }, i))), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }), showHelp && _jsx(Help, { commands: slashCommands }), _jsxs(Box, { flexDirection: "column", ref: footerRef, children: [(allStartupWarnings.length > 0 || transientWarnings.length > 0) && (_jsxs(Box, { borderStyle: "round", borderColor: Colors.AccentYellow, paddingX: 1, marginY: 1, flexDirection: "column", children: [allStartupWarnings.map((warning, index) => (_jsx(Text, { color: Colors.AccentYellow, children: warning }, index))), transientWarnings.map((warning, index) => (_jsx(Text, { color: Colors.AccentYellow, children: warning }, index)))] })), isThemeDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [appState.errors.theme && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: appState.errors.theme }) })), _jsx(ThemeDialog, { onSelect: handleThemeSelect, onHighlight: handleThemeHighlight, settings: settings, availableTerminalHeight: constrainHeight
460
- ? terminalHeight - 3 // margins and padding
461
- : undefined, terminalWidth: mainAreaWidth })] })) : authIsAuthenticating ? (_jsxs(_Fragment, { children: [_jsx(AuthInProgress, { onTimeout: onAuthTimeout }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }))] })) : isAuthDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AuthDialog, { onSelect: handleAuthSelect, settings: settings, initialErrorMessage: appState.errors.auth }) })) : isEditorDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [appState.errors.editor && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: appState.errors.editor }) })), _jsx(EditorSettingsDialog, { onSelect: handleEditorSelect, settings: settings, onExit: exitEditorDialog })] })) : providerModelDialog.showDialog ? (_jsx(Box, { flexDirection: "column", children: _jsx(ProviderModelDialog, { models: providerModelDialog.models, currentModel: providerModelDialog.currentModel, onSelect: providerModelDialog.handleSelect, onClose: providerModelDialog.closeDialog }) })) : providerDialog.showDialog ? (_jsx(Box, { flexDirection: "column", children: _jsx(ProviderDialog, { providers: providerDialog.providers, currentProvider: providerDialog.currentProvider, onSelect: providerDialog.handleSelect, onClose: providerDialog.closeDialog }) })) : showPrivacyNotice ? (_jsx(PrivacyNotice, { onExit: closePrivacyNotice, config: config })) : (_jsxs(_Fragment, { children: [_jsx(LoadingIndicator, { thought: streamingState === StreamingState.WaitingForConfirmation ||
583
+ item: { ...item, id: 0 }, isPending: true, config: config, isFocused: !isEditorDialogOpen }, i))), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }), showHelp && _jsx(Help, { commands: slashCommands }), _jsxs(Box, { flexDirection: "column", ref: mainControlsRef, children: [updateInfo && _jsx(UpdateNotification, { message: updateInfo.message }), startupWarnings.length > 0 && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentYellow, paddingX: 1, marginY: 1, flexDirection: "column", children: startupWarnings.map((warning, index) => (_jsx(Text, { color: Colors.AccentYellow, children: warning }, index))) })), shellConfirmationRequest ? (_jsx(ShellConfirmationDialog, { request: shellConfirmationRequest })) : isThemeDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [_themeError && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: _themeError }) })), _jsx(ThemeDialog, { onSelect: handleThemeSelect, onHighlight: handleThemeHighlight, settings: settings, availableTerminalHeight: constrainHeight
584
+ ? terminalHeight - staticExtraHeight
585
+ : undefined, terminalWidth: mainAreaWidth })] })) : isAuthenticating ? (_jsxs(_Fragment, { children: [_jsx(AuthInProgress, { onTimeout: handleAuthTimeout }), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) }))] })) : isAuthDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(AuthDialog, { onSelect: handleAuthSelect, settings: settings, initialErrorMessage: authError }) })) : isEditorDialogOpen ? (_jsxs(Box, { flexDirection: "column", children: [_editorError && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.AccentRed, children: _editorError }) })), _jsx(EditorSettingsDialog, { onSelect: handleEditorSelect, settings: settings, onExit: exitEditorDialog })] })) : isProviderDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(ProviderDialog, { providers: providerManager.listProviders(), currentProvider: providerManager.getActiveProviderName(), onSelect: handleProviderSelect, onClose: exitProviderDialog }) })) : isProviderModelDialogOpen ? (_jsx(Box, { flexDirection: "column", children: _jsx(ProviderModelDialog, { models: providerModels, currentModel: currentModel, onSelect: handleProviderModelChange, onClose: exitProviderModelDialog }) })) : showPrivacyNotice ? (_jsx(PrivacyNotice, { onExit: handlePrivacyNoticeExit, config: config })) : (_jsxs(_Fragment, { children: [_jsx(LoadingIndicator, { thought: streamingState === StreamingState.WaitingForConfirmation ||
462
586
  config.getAccessibility()?.disableLoadingPhrases
463
587
  ? undefined
464
588
  : thought, currentLoadingPhrase: config.getAccessibility()?.disableLoadingPhrases
465
589
  ? undefined
466
- : currentLoadingPhrase, elapsedTime: elapsedTime }), _jsxs(Box, { marginTop: 1, display: "flex", justifyContent: "space-between", width: "100%", children: [_jsxs(Box, { children: [process.env.GEMINI_SYSTEM_MD && (_jsx(Text, { color: Colors.AccentRed, children: "|\u2310\u25A0_\u25A0| " })), ctrlCPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+C again to exit." })) : ctrlDPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+D again to exit." })) : (_jsx(ContextSummaryDisplay, { activeFile: _activeFile, openFiles: openFiles, llxprtMdFileCount: llxprtMdFileCount, contextFileNames: contextFileNames, mcpServers: config.getMcpServers(), blockedMcpServers: config.getBlockedMcpServers(), showToolDescriptions: showToolDescriptions }))] }), _jsxs(Box, { children: [showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
467
- !shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showIDEContextDetail && (_jsx(IDEContextDetailDisplay, { openFiles: openFiles })), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, focus: isFocused }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), _jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() || config.getShowMemoryUsage(), promptTokenCount: sessionStats.lastPromptTokenCount, isPaidMode: isPaidMode, nightly: nightly })] })] }) }));
468
- };
469
- // Intermediate component to pass isAuthenticating to SessionController
470
- const AppWithAuth = (props) => {
471
- const [isAuthenticating, setIsAuthenticating] = useState(false);
472
- return (_jsx(SessionController, { config: props.config, isAuthenticating: isAuthenticating, children: _jsx(AppInner, { ...props, isAuthenticating: isAuthenticating, setIsAuthenticating: setIsAuthenticating }) }));
590
+ : currentLoadingPhrase, elapsedTime: elapsedTime }), _jsxs(Box, { marginTop: 1, display: "flex", justifyContent: "space-between", width: "100%", children: [_jsxs(Box, { children: [process.env.GEMINI_SYSTEM_MD && (_jsx(Text, { color: Colors.AccentRed, children: "|\u2310\u25A0_\u25A0| " })), ctrlCPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+C again to exit." })) : ctrlDPressedOnce ? (_jsx(Text, { color: Colors.AccentYellow, children: "Press Ctrl+D again to exit." })) : (_jsx(ContextSummaryDisplay, { ideContext: ideContextState, llxprtMdFileCount: llxprtMdFileCount, contextFileNames: contextFileNames, mcpServers: config.getMcpServers(), blockedMcpServers: config.getBlockedMcpServers(), showToolDescriptions: showToolDescriptions }))] }), _jsxs(Box, { children: [showAutoAcceptIndicator !== ApprovalMode.DEFAULT &&
591
+ !shellModeActive && (_jsx(AutoAcceptIndicator, { approvalMode: showAutoAcceptIndicator })), shellModeActive && _jsx(ShellModeIndicator, {})] })] }), showIDEContextDetail && (_jsx(IDEContextDetailDisplay, { ideContext: ideContextState })), showErrorDetails && (_jsx(OverflowProvider, { children: _jsxs(Box, { flexDirection: "column", children: [_jsx(DetailedMessagesDisplay, { messages: filteredConsoleMessages, maxHeight: constrainHeight ? debugConsoleMaxHeight : undefined, width: inputWidth }), _jsx(ShowMoreLines, { constrainHeight: constrainHeight })] }) })), isInputActive && (_jsx(InputPrompt, { buffer: buffer, inputWidth: inputWidth, suggestionsWidth: suggestionsWidth, onSubmit: handleFinalSubmit, userMessages: userMessages, onClearScreen: handleClearScreen, config: config, slashCommands: slashCommands, commandContext: commandContext, shellModeActive: shellModeActive, setShellModeActive: setShellModeActive, focus: isFocused, vimHandleInput: vimHandleInput, placeholder: placeholder }))] })), initError && streamingState !== StreamingState.Responding && (_jsx(Box, { borderStyle: "round", borderColor: Colors.AccentRed, paddingX: 1, marginBottom: 1, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text ? (_jsx(Text, { color: Colors.AccentRed, children: history.find((item) => item.type === 'error' && item.text?.includes(initError))?.text })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Initialization Error: ", initError] }), _jsxs(Text, { color: Colors.AccentRed, children: [' ', "Please check API key and configuration."] })] })) })), _jsx(Footer, { model: currentModel, targetDir: config.getTargetDir(), debugMode: config.getDebugMode(), branchName: branchName, debugMessage: debugMessage, errorCount: errorCount, showErrorDetails: showErrorDetails, showMemoryUsage: config.getDebugMode() || config.getShowMemoryUsage(), promptTokenCount: sessionStats.lastPromptTokenCount, nightly: nightly, vimMode: vimModeEnabled ? vimMode : undefined })] })] }) }));
473
592
  };
474
- // Main App component that provides the UIStateShell wrapper
475
- const App = (props) => (_jsx(UIStateShell, { children: _jsx(AppWithAuth, { ...props }) }));
476
593
  //# sourceMappingURL=App.js.map