mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-13 23:01:09 -07:00
refactor(cli): code review cleanup fix for tab+tab (#18967)
This commit is contained in:
69
packages/cli/src/ui/hooks/useRepeatedKeyPress.ts
Normal file
69
packages/cli/src/ui/hooks/useRepeatedKeyPress.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useRef, useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export interface UseRepeatedKeyPressOptions {
|
||||
onRepeat?: (count: number) => void;
|
||||
onReset?: () => void;
|
||||
windowMs: number;
|
||||
}
|
||||
|
||||
export function useRepeatedKeyPress(options: UseRepeatedKeyPressOptions) {
|
||||
const [pressCount, setPressCount] = useState(0);
|
||||
const pressCountRef = useRef(0);
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// To avoid stale closures
|
||||
const optionsRef = useRef(options);
|
||||
useEffect(() => {
|
||||
optionsRef.current = options;
|
||||
}, [options]);
|
||||
|
||||
const resetCount = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
timerRef.current = null;
|
||||
}
|
||||
if (pressCountRef.current > 0) {
|
||||
pressCountRef.current = 0;
|
||||
setPressCount(0);
|
||||
optionsRef.current.onReset?.();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handlePress = useCallback((): number => {
|
||||
const newCount = pressCountRef.current + 1;
|
||||
pressCountRef.current = newCount;
|
||||
setPressCount(newCount);
|
||||
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
pressCountRef.current = 0;
|
||||
setPressCount(0);
|
||||
timerRef.current = null;
|
||||
optionsRef.current.onReset?.();
|
||||
}, optionsRef.current.windowMs);
|
||||
|
||||
optionsRef.current.onRepeat?.(newCount);
|
||||
|
||||
return newCount;
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return { pressCount, handlePress, resetCount };
|
||||
}
|
||||
79
packages/cli/src/ui/hooks/useVisibilityToggle.ts
Normal file
79
packages/cli/src/ui/hooks/useVisibilityToggle.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { persistentState } from '../../utils/persistentState.js';
|
||||
|
||||
export const APPROVAL_MODE_REVEAL_DURATION_MS = 1200;
|
||||
const FOCUS_UI_ENABLED_STATE_KEY = 'focusUiEnabled';
|
||||
|
||||
export function useVisibilityToggle() {
|
||||
const [focusUiEnabledByDefault] = useState(
|
||||
() => persistentState.get(FOCUS_UI_ENABLED_STATE_KEY) === true,
|
||||
);
|
||||
const [cleanUiDetailsVisible, setCleanUiDetailsVisibleState] = useState(
|
||||
!focusUiEnabledByDefault,
|
||||
);
|
||||
const modeRevealTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const cleanUiDetailsPinnedRef = useRef(!focusUiEnabledByDefault);
|
||||
|
||||
const clearModeRevealTimeout = useCallback(() => {
|
||||
if (modeRevealTimeoutRef.current) {
|
||||
clearTimeout(modeRevealTimeoutRef.current);
|
||||
modeRevealTimeoutRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const persistFocusUiPreference = useCallback((isFullUiVisible: boolean) => {
|
||||
persistentState.set(FOCUS_UI_ENABLED_STATE_KEY, !isFullUiVisible);
|
||||
}, []);
|
||||
|
||||
const setCleanUiDetailsVisible = useCallback(
|
||||
(visible: boolean) => {
|
||||
clearModeRevealTimeout();
|
||||
cleanUiDetailsPinnedRef.current = visible;
|
||||
setCleanUiDetailsVisibleState(visible);
|
||||
persistFocusUiPreference(visible);
|
||||
},
|
||||
[clearModeRevealTimeout, persistFocusUiPreference],
|
||||
);
|
||||
|
||||
const toggleCleanUiDetailsVisible = useCallback(() => {
|
||||
clearModeRevealTimeout();
|
||||
setCleanUiDetailsVisibleState((visible) => {
|
||||
const nextVisible = !visible;
|
||||
cleanUiDetailsPinnedRef.current = nextVisible;
|
||||
persistFocusUiPreference(nextVisible);
|
||||
return nextVisible;
|
||||
});
|
||||
}, [clearModeRevealTimeout, persistFocusUiPreference]);
|
||||
|
||||
const revealCleanUiDetailsTemporarily = useCallback(
|
||||
(durationMs: number = APPROVAL_MODE_REVEAL_DURATION_MS) => {
|
||||
if (cleanUiDetailsPinnedRef.current) {
|
||||
return;
|
||||
}
|
||||
clearModeRevealTimeout();
|
||||
setCleanUiDetailsVisibleState(true);
|
||||
modeRevealTimeoutRef.current = setTimeout(() => {
|
||||
if (!cleanUiDetailsPinnedRef.current) {
|
||||
setCleanUiDetailsVisibleState(false);
|
||||
}
|
||||
modeRevealTimeoutRef.current = null;
|
||||
}, durationMs);
|
||||
},
|
||||
[clearModeRevealTimeout],
|
||||
);
|
||||
|
||||
useEffect(() => () => clearModeRevealTimeout(), [clearModeRevealTimeout]);
|
||||
|
||||
return {
|
||||
cleanUiDetailsVisible,
|
||||
setCleanUiDetailsVisible,
|
||||
toggleCleanUiDetailsVisible,
|
||||
revealCleanUiDetailsTemporarily,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user