@classic-homes/agent-chat 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +67 -2
  2. package/dist/components/AgentChat.svelte +60 -25
  3. package/dist/components/AgentChat.svelte.d.ts +11 -1
  4. package/dist/components/AgentChat.svelte.d.ts.map +1 -1
  5. package/dist/components/ArtifactPanel.svelte +4 -2
  6. package/dist/components/AssistantMessage.svelte +58 -16
  7. package/dist/components/AssistantMessage.svelte.d.ts +2 -0
  8. package/dist/components/AssistantMessage.svelte.d.ts.map +1 -1
  9. package/dist/components/ChatHeader.svelte +4 -1
  10. package/dist/components/ChatHeader.svelte.d.ts +2 -0
  11. package/dist/components/ChatHeader.svelte.d.ts.map +1 -1
  12. package/dist/components/ChatLauncher.svelte +175 -0
  13. package/dist/components/ChatLauncher.svelte.d.ts +26 -0
  14. package/dist/components/ChatLauncher.svelte.d.ts.map +1 -0
  15. package/dist/components/Composer.svelte +7 -3
  16. package/dist/components/Composer.svelte.d.ts.map +1 -1
  17. package/dist/components/FloatingWindow.svelte +280 -0
  18. package/dist/components/FloatingWindow.svelte.d.ts +35 -0
  19. package/dist/components/FloatingWindow.svelte.d.ts.map +1 -0
  20. package/dist/components/Markdown.svelte +4 -0
  21. package/dist/components/Markdown.svelte.d.ts.map +1 -1
  22. package/dist/components/MessageFeedback.svelte +11 -4
  23. package/dist/components/MessageFeedback.svelte.d.ts.map +1 -1
  24. package/dist/components/MessageFiles.svelte +133 -40
  25. package/dist/components/MessageFiles.svelte.d.ts +6 -0
  26. package/dist/components/MessageFiles.svelte.d.ts.map +1 -1
  27. package/dist/components/MessageItem.svelte +2 -0
  28. package/dist/components/MessageItem.svelte.d.ts +2 -0
  29. package/dist/components/MessageItem.svelte.d.ts.map +1 -1
  30. package/dist/components/MessageList.svelte +40 -7
  31. package/dist/components/MessageList.svelte.d.ts +3 -1
  32. package/dist/components/MessageList.svelte.d.ts.map +1 -1
  33. package/dist/components/ShortcutsDialog.svelte +10 -3
  34. package/dist/components/ShortcutsDialog.svelte.d.ts +2 -0
  35. package/dist/components/ShortcutsDialog.svelte.d.ts.map +1 -1
  36. package/dist/components/UiFrame.svelte +12 -4
  37. package/dist/components/UiFrame.svelte.d.ts.map +1 -1
  38. package/dist/components/UserMessage.svelte +10 -1
  39. package/dist/components/UserMessage.svelte.d.ts.map +1 -1
  40. package/dist/index.d.ts +5 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +5 -0
  43. package/dist/markdown.d.ts.map +1 -1
  44. package/dist/markdown.js +94 -61
  45. package/dist/reducer.d.ts +11 -0
  46. package/dist/reducer.d.ts.map +1 -1
  47. package/dist/reducer.js +18 -2
  48. package/dist/session.svelte.d.ts +40 -0
  49. package/dist/session.svelte.d.ts.map +1 -1
  50. package/dist/session.svelte.js +178 -23
  51. package/dist/shortcuts.d.ts +7 -0
  52. package/dist/shortcuts.d.ts.map +1 -1
  53. package/dist/shortcuts.js +9 -0
  54. package/dist/surface-geometry.d.ts +60 -0
  55. package/dist/surface-geometry.d.ts.map +1 -0
  56. package/dist/surface-geometry.js +112 -0
  57. package/dist/surface.svelte.d.ts +79 -0
  58. package/dist/surface.svelte.d.ts.map +1 -0
  59. package/dist/surface.svelte.js +178 -0
  60. package/dist/thinking.d.ts +10 -0
  61. package/dist/thinking.d.ts.map +1 -0
  62. package/dist/thinking.js +61 -0
  63. package/dist/ui-frame-protocol.d.ts +6 -0
  64. package/dist/ui-frame-protocol.d.ts.map +1 -1
  65. package/dist/ui-frame-protocol.js +11 -0
  66. package/package.json +8 -7
  67. package/dist/artifact-pages.test.d.ts +0 -2
  68. package/dist/artifact-pages.test.d.ts.map +0 -1
  69. package/dist/artifact-pages.test.js +0 -102
  70. package/dist/components/a11y.svelte.test.d.ts +0 -2
  71. package/dist/components/a11y.svelte.test.d.ts.map +0 -1
  72. package/dist/components/a11y.svelte.test.js +0 -183
  73. package/dist/components/agent-picker.svelte.test.d.ts +0 -2
  74. package/dist/components/agent-picker.svelte.test.d.ts.map +0 -1
  75. package/dist/components/agent-picker.svelte.test.js +0 -84
  76. package/dist/components/composer.svelte.test.d.ts +0 -2
  77. package/dist/components/composer.svelte.test.d.ts.map +0 -1
  78. package/dist/components/composer.svelte.test.js +0 -162
  79. package/dist/components/continue.svelte.test.d.ts +0 -2
  80. package/dist/components/continue.svelte.test.d.ts.map +0 -1
  81. package/dist/components/continue.svelte.test.js +0 -109
  82. package/dist/components/conversation-chrome.svelte.test.d.ts +0 -2
  83. package/dist/components/conversation-chrome.svelte.test.d.ts.map +0 -1
  84. package/dist/components/conversation-chrome.svelte.test.js +0 -147
  85. package/dist/components/feedback.svelte.test.d.ts +0 -2
  86. package/dist/components/feedback.svelte.test.d.ts.map +0 -1
  87. package/dist/components/feedback.svelte.test.js +0 -99
  88. package/dist/components/live-fixes.svelte.test.d.ts +0 -2
  89. package/dist/components/live-fixes.svelte.test.d.ts.map +0 -1
  90. package/dist/components/live-fixes.svelte.test.js +0 -127
  91. package/dist/components/markdown-prose.svelte.test.d.ts +0 -2
  92. package/dist/components/markdown-prose.svelte.test.d.ts.map +0 -1
  93. package/dist/components/markdown-prose.svelte.test.js +0 -26
  94. package/dist/components/messages.svelte.test.d.ts +0 -2
  95. package/dist/components/messages.svelte.test.d.ts.map +0 -1
  96. package/dist/components/messages.svelte.test.js +0 -215
  97. package/dist/components/regenerate.svelte.test.d.ts +0 -2
  98. package/dist/components/regenerate.svelte.test.d.ts.map +0 -1
  99. package/dist/components/regenerate.svelte.test.js +0 -108
  100. package/dist/components/welcome.svelte.test.d.ts +0 -2
  101. package/dist/components/welcome.svelte.test.d.ts.map +0 -1
  102. package/dist/components/welcome.svelte.test.js +0 -119
  103. package/dist/composer-keys.test.d.ts +0 -2
  104. package/dist/composer-keys.test.d.ts.map +0 -1
  105. package/dist/composer-keys.test.js +0 -16
  106. package/dist/markdown.test.d.ts +0 -2
  107. package/dist/markdown.test.d.ts.map +0 -1
  108. package/dist/markdown.test.js +0 -35
  109. package/dist/reducer.test.d.ts +0 -2
  110. package/dist/reducer.test.d.ts.map +0 -1
  111. package/dist/reducer.test.js +0 -153
  112. package/dist/ui-frame-protocol.test.d.ts +0 -2
  113. package/dist/ui-frame-protocol.test.d.ts.map +0 -1
  114. package/dist/ui-frame-protocol.test.js +0 -48
  115. package/dist/ui-frame-relay.test.d.ts +0 -2
  116. package/dist/ui-frame-relay.test.d.ts.map +0 -1
  117. package/dist/ui-frame-relay.test.js +0 -36
