From e8d767bd2ea9611ff7f68b74712fc335b68e3bbb Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 30 Aug 2026 19:12:19 -0700 Subject: [PATCH] refactor(ui): unify working indicator status rendering (#133713) --- .../chat/components/chat-message.test.ts | 11 +++- .../chat/components/chat-working-indicator.ts | 60 ++++++++----------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/ui/src/pages/chat/components/chat-message.test.ts b/ui/src/pages/chat/components/chat-message.test.ts index df0e7a87789..4fc307e818d 100644 --- a/ui/src/pages/chat/components/chat-message.test.ts +++ b/ui/src/pages/chat/components/chat-message.test.ts @@ -1788,6 +1788,7 @@ describe("grouped chat rendering", () => { ); expect(container.querySelector(".chat-working-indicator__elapsed")).not.toBeNull(); expect(container.querySelector(".chat-working-indicator__status > .sr-only")).toBeNull(); + expect(container.querySelector("openclaw-working-phrase")).toBeNull(); }); it("formats terminal recap durations with full localized units", () => { @@ -1815,19 +1816,23 @@ describe("grouped chat rendering", () => { ).toBe("Done in 30 seconds · 2,400 output tokens"); }); - it("shows live output usage beside elapsed time", () => { + it.each([ + [0, "0 output tokens"], + [1, "1 output token"], + [5_500, "5,500 output tokens"], + ])("shows %i output tokens beside elapsed time", (outputTokens, label) => { const container = document.createElement("div"); render( renderStreamGroup([{ kind: "reading-indicator", key: "reading", startedAt: 1_000 }], { - runOutputTokens: 5_500, + runOutputTokens: outputTokens, }), container, ); expect(container.querySelector(".chat-working-indicator__elapsed")).not.toBeNull(); expect(container.querySelector(".chat-working-indicator__tokens")?.textContent?.trim()).toBe( - "5,500 output tokens", + label, ); // Known usage replaces the pre-usage working phrase. expect(container.querySelector("openclaw-working-phrase")).toBeNull(); diff --git a/ui/src/pages/chat/components/chat-working-indicator.ts b/ui/src/pages/chat/components/chat-working-indicator.ts index 1eab1428fe0..872ba91bf20 100644 --- a/ui/src/pages/chat/components/chat-working-indicator.ts +++ b/ui/src/pages/chat/components/chat-working-indicator.ts @@ -46,16 +46,6 @@ function outputTokensLabel(outputTokens: number): string { : t("chat.turnRecap.tokens", { count: outputTokens.toLocaleString(i18n.getLocale()) }); } -function renderLiveOutputTokens(outputTokens: number | null | undefined) { - if (outputTokens === null || outputTokens === undefined) { - return nothing; - } - return html` - - ${outputTokensLabel(outputTokens)} - `; -} - export function renderChatWorkingIndicator( part: Extract, options: { @@ -67,9 +57,13 @@ export function renderChatWorkingIndicator( ) { const waitingApproval = options.waitingApproval === true; const continuation = options.presentation === "continuation"; + const statusLabel = waitingApproval + ? t("chat.waitingForApproval") + : options.startupLabel || t("common.working"); + const working = !waitingApproval && !options.startupLabel; // Providers report exact usage at response boundaries, not per text delta. // Keep the latest count visible while the run continues through tools. - const hasTokens = options.outputTokens !== null && options.outputTokens !== undefined; + const outputTokens = options.outputTokens; // The animated claw stays decorative; the text status exposes progress without // announcing every elapsed-time tick to screen readers. return html` @@ -91,35 +85,29 @@ export function renderChatWorkingIndicator( `} + ${statusLabel} ${waitingApproval - ? html`${t("chat.waitingForApproval")}${renderLiveOutputTokens( - options.outputTokens, - )}` - : options.startupLabel + ? nothing + : html` + + `} + ${outputTokens !== null && outputTokens !== undefined + ? html` + + ${outputTokensLabel(outputTokens)} + ` + : working ? html` - ${options.startupLabel} - - ${renderLiveOutputTokens(options.outputTokens)} + .seed=${part.key} + > ` - : html` - ${t("common.working")} - - ${hasTokens - ? renderLiveOutputTokens(options.outputTokens) - : html` - - `} - `} + : nothing} `;