From bfcb388dd759d2c3782652312f8c457880322750 Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Sun, 6 Sep 2026 15:13:38 +1000 Subject: [PATCH] feat(app): pulse the status dot while the event stream reconnects (#47574) --- packages/app/src/shell/status/indicator.test.ts | 13 +++++++++++++ packages/app/src/shell/status/indicator.ts | 4 ++++ packages/app/src/shell/status/status-popover.tsx | 2 ++ 3 files changed, 19 insertions(+) diff --git a/packages/app/src/shell/status/indicator.test.ts b/packages/app/src/shell/status/indicator.test.ts index 88ae72bfc55..7a7c6f58ef3 100644 --- a/packages/app/src/shell/status/indicator.test.ts +++ b/packages/app/src/shell/status/indicator.test.ts @@ -21,6 +21,19 @@ describe("serverStatusDotClass", () => { expect(serverStatusDotClass({ ready: true, serverHealth: false, issue: true })).toBe("bg-icon-critical-base") }) + test("pulses the neutral dot while the event stream is reconnecting", () => { + expect(serverStatusDotClass({ ready: true, serverHealth: true, issue: false, connecting: true })).toBe( + "bg-border-weak-base animate-pulse", + ) + expect(serverStatusDotClass({ ready: false, serverHealth: undefined, issue: false, connecting: true })).toBe( + "bg-border-weak-base animate-pulse", + ) + // A server that is known to be down stays critical rather than looking like a routine reconnect. + expect(serverStatusDotClass({ ready: true, serverHealth: false, issue: false, connecting: true })).toBe( + "bg-icon-critical-base", + ) + }) + test("stays neutral before status is ready", () => { expect(serverStatusDotClass({ ready: false, serverHealth: true, issue: false })).toBe("bg-border-weak-base") expect(serverStatusDotClass({ ready: false, serverHealth: undefined, issue: false })).toBe("bg-border-weak-base") diff --git a/packages/app/src/shell/status/indicator.ts b/packages/app/src/shell/status/indicator.ts index 8695d17d4a1..3a93f24e0fc 100644 --- a/packages/app/src/shell/status/indicator.ts +++ b/packages/app/src/shell/status/indicator.ts @@ -20,8 +20,12 @@ export function serverStatusDotClass(input: { serverHealth: boolean | undefined attention?: boolean issue: boolean + connecting?: boolean }) { if (input.serverHealth === false) return "bg-icon-critical-base" + // The event stream is (re)connecting: keep the neutral dot but let it breathe so a stale + // session is visibly waiting on the server rather than silently frozen. + if (input.connecting) return "bg-border-weak-base animate-pulse" if (!input.ready || input.serverHealth === undefined) return "bg-border-weak-base" if (input.attention) return "bg-v2-background-bg-accent" if (input.issue) return "bg-icon-warning-base" diff --git a/packages/app/src/shell/status/status-popover.tsx b/packages/app/src/shell/status/status-popover.tsx index 4e59f2178cf..ca3688a9960 100644 --- a/packages/app/src/shell/status/status-popover.tsx +++ b/packages/app/src/shell/status/status-popover.tsx @@ -42,6 +42,7 @@ export function StatusPopover() { serverHealth: serverHealth(), attention: attention(), issue: issue(), + connecting: server.ctx.sdk.connection.status() !== "connected", sidebar: sidebar(), placement: sidebar() ? "top-start" : "bottom-end", shift: sidebar() ? 0 : -168, @@ -63,6 +64,7 @@ type StatusPopoverState = { serverHealth: boolean | undefined attention: boolean issue: boolean + connecting: boolean sidebar: boolean placement: "top-start" | "bottom-end" shift: number