package/README.md CHANGED
@@ -5,7 +5,7 @@ artifacts and MCP-UI sandbox frames. The Classic App's `/agent` page and agent p
5
5
 
6
6
  ## Requirements
7
7
 
8
- - `svelte` 5 and `@classic-homes/theme-svelte` 1.39 or later (peer dependencies).
8
+ - `svelte` `^5.31.0` and `@classic-homes/theme-svelte` `^1.42.2` (peer dependencies).
9
9
  - Tailwind with `@classic-homes/theme-tailwind-preset`. The components are styled with Tailwind
10
10
  utility classes and theme tokens, so the consumer's Tailwind build has to scan this package:
11
11
 
@@ -38,13 +38,75 @@ artifacts and MCP-UI sandbox frames. The Classic App's `/agent` page and agent p
38
38
  `client` is an `AgentApiClient` from `@classic-homes/agent-client`. `sandboxOrigin` is the
39
39
  agent-api origin that serves `/frame`; artifacts and MCP-UI resources only ever render there.
40
40
 
41
+ ### `conversationId`
42
+
43
+ - `null` (or absent): a new chat. When the first message starts a conversation,
44
+ `onconversationstarted(id)` reports its id; the host may pass that id back as
45
+ `conversationId` (the chat stays mounted and does not reload) or keep passing `null` (the
46
+ chat carries on in that conversation).
47
+ - Another id: the chat loads that transcript, re-attaching to a turn still running there. A
48
+ stream of the conversation shown before is dropped (not cancelled on the server; it finishes
49
+ there) and does not fire `onturnend`. A load that fails shows Try again; it is not retried
50
+ automatically for the same id.
51
+ - From an id back to `null` (the host's "New chat"): the transcript and the conversation are
52
+ cleared, so the next message starts a new conversation.
53
+
54
+ `ChatSession` (used by `AgentChat`, exported for other layouts) has the matching methods:
55
+ `load(id)`, `reset()` (forget the conversation and drop a running stream without a server
56
+ cancel), `send`, `cancel` (asks the runtime to stop and keeps reading until it confirms, at
57
+ most `cancelGraceMs`, default 3 s, so the text up to the stop is kept), `retry`, `regenerate`,
58
+ `edit` and `continue(requestId)`.
59
+
60
+ ### Continue
61
+
62
+ An answer cut short by the output limit (`finishReason: 'length'`) or stopped by the user
63
+ (`'cancelled'`) shows Continue: `ChatSession.continue` asks the runtime to carry on and streams
64
+ the rest into the same message. A connection that drops mid-answer is re-attached
65
+ automatically (twice); if that fails the answer shows a retryable error (Try again), not
66
+ Continue. When a continuation itself fails, the text so far stays and Continue is offered again.
67
+ Continue needs a runtime that supports `ChatRequest.continues`; an older one ignores it.
68
+
69
+ ### Files
70
+
71
+ The file endpoint needs the bearer, which `<img src>` and download links cannot send. `AgentChat`
72
+ loads produced files with `client.fetchFile` (blob URLs, revoked when the message goes away);
73
+ `MessageFiles` takes a `loadFile(fileId): Promise<Blob>` prop and links to `fileUrl` without one
74
+ (only for cookie-authenticated hosts).
75
+
76
+ ### Detached chat (launcher, floating window, dock)
77
+
78
+ A host can show the chat over its pages instead of on a route of its own:
79
+
80
+ - `createChatSurface({ storageKey })` holds which presentation is showing (`launcher`, `float`
81
+ or `dock`), where the launcher sits (`corner`, or `launcher` once the user has placed it), the
82
+ window's geometry and an `unread` flag; it persists to `localStorage` under `storageKey` and
83
+ keeps both inside the viewport. The launcher's place and the window's are separate.
84
+ - `ChatLauncher` is the floating button. The user can drag it anywhere; pass `position` and save
85
+ the drop from `onmove` (`surface.setLauncher`). While answering an arc orbits it; it also shows
86
+ an unseen answer, an error and a MOCK marker.
87
+ - `FloatingWindow` is a non-modal window around any content: one header (mark, `title`, optional
88
+ `subtitle` such as the conversation name, your `actions`, Dock, Minimize), drag by the header,
89
+ resize from the edges, drop on the right edge to dock, arrow keys on the move handle, Escape to
90
+ minimize. It takes the keyboard when it appears (the first text field, else the window;
91
+ `autofocus={false}` to opt out). The dock itself is the host's (e.g. a theme `DataPanel`).
92
+ For history, render `ConversationSidebar` in place of the chat (`class="w-full border-r-0"`);
93
+ with a host-owned session nothing is lost while it shows.
94
+ - Pass `AgentChat` a host-owned `session` (`new ChatSession({...})`) and `variant="float"` or
95
+ `"panel"`. The session then survives the view moving between presentations, including a
96
+ running answer and the unsent composer text (`session.draft`). With `session` set, configure `mode`, `agent`, `clientKind`, `getContext` and
97
+ the turn callbacks on the session; those `AgentChat` props are ignored. Call
98
+ `surface.notifyTurnEnd()` from the session's `onTurnEnd` to flag an answer that finished while
99
+ collapsed.
100
+
41
101
  ## Components
42
102
 
43
103
  `AgentChat` composes the rest; each is also exported for other layouts:
44
104
 
45
105
  - `ChatHeader`, `ConversationSidebar`: title and conversation actions; the history rail.
46
106
  `ChatHeader`'s menu opens `ShortcutsDialog` (the chat's `CHAT_SHORTCUTS` plus the host's
47
- `shortcuts`, e.g. a panel toggle). Hosts implement their own app-level shortcuts.
107
+ `shortcuts`, e.g. a panel toggle). Hosts implement their own app-level shortcuts. Pass
108
+ `savedPrompts={false}` to `ChatHeader` when the chat has no `prompts`, so the `/` entry is
109
+ left out.
48
110
  - `MessageList`: transcript with day separators and a jump-to-latest button.
