feat(web): operator questions inline in chat, mascot reactions scoped to the focused task
The pending operator-question card now renders inline in ChatThread (the newest thing in the conversation) instead of in the context rail — it's part of the chat, not a separate side panel, and the panel's hasContext gate no longer needs to special-case it. The desktop mascot's reactions are now entirely about whichever task window has focus, not fleet-wide events: thinking/talking is a new continuous `busy` behavior that tracks the focused session's own streaming state (thinking before any text arrives, talking once it does — using the previously-unwired peep/talk sprite), eureka fires with the actual knowledge title that was recorded, happy fires with the task's own completion summary, and alarmed now means "this task needs your OK" (an operator question was raised) rather than a fleet-wide critical/signal event. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
import { Textarea } from '$lib/components/ui/textarea'
|
||||
import Spinner from './Spinner.svelte'
|
||||
import ToolCallCard from './ToolCallCard.svelte'
|
||||
import OperatorQuestion from './OperatorQuestion.svelte'
|
||||
import CornerDownLeftIcon from '@lucide/svelte/icons/corner-down-left'
|
||||
import CheckIcon from '@lucide/svelte/icons/check'
|
||||
import XIcon from '@lucide/svelte/icons/x'
|
||||
@@ -19,6 +20,7 @@
|
||||
import { marked } from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
import type { ChatMessage } from '$lib/stores/chat'
|
||||
import type { SessionQuestion } from '$lib/api'
|
||||
|
||||
let {
|
||||
messages,
|
||||
@@ -31,7 +33,9 @@
|
||||
onReconnect,
|
||||
onDismissError,
|
||||
suggestions = [],
|
||||
activityLog: activityLogProp = activityLog
|
||||
activityLog: activityLogProp = activityLog,
|
||||
sessionId = null,
|
||||
question = null
|
||||
}: {
|
||||
messages: ChatMessage[]
|
||||
streaming: boolean
|
||||
@@ -44,6 +48,10 @@
|
||||
onDismissError: (id: string) => void
|
||||
suggestions?: string[]
|
||||
activityLog?: Readable<ActivityEntry[]>
|
||||
/** Session this thread's pending question (below) should post its answer against — see OperatorQuestion.svelte. */
|
||||
sessionId?: string | null
|
||||
/** The session's open operator question, if any — rendered as an inline card at the end of the thread (the newest thing, blocking the agent until answered). */
|
||||
question?: SessionQuestion | null
|
||||
} = $props()
|
||||
|
||||
let input = $state('')
|
||||
@@ -121,9 +129,11 @@
|
||||
scrolledUp = !isNearBottom()
|
||||
}
|
||||
|
||||
// Auto-scroll to bottom on new messages — unless user scrolled up to read.
|
||||
// Auto-scroll to bottom on new messages (or a freshly-raised question) —
|
||||
// unless user scrolled up to read.
|
||||
$effect(() => {
|
||||
void messages
|
||||
void question
|
||||
if (streaming || !scrolledUp) {
|
||||
setTimeout(() => messagesEnd?.scrollIntoView({ behavior: 'smooth' }), 50)
|
||||
}
|
||||
@@ -246,6 +256,9 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{#if question}
|
||||
<OperatorQuestion {sessionId} {question} />
|
||||
{/if}
|
||||
<div bind:this={messagesEnd}></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user