unified agent indicator at end of conversation
- New AgentIndicator component: replaces 3 separate indicators (loading dots, ToolCallGroup summary, activity bar) with one - Positioned as last item in message list — scrolls naturally - Shows current tool action: 'Researching lxc:nfs-export…' etc - Spinner during work, check on completion, X on error - Fades out 3s after turn completes - Activity bar, loading dots, statusLabel removed from Chat - Continue button moved to sidebar session panel
This commit is contained in:
68
web/src/lib/components/AgentIndicator.svelte
Normal file
68
web/src/lib/components/AgentIndicator.svelte
Normal file
@@ -0,0 +1,68 @@
|
||||
<script lang="ts">
|
||||
import type { ToolCallResult } from '$lib/stores/chat'
|
||||
import LoaderCircleIcon from '@lucide/svelte/icons/loader-circle'
|
||||
import CheckIcon from '@lucide/svelte/icons/check'
|
||||
import XIcon from '@lucide/svelte/icons/x'
|
||||
|
||||
let { active = false, lastTool = null as ToolCallResult | null, error = '' }: { active?: boolean; lastTool?: ToolCallResult | null; error?: string } = $props()
|
||||
|
||||
let done = $state(false)
|
||||
let wasActive = $state(false)
|
||||
|
||||
$effect(() => {
|
||||
if (active) { done = false; wasActive = true }
|
||||
if (!active && wasActive) {
|
||||
done = true
|
||||
// Fade out after 3s
|
||||
const t = setTimeout(() => { done = false; wasActive = false }, 3000)
|
||||
return () => clearTimeout(t)
|
||||
}
|
||||
})
|
||||
|
||||
const label = $derived.by(() => {
|
||||
if (error) return error
|
||||
if (!active && done) return 'Done'
|
||||
if (!lastTool) return 'Agent is thinking…'
|
||||
const args = lastTool.args ?? {}
|
||||
switch (lastTool.name) {
|
||||
case 'set_goal': return 'Setting goal…'
|
||||
case 'propose_plan': return 'Building plan…'
|
||||
case 'search_knowledge': return `Researching: ${args.query ?? ''}`
|
||||
case 'get_entity': return `Looking up ${args.slug_or_id ?? ''}`
|
||||
case 'get_entity_knowledge': return `Checking prior knowledge…`
|
||||
case 'get_relations': return `Checking relationships…`
|
||||
case 'list_lxcs': return 'Listing containers…'
|
||||
case 'list_entities': return 'Listing entities…'
|
||||
case 'get_health_summary': return 'Checking fleet health…'
|
||||
case 'run': {
|
||||
const purpose = args.purpose ?? ''
|
||||
const target = args.target ?? ''
|
||||
if (purpose) return purpose
|
||||
if (target) return `Running on ${target}…`
|
||||
return 'Running command…'
|
||||
}
|
||||
case 'get_execution_status': return 'Checking execution status…'
|
||||
case 'update_plan_step': return 'Updating progress…'
|
||||
case 'upsert_knowledge': return 'Recording knowledge…'
|
||||
case 'complete_task': return 'Wrapping up…'
|
||||
case 'ping_service': return 'Checking service…'
|
||||
case 'ask_operator': return 'Asking operator…'
|
||||
default: return `${lastTool.name}…`
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if active || done || error}
|
||||
<div class="flex items-center gap-2 py-2 text-xs {error ? 'text-destructive' : done ? 'text-success' : 'text-muted-foreground'}" class:opacity-50={done} class:transition-opacity>
|
||||
<span class="shrink-0">
|
||||
{#if error}
|
||||
<XIcon class="size-3" />
|
||||
{:else if done}
|
||||
<CheckIcon class="size-3" />
|
||||
{:else}
|
||||
<LoaderCircleIcon class="size-3 animate-spin text-primary" />
|
||||
{/if}
|
||||
</span>
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user