49
111
  - `MessageItem` → `UserMessage` / `AssistantMessage`, with `MessageActions` (copy, time,
50
112
  `MessageFeedback` thumbs up/down with an optional note, Regenerate and Edit on the latest turn),
@@ -63,4 +125,7 @@ agent-api origin that serves `/frame`; artifacts and MCP-UI resources only ever
63
125
  an error with Try again when that load fails.
64
126
  - `ArtifactPanel`, `UiFrame`, `ModeBanner`.
65
127
 
128
+ While an answer has nothing to show yet, the assistant message cycles through
129
+ `THINKING_PHRASES` (homebuilding verbs, as in the Classic Chat widget) beside a stepped loader.
130
+
66
131
  Publishing: `docs/operations/package-publishing.md`.
@@ -32,6 +32,14 @@
32
32
 
33
33
  interface Props {
34
34
  client: AgentApiClient;
35
+ /**
36
+ * A session the host owns, so it survives this component being unmounted and mounted
37
+ * elsewhere (launcher, floating window, dock) without dropping a running answer. The host
38
+ * then configures it itself: `mode`, `agent`, `clientKind`, `getContext`,
39
+ * `onconversationstarted`, `ontoolresult` and `onturnend` here are ignored. Without it the
40
+ * component creates its own, as before.
41
+ */
42
+ session?: ChatSession;
35
43
  /** Sandbox origin for artifacts and MCP-UI (agent-api `/frame`). */
36
44
  sandboxOrigin: string;
37
45
  conversationId?: string | null;
@@ -47,7 +55,8 @@
47
55
  /** The agent of this conversation changed (picked, or learned from a loaded conversation). */
48
56
  onagentchange?: (agent: AgentKey) => void;
49
57
  clientKind?: ClientKind;
50
- variant?: 'page' | 'panel';
58
+ /** `float` is the detached window: laid out like the panel. */
59
+ variant?: 'page' | 'panel' | 'float';
51
60
  getContext?: () => DocumentContext | null | undefined;
52
61
  onconversationstarted?: (conversationId: string) => void;
53
62
  ontoolresult?: (name: string, status: string) => void;
@@ -83,6 +92,7 @@
83
92
  }
