diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx
index 32d085ec5e..b253ff88b5 100644
--- a/packages/cli/src/ui/AppContainer.tsx
+++ b/packages/cli/src/ui/AppContainer.tsx
@@ -799,7 +799,7 @@ Logging in with Google... Please restart Gemini CLI to continue.
const [showIdeRestartPrompt, setShowIdeRestartPrompt] = useState(false);
const { isFolderTrustDialogOpen, handleFolderTrustSelect, isRestarting } =
- useFolderTrust(settings, config, setIsTrustedFolder);
+ useFolderTrust(settings, config, setIsTrustedFolder, refreshStatic);
const { needsRestart: ideNeedsRestart } = useIdeTrustListener();
const isInitialMount = useRef(true);
diff --git a/packages/cli/src/ui/components/AppHeader.tsx b/packages/cli/src/ui/components/AppHeader.tsx
index 685799111b..d13af344ad 100644
--- a/packages/cli/src/ui/components/AppHeader.tsx
+++ b/packages/cli/src/ui/components/AppHeader.tsx
@@ -18,15 +18,18 @@ interface AppHeaderProps {
export const AppHeader = ({ version }: AppHeaderProps) => {
const settings = useSettings();
const config = useConfig();
- const { nightly } = useUIState();
+ const { nightly, isFolderTrustDialogOpen } = useUIState();
+ const showTips =
+ !isFolderTrustDialogOpen &&
+ !settings.merged.ui?.hideTips &&
+ !config.getScreenReader();
+
return (
{!(settings.merged.ui?.hideBanner || config.getScreenReader()) && (
)}
- {!(settings.merged.ui?.hideTips || config.getScreenReader()) && (
-
- )}
+ {showTips && }
);
};
diff --git a/packages/cli/src/ui/hooks/useFolderTrust.test.ts b/packages/cli/src/ui/hooks/useFolderTrust.test.ts
index 99a7c3b05e..180593fa9f 100644
--- a/packages/cli/src/ui/hooks/useFolderTrust.test.ts
+++ b/packages/cli/src/ui/hooks/useFolderTrust.test.ts
@@ -27,12 +27,16 @@ describe('useFolderTrust', () => {
let loadTrustedFoldersSpy: vi.SpyInstance;
let isWorkspaceTrustedSpy: vi.SpyInstance;
let onTrustChange: (isTrusted: boolean | undefined) => void;
+ let refreshStatic: () => void;
beforeEach(() => {
mockSettings = {
merged: {
- folderTrustFeature: true,
- folderTrust: undefined,
+ security: {
+ folderTrust: {
+ enabled: true,
+ },
+ },
},
setValue: vi.fn(),
} as unknown as LoadedSettings;
@@ -49,6 +53,7 @@ describe('useFolderTrust', () => {
isWorkspaceTrustedSpy = vi.spyOn(trustedFolders, 'isWorkspaceTrusted');
(process.cwd as vi.Mock).mockReturnValue('/test/path');
onTrustChange = vi.fn();
+ refreshStatic = vi.fn();
});
afterEach(() => {
@@ -58,7 +63,7 @@ describe('useFolderTrust', () => {
it('should not open dialog when folder is already trusted', () => {
isWorkspaceTrustedSpy.mockReturnValue(true);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
expect(result.current.isFolderTrustDialogOpen).toBe(false);
expect(onTrustChange).toHaveBeenCalledWith(true);
@@ -67,7 +72,7 @@ describe('useFolderTrust', () => {
it('should not open dialog when folder is already untrusted', () => {
isWorkspaceTrustedSpy.mockReturnValue(false);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
expect(result.current.isFolderTrustDialogOpen).toBe(false);
expect(onTrustChange).toHaveBeenCalledWith(false);
@@ -76,7 +81,7 @@ describe('useFolderTrust', () => {
it('should open dialog when folder trust is undefined', () => {
isWorkspaceTrustedSpy.mockReturnValue(undefined);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
expect(result.current.isFolderTrustDialogOpen).toBe(true);
expect(onTrustChange).toHaveBeenCalledWith(undefined);
@@ -87,7 +92,7 @@ describe('useFolderTrust', () => {
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(true);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
isWorkspaceTrustedSpy.mockReturnValue(true);
@@ -109,7 +114,7 @@ describe('useFolderTrust', () => {
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(true);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
act(() => {
@@ -129,7 +134,7 @@ describe('useFolderTrust', () => {
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(false);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
act(() => {
@@ -148,7 +153,7 @@ describe('useFolderTrust', () => {
it('should do nothing for default choice', () => {
isWorkspaceTrustedSpy.mockReturnValue(undefined);
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
act(() => {
@@ -166,7 +171,7 @@ describe('useFolderTrust', () => {
it('should set isRestarting to true when trust status changes from false to true', () => {
isWorkspaceTrustedSpy.mockReturnValueOnce(false).mockReturnValueOnce(true); // Initially untrusted, then trusted
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
act(() => {
@@ -182,7 +187,7 @@ describe('useFolderTrust', () => {
.mockReturnValueOnce(undefined)
.mockReturnValueOnce(true); // Initially undefined, then trust
const { result } = renderHook(() =>
- useFolderTrust(mockSettings, mockConfig, onTrustChange),
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
);
act(() => {
@@ -192,4 +197,26 @@ describe('useFolderTrust', () => {
expect(result.current.isRestarting).toBe(false);
expect(result.current.isFolderTrustDialogOpen).toBe(false); // Dialog should close
});
+
+ it('should call refreshStatic when dialog opens and closes', () => {
+ isWorkspaceTrustedSpy.mockReturnValue(undefined);
+ const { result } = renderHook(() =>
+ useFolderTrust(mockSettings, mockConfig, onTrustChange, refreshStatic),
+ );
+
+ // The hook runs, isFolderTrustDialogOpen becomes true, useEffect triggers.
+ // It's called once on mount, and once when the dialog state changes.
+ expect(refreshStatic).toHaveBeenCalledTimes(2);
+ expect(result.current.isFolderTrustDialogOpen).toBe(true);
+
+ // Now, simulate closing the dialog
+ isWorkspaceTrustedSpy.mockReturnValue(true); // So the state update works
+ act(() => {
+ result.current.handleFolderTrustSelect(FolderTrustChoice.TRUST_FOLDER);
+ });
+
+ // The state isFolderTrustDialogOpen becomes false, useEffect triggers again
+ expect(refreshStatic).toHaveBeenCalledTimes(3);
+ expect(result.current.isFolderTrustDialogOpen).toBe(false);
+ });
});
diff --git a/packages/cli/src/ui/hooks/useFolderTrust.ts b/packages/cli/src/ui/hooks/useFolderTrust.ts
index 1d1b6b8776..93e019ae20 100644
--- a/packages/cli/src/ui/hooks/useFolderTrust.ts
+++ b/packages/cli/src/ui/hooks/useFolderTrust.ts
@@ -19,6 +19,7 @@ export const useFolderTrust = (
settings: LoadedSettings,
config: Config,
onTrustChange: (isTrusted: boolean | undefined) => void,
+ refreshStatic: () => void,
) => {
const [isTrusted, setIsTrusted] = useState(undefined);
const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = useState(false);
@@ -33,6 +34,12 @@ export const useFolderTrust = (
onTrustChange(trusted);
}, [folderTrust, onTrustChange, settings.merged]);
+ useEffect(() => {
+ // When the folder trust dialog is about to open/close, we need to force a refresh
+ // of the static content to ensure the Tips are hidden/shown correctly.
+ refreshStatic();
+ }, [isFolderTrustDialogOpen, refreshStatic]);
+
const handleFolderTrustSelect = useCallback(
(choice: FolderTrustChoice) => {
const trustedFolders = loadTrustedFolders();