Skip to content

Commit 885af07

Browse files
Lyonk71jacob314
andauthored
feat(ui): Introduce useUI Hook and UIContext (google-gemini#5488)
Co-authored-by: Jacob Richman <[email protected]>
1 parent fe15b04 commit 885af07

40 files changed

+3435
-3380
lines changed

docs/mermaid/context.mmd

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
graph LR
2+
%% --- Style Definitions ---
3+
classDef new fill:#98fb98,color:#000
4+
classDef changed fill:#add8e6,color:#000
5+
classDef unchanged fill:#f0f0f0,color:#000
6+
7+
%% --- Subgraphs ---
8+
subgraph "Context Providers"
9+
direction TB
10+
A["gemini.tsx"]
11+
B["AppContainer.tsx"]
12+
end
13+
14+
subgraph "Contexts"
15+
direction TB
16+
CtxSession["SessionContext"]
17+
CtxVim["VimModeContext"]
18+
CtxSettings["SettingsContext"]
19+
CtxApp["AppContext"]
20+
CtxConfig["ConfigContext"]
21+
CtxUIState["UIStateContext"]
22+
CtxUIActions["UIActionsContext"]
23+
end
24+
25+
subgraph "Component Consumers"
26+
direction TB
27+
ConsumerApp["App"]
28+
ConsumerAppContainer["AppContainer"]
29+
ConsumerAppHeader["AppHeader"]
30+
ConsumerDialogManager["DialogManager"]
31+
ConsumerHistoryItem["HistoryItemDisplay"]
32+
ConsumerComposer["Composer"]
33+
ConsumerMainContent["MainContent"]
34+
ConsumerNotifications["Notifications"]
35+
end
36+
37+
%% --- Provider -> Context Connections ---
38+
A -.-> CtxSession
39+
A -.-> CtxVim
40+
A -.-> CtxSettings
41+
42+
B -.-> CtxApp
43+
B -.-> CtxConfig
44+
B -.-> CtxUIState
45+
B -.-> CtxUIActions
46+
B -.-> CtxSettings
47+
48+
%% --- Context -> Consumer Connections ---
49+
CtxSession -.-> ConsumerAppContainer
50+
CtxSession -.-> ConsumerApp
51+
52+
CtxVim -.-> ConsumerAppContainer
53+
CtxVim -.-> ConsumerComposer
54+
CtxVim -.-> ConsumerApp
55+
56+
CtxSettings -.-> ConsumerAppContainer
57+
CtxSettings -.-> ConsumerAppHeader
58+
CtxSettings -.-> ConsumerDialogManager
59+
CtxSettings -.-> ConsumerApp
60+
61+
CtxApp -.-> ConsumerAppHeader
62+
CtxApp -.-> ConsumerNotifications
63+
64+
CtxConfig -.-> ConsumerAppHeader
65+
CtxConfig -.-> ConsumerHistoryItem
66+
CtxConfig -.-> ConsumerComposer
67+
CtxConfig -.-> ConsumerDialogManager
68+
69+
70+
71+
CtxUIState -.-> ConsumerApp
72+
CtxUIState -.-> ConsumerMainContent
73+
CtxUIState -.-> ConsumerComposer
74+
CtxUIState -.-> ConsumerDialogManager
75+
76+
CtxUIActions -.-> ConsumerComposer
77+
CtxUIActions -.-> ConsumerDialogManager
78+
79+
%% --- Apply Styles ---
80+
%% New Elements (Green)
81+
class B,CtxApp,CtxConfig,CtxUIState,CtxUIActions,ConsumerAppHeader,ConsumerDialogManager,ConsumerComposer,ConsumerMainContent,ConsumerNotifications new
82+
83+
%% Heavily Changed Elements (Blue)
84+
class A,ConsumerApp,ConsumerAppContainer,ConsumerHistoryItem changed
85+
86+
%% Mostly Unchanged Elements (Gray)
87+
class CtxSession,CtxVim,CtxSettings unchanged
88+
89+
%% --- Link Styles ---
90+
%% CtxSession (Red)
91+
linkStyle 0,8,9 stroke:#e57373,stroke-width:2px
92+
%% CtxVim (Orange)
93+
linkStyle 1,10,11,12 stroke:#ffb74d,stroke-width:2px
94+
%% CtxSettings (Yellow)
95+
linkStyle 2,7,13,14,15,16 stroke:#fff176,stroke-width:2px
96+
%% CtxApp (Green)
97+
linkStyle 3,17,18 stroke:#81c784,stroke-width:2px
98+
%% CtxConfig (Blue)
99+
linkStyle 4,19,20,21,22 stroke:#64b5f6,stroke-width:2px
100+
%% CtxUIState (Indigo)
101+
linkStyle 5,23,24,25,26 stroke:#7986cb,stroke-width:2px
102+
%% CtxUIActions (Violet)
103+
linkStyle 6,27,28 stroke:#ba68c8,stroke-width:2px

docs/mermaid/render-path.mmd

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
graph TD
2+
%% --- Style Definitions ---
3+
classDef new fill:#98fb98,color:#000
4+
classDef changed fill:#add8e6,color:#000
5+
classDef unchanged fill:#f0f0f0,color:#000
6+
classDef dispatcher fill:#f9e79f,color:#000,stroke:#333,stroke-width:1px
7+
classDef container fill:#f5f5f5,color:#000,stroke:#ccc
8+
9+
%% --- Component Tree ---
10+
subgraph "Entry Point"
11+
A["gemini.tsx"]
12+
end
13+
14+
subgraph "State & Logic Wrapper"
15+
B["AppContainer.tsx"]
16+
end
17+
18+
subgraph "Primary Layout"
19+
C["App.tsx"]
20+
end
21+
22+
A -.-> B
23+
B -.-> C
24+
25+
subgraph "UI Containers"
26+
direction LR
27+
C -.-> D["MainContent"]
28+
C -.-> G["Composer"]
29+
C -.-> F["DialogManager"]
30+
C -.-> E["Notifications"]
31+
end
32+
33+
subgraph "MainContent"
34+
direction TB
35+
D -.-> H["AppHeader"]
36+
D -.-> I["HistoryItemDisplay"]:::dispatcher
37+
D -.-> L["ShowMoreLines"]
38+
end
39+
40+
subgraph "Composer"
41+
direction TB
42+
G -.-> K_Prompt["InputPrompt"]
43+
G -.-> K_Footer["Footer"]
44+
end
45+
46+
subgraph "DialogManager"
47+
F -.-> J["Various Dialogs<br>(Auth, Theme, Settings, etc.)"]
48+
end
49+
50+
%% --- Apply Styles ---
51+
class B,D,E,F,G,H,J,K_Prompt,L new
52+
class A,C,I changed
53+
class K_Footer unchanged
54+
55+
%% --- Link Styles ---
56+
%% MainContent Branch (Blue)
57+
linkStyle 2,6,7,8 stroke:#64b5f6,stroke-width:2px
58+
%% Composer Branch (Green)
59+
linkStyle 3,9,10 stroke:#81c784,stroke-width:2px
60+
%% DialogManager Branch (Orange)
61+
linkStyle 4,11 stroke:#ffb74d,stroke-width:2px
62+
%% Notifications Branch (Violet)
63+
linkStyle 5 stroke:#ba68c8,stroke-width:2px
64+

package-lock.json

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/src/core/auth.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {
8+
type AuthType,
9+
type Config,
10+
getErrorMessage,
11+
} from '@google/gemini-cli-core';
12+
13+
/**
14+
* Handles the initial authentication flow.
15+
* @param config The application config.
16+
* @param authType The selected auth type.
17+
* @returns An error message if authentication fails, otherwise null.
18+
*/
19+
export async function performInitialAuth(
20+
config: Config,
21+
authType: AuthType | undefined,
22+
): Promise<string | null> {
23+
if (!authType) {
24+
return null;
25+
}
26+
27+
try {
28+
await config.refreshAuth(authType);
29+
// The console.log is intentionally left out here.
30+
// We can add a dedicated startup message later if needed.
31+
} catch (e) {
32+
return `Failed to login. Message: ${getErrorMessage(e)}`;
33+
}
34+
35+
return null;
36+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { type Config } from '@google/gemini-cli-core';
8+
import { type LoadedSettings } from '../config/settings.js';
9+
import { performInitialAuth } from './auth.js';
10+
import { validateTheme } from './theme.js';
11+
12+
export interface InitializationResult {
13+
authError: string | null;
14+
themeError: string | null;
15+
shouldOpenAuthDialog: boolean;
16+
geminiMdFileCount: number;
17+
}
18+
19+
/**
20+
* Orchestrates the application's startup initialization.
21+
* This runs BEFORE the React UI is rendered.
22+
* @param config The application config.
23+
* @param settings The loaded application settings.
24+
* @returns The results of the initialization.
25+
*/
26+
export async function initializeApp(
27+
config: Config,
28+
settings: LoadedSettings,
29+
): Promise<InitializationResult> {
30+
const authError = await performInitialAuth(
31+
config,
32+
settings.merged.security?.auth?.selectedType,
33+
);
34+
const themeError = validateTheme(settings);
35+
36+
const shouldOpenAuthDialog =
37+
settings.merged.security?.auth?.selectedType === undefined || !!authError;
38+
39+
return {
40+
authError,
41+
themeError,
42+
shouldOpenAuthDialog,
43+
geminiMdFileCount: config.getGeminiMdFileCount(),
44+
};
45+
}

packages/cli/src/core/theme.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { themeManager } from '../ui/themes/theme-manager.js';
8+
import { type LoadedSettings } from '../config/settings.js';
9+
10+
/**
11+
* Validates the configured theme.
12+
* @param settings The loaded application settings.
13+
* @returns An error message if the theme is not found, otherwise null.
14+
*/
15+
export function validateTheme(settings: LoadedSettings): string | null {
16+
const effectiveTheme = settings.merged.ui?.theme;
17+
if (effectiveTheme && !themeManager.findThemeByName(effectiveTheme)) {
18+
return `Theme "${effectiveTheme}" not found.`;
19+
}
20+
return null;
21+
}

packages/cli/src/gemini.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ describe('gemini.tsx main function kitty protocol', () => {
195195
getIdeMode: () => false,
196196
getExperimentalZedIntegration: () => false,
197197
getScreenReader: () => false,
198+
getGeminiMdFileCount: () => 0,
198199
} as unknown as Config);
199200
vi.mocked(loadSettings).mockReturnValue({
200201
errors: [],
@@ -323,11 +324,19 @@ describe('startInteractiveUI', () => {
323324
const { render } = await import('ink');
324325
const renderSpy = vi.mocked(render);
325326

327+
const mockInitializationResult = {
328+
authError: null,
329+
themeError: null,
330+
shouldOpenAuthDialog: false,
331+
geminiMdFileCount: 0,
332+
};
333+
326334
await startInteractiveUI(
327335
mockConfig,
328336
mockSettings,
329337
mockStartupWarnings,
330338
mockWorkspaceRoot,
339+
mockInitializationResult,
331340
);
332341

333342
// Verify render was called with correct options
@@ -349,11 +358,19 @@ describe('startInteractiveUI', () => {
349358
const { checkForUpdates } = await import('./ui/utils/updateCheck.js');
350359
const { registerCleanup } = await import('./utils/cleanup.js');
351360

361+
const mockInitializationResult = {
362+
authError: null,
363+
themeError: null,
364+
shouldOpenAuthDialog: false,
365+
geminiMdFileCount: 0,
366+
};
367+
352368
await startInteractiveUI(
353369
mockConfig,
354370
mockSettings,
355371
mockStartupWarnings,
356372
mockWorkspaceRoot,
373+
mockInitializationResult,
357374
);
358375

359376
// Verify all startup tasks were called

0 commit comments

Comments
 (0)