style: lean tool-call rendering and tighter ai chat spacing

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-06-10 16:24:24 +02:00
co-authored by Claude Opus 4.8
parent 53da8ac148
commit e32fe4b6ca
4 changed files with 32 additions and 46 deletions
@@ -422,13 +422,13 @@
{#if showTypingIndicator}
<div
class={twMerge(
'sticky z-10 mt-0.5 ml-2 self-start pointer-events-none',
'sticky z-10 -mt-10 ml-2 self-start pointer-events-none',
showFlowPendingActionControls ? 'bottom-14' : 'bottom-2'
)}
>
{#if waitingForUserAction}
<span
class="inline-flex items-center gap-1.5 text-2xs text-accent"
class="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-surface/80 backdrop-blur text-2xs text-accent"
aria-label="Waiting for your input"
>
<Hourglass class="w-3 h-3 hourglass-flip" />
@@ -41,6 +41,7 @@
<div
class={twMerge(
'mb-2 min-w-0',
message.role === 'tool' && 'mb-1',
message.role === 'user' && messageIndex > 0 && 'mt-4 mb-6',
isLast && '!mb-12',
message.role !== 'user' ? 'cursor-default' : 'cursor-pointer'
@@ -74,7 +75,7 @@
/>
</div>
{:else}
<div class={twMerge('text-sm py-1 px-2', message.role === 'tool' && 'text-primary')}>
<div class={twMerge('text-sm py-1 px-2', message.role === 'tool' && 'text-primary py-0')}>
{#if message.role === 'assistant'}
<div class="px-[1px]"><AssistantMessage {message} /></div>
{:else if message.role === 'tool'}
@@ -104,14 +104,14 @@
</script>
{#if showWhileLoading || (!loading && hasContent) || streaming}
<div class="space-y-2">
<div class="space-y-2 group">
<div class="flex items-center justify-between">
<span class="text-2xs">
<span class="text-2xs text-hint">
{title}:
</span>
{#if showCopy && hasContent && !streaming}
<button
class="p-1 rounded hover:bg-surface-secondary text-primary hover:text-secondary transition-colors"
class="p-1 rounded hover:bg-surface-secondary text-primary hover:text-secondary transition-opacity duration-200 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
onclick={copyToClipboard}
title="Copy {title.toLowerCase()}"
>
@@ -125,26 +125,20 @@
</div>
{#if loading && !streaming && !hasContent}
<div
class="bg-surface-secondary border border-gray-200 dark:border-gray-700 rounded p-3 flex items-center gap-2 text-primary"
>
<div class="flex items-center gap-2 text-primary">
<Loader2 class="w-3 h-3 animate-spin" />
<span class="text-2xs">Executing...</span>
</div>
{:else if error}
<div
class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded p-3 overflow-x-auto max-h-28 overflow-y-auto"
>
<div class="overflow-x-auto max-h-28 overflow-y-auto">
<pre class="text-2xs text-red-700 dark:text-red-300 whitespace-pre-wrap">{error}</pre>
</div>
{:else if hasContent}
<div
class="bg-surface-secondary border border-gray-200 dark:border-gray-700 rounded overflow-hidden relative"
>
<div class="relative">
<div
bind:this={scrollEl}
onscroll={updateCanScrollDown}
class="p-3 overflow-x-auto max-h-28 overflow-y-auto"
class="overflow-x-auto max-h-28 overflow-y-auto"
>
<pre class="text-2xs text-primary whitespace-pre-wrap"
>{formatJson($state.snapshot(content))}</pre
@@ -152,16 +146,12 @@
</div>
{#if showFade && canScrollDown}
<div
class="absolute bottom-0 left-0 right-0 h-16 pointer-events-none bg-gradient-to-t from-surface-secondary via-surface-secondary/70 via-surface-secondary/40 to-transparent"
class="absolute bottom-0 left-0 right-0 h-16 pointer-events-none bg-gradient-to-t from-surface via-surface/70 via-surface/40 to-transparent"
></div>
{/if}
</div>
{:else}
<div
class="bg-surface-secondary border border-gray-200 dark:border-gray-700 rounded p-3 text-center"
>
<span class="text-2xs text-primary">No {title.toLowerCase()} yet</span>
</div>
<span class="text-2xs text-tertiary">No {title.toLowerCase()} yet</span>
{/if}
</div>
{/if}
@@ -1,11 +1,12 @@
<script lang="ts">
import { Loader2, ChevronDown, ChevronRight, XCircle, Play } from 'lucide-svelte'
import { Loader2, ChevronRight, XCircle, Play } from 'lucide-svelte'
import { Button } from '$lib/components/common'
import { getAiChatManager } from './aiChatManagerContext'
const aiChatManager = getAiChatManager()
import { isActiveUserQuestion, type ToolDisplayMessage } from './shared'
import { twMerge } from 'tailwind-merge'
import { slide } from 'svelte/transition'
import ToolContentDisplay from './ToolContentDisplay.svelte'
import ToolMessageActions from './ToolMessageActions.svelte'
import AskUserQuestionDisplay from './AskUserQuestionDisplay.svelte'
@@ -48,42 +49,41 @@
{#if activeUserQuestion}
<AskUserQuestionDisplay toolCallId={message.tool_call_id} userQuestion={activeUserQuestion} />
{:else}
<div class="bg-surface border border-border-light rounded-md overflow-hidden font-mono text-xs">
<div class="font-mono text-xs">
<!-- Collapsible Header -->
<button
class={twMerge(
'w-full p-2 bg-surface-secondary/30 hover:bg-surface-hover transition-colors flex items-center justify-between text-left',
isExpanded ? 'border-b border-border-light' : '',
'py-0.5 my-0.5 rounded-md hover:bg-surface-hover transition-colors inline-flex items-center text-left',
message.needsConfirmation ? 'opacity-80' : ''
)}
onclick={() => (isExpanded = !isExpanded)}
disabled={!message.showDetails && !message.isStreamingArguments}
>
<div class="flex items-center gap-2 flex-1">
{#if message.showDetails || message.isStreamingArguments}
{#if isExpanded}
<ChevronDown class="w-3 h-3 text-secondary" />
{:else}
<ChevronRight class="w-3 h-3 text-secondary" />
{/if}
{/if}
<div class="flex items-center gap-2">
{#if message.isLoading && !message.needsConfirmation}
<Loader2 class="w-3.5 h-3.5 animate-spin text-blue-500" />
{:else if message.error}
<span class="text-red-500">✗</span>
{:else if !message.isLoading && !message.error}
<span class="text-green-500">✓</span>
{/if}
<span class="text-primary font-medium text-2xs">
{message.content}
</span>
{#if message.showDetails || message.isStreamingArguments}
<ChevronRight
class={twMerge(
'w-3 h-3 text-secondary transition-transform duration-150',
isExpanded ? 'rotate-90' : ''
)}
/>
{/if}
</div>
</button>
<!-- Expanded Content -->
{#if isExpanded}
<div class="p-2 bg-surface space-y-3">
<div
transition:slide={{ duration: 150 }}
class="border border-border-light rounded-md bg-surface p-3 space-y-3"
>
<!-- Parameters Section - show if we have parameters, or if confirmation is needed (even with empty params) -->
{#if hasParameters || message.needsConfirmation}
<div class={message.needsConfirmation ? 'opacity-80' : ''}>
@@ -99,12 +99,7 @@
<!-- Confirmation Footer -->
{#if message.needsConfirmation}
<div
class={twMerge(
'mt-3 pt-3 flex flex-row items-center justify-end gap-2',
hasParameters ? 'border-t border-border-light' : ''
)}
>
<div class="flex flex-row items-center justify-end gap-2">
<Button
variant="default"
size="xs"