Spaces:
Build error
Build error
| <script lang="ts"> | |
| import { decode } from 'html-entities'; | |
| import { v4 as uuidv4 } from 'uuid'; | |
| import { getContext } from 'svelte'; | |
| const i18n = getContext('i18n'); | |
| import { slide } from 'svelte/transition'; | |
| import { quintOut } from 'svelte/easing'; | |
| import ChevronUp from '../icons/ChevronUp.svelte'; | |
| import ChevronDown from '../icons/ChevronDown.svelte'; | |
| import Spinner from './Spinner.svelte'; | |
| import Markdown from '../chat/Messages/Markdown.svelte'; | |
| import Image from './Image.svelte'; | |
| import FullHeightIframe from './FullHeightIframe.svelte'; | |
| export let id: string = ''; | |
| export let attributes: { | |
| type?: string; | |
| id?: string; | |
| name?: string; | |
| arguments?: string; | |
| result?: string; | |
| files?: string; | |
| embeds?: string; | |
| done?: string; | |
| } = {}; | |
| export let open = false; | |
| export let className = ''; | |
| export let buttonClassName = | |
| 'w-fit text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition'; | |
| const componentId = id || uuidv4(); | |
| function parseJSONString(str: string) { | |
| try { | |
| return parseJSONString(JSON.parse(str)); | |
| } catch (e) { | |
| return str; | |
| } | |
| } | |
| function formatJSONString(str: string) { | |
| try { | |
| const parsed = parseJSONString(str); | |
| // If parsed is an object/array, then it's valid JSON | |
| if (typeof parsed === 'object') { | |
| return JSON.stringify(parsed, null, 2); | |
| } else { | |
| // It's a primitive value like a number, boolean, etc. | |
| return `${JSON.stringify(String(parsed))}`; | |
| } | |
| } catch (e) { | |
| // Not valid JSON, return as-is | |
| return str; | |
| } | |
| } | |
| // Decode and parse attributes | |
| $: args = decode(attributes?.arguments ?? ''); | |
| $: result = decode(attributes?.result ?? ''); | |
| $: files = parseJSONString(decode(attributes?.files ?? '')); | |
| $: embeds = parseJSONString(decode(attributes?.embeds ?? '')); | |
| $: isDone = attributes?.done === 'true'; | |
| $: isExecuting = attributes?.done && attributes?.done !== 'true'; | |
| </script> | |
| <div {id} class={className}> | |
| {#if embeds && Array.isArray(embeds) && embeds.length > 0} | |
| <!-- Embed Mode: Show iframes without collapsible behavior --> | |
| <div class="py-1 w-full cursor-pointer"> | |
| <div class="w-full text-xs text-gray-500"> | |
| <div class=""> | |
| {attributes.name} | |
| </div> | |
| </div> | |
| {#each embeds as embed, idx} | |
| <div class="my-2" id={`${componentId}-tool-call-embed-${idx}`}> | |
| <FullHeightIframe | |
| src={embed} | |
| {args} | |
| allowScripts={true} | |
| allowForms={true} | |
| allowSameOrigin={true} | |
| allowPopups={true} | |
| /> | |
| </div> | |
| {/each} | |
| </div> | |
| {:else} | |
| <!-- Standard collapsible tool call display --> | |
| <div | |
| class="{buttonClassName} cursor-pointer" | |
| on:pointerup={() => { | |
| open = !open; | |
| }} | |
| > | |
| <div | |
| class="w-full font-medium flex items-center justify-between gap-2 {isExecuting | |
| ? 'shimmer' | |
| : ''}" | |
| > | |
| {#if isExecuting} | |
| <div> | |
| <Spinner className="size-4" /> | |
| </div> | |
| {/if} | |
| <div class=""> | |
| {#if isDone} | |
| <Markdown | |
| id={`${componentId}-tool-call-title`} | |
| content={$i18n.t('View Result from **{{NAME}}**', { | |
| NAME: attributes.name | |
| })} | |
| /> | |
| {:else} | |
| <Markdown | |
| id={`${componentId}-tool-call-executing`} | |
| content={$i18n.t('Executing **{{NAME}}**...', { | |
| NAME: attributes.name | |
| })} | |
| /> | |
| {/if} | |
| </div> | |
| <div class="flex self-center translate-y-[1px]"> | |
| {#if open} | |
| <ChevronUp strokeWidth="3.5" className="size-3.5" /> | |
| {:else} | |
| <ChevronDown strokeWidth="3.5" className="size-3.5" /> | |
| {/if} | |
| </div> | |
| </div> | |
| </div> | |
| {#if open} | |
| <div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}> | |
| {#if isDone} | |
| <Markdown | |
| id={`${componentId}-tool-call-result`} | |
| content={`> \`\`\`json | |
| > ${formatJSONString(args)} | |
| > ${formatJSONString(result)} | |
| > \`\`\``} | |
| /> | |
| {:else} | |
| <Markdown | |
| id={`${componentId}-tool-call-args`} | |
| content={`> \`\`\`json | |
| > ${formatJSONString(args)} | |
| > \`\`\``} | |
| /> | |
| {/if} | |
| </div> | |
| {/if} | |
| {/if} | |
| <!-- Files display (images etc.) when done --> | |
| {#if isDone} | |
| {#if typeof files === 'object'} | |
| {#each files ?? [] as file, idx} | |
| {#if typeof file === 'string'} | |
| {#if file.startsWith('data:image/')} | |
| <Image id={`${componentId}-tool-call-result-${idx}`} src={file} alt="Image" /> | |
| {/if} | |
| {:else if typeof file === 'object'} | |
| {#if (file.type === 'image' || (file?.content_type ?? '').startsWith('image/')) && file.url} | |
| <Image id={`${componentId}-tool-call-result-${idx}`} src={file.url} alt="Image" /> | |
| {/if} | |
| {/if} | |
| {/each} | |
| {/if} | |
| {/if} | |
| </div> | |