From 82ba63e148f35492d6f425f9765e99c82dd9b8e2 Mon Sep 17 00:00:00 2001 From: Xubin Ren Date: Wed, 13 May 2026 08:05:34 +0000 Subject: [PATCH] fix(webui): compact spacing between auxiliary trace rows Thinking and Used tools are both auxiliary trace rows, but the thread list was applying the same large gap used between full chat turns. That made alternating Thinking / Used tools sequences look uneven and too airy. Move row spacing from a fixed flex gap to per-row margins: full chat turns keep mt-5, while consecutive auxiliary rows use mt-2. Add coverage for Thinking -> Used tools -> Thinking spacing. Co-authored-by: Cursor --- .../src/components/thread/ThreadMessages.tsx | 29 +++++++++-- webui/src/tests/thread-messages.test.tsx | 52 +++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 webui/src/tests/thread-messages.test.tsx diff --git a/webui/src/components/thread/ThreadMessages.tsx b/webui/src/components/thread/ThreadMessages.tsx index 1ef5c864b..3d3d068f3 100644 --- a/webui/src/components/thread/ThreadMessages.tsx +++ b/webui/src/components/thread/ThreadMessages.tsx @@ -1,4 +1,5 @@ import { MessageBubble } from "@/components/MessageBubble"; +import { cn } from "@/lib/utils"; import type { UIMessage } from "@/lib/types"; interface ThreadMessagesProps { @@ -7,10 +8,30 @@ interface ThreadMessagesProps { export function ThreadMessages({ messages }: ThreadMessagesProps) { return ( -
- {messages.map((message) => ( - - ))} +
+ {messages.map((message, index) => { + const prev = messages[index - 1]; + const compact = isAuxiliaryRow(message) && prev && isAuxiliaryRow(prev); + return ( +
0 && (compact ? "mt-2" : "mt-5"))} + > + +
+ ); + })}
); } + +function isAuxiliaryRow(message: UIMessage): boolean { + return ( + message.kind === "trace" + || ( + message.role === "assistant" + && message.content.trim().length === 0 + && (!!message.reasoning || !!message.reasoningStreaming) + ) + ); +} diff --git a/webui/src/tests/thread-messages.test.tsx b/webui/src/tests/thread-messages.test.tsx new file mode 100644 index 000000000..710b86298 --- /dev/null +++ b/webui/src/tests/thread-messages.test.tsx @@ -0,0 +1,52 @@ +import { render } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { ThreadMessages } from "@/components/thread/ThreadMessages"; +import type { UIMessage } from "@/lib/types"; + +describe("ThreadMessages", () => { + it("uses compact spacing between consecutive auxiliary rows", () => { + const messages: UIMessage[] = [ + { + id: "r1", + role: "assistant", + content: "", + reasoning: "thinking", + reasoningStreaming: false, + isStreaming: true, + createdAt: Date.now(), + }, + { + id: "t1", + role: "tool", + kind: "trace", + content: "search()", + traces: ["search()"], + createdAt: Date.now(), + }, + { + id: "r2", + role: "assistant", + content: "", + reasoning: "more thinking", + reasoningStreaming: false, + isStreaming: true, + createdAt: Date.now(), + }, + { + id: "a1", + role: "assistant", + content: "final answer", + createdAt: Date.now(), + }, + ]; + + const { container } = render(); + const rows = Array.from(container.firstElementChild?.children ?? []); + + expect(rows[0]).not.toHaveClass("mt-2", "mt-5"); + expect(rows[1]).toHaveClass("mt-2"); + expect(rows[2]).toHaveClass("mt-2"); + expect(rows[3]).toHaveClass("mt-5"); + }); +});