Fix flicker showing message to press ctrl-O again to collapse. (#20414)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Jacob Richman
2026-02-27 07:07:14 -08:00
committed by GitHub
parent 66b8922d66
commit 4d9cc36146
2 changed files with 25 additions and 72 deletions

View File

@@ -264,14 +264,16 @@ export const AppContainer = (props: AppContainerProps) => {
() => isWorkspaceTrusted(settings.merged).isTrusted,
);
const [queueErrorMessage, setQueueErrorMessage] = useState<string | null>(
null,
const [queueErrorMessage, setQueueErrorMessage] = useTimedMessage<string>(
QUEUE_ERROR_DISPLAY_DURATION_MS,
);
const [newAgents, setNewAgents] = useState<AgentDefinition[] | null>(null);
const [constrainHeight, setConstrainHeight] = useState<boolean>(true);
const [showIsExpandableHint, setShowIsExpandableHint] = useState(false);
const expandHintTimerRef = useRef<NodeJS.Timeout | null>(null);
const [expandHintTrigger, triggerExpandHint] = useTimedMessage<boolean>(
EXPAND_HINT_DURATION_MS,
);
const showIsExpandableHint = Boolean(expandHintTrigger);
const overflowState = useOverflowState();
const overflowingIdsSize = overflowState?.overflowingIds.size ?? 0;
const hasOverflowState = overflowingIdsSize > 0 || !constrainHeight;
@@ -284,39 +286,15 @@ export const AppContainer = (props: AppContainerProps) => {
* boolean dependency (hasOverflowState) to ensure the timer only resets on
* genuine state transitions, preventing it from infinitely resetting during
* active text streaming.
*
* In alternate buffer mode, we don't trigger the hint automatically on overflow
* to avoid noise, but the user can still trigger it manually with Ctrl+O.
*/
useEffect(() => {
if (isAlternateBuffer) {
setShowIsExpandableHint(false);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
return;
if (hasOverflowState && !isAlternateBuffer) {
triggerExpandHint(true);
}
if (hasOverflowState) {
setShowIsExpandableHint(true);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
expandHintTimerRef.current = setTimeout(() => {
setShowIsExpandableHint(false);
}, EXPAND_HINT_DURATION_MS);
}
}, [hasOverflowState, isAlternateBuffer, constrainHeight]);
/**
* Safe cleanup to ensure the expansion hint timer is cancelled when the
* component unmounts, preventing memory leaks.
*/
useEffect(
() => () => {
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
},
[],
);
}, [hasOverflowState, isAlternateBuffer, triggerExpandHint]);
const [defaultBannerText, setDefaultBannerText] = useState('');
const [warningBannerText, setWarningBannerText] = useState('');
@@ -1252,10 +1230,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
async (submittedValue: string) => {
reset();
// Explicitly hide the expansion hint and clear its x-second timer when a new turn begins.
setShowIsExpandableHint(false);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
triggerExpandHint(null);
if (!constrainHeight) {
setConstrainHeight(true);
if (!isAlternateBuffer) {
@@ -1327,16 +1302,14 @@ Logging in with Google... Restarting Gemini CLI to continue.
refreshStatic,
reset,
handleHintSubmit,
triggerExpandHint,
],
);
const handleClearScreen = useCallback(() => {
reset();
// Explicitly hide the expansion hint and clear its x-second timer when clearing the screen.
setShowIsExpandableHint(false);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
triggerExpandHint(null);
historyManager.clearItems();
clearConsoleMessagesState();
refreshStatic();
@@ -1345,7 +1318,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
clearConsoleMessagesState,
refreshStatic,
reset,
setShowIsExpandableHint,
triggerExpandHint,
]);
const { handleInput: vimHandleInput } = useVim(buffer, handleFinalSubmit);
@@ -1632,17 +1605,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
}
}, [ideNeedsRestart]);
useEffect(() => {
if (queueErrorMessage) {
const timer = setTimeout(() => {
setQueueErrorMessage(null);
}, QUEUE_ERROR_DISPLAY_DURATION_MS);
return () => clearTimeout(timer);
}
return undefined;
}, [queueErrorMessage, setQueueErrorMessage]);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
@@ -1748,13 +1710,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
setConstrainHeight(true);
if (keyMatchers[Command.SHOW_MORE_LINES](key)) {
// If the user manually collapses the view, show the hint and reset the x-second timer.
setShowIsExpandableHint(true);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
expandHintTimerRef.current = setTimeout(() => {
setShowIsExpandableHint(false);
}, EXPAND_HINT_DURATION_MS);
triggerExpandHint(true);
}
if (!isAlternateBuffer) {
refreshStatic();
@@ -1803,13 +1759,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
) {
setConstrainHeight(false);
// If the user manually expands the view, show the hint and reset the x-second timer.
setShowIsExpandableHint(true);
if (expandHintTimerRef.current) {
clearTimeout(expandHintTimerRef.current);
}
expandHintTimerRef.current = setTimeout(() => {
setShowIsExpandableHint(false);
}, EXPAND_HINT_DURATION_MS);
triggerExpandHint(true);
if (!isAlternateBuffer) {
refreshStatic();
}
@@ -1914,6 +1864,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
showTransientMessage,
settings.merged.general.devtools,
showErrorDetails,
triggerExpandHint,
],
);

View File

@@ -15,14 +15,16 @@ export function useTimedMessage<T>(durationMs: number) {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const showMessage = useCallback(
(msg: T) => {
(msg: T | null) => {
setMessage(msg);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setMessage(null);
}, durationMs);
if (msg !== null) {
timeoutRef.current = setTimeout(() => {
setMessage(null);
}, durationMs);
}
},
[durationMs],
);