From bb78330fe158868c699621135f3b8c34a81a1d5d Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 20 Oct 2026 18:21:43 -0700 Subject: [PATCH 1/3] test(next): a streaming reply draws no caret after its text (red) Red: bun test src/agent-session/acpmux/conversation/StreamingEdge.test.tsx fails with the .cv-caret span after the newest character. Co-Authored-By: Claude Opus 5.5 --- .../conversation/StreamingEdge.test.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 webviews/src/agent-session/acpmux/conversation/StreamingEdge.test.tsx diff --git a/webviews/src/agent-session/acpmux/conversation/StreamingEdge.test.tsx b/webviews/src/agent-session/acpmux/conversation/StreamingEdge.test.tsx new file mode 100644 index 000000000000..25f0dc07baa2 --- /dev/null +++ b/webviews/src/agent-session/acpmux/conversation/StreamingEdge.test.tsx @@ -0,0 +1,20 @@ +// l10n-allow-file: test fixture reply text, not shipped UI. +// A streaming reply ends at its newest character: no caret or bar after it, while the reveal +// writes or while it waits for more (Leo 2026-10-20: the blinking caret goes). +import { expect, test } from "bun:test"; +import { renderToStaticMarkup } from "react-dom/server"; +import { Markdown } from "./Markdown"; + +const fresh = [{ id: 1, count: 5, born: 0 }]; + +test("a streaming reply draws no caret after its text", () => { + for (const waiting of [false, true]) { + const html = renderToStaticMarkup( + + {"Hello there world"} + , + ); + expect(html).toContain("world"); + expect(html).not.toContain("cv-caret"); + } +}); From de92930941f43ca7f21841ec82104675d8fe267d Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 20 Oct 2026 18:23:23 -0700 Subject: [PATCH 2/3] fix(next): a streaming reply ends at its newest character, with no caret The live-edge bar after streaming text (R104) and its wait pulse are gone, with the waiting/is-waiting plumbing and the reveal hook's settled state that only fed it. The newest characters still fade in. Green: bun test src/agent-session/acpmux/conversation/StreamingEdge.test.tsx Co-Authored-By: Claude Opus 5.5 --- .../acpmux/conversation/Markdown.tsx | 14 +++----- .../acpmux/conversation/RevealedMarkdown.tsx | 18 ++++------ .../conversation/StreamingEdge.test.tsx | 5 +-- .../acpmux/conversation/conversation.css | 34 ------------------- webviews/test/static-status-loops.test.ts | 7 ++-- 5 files changed, 17 insertions(+), 61 deletions(-) diff --git a/webviews/src/agent-session/acpmux/conversation/Markdown.tsx b/webviews/src/agent-session/acpmux/conversation/Markdown.tsx index 5727e7f3ea11..398ae3d8bc92 100644 --- a/webviews/src/agent-session/acpmux/conversation/Markdown.tsx +++ b/webviews/src/agent-session/acpmux/conversation/Markdown.tsx @@ -726,11 +726,8 @@ export type FreshTail = { steps: Reveal[]; text: string; now: number }; /// and a negative animation delay, so a re-render never restarts its fade. Only plain trailing /// text fades by step; a suffix with Markdown syntax or a line break draws as usual. function renderFresh(text: string, tail: FreshTail, opts: InlineOptions): ReactNode[] { - // The live edge: a soft bar after the newest character, out of the text's layout (no reflow - // when it leaves); it pulses only while the reveal waits for more (`.cv-md.is-waiting`). - const caret =