84
93
  let {
85
94
  client,
95
+ session: hostSession,
86
96
  sandboxOrigin,
87
97
  conversationId = null,
88
98
  mode,
@@ -111,29 +121,54 @@
111
121
  class: className,
112
122
  }: Props = $props();
113
123
 
114
- const session = new ChatSession({
115
- client,
116
- conversationId,
117
- mode,
118
- agent,
119
- clientKind,
120
- getContext,
121
- onConversationStarted: (id) => onconversationstarted?.(id),
122
- onToolResult: (name, status) => ontoolresult?.(name, status),
123
- onTurnEnd: (info) => onturnend?.(info),
124
- // Open the pane when the runtime starts an artifact, not by watching state later: a fast
125
- // stream can open and close it before any effect sees `open`.
126
- onEvent: (event) => {
127
- if (event.type === 'artifact' && event.op === 'open') {
124
+ // One session for the component's lifetime: the host's, or our own.
125
+ // svelte-ignore state_referenced_locally
126
+ const session =
127
+ hostSession ??
128
+ new ChatSession({
129
+ client,
130
+ conversationId,
131
+ mode,
132
+ agent,
133
+ clientKind,
134
+ getContext,
135
+ onConversationStarted: (id) => onconversationstarted?.(id),
136
+ onToolResult: (name, status) => ontoolresult?.(name, status),
137
+ onTurnEnd: (info) => onturnend?.(info),
138
+ });
139
+ // Open the pane when the runtime starts an artifact, not by watching state later: a fast
140
+ // stream can open and close it before any effect sees `open`. Only beside the chat: a sheet
141
+ // (narrow screens, the panel, the floating window) would cover the answer mid-stream, so the
142
+ // chip opens it there.
143
+ $effect(() =>
144
+ session.subscribe((event) => {
145
+ if (event.type === 'artifact' && event.op === 'open' && sidePane) {
128
146
  selectedArtifact = event.artifactId;
129
147
  artifactsOpen = true;
130
148
  }
131
- },
132
- });
149
+ }),
150
+ );
151
+ const compact = $derived(variant !== 'page');
133
152
  let attemptedId: string | null = null;
153
+ /** The prop as last seen, to tell the host's "New chat" (id → null) from a chat started here. */
154
+ let previousId: string | null = null;
134
155
  // The constructor already knows `conversationId`; what decides a fetch is whether the
135
- // transcript for it has been loaded (or started) in this session.
156
+ // transcript for it has been loaded (or started) in this session. `load` stops a stream of
157
+ // the conversation shown before.
136
158
  $effect(() => {
159
+ const before = previousId;
160
+ previousId = conversationId;
161
+ if (!conversationId) {
162
+ // The host left a conversation for a new chat: forget it, or the next message would go
163
+ // to it. A chat started here while the prop stayed null keeps going (null → null).
164
+ if (before && session.conversationId) {
165
+ session.reset();
166
+ attemptedId = null;
167
+ selectedArtifact = null;
168
+ artifactsOpen = false;
169
+ }
170
+ return;
171
+ }
137
172
  if (
138
173
  conversationId &&
139
174
  conversationId !== session.loadedConversationId &&
@@ -146,11 +181,10 @@
146
181
  }
147
182
  });
148
183
  $effect(() => {
149
- if (mode && !session.mode) session.mode = mode;
184
+ if (mode && !hostSession && !session.mode) session.mode = mode;
150
185
  });
151
186
 
152
187
  let composer: Composer | undefined = $state();
153
- let draft = $state('');
154
188
  let files = $state<FileRef[]>([]);
155
189
  let selectedArtifact = $state<string | null>(null);
156
190
  let artifactsOpen = $state(false);
@@ -174,7 +208,7 @@
174
208
  }
