From 0ab13155b0d32a6062797b3f3da1c093b30cc9f0 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Mon, 22 Jun 2026 14:40:57 +0900 Subject: feat: collapsible tool output (collapsed by default like thinking) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tool calls and results now use the same DaisyUI collapse pattern as thinking blocks — collapsed by default, click to expand. Each card shows the tool name + a wrench icon in the title; expanding reveals the input/output with overflow-x-auto for long lines and max-h-96 overflow-y-auto for very long output. Batched tool calls: each entry is its own collapse card (was a DaisyUI list). Pending results show a spinner in the title. Errors show a red badge. 686 tests green. --- src/features/chat/ui.test.ts | 7 ++- src/features/chat/ui/ChatView.svelte | 94 +++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/src/features/chat/ui.test.ts b/src/features/chat/ui.test.ts index 2891e5b..b8b3193 100644 --- a/src/features/chat/ui.test.ts +++ b/src/features/chat/ui.test.ts @@ -242,10 +242,9 @@ describe("ChatView", () => { const { container } = render(ChatView, { props: { chunks } }); - // One DaisyUI list with two rows (one per call), not separate cards. - const lists = container.querySelectorAll("ul.list"); - expect(lists).toHaveLength(1); - expect(container.querySelectorAll("ul.list > li.list-row")).toHaveLength(2); + // Batched calls render as collapsible cards (one per call), not a list. + const collapses = container.querySelectorAll(".collapse"); + expect(collapses).toHaveLength(2); // Both call names + the available result are shown; the result is absorbed // (no standalone tool-result card). diff --git a/src/features/chat/ui/ChatView.svelte b/src/features/chat/ui/ChatView.svelte index 5aced29..2b55eb3 100644 --- a/src/features/chat/ui/ChatView.svelte +++ b/src/features/chat/ui/ChatView.svelte @@ -115,25 +115,37 @@ {:else if rendered.chunk.type === "tool-call" || rendered.chunk.type === "tool-result"} - -
-
- {#if rendered.chunk.type === "tool-call"} -
- {rendered.chunk.toolName} -
{JSON.stringify(rendered.chunk.input, null, 2)}
-
- {:else} -
- {rendered.chunk.toolName} -
{rendered.chunk.content}
-
- {/if} + +
+
+ {#if rendered.chunk.type === "tool-call"} +
+ +
+ + {rendered.chunk.toolName} +
+
+
{JSON.stringify(rendered.chunk.input, null, 2)}
+
+
+ {:else} +
+ +
+ + {rendered.chunk.toolName} + {#if rendered.chunk.isError} + error + {/if} +
+
+
{rendered.chunk.content}
+
+
+ {/if}
{:else} @@ -219,26 +231,32 @@ {:else if row.group.kind === "single"} {@render chunkRow(row.group.chunk)} {:else} - -
-
-
    - {#each row.group.entries as entry (entry.call.toolCallId)} -
  • -
    - {entry.call.toolName} -
    {JSON.stringify(entry.call.input, null, 2)}
    - {#if entry.result} -
    {entry.result.content}
    - {/if} + +
    +
    +
    + {#each row.group.entries as entry (entry.call.toolCallId)} +
    + +
    + + {entry.call.toolName} + {#if entry.result?.isError} + error + {:else if entry.result === null} + + {/if} +
    +
    +
    {JSON.stringify(entry.call.input, null, 2)}
    + {#if entry.result} +
    {entry.result.content}
    + {/if} +
    -
  • - {/each} -
+ {/each} +
{/if} -- cgit v1.2.3