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");
+ });
+});