This commit is contained in:
Timothy Jaeryang Baek
2026-08-13 19:59:11 -06:00
parent b018feb741
commit 7d99b2716a
27 changed files with 1069 additions and 111 deletions
@@ -0,0 +1,22 @@
<script lang="ts">
import { getContext } from 'svelte';
import type { Writable } from 'svelte/store';
import Tooltip from './Tooltip.svelte';
type I18nStore = Writable<{
t: (key: string, options?: Record<string, unknown>) => string;
}>;
const i18n: I18nStore = getContext('i18n');
$: label = $i18n.t('Experimental');
$: content = $i18n.t(
'This is an experimental feature, it may not function as expected and is subject to change at any time.'
);
</script>
<Tooltip {content} className="inline-flex">
<span class="text-[0.625rem] font-normal uppercase leading-none text-gray-400 dark:text-gray-600">
{label}
</span>
</Tooltip>
@@ -13,6 +13,7 @@
import Spinner from './Spinner.svelte';
import WrenchSolid from '../icons/WrenchSolid.svelte';
import CheckCircle from '../icons/CheckCircle.svelte';
import XMark from '../icons/XMark.svelte';
import Image from './Image.svelte';
import FullHeightIframe from './FullHeightIframe.svelte';
import { settings } from '$lib/stores';
@@ -27,18 +28,22 @@
files?: string;
embeds?: string;
done?: string;
status?: string;
} = {};
export let open = false;
export let grouped = false;
export let className = '';
export let resolvable = false;
export let resolving = false;
export let onResolve: (approved: boolean) => void = () => {};
const RESULT_PREVIEW_LIMIT = 10000;
let expandedResult = false;
$: if (!open) expandedResult = false;
export let buttonClassName =
'w-fit py-1 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition';
'py-1 text-[0.9375rem] text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition';
const componentId = id || uuidv4();
@@ -91,7 +96,9 @@
$: args =
open || (Array.isArray(embeds) && embeds.length > 0) ? decode(attributes?.arguments ?? '') : '';
$: isDone = attributes?.done === 'true';
$: isExecuting = attributes?.done && attributes?.done !== 'true';
$: needsApproval = attributes?.status === 'pending' && resolvable;
$: isRejected = attributes?.status === 'rejected';
$: isExecuting = !needsApproval && attributes?.done && attributes?.done !== 'true';
$: parsedArgs = parseArguments(args);
$: parsedResult = parseJSONString(result);
@@ -133,14 +140,14 @@
{:else}
<!-- Tool call display -->
<div
class="{buttonClassName} cursor-pointer"
class="{buttonClassName} w-full min-w-0 cursor-pointer"
role="button"
tabindex="0"
on:click={toggleOpen}
on:keydown={toggleOpenOnKeydown}
>
<div
class="w-full max-w-full font-normal flex items-center gap-1.5 {isExecuting
class="w-full min-w-0 max-w-full font-normal flex items-center gap-1.5 {isExecuting
? 'shimmer'
: ''}"
>
@@ -149,6 +156,10 @@
<div>
<Spinner className="size-4" />
</div>
{:else if isRejected}
<div class="text-red-400 dark:text-red-500">
<XMark className="size-4" strokeWidth="2.5" />
</div>
{:else if isDone}
<div class="text-emerald-500 dark:text-emerald-400">
<CheckCircle className="size-4" strokeWidth="2" />
@@ -160,27 +171,52 @@
{/if}
<!-- Label -->
<div class="flex-1 line-clamp-1">
<div class="flex-1 min-w-0 line-clamp-1">
<!-- Short label (below md) -->
<span class="@md:hidden text-black dark:text-white">{attributes.name}</span>
<!-- Full label (md and above) -->
<span class="hidden @md:inline font-normal">
{#if isDone}
{$i18n.t('View Result from {{NAME}}', { NAME: attributes.name })}
{:else if needsApproval}
{$i18n.t('Allow {{NAME}}?', { NAME: attributes.name })}
{:else if isRejected}
{$i18n.t('Denied {{NAME}}', { NAME: attributes.name })}
{:else}
{$i18n.t('Executing {{NAME}}...', { NAME: attributes.name })}
{/if}
</span>
</div>
<!-- Chevron -->
<div class="flex shrink-0 self-center translate-y-[1px]">
{#if open}
<ChevronUp strokeWidth="3.5" className="size-3" />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3" />
{/if}
</div>
{#if needsApproval}
<span class="flex gap-1 shrink-0">
<button
type="button"
class="text-[0.6875rem] px-2.5 py-0.5 rounded-md text-gray-600 dark:text-gray-300 bg-gray-100 dark:bg-white/8 hover:bg-gray-200 dark:hover:bg-white/12 transition-colors duration-100 disabled:opacity-50"
disabled={resolving}
on:click|stopPropagation={() => onResolve(true)}
>
{$i18n.t('Allow')}
</button>
<button
type="button"
class="text-[0.6875rem] px-2 py-0.5 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-100 disabled:opacity-50"
disabled={resolving}
on:click|stopPropagation={() => onResolve(false)}
>
{$i18n.t('Deny')}
</button>
</span>
{:else}
<!-- Chevron -->
<div class="flex shrink-0 self-center translate-y-[1px]">
{#if open}
<ChevronUp strokeWidth="3.5" className="size-3" />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3" />
{/if}
</div>
{/if}
</div>
</div>