From 6db496b04bad671cb4decc90e00e1ebedc5824c0 Mon Sep 17 00:00:00 2001 From: ZengWen-DT Date: Sun, 14 Jun 2026 22:59:29 +0800 Subject: [PATCH] fix(tui): keep spinner active when toggling tools (#92909) * fix(tui): keep spinner active when toggling tools * fix(tui): preserve finishing status when toggling tools --------- Co-authored-by: zengwen Co-authored-by: Vincent Koc --- src/tui/tui.test.ts | 30 ++++++++++++++++++++++++++++++ src/tui/tui.ts | 20 +++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/tui/tui.test.ts b/src/tui/tui.test.ts index 1f07834c7f53..76f7ac6c00b6 100644 --- a/src/tui/tui.test.ts +++ b/src/tui/tui.test.ts @@ -18,6 +18,7 @@ import { resolveFinalAssistantText, resolveGatewayDisconnectState, resolveInitialTuiAgentId, + resolveTuiToolsToggleActivityStatus, isTuiBusyActivityStatus, resolveLocalAuthCliInvocation, resolveLocalAuthSpawnCwd, @@ -191,6 +192,35 @@ describe("isTuiBusyActivityStatus", () => { }); }); +describe("resolveTuiToolsToggleActivityStatus", () => { + it("preserves busy status while an active run exists", () => { + expect( + resolveTuiToolsToggleActivityStatus({ + currentStatus: "streaming", + toolsExpanded: true, + }), + ).toBe("streaming"); + }); + + it("preserves finishing context after the active run id clears", () => { + expect( + resolveTuiToolsToggleActivityStatus({ + currentStatus: "finishing context", + toolsExpanded: false, + }), + ).toBe("finishing context"); + }); + + it("uses the tool toggle status when activity is idle", () => { + expect( + resolveTuiToolsToggleActivityStatus({ + currentStatus: "idle", + toolsExpanded: false, + }), + ).toBe("tools collapsed"); + }); +}); + describe("resolveTuiShutdownHardExitMs", () => { it("keeps gateway shutdown bounded by the hard-exit timer", () => { expect(resolveTuiShutdownHardExitMs({ localMode: false })).toBe(2000); diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 31cce2298056..263dbf3326e7 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -411,6 +411,17 @@ export function isTuiBusyActivityStatus(status: string): boolean { return TUI_BUSY_ACTIVITY_STATUSES.has(status); } +export function resolveTuiToolsToggleActivityStatus(params: { + currentStatus: string; + toolsExpanded: boolean; +}): string { + const toolsStatus = params.toolsExpanded ? "tools expanded" : "tools collapsed"; + if (isTuiBusyActivityStatus(params.currentStatus)) { + return params.currentStatus; + } + return toolsStatus; +} + export function resolveTuiShutdownHardExitMs(params: { localMode?: boolean } = {}): number { return TUI_SHUTDOWN_HARD_EXIT_MS + (params.localMode ? resolveLocalRunShutdownGraceMs() : 0); } @@ -1460,7 +1471,14 @@ export async function runTui(opts: RunTuiOptions): Promise { editor.onCtrlO = () => { toolsExpanded = !toolsExpanded; chatLog.setToolsExpanded(toolsExpanded); - setActivityStatus(toolsExpanded ? "tools expanded" : "tools collapsed"); + // Ctrl+O is presentation-only; preserve busy activity so the status loader + // does not disappear before the run lifecycle ends. + setActivityStatus( + resolveTuiToolsToggleActivityStatus({ + currentStatus: activityStatus, + toolsExpanded, + }), + ); tui.requestRender(); }; editor.onCtrlL = () => {