175
209
  function retry(): void {
176
210
  const last = session.messages[session.messages.length - 1];
177
- if (last?.role === 'assistant') void session.retry(last.requestId);
211
+ if (last?.role === 'assistant') thenFocus(session.retry(last.requestId));
178
212
  }
179
213
  const agentName = $derived(agents?.find((a) => a.key === session.agent)?.name);
180
214
  const pickAgent = $derived(
@@ -213,7 +247,7 @@
213
247
  if (conversationId) void session.load(conversationId);
214
248
  }
215
249
  function prompt(text: string): void {
216
- draft = text;
250
+ session.draft = text;
217
251
  composer?.focus();
218
252
  }
219
253
  </script>
@@ -259,7 +293,7 @@
259
293
  title={welcomeTitle}
260
294
  description={welcomeDescription}
261
295
  {suggestions}
262
- compact={variant === 'panel'}
296
+ {compact}
263
297
  disabled={session.status !== 'idle'}
264
298
  onselect={(text) => thenFocus(send(text))}
265
299
  />
@@ -292,6 +326,7 @@
292
326
  streaming={session.streaming}
293
327
  {sandboxOrigin}
294
328
  fileUrl={(id) => client.fileUrl(id)}
329
+ loadFile={client.fetchFile ? (id) => client.fetchFile!(id) : undefined}
295
330
  {toolLabels}
296
331
  {onnavigate}
