From f42c82417a1979c6dc8a973e631dfdd365cc7c71 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 19:44:35 +0000 Subject: [PATCH] fix(chat): render avatar progress badge that was invisible The progress badge reported visible:true via the debug API but never painted. Two issues compounded: 1. The empty-state ChatAvatar was never passed isProcessing, so its badge was never created (the transcript avatar passed it correctly). 2. BusyIndicator is a bare (display:inline), so its inline width/height were ignored. Its only prior use was inside a flex parent (tool-call chip) which blockified it; reused inside the non-flex ProgressBadge wrapper it collapsed to 0x0 and never painted. Pass isProcessing to the empty-state avatar and make BusyIndicator inline-block so it lays out at its declared size regardless of parent. Co-Authored-By: vargas@vellum.ai --- .../chat/components/busy-indicator.test.tsx | 59 +++++++++++++++++++ .../chat/components/busy-indicator.tsx | 2 +- .../chat/components/chat-route-content.tsx | 1 + 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/domains/chat/components/busy-indicator.test.tsx diff --git a/apps/web/src/domains/chat/components/busy-indicator.test.tsx b/apps/web/src/domains/chat/components/busy-indicator.test.tsx new file mode 100644 index 00000000000..e9c0472ca1d --- /dev/null +++ b/apps/web/src/domains/chat/components/busy-indicator.test.tsx @@ -0,0 +1,59 @@ +/** + * Tests for `BusyIndicator`. + * + * The dot is a bare ``, whose default `display: inline` makes the + * CSS box model ignore `width`/`height`. Inside a flex parent the dot is + * blockified and sizes correctly, but standalone (e.g. the avatar + * `ProgressBadge`) it would collapse to 0×0 and never paint. The + * `inline-block` class guarantees the dot lays out at its declared size + * regardless of its parent's layout. Uses happy-dom via the bun:test + * preload configured in `web/bunfig.toml`. + */ + +import { afterEach, describe, expect, test } from "bun:test"; + +import { cleanup, render } from "@testing-library/react"; + +import { BusyIndicator } from "@/domains/chat/components/busy-indicator"; + +afterEach(() => { + cleanup(); +}); + +describe("BusyIndicator", () => { + test("is block-level so its size applies outside a flex parent", () => { + /** + * Tests that the dot is block-level and carries its declared size, + * so it paints even when its parent is not a flex container. + */ + + // GIVEN a 6px busy indicator rendered with no flex parent + const { container } = render(); + + // WHEN we read the rendered dot element + const dot = container.firstElementChild as HTMLElement; + + // THEN it is block-level so width/height are not ignored + expect(dot.className).toContain("inline-block"); + + // AND it lays out at the requested size + expect(dot.style.width).toBe("6px"); + expect(dot.style.height).toBe("6px"); + }); + + test("uses the shared busy-pulse keyframe", () => { + /** + * Tests that the dot keeps the shared pulse class so its animation + * matches every other busy affordance in the app. + */ + + // GIVEN a default busy indicator + const { container } = render(); + + // WHEN we read the rendered dot element + const dot = container.firstElementChild as HTMLElement; + + // THEN it carries the shared busy-indicator pulse class + expect(dot.className).toContain("busy-indicator"); + }); +}); diff --git a/apps/web/src/domains/chat/components/busy-indicator.tsx b/apps/web/src/domains/chat/components/busy-indicator.tsx index 325d1928a3c..bfb8ddcd9a3 100644 --- a/apps/web/src/domains/chat/components/busy-indicator.tsx +++ b/apps/web/src/domains/chat/components/busy-indicator.tsx @@ -14,7 +14,7 @@ export function BusyIndicator({ size = 8 }: { size?: number }) { return (