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}
`;