From f2204e5d37517387638a0caad0ca28164a7a1b74 Mon Sep 17 00:00:00 2001 From: tt-a1i Date: Sun, 23 Aug 2026 16:52:24 +0800 Subject: [PATCH] fix(ui): mute completed tool activity --- .../file-mutation-display/render.test.ts | 72 ++++++++++++++++++- extensions/file-mutation-display/render.ts | 8 ++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/extensions/file-mutation-display/render.test.ts b/extensions/file-mutation-display/render.test.ts index 846a0cf4..a780b01e 100644 --- a/extensions/file-mutation-display/render.test.ts +++ b/extensions/file-mutation-display/render.test.ts @@ -162,6 +162,7 @@ function renderCollapsed( result?: AgentToolResult, isError = false, width = 120, + renderTheme = theme, ) { const state = {}; const context = { @@ -178,14 +179,14 @@ function renderCollapsed( showImages: false, isError, }; - const call = definition.renderCall?.(args, theme, context); + const call = definition.renderCall?.(args, renderTheme, context); assert.ok(call); let resultComponent: Component | undefined; if (result) { resultComponent = definition.renderResult?.( result, { expanded: false, isPartial: false }, - theme, + renderTheme, { ...context, isPartial: false, isError }, ); } @@ -195,6 +196,21 @@ function renderCollapsed( ].filter((line) => line.trim().length > 0); } +function recordingTheme(calls: Array<[string, string]>) { + return new Proxy( + {}, + { + get: (_target, property) => + property === "fg" || property === "bg" + ? (color: string, text: string) => { + calls.push([color, text]); + return text; + } + : (text: string) => text, + }, + ) as Theme; +} + test("all activity tools render one semantic success row", () => { for (const fixture of fixtures) { const definition = withActivityRenderer(fixture.definition); @@ -207,6 +223,58 @@ test("all activity tools render one semantic success row", () => { } }); +test("completed targets are muted without weakening pending or failed targets", () => { + const definition = withActivityRenderer(createBashToolDefinition(cwd)); + const args = { command: "bun run check" }; + + const successColors: Array<[string, string]> = []; + renderCollapsed( + definition, + args, + { content: [{ type: "text", text: "ok" }], details: undefined }, + false, + 120, + recordingTheme(successColors), + ); + assert.ok( + successColors.some( + ([color, text]) => color === "muted" && text === args.command, + ), + ); + + const cases: Array<{ + label: string; + result?: AgentToolResult; + isError: boolean; + }> = [ + { label: "pending", isError: false }, + { + label: "failed", + result: { + content: [{ type: "text", text: "failed" }], + details: undefined, + }, + isError: true, + }, + ]; + for (const { label, result, isError } of cases) { + const calls: Array<[string, string]> = []; + renderCollapsed( + definition, + args, + result, + isError, + 120, + recordingTheme(calls), + ); + assert.equal( + calls.some(([color, text]) => color === "muted" && text === args.command), + false, + label, + ); + } +}); + test("wrapping changes only renderer slots and shell ownership", () => { for (const fixture of fixtures) { const native = fixture.definition; diff --git a/extensions/file-mutation-display/render.ts b/extensions/file-mutation-display/render.ts index fe9c1b07..47bbc0cd 100644 --- a/extensions/file-mutation-display/render.ts +++ b/extensions/file-mutation-display/render.ts @@ -248,7 +248,11 @@ function activityText( : row.verb ).padEnd(8); const verb = theme.fg( - state.status === "error" ? "error" : "toolTitle", + state.status === "error" + ? "error" + : state.status === "success" + ? "muted" + : "toolTitle", verbText, ); if (state.status === "pending") { @@ -274,7 +278,7 @@ function activityText( } if (elapsed) parts.push(theme.fg("dim", elapsed)); const detail = parts.join(theme.fg("dim", " ยท ")); - return `${theme.fg("dim", activityIcon(name))} ${verb} ${row.target}${detail ? ` ${detail}` : ""}`; + return `${theme.fg("dim", activityIcon(name))} ${verb} ${theme.fg("muted", row.target)}${detail ? ` ${detail}` : ""}`; } function activityComponent(