@timbal-ai/timbal-react 0.2.2 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -30,15 +30,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- ActionBarPrimitive: () => import_react13.ActionBarPrimitive,
33
+ ARTIFACT_AGENT_INSTRUCTIONS: () => ARTIFACT_AGENT_INSTRUCTIONS,
34
+ ARTIFACT_FENCE_LANGUAGES: () => ARTIFACT_FENCE_LANGUAGES,
35
+ ActionBarPrimitive: () => import_react26.ActionBarPrimitive,
36
+ ArtifactCard: () => ArtifactCard,
37
+ ArtifactRegistryProvider: () => ArtifactRegistryProvider,
38
+ ArtifactView: () => ArtifactView,
39
+ AssistantRuntimeProvider: () => import_react26.AssistantRuntimeProvider,
34
40
  AuthGuard: () => AuthGuard,
35
41
  Avatar: () => Avatar,
36
42
  AvatarFallback: () => AvatarFallback,
37
43
  AvatarImage: () => AvatarImage,
38
44
  Button: () => Button,
45
+ ChartArtifactView: () => ChartArtifactView,
46
+ Composer: () => Composer,
39
47
  ComposerAddAttachment: () => ComposerAddAttachment,
40
48
  ComposerAttachments: () => ComposerAttachments,
41
- ComposerPrimitive: () => import_react13.ComposerPrimitive,
49
+ ComposerPrimitive: () => import_react26.ComposerPrimitive,
50
+ DEFAULT_UPLOAD_ACCEPT: () => DEFAULT_UPLOAD_ACCEPT,
42
51
  Dialog: () => Dialog,
43
52
  DialogClose: () => DialogClose,
44
53
  DialogContent: () => DialogContent,
@@ -46,37 +55,72 @@ __export(index_exports, {
46
55
  DialogPortal: () => DialogPortal,
47
56
  DialogTitle: () => DialogTitle,
48
57
  DialogTrigger: () => DialogTrigger,
58
+ HtmlArtifactView: () => HtmlArtifactView,
59
+ JsonArtifactView: () => JsonArtifactView,
49
60
  MarkdownText: () => MarkdownText,
50
- MessagePrimitive: () => import_react13.MessagePrimitive,
61
+ MessagePrimitive: () => import_react26.MessagePrimitive,
62
+ QuestionArtifactView: () => QuestionArtifactView,
51
63
  SessionProvider: () => SessionProvider,
52
64
  Shimmer: () => Shimmer,
65
+ Suggestions: () => Suggestions,
53
66
  SyntaxHighlighter: () => syntax_highlighter_default,
67
+ TableArtifactView: () => TableArtifactView,
54
68
  Thread: () => Thread,
55
- ThreadPrimitive: () => import_react13.ThreadPrimitive,
69
+ ThreadPrimitive: () => import_react26.ThreadPrimitive,
56
70
  TimbalChat: () => TimbalChat,
71
+ TimbalChatShell: () => TimbalChatShell,
57
72
  TimbalRuntimeProvider: () => TimbalRuntimeProvider,
73
+ ToolArtifactFallback: () => ToolArtifactFallback,
58
74
  ToolFallback: () => ToolFallback,
59
75
  Tooltip: () => Tooltip,
60
76
  TooltipContent: () => TooltipContent,
61
77
  TooltipIconButton: () => TooltipIconButton,
62
78
  TooltipProvider: () => TooltipProvider,
63
79
  TooltipTrigger: () => TooltipTrigger,
80
+ UiArtifactView: () => UiArtifactView,
81
+ UiCustomNodeRegistryProvider: () => UiCustomNodeRegistryProvider,
82
+ UiEventProvider: () => UiEventProvider,
83
+ UiNodeView: () => UiNodeView,
64
84
  UserMessageAttachments: () => UserMessageAttachments,
85
+ WorkforceSelector: () => WorkforceSelector,
65
86
  authFetch: () => authFetch,
66
87
  buttonVariants: () => buttonVariants,
67
88
  clearTokens: () => clearTokens,
68
89
  cn: () => cn,
90
+ createDefaultAttachmentAdapter: () => createDefaultAttachmentAdapter,
91
+ createUploadAttachmentAdapter: () => createUploadAttachmentAdapter,
92
+ defaultArtifactRenderers: () => defaultArtifactRenderers,
69
93
  fetchCurrentUser: () => fetchCurrentUser,
94
+ findMarkdownArtifacts: () => findMarkdownArtifacts,
70
95
  getAccessToken: () => getAccessToken,
96
+ getPath: () => getPath,
71
97
  getRefreshToken: () => getRefreshToken,
98
+ isArtifact: () => isArtifact,
99
+ isArtifactFenceLanguage: () => isArtifactFenceLanguage,
100
+ isUiBinding: () => isUiBinding,
101
+ parseArtifactFromToolResult: () => parseArtifactFromToolResult,
102
+ parseSSELine: () => import_timbal_sdk2.parseSSELine,
72
103
  refreshAccessToken: () => refreshAccessToken,
104
+ resolveAttachmentAdapter: () => resolveAttachmentAdapter,
105
+ resolveBindable: () => resolveBindable,
73
106
  setAccessToken: () => setAccessToken,
107
+ setPath: () => setPath,
74
108
  setRefreshToken: () => setRefreshToken,
75
- useComposerRuntime: () => import_react13.useComposerRuntime,
76
- useMessageRuntime: () => import_react13.useMessageRuntime,
109
+ splitMarkdownByArtifacts: () => splitMarkdownByArtifacts,
110
+ useArtifactRegistry: () => useArtifactRegistry,
111
+ useComposerRuntime: () => import_react26.useComposerRuntime,
112
+ useMessageRuntime: () => import_react26.useMessageRuntime,
113
+ useResolvedSuggestions: () => useResolvedSuggestions,
77
114
  useSession: () => useSession,
78
- useThread: () => import_react13.useThread,
79
- useThreadRuntime: () => import_react13.useThreadRuntime
115
+ useThread: () => import_react26.useThread,
116
+ useThreadRuntime: () => import_react26.useThreadRuntime,
117
+ useTimbalRuntime: () => useTimbalRuntime,
118
+ useTimbalStream: () => useTimbalStream,
119
+ useUiCustomNodeRegistry: () => useUiCustomNodeRegistry,
120
+ useUiDispatch: () => useUiDispatch,
121
+ useUiEventEmitter: () => useUiEventEmitter,
122
+ useUiState: () => useUiState,
123
+ useWorkforces: () => useWorkforces
80
124
  });
81
125
  module.exports = __toCommonJS(index_exports);
82
126
 
@@ -166,13 +210,506 @@ var fetchCurrentUser = async () => {
166
210
  }
167
211
  };
168
212
 
213
+ // src/artifacts/types.ts
214
+ function isArtifact(value) {
215
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
216
+ }
217
+
218
+ // src/runtime/reducer.ts
219
+ function createReducerState() {
220
+ return { parts: [], toolIndexById: /* @__PURE__ */ new Map() };
221
+ }
222
+ function reduceSseEvent(state, event) {
223
+ switch (event.type) {
224
+ case "DELTA":
225
+ return reduceDelta(state, event.item);
226
+ case "OUTPUT": {
227
+ const path = event.path;
228
+ const isNested = typeof path === "string" && path.includes(".");
229
+ if (!isNested) {
230
+ const errorMessage = readErrorMessage(event);
231
+ if (errorMessage) {
232
+ state.parts.push({ type: "text", text: `**Error:** ${errorMessage}` });
233
+ return true;
234
+ }
235
+ }
236
+ if (isNested) {
237
+ return reduceNestedOutput(
238
+ state,
239
+ path,
240
+ event.output
241
+ );
242
+ }
243
+ return reduceOutput(
244
+ state,
245
+ event.output
246
+ );
247
+ }
248
+ default:
249
+ return false;
250
+ }
251
+ }
252
+ function reduceDelta(state, item) {
253
+ if (!item) return false;
254
+ if (item.type === "text_delta" && typeof item.text_delta === "string") {
255
+ lastTextPart(state).text += item.text_delta;
256
+ return true;
257
+ }
258
+ if (item.type === "thinking_delta" && typeof item.thinking_delta === "string") {
259
+ lastThinkingPart(state).text += item.thinking_delta;
260
+ return true;
261
+ }
262
+ if (item.type === "tool_use") {
263
+ const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
264
+ const inputStr = stringifyInput(item.input);
265
+ const part = {
266
+ type: "tool-call",
267
+ toolCallId,
268
+ toolName: item.name || "unknown",
269
+ argsText: inputStr,
270
+ status: "running"
271
+ };
272
+ state.parts.push(part);
273
+ state.toolIndexById.set(toolCallId, state.parts.length - 1);
274
+ return true;
275
+ }
276
+ if (item.type === "tool_use_delta") {
277
+ const idx = state.toolIndexById.get(item.id);
278
+ if (idx !== void 0 && typeof item.input_delta === "string") {
279
+ state.parts[idx].argsText += item.input_delta;
280
+ return true;
281
+ }
282
+ }
283
+ return false;
284
+ }
285
+ function reduceNestedOutput(state, path, output) {
286
+ if (!output || typeof output !== "object") return false;
287
+ if (!Array.isArray(output.content) && isArtifact(output)) {
288
+ const toolName = toolNameFromPath(path);
289
+ if (toolName && attachToolResult(state, { toolName, result: output })) {
290
+ return true;
291
+ }
292
+ }
293
+ return reduceOutput(state, output, { toolResultsOnly: true, allowOrphan: false });
294
+ }
295
+ function reduceOutput(state, output, options) {
296
+ if (!output) return false;
297
+ if (typeof output === "string") {
298
+ if (state.parts.length === 0) {
299
+ state.parts.push({ type: "text", text: output });
300
+ return true;
301
+ }
302
+ return false;
303
+ }
304
+ if (Array.isArray(output.content)) {
305
+ let changed = false;
306
+ const blocks = output.content;
307
+ for (const block of blocks) {
308
+ if (block.type === "tool_use") {
309
+ if (!options?.toolResultsOnly && recordToolUse(state, block)) {
310
+ changed = true;
311
+ }
312
+ } else if (block.type === "tool_result") {
313
+ if (recordToolResult(state, block, options)) changed = true;
314
+ } else if (!options?.toolResultsOnly) {
315
+ if (block.type === "text" && typeof block.text === "string" && !lastTextPart(state).text) {
316
+ lastTextPart(state).text = block.text;
317
+ changed = true;
318
+ } else if (block.type === "thinking" && typeof block.thinking === "string" && !lastThinkingPart(state).text) {
319
+ lastThinkingPart(state).text = block.thinking;
320
+ changed = true;
321
+ }
322
+ }
323
+ }
324
+ return changed;
325
+ }
326
+ if (state.parts.length === 0) {
327
+ state.parts.push({ type: "text", text: JSON.stringify(output) });
328
+ return true;
329
+ }
330
+ return false;
331
+ }
332
+ function recordToolUse(state, block) {
333
+ const id = block.id || `tool-${crypto.randomUUID()}`;
334
+ if (state.toolIndexById.has(id)) return false;
335
+ const inputStr = stringifyInput(block.input);
336
+ const part = {
337
+ type: "tool-call",
338
+ toolCallId: id,
339
+ toolName: block.name || "unknown",
340
+ argsText: inputStr,
341
+ status: "running"
342
+ };
343
+ state.parts.push(part);
344
+ state.toolIndexById.set(id, state.parts.length - 1);
345
+ return true;
346
+ }
347
+ function recordToolResult(state, block, options) {
348
+ const allowOrphan = options?.allowOrphan !== false;
349
+ const id = block.id || block.tool_use_id || "";
350
+ const { result, resultText } = parseToolResultContent(block.content);
351
+ const toolName = block.name || void 0;
352
+ if (id) {
353
+ const idx = state.toolIndexById.get(id);
354
+ if (idx !== void 0) {
355
+ const part2 = state.parts[idx];
356
+ part2.result = result;
357
+ if (resultText) part2.resultText = resultText;
358
+ part2.status = "complete";
359
+ return true;
360
+ }
361
+ if (!allowOrphan) return false;
362
+ }
363
+ if (!id && toolName && attachToolResult(state, {
364
+ toolName,
365
+ result,
366
+ resultText
367
+ })) {
368
+ return true;
369
+ }
370
+ if (!id || !allowOrphan) return false;
371
+ const part = {
372
+ type: "tool-call",
373
+ toolCallId: id,
374
+ toolName: toolName || "unknown",
375
+ argsText: "",
376
+ result,
377
+ resultText,
378
+ status: "complete"
379
+ };
380
+ state.parts.push(part);
381
+ state.toolIndexById.set(id, state.parts.length - 1);
382
+ return true;
383
+ }
384
+ function toolNameFromPath(path) {
385
+ const segment = path.split(".").pop();
386
+ return segment && segment !== "agent" && segment !== "llm" ? segment : null;
387
+ }
388
+ function attachToolResult(state, {
389
+ toolCallId,
390
+ toolName,
391
+ result,
392
+ resultText
393
+ }) {
394
+ if (toolCallId) {
395
+ const idx = state.toolIndexById.get(toolCallId);
396
+ if (idx !== void 0) {
397
+ const part = state.parts[idx];
398
+ part.result = result;
399
+ if (resultText) part.resultText = resultText;
400
+ part.status = "complete";
401
+ return true;
402
+ }
403
+ }
404
+ if (toolName) {
405
+ for (let i = state.parts.length - 1; i >= 0; i--) {
406
+ const part = state.parts[i];
407
+ if (part.type === "tool-call" && part.toolName === toolName && part.result === void 0) {
408
+ part.result = result;
409
+ if (resultText) part.resultText = resultText;
410
+ part.status = "complete";
411
+ return true;
412
+ }
413
+ }
414
+ }
415
+ return false;
416
+ }
417
+ function parseToolResultContent(content) {
418
+ if (typeof content === "string") {
419
+ return { result: content, resultText: content };
420
+ }
421
+ if (!Array.isArray(content)) {
422
+ return { result: content };
423
+ }
424
+ const textChunks = [];
425
+ for (const item of content) {
426
+ if (typeof item === "string") {
427
+ textChunks.push(item);
428
+ } else if (item && typeof item === "object") {
429
+ const obj = item;
430
+ if (obj.type === "text" && typeof obj.text === "string") {
431
+ textChunks.push(obj.text);
432
+ } else if (obj.type === "thinking" && typeof obj.thinking === "string") {
433
+ textChunks.push(obj.thinking);
434
+ }
435
+ }
436
+ }
437
+ return {
438
+ result: content,
439
+ resultText: textChunks.length > 0 ? textChunks.join("\n") : void 0
440
+ };
441
+ }
442
+ function readErrorMessage(event) {
443
+ const status = event.status;
444
+ const isErrorStatus = status?.code === "error";
445
+ const error = event.error;
446
+ let type = null;
447
+ let message = null;
448
+ if (isErrorStatus && typeof status?.message === "string" && status.message.length > 0) {
449
+ message = status.message;
450
+ }
451
+ if (!message && typeof error === "string" && error.length > 0) {
452
+ message = error;
453
+ } else if (error && typeof error === "object") {
454
+ const obj = error;
455
+ if (typeof obj.type === "string" && obj.type.length > 0) {
456
+ type = obj.type;
457
+ }
458
+ if (!message && typeof obj.message === "string" && obj.message.length > 0) {
459
+ message = obj.message;
460
+ }
461
+ }
462
+ if (!message && !isErrorStatus) return null;
463
+ if (!message) return "The agent failed to generate a response.";
464
+ const compact = compactError(message);
465
+ return type ? `${type}: ${compact}` : compact;
466
+ }
467
+ var ERROR_MAX_CHARS = 480;
468
+ function compactError(message) {
469
+ const trimmed = message.split(/\n\s*Traceback \(most recent call last\):/u)[0].trim();
470
+ if (trimmed.length <= ERROR_MAX_CHARS) return trimmed;
471
+ return `${trimmed.slice(0, ERROR_MAX_CHARS).trimEnd()}\u2026`;
472
+ }
473
+ function stringifyInput(input) {
474
+ if (input === void 0 || input === null) return "{}";
475
+ return typeof input === "string" ? input : JSON.stringify(input);
476
+ }
477
+ function lastTextPart(state) {
478
+ const last = state.parts[state.parts.length - 1];
479
+ if (last?.type === "text") return last;
480
+ const next = { type: "text", text: "" };
481
+ state.parts.push(next);
482
+ return next;
483
+ }
484
+ function lastThinkingPart(state) {
485
+ const last = state.parts[state.parts.length - 1];
486
+ if (last?.type === "thinking") return last;
487
+ const next = { type: "thinking", text: "" };
488
+ state.parts.push(next);
489
+ return next;
490
+ }
491
+
492
+ // src/runtime/attachments.ts
493
+ async function extractAttachment(attachment) {
494
+ const file = attachment.file;
495
+ let src = null;
496
+ let contentType;
497
+ let name = attachment.name ?? file?.name;
498
+ const content = attachment.content;
499
+ if (content) {
500
+ for (const block of content) {
501
+ if (block.type === "image" && typeof block.image === "string") {
502
+ src = block.image;
503
+ if (typeof block.mimeType === "string") {
504
+ contentType = block.mimeType;
505
+ }
506
+ break;
507
+ }
508
+ if (block.type === "file" && typeof block.data === "string") {
509
+ src = block.data;
510
+ if (typeof block.mimeType === "string") contentType = block.mimeType;
511
+ break;
512
+ }
513
+ }
514
+ }
515
+ if (src === null && file) {
516
+ src = await fileToDataUrl(file);
517
+ if (!contentType) contentType = file.type || void 0;
518
+ if (!name) name = file.name;
519
+ }
520
+ if (!src) return null;
521
+ if (!contentType) contentType = mimeFromDataUrl(src);
522
+ const rawType = String(attachment.type ?? "file");
523
+ const type = rawType === "image" || rawType === "document" ? rawType : "file";
524
+ return {
525
+ id: attachment.id ?? crypto.randomUUID(),
526
+ type,
527
+ ...name !== void 0 ? { name } : {},
528
+ ...contentType !== void 0 ? { contentType } : {},
529
+ dataUrl: src
530
+ };
531
+ }
532
+ function fileToDataUrl(file) {
533
+ return new Promise((resolve, reject) => {
534
+ const reader = new FileReader();
535
+ reader.onload = () => resolve(reader.result);
536
+ reader.onerror = () => reject(reader.error);
537
+ reader.readAsDataURL(file);
538
+ });
539
+ }
540
+ function mimeFromDataUrl(dataUrl) {
541
+ const match = /^data:([^;,]+)[;,]/.exec(dataUrl);
542
+ return match?.[1];
543
+ }
544
+ function buildPromptBody({
545
+ input,
546
+ attachments,
547
+ parentId
548
+ }) {
549
+ const context = { parent_id: parentId };
550
+ const files = attachments ?? [];
551
+ if (files.length === 0) {
552
+ return { prompt: input, context };
553
+ }
554
+ const parts = [];
555
+ if (input) parts.push({ type: "text", text: input });
556
+ for (const attachment of files) {
557
+ parts.push({ type: "file", file: attachment.dataUrl });
558
+ }
559
+ return { prompt: parts, context };
560
+ }
561
+
562
+ // src/runtime/upload-adapter.ts
563
+ var DEFAULT_UPLOAD_ACCEPT = "image/*,application/pdf,text/*,.md,.json,.csv,.tsv,.xlsx,.docx";
564
+ function createDefaultAttachmentAdapter({
565
+ baseUrl = "",
566
+ uploadUrl,
567
+ fetch: fetchFn = authFetch,
568
+ accept = DEFAULT_UPLOAD_ACCEPT
569
+ } = {}) {
570
+ const base = baseUrl.replace(/\/$/, "");
571
+ const resolvedUploadUrl = uploadUrl ?? `${base}/files/upload`;
572
+ return {
573
+ accept,
574
+ async add({ file }) {
575
+ const isImage = file.type.startsWith("image/");
576
+ const pending = {
577
+ id: crypto.randomUUID(),
578
+ type: isImage ? "image" : "file",
579
+ name: file.name,
580
+ contentType: file.type || "application/octet-stream",
581
+ file,
582
+ status: { type: "requires-action", reason: "composer-send" }
583
+ };
584
+ return pending;
585
+ },
586
+ async send(attachment) {
587
+ const fd = new FormData();
588
+ fd.append("file", attachment.file);
589
+ const res = await fetchFn(resolvedUploadUrl, { method: "POST", body: fd });
590
+ if (!res.ok) {
591
+ const detail = await res.text().catch(() => "");
592
+ throw new Error(
593
+ `Attachment upload failed (${res.status}): ${detail || res.statusText}`
594
+ );
595
+ }
596
+ const remoteUrl = await readUploadedUrl(res);
597
+ const mime = attachment.contentType ?? "application/octet-stream";
598
+ const filename = attachment.name;
599
+ const complete = {
600
+ ...attachment,
601
+ status: { type: "complete" },
602
+ content: mime.startsWith("image/") ? [{ type: "image", image: remoteUrl, filename }] : [{ type: "file", data: remoteUrl, mimeType: mime, filename }]
603
+ };
604
+ return complete;
605
+ },
606
+ async remove() {
607
+ }
608
+ };
609
+ }
610
+ var createUploadAttachmentAdapter = createDefaultAttachmentAdapter;
611
+ async function readUploadedUrl(res) {
612
+ const contentType = res.headers.get("content-type") ?? "";
613
+ if (contentType.includes("application/json")) {
614
+ const data = await res.json();
615
+ const candidate = data.url ?? data.signed_url ?? data.id;
616
+ if (typeof candidate === "string" && candidate.length > 0) {
617
+ return candidate;
618
+ }
619
+ throw new Error(
620
+ "Attachment upload response did not include a `url`, `signed_url`, or `id` field."
621
+ );
622
+ }
623
+ const text = (await res.text()).trim();
624
+ if (!text) {
625
+ throw new Error("Attachment upload response was empty.");
626
+ }
627
+ return text;
628
+ }
629
+
630
+ // src/runtime/resolve-attachments.ts
631
+ function isAttachmentAdapter(value) {
632
+ return typeof value === "object" && value !== null && "accept" in value && typeof value.add === "function" && typeof value.send === "function" && typeof value.remove === "function";
633
+ }
634
+ function resolveAttachmentAdapter(attachments, options = {}) {
635
+ const baseUrl = options.baseUrl ?? "/api";
636
+ const legacyUploadUrl = options.uploadUrl;
637
+ const legacyAccept = options.accept;
638
+ if (attachments === null) return void 0;
639
+ const legacyEnables = legacyUploadUrl !== void 0 || legacyAccept !== void 0;
640
+ if (attachments === void 0) {
641
+ if (!legacyEnables) return void 0;
642
+ return createDefaultAttachmentAdapter({
643
+ baseUrl,
644
+ fetch: options.fetch,
645
+ uploadUrl: legacyUploadUrl,
646
+ accept: legacyAccept
647
+ });
648
+ }
649
+ if (attachments === true) {
650
+ return createDefaultAttachmentAdapter({
651
+ baseUrl,
652
+ fetch: options.fetch,
653
+ uploadUrl: legacyUploadUrl,
654
+ accept: legacyAccept
655
+ });
656
+ }
657
+ if (isAttachmentAdapter(attachments)) return attachments;
658
+ const config = attachments;
659
+ return createDefaultAttachmentAdapter({
660
+ baseUrl,
661
+ fetch: options.fetch,
662
+ uploadUrl: config.uploadUrl ?? legacyUploadUrl,
663
+ accept: config.accept ?? legacyAccept
664
+ });
665
+ }
666
+
169
667
  // src/runtime/provider.tsx