297
332
  onopenartifact={(id) => {
@@ -312,13 +347,13 @@
312
347
  : undefined}
313
348
  oncontinue={() => {
314
349
  const answer = session.current;
315
- if (answer) void session.continue(answer.requestId);
350
+ if (answer) thenFocus(session.continue(answer.requestId));
316
351
  }}
317
352
  empty={emptyState}
318
353
  />
319
354
  <Composer
320
355
  bind:this={composer}
321
- bind:value={draft}
356
+ bind:value={session.draft}
322
357
  bind:files
323
358
  disabled={session.status === 'loading'}
324
359
  streaming={session.streaming}
@@ -2,9 +2,18 @@ import type { AgentApiClient } from '@classic-homes/agent-client';
2
2
  import type { AgentKey, AgentSummary, ClientKind, DocumentContext } from '@classic-homes/agent-contracts';
3
3
  import type { BackendMode } from '@classic-homes/agent-contracts';
4
4
  import type { ComposerPrompt } from '../composer-prompts.js';
5
+ import { ChatSession } from '../session.svelte.js';
5
6
  import type { WelcomeSuggestion } from '../welcome.js';
6
7
  interface Props {
7
8
  client: AgentApiClient;
9
+ /**
10
+ * A session the host owns, so it survives this component being unmounted and mounted
11
+ * elsewhere (launcher, floating window, dock) without dropping a running answer. The host
12
+ * then configures it itself: `mode`, `agent`, `clientKind`, `getContext`,
13
+ * `onconversationstarted`, `ontoolresult` and `onturnend` here are ignored. Without it the
14
+ * component creates its own, as before.
15
+ */
16
+ session?: ChatSession;
8
17
  /** Sandbox origin for artifacts and MCP-UI (agent-api `/frame`). */
9
18
  sandboxOrigin: string;
10
19
  conversationId?: string | null;
@@ -20,7 +29,8 @@ interface Props {
20
29
  /** The agent of this conversation changed (picked, or learned from a loaded conversation). */
21
30
  onagentchange?: (agent: AgentKey) => void;
22
31
  clientKind?: ClientKind;
23
- variant?: 'page' | 'panel';
32
+ /** `float` is the detached window: laid out like the panel. */
33
+ variant?: 'page' | 'panel' | 'float';
24
34
  getContext?: () => DocumentContext | null | undefined;
25
35
  onconversationstarted?: (conversationId: string) => void;
26
36
  ontoolresult?: (name: string, status: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"AgentChat.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AgentChat.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EACR,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,eAAe,EAEhB,MAAM,gCAAgC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAclE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAQrD,UAAU,KAAK;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,yEAAyE;IACzE,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC1C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IACtD,qBAAqB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,gFAAgF;IAChF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,iGAAiG;IACjG,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6FAA6F;IAC7F,MAAM,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACnC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqPH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AgentChat.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AgentChat.svelte.ts"],"names":[],"mappings":"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"}
@@ -115,8 +115,10 @@
115
115
  <div class="min-h-0 flex-1 overflow-auto p-3">
116
116
  {#if selected.mediaType === 'text/markdown'}
117
117
  <Markdown html={markdownHtml} />
118
- {:else if selected.mediaType === 'application/vnd.ant.code' || (selected.mediaType === 'application/vnd.ant.mermaid' && selected.open)}
119
- <!-- Mermaid renders once the artifact is complete; a partial diagram is a parse error. -->
118
+ {:else if selected.mediaType === 'application/vnd.ant.code' || selected.open}
119
+ <!-- Framed artifacts (html, svg, react, mermaid) render once complete: the source shows
120
+ while it streams, so the sandbox is not reloaded on every delta (and a partial
121
+ diagram is a parse error). -->
120
122
  <pre class="overflow-auto whitespace-pre font-mono text-[13px] leading-relaxed"><code
121
123
  >{selected.text}</code
122
124
  ></pre>
@@ -7,8 +7,9 @@
7
7
  import { Alert, Button, Icon } from '@classic-homes/theme-svelte';
8
8
  import type { TurnRating } from '@classic-homes/agent-contracts';
9
9
  import type { AssistantMessage } from '../reducer.js';
10
- import { errorTitle } from '../reducer.js';
10
+ import { errorTitle, isContinuable } from '../reducer.js';
11
11
  import { parseMarkdown } from '../markdown.js';
12
+ import { THINKING_PHRASE_MS, pickThinkingPhrase } from '../thinking.js';
12
13
  import ArtifactChip from './ArtifactChip.svelte';
13
14
  import Markdown from './Markdown.svelte';
14
15
  import MessageActions from './MessageActions.svelte';
@@ -24,6 +25,8 @@
24
25
  last?: boolean;
25
26
  sandboxOrigin: string;
26
27
  fileUrl: (fileId: string) => string;
28
+ /** Fetch a file with the host's credentials (blob URLs); without it files link to `fileUrl`. */
29
+ loadFile?: (fileId: string) => Promise<Blob>;
27
30
  toolLabels?: Record<string, string>;
28
31
  onnavigate?: (href: string) => void;
29
32
  onopenartifact?: (artifactId: string) => void;
@@ -44,6 +47,7 @@
44
47
  last = false,
45
48
  sandboxOrigin,
46
49
  fileUrl,
50
+ loadFile,
47
51
  toolLabels,
48
52
  onnavigate,
49
53
  onopenartifact,
@@ -98,8 +102,16 @@
98
102
  !message.tools.some((t) => !t.status) &&
99
103
  !message.artifacts.some((a) => a.open),
100
104
  );
101
- // Continue carries on the text so far; a reply stopped before any text is retried instead.
102
- const canContinue = $derived(last && !!oncontinue && !!message.text);
105
+ // A homebuilding verb while there is nothing to show yet, changed every few seconds.
106
+ let phrase = $state(pickThinkingPhrase());
107
+ $effect(() => {
108
+ if (!thinking) return;
109
+ const timer = setInterval(() => (phrase = pickThinkingPhrase(phrase)), THINKING_PHRASE_MS);
110
+ return () => clearInterval(timer);
111
+ });
112
+ // Continue carries on the text so far of an answer cut short or stopped; a reply stopped before
113
+ // any text, or one that failed or lost its connection, is retried instead.
114
+ const canContinue = $derived(last && !!oncontinue && isContinuable(message));
103
115
  </script>
104
116
 
105
117
  <div class="group/message flex gap-3 px-4 py-2" data-role="assistant" data-status={message.status}>
@@ -120,17 +132,15 @@
120
132
  {/if}
121
133
  {#if html}<Markdown {html} streaming={streaming && !!message.text} onclick={handleClick} />{/if}
122
134
  {#if thinking}
123
- <div class="flex items-center gap-2 text-sm text-muted-foreground" role="status">
124
- <span class="flex gap-1" aria-hidden="true">
125
- <span class="h-1.5 w-1.5 rounded-full bg-current motion-safe:animate-bounce"></span>
126
- <span
127
- class="h-1.5 w-1.5 rounded-full bg-current motion-safe:animate-bounce [animation-delay:150ms]"
128
- ></span>
129
- <span
130
- class="h-1.5 w-1.5 rounded-full bg-current motion-safe:animate-bounce [animation-delay:300ms]"
131
- ></span>
132
- </span>
133
- Thinking
135
+ <!-- aria-live off: the phrase changes for the eye; announcing each one would be noise. -->
136
+ <div
137
+ class="flex items-center gap-2 text-sm italic text-muted-foreground"
138
+ role="status"
139
+ aria-live="off"
140
+ data-agent-thinking
141
+ >
142
+ <span>{phrase}</span>
143
+ <span class="thinking-loader" aria-hidden="true"></span>
134
144
  </div>
135
145
  {/if}
136
146
  {#if message.artifacts.length}
@@ -143,7 +153,7 @@
143
153
  {#each message.ui as resource (resource.callId + resource.uri)}
144
154
  <UiFrame {resource} {sandboxOrigin} {onprompt} {ontool} {onnotify} />
145
155
  {/each}
146
- {#if message.files.length}<MessageFiles files={message.files} {fileUrl} />{/if}
156
+ {#if message.files.length}<MessageFiles files={message.files} {fileUrl} {loadFile} />{/if}
147
157
  {#if message.proposals.length}
148
158
  <div class="flex flex-col gap-1.5">
149
159
  {#each message.proposals as operation (operation.operationId)}
@@ -158,7 +168,10 @@
158
168
  <strong class="font-semibold">{errorTitle(message.error.code)}.</strong>
159
169
  {message.error.message}
160
170
  </p>
161
- {#if message.error.retryable && onretry}
171
+ {#if canContinue}
172
+ <!-- A continuation failed: the text so far is kept and can still be continued. -->
173
+ <Button variant="outline" size="sm" onclick={oncontinue}>Continue</Button>
174
+ {:else if message.error.retryable && onretry}
162
175
  <Button variant="outline" size="sm" onclick={onretry}>
163
176
  <Icon name="refresh" size={14} /> Try again
164
177
  </Button>
@@ -170,6 +183,8 @@
170
183
  <Icon name="stop" size={14} /> Stopped
171
184
  {#if canContinue}
172
185
  <Button variant="link" size="sm" class="h-auto p-0" onclick={oncontinue}>Continue</Button>
186
+ {:else if last && !message.text && onretry}
187
+ <Button variant="link" size="sm" class="h-auto p-0" onclick={onretry}>Try again</Button>
173
188
  {/if}
174
189
  </div>
175
190
  {:else if message.status === 'incomplete'}
@@ -195,3 +210,30 @@
195
210
  {/if}
196
211
  </div>
197
212
  </div>
213
+
214
+ <style>
215
+ /* Six blocks with one lit, stepping along (the Classic Chat widget's loader). Only custom
216
+ properties and an animation here: nothing a host's base styles compete with. */
217
+ .thinking-loader {
218
+ display: inline-block;
219
+ width: 60px;
220
+ height: 10px;
221
+ flex-shrink: 0;
222
+ -webkit-mask: linear-gradient(90deg, #000 70%, #0000 0) left / 20% 100%;
223
+ mask: linear-gradient(90deg, #000 70%, #0000 0) left / 20% 100%;
224
+ background: linear-gradient(hsl(var(--primary)) 0 0) left -25% top 0 / 20% 100% no-repeat
225
+ hsl(var(--muted-foreground) / 0.25);
226
+ animation: thinking-slide 1s infinite steps(6);
227
+ }
228
+ @keyframes thinking-slide {
229
+ 100% {
230
+ background-position: right -25% top 0;
231
+ }
232
+ }
233
+ @media (prefers-reduced-motion: reduce) {
234
+ .thinking-loader {
235
+ animation: none;
236
+ background-position: left 37.5% top 0;
237
+ }
238
+ }
239
+ </style>
@@ -7,6 +7,8 @@ interface Props {
7
7
  last?: boolean;
8
8
  sandboxOrigin: string;
9
9
  fileUrl: (fileId: string) => string;
10
+ /** Fetch a file with the host's credentials (blob URLs); without it files link to `fileUrl`. */
11
+ loadFile?: (fileId: string) => Promise<Blob>;
10
12
  toolLabels?: Record<string, string>;
11
13
  onnavigate?: (href: string) => void;
12
14
  onopenartifact?: (artifactId: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantMessage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AssistantMessage.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAYpD,UAAU,KAAK;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA4JH;;;GAGG;AACH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"AssistantMessage.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AssistantMessage.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAapD,UAAU,KAAK;IACb,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AA0KH;;;GAGG;AACH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -34,6 +34,8 @@
34
34
  onerror?: (message: string) => void;
35
35
  /** Host shortcuts for the "Keyboard shortcuts" dialog, e.g. opening the panel. */
36
36
  shortcuts?: ChatShortcut[];
37
+ /** Whether the chat offers saved prompts (`AgentChat`'s `prompts`); false hides the `/` shortcut. */
38
+ savedPrompts?: boolean;
37
39
  /** Extra controls before the menu. */
38
40
  actions?: Snippet;
39
41
  class?: string;
@@ -49,6 +51,7 @@
49
51
  ondelete,
50
52
  onerror,
51
53
  shortcuts,
54
+ savedPrompts = true,
52
55
  actions,
53
56
  class: className,
54
57
  }: Props = $props();
@@ -131,4 +134,4 @@
131
134
  </header>
132
135
 
133
136
  <ConversationDialogs bind:renaming bind:deleting {onrename} {ondelete} {onerror} {returnFocus} />
134
- <ShortcutsDialog bind:open={showingShortcuts} {shortcuts} {returnFocus} />
137
+ <ShortcutsDialog bind:open={showingShortcuts} {shortcuts} {returnFocus} {savedPrompts} />
@@ -15,6 +15,8 @@ interface Props {
15
15
  onerror?: (message: string) => void;
16
16
  /** Host shortcuts for the "Keyboard shortcuts" dialog, e.g. opening the panel. */
17
17
  shortcuts?: ChatShortcut[];
18
+ /** Whether the chat offers saved prompts (`AgentChat`'s `prompts`); false hides the `/` shortcut. */
19
+ savedPrompts?: boolean;
18
20
  /** Extra controls before the menu. */
19
21
  actions?: Snippet;
20
22
  class?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatHeader.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/ChatHeader.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKlD,UAAU,KAAK;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,kFAAkF;IAClF,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuFH;;;;;GAKG;AACH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ChatHeader.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/ChatHeader.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKlD,UAAU,KAAK;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,kFAAkF;IAClF,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,qGAAqG;IACrG,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwFH;;;;;GAKG;AACH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}