2 // Fall-through renderer for tool calls without a dedicated block.
3 // Renders section.toolArgs / section.toolResult directly using the
4 // shared chrome shell.
6 import ToolCallBlock from './ToolCallBlock.svelte';
7 import { Loader2 } from '@lucide/svelte';
8 import { MarkdownContent, SyntaxHighlightedCode } from '$lib/components/app';
9 import { MAX_HEIGHT_CODE_BLOCK } from '$lib/constants';
10 import { getBuiltinToolUi } from '$lib/constants/built-in-tools';
11 import { AttachmentType, FileTypeText, MimeTypeAudio, ToolResultKind } from '$lib/enums';
12 import type { DatabaseMessageExtra } from '$lib/types';
17 parseToolResultWithMedia,
20 import { createBase64DataUrl } from '$lib/utils/data-url';
23 section: AgenticSection;
26 attachments?: DatabaseMessageExtra[];
27 onToggle?: () => void;
30 let { attachments, isStreaming, onToggle, open, section }: Props = $props();
32 const title = $derived(getBuiltinToolUi(section.toolName)?.label ?? section.toolName ?? '');
33 const outputKind = $derived(classifyToolResult(section.toolResult));
34 const parsedLines: ToolResultLine[] = $derived(
35 section.toolResult ? parseToolResultWithMedia(section.toolResult, attachments) : []
39 <ToolCallBlock {section} {open} {isStreaming} meta={null} {title} {onToggle}>
40 {#snippet children(_meta, ctx)}
41 {#if ctx.isStreamingCall}
42 <div class="mb-2 flex items-center gap-2 text-xs text-muted-foreground/70">
45 <Loader2 class="h-3 w-3 animate-spin" />
48 {#if section.toolArgs}
49 <SyntaxHighlightedCode
50 code={formatJsonPretty(section.toolArgs)}
51 language={FileTypeText.JSON}
52 maxHeight={MAX_HEIGHT_CODE_BLOCK}
53 streaming={ctx.isCodeStreaming}
55 {:else if ctx.isStreaming}
56 <div class="rounded bg-muted/20 p-2 text-xs text-muted-foreground/70 italic">
57 Receiving arguments...
61 class="rounded bg-yellow-500/10 p-2 text-xs text-yellow-600 italic dark:text-yellow-400"
63 Response was truncated
67 {@const showInput = Boolean(section.toolArgs)}
69 <div class="mb-1.5 flex items-center gap-2 text-xs text-muted-foreground/70">
72 <SyntaxHighlightedCode
73 code={formatJsonPretty(section.toolArgs ?? '')}
74 language={FileTypeText.JSON}
75 maxHeight={MAX_HEIGHT_CODE_BLOCK}
76 streaming={ctx.isCodeStreaming}
81 ? 'mt-4 mb-1.5 flex items-center gap-2 text-xs text-muted-foreground/70'
82 : 'mb-1.5 flex items-center gap-2 text-xs text-muted-foreground/70'}
86 <Loader2 class="h-3 w-3 animate-spin" />
90 <div class="rounded bg-muted/20 p-2 text-xs text-muted-foreground/70 italic">
93 {:else if section.toolResult}
94 {#if outputKind === ToolResultKind.JSON}
95 <SyntaxHighlightedCode
96 code={formatJsonPretty(section.toolResult)}
97 language={FileTypeText.JSON}
98 maxHeight={MAX_HEIGHT_CODE_BLOCK}
100 {:else if outputKind === ToolResultKind.MARKDOWN}
101 <MarkdownContent content={section.toolResult} {attachments} />
103 <div class="overflow-auto">
104 {#each parsedLines as line, i (i)}
105 <div class="font-mono text-[11px] leading-relaxed whitespace-pre-wrap">
109 {#if line.media.type === AttachmentType.AUDIO}
110 {@const audioMimeType = line.media.mimeType ?? MimeTypeAudio.MP3_MPEG}
111 <div class="mt-2 mb-2">
112 <audio controls class="w-full rounded-lg">
114 src={createBase64DataUrl(audioMimeType, line.media.base64Data)}
117 Your browser does not support the audio element.
122 src={line.media.base64Url}
123 alt={line.media.name}
124 class="mt-2 mb-2 h-auto max-w-full rounded-lg"
133 <div class="rounded bg-muted/20 p-2 text-xs text-muted-foreground/70 italic">No output</div>