170
668
  var import_jsx_runtime = require("react/jsx-runtime");
171
- var convertMessage = (message) => ({
172
- role: message.role,
173
- content: message.content,
174
- id: message.id
175
- });
669
+ function projectAttachment(attachment) {
670
+ const filename = attachment.name ?? "attachment";
671
+ const mimeType = attachment.contentType ?? "application/octet-stream";
672
+ if (attachment.type === "image") {
673
+ return {
674
+ id: attachment.id,
675
+ type: "image",
676
+ name: filename,
677
+ contentType: mimeType,
678
+ status: { type: "complete" },
679
+ content: [{ type: "image", image: attachment.dataUrl, filename }]
680
+ };
681
+ }
682
+ return {
683
+ id: attachment.id,
684
+ type: attachment.type,
685
+ name: filename,
686
+ contentType: mimeType,
687
+ status: { type: "complete" },
688
+ content: [
689
+ { type: "file", data: attachment.dataUrl, mimeType, filename }
690
+ ]
691
+ };
692
+ }
693
+ var convertMessage = (message) => {
694
+ const content = message.content.map((part) => {
695
+ if (part.type === "text") return { type: "text", text: part.text };
696
+ if (part.type === "thinking") return { type: "reasoning", text: part.text };
697
+ return {
698
+ type: "tool-call",
699
+ toolCallId: part.toolCallId,
700
+ toolName: part.toolName,
701
+ argsText: part.argsText,
702
+ ...part.result !== void 0 ? { result: part.result } : {}
703
+ };
704
+ });
705
+ const attachments = message.attachments && message.attachments.length > 0 ? message.attachments.map(projectAttachment) : void 0;
706
+ return {
707
+ role: message.role,
708
+ content,
709
+ id: message.id,
710
+ ...attachments ? { attachments } : {}
711
+ };
712
+ };
176
713
  function findParentId(messages, beforeIndex) {
177
714
  const slice = beforeIndex !== void 0 ? messages.slice(0, beforeIndex) : messages;
178
715
  for (let i = slice.length - 1; i >= 0; i--) {
@@ -180,18 +717,18 @@ function findParentId(messages, beforeIndex) {
180
717
  }
181
718
  return null;
182
719
  }
183
- function isTopLevelStart(event) {
184
- return event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".");
185
- }
186
720
  function getTextFromMessage(message) {
187
721
  const part = message.content.find((c) => c.type === "text");
188
- return part?.type === "text" ? part.text : null;
722
+ return part?.type === "text" ? part.text : "";
189
723
  }
190
- function TimbalRuntimeProvider({
724
+ function getAttachmentsFromMessage(message) {
725
+ return message.attachments?.length ? message.attachments : void 0;
726
+ }
727
+ function useTimbalStream({
191
728
  workforceId,
192
- children,
193
729
  baseUrl = "/api",
194
- fetch: fetchFn
730
+ fetch: fetchFn,
731
+ debug = false
195
732
  }) {
196
733
  const [messages, setMessages] = (0, import_react.useState)([]);
197
734
  const [isRunning, setIsRunning] = (0, import_react.useState)(false);
@@ -201,40 +738,41 @@ function TimbalRuntimeProvider({
201
738
  (0, import_react.useEffect)(() => {
202
739
  fetchFnRef.current = fetchFn ?? authFetch;
203
740
  }, [fetchFn]);
741
+ const debugRef = (0, import_react.useRef)(debug);
742
+ (0, import_react.useEffect)(() => {
743
+ debugRef.current = debug;
744
+ }, [debug]);
204
745
  (0, import_react.useEffect)(() => {
205
746
  messagesRef.current = messages;
206
747
  }, [messages]);
207
748
  const streamAssistantResponse = (0, import_react.useCallback)(
208
- async (input, userId, assistantId, parentId, signal) => {
209
- const parts = [];
210
- const toolIndexById = /* @__PURE__ */ new Map();
211
- const lastTextPart = () => {
212
- const last = parts[parts.length - 1];
213
- if (last?.type === "text") return last;
214
- const next = { type: "text", text: "" };
215
- parts.push(next);
216
- return next;
217
- };
749
+ async (input, attachments, userId, assistantId, parentId, signal) => {
750
+ const state = createReducerState();
218
751
  const flush = () => {
219
752
  setMessages(
220
- (prev) => prev.map((m) => m.id === assistantId ? { ...m, content: [...parts] } : m)
753
+ (prev) => prev.map(
754
+ (m) => m.id === assistantId ? { ...m, content: [...state.parts] } : m
755
+ )
221
756
  );
222
757
  };
223
758
  const stampRunId = (runId) => {
224
759
  setMessages(
225
- (prev) => prev.map((m) => m.id === userId || m.id === assistantId ? { ...m, runId } : m)
760
+ (prev) => prev.map(
761
+ (m) => m.id === userId || m.id === assistantId ? { ...m, runId } : m
762
+ )
226
763
  );
227
764
  };
228
765
  try {
229
- const res = await fetchFnRef.current(`${baseUrl}/workforce/${workforceId}/stream`, {
230
- method: "POST",
231
- headers: { "Content-Type": "application/json" },
232
- body: JSON.stringify({
233
- prompt: input,
234
- context: { parent_id: parentId }
235
- }),
236
- signal
237
- });
766
+ const body = buildPromptBody({ input, attachments, parentId });
767
+ const res = await fetchFnRef.current(
768
+ `${baseUrl}/workforce/${workforceId}/stream`,
769
+ {
770
+ method: "POST",
771
+ headers: { "Content-Type": "application/json" },
772
+ body: JSON.stringify(body),
773
+ signal
774
+ }
775
+ );
238
776
  if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`);
239
777
  const reader = res.body.getReader();
240
778
  const decoder = new TextDecoder();
@@ -249,84 +787,34 @@ function TimbalRuntimeProvider({
249
787
  for (const line of lines) {
250
788
  const event = (0, import_timbal_sdk.parseSSELine)(line);
251
789
  if (!event) continue;
252
- if (!capturedRunId && isTopLevelStart(event)) {
253
- capturedRunId = event.run_id;
254
- stampRunId(capturedRunId);
790
+ if (debugRef.current) {
791
+ console.debug("[timbal]", event.type, event);
255
792
  }
256
- switch (event.type) {
257
- case "DELTA": {
258
- const item = event.item;
259
- if (!item) break;
260
- if (item.type === "text_delta" && typeof item.text_delta === "string") {
261
- lastTextPart().text += item.text_delta;
262
- flush();
263
- } else if (item.type === "tool_use") {
264
- const toolCallId = item.id || `tool-${crypto.randomUUID()}`;
265
- const inputStr = typeof item.input === "string" ? item.input : JSON.stringify(item.input ?? {});
266
- parts.push({
267
- type: "tool-call",
268
- toolCallId,
269
- toolName: item.name || "unknown",
270
- argsText: inputStr
271
- });
272
- toolIndexById.set(toolCallId, parts.length - 1);
273
- flush();
274
- } else if (item.type === "tool_use_delta") {
275
- const idx = toolIndexById.get(item.id);
276
- if (idx !== void 0 && typeof item.input_delta === "string") {
277
- parts[idx].argsText += item.input_delta;
278
- flush();
279
- }
280
- }
281
- break;
282
- }
283
- case "OUTPUT": {
284
- const output = event.output;
285
- if (!output) break;
286
- if (typeof output === "object" && Array.isArray(output.content)) {
287
- for (const block of output.content) {
288
- if (block.type === "tool_use") {
289
- const id = block.id || `tool-${crypto.randomUUID()}`;
290
- const idx = toolIndexById.get(id);
291
- if (idx !== void 0) {
292
- parts[idx].result = "Tool executed";
293
- } else {
294
- const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {});
295
- parts.push({
296
- type: "tool-call",
297
- toolCallId: id,
298
- toolName: block.name || "unknown",
299
- argsText: inputStr,
300
- result: "Tool executed"
301
- });
302
- toolIndexById.set(id, parts.length - 1);
303
- }
304
- } else if (block.type === "text" && typeof block.text === "string" && !lastTextPart().text) {
305
- lastTextPart().text = block.text;
306
- }
307
- }
308
- flush();
309
- } else if (parts.length === 0) {
310
- const text = typeof output === "string" ? output : JSON.stringify(output);
311
- parts.push({ type: "text", text });
312
- flush();
313
- }
314
- break;
793
+ if (!capturedRunId) {
794
+ const runId = readTopLevelStartRunId(event);
795
+ if (runId) {
796
+ capturedRunId = runId;
797
+ stampRunId(runId);
315
798
  }
316
799
  }
800
+ const changed = reduceSseEvent(state, event);
801
+ if (changed) flush();
317
802
  }
318
803
  }
319
804
  if (buffer.trim()) {
320
805
  const event = (0, import_timbal_sdk.parseSSELine)(buffer);
321
- if (event?.type === "OUTPUT" && parts.length === 0 && event.output) {
322
- const text = typeof event.output === "string" ? event.output : JSON.stringify(event.output);
323
- parts.push({ type: "text", text });
324
- flush();
806
+ if (event) {
807
+ if (debugRef.current) {
808
+ console.debug("[timbal]", event.type, event);
809
+ }
810
+ if (reduceSseEvent(state, event)) flush();
325
811
  }
326
812
  }
327
813
  } catch (err) {
328
814
  if (err.name !== "AbortError") {
329
- if (parts.length === 0) parts.push({ type: "text", text: "Something went wrong." });
815
+ if (state.parts.length === 0) {
816
+ state.parts.push({ type: "text", text: "Something went wrong." });
817
+ }
330
818
  flush();
331
819
  }
332
820
  } finally {
@@ -336,44 +824,46 @@ function TimbalRuntimeProvider({
336
824
  },
337
825
  [workforceId, baseUrl]
338
826
  );
339
- const onNew = (0, import_react.useCallback)(
340
- async (message) => {
341
- const textPart = message.content.find((c) => c.type === "text");
342
- if (!textPart || textPart.type !== "text") return;
343
- const input = textPart.text;
827
+ const send = (0, import_react.useCallback)(
828
+ async (input, options) => {
344
829
  const userId = crypto.randomUUID();
345
830
  const assistantId = crypto.randomUUID();
346
- let base = messagesRef.current;
347
- if (message.parentId !== null) {
348
- const parentIdx = base.findIndex((m) => m.id === message.parentId);
349
- if (parentIdx >= 0) {
350
- base = base.slice(0, parentIdx + 1);
351
- }
352
- }
353
- const parentId = findParentId(base);
831
+ const base = messagesRef.current;
832
+ const parentId = options?.parentId !== void 0 ? options.parentId : findParentId(base);
833
+ const userMessage = {
834
+ id: userId,
835
+ role: "user",
836
+ content: input ? [{ type: "text", text: input }] : [],
837
+ ...options?.attachments && options.attachments.length > 0 ? { attachments: options.attachments } : {}
838
+ };
354
839
  setMessages([
355
840
  ...base,
356
- { id: userId, role: "user", content: [{ type: "text", text: input }] }
357
- ]);
358
- setIsRunning(true);
359
- setMessages((prev) => [
360
- ...prev,
841
+ userMessage,
361
842
  { id: assistantId, role: "assistant", content: [] }
362
843
  ]);
844
+ setIsRunning(true);
363
845
  const controller = new AbortController();
364
846
  abortRef.current = controller;
365
- await streamAssistantResponse(input, userId, assistantId, parentId, controller.signal);
847
+ await streamAssistantResponse(
848
+ input,
849
+ options?.attachments,
850
+ userId,
851
+ assistantId,
852
+ parentId,
853
+ controller.signal
854
+ );
366
855
  },
367
856
  [streamAssistantResponse]
368
857
  );
369
- const onReload = (0, import_react.useCallback)(
858
+ const reload = (0, import_react.useCallback)(
370
859
  async (messageId) => {
371
860
  const current = messagesRef.current;
372
861
  const idx = messageId ? current.findIndex((m) => m.id === messageId) : current.length - 2;
373
862
  const userMessage = idx >= 0 ? current[idx] : null;
374
863
  if (!userMessage || userMessage.role !== "user") return;
375
864
  const input = getTextFromMessage(userMessage);
376
- if (!input) return;
865
+ const messageAttachments = getAttachmentsFromMessage(userMessage);
866
+ if (!input && !messageAttachments?.length) return;
377
867
  const assistantId = crypto.randomUUID();
378
868
  const parentId = findParentId(current, idx);
379
869
  setMessages((prev) => [
@@ -383,25 +873,115 @@ function TimbalRuntimeProvider({
383
873
  setIsRunning(true);
384
874
  const controller = new AbortController();
385
875
  abortRef.current = controller;
386
- await streamAssistantResponse(input, userMessage.id, assistantId, parentId, controller.signal);
876
+ await streamAssistantResponse(
877
+ input,
878
+ messageAttachments,
879
+ userMessage.id,
880
+ assistantId,
881
+ parentId,
882
+ controller.signal
883
+ );
387
884
  },
388
885
  [streamAssistantResponse]
389
886
  );
390
- const onCancel = (0, import_react.useCallback)(async () => {
887
+ const cancel = (0, import_react.useCallback)(() => {
888
+ abortRef.current?.abort();
889
+ }, []);
890
+ const clear = (0, import_react.useCallback)(() => {
391
891
  abortRef.current?.abort();
892
+ setMessages([]);
392
893
  }, []);
894
+ return (0, import_react.useMemo)(
895
+ () => ({ messages, isRunning, send, reload, cancel, clear }),
896
+ [messages, isRunning, send, reload, cancel, clear]
897
+ );
898
+ }
899
+ function readTopLevelStartRunId(event) {
900
+ if (event.type === "START" && typeof event.run_id === "string" && typeof event.path === "string" && !event.path.includes(".")) {
901
+ return event.run_id;
902
+ }
903
+ return null;
904
+ }
905
+ var TimbalStreamContext = (0, import_react.createContext)(null);
906
+ function useTimbalRuntime() {
907
+ const ctx = (0, import_react.useContext)(TimbalStreamContext);
908
+ if (!ctx) {
909
+ throw new Error(
910
+ "useTimbalRuntime must be used inside a <TimbalRuntimeProvider>."
911
+ );
912
+ }
913
+ return ctx;
914
+ }
915
+ function TimbalRuntimeProvider({
916
+ workforceId,
917
+ children,
918
+ baseUrl = "/api",
919
+ fetch: fetchFn,
920
+ attachments,
921
+ attachmentsUploadUrl,
922
+ attachmentsAccept,
923
+ debug
924
+ }) {
925
+ const stream = useTimbalStream({
926
+ workforceId,
927
+ baseUrl,
928
+ fetch: fetchFn,
929
+ debug
930
+ });
931
+ const attachmentAdapter = (0, import_react.useMemo)(
932
+ () => resolveAttachmentAdapter(attachments, {
933
+ baseUrl,
934
+ fetch: fetchFn,
935
+ uploadUrl: attachmentsUploadUrl,
936
+ accept: attachmentsAccept
937
+ }),
938
+ [attachments, attachmentsUploadUrl, attachmentsAccept, baseUrl, fetchFn]
939
+ );
940
+ const onNew = (0, import_react.useCallback)(
941
+ async (message) => {
942
+ const textPart = message.content.find((c) => c.type === "text");
943
+ const input = textPart && textPart.type === "text" ? textPart.text : "";
944
+ const auiAttachments = message.attachments;
945
+ const attachments2 = auiAttachments ? (await Promise.all(auiAttachments.map(extractAttachment))).filter((a) => a !== null) : [];
946
+ if (!input && attachments2.length === 0) return;
947
+ const parentId = message.parentId !== null && message.parentId !== void 0 ? findParentIdFromAuiParent(stream.messages, message.parentId) : void 0;
948
+ await stream.send(input, {
949
+ attachments: attachments2.length > 0 ? attachments2 : void 0,
950
+ ...parentId !== void 0 ? { parentId } : {}
951
+ });
952
+ },
953
+ [stream]
954
+ );
955
+ const onReload = (0, import_react.useCallback)(
956
+ async (messageId) => {
957
+ await stream.reload(messageId);
958
+ },
959
+ [stream]
960
+ );
961
+ const onCancel = (0, import_react.useCallback)(async () => {
962
+ stream.cancel();
963
+ }, [stream]);
393
964
  const runtime = (0, import_react2.useExternalStoreRuntime)({
394
- isRunning,
395
- messages,
965
+ isRunning: stream.isRunning,
966
+ messages: stream.messages,
396
967
  convertMessage,
397
968
  onNew,
398
969
  onEdit: onNew,
399
970
  onReload,
400
- onCancel
971
+ onCancel,
972
+ ...attachmentAdapter ? { adapters: { attachments: attachmentAdapter } } : {}
401
973
  });
402
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AssistantRuntimeProvider, { runtime, children });
974
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimbalStreamContext.Provider, { value: stream, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AssistantRuntimeProvider, { runtime, children }) });
975
+ }
976
+ function findParentIdFromAuiParent(messages, auiParentId) {
977
+ const idx = messages.findIndex((m) => m.id === auiParentId);
978
+ if (idx < 0) return null;
979
+ return findParentId(messages.slice(0, idx + 1));
403
980
  }
404
981
 
982
+ // src/index.ts
983
+ var import_timbal_sdk2 = require("@timbal-ai/timbal-sdk");
984
+
405
985
  // src/components/attachment.tsx
406
986
  var import_react4 = require("react");
407
987
  var import_lucide_react2 = require("lucide-react");
@@ -854,78 +1434,1117 @@ var import_react_markdown = require("@assistant-ui/react-markdown");
854
1434
  var import_remark_gfm = __toESM(require("remark-gfm"), 1);
855
1435
  var import_remark_math = __toESM(require("remark-math"), 1);
856
1436
  var import_rehype_katex = __toESM(require("rehype-katex"), 1);
857
- var import_react7 = require("react");
858
- var import_lucide_react3 = require("lucide-react");
1437
+ var import_react16 = require("react");
1438
+ var import_lucide_react4 = require("lucide-react");
859
1439
 
860
1440
  // src/components/syntax-highlighter.tsx
861
- var import_react6 = require("react");
1441
+ var import_react15 = require("react");
862
1442
  var import_core = require("shiki/core");
863
1443
  var import_javascript = require("shiki/engine/javascript");
864
- var import_javascript2 = __toESM(require("shiki/langs/javascript.mjs"), 1);
865
- var import_typescript = __toESM(require("shiki/langs/typescript.mjs"), 1);
866
- var import_python = __toESM(require("shiki/langs/python.mjs"), 1);
867
- var import_html = __toESM(require("shiki/langs/html.mjs"), 1);
868
- var import_css = __toESM(require("shiki/langs/css.mjs"), 1);
869
- var import_json = __toESM(require("shiki/langs/json.mjs"), 1);
870
- var import_bash = __toESM(require("shiki/langs/bash.mjs"), 1);
871
- var import_markdown = __toESM(require("shiki/langs/markdown.mjs"), 1);
872
- var import_jsx = __toESM(require("shiki/langs/jsx.mjs"), 1);
873
- var import_tsx = __toESM(require("shiki/langs/tsx.mjs"), 1);
874
- var import_sql = __toESM(require("shiki/langs/sql.mjs"), 1);
875
- var import_yaml = __toESM(require("shiki/langs/yaml.mjs"), 1);
876
- var import_rust = __toESM(require("shiki/langs/rust.mjs"), 1);
877
- var import_go = __toESM(require("shiki/langs/go.mjs"), 1);
878
- var import_java = __toESM(require("shiki/langs/java.mjs"), 1);
879
- var import_c = __toESM(require("shiki/langs/c.mjs"), 1);
880
- var import_cpp = __toESM(require("shiki/langs/cpp.mjs"), 1);
881
- var import_vitesse_dark = __toESM(require("shiki/themes/vitesse-dark.mjs"), 1);
882
- var import_vitesse_light = __toESM(require("shiki/themes/vitesse-light.mjs"), 1);
1444
+
1445
+ // src/artifacts/registry.tsx
1446
+ var import_react14 = require("react");
1447
+
1448
+ // src/artifacts/chart-artifact.tsx
1449
+ var import_react6 = require("react");
1450
+
1451
+ // src/artifacts/artifact-card.tsx
883
1452
  var import_jsx_runtime8 = require("react/jsx-runtime");
884
- var SHIKI_THEME_DARK = "vitesse-dark";
885
- var SHIKI_THEME_LIGHT = "vitesse-light";
886
- var highlighterPromise = null;
887
- function getHighlighter() {
888
- if (!highlighterPromise) {
889
- highlighterPromise = (0, import_core.createHighlighterCore)({
890
- themes: [import_vitesse_dark.default, import_vitesse_light.default],
891
- langs: [
892
- import_javascript2.default,
893
- import_typescript.default,
894
- import_python.default,
895
- import_html.default,
896
- import_css.default,
897
- import_json.default,
898
- import_bash.default,
899
- import_markdown.default,
900
- import_jsx.default,
901
- import_tsx.default,
902
- import_sql.default,
903
- import_yaml.default,
904
- import_rust.default,
905
- import_go.default,
906
- import_java.default,
907
- import_c.default,
908
- import_cpp.default
909
- ],
910
- engine: (0, import_javascript.createJavaScriptRegexEngine)()
911
- });
912
- }
913
- return highlighterPromise;
914
- }
915
- getHighlighter();
916
- var ShikiSyntaxHighlighter = ({
917
- components: { Pre, Code: Code2 },
918
- language,
919
- code
920
- }) => {
921
- const [html, setHtml] = (0, import_react6.useState)(null);
922
- (0, import_react6.useEffect)(() => {
923
- let cancelled = false;
924
- (async () => {
925
- try {
926
- const highlighter = await getHighlighter();
927
- const loadedLangs = highlighter.getLoadedLanguages();
928
- if (!loadedLangs.includes(language)) {
1453
+ var ArtifactCard = ({ title, kind, className, bodyClassName, toolbar, children }) => {
1454
+ const hasHeader = Boolean(title || toolbar);
1455
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1456
+ "div",
1457
+ {
1458
+ className: cn(
1459
+ "aui-artifact-root my-3 overflow-hidden rounded-xl border border-border/60 bg-background shadow-sm",
1460
+ className
1461
+ ),
1462
+ "data-artifact-kind": kind,
1463
+ children: [
1464
+ hasHeader && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "aui-artifact-header flex items-center gap-2 border-b border-border/40 bg-muted/30 px-3 py-1.5", children: [
1465
+ title && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "aui-artifact-title flex-1 truncate text-xs font-semibold text-foreground/80", children: title }),
1466
+ !title && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "flex-1" }),
1467
+ toolbar
1468
+ ] }),
1469
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn("aui-artifact-body", bodyClassName), children })
1470
+ ]
1471
+ }
1472
+ );
1473
+ };
1474
+
1475
+ // src/artifacts/chart-artifact.tsx
1476
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1477
+ var ChartArtifactView = ({
1478
+ artifact
1479
+ }) => {
1480
+ const { type: _t, chartType = "bar", data = [] } = artifact;
1481
+ const xKey = artifact.xKey ?? inferXKey(data);
1482
+ const dataKeys = (0, import_react6.useMemo)(() => {
1483
+ if (Array.isArray(artifact.dataKey)) return artifact.dataKey;
1484
+ if (typeof artifact.dataKey === "string") return [artifact.dataKey];
1485
+ return inferDataKeys(data, xKey);
1486
+ }, [artifact.dataKey, data, xKey]);
1487
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ArtifactCard, { title: artifact.title, kind: "chart", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "aui-artifact-chart p-3", children: [
1488
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1489
+ ChartSvg,
1490
+ {
1491
+ chartType,
1492
+ data,
1493
+ xKey,
1494
+ dataKeys,
1495
+ unit: artifact.unit
1496
+ }
1497
+ ),
1498
+ dataKeys.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Legend, { dataKeys })
1499
+ ] }) });
1500
+ };
1501
+ var COLORS = [
1502
+ "var(--primary, #6366f1)",
1503
+ "#22c55e",
1504
+ "#f59e0b",
1505
+ "#ef4444",
1506
+ "#06b6d4",
1507
+ "#a855f7"
1508
+ ];
1509
+ var W = 600;
1510
+ var H = 240;
1511
+ var PAD = { top: 12, right: 16, bottom: 28, left: 36 };
1512
+ var ChartSvg = ({ chartType, data, xKey, dataKeys, unit }) => {
1513
+ if (data.length === 0 || dataKeys.length === 0) {
1514
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EmptyState, {});
1515
+ }
1516
+ if (chartType === "pie") {
1517
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PieChart, { data, xKey, dataKey: dataKeys[0] });
1518
+ }
1519
+ const innerW = W - PAD.left - PAD.right;
1520
+ const innerH = H - PAD.top - PAD.bottom;
1521
+ const all = dataKeys.flatMap((k) => data.map((d) => toNum(d[k])));
1522
+ const maxV = Math.max(0, ...all);
1523
+ const minV = Math.min(0, ...all);
1524
+ const range = maxV - minV || 1;
1525
+ const yScale = (v) => PAD.top + innerH - (v - minV) / range * innerH;
1526
+ const xCount = data.length;
1527
+ const xStep = xCount > 1 ? innerW / (xCount - 1) : innerW;
1528
+ const xPos = (i) => chartType === "bar" ? PAD.left + innerW * (i + 0.5) / xCount : PAD.left + i * xStep;
1529
+ const ticks = niceTicks(minV, maxV);
1530
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1531
+ "svg",
1532
+ {
1533
+ viewBox: `0 0 ${W} ${H}`,
1534
+ className: "aui-artifact-chart-svg w-full",
1535
+ role: "img",
1536
+ "aria-label": "Chart",
1537
+ children: [
1538
+ ticks.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
1539
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1540
+ "line",
1541
+ {
1542
+ x1: PAD.left,
1543
+ x2: W - PAD.right,
1544
+ y1: yScale(t),
1545
+ y2: yScale(t),
1546
+ stroke: "currentColor",
1547
+ strokeOpacity: 0.08
1548
+ }
1549
+ ),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1551
+ "text",
1552
+ {
1553
+ x: PAD.left - 6,
1554
+ y: yScale(t),
1555
+ textAnchor: "end",
1556
+ dominantBaseline: "middle",
1557
+ className: "fill-muted-foreground text-[10px]",
1558
+ children: formatTick(t, unit)
1559
+ }
1560
+ )
1561
+ ] }, i)),
1562
+ data.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1563
+ "text",
1564
+ {
1565
+ x: xPos(i),
1566
+ y: H - PAD.bottom + 14,
1567
+ textAnchor: "middle",
1568
+ className: "fill-muted-foreground text-[10px]",
1569
+ children: String(d[xKey] ?? i)
1570
+ },
1571
+ i
1572
+ )),
1573
+ chartType === "bar" && renderBars({ data, dataKeys, xCount, xPos, yScale, minV, innerW }),
1574
+ chartType === "line" && dataKeys.map((k, ki) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1575
+ Polyline,
1576
+ {
1577
+ data,
1578
+ dataKey: k,
1579
+ xPos,
1580
+ yScale,
1581
+ color: COLORS[ki % COLORS.length]
1582
+ },
1583
+ k
1584
+ )),
1585
+ chartType === "area" && dataKeys.map((k, ki) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1586
+ Area,
1587
+ {
1588
+ data,
1589
+ dataKey: k,
1590
+ xPos,
1591
+ yScale,
1592
+ baseY: yScale(Math.max(0, minV)),
1593
+ color: COLORS[ki % COLORS.length]
1594
+ },
1595
+ k
1596
+ ))
1597
+ ]
1598
+ }
1599
+ );
1600
+ };
1601
+ function renderBars(args) {
1602
+ const { data, dataKeys, xCount, xPos, yScale, minV, innerW } = args;
1603
+ const groupWidth = innerW / xCount * 0.7;
1604
+ const barWidth = groupWidth / dataKeys.length;
1605
+ const baseY = yScale(Math.max(0, minV));
1606
+ return dataKeys.flatMap(
1607
+ (k, ki) => data.map((d, i) => {
1608
+ const v = toNum(d[k]);
1609
+ const y = yScale(v);
1610
+ const x = xPos(i) - groupWidth / 2 + ki * barWidth;
1611
+ const top = Math.min(y, baseY);
1612
+ const height = Math.abs(y - baseY);
1613
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1614
+ "rect",
1615
+ {
1616
+ x,
1617
+ y: top,
1618
+ width: Math.max(1, barWidth - 2),
1619
+ height: Math.max(1, height),
1620
+ rx: 2,
1621
+ fill: COLORS[ki % COLORS.length]
1622
+ },
1623
+ `${k}-${i}`
1624
+ );
1625
+ })
1626
+ );
1627
+ }
1628
+ var Polyline = ({ data, dataKey, xPos, yScale, color }) => {
1629
+ const points = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
1630
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1631
+ "polyline",
1632
+ {
1633
+ points,
1634
+ fill: "none",
1635
+ stroke: color,
1636
+ strokeWidth: 2,
1637
+ strokeLinejoin: "round",
1638
+ strokeLinecap: "round"
1639
+ }
1640
+ );
1641
+ };
1642
+ var Area = ({ data, dataKey, xPos, yScale, baseY, color }) => {
1643
+ if (data.length === 0) return null;
1644
+ const top = data.map((d, i) => `${xPos(i)},${yScale(toNum(d[dataKey]))}`).join(" ");
1645
+ const path = `M ${xPos(0)},${baseY} L ${top} L ${xPos(data.length - 1)},${baseY} Z`;
1646
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1647
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: path, fill: color, fillOpacity: 0.18 }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Polyline, { data, dataKey, xPos, yScale, color })
1649
+ ] });
1650
+ };
1651
+ var PieChart = ({ data, xKey, dataKey }) => {
1652
+ const cx = W / 2;
1653
+ const cy = H / 2;
1654
+ const r = Math.min(W, H) / 2 - 16;
1655
+ const total = data.reduce((sum, d) => sum + toNum(d[dataKey]), 0) || 1;
1656
+ let acc = 0;
1657
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1658
+ "svg",
1659
+ {
1660
+ viewBox: `0 0 ${W} ${H}`,
1661
+ className: "aui-artifact-chart-svg w-full",
1662
+ role: "img",
1663
+ "aria-label": "Pie chart",
1664
+ children: data.map((d, i) => {
1665
+ const value = toNum(d[dataKey]);
1666
+ const start = acc / total * Math.PI * 2;
1667
+ acc += value;
1668
+ const end = acc / total * Math.PI * 2;
1669
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1670
+ PieSlice,
1671
+ {
1672
+ cx,
1673
+ cy,
1674
+ r,
1675
+ start,
1676
+ end,
1677
+ color: COLORS[i % COLORS.length],
1678
+ label: String(d[xKey] ?? i)
1679
+ },
1680
+ i
1681
+ );
1682
+ })
1683
+ }
1684
+ );
1685
+ };
1686
+ var PieSlice = ({ cx, cy, r, start, end, color, label }) => {
1687
+ const x1 = cx + Math.sin(start) * r;
1688
+ const y1 = cy - Math.cos(start) * r;
1689
+ const x2 = cx + Math.sin(end) * r;
1690
+ const y2 = cy - Math.cos(end) * r;
1691
+ const large = end - start > Math.PI ? 1 : 0;
1692
+ const path = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;
1693
+ const mid = (start + end) / 2;
1694
+ const lx = cx + Math.sin(mid) * (r * 0.65);
1695
+ const ly = cy - Math.cos(mid) * (r * 0.65);
1696
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
1697
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: path, fill: color, stroke: "var(--background, #fff)", strokeWidth: 1 }),
1698
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1699
+ "text",
1700
+ {
1701
+ x: lx,
1702
+ y: ly,
1703
+ textAnchor: "middle",
1704
+ dominantBaseline: "middle",
1705
+ className: "fill-white text-[10px] font-semibold",
1706
+ children: label
1707
+ }
1708
+ )
1709
+ ] });
1710
+ };
1711
+ var Legend = ({ dataKeys }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "aui-artifact-chart-legend mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: dataKeys.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "inline-flex items-center gap-1.5", children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1713
+ "span",
1714
+ {
1715
+ className: "inline-block size-2 rounded-sm",
1716
+ style: { background: COLORS[i % COLORS.length] },
1717
+ "aria-hidden": true
1718
+ }
1719
+ ),
1720
+ k
1721
+ ] }, k)) });
1722
+ var EmptyState = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "aui-artifact-chart-empty flex h-32 items-center justify-center text-xs text-muted-foreground", children: "No data" });
1723
+ function inferXKey(data) {
1724
+ if (data.length === 0) return "x";
1725
+ for (const k of Object.keys(data[0])) {
1726
+ if (typeof data[0][k] !== "number") return k;
1727
+ }
1728
+ return Object.keys(data[0])[0] ?? "x";
1729
+ }
1730
+ function inferDataKeys(data, xKey) {
1731
+ if (data.length === 0) return [];
1732
+ return Object.keys(data[0]).filter(
1733
+ (k) => k !== xKey && typeof data[0][k] === "number"
1734
+ );
1735
+ }
1736
+ function toNum(value) {
1737
+ const n = typeof value === "number" ? value : Number(value);
1738
+ return Number.isFinite(n) ? n : 0;
1739
+ }
1740
+ function niceTicks(min, max, count = 4) {
1741
+ if (max === min) return [min];
1742
+ const range = max - min;
1743
+ const step = niceStep(range / count);
1744
+ const start = Math.floor(min / step) * step;
1745
+ const out = [];
1746
+ for (let v = start; v <= max + step / 2; v += step) {
1747
+ out.push(round(v));
1748
+ }
1749
+ return out;
1750
+ }
1751
+ function niceStep(raw) {
1752
+ const exp = Math.floor(Math.log10(Math.abs(raw))) || 0;
1753
+ const base = Math.pow(10, exp);
1754
+ const norm = raw / base;
1755
+ let nice = 1;
1756
+ if (norm >= 5) nice = 5;
1757
+ else if (norm >= 2) nice = 2;
1758
+ return nice * base;
1759
+ }
1760
+ function round(v) {
1761
+ return Math.round(v * 1e6) / 1e6;
1762
+ }
1763
+ function formatTick(v, unit) {
1764
+ const s = Math.abs(v) >= 1e3 ? `${(v / 1e3).toFixed(1)}k` : String(round(v));
1765
+ return unit ? `${s}${unit}` : s;
1766
+ }
1767
+
1768
+ // src/artifacts/question-artifact.tsx
1769
+ var import_react7 = require("react");
1770
+ var import_react8 = require("@assistant-ui/react");
1771
+ var import_lucide_react3 = require("lucide-react");
1772
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1773
+ var QuestionArtifactView = ({
1774
+ artifact
1775
+ }) => {
1776
+ const runtime = (0, import_react8.useThreadRuntime)();
1777
+ const [selected, setSelected] = (0, import_react7.useState)([]);
1778
+ const [submitted, setSubmitted] = (0, import_react7.useState)(null);
1779
+ const isMulti = artifact.multi === true;
1780
+ const send = (labels) => {
1781
+ if (labels.length === 0) return;
1782
+ const text = labels.join(", ");
1783
+ setSubmitted(text);
1784
+ runtime.append({ role: "user", content: [{ type: "text", text }] });
1785
+ };
1786
+ const onPick = (option) => {
1787
+ if (submitted) return;
1788
+ if (!isMulti) {
1789
+ send([option.label]);
1790
+ return;
1791
+ }
1792
+ setSelected(
1793
+ (prev) => prev.includes(option.id) ? prev.filter((id) => id !== option.id) : [...prev, option.id]
1794
+ );
1795
+ };
1796
+ const onConfirm = () => {
1797
+ const labels = artifact.options.filter((o) => selected.includes(o.id)).map((o) => o.label);
1798
+ send(labels);
1799
+ };
1800
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ArtifactCard, { kind: "question", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "aui-artifact-question p-3", children: [
1801
+ artifact.prompt && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "aui-artifact-question-prompt mb-2 text-sm text-foreground/85", children: artifact.prompt }),
1802
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "aui-artifact-question-options flex flex-col gap-1.5", children: artifact.options.map((option) => {
1803
+ const isSelected = isMulti && selected.includes(option.id);
1804
+ const isDisabled = Boolean(submitted);
1805
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1806
+ "button",
1807
+ {
1808
+ type: "button",
1809
+ disabled: isDisabled,
1810
+ onClick: () => onPick(option),
1811
+ className: cn(
1812
+ "aui-artifact-question-option flex items-center gap-2 rounded-lg border px-3 py-2 text-left text-sm transition-colors",
1813
+ "border-border/60 hover:border-primary/40 hover:bg-muted/40",
1814
+ isSelected && "border-primary/60 bg-primary/5",
1815
+ isDisabled && "cursor-not-allowed opacity-60 hover:border-border/60 hover:bg-transparent"
1816
+ ),
1817
+ children: [
1818
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1819
+ "span",
1820
+ {
1821
+ className: cn(
1822
+ "flex size-4 shrink-0 items-center justify-center rounded-full border",
1823
+ isSelected ? "border-primary bg-primary text-primary-foreground" : "border-border"
1824
+ ),
1825
+ "aria-hidden": true,
1826
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react3.CheckIcon, { className: "size-3" })
1827
+ }
1828
+ ),
1829
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "aui-artifact-question-option-text flex-1", children: [
1830
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "font-medium text-foreground/90", children: option.label }),
1831
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-xs text-muted-foreground", children: option.description })
1832
+ ] })
1833
+ ]
1834
+ },
1835
+ option.id
1836
+ );
1837
+ }) }),
1838
+ isMulti && !submitted && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1839
+ Button,
1840
+ {
1841
+ type: "button",
1842
+ size: "sm",
1843
+ disabled: selected.length === 0,
1844
+ onClick: onConfirm,
1845
+ children: "Confirm"
1846
+ }
1847
+ ) }),
1848
+ submitted && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "aui-artifact-question-submitted mt-2 text-xs text-muted-foreground", children: [
1849
+ "Sent: ",
1850
+ submitted
1851
+ ] })
1852
+ ] }) });
1853
+ };
1854
+
1855
+ // src/artifacts/html-artifact.tsx
1856
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1857
+ var HtmlArtifactView = ({ artifact }) => {
1858
+ const sandboxed = artifact.sandboxed !== false;
1859
+ const sandbox = sandboxed ? "allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-pointer-lock" : void 0;
1860
+ const height = artifact.height ?? "320px";
1861
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ArtifactCard, { title: artifact.title, kind: "html", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1862
+ "iframe",
1863
+ {
1864
+ title: artifact.title ?? "HTML artifact",
1865
+ srcDoc: artifact.content,
1866
+ sandbox,
1867
+ className: "aui-artifact-html w-full border-0 bg-background",
1868
+ style: { height }
1869
+ }
1870
+ ) });
1871
+ };
1872
+
1873
+ // src/artifacts/json-artifact.tsx
1874
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1875
+ var JsonArtifactView = ({
1876
+ artifact
1877
+ }) => {
1878
+ const data = "data" in artifact ? artifact.data : artifact;
1879
+ const title = artifact.title;
1880
+ let body;
1881
+ try {
1882
+ body = JSON.stringify(data, null, 2);
1883
+ } catch {
1884
+ body = String(data);
1885
+ }
1886
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ArtifactCard, { title, kind: "json", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "aui-artifact-json m-0 max-h-[420px] overflow-auto p-3 font-mono text-[12px] leading-relaxed text-foreground/85", children: body }) });
1887
+ };
1888
+
1889
+ // src/artifacts/table-artifact.tsx
1890
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1891
+ var TableArtifactView = ({ artifact }) => {
1892
+ const rows = artifact.rows ?? [];
1893
+ const columns = artifact.columns ?? deriveColumns(rows);
1894
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ArtifactCard, { title: artifact.title, kind: "table", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "aui-artifact-table-wrap overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("table", { className: "aui-artifact-table w-full border-collapse text-sm", children: [
1895
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tr", { className: "border-b border-border/40 bg-muted/20", children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1896
+ "th",
1897
+ {
1898
+ className: "px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-muted-foreground",
1899
+ children: col.label ?? col.key
1900
+ },
1901
+ col.key
1902
+ )) }) }),
1903
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("tbody", { children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1904
+ "tr",
1905
+ {
1906
+ className: "border-b border-border/30 transition-colors last:border-b-0 hover:bg-muted/20",
1907
+ children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1908
+ "td",
1909
+ {
1910
+ className: "px-3 py-2 align-top text-foreground/85",
1911
+ children: formatCell(row[col.key])
1912
+ },
1913
+ col.key
1914
+ ))
1915
+ },
1916
+ i
1917
+ )) })
1918
+ ] }) }) });
1919
+ };
1920
+ function deriveColumns(rows) {
1921
+ const seen = /* @__PURE__ */ new Set();
1922
+ const cols = [];
1923
+ for (const row of rows) {
1924
+ for (const key of Object.keys(row)) {
1925
+ if (!seen.has(key)) {
1926
+ seen.add(key);
1927
+ cols.push({ key });
1928
+ }
1929
+ }
1930
+ }
1931
+ return cols;
1932
+ }
1933
+ function formatCell(value) {
1934
+ if (value === null || value === void 0) return "";
1935
+ if (typeof value === "object") return JSON.stringify(value);
1936
+ return String(value);
1937
+ }
1938
+
1939
+ // src/artifacts/ui/ui-artifact.tsx
1940
+ var import_react13 = require("react");
1941
+
1942
+ // src/artifacts/ui/types.ts
1943
+ function isUiBinding(value) {
1944
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.$bind === "string";
1945
+ }
1946
+
1947
+ // src/artifacts/ui/state.ts
1948
+ function uiStateReducer(state, action) {
1949
+ switch (action.type) {
1950
+ case "set":
1951
+ return setPath(state, action.path, action.value);
1952
+ case "toggle": {
1953
+ const current = getPath(state, action.path);
1954
+ return setPath(state, action.path, !current);
1955
+ }
1956
+ case "replace":
1957
+ return action.state;
1958
+ }
1959
+ }
1960
+ function getPath(state, path) {
1961
+ if (!path) return void 0;
1962
+ const parts = path.split(".");
1963
+ let cursor = state;
1964
+ for (const part of parts) {
1965
+ if (typeof cursor !== "object" || cursor === null) return void 0;
1966
+ cursor = cursor[part];
1967
+ }
1968
+ return cursor;
1969
+ }
1970
+ function setPath(state, path, value) {
1971
+ if (!path) return state;
1972
+ const parts = path.split(".");
1973
+ const next = { ...state };
1974
+ let cursor = next;
1975
+ for (let i = 0; i < parts.length - 1; i++) {
1976
+ const key = parts[i];
1977
+ const child = cursor[key];
1978
+ const cloned = typeof child === "object" && child !== null && !Array.isArray(child) ? { ...child } : {};
1979
+ cursor[key] = cloned;
1980
+ cursor = cloned;
1981
+ }
1982
+ cursor[parts[parts.length - 1]] = value;
1983
+ return next;
1984
+ }
1985
+ function resolveBindable(value, state) {
1986
+ if (isUiBinding(value)) {
1987
+ return getPath(state, value.$bind);
1988
+ }
1989
+ return value;
1990
+ }
1991
+
1992
+ // src/artifacts/ui/registry.tsx
1993
+ var import_react9 = require("react");
1994
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1995
+ var UiStateContext = (0, import_react9.createContext)({});
1996
+ var UiDispatchContext = (0, import_react9.createContext)(() => {
1997
+ });
1998
+ var UiStateProvider = ({ state, dispatch, children }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UiStateContext.Provider, { value: state, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UiDispatchContext.Provider, { value: dispatch, children }) });
1999
+ function useUiState() {
2000
+ return (0, import_react9.useContext)(UiStateContext);
2001
+ }
2002
+ function useUiDispatch() {
2003
+ return (0, import_react9.useContext)(UiDispatchContext);
2004
+ }
2005
+ var UiEventContext = (0, import_react9.createContext)(
2006
+ null
2007
+ );
2008
+ var UiEventProvider = ({ onEvent, children }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UiEventContext.Provider, { value: onEvent, children });
2009
+ function useUiEventEmitter() {
2010
+ return (0, import_react9.useContext)(UiEventContext);
2011
+ }
2012
+ var UiCustomNodeRegistryContext = (0, import_react9.createContext)({});
2013
+ var UiCustomNodeRegistryProvider = ({ renderers, children }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(UiCustomNodeRegistryContext.Provider, { value: renderers, children });
2014
+ function useUiCustomNodeRegistry() {
2015
+ return (0, import_react9.useContext)(UiCustomNodeRegistryContext);
2016
+ }
2017
+
2018
+ // src/artifacts/ui/nodes.tsx
2019
+ var import_react10 = require("react");
2020
+ var import_react11 = require("motion/react");
2021
+ var import_react12 = require("@assistant-ui/react");
2022
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2023
+ var UiNodeView = ({ node }) => {
2024
+ switch (node.kind) {
2025
+ case "box":
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BoxNode, { node });
2027
+ case "text":
2028
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TextNode, { node });
2029
+ case "heading":
2030
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(HeadingNode, { node });
2031
+ case "badge":
2032
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(BadgeNode, { node });
2033
+ case "button":
2034
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ButtonNode, { node });
2035
+ case "toggle":
2036
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ToggleNode, { node });
2037
+ case "slider":
2038
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SliderNode, { node });
2039
+ case "tooltip":
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TooltipNode, { node });
2041
+ case "draggable":
2042
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DraggableNode, { node });
2043
+ case "custom":
2044
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CustomNode, { node });
2045
+ default:
2046
+ return null;
2047
+ }
2048
+ };
2049
+ function useActionRunner() {
2050
+ const state = useUiState();
2051
+ const dispatch = useUiDispatch();
2052
+ const runtime = (0, import_react12.useThreadRuntime)();
2053
+ const emit = useUiEventEmitter();
2054
+ return (0, import_react10.useCallback)(
2055
+ (actions) => {
2056
+ if (!actions) return;
2057
+ const list = Array.isArray(actions) ? actions : [actions];
2058
+ for (const action of list) {
2059
+ switch (action.kind) {
2060
+ case "message": {
2061
+ const text = resolveBindable(action.text, state);
2062
+ if (typeof text === "string" && text.length > 0) {
2063
+ runtime?.append({
2064
+ role: "user",
2065
+ content: [{ type: "text", text }]
2066
+ });
2067
+ }
2068
+ break;
2069
+ }
2070
+ case "set": {
2071
+ const value = resolveBindable(action.value, state);
2072
+ dispatch({ type: "set", path: action.path, value });
2073
+ break;
2074
+ }
2075
+ case "toggle": {
2076
+ dispatch({ type: "toggle", path: action.path });
2077
+ break;
2078
+ }
2079
+ case "emit": {
2080
+ emit?.({ name: action.name, payload: action.payload });
2081
+ break;
2082
+ }
2083
+ }
2084
+ }
2085
+ },
2086
+ [state, dispatch, runtime, emit]
2087
+ );
2088
+ }
2089
+ var ALIGN_CLS = {
2090
+ start: "items-start",
2091
+ center: "items-center",
2092
+ end: "items-end",
2093
+ stretch: "items-stretch"
2094
+ };
2095
+ var JUSTIFY_CLS = {
2096
+ start: "justify-start",
2097
+ center: "justify-center",
2098
+ end: "justify-end",
2099
+ between: "justify-between",
2100
+ around: "justify-around"
2101
+ };
2102
+ var BoxNode = ({ node }) => {
2103
+ const dir = node.direction ?? "col";
2104
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2105
+ "div",
2106
+ {
2107
+ className: cn(
2108
+ "aui-ui-box flex",
2109
+ dir === "col" ? "flex-col" : "flex-row",
2110
+ node.wrap && "flex-wrap",
2111
+ node.align && ALIGN_CLS[node.align],
2112
+ node.justify && JUSTIFY_CLS[node.justify],
2113
+ node.className
2114
+ ),
2115
+ style: {
2116
+ gap: node.gap !== void 0 ? `${node.gap * 0.25}rem` : void 0,
2117
+ padding: node.padding !== void 0 ? `${node.padding * 0.25}rem` : void 0
2118
+ },
2119
+ children: node.children?.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(UiNodeView, { node: child }, child.id ?? i))
2120
+ }
2121
+ );
2122
+ };
2123
+ var TEXT_SIZE = {
2124
+ xs: "text-xs",
2125
+ sm: "text-sm",
2126
+ base: "text-base",
2127
+ lg: "text-lg"
2128
+ };
2129
+ var TEXT_WEIGHT = {
2130
+ normal: "font-normal",
2131
+ medium: "font-medium",
2132
+ semibold: "font-semibold",
2133
+ bold: "font-bold"
2134
+ };
2135
+ var TextNode = ({ node }) => {
2136
+ const state = useUiState();
2137
+ const value = resolveBindable(node.value, state);
2138
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2139
+ "span",
2140
+ {
2141
+ className: cn(
2142
+ "aui-ui-text",
2143
+ node.muted && "text-muted-foreground",
2144
+ node.size && TEXT_SIZE[node.size],
2145
+ node.weight && TEXT_WEIGHT[node.weight],
2146
+ node.className
2147
+ ),
2148
+ children: value === void 0 || value === null ? "" : String(value)
2149
+ }
2150
+ );
2151
+ };
2152
+ var HEADING_CLS = {
2153
+ 1: "text-2xl",
2154
+ 2: "text-xl",
2155
+ 3: "text-lg",
2156
+ 4: "text-base"
2157
+ };
2158
+ var HeadingNode = ({ node }) => {
2159
+ const state = useUiState();
2160
+ const value = String(resolveBindable(node.value, state) ?? "");
2161
+ const level = node.level ?? 2;
2162
+ const cls = cn(
2163
+ "aui-ui-heading font-semibold text-foreground",
2164
+ HEADING_CLS[level],
2165
+ node.className
2166
+ );
2167
+ switch (level) {
2168
+ case 1:
2169
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h1", { className: cls, children: value });
2170
+ case 2:
2171
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: cls, children: value });
2172
+ case 3:
2173
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: cls, children: value });
2174
+ case 4:
2175
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h4", { className: cls, children: value });
2176
+ }
2177
+ };
2178
+ var BADGE_TONE = {
2179
+ default: "bg-muted text-foreground",
2180
+ primary: "bg-primary/10 text-primary",
2181
+ success: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
2182
+ warn: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
2183
+ danger: "bg-destructive/10 text-destructive"
2184
+ };
2185
+ var BadgeNode = ({ node }) => {
2186
+ const state = useUiState();
2187
+ const value = String(resolveBindable(node.value, state) ?? "");
2188
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2189
+ "span",
2190
+ {
2191
+ className: cn(
2192
+ "aui-ui-badge inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium",
2193
+ BADGE_TONE[node.tone ?? "default"],
2194
+ node.className
2195
+ ),
2196
+ children: value
2197
+ }
2198
+ );
2199
+ };
2200
+ var ButtonNode = ({ node }) => {
2201
+ const state = useUiState();
2202
+ const run = useActionRunner();
2203
+ const label = String(resolveBindable(node.label, state) ?? "");
2204
+ const disabled = node.disabled !== void 0 ? Boolean(resolveBindable(node.disabled, state)) : false;
2205
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2206
+ Button,
2207
+ {
2208
+ variant: node.variant ?? "default",
2209
+ size: node.size ?? "default",
2210
+ disabled,
2211
+ className: node.className,
2212
+ onClick: () => run(node.onClick),
2213
+ children: label
2214
+ }
2215
+ );
2216
+ };
2217
+ var ToggleNode = ({ node }) => {
2218
+ const state = useUiState();
2219
+ const dispatch = useUiDispatch();
2220
+ const run = useActionRunner();
2221
+ const value = Boolean(getPath(state, node.binding));
2222
+ const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
2223
+ const onToggle = () => {
2224
+ dispatch({ type: "toggle", path: node.binding });
2225
+ run(node.onChange);
2226
+ };
2227
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2228
+ "label",
2229
+ {
2230
+ className: cn(
2231
+ "aui-ui-toggle inline-flex cursor-pointer items-center gap-2 text-sm select-none",
2232
+ node.className
2233
+ ),
2234
+ children: [
2235
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2236
+ "button",
2237
+ {
2238
+ type: "button",
2239
+ role: "switch",
2240
+ "aria-checked": value,
2241
+ onClick: onToggle,
2242
+ className: cn(
2243
+ "relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors",
2244
+ value ? "border-primary bg-primary" : "border-border bg-muted hover:bg-muted/80"
2245
+ ),
2246
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2247
+ "span",
2248
+ {
2249
+ className: cn(
2250
+ "inline-block size-4 transform rounded-full bg-background shadow transition-transform",
2251
+ value ? "translate-x-4" : "translate-x-0.5"
2252
+ ),
2253
+ "aria-hidden": true
2254
+ }
2255
+ )
2256
+ }
2257
+ ),
2258
+ label && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-foreground/85", children: label })
2259
+ ]
2260
+ }
2261
+ );
2262
+ };
2263
+ var SliderNode = ({ node }) => {
2264
+ const state = useUiState();
2265
+ const dispatch = useUiDispatch();
2266
+ const run = useActionRunner();
2267
+ const min = node.min ?? 0;
2268
+ const max = node.max ?? 100;
2269
+ const step = node.step ?? 1;
2270
+ const raw = getPath(state, node.binding);
2271
+ const value = typeof raw === "number" ? raw : min;
2272
+ const showValue = node.showValue ?? true;
2273
+ const label = node.label ? String(resolveBindable(node.label, state) ?? "") : null;
2274
+ const onChange = (e) => {
2275
+ const next = Number(e.target.value);
2276
+ dispatch({ type: "set", path: node.binding, value: next });
2277
+ };
2278
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: cn("aui-ui-slider flex flex-col gap-1", node.className), children: [
2279
+ (label || showValue) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
2280
+ label && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: label }),
2281
+ showValue && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "font-mono", children: value })
2282
+ ] }),
2283
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2284
+ "input",
2285
+ {
2286
+ type: "range",
2287
+ min,
2288
+ max,
2289
+ step,
2290
+ value,
2291
+ onChange,
2292
+ onMouseUp: () => run(node.onChange),
2293
+ onKeyUp: (e) => {
2294
+ if (e.key === "Enter" || e.key === " ") run(node.onChange);
2295
+ },
2296
+ onTouchEnd: () => run(node.onChange),
2297
+ className: "aui-ui-slider-input h-1.5 w-full cursor-pointer appearance-none rounded-full bg-muted accent-primary"
2298
+ }
2299
+ )
2300
+ ] });
2301
+ };
2302
+ var TooltipNode = ({ node }) => {
2303
+ const state = useUiState();
2304
+ const content = String(resolveBindable(node.content, state) ?? "");
2305
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Tooltip, { children: [
2306
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: cn("aui-ui-tooltip-trigger inline-flex", node.className), children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(UiNodeView, { node: node.child }) }) }),
2307
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TooltipContent, { side: node.side ?? "top", children: content })
2308
+ ] }) });
2309
+ };
2310
+ var DraggableNode = ({ node }) => {
2311
+ const run = useActionRunner();
2312
+ const snapBack = node.snapBack ?? true;
2313
+ const axis = node.axis ?? "both";
2314
+ const dragProp = axis === "both" ? true : axis;
2315
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2316
+ import_react11.motion.div,
2317
+ {
2318
+ drag: dragProp,
2319
+ dragMomentum: false,
2320
+ dragSnapToOrigin: snapBack,
2321
+ whileDrag: { scale: 1.02, cursor: "grabbing" },
2322
+ onDragEnd: () => run(node.onDragEnd),
2323
+ className: cn(
2324
+ "aui-ui-draggable inline-block cursor-grab touch-none",
2325
+ node.className
2326
+ ),
2327
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(UiNodeView, { node: node.child })
2328
+ }
2329
+ );
2330
+ };
2331
+ var CustomNode = ({ node }) => {
2332
+ const state = useUiState();
2333
+ const registry = useUiCustomNodeRegistry();
2334
+ const Renderer = registry[node.name];
2335
+ if (!Renderer) return null;
2336
+ const resolvedProps = resolveProps(node.props ?? {}, state);
2337
+ const children = node.children?.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(UiNodeView, { node: child }, child.id ?? i));
2338
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Renderer, { props: resolvedProps, children });
2339
+ };
2340
+ function resolveProps(props, state) {
2341
+ const out = {};
2342
+ for (const [k, v] of Object.entries(props)) {
2343
+ out[k] = resolveBindable(v, state);
2344
+ }
2345
+ return out;
2346
+ }
2347
+
2348
+ // src/artifacts/ui/ui-artifact.tsx
2349
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2350
+ var UiArtifactView = ({ artifact }) => {
2351
+ const [state, dispatch] = (0, import_react13.useReducer)(
2352
+ uiStateReducer,
2353
+ artifact.initialState ?? {}
2354
+ );
2355
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ArtifactCard, { title: artifact.title, kind: "ui", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(UiStateProvider, { state, dispatch, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "aui-ui-root p-3", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(UiNodeView, { node: artifact.root }) }) }) });
2356
+ };
2357
+
2358
+ // src/artifacts/registry.tsx
2359
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2360
+ var defaultArtifactRenderers = {
2361
+ chart: ChartArtifactView,
2362
+ question: QuestionArtifactView,
2363
+ html: HtmlArtifactView,
2364
+ json: JsonArtifactView,
2365
+ table: TableArtifactView,
2366
+ ui: UiArtifactView
2367
+ };
2368
+ var ArtifactRegistryContext = (0, import_react14.createContext)(
2369
+ defaultArtifactRenderers
2370
+ );
2371
+ var ArtifactRegistryProvider = ({ renderers, override, children }) => {
2372
+ const merged = (0, import_react14.useMemo)(() => {
2373
+ if (!renderers) return defaultArtifactRenderers;
2374
+ if (override) return renderers;
2375
+ return { ...defaultArtifactRenderers, ...renderers };
2376
+ }, [renderers, override]);
2377
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ArtifactRegistryContext.Provider, { value: merged, children });
2378
+ };
2379
+ function useArtifactRegistry() {
2380
+ return (0, import_react14.useContext)(ArtifactRegistryContext);
2381
+ }
2382
+ var ArtifactView = ({ artifact }) => {
2383
+ const registry = useArtifactRegistry();
2384
+ const Renderer = registry[artifact.type] ?? registry.json;
2385
+ if (!Renderer) return null;
2386
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Renderer, { artifact });
2387
+ };
2388
+
2389
+ // src/artifacts/parse.ts
2390
+ var ARTIFACT_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
2391
+ "timbal-artifact",
2392
+ "timbal"
2393
+ ]);
2394
+ function isArtifactFenceLanguage(language) {
2395
+ return typeof language === "string" && ARTIFACT_FENCE_LANGUAGES.has(language);
2396
+ }
2397
+ function tryParseStructuredText(text) {
2398
+ const trimmed = text.trim();
2399
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
2400
+ try {
2401
+ return JSON.parse(trimmed);
2402
+ } catch {
2403
+ }
2404
+ try {
2405
+ const asJson = trimmed.replace(/\bTrue\b/g, "true").replace(/\bFalse\b/g, "false").replace(/\bNone\b/g, "null").replace(/'/g, '"');
2406
+ return JSON.parse(asJson);
2407
+ } catch {
2408
+ return null;
2409
+ }
2410
+ }
2411
+ function parseArtifactFromToolResult(result) {
2412
+ if (result === void 0 || result === null) return null;
2413
+ if (typeof result === "string") {
2414
+ const parsed = tryParseStructuredText(result);
2415
+ if (parsed === null) return null;
2416
+ return parseArtifactFromToolResult(parsed);
2417
+ }
2418
+ if (Array.isArray(result)) {
2419
+ for (const item of result) {
2420
+ if (typeof item === "object" && item !== null && "text" in item) {
2421
+ const text = item.text;
2422
+ if (typeof text === "string") {
2423
+ const fromText = parseArtifactFromToolResult(text);
2424
+ if (fromText) return fromText;
2425
+ }
2426
+ }
2427
+ }
2428
+ return null;
2429
+ }
2430
+ if (typeof result === "object") {
2431
+ const obj = result;
2432
+ if (obj.type === "text" && typeof obj.text === "string") {
2433
+ return parseArtifactFromToolResult(obj.text);
2434
+ }
2435
+ if (obj.type === "thinking" && typeof obj.thinking === "string") {
2436
+ return parseArtifactFromToolResult(obj.thinking);
2437
+ }
2438
+ }
2439
+ return isArtifact(result) ? result : null;
2440
+ }
2441
+ var FENCE_RE = /```(?:timbal-artifact|timbal)\s*\n([\s\S]*?)\n```/g;
2442
+ function findMarkdownArtifacts(markdown) {
2443
+ const matches = [];
2444
+ FENCE_RE.lastIndex = 0;
2445
+ let m;
2446
+ while ((m = FENCE_RE.exec(markdown)) !== null) {
2447
+ const raw = m[0];
2448
+ const body = m[1];
2449
+ try {
2450
+ const parsed = JSON.parse(body);
2451
+ if (isArtifact(parsed)) {
2452
+ matches.push({
2453
+ artifact: parsed,
2454
+ raw,
2455
+ start: m.index,
2456
+ end: m.index + raw.length
2457
+ });
2458
+ }
2459
+ } catch {
2460
+ }
2461
+ }
2462
+ return matches;
2463
+ }
2464
+ function splitMarkdownByArtifacts(markdown) {
2465
+ const matches = findMarkdownArtifacts(markdown);
2466
+ if (matches.length === 0) return [{ kind: "text", text: markdown }];
2467
+ const segments = [];
2468
+ let cursor = 0;
2469
+ for (const match of matches) {
2470
+ if (match.start > cursor) {
2471
+ segments.push({ kind: "text", text: markdown.slice(cursor, match.start) });
2472
+ }
2473
+ segments.push({ kind: "artifact", artifact: match.artifact });
2474
+ cursor = match.end;
2475
+ }
2476
+ if (cursor < markdown.length) {
2477
+ segments.push({ kind: "text", text: markdown.slice(cursor) });
2478
+ }
2479
+ return segments;
2480
+ }
2481
+
2482
+ // src/components/syntax-highlighter.tsx
2483
+ var import_javascript2 = __toESM(require("shiki/langs/javascript.mjs"), 1);
2484
+ var import_typescript = __toESM(require("shiki/langs/typescript.mjs"), 1);
2485
+ var import_python = __toESM(require("shiki/langs/python.mjs"), 1);
2486
+ var import_html = __toESM(require("shiki/langs/html.mjs"), 1);
2487
+ var import_css = __toESM(require("shiki/langs/css.mjs"), 1);
2488
+ var import_json = __toESM(require("shiki/langs/json.mjs"), 1);
2489
+ var import_bash = __toESM(require("shiki/langs/bash.mjs"), 1);
2490
+ var import_markdown = __toESM(require("shiki/langs/markdown.mjs"), 1);
2491
+ var import_jsx = __toESM(require("shiki/langs/jsx.mjs"), 1);
2492
+ var import_tsx = __toESM(require("shiki/langs/tsx.mjs"), 1);
2493
+ var import_sql = __toESM(require("shiki/langs/sql.mjs"), 1);
2494
+ var import_yaml = __toESM(require("shiki/langs/yaml.mjs"), 1);
2495
+ var import_rust = __toESM(require("shiki/langs/rust.mjs"), 1);
2496
+ var import_go = __toESM(require("shiki/langs/go.mjs"), 1);
2497
+ var import_java = __toESM(require("shiki/langs/java.mjs"), 1);
2498
+ var import_c = __toESM(require("shiki/langs/c.mjs"), 1);
2499
+ var import_cpp = __toESM(require("shiki/langs/cpp.mjs"), 1);
2500
+ var import_vitesse_dark = __toESM(require("shiki/themes/vitesse-dark.mjs"), 1);
2501
+ var import_vitesse_light = __toESM(require("shiki/themes/vitesse-light.mjs"), 1);
2502
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2503
+ var SHIKI_THEME_DARK = "vitesse-dark";
2504
+ var SHIKI_THEME_LIGHT = "vitesse-light";
2505
+ var highlighterPromise = null;
2506
+ function getHighlighter() {
2507
+ if (!highlighterPromise) {
2508
+ highlighterPromise = (0, import_core.createHighlighterCore)({
2509
+ themes: [import_vitesse_dark.default, import_vitesse_light.default],
2510
+ langs: [
2511
+ import_javascript2.default,
2512
+ import_typescript.default,
2513
+ import_python.default,
2514
+ import_html.default,
2515
+ import_css.default,
2516
+ import_json.default,
2517
+ import_bash.default,
2518
+ import_markdown.default,
2519
+ import_jsx.default,
2520
+ import_tsx.default,
2521
+ import_sql.default,
2522
+ import_yaml.default,
2523
+ import_rust.default,
2524
+ import_go.default,
2525
+ import_java.default,
2526
+ import_c.default,
2527
+ import_cpp.default
2528
+ ],
2529
+ engine: (0, import_javascript.createJavaScriptRegexEngine)()
2530
+ });
2531
+ }
2532
+ return highlighterPromise;
2533
+ }
2534
+ getHighlighter();
2535
+ var ShikiSyntaxHighlighter = ({
2536
+ components: { Pre, Code: Code2 },
2537
+ language,
2538
+ code
2539
+ }) => {
2540
+ const [html, setHtml] = (0, import_react15.useState)(null);
2541
+ (0, import_react15.useEffect)(() => {
2542
+ let cancelled = false;
2543
+ (async () => {
2544
+ try {
2545
+ const highlighter = await getHighlighter();
2546
+ const loadedLangs = highlighter.getLoadedLanguages();
2547
+ if (!loadedLangs.includes(language)) {
929
2548
  if (!cancelled) setHtml(null);
930
2549
  return;
931
2550
  }
@@ -945,8 +2564,17 @@ var ShikiSyntaxHighlighter = ({
945
2564
  cancelled = true;
946
2565
  };
947
2566
  }, [code, language]);
2567
+ if (isArtifactFenceLanguage(language)) {
2568
+ try {
2569
+ const parsed = JSON.parse(code);
2570
+ if (isArtifact(parsed)) {
2571
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ArtifactView, { artifact: parsed });
2572
+ }
2573
+ } catch {
2574
+ }
2575
+ }
948
2576
  if (html) {
949
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2577
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
950
2578
  "div",
951
2579
  {
952
2580
  className: "shiki-wrapper [&>pre]:!m-0 [&>pre]:!rounded-t-none [&>pre]:!rounded-b-lg [&>pre]:!border [&>pre]:!border-t-0 [&>pre]:!border-border/50 [&>pre]:!p-3 [&>pre]:!text-xs [&>pre]:!leading-relaxed [&>pre]:overflow-x-auto",
@@ -954,14 +2582,14 @@ var ShikiSyntaxHighlighter = ({
954
2582
  }
955
2583
  );
956
2584
  }
957
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Pre, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Code2, { children: code }) });
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Pre, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Code2, { children: code }) });
958
2586
  };
959
2587
  var syntax_highlighter_default = ShikiSyntaxHighlighter;
960
2588
 
961
2589
  // src/components/markdown-text.tsx
962
- var import_jsx_runtime9 = require("react/jsx-runtime");
2590
+ var import_jsx_runtime19 = require("react/jsx-runtime");
963
2591
  var MarkdownTextImpl = () => {
964
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2592
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
965
2593
  import_react_markdown.MarkdownTextPrimitive,
966
2594
  {
967
2595
  remarkPlugins: [import_remark_gfm.default, import_remark_math.default],
@@ -974,27 +2602,28 @@ var MarkdownTextImpl = () => {
974
2602
  }
975
2603
  );
976
2604
  };
977
- var MarkdownText = (0, import_react7.memo)(MarkdownTextImpl);
2605
+ var MarkdownText = (0, import_react16.memo)(MarkdownTextImpl);
978
2606
  var CodeHeader = ({ language, code }) => {
979
2607
  const { isCopied, copyToClipboard } = useCopyToClipboard();
2608
+ if (isArtifactFenceLanguage(language)) return null;
980
2609
  const onCopy = () => {
981
2610
  if (!code || isCopied) return;
982
2611
  copyToClipboard(code);
983
2612
  };
984
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "aui-code-header flex items-center justify-between rounded-t-lg border border-b-0 border-border/50 bg-zinc-100 px-4 py-2 dark:bg-zinc-800/80", children: [
985
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-wide text-muted-foreground/80 uppercase", children: [
986
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "inline-block h-2 w-2 rounded-full bg-primary/40" }),
2613
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "aui-code-header flex items-center justify-between rounded-t-lg border border-b-0 border-border/50 bg-zinc-100 px-4 py-2 dark:bg-zinc-800/80", children: [
2614
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "flex items-center gap-2 text-xs font-semibold tracking-wide text-muted-foreground/80 uppercase", children: [
2615
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "inline-block h-2 w-2 rounded-full bg-primary/40" }),
987
2616
  language
988
2617
  ] }),
989
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2618
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
990
2619
  TooltipIconButton,
991
2620
  {
992
2621
  tooltip: isCopied ? "Copied!" : "Copy",
993
2622
  onClick: onCopy,
994
2623
  className: "transition-colors hover:text-foreground",
995
2624
  children: [
996
- !isCopied && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.CopyIcon, { className: "h-3.5 w-3.5" }),
997
- isCopied && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.CheckIcon, { className: "h-3.5 w-3.5 text-emerald-500" })
2625
+ !isCopied && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.CopyIcon, { className: "h-3.5 w-3.5" }),
2626
+ isCopied && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react4.CheckIcon, { className: "h-3.5 w-3.5 text-emerald-500" })
998
2627
  ]
999
2628
  }
1000
2629
  )
@@ -1003,7 +2632,7 @@ var CodeHeader = ({ language, code }) => {
1003
2632
  var useCopyToClipboard = ({
1004
2633
  copiedDuration = 3e3
1005
2634
  } = {}) => {
1006
- const [isCopied, setIsCopied] = (0, import_react7.useState)(false);
2635
+ const [isCopied, setIsCopied] = (0, import_react16.useState)(false);
1007
2636
  const copyToClipboard = (value) => {
1008
2637
  if (!value) return;
1009
2638
  navigator.clipboard.writeText(value).then(() => {
@@ -1014,7 +2643,7 @@ var useCopyToClipboard = ({
1014
2643
  return { isCopied, copyToClipboard };
1015
2644
  };
1016
2645
  var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownComponents)({
1017
- h1: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2646
+ h1: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1018
2647
  "h1",
1019
2648
  {
1020
2649
  className: cn(
@@ -1024,7 +2653,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1024
2653
  ...props
1025
2654
  }
1026
2655
  ),
1027
- h2: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2656
+ h2: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1028
2657
  "h2",
1029
2658
  {
1030
2659
  className: cn(
@@ -1034,7 +2663,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1034
2663
  ...props
1035
2664
  }
1036
2665
  ),
1037
- h3: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2666
+ h3: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1038
2667
  "h3",
1039
2668
  {
1040
2669
  className: cn(
@@ -1044,7 +2673,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1044
2673
  ...props
1045
2674
  }
1046
2675
  ),
1047
- h4: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2676
+ h4: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1048
2677
  "h4",
1049
2678
  {
1050
2679
  className: cn(
@@ -1054,7 +2683,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1054
2683
  ...props
1055
2684
  }
1056
2685
  ),
1057
- h5: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2686
+ h5: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1058
2687
  "h5",
1059
2688
  {
1060
2689
  className: cn(
@@ -1064,7 +2693,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1064
2693
  ...props
1065
2694
  }
1066
2695
  ),
1067
- h6: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2696
+ h6: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1068
2697
  "h6",
1069
2698
  {
1070
2699
  className: cn(
@@ -1074,7 +2703,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1074
2703
  ...props
1075
2704
  }
1076
2705
  ),
1077
- p: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2706
+ p: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1078
2707
  "p",
1079
2708
  {
1080
2709
  className: cn(
@@ -1084,7 +2713,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1084
2713
  ...props
1085
2714
  }
1086
2715
  ),
1087
- a: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2716
+ a: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1088
2717
  "a",
1089
2718
  {
1090
2719
  className: cn(
@@ -1096,7 +2725,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1096
2725
  ...props
1097
2726
  }
1098
2727
  ),
1099
- blockquote: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2728
+ blockquote: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1100
2729
  "blockquote",
1101
2730
  {
1102
2731
  className: cn(
@@ -1106,7 +2735,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1106
2735
  ...props
1107
2736
  }
1108
2737
  ),
1109
- ul: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2738
+ ul: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1110
2739
  "ul",
1111
2740
  {
1112
2741
  className: cn(
@@ -1116,7 +2745,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1116
2745
  ...props
1117
2746
  }
1118
2747
  ),
1119
- ol: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2748
+ ol: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1120
2749
  "ol",
1121
2750
  {
1122
2751
  className: cn(
@@ -1126,7 +2755,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1126
2755
  ...props
1127
2756
  }
1128
2757
  ),
1129
- hr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2758
+ hr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1130
2759
  "hr",
1131
2760
  {
1132
2761
  className: cn(
@@ -1136,14 +2765,14 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1136
2765
  ...props
1137
2766
  }
1138
2767
  ),
1139
- table: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "my-4 w-full overflow-x-auto rounded-lg border border-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2768
+ table: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "my-4 w-full overflow-x-auto rounded-lg border border-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1140
2769
  "table",
1141
2770
  {
1142
2771
  className: cn("aui-md-table w-full border-collapse text-sm", className),
1143
2772
  ...props
1144
2773
  }
1145
2774
  ) }),
1146
- th: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2775
+ th: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1147
2776
  "th",
1148
2777
  {
1149
2778
  className: cn(
@@ -1153,7 +2782,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1153
2782
  ...props
1154
2783
  }
1155
2784
  ),
1156
- td: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2785
+ td: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1157
2786
  "td",
1158
2787
  {
1159
2788
  className: cn(
@@ -1163,7 +2792,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1163
2792
  ...props
1164
2793
  }
1165
2794
  ),
1166
- tr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2795
+ tr: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1167
2796
  "tr",
1168
2797
  {
1169
2798
  className: cn(
@@ -1173,8 +2802,8 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1173
2802
  ...props
1174
2803
  }
1175
2804
  ),
1176
- li: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("li", { className: cn("aui-md-li leading-[1.7]", className), ...props }),
1177
- sup: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2805
+ li: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("li", { className: cn("aui-md-li leading-[1.7]", className), ...props }),
2806
+ sup: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1178
2807
  "sup",
1179
2808
  {
1180
2809
  className: cn(
@@ -1184,7 +2813,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1184
2813
  ...props
1185
2814
  }
1186
2815
  ),
1187
- pre: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2816
+ pre: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1188
2817
  "pre",
1189
2818
  {
1190
2819
  className: cn(
@@ -1196,7 +2825,7 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1196
2825
  ),
1197
2826
  code: function Code({ className, ...props }) {
1198
2827
  const isCodeBlock = (0, import_react_markdown.useIsMarkdownCodeBlock)();
1199
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2828
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1200
2829
  "code",
1201
2830
  {
1202
2831
  className: cn(
@@ -1207,19 +2836,19 @@ var defaultComponents = (0, import_react_markdown.unstable_memoizeMarkdownCompon
1207
2836
  }
1208
2837
  );
1209
2838
  },
1210
- strong: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { className: cn("font-semibold text-foreground", className), ...props }),
1211
- em: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("em", { className: cn("italic", className), ...props }),
2839
+ strong: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("strong", { className: cn("font-semibold text-foreground", className), ...props }),
2840
+ em: ({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("em", { className: cn("italic", className), ...props }),
1212
2841
  CodeHeader
1213
2842
  });
1214
2843
 
1215
2844
  // src/components/tool-fallback.tsx
1216
- var import_react10 = require("react");
1217
- var import_lucide_react4 = require("lucide-react");
2845
+ var import_react19 = require("react");
2846
+ var import_lucide_react5 = require("lucide-react");
1218
2847
 
1219
2848
  // src/ui/shimmer.tsx
1220
- var import_react8 = require("motion/react");
1221
- var import_react9 = require("react");
1222
- var import_jsx_runtime10 = require("react/jsx-runtime");
2849
+ var import_react17 = require("motion/react");
2850
+ var import_react18 = require("react");
2851
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1223
2852
  var ShimmerComponent = ({
1224
2853
  children,
1225
2854
  as: Component = "p",
@@ -1227,14 +2856,14 @@ var ShimmerComponent = ({
1227
2856
  duration = 2,
1228
2857
  spread = 2
1229
2858
  }) => {
1230
- const MotionComponent = import_react8.motion.create(
2859
+ const MotionComponent = import_react17.motion.create(
1231
2860
  Component
1232
2861
  );
1233
- const dynamicSpread = (0, import_react9.useMemo)(
2862
+ const dynamicSpread = (0, import_react18.useMemo)(
1234
2863
  () => (children?.length ?? 0) * spread,
1235
2864
  [children, spread]
1236
2865
  );
1237
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2866
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1238
2867
  MotionComponent,
1239
2868
  {
1240
2869
  animate: { backgroundPosition: "0% center" },
@@ -1257,36 +2886,303 @@ var ShimmerComponent = ({
1257
2886
  }
1258
2887
  );
1259
2888
  };
1260
- var Shimmer = (0, import_react9.memo)(ShimmerComponent);
2889
+ var Shimmer = (0, import_react18.memo)(ShimmerComponent);
1261
2890
 
1262
2891
  // src/components/tool-fallback.tsx
1263
- var import_jsx_runtime11 = require("react/jsx-runtime");
2892
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1264
2893
  var ToolFallbackImpl = ({
1265
2894
  toolName,
2895
+ argsText,
2896
+ result,
1266
2897
  status
1267
2898
  }) => {
1268
- if (status?.type !== "running") return null;
1269
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 py-1 text-sm text-muted-foreground", children: [
1270
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react4.WrenchIcon, { className: "size-4" }),
1271
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Shimmer, { as: "span", duration: 1.8, spread: 2.5, children: `Using tool: ${toolName}` })
1272
- ] });
2899
+ const isRunning = status?.type === "running";
2900
+ const isError = status?.type === "incomplete" && status.reason !== "cancelled";
2901
+ if (isRunning) {
2902
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "aui-tool-fallback-running flex items-center gap-2 py-1 text-sm text-muted-foreground", children: [
2903
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react5.WrenchIcon, { className: "size-4" }),
2904
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Shimmer, { as: "span", duration: 1.8, spread: 2.5, children: `Using tool: ${toolName}` })
2905
+ ] });
2906
+ }
2907
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2908
+ ToolPanel,
2909
+ {
2910
+ toolName,
2911
+ argsText,
2912
+ result,
2913
+ isError
2914
+ }
2915
+ );
2916
+ };
2917
+ var ToolPanel = ({ toolName, argsText, result, isError }) => {
2918
+ const [open, setOpen] = (0, import_react19.useState)(false);
2919
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2920
+ "div",
2921
+ {
2922
+ className: cn(
2923
+ "aui-tool-fallback-root my-2 overflow-hidden rounded-lg border border-border/60 bg-muted/30 text-sm",
2924
+ isError && "border-destructive/50 bg-destructive/5"
2925
+ ),
2926
+ children: [
2927
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2928
+ "button",
2929
+ {
2930
+ type: "button",
2931
+ onClick: () => setOpen((v) => !v),
2932
+ className: "aui-tool-fallback-header flex w-full items-center gap-2 px-3 py-2 text-left text-muted-foreground transition-colors hover:bg-muted/50",
2933
+ "aria-expanded": open,
2934
+ children: [
2935
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react5.WrenchIcon, { className: "size-3.5" }),
2936
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "aui-tool-fallback-name flex-1 truncate font-mono text-xs font-medium text-foreground/80", children: toolName }),
2937
+ isError && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "aui-tool-fallback-status text-xs font-medium text-destructive", children: "error" }),
2938
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2939
+ import_lucide_react5.ChevronDownIcon,
2940
+ {
2941
+ className: cn(
2942
+ "size-3.5 shrink-0 transition-transform",
2943
+ open && "rotate-180"
2944
+ )
2945
+ }
2946
+ )
2947
+ ]
2948
+ }
2949
+ ),
2950
+ open && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "aui-tool-fallback-body grid gap-2 border-t border-border/40 bg-background/50 px-3 py-2.5 text-xs", children: [
2951
+ argsText && argsText !== "{}" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Section, { label: "Input", value: argsText }),
2952
+ result !== void 0 && result !== null && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Section, { label: "Output", value: formatResult(result) })
2953
+ ] })
2954
+ ]
2955
+ }
2956
+ );
1273
2957
  };
1274
- var ToolFallback = (0, import_react10.memo)(
2958
+ var Section = ({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "aui-tool-fallback-section", children: [
2959
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "aui-tool-fallback-section-label mb-0.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
2960
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("pre", { className: "aui-tool-fallback-section-value overflow-x-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground/85", children: value })
2961
+ ] });
2962
+ function formatResult(result) {
2963
+ if (typeof result === "string") return result;
2964
+ try {
2965
+ return JSON.stringify(result, null, 2);
2966
+ } catch {
2967
+ return String(result);
2968
+ }
2969
+ }
2970
+ var ToolFallback = (0, import_react19.memo)(
1275
2971
  ToolFallbackImpl
1276
2972
  );
1277
2973
  ToolFallback.displayName = "ToolFallback";
1278
2974
 
2975
+ // src/artifacts/tool-artifact.tsx
2976
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2977
+ var ToolArtifactFallback = (props) => {
2978
+ const registry = useArtifactRegistry();
2979
+ const isRunning = props.status?.type === "running";
2980
+ if (!isRunning) {
2981
+ const artifact = parseArtifactFromToolResult(props.result);
2982
+ if (artifact) {
2983
+ const Renderer = registry[artifact.type];
2984
+ if (Renderer) {
2985
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Renderer, { artifact });
2986
+ }
2987
+ }
2988
+ }
2989
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolFallback, { ...props });
2990
+ };
2991
+
2992
+ // src/components/composer.tsx
2993
+ var import_react20 = require("@assistant-ui/react");
2994
+ var import_lucide_react6 = require("lucide-react");
2995
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2996
+ var Composer = ({
2997
+ placeholder = "Send a message...",
2998
+ showAttachments = true,
2999
+ toolbar,
3000
+ sendTooltip = "Send message",
3001
+ noAutoFocus,
3002
+ className
3003
+ }) => {
3004
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3005
+ import_react20.ComposerPrimitive.Root,
3006
+ {
3007
+ className: cn(
3008
+ "aui-composer-root relative mt-3 flex w-full flex-col",
3009
+ className
3010
+ ),
3011
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3012
+ import_react20.ComposerPrimitive.AttachmentDropzone,
3013
+ {
3014
+ className: "aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50",
3015
+ children: [
3016
+ showAttachments && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ComposerAttachments, {}),
3017
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ComposerInput, { placeholder, autoFocus: !noAutoFocus }),
3018
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3019
+ ComposerToolbar,
3020
+ {
3021
+ showAttachments,
3022
+ toolbar,
3023
+ sendTooltip
3024
+ }
3025
+ )
3026
+ ]
3027
+ }
3028
+ )
3029
+ }
3030
+ );
3031
+ };
3032
+ var ComposerInput = ({
3033
+ placeholder,
3034
+ autoFocus
3035
+ }) => {
3036
+ const composer = (0, import_react20.useComposerRuntime)();
3037
+ const onKeyDown = (e) => {
3038
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
3039
+ e.preventDefault();
3040
+ composer.send();
3041
+ }
3042
+ };
3043
+ const onInput = (e) => {
3044
+ const el = e.currentTarget;
3045
+ el.style.height = "auto";
3046
+ el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
3047
+ };
3048
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3049
+ import_react20.ComposerPrimitive.Input,
3050
+ {
3051
+ placeholder,
3052
+ className: "aui-composer-input mb-1 max-h-60 min-h-12 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0",
3053
+ rows: 1,
3054
+ autoFocus,
3055
+ "aria-label": "Message input",
3056
+ onKeyDown,
3057
+ onInput
3058
+ }
3059
+ );
3060
+ };
3061
+ var ComposerToolbar = ({ showAttachments, toolbar, sendTooltip }) => {
3062
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center gap-1", children: [
3063
+ showAttachments && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ComposerAddAttachment, {}),
3064
+ toolbar,
3065
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1" }),
3066
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ComposerSendOrCancel, { sendTooltip })
3067
+ ] });
3068
+ };
3069
+ var ComposerSendOrCancel = ({ sendTooltip }) => {
3070
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
3071
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react20.AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react20.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3072
+ TooltipIconButton,
3073
+ {
3074
+ tooltip: sendTooltip,
3075
+ side: "bottom",
3076
+ type: "submit",
3077
+ variant: "default",
3078
+ size: "icon",
3079
+ className: "aui-composer-send size-8 rounded-full",
3080
+ "aria-label": "Send message",
3081
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react6.ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
3082
+ }
3083
+ ) }) }),
3084
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react20.AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react20.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3085
+ Button,
3086
+ {
3087
+ type: "button",
3088
+ variant: "default",
3089
+ size: "icon",
3090
+ className: "aui-composer-cancel size-8 rounded-full",
3091
+ "aria-label": "Stop generating",
3092
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react6.SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
3093
+ }
3094
+ ) }) })
3095
+ ] });
3096
+ };
3097
+
3098
+ // src/components/suggestions.tsx
3099
+ var import_react21 = require("react");
3100
+ var import_react22 = require("@assistant-ui/react");
3101
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3102
+ var Suggestions = ({
3103
+ suggestions,
3104
+ layout = "grid",
3105
+ className
3106
+ }) => {
3107
+ const items = useResolvedSuggestions(suggestions);
3108
+ if (!items || items.length === 0) return null;
3109
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3110
+ "div",
3111
+ {
3112
+ className: cn(
3113
+ "aui-thread-suggestions w-full pb-4",
3114
+ layout === "grid" ? "grid gap-2 @md:grid-cols-2" : "flex gap-2 overflow-x-auto pb-1 [&::-webkit-scrollbar]:hidden",
3115
+ className
3116
+ ),
3117
+ children: items.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SuggestionChip, { suggestion: s, compact: layout === "row" }, s.title + i))
3118
+ }
3119
+ );
3120
+ };
3121
+ var SuggestionChip = ({
3122
+ suggestion,
3123
+ compact
3124
+ }) => {
3125
+ const runtime = (0, import_react22.useThreadRuntime)();
3126
+ const onClick = () => {
3127
+ const text = suggestion.prompt ?? suggestion.title;
3128
+ runtime.append({ role: "user", content: [{ type: "text", text }] });
3129
+ };
3130
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "aui-thread-suggestion-display fade-in slide-in-from-bottom-2 animate-in fill-mode-both duration-200", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3131
+ Button,
3132
+ {
3133
+ variant: "ghost",
3134
+ onClick,
3135
+ className: cn(
3136
+ "aui-thread-suggestion h-auto rounded-2xl border text-left text-sm transition-colors hover:bg-muted",
3137
+ compact ? "shrink-0 flex-row items-center gap-2 whitespace-nowrap px-3 py-2" : "w-full flex-wrap items-start justify-start gap-1 px-4 py-3 @md:flex-col"
3138
+ ),
3139
+ children: [
3140
+ suggestion.icon && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "aui-thread-suggestion-icon shrink-0 text-muted-foreground", children: suggestion.icon }),
3141
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "aui-thread-suggestion-text-1 font-medium", children: suggestion.title }),
3142
+ suggestion.description && !compact && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "aui-thread-suggestion-text-2 text-muted-foreground", children: suggestion.description })
3143
+ ]
3144
+ }
3145
+ ) });
3146
+ };
3147
+ function useResolvedSuggestions(source) {
3148
+ const [resolved, setResolved] = (0, import_react21.useState)(
3149
+ () => Array.isArray(source) ? source : void 0
3150
+ );
3151
+ (0, import_react21.useEffect)(() => {
3152
+ if (!source) {
3153
+ setResolved(void 0);
3154
+ return;
3155
+ }
3156
+ if (Array.isArray(source)) {
3157
+ setResolved(source);
3158
+ return;
3159
+ }
3160
+ let cancelled = false;
3161
+ Promise.resolve().then(() => source()).then((value) => {
3162
+ if (!cancelled) setResolved(value);
3163
+ }).catch(() => {
3164
+ if (!cancelled) setResolved([]);
3165
+ });
3166
+ return () => {
3167
+ cancelled = true;
3168
+ };
3169
+ }, [source]);
3170
+ return (0, import_react21.useMemo)(() => resolved, [resolved]);
3171
+ }
3172
+
1279
3173
  // src/components/thread.tsx
1280
- var import_react11 = require("@assistant-ui/react");
1281
- var import_lucide_react5 = require("lucide-react");
1282
- var import_jsx_runtime12 = require("react/jsx-runtime");
3174
+ var import_react23 = require("@assistant-ui/react");
3175
+ var import_lucide_react7 = require("lucide-react");
3176
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1283
3177
  var Thread = ({
1284
3178
  className,
1285
3179
  maxWidth = "44rem",
1286
3180
  welcome,
1287
3181
  suggestions,
1288
3182
  composerPlaceholder = "Send a message...",
1289
- components
3183
+ components,
3184
+ artifacts,
3185
+ onArtifactEvent
1290
3186
  }) => {
1291
3187
  const WelcomeSlot = components?.Welcome ?? ThreadWelcome;
1292
3188
  const ComposerSlot = components?.Composer ?? Composer;
@@ -1294,59 +3190,79 @@ var Thread = ({
1294
3190
  const AssistantMessageSlot = components?.AssistantMessage ?? AssistantMessage;
1295
3191
  const EditComposerSlot = components?.EditComposer ?? EditComposer;
1296
3192
  const ScrollToBottomSlot = components?.ScrollToBottom ?? ThreadScrollToBottom;
1297
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1298
- import_react11.ThreadPrimitive.Root,
3193
+ const SuggestionsSlot = components?.Suggestions ?? Suggestions;
3194
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3195
+ ArtifactRegistryProvider,
1299
3196
  {
1300
- className: cn(
1301
- "aui-root aui-thread-root @container flex h-full flex-col bg-background",
1302
- className
1303
- ),
1304
- style: { ["--thread-max-width"]: maxWidth },
1305
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1306
- import_react11.ThreadPrimitive.Viewport,
3197
+ renderers: artifacts?.renderers,
3198
+ override: artifacts?.override,
3199
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(UiEventProvider, { onEvent: onArtifactEvent ?? (() => {
3200
+ }), children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3201
+ import_react23.ThreadPrimitive.Root,
1307
3202
  {
1308
- turnAnchor: "bottom",
1309
- className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4 pt-4",
1310
- children: [
1311
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WelcomeSlot, { config: welcome, suggestions }),
1312
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1313
- import_react11.ThreadPrimitive.Messages,
1314
- {
1315
- components: {
1316
- UserMessage: UserMessageSlot,
1317
- EditComposer: EditComposerSlot,
1318
- AssistantMessage: AssistantMessageSlot
1319
- }
1320
- }
1321
- ),
1322
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6", children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ScrollToBottomSlot, {}),
1324
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ComposerSlot, { placeholder: composerPlaceholder })
1325
- ] })
1326
- ]
3203
+ className: cn(
3204
+ "aui-root aui-thread-root @container flex h-full flex-col bg-background",
3205
+ className
3206
+ ),
3207
+ style: { ["--thread-max-width"]: maxWidth },
3208
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3209
+ import_react23.ThreadPrimitive.Viewport,
3210
+ {
3211
+ turnAnchor: "bottom",
3212
+ className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll px-4 pt-4",
3213
+ children: [
3214
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3215
+ WelcomeSlot,
3216
+ {
3217
+ config: welcome,
3218
+ suggestions,
3219
+ Suggestions: SuggestionsSlot
3220
+ }
3221
+ ),
3222
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3223
+ import_react23.ThreadPrimitive.Messages,
3224
+ {
3225
+ components: {
3226
+ UserMessage: UserMessageSlot,
3227
+ EditComposer: EditComposerSlot,
3228
+ AssistantMessage: AssistantMessageSlot
3229
+ }
3230
+ }
3231
+ ),
3232
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react23.ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6", children: [
3233
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScrollToBottomSlot, {}),
3234
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ComposerSlot, { placeholder: composerPlaceholder })
3235
+ ] })
3236
+ ]
3237
+ }
3238
+ )
1327
3239
  }
1328
- )
3240
+ ) })
1329
3241
  }
1330
3242
  );
1331
3243
  };
1332
3244
  var ThreadScrollToBottom = () => {
1333
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3245
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1334
3246
  TooltipIconButton,
1335
3247
  {
1336
3248
  tooltip: "Scroll to bottom",
1337
3249
  variant: "outline",
1338
3250
  className: "aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent",
1339
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.ArrowDownIcon, {})
3251
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.ArrowDownIcon, {})
1340
3252
  }
1341
3253
  ) });
1342
3254
  };
1343
- var ThreadWelcome = ({ config, suggestions }) => {
1344
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.thread.isEmpty, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
1345
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-thread-welcome-message flex size-full flex-col items-center justify-center px-4 text-center", children: [
1346
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fade-in animate-in fill-mode-both relative mb-6 flex size-14 items-center justify-center duration-300", children: [
1347
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "animate-ai-ring-glow absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 ring-1 ring-primary/15" }),
1348
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "animate-ai-pulse-ring absolute inset-0" }),
1349
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3255
+ var ThreadWelcome = ({
3256
+ config,
3257
+ suggestions,
3258
+ Suggestions: SuggestionsSlot = Suggestions
3259
+ }) => {
3260
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.AuiIf, { condition: (s) => s.thread.isEmpty, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
3261
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "aui-thread-welcome-message flex size-full flex-col items-center justify-center px-4 text-center", children: [
3262
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "fade-in animate-in fill-mode-both relative mb-6 flex size-14 items-center justify-center duration-300", children: [
3263
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "animate-ai-ring-glow absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 ring-1 ring-primary/15" }),
3264
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "animate-ai-pulse-ring absolute inset-0" }),
3265
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1350
3266
  "svg",
1351
3267
  {
1352
3268
  xmlns: "http://www.w3.org/2000/svg",
@@ -1357,139 +3273,74 @@ var ThreadWelcome = ({ config, suggestions }) => {
1357
3273
  strokeLinecap: "round",
1358
3274
  strokeLinejoin: "round",
1359
3275
  className: "animate-ai-breathe relative size-7 text-primary/75",
1360
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" })
3276
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z" })
1361
3277
  }
1362
3278
  )
1363
3279
  ] }),
1364
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both font-semibold text-2xl duration-200", children: config?.heading ?? "How can I help you today?" }),
1365
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground mt-2 delay-75 duration-200", children: config?.subheading ?? "Send a message to start a conversation." })
3280
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both font-semibold text-2xl duration-200", children: config?.heading ?? "How can I help you today?" }),
3281
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground mt-2 delay-75 duration-200", children: config?.subheading ?? "Send a message to start a conversation." })
1366
3282
  ] }) }),
1367
- suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ThreadSuggestions, { suggestions })
1368
- ] }) });
1369
- };
1370
- var ThreadSuggestions = ({ suggestions }) => {
1371
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ThreadSuggestionItem, { title: s.title, description: s.description }, i)) });
1372
- };
1373
- var ThreadSuggestionItem = ({ title, description }) => {
1374
- const runtime = (0, import_react11.useThreadRuntime)();
1375
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 animate-in fill-mode-both duration-200", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1376
- Button,
1377
- {
1378
- variant: "ghost",
1379
- className: "aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted",
1380
- onClick: () => runtime.append({
1381
- role: "user",
1382
- content: [{ type: "text", text: title }]
1383
- }),
1384
- children: [
1385
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "aui-thread-welcome-suggestion-text-1 font-medium", children: title }),
1386
- description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "aui-thread-welcome-suggestion-text-2 text-muted-foreground", children: description })
1387
- ]
1388
- }
1389
- ) });
1390
- };
1391
- var Composer = ({ placeholder }) => {
1392
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Root, { className: "aui-composer-root relative mt-3 flex w-full flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ComposerPrimitive.AttachmentDropzone, { className: "aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50", children: [
1393
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ComposerAttachments, {}),
1394
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1395
- import_react11.ComposerPrimitive.Input,
1396
- {
1397
- placeholder: placeholder ?? "Send a message...",
1398
- className: "aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0",
1399
- rows: 1,
1400
- autoFocus: true,
1401
- "aria-label": "Message input"
1402
- }
1403
- ),
1404
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ComposerAction, {})
3283
+ suggestions && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SuggestionsSlot, { suggestions })
1405
3284
  ] }) });
1406
3285
  };
1407
- var ComposerAction = () => {
1408
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-end", children: [
1409
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1410
- TooltipIconButton,
1411
- {
1412
- tooltip: "Send message",
1413
- side: "bottom",
1414
- type: "submit",
1415
- variant: "default",
1416
- size: "icon",
1417
- className: "aui-composer-send size-8 rounded-full",
1418
- "aria-label": "Send message",
1419
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
1420
- }
1421
- ) }) }),
1422
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1423
- Button,
1424
- {
1425
- type: "button",
1426
- variant: "default",
1427
- size: "icon",
1428
- className: "aui-composer-cancel size-8 rounded-full",
1429
- "aria-label": "Stop generating",
1430
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
1431
- }
1432
- ) }) })
1433
- ] });
1434
- };
1435
3286
  var MessageError = () => {
1436
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Error, { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ErrorPrimitive.Root, { className: "aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
3287
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.MessagePrimitive.Error, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ErrorPrimitive.Root, { className: "aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
1437
3288
  };
1438
3289
  var AssistantMessage = () => {
1439
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1440
- import_react11.MessagePrimitive.Root,
3290
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3291
+ import_react23.MessagePrimitive.Root,
1441
3292
  {
1442
3293
  className: "aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-3 duration-150",
1443
3294
  "data-role": "assistant",
1444
3295
  children: [
1445
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed", children: [
1446
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1447
- import_react11.MessagePrimitive.Parts,
3296
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed", children: [
3297
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3298
+ import_react23.MessagePrimitive.Parts,
1448
3299
  {
1449
3300
  components: {
1450
3301
  Text: MarkdownText,
1451
- tools: { Fallback: ToolFallback }
3302
+ tools: { Fallback: ToolArtifactFallback }
1452
3303
  }
1453
3304
  }
1454
3305
  ),
1455
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MessageError, {})
3306
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageError, {})
1456
3307
  ] }),
1457
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-assistant-message-footer mt-1 ml-2 flex", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AssistantActionBar, {}) })
3308
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "aui-assistant-message-footer mt-1 ml-2 flex", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AssistantActionBar, {}) })
1458
3309
  ]
1459
3310
  }
1460
3311
  );
1461
3312
  };
1462
3313
  var AssistantActionBar = () => {
1463
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1464
- import_react11.ActionBarPrimitive.Root,
3314
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3315
+ import_react23.ActionBarPrimitive.Root,
1465
3316
  {
1466
3317
  hideWhenRunning: true,
1467
3318
  autohide: "not-last",
1468
3319
  autohideFloat: "single-branch",
1469
3320
  className: "aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-md data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm",
1470
3321
  children: [
1471
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(TooltipIconButton, { tooltip: "Copy", children: [
1472
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.CheckIcon, {}) }),
1473
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.AuiIf, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.CopyIcon, {}) })
3322
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(TooltipIconButton, { tooltip: "Copy", children: [
3323
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.AuiIf, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.CheckIcon, {}) }),
3324
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.AuiIf, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.CopyIcon, {}) })
1474
3325
  ] }) }),
1475
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TooltipIconButton, { tooltip: "Refresh", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.RefreshCwIcon, {}) }) }),
1476
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ActionBarMorePrimitive.Root, { children: [
1477
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3326
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipIconButton, { tooltip: "Refresh", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.RefreshCwIcon, {}) }) }),
3327
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react23.ActionBarMorePrimitive.Root, { children: [
3328
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1478
3329
  TooltipIconButton,
1479
3330
  {
1480
3331
  tooltip: "More",
1481
3332
  className: "data-[state=open]:bg-accent",
1482
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.MoreHorizontalIcon, {})
3333
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.MoreHorizontalIcon, {})
1483
3334
  }
1484
3335
  ) }),
1485
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1486
- import_react11.ActionBarMorePrimitive.Content,
3336
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3337
+ import_react23.ActionBarMorePrimitive.Content,
1487
3338
  {
1488
3339
  side: "bottom",
1489
3340
  align: "start",
1490
3341
  className: "aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
1491
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ActionBarMorePrimitive.Item, { className: "aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", children: [
1492
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.DownloadIcon, { className: "size-4" }),
3342
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react23.ActionBarMorePrimitive.Item, { className: "aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", children: [
3343
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.DownloadIcon, { className: "size-4" }),
1493
3344
  "Export as Markdown"
1494
3345
  ] }) })
1495
3346
  }
@@ -1500,65 +3351,372 @@ var AssistantActionBar = () => {
1500
3351
  );
1501
3352
  };
1502
3353
  var UserMessage = () => {
1503
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1504
- import_react11.MessagePrimitive.Root,
3354
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3355
+ import_react23.MessagePrimitive.Root,
1505
3356
  {
1506
3357
  className: "aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-3 duration-150 [&:where(>*)]:col-start-2",
1507
3358
  "data-role": "user",
1508
3359
  children: [
1509
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UserMessageAttachments, {}),
1510
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [
1511
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Parts, {}) }),
1512
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UserActionBar, {}) })
3360
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(UserMessageAttachments, {}),
3361
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [
3362
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.MessagePrimitive.Parts, {}) }),
3363
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(UserActionBar, {}) })
1513
3364
  ] })
1514
3365
  ]
1515
3366
  }
1516
3367
  );
1517
3368
  };
1518
3369
  var UserActionBar = () => {
1519
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1520
- import_react11.ActionBarPrimitive.Root,
3370
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3371
+ import_react23.ActionBarPrimitive.Root,
1521
3372
  {
1522
3373
  hideWhenRunning: true,
1523
3374
  autohide: "not-last",
1524
3375
  className: "aui-user-action-bar-root flex flex-col items-end",
1525
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TooltipIconButton, { tooltip: "Edit", className: "aui-user-action-edit p-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.PencilIcon, {}) }) })
3376
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TooltipIconButton, { tooltip: "Edit", className: "aui-user-action-edit p-4", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react7.PencilIcon, {}) }) })
1526
3377
  }
1527
3378
  );
1528
3379
  };
1529
3380
  var EditComposer = () => {
1530
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.MessagePrimitive.Root, { className: "aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react11.ComposerPrimitive.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted", children: [
1531
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1532
- import_react11.ComposerPrimitive.Input,
3381
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.MessagePrimitive.Root, { className: "aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react23.ComposerPrimitive.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted", children: [
3382
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3383
+ import_react23.ComposerPrimitive.Input,
1533
3384
  {
1534
3385
  className: "aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none",
1535
3386
  autoFocus: true
1536
3387
  }
1537
3388
  ),
1538
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
1539
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
1540
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react11.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button, { size: "sm", children: "Update" }) })
3389
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
3390
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ComposerPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
3391
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react23.ComposerPrimitive.Send, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Button, { size: "sm", children: "Update" }) })
1541
3392
  ] })
1542
3393
  ] }) });
1543
3394
  };
1544
3395
 
1545
3396
  // src/components/chat.tsx
1546
- var import_jsx_runtime13 = require("react/jsx-runtime");
3397
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1547
3398
  function TimbalChat({
1548
3399
  workforceId,
1549
3400
  baseUrl,
1550
3401
  fetch: fetch2,
3402
+ attachments,
3403
+ attachmentsUploadUrl,
3404
+ attachmentsAccept,
3405
+ debug,
1551
3406
  ...threadProps
1552
3407
  }) {
1553
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TimbalRuntimeProvider, { workforceId, baseUrl, fetch: fetch2, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Thread, { ...threadProps }) });
3408
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3409
+ TimbalRuntimeProvider,
3410
+ {
3411
+ workforceId,
3412
+ baseUrl,
3413
+ fetch: fetch2,
3414
+ attachments,
3415
+ attachmentsUploadUrl,
3416
+ attachmentsAccept,
3417
+ debug,
3418
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Thread, { ...threadProps })
3419
+ }
3420
+ );
3421
+ }
3422
+
3423
+ // src/artifacts/agent-instructions.ts
3424
+ var ARTIFACT_AGENT_INSTRUCTIONS = `
3425
+ ## Rich artifacts (Timbal chat UI)
3426
+
3427
+ When you need charts, tables, choice widgets, or interactive controls, return a **JSON artifact object** instead of plain prose. The chat UI renders these automatically.
3428
+
3429
+ ### Delivery channels (either works)
3430
+
3431
+ 1. **Tool result (preferred)** \u2014 return a single JSON object (or a JSON string) from a tool. The object must include a string \`type\` field.
3432
+ 2. **Inline markdown fence** \u2014 embed the same JSON inside a fenced block:
3433
+
3434
+ \`\`\`timbal-artifact
3435
+ {"type":"chart","data":[{"month":"Jan","sales":120}]}
3436
+ \`\`\`
3437
+
3438
+ The alias \`\`\`timbal\`\`\` is also accepted.
3439
+
3440
+ ### Built-in artifact types
3441
+
3442
+ | \`type\` | Use for |
3443
+ |---|---|
3444
+ | \`chart\` | Bar, line, area, or pie charts. Fields: \`data\`, optional \`chartType\`, \`xKey\`, \`dataKey\`, \`title\`, \`unit\`. |
3445
+ | \`table\` | Tabular data. Fields: \`rows\`, optional \`columns\`, \`title\`. |
3446
+ | \`question\` | In-thread multiple choice. Fields: \`options: [{ id, label, description? }]\`, optional \`prompt\`, \`multi\`. User replies are sent back as a normal user message. |
3447
+ | \`html\` | Custom HTML/CSS/JS in an iframe. Fields: \`content\` (HTML document or fragment), optional \`title\`, \`height\`, \`sandboxed\` (default \`true\`; set \`false\` for unrestricted scripts/CDN). |
3448
+ | \`json\` | Fallback structured view. Fields: \`data\`, optional \`title\`. |
3449
+ | \`ui\` | **Interactive UI** composed from a fixed node palette (hover, click, drag). See below. |
3450
+
3451
+ ### When to use \`type: "html"\`
3452
+
3453
+ Use \`html\` when the user wants a **rich visual or interactive page** that does not fit the \`ui\` palette \u2014 e.g. styled layouts, SVG graphics, CSS animations, canvas, small games, calculators, or multi-section mockups. Pass a full HTML document or a body fragment in \`content\`.
3454
+
3455
+ - Inline \`<style>\`, \`<script>\`, SVG, and canvas are supported.
3456
+ - Default \`sandboxed: true\` runs in an isolated iframe with scripts enabled.
3457
+ - Set \`sandboxed: false\` only for trusted content that needs external CDN scripts/styles or full DOM freedom.
3458
+ - Prefer \`ui\` when controls should send chat messages or host events; prefer \`html\` for self-contained mini-apps and visual demos.
3459
+
3460
+ ### When to use \`type: "ui"\`
3461
+
3462
+ Use a \`ui\` artifact when the user should **hover, click, drag, or adjust controls** in-thread and those actions should integrate with the chat runtime (messages, \`onArtifactEvent\`). For standalone visual/interactive HTML, use \`html\` instead.
3463
+
3464
+ Each \`ui\` artifact has:
3465
+
3466
+ - \`initialState\` \u2014 optional object seeding local state (per widget instance).
3467
+ - \`root\` \u2014 a single node tree (see node kinds below).
3468
+ - optional \`title\` \u2014 card heading.
3469
+
3470
+ **Bindings:** anywhere a primitive is accepted, you may use \`{ "$bind": "dotted.path" }\` to read from \`initialState\` / local state (e.g. \`{ "$bind": "qty" }\`).
3471
+
3472
+ **Actions:** nodes may attach \`onClick\`, \`onChange\`, or \`onDragEnd\` with one action or an array:
3473
+
3474
+ | Action | Shape | Effect |
3475
+ |---|---|---|
3476
+ | User message | \`{ "kind": "message", "text": "..." }\` or \`{ "kind": "message", "text": { "$bind": "path" } }\` | Sends text as the next user message. |
3477
+ | Set state | \`{ "kind": "set", "path": "foo", "value": 1 }\` | Writes local widget state. |
3478
+ | Toggle boolean | \`{ "kind": "toggle", "path": "enabled" }\` | Flips a boolean at \`path\`. |
3479
+ | Host event | \`{ "kind": "emit", "name": "event-name", "payload": { ... } }\` | Bubbles to the host app (\`onArtifactEvent\` on \`<Thread>\`). |
3480
+
3481
+ ### \`ui\` node palette (\`root.kind\`)
3482
+
3483
+ | \`kind\` | Purpose | Key fields |
3484
+ |---|---|---|
3485
+ | \`box\` | Layout container | \`children\`, \`direction\` (\`row\`/\`col\`), \`gap\`, \`padding\`, \`align\`, \`justify\`, \`wrap\` |
3486
+ | \`text\` | Body text | \`value\`, optional \`muted\`, \`size\`, \`weight\` |
3487
+ | \`heading\` | Heading | \`value\`, optional \`level\` (1\u20134) |
3488
+ | \`badge\` | Pill label | \`value\`, optional \`tone\` (\`default\`, \`primary\`, \`success\`, \`warn\`, \`danger\`) |
3489
+ | \`button\` | Clickable button | \`label\`, optional \`variant\`, \`size\`, \`disabled\`, \`onClick\` |
3490
+ | \`toggle\` | Boolean switch | \`binding\` (state path), optional \`label\`, \`onChange\` |
3491
+ | \`slider\` | Numeric range | \`binding\`, optional \`min\`, \`max\`, \`step\`, \`label\`, \`showValue\`, \`onChange\` |
3492
+ | \`tooltip\` | Hover tooltip | \`content\`, \`child\` (single node), optional \`side\` |
3493
+ | \`draggable\` | Drag gesture | \`child\`, optional \`axis\` (\`x\`/\`y\`/\`both\`), \`snapBack\`, \`onDragEnd\` |
3494
+ | \`custom\` | Host-registered widget | \`name\`, optional \`props\`, \`children\` \u2014 only if the app registered that name |
3495
+
3496
+ ### Example \`ui\` artifact
3497
+
3498
+ \`\`\`json
3499
+ {
3500
+ "type": "ui",
3501
+ "title": "Configure plan",
3502
+ "initialState": { "qty": 1, "premium": false },
3503
+ "root": {
3504
+ "kind": "box",
3505
+ "direction": "col",
3506
+ "gap": 3,
3507
+ "children": [
3508
+ { "kind": "heading", "value": "Choose quantity", "level": 3 },
3509
+ {
3510
+ "kind": "tooltip",
3511
+ "content": "Drag to adjust quantity",
3512
+ "child": {
3513
+ "kind": "slider",
3514
+ "binding": "qty",
3515
+ "min": 1,
3516
+ "max": 50,
3517
+ "label": "Quantity",
3518
+ "onChange": { "kind": "emit", "name": "qty-changed" }
3519
+ }
3520
+ },
3521
+ { "kind": "toggle", "binding": "premium", "label": "Premium support" },
3522
+ {
3523
+ "kind": "button",
3524
+ "label": "Confirm",
3525
+ "onClick": { "kind": "message", "text": { "$bind": "qty" } }
3526
+ }
3527
+ ]
3528
+ }
1554
3529
  }
3530
+ \`\`\`
3531
+
3532
+ ### Rules
3533
+
3534
+ - Always set \`type\` to a built-in value above unless the app documented a custom type.
3535
+ - Prefer \`ui\` over \`html\` when actions must bubble to the host chat (\`message\`, \`emit\`).
3536
+ - Prefer \`question\` for simple A/B/C choices; use \`ui\` when you need sliders, toggles, drag, or multi-control layouts.
3537
+ - Keep \`data\` arrays reasonably small (charts/tables).
3538
+
3539
+ ### After calling an artifact tool (critical)
3540
+
3541
+ When you call a tool that returns an artifact (\`make_chart\`, \`ask_question\`, \`show_table\`, \`show_html\`, \`make_ui_demo\`, etc.):
3542
+
3543
+ 1. **Do not** paste, quote, paraphrase as JSON, or fence the tool return value in your assistant message. The chat UI already renders it from the tool result.
3544
+ 2. **Do not** emit a matching \`\`\`timbal-artifact\`\`\` block for the same payload \u2014 pick **one** channel (tool result only).
3545
+ 3. Your follow-up text should be **empty**, or at most **one short sentence** (e.g. "Pick an option above." / "Try the controls."). Never include \`type\`, \`options\`, \`data\`, or dict/JSON syntax.
3546
+ 4. Treat the widget as visible to the user; refer to it as "above" / "the chart" / "the choices" \u2014 never reproduce its contents.
3547
+ `.trim();
1555
3548
 
1556
3549
  // src/index.ts
1557
- var import_react13 = require("@assistant-ui/react");
3550
+ var import_react26 = require("@assistant-ui/react");
3551
+
3552
+ // src/hooks/use-workforces.ts
3553
+ var import_react24 = require("react");
3554
+ function useWorkforces(options = {}) {
3555
+ const { baseUrl = "/api", fetch: fetchFn, pickInitial } = options;
3556
+ const [workforces, setWorkforces] = (0, import_react24.useState)([]);
3557
+ const [selectedId, setSelectedId] = (0, import_react24.useState)("");
3558
+ const [isLoading, setIsLoading] = (0, import_react24.useState)(true);
3559
+ const [error, setError] = (0, import_react24.useState)(null);
3560
+ const fetchFnRef = (0, import_react24.useRef)(fetchFn ?? authFetch);
3561
+ (0, import_react24.useEffect)(() => {
3562
+ fetchFnRef.current = fetchFn ?? authFetch;
3563
+ }, [fetchFn]);
3564
+ const pickInitialRef = (0, import_react24.useRef)(pickInitial);
3565
+ (0, import_react24.useEffect)(() => {
3566
+ pickInitialRef.current = pickInitial;
3567
+ }, [pickInitial]);
3568
+ const load = (0, import_react24.useMemo)(() => {
3569
+ return async () => {
3570
+ setIsLoading(true);
3571
+ setError(null);
3572
+ try {
3573
+ const res = await fetchFnRef.current(`${baseUrl}/workforce`);
3574
+ if (!res.ok) throw new Error(`Failed to load workforces (${res.status})`);
3575
+ const data = await res.json();
3576
+ setWorkforces(data);
3577
+ setSelectedId((current) => {
3578
+ if (current && data.some((w) => idOf(w) === current)) return current;
3579
+ const initial = pickInitialRef.current?.(data) ?? data.find((w) => w.type === "agent") ?? data[0];
3580
+ return initial ? idOf(initial) : "";
3581
+ });
3582
+ } catch (err) {
3583
+ setError(err instanceof Error ? err : new Error(String(err)));
3584
+ } finally {
3585
+ setIsLoading(false);
3586
+ }
3587
+ };
3588
+ }, [baseUrl]);
3589
+ (0, import_react24.useEffect)(() => {
3590
+ load();
3591
+ }, [load]);
3592
+ const selected = (0, import_react24.useMemo)(
3593
+ () => workforces.find((w) => idOf(w) === selectedId),
3594
+ [workforces, selectedId]
3595
+ );
3596
+ return {
3597
+ workforces,
3598
+ selectedId,
3599
+ setSelectedId,
3600
+ selected,
3601
+ isLoading,
3602
+ error,
3603
+ refresh: load
3604
+ };
3605
+ }
3606
+ function idOf(item) {
3607
+ return item.id ?? item.uid ?? item.name ?? "";
3608
+ }
3609
+
3610
+ // src/components/workforce-selector.tsx
3611
+ var import_lucide_react8 = require("lucide-react");
3612
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3613
+ var WorkforceSelector = ({
3614
+ workforces,
3615
+ value,
3616
+ onChange,
3617
+ hideWhenSingle = true,
3618
+ className,
3619
+ placeholder = "Select agent"
3620
+ }) => {
3621
+ if (workforces.length === 0) return null;
3622
+ if (hideWhenSingle && workforces.length === 1) return null;
3623
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("aui-workforce-selector relative inline-flex items-center", className), children: [
3624
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3625
+ "select",
3626
+ {
3627
+ className: "aui-workforce-selector-input h-7 cursor-pointer appearance-none rounded-md border-none bg-transparent pr-5 pl-1.5 text-xs font-medium text-muted-foreground shadow-none outline-none ring-0 transition-colors hover:text-foreground focus:ring-0",
3628
+ value,
3629
+ onChange: (e) => onChange(e.target.value),
3630
+ "aria-label": placeholder,
3631
+ children: [
3632
+ !value && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: "", children: placeholder }),
3633
+ workforces.map((w) => {
3634
+ const id = idOf2(w);
3635
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: id, children: w.name ?? id }, id);
3636
+ })
3637
+ ]
3638
+ }
3639
+ ),
3640
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react8.ChevronDownIcon, { className: "aui-workforce-selector-icon pointer-events-none absolute right-1 size-3 text-muted-foreground" })
3641
+ ] });
3642
+ };
3643
+ function idOf2(item) {
3644
+ return item.id ?? item.uid ?? item.name ?? "";
3645
+ }
3646
+
3647
+ // src/components/chat-shell.tsx
3648
+ var import_jsx_runtime28 = require("react/jsx-runtime");
3649
+ var TimbalChatShell = ({
3650
+ workforceId,
3651
+ brand,
3652
+ headerActions,
3653
+ hideWorkforceSelector,
3654
+ className,
3655
+ headerClassName,
3656
+ baseUrl,
3657
+ fetch: fetch2,
3658
+ ...chatProps
3659
+ }) => {
3660
+ const {
3661
+ workforces,
3662
+ selectedId,
3663
+ setSelectedId
3664
+ } = useWorkforces({ baseUrl, fetch: fetch2 });
3665
+ const effectiveId = workforceId ?? selectedId;
3666
+ const showSelector = !hideWorkforceSelector && !workforceId && workforces.length > 0;
3667
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3668
+ "div",
3669
+ {
3670
+ className: cn(
3671
+ "aui-chat-shell flex h-screen flex-col overflow-hidden",
3672
+ className
3673
+ ),
3674
+ children: [
3675
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3676
+ "header",
3677
+ {
3678
+ className: cn(
3679
+ "aui-chat-shell-header flex shrink-0 items-center justify-between border-b border-border/50 bg-background/90 px-5 py-2 backdrop-blur-md",
3680
+ headerClassName
3681
+ ),
3682
+ children: [
3683
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center", children: [
3684
+ brand,
3685
+ showSelector && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
3686
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "mx-3.5 h-3.5 w-px bg-border" }),
3687
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3688
+ WorkforceSelector,
3689
+ {
3690
+ workforces,
3691
+ value: selectedId,
3692
+ onChange: setSelectedId
3693
+ }
3694
+ )
3695
+ ] })
3696
+ ] }),
3697
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex items-center gap-0.5", children: headerActions })
3698
+ ]
3699
+ }
3700
+ ),
3701
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3702
+ TimbalChat,
3703
+ {
3704
+ workforceId: effectiveId,
3705
+ baseUrl,
3706
+ fetch: fetch2,
3707
+ className: "min-h-0 flex-1",
3708
+ ...chatProps
3709
+ },
3710
+ effectiveId
3711
+ )
3712
+ ]
3713
+ }
3714
+ );
3715
+ };
1558
3716
 
1559
3717
  // src/auth/provider.tsx
1560
- var import_react12 = require("react");
1561
- var import_jsx_runtime14 = require("react/jsx-runtime");
3718
+ var import_react25 = require("react");
3719
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1562
3720
  function isInsideIframe() {
1563
3721
  try {
1564
3722
  return typeof window !== "undefined" && window.self !== window.top;
@@ -1566,9 +3724,9 @@ function isInsideIframe() {
1566
3724
  return true;
1567
3725
  }
1568
3726
  }
1569
- var SessionContext = (0, import_react12.createContext)(void 0);
3727
+ var SessionContext = (0, import_react25.createContext)(void 0);
1570
3728
  var useSession = () => {
1571
- const context = (0, import_react12.useContext)(SessionContext);
3729
+ const context = (0, import_react25.useContext)(SessionContext);
1572
3730
  if (context === void 0) {
1573
3731
  throw new Error("useSession must be used within a SessionProvider");
1574
3732
  }
@@ -1578,10 +3736,10 @@ var SessionProvider = ({
1578
3736
  children,
1579
3737
  enabled = true
1580
3738
  }) => {
1581
- const [user, setUser] = (0, import_react12.useState)(null);
1582
- const [loading, setLoading] = (0, import_react12.useState)(enabled);
1583
- const [embedded] = (0, import_react12.useState)(isInsideIframe);
1584
- (0, import_react12.useEffect)(() => {
3739
+ const [user, setUser] = (0, import_react25.useState)(null);
3740
+ const [loading, setLoading] = (0, import_react25.useState)(enabled);
3741
+ const [embedded] = (0, import_react25.useState)(isInsideIframe);
3742
+ (0, import_react25.useEffect)(() => {
1585
3743
  if (!enabled) {
1586
3744
  setLoading(false);
1587
3745
  return;
@@ -1642,7 +3800,7 @@ var SessionProvider = ({
1642
3800
  messageCleanup?.();
1643
3801
  };
1644
3802
  }, [enabled, embedded]);
1645
- const logout = (0, import_react12.useCallback)(() => {
3803
+ const logout = (0, import_react25.useCallback)(() => {
1646
3804
  clearTokens();
1647
3805
  setUser(null);
1648
3806
  const returnTo = encodeURIComponent(
@@ -1652,7 +3810,7 @@ var SessionProvider = ({
1652
3810
  () => window.location.href = `/api/auth/login?return_to=${returnTo}`
1653
3811
  );
1654
3812
  }, []);
1655
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3813
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1656
3814
  SessionContext.Provider,
1657
3815
  {
1658
3816
  value: {
@@ -1668,8 +3826,8 @@ var SessionProvider = ({
1668
3826
  };
1669
3827
 
1670
3828
  // src/auth/guard.tsx
1671
- var import_lucide_react6 = require("lucide-react");
1672
- var import_jsx_runtime15 = require("react/jsx-runtime");
3829
+ var import_lucide_react9 = require("lucide-react");
3830
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1673
3831
  var AuthGuard = ({
1674
3832
  children,
1675
3833
  requireAuth = false,
@@ -1680,7 +3838,7 @@ var AuthGuard = ({
1680
3838
  return children;
1681
3839
  }
1682
3840
  if (loading) {
1683
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react6.Loader2, { className: "w-8 h-8 animate-spin" }) });
3841
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex items-center justify-center h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react9.Loader2, { className: "w-8 h-8 animate-spin" }) });
1684
3842
  }
1685
3843
  if (requireAuth && !isAuthenticated && !isEmbedded) {
1686
3844
  const returnTo = encodeURIComponent(
@@ -1693,15 +3851,24 @@ var AuthGuard = ({
1693
3851
  };
1694
3852
  // Annotate the CommonJS export names for ESM import in node:
1695
3853
  0 && (module.exports = {
3854
+ ARTIFACT_AGENT_INSTRUCTIONS,
3855
+ ARTIFACT_FENCE_LANGUAGES,
1696
3856
  ActionBarPrimitive,
3857
+ ArtifactCard,
3858
+ ArtifactRegistryProvider,
3859
+ ArtifactView,
3860
+ AssistantRuntimeProvider,
1697
3861
  AuthGuard,
1698
3862
  Avatar,
1699
3863
  AvatarFallback,
1700
3864
  AvatarImage,
1701
3865
  Button,
3866
+ ChartArtifactView,
3867
+ Composer,
1702
3868
  ComposerAddAttachment,
1703
3869
  ComposerAttachments,
1704
3870
  ComposerPrimitive,
3871
+ DEFAULT_UPLOAD_ACCEPT,
1705
3872
  Dialog,
1706
3873
  DialogClose,
1707
3874
  DialogContent,
@@ -1709,35 +3876,70 @@ var AuthGuard = ({
1709
3876
  DialogPortal,
1710
3877
  DialogTitle,
1711
3878
  DialogTrigger,
3879
+ HtmlArtifactView,
3880
+ JsonArtifactView,
1712
3881
  MarkdownText,
1713
3882
  MessagePrimitive,
3883
+ QuestionArtifactView,
1714
3884
  SessionProvider,
1715
3885
  Shimmer,
3886
+ Suggestions,
1716
3887
  SyntaxHighlighter,
3888
+ TableArtifactView,
1717
3889
  Thread,
1718
3890
  ThreadPrimitive,
1719
3891
  TimbalChat,
3892
+ TimbalChatShell,
1720
3893
  TimbalRuntimeProvider,
3894
+ ToolArtifactFallback,
1721
3895
  ToolFallback,
1722
3896
  Tooltip,
1723
3897
  TooltipContent,
1724
3898
  TooltipIconButton,
1725
3899
  TooltipProvider,
1726
3900
  TooltipTrigger,
3901
+ UiArtifactView,
3902
+ UiCustomNodeRegistryProvider,
3903
+ UiEventProvider,
3904
+ UiNodeView,
1727
3905
  UserMessageAttachments,
3906
+ WorkforceSelector,
1728
3907
  authFetch,
1729
3908
  buttonVariants,
1730
3909
  clearTokens,
1731
3910
  cn,
3911
+ createDefaultAttachmentAdapter,
3912
+ createUploadAttachmentAdapter,
3913
+ defaultArtifactRenderers,
1732
3914
  fetchCurrentUser,
3915
+ findMarkdownArtifacts,
1733
3916
  getAccessToken,
3917
+ getPath,
1734
3918
  getRefreshToken,
3919
+ isArtifact,
3920
+ isArtifactFenceLanguage,
3921
+ isUiBinding,
3922
+ parseArtifactFromToolResult,
3923
+ parseSSELine,
1735
3924
  refreshAccessToken,
3925
+ resolveAttachmentAdapter,
3926
+ resolveBindable,
1736
3927
  setAccessToken,
3928
+ setPath,
1737
3929
  setRefreshToken,
3930
+ splitMarkdownByArtifacts,
3931
+ useArtifactRegistry,
1738
3932
  useComposerRuntime,
1739
3933
  useMessageRuntime,
3934
+ useResolvedSuggestions,
1740
3935
  useSession,
1741
3936
  useThread,
1742
- useThreadRuntime
3937
+ useThreadRuntime,
3938
+ useTimbalRuntime,
3939
+ useTimbalStream,
3940
+ useUiCustomNodeRegistry,
3941
+ useUiDispatch,
3942
+ useUiEventEmitter,
3943
+ useUiState,
3944
+ useWorkforces
1743
3945
  });