@sarchauhan/chat 0.0.0-stage → 0.0.1

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.
@@ -0,0 +1,2201 @@
1
+ "use client";
2
+
3
+ // src/widgets/read-file.tsx
4
+ import { useState } from "react";
5
+
6
+ // src/widgets/tool-frame.tsx
7
+ import { ChevronDown } from "lucide-react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ function ToolFrame({
10
+ label,
11
+ title,
12
+ meta,
13
+ state = "complete",
14
+ error,
15
+ className = "",
16
+ children
17
+ }) {
18
+ const status = {
19
+ running: "Working",
20
+ complete: "Done",
21
+ error: "Failed",
22
+ denied: "Denied",
23
+ approval: "Needs approval"
24
+ }[state];
25
+ return /* @__PURE__ */ jsxs(
26
+ "details",
27
+ {
28
+ className: `chat-prebuilt chat-tool-widget ${className}`,
29
+ "data-state": state,
30
+ open: state !== "complete",
31
+ children: [
32
+ /* @__PURE__ */ jsxs("summary", { children: [
33
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, "aria-hidden": "true" }),
34
+ /* @__PURE__ */ jsx("span", { className: "chat-tool-widget-label", children: label }),
35
+ /* @__PURE__ */ jsx("span", { className: "chat-tool-widget-title", title, children: title }),
36
+ /* @__PURE__ */ jsx("span", { className: "chat-tool-widget-meta", children: meta }),
37
+ /* @__PURE__ */ jsx("span", { className: "chat-tool-widget-status", role: "status", children: status })
38
+ ] }),
39
+ /* @__PURE__ */ jsx("div", { className: "chat-tool-widget-body", children: error ? /* @__PURE__ */ jsx("p", { className: "chat-widget-error", role: "alert", children: error }) : children })
40
+ ]
41
+ }
42
+ );
43
+ }
44
+
45
+ // src/widgets/read-file.tsx
46
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
47
+ function ReadFileWidget({
48
+ path,
49
+ content,
50
+ startLine = 1,
51
+ language,
52
+ maxLines = 80,
53
+ ...props
54
+ }) {
55
+ var _a;
56
+ const [expanded, setExpanded] = useState(false);
57
+ const lines = (_a = content == null ? void 0 : content.split(/\r?\n/)) != null ? _a : [];
58
+ const limit = Math.max(1, maxLines);
59
+ const firstLine = Math.max(1, Math.floor(startLine));
60
+ return /* @__PURE__ */ jsxs2(
61
+ ToolFrame,
62
+ {
63
+ ...props,
64
+ label: "Read file",
65
+ title: path,
66
+ meta: lines.length ? `${lines.length} lines` : language,
67
+ children: [
68
+ content === void 0 ? /* @__PURE__ */ jsx2("p", { className: "chat-widget-empty", children: props.state === "running" ? "Reading file\u2026" : "No file content supplied." }) : /* @__PURE__ */ jsx2(
69
+ "div",
70
+ {
71
+ className: "chat-code-preview",
72
+ role: "region",
73
+ "aria-label": `Contents of ${path}`,
74
+ tabIndex: 0,
75
+ children: (expanded ? lines : lines.slice(0, limit)).map((line, index) => /* @__PURE__ */ jsxs2("div", { className: "chat-code-line", children: [
76
+ /* @__PURE__ */ jsx2("span", { className: "chat-line-number", "aria-hidden": "true", children: firstLine + index }),
77
+ /* @__PURE__ */ jsx2("code", { children: line || " " })
78
+ ] }, index))
79
+ }
80
+ ),
81
+ !expanded && lines.length > limit ? /* @__PURE__ */ jsxs2(
82
+ "button",
83
+ {
84
+ className: "chat-widget-more",
85
+ type: "button",
86
+ onClick: () => setExpanded(true),
87
+ children: [
88
+ "Show ",
89
+ lines.length - limit,
90
+ " more lines"
91
+ ]
92
+ }
93
+ ) : null
94
+ ]
95
+ }
96
+ );
97
+ }
98
+
99
+ // src/widgets/patch.tsx
100
+ import { useMemo, useState as useState2 } from "react";
101
+
102
+ // src/widgets/compute.ts
103
+ import { parseArtifact } from "@sarchauhan/protocol";
104
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
105
+ var string = (...values) => values.find((value) => typeof value === "string");
106
+ var number = (...values) => values.find(
107
+ (value) => typeof value === "number" && Number.isFinite(value)
108
+ );
109
+ var aliases = {
110
+ read: "read-file",
111
+ read_file: "read-file",
112
+ readFile: "read-file",
113
+ "read-file": "read-file",
114
+ patch: "patch",
115
+ apply_patch: "patch",
116
+ edit_file: "patch",
117
+ edit: "patch",
118
+ search: "search",
119
+ grep: "search",
120
+ file_search: "search",
121
+ web_search: "search",
122
+ question: "question",
123
+ ask_question: "question"
124
+ };
125
+ function computeToolState(part) {
126
+ if (part.state === "output-error") return "error";
127
+ if (part.state === "output-denied") return "denied";
128
+ if (part.state === "approval-requested") return "approval";
129
+ return part.state === "output-available" && !part.preliminary ? "complete" : "running";
130
+ }
131
+ function normalizeSearchResults(value) {
132
+ if (!Array.isArray(value)) return [];
133
+ return value.flatMap((item) => {
134
+ const result = record(item);
135
+ const path = string(result.path, result.file, result.url);
136
+ if (!path) return [];
137
+ return [
138
+ {
139
+ path,
140
+ line: number(result.line, result.lineNumber),
141
+ text: string(result.text, result.snippet, result.content),
142
+ title: string(result.title),
143
+ url: string(result.url)
144
+ }
145
+ ];
146
+ });
147
+ }
148
+ function computeToolWidget(part) {
149
+ var _a, _b, _c;
150
+ const name = aliases[part.toolName];
151
+ if (!name) {
152
+ const output2 = record(part.output);
153
+ if (part.state !== "output-available") return null;
154
+ if (Array.isArray(output2.artifacts)) {
155
+ const artifacts = output2.artifacts.flatMap((value) => {
156
+ const artifact2 = parseArtifact(value);
157
+ return artifact2 ? [artifact2] : [];
158
+ });
159
+ return artifacts.length ? { type: "widget", name: "artifacts", id: part.toolCallId, props: { artifacts } } : null;
160
+ }
161
+ const artifact = parseArtifact(output2.artifact);
162
+ return artifact ? { type: "widget", name: "artifact", id: part.toolCallId, props: { artifact } } : null;
163
+ }
164
+ const input = record(part.input), output = record(part.output);
165
+ const values = { ...input, ...output };
166
+ const common2 = { state: computeToolState(part), error: part.errorText };
167
+ let props;
168
+ if (name === "read-file") {
169
+ const path = string(
170
+ values.path,
171
+ values.file,
172
+ values.filename,
173
+ values.file_path
174
+ );
175
+ if (!path) return null;
176
+ props = {
177
+ ...common2,
178
+ path,
179
+ content: string(output.content, output.text, part.output),
180
+ startLine: number(values.startLine, values.start_line, values.offset),
181
+ language: string(values.language)
182
+ };
183
+ if (part.state === "output-available" && props.content === void 0)
184
+ return null;
185
+ } else if (name === "patch") {
186
+ const before = string(values.before, values.old_string, values.oldText);
187
+ const after = string(values.after, values.new_string, values.newText);
188
+ const diff = (_a = string(
189
+ output.diff,
190
+ output.patch,
191
+ input.diff,
192
+ input.patch,
193
+ part.input,
194
+ part.output
195
+ )) != null ? _a : before !== void 0 && after !== void 0 ? computeTextDiff(
196
+ before,
197
+ after,
198
+ string(values.path, values.file_path, "file")
199
+ ) : void 0;
200
+ if (diff === void 0 || diff && !/^(@@|diff --git|--- |\*\*\* Begin Patch)/m.test(diff))
201
+ return null;
202
+ props = {
203
+ ...common2,
204
+ path: string(values.path, values.file, values.file_path),
205
+ diff
206
+ };
207
+ } else if (name === "search") {
208
+ const query = string(values.query, values.pattern, values.search);
209
+ if (query === void 0) return null;
210
+ const results = (_c = (_b = output.results) != null ? _b : output.matches) != null ? _c : Array.isArray(part.output) ? part.output : void 0;
211
+ if (part.state === "output-available" && !Array.isArray(results))
212
+ return null;
213
+ props = {
214
+ ...common2,
215
+ query,
216
+ results: normalizeSearchResults(results),
217
+ total: number(output.total, output.totalCount)
218
+ };
219
+ } else {
220
+ if (["output-error", "output-denied", "approval-requested"].includes(
221
+ part.state
222
+ ))
223
+ return null;
224
+ const prompt = string(input.prompt, input.question);
225
+ if (!prompt) return null;
226
+ props = { ...input, prompt };
227
+ }
228
+ return {
229
+ type: "widget",
230
+ name,
231
+ id: part.toolCallId,
232
+ props,
233
+ interactive: name === "question" && part.state === "input-available"
234
+ };
235
+ }
236
+ function computeDiff(diff) {
237
+ let oldLine = 0, newLine = 0, inHunk = false, oldRemaining = 0, newRemaining = 0, additions = 0, deletions = 0;
238
+ const lines = diff.replace(/\r\n/g, "\n").split("\n").map((text) => {
239
+ var _a, _b;
240
+ const hunk = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(text);
241
+ if (hunk) {
242
+ oldLine = Number(hunk[1]);
243
+ newLine = Number(hunk[3]);
244
+ oldRemaining = Number((_a = hunk[2]) != null ? _a : 1);
245
+ newRemaining = Number((_b = hunk[4]) != null ? _b : 1);
246
+ inHunk = true;
247
+ return { kind: "header", text };
248
+ }
249
+ if (oldRemaining === 0 && newRemaining === 0) inHunk = false;
250
+ if (!inHunk && /^(diff --git|--- |\+\+\+ |\*\*\* )/.test(text)) {
251
+ inHunk = false;
252
+ return { kind: "header", text };
253
+ }
254
+ if (text.startsWith("@@")) {
255
+ inHunk = false;
256
+ return { kind: "header", text };
257
+ }
258
+ if (text.startsWith("+")) {
259
+ additions++;
260
+ if (inHunk) newRemaining--;
261
+ return { kind: "add", text, newLine: inHunk ? newLine++ : void 0 };
262
+ }
263
+ if (text.startsWith("-")) {
264
+ deletions++;
265
+ if (inHunk) oldRemaining--;
266
+ return {
267
+ kind: "remove",
268
+ text,
269
+ oldLine: inHunk ? oldLine++ : void 0
270
+ };
271
+ }
272
+ if (inHunk && text.startsWith(" ")) {
273
+ oldRemaining--;
274
+ newRemaining--;
275
+ return {
276
+ kind: "context",
277
+ text,
278
+ oldLine: oldLine++,
279
+ newLine: newLine++
280
+ };
281
+ }
282
+ return { kind: "header", text };
283
+ });
284
+ return { lines, additions, deletions };
285
+ }
286
+ function safeResultUrl(url) {
287
+ try {
288
+ const parsed = new URL(url != null ? url : "");
289
+ return ["https:", "http:"].includes(parsed.protocol) ? parsed.href : void 0;
290
+ } catch {
291
+ return void 0;
292
+ }
293
+ }
294
+ function computeTextDiff(before, after, path = "file") {
295
+ if (before === after) return "";
296
+ const split = (text) => text === "" ? [] : text.replace(/\r\n/g, "\n").replace(/\n$/, "").split("\n");
297
+ const oldLines = split(before), newLines = split(after);
298
+ let prefix = 0, suffix = 0;
299
+ while (prefix < oldLines.length && prefix < newLines.length && oldLines[prefix] === newLines[prefix])
300
+ prefix++;
301
+ while (suffix < oldLines.length - prefix && suffix < newLines.length - prefix && oldLines[oldLines.length - 1 - suffix] === newLines[newLines.length - 1 - suffix])
302
+ suffix++;
303
+ if (before.endsWith("\n") !== after.endsWith("\n")) suffix = 0;
304
+ if (prefix === oldLines.length && prefix === newLines.length) {
305
+ prefix = Math.max(0, prefix - 1);
306
+ suffix = 0;
307
+ }
308
+ const start = Math.max(0, prefix - 3), tail = Math.min(3, suffix);
309
+ const oldEnd = oldLines.length - suffix, newEnd = newLines.length - suffix;
310
+ const oldCount = oldEnd + tail - start, newCount = newEnd + tail - start;
311
+ const label = path.replace(/[\r\n]/g, "");
312
+ const body = [
313
+ ...oldLines.slice(start, prefix).map((line) => " " + line),
314
+ ...oldLines.slice(prefix, oldEnd).flatMap((line, index) => [
315
+ "-" + line,
316
+ ...prefix + index === oldLines.length - 1 && !before.endsWith("\n") ? ["\"] : []
317
+ ]),
318
+ ...newLines.slice(prefix, newEnd).flatMap((line, index) => [
319
+ "+" + line,
320
+ ...prefix + index === newLines.length - 1 && !after.endsWith("\n") ? ["\"] : []
321
+ ]),
322
+ ...oldLines.slice(oldEnd, oldEnd + tail).map((line) => " " + line)
323
+ ];
324
+ return [
325
+ `--- a/${label}`,
326
+ `+++ b/${label}`,
327
+ `@@ -${oldCount ? start + 1 : start},${oldCount} +${newCount ? start + 1 : start},${newCount} @@`,
328
+ ...body
329
+ ].join("\n");
330
+ }
331
+
332
+ // src/widgets/patch.tsx
333
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
334
+ function PatchWidget({
335
+ path = "File changes",
336
+ diff: rawDiff,
337
+ before,
338
+ after,
339
+ maxLines = 100,
340
+ ...props
341
+ }) {
342
+ const diff = useMemo(
343
+ () => rawDiff != null ? rawDiff : before !== void 0 && after !== void 0 ? computeTextDiff(before, after, path) : "",
344
+ [rawDiff, before, after, path]
345
+ );
346
+ const [expanded, setExpanded] = useState2(false);
347
+ const { lines, additions, deletions } = useMemo(
348
+ () => computeDiff(diff),
349
+ [diff]
350
+ );
351
+ const limit = Math.max(1, maxLines);
352
+ return /* @__PURE__ */ jsxs3(
353
+ ToolFrame,
354
+ {
355
+ ...props,
356
+ label: "Patch",
357
+ title: path,
358
+ meta: /* @__PURE__ */ jsxs3(Fragment, { children: [
359
+ /* @__PURE__ */ jsxs3("span", { className: "chat-diff-add-count", children: [
360
+ "+",
361
+ additions
362
+ ] }),
363
+ " ",
364
+ /* @__PURE__ */ jsxs3("span", { className: "chat-diff-remove-count", children: [
365
+ "\u2212",
366
+ deletions
367
+ ] })
368
+ ] }),
369
+ children: [
370
+ diff ? /* @__PURE__ */ jsx3(
371
+ "div",
372
+ {
373
+ className: "chat-code-preview chat-diff",
374
+ role: "region",
375
+ "aria-label": `Diff for ${path}`,
376
+ tabIndex: 0,
377
+ children: (expanded ? lines : lines.slice(0, limit)).map((line, index) => /* @__PURE__ */ jsxs3("div", { className: "chat-code-line", "data-kind": line.kind, children: [
378
+ /* @__PURE__ */ jsx3("span", { className: "chat-line-number", "aria-hidden": "true", children: line.oldLine }),
379
+ /* @__PURE__ */ jsx3("span", { className: "chat-line-number", "aria-hidden": "true", children: line.newLine }),
380
+ /* @__PURE__ */ jsx3("code", { children: line.text || " " })
381
+ ] }, index))
382
+ }
383
+ ) : /* @__PURE__ */ jsx3("p", { className: "chat-widget-empty", children: "No changes supplied." }),
384
+ !expanded && lines.length > limit ? /* @__PURE__ */ jsxs3(
385
+ "button",
386
+ {
387
+ className: "chat-widget-more",
388
+ type: "button",
389
+ onClick: () => setExpanded(true),
390
+ children: [
391
+ "Show full diff (",
392
+ lines.length,
393
+ " lines)"
394
+ ]
395
+ }
396
+ ) : null
397
+ ]
398
+ }
399
+ );
400
+ }
401
+
402
+ // src/widgets/search.tsx
403
+ import { useState as useState3 } from "react";
404
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
405
+ function SearchWidget({
406
+ query,
407
+ results = [],
408
+ total,
409
+ maxResults = 5,
410
+ ...props
411
+ }) {
412
+ const [expanded, setExpanded] = useState3(false);
413
+ const limit = Math.max(1, maxResults);
414
+ const count = Math.max(results.length, total != null ? total : 0);
415
+ return /* @__PURE__ */ jsxs4(
416
+ ToolFrame,
417
+ {
418
+ ...props,
419
+ label: "Search",
420
+ title: query,
421
+ meta: `${count} ${count === 1 ? "result" : "results"}`,
422
+ children: [
423
+ !results.length ? /* @__PURE__ */ jsx4("p", { className: "chat-widget-empty", children: props.state === "running" ? "Searching\u2026" : "No results found." }) : /* @__PURE__ */ jsx4("ol", { className: "chat-search-results", children: (expanded ? results : results.slice(0, limit)).map(
424
+ (result, index) => {
425
+ const href = safeResultUrl(result.url);
426
+ const label = /* @__PURE__ */ jsxs4(Fragment2, { children: [
427
+ result.title || result.path,
428
+ result.line ? /* @__PURE__ */ jsxs4("span", { className: "chat-search-line", children: [
429
+ ":",
430
+ result.line
431
+ ] }) : null
432
+ ] });
433
+ return /* @__PURE__ */ jsxs4("li", { children: [
434
+ href ? /* @__PURE__ */ jsx4("a", { href, target: "_blank", rel: "noreferrer", children: label }) : /* @__PURE__ */ jsx4("div", { className: "chat-search-path", children: label }),
435
+ result.text ? /* @__PURE__ */ jsx4("p", { children: result.text }) : null
436
+ ] }, `${result.path}-${result.line}-${index}`);
437
+ }
438
+ ) }),
439
+ !expanded && results.length > limit ? /* @__PURE__ */ jsxs4(
440
+ "button",
441
+ {
442
+ className: "chat-widget-more",
443
+ type: "button",
444
+ onClick: () => setExpanded(true),
445
+ children: [
446
+ "Show ",
447
+ results.length - limit,
448
+ " more results"
449
+ ]
450
+ }
451
+ ) : null
452
+ ]
453
+ }
454
+ );
455
+ }
456
+
457
+ // src/widgets/question.tsx
458
+ import { useId, useRef, useState as useState4 } from "react";
459
+ import { ArrowUp, Check } from "lucide-react";
460
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
461
+ function QuestionWidget({
462
+ prompt,
463
+ description,
464
+ options = [],
465
+ multiple = false,
466
+ allowCustom = false,
467
+ disabled = false,
468
+ onSubmit,
469
+ className = ""
470
+ }) {
471
+ const id = useId();
472
+ const [selected, setSelected] = useState4([]), [text, setText] = useState4("");
473
+ const [status, setStatus] = useState4("idle");
474
+ const [error, setError] = useState4();
475
+ const submitting = useRef(false);
476
+ const choices = options.map(
477
+ (option) => typeof option === "string" ? { label: option, value: option } : option
478
+ );
479
+ const locked = disabled || status !== "idle" || !onSubmit;
480
+ return /* @__PURE__ */ jsxs5(
481
+ "form",
482
+ {
483
+ className: `chat-prebuilt chat-question ${className}`,
484
+ "aria-labelledby": `${id}-prompt`,
485
+ onSubmit: async (event) => {
486
+ event.preventDefault();
487
+ if (locked || submitting.current || !selected.length && !text.trim())
488
+ return;
489
+ submitting.current = true;
490
+ setStatus("sending");
491
+ setError(void 0);
492
+ try {
493
+ await (onSubmit == null ? void 0 : onSubmit({
494
+ values: selected,
495
+ ...text.trim() ? { text: text.trim() } : {}
496
+ }));
497
+ setStatus("sent");
498
+ } catch (cause) {
499
+ setError(
500
+ cause instanceof Error ? cause.message : "Could not send your answer. Try again."
501
+ );
502
+ setStatus("idle");
503
+ } finally {
504
+ submitting.current = false;
505
+ }
506
+ },
507
+ children: [
508
+ /* @__PURE__ */ jsx5("div", { className: "chat-question-eyebrow", children: "Your input" }),
509
+ /* @__PURE__ */ jsx5("h3", { id: `${id}-prompt`, children: prompt }),
510
+ description ? /* @__PURE__ */ jsx5("p", { className: "chat-question-description", children: description }) : null,
511
+ /* @__PURE__ */ jsxs5("fieldset", { disabled: locked, children: [
512
+ /* @__PURE__ */ jsx5("legend", { className: "chat-sr-only", children: multiple ? "Select one or more options" : "Select an option" }),
513
+ choices.map((option, index) => /* @__PURE__ */ jsxs5(
514
+ "label",
515
+ {
516
+ className: "chat-question-option",
517
+ "data-selected": selected.includes(option.value),
518
+ children: [
519
+ /* @__PURE__ */ jsx5(
520
+ "input",
521
+ {
522
+ type: multiple ? "checkbox" : "radio",
523
+ name: id,
524
+ value: option.value,
525
+ checked: selected.includes(option.value),
526
+ onChange: () => setSelected(
527
+ (current) => multiple ? current.includes(option.value) ? current.filter((value) => value !== option.value) : [...current, option.value] : [option.value]
528
+ )
529
+ }
530
+ ),
531
+ /* @__PURE__ */ jsxs5("span", { children: [
532
+ /* @__PURE__ */ jsxs5("span", { className: "chat-question-option-title", children: [
533
+ option.label,
534
+ option.recommended ? /* @__PURE__ */ jsx5("small", { children: "Recommended" }) : null
535
+ ] }),
536
+ option.description ? /* @__PURE__ */ jsx5("span", { className: "chat-question-option-description", children: option.description }) : null
537
+ ] })
538
+ ]
539
+ },
540
+ `${option.value}-${index}`
541
+ )),
542
+ allowCustom || !choices.length ? /* @__PURE__ */ jsxs5("label", { className: "chat-question-custom", children: [
543
+ choices.length ? "Additional context" : "Your answer",
544
+ /* @__PURE__ */ jsx5(
545
+ "textarea",
546
+ {
547
+ rows: 2,
548
+ value: text,
549
+ onChange: (event) => setText(event.target.value),
550
+ placeholder: "Type your answer\u2026"
551
+ }
552
+ )
553
+ ] }) : null
554
+ ] }),
555
+ /* @__PURE__ */ jsxs5("footer", { children: [
556
+ /* @__PURE__ */ jsx5("span", { role: "status", children: status === "sent" ? "Answer sent" : status === "sending" ? "Sending\u2026" : multiple ? "Choose one or more" : choices.length ? "Choose and confirm" : "Write and confirm" }),
557
+ /* @__PURE__ */ jsxs5(
558
+ "button",
559
+ {
560
+ type: "submit",
561
+ disabled: locked || !selected.length && !text.trim(),
562
+ children: [
563
+ status === "sent" ? /* @__PURE__ */ jsx5(Check, { size: 14, "aria-hidden": "true" }) : /* @__PURE__ */ jsx5(ArrowUp, { size: 14, "aria-hidden": "true" }),
564
+ status === "sent" ? "Submitted" : "Confirm"
565
+ ]
566
+ }
567
+ )
568
+ ] }),
569
+ error ? /* @__PURE__ */ jsx5("p", { className: "chat-widget-error", role: "alert", children: error }) : null
570
+ ]
571
+ }
572
+ );
573
+ }
574
+
575
+ // src/widgets/registry.tsx
576
+ import { parseArtifact as parseArtifact3, parseSubagent as parseSubagent2 } from "@sarchauhan/protocol";
577
+
578
+ // src/widgets/artifact.tsx
579
+ import { useState as useState6 } from "react";
580
+ import { Download, FileText, Image as ImageIcon, ExternalLink } from "lucide-react";
581
+
582
+ // src/widgets/responsive-detail.tsx
583
+ import { useRef as useRef2, useState as useState5 } from "react";
584
+ import { Dialog } from "radix-ui";
585
+ import { X } from "lucide-react";
586
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
587
+ function ResponsiveDetail({ title, description = "Details from this conversation", trigger, children }) {
588
+ const origin = useRef2(null);
589
+ const [presentation, setPresentation] = useState5({ theme: "light", style: {} });
590
+ return /* @__PURE__ */ jsx6("div", { ref: origin, className: "chat-detail-origin", children: /* @__PURE__ */ jsxs6(Dialog.Root, { onOpenChange: (open) => {
591
+ var _a, _b;
592
+ if (!open || !origin.current) return;
593
+ const host = (_a = origin.current.closest(".chat-root")) != null ? _a : origin.current;
594
+ const computed = window.getComputedStyle(host);
595
+ const tokens = {};
596
+ for (let index = 0; index < computed.length; index++) {
597
+ const name = computed.item(index);
598
+ if (name.startsWith("--chat-")) tokens[name] = computed.getPropertyValue(name);
599
+ }
600
+ const requested = host.getAttribute("data-theme");
601
+ const dark = requested === "dark" || requested === "system" && ((_b = window.matchMedia) == null ? void 0 : _b.call(window, "(prefers-color-scheme: dark)").matches);
602
+ setPresentation({ theme: dark ? "dark" : "light", style: tokens });
603
+ }, children: [
604
+ /* @__PURE__ */ jsx6(Dialog.Trigger, { asChild: true, children: trigger }),
605
+ /* @__PURE__ */ jsxs6(Dialog.Portal, { children: [
606
+ /* @__PURE__ */ jsx6(Dialog.Overlay, { className: "chat-detail-overlay" }),
607
+ /* @__PURE__ */ jsxs6(Dialog.Content, { className: "chat-root chat-detail-panel", "data-theme": presentation.theme, style: presentation.style, children: [
608
+ /* @__PURE__ */ jsx6("div", { className: "chat-detail-handle", "aria-hidden": "true" }),
609
+ /* @__PURE__ */ jsxs6("header", { className: "chat-detail-header", children: [
610
+ /* @__PURE__ */ jsxs6("div", { children: [
611
+ /* @__PURE__ */ jsx6(Dialog.Title, { className: "chat-detail-title", children: title }),
612
+ /* @__PURE__ */ jsx6(Dialog.Description, { className: "chat-detail-description", children: description })
613
+ ] }),
614
+ /* @__PURE__ */ jsx6(Dialog.Close, { className: "chat-detail-close", "aria-label": "Close details", children: /* @__PURE__ */ jsx6(X, { size: 18, "aria-hidden": "true" }) })
615
+ ] }),
616
+ /* @__PURE__ */ jsx6("div", { className: "chat-detail-body", children })
617
+ ] })
618
+ ] })
619
+ ] }) });
620
+ }
621
+
622
+ // src/widgets/artifact.tsx
623
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
624
+ function safeArtifactUrl(value, image = false) {
625
+ if (!value || /[\u0000-\u0020\u007f]/.test(value)) return void 0;
626
+ if (/^blob:/i.test(value)) {
627
+ try {
628
+ new URL(value);
629
+ return value;
630
+ } catch {
631
+ return void 0;
632
+ }
633
+ }
634
+ if (image && /^data:image\/(?:png|jpeg|gif|webp|avif);base64,[a-z0-9+/=]+$/i.test(value)) return value;
635
+ try {
636
+ const parsed = new URL(value, "https://chat.invalid");
637
+ if (!["https:", "http:"].includes(parsed.protocol)) return void 0;
638
+ return value;
639
+ } catch {
640
+ return void 0;
641
+ }
642
+ }
643
+ function formatArtifactSize(bytes) {
644
+ if (bytes === void 0 || !Number.isFinite(bytes) || bytes < 0) return void 0;
645
+ if (bytes < 1024) return bytes + " B";
646
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
647
+ return (bytes / (1024 * 1024)).toFixed(1) + " MB";
648
+ }
649
+ function ArtifactWidget({ artifact, className = "" }) {
650
+ var _a;
651
+ const { filename, mediaType, content, sizeBytes, error } = artifact;
652
+ const state = (_a = artifact.status) != null ? _a : "ready";
653
+ const image = mediaType.startsWith("image/");
654
+ const [failedUrl, setFailedUrl] = useState6();
655
+ const previewUrl = image ? safeArtifactUrl(artifact.url, true) : void 0;
656
+ const link = safeArtifactUrl(artifact.url, image);
657
+ const ready = state === "ready";
658
+ const preview = ready && (previewUrl && failedUrl !== previewUrl || content !== void 0);
659
+ const Icon = image ? ImageIcon : FileText;
660
+ const label = state === "creating" ? "Creating\u2026" : state === "error" ? "Could not create file" : "Created";
661
+ const metadata = [mediaType, formatArtifactSize(sizeBytes)].filter(Boolean).join(" \xB7 ");
662
+ return /* @__PURE__ */ jsxs7("section", { className: "chat-prebuilt chat-artifact " + className, "aria-label": "Artifact: " + filename, "data-state": state, children: [
663
+ ready && previewUrl && failedUrl !== previewUrl ? /* @__PURE__ */ jsx7("div", { className: "chat-artifact-thumbnail", children: /* @__PURE__ */ jsx7("img", { src: previewUrl, alt: filename, loading: "lazy", onError: () => setFailedUrl(previewUrl) }) }) : null,
664
+ /* @__PURE__ */ jsxs7("div", { className: "chat-artifact-row", children: [
665
+ /* @__PURE__ */ jsx7("span", { className: "chat-artifact-icon", children: /* @__PURE__ */ jsx7(Icon, { size: 20, "aria-hidden": "true" }) }),
666
+ /* @__PURE__ */ jsxs7("div", { className: "chat-artifact-heading", children: [
667
+ /* @__PURE__ */ jsx7("strong", { title: filename, children: filename }),
668
+ /* @__PURE__ */ jsx7("span", { children: metadata })
669
+ ] }),
670
+ /* @__PURE__ */ jsx7("span", { className: "chat-artifact-state", role: "status", children: label })
671
+ ] }),
672
+ state === "error" && error ? /* @__PURE__ */ jsx7("p", { className: "chat-widget-error", role: "alert", children: error }) : null,
673
+ ready && image && previewUrl && failedUrl === previewUrl ? /* @__PURE__ */ jsx7("p", { className: "chat-widget-empty", children: "Image preview unavailable." }) : null,
674
+ ready && (preview || link) ? /* @__PURE__ */ jsxs7("footer", { className: "chat-artifact-actions", children: [
675
+ preview ? /* @__PURE__ */ jsx7(ResponsiveDetail, { title: filename, description: metadata, trigger: /* @__PURE__ */ jsx7("button", { type: "button", children: "Preview" }), children: image && previewUrl && failedUrl !== previewUrl ? /* @__PURE__ */ jsx7("img", { className: "chat-artifact-full-image", src: previewUrl, alt: filename, onError: () => setFailedUrl(previewUrl) }) : /* @__PURE__ */ jsx7("pre", { className: "chat-artifact-text", tabIndex: 0, children: content }) }) : null,
676
+ link ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
677
+ /* @__PURE__ */ jsxs7("a", { href: link, target: "_blank", rel: "noreferrer", children: [
678
+ /* @__PURE__ */ jsx7(ExternalLink, { size: 14, "aria-hidden": "true" }),
679
+ "Open"
680
+ ] }),
681
+ /* @__PURE__ */ jsxs7("a", { href: link, download: filename, target: "_blank", rel: "noreferrer", children: [
682
+ /* @__PURE__ */ jsx7(Download, { size: 14, "aria-hidden": "true" }),
683
+ "Download"
684
+ ] })
685
+ ] }) : null
686
+ ] }) : null
687
+ ] });
688
+ }
689
+ function ArtifactsWidget({ artifacts, className = "" }) {
690
+ return /* @__PURE__ */ jsx7("div", { className: "chat-artifacts " + className, children: artifacts.map((artifact) => /* @__PURE__ */ jsx7(ArtifactWidget, { artifact }, artifact.id)) });
691
+ }
692
+
693
+ // src/widgets/subagent.tsx
694
+ import { createContext as createContext3, useContext as useContext3 } from "react";
695
+ import { ArrowUpRight, CircleCheck, CircleX, LoaderCircle, Bot } from "lucide-react";
696
+
697
+ // src/components/Message/message.content.tsx
698
+ import {
699
+ normalizeAgentParts,
700
+ parseArtifact as parseArtifact2,
701
+ parseSubagent
702
+ } from "@sarchauhan/protocol";
703
+ import { ChevronDown as ChevronDown3 } from "lucide-react";
704
+ import { useEffect as useEffect6, useLayoutEffect, useRef as useRef7, useState as useState12 } from "react";
705
+
706
+ // src/lib/message/segment.ts
707
+ var splitThinkingSegments = (content) => {
708
+ const segments = [];
709
+ let cursor = 0;
710
+ while (cursor < content.length) {
711
+ const openingIndex = content.indexOf("<thinking>", cursor);
712
+ if (openingIndex === -1) {
713
+ segments.push({
714
+ type: "markdown",
715
+ content: content.slice(cursor)
716
+ });
717
+ break;
718
+ }
719
+ if (openingIndex > cursor) {
720
+ segments.push({
721
+ type: "markdown",
722
+ content: content.slice(cursor, openingIndex)
723
+ });
724
+ }
725
+ const thinkingStart = openingIndex + "<thinking>".length;
726
+ const closingIndex = content.indexOf("</thinking>", thinkingStart);
727
+ if (closingIndex === -1) {
728
+ segments.push({
729
+ type: "thinking",
730
+ content: content.slice(thinkingStart).trim(),
731
+ isComplete: false
732
+ });
733
+ break;
734
+ }
735
+ segments.push({
736
+ type: "thinking",
737
+ content: content.slice(thinkingStart, closingIndex).trim(),
738
+ isComplete: true
739
+ });
740
+ cursor = closingIndex + "</thinking>".length;
741
+ }
742
+ if (!segments.length) {
743
+ return [{ type: "markdown", content }];
744
+ }
745
+ return segments;
746
+ };
747
+
748
+ // src/lib/message/user.ts
749
+ var REFERENCE_PREFIX = "Use the following selected references as context:\n\n";
750
+ var USER_MESSAGE_MARKER = "\n\nUser message:\n";
751
+ var REFERENCE_PATTERN = /<reference \d+>\n([\s\S]*?)\n<\/reference \d+>/g;
752
+ var getMessageTextContent = (message) => message.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n");
753
+ var parseUserReferenceMessage = (content) => {
754
+ if (!content.startsWith(REFERENCE_PREFIX)) {
755
+ return null;
756
+ }
757
+ const markerIndex = content.indexOf(USER_MESSAGE_MARKER);
758
+ if (markerIndex === -1) {
759
+ return null;
760
+ }
761
+ const references = [...content.slice(REFERENCE_PREFIX.length, markerIndex).matchAll(REFERENCE_PATTERN)].map((match) => match[1].trim()).filter(Boolean);
762
+ return references.length ? { references, message: content.slice(markerIndex + USER_MESSAGE_MARKER.length) } : null;
763
+ };
764
+ var getUserDisplayText = (message) => {
765
+ var _a, _b;
766
+ const text = getMessageTextContent(message);
767
+ return (_b = (_a = parseUserReferenceMessage(text)) == null ? void 0 : _a.message) != null ? _b : text;
768
+ };
769
+
770
+ // src/lib/utils.ts
771
+ import { clsx } from "clsx";
772
+ import { twMerge } from "tailwind-merge";
773
+ function cn(...inputs) {
774
+ return twMerge(clsx(inputs));
775
+ }
776
+
777
+ // src/motion/classes.ts
778
+ var motion = {
779
+ textSwap: "t-text-swap",
780
+ isExit: "is-exit",
781
+ isEnterStart: "is-enter-start",
782
+ iconSwap: "t-icon-swap",
783
+ icon: "t-icon",
784
+ successCheck: "t-success-check",
785
+ dropdown: "t-dropdown",
786
+ isOpen: "is-open",
787
+ isClosing: "is-closing",
788
+ inputWrap: "t-input-wrap",
789
+ input: "t-input",
790
+ errorMsg: "t-error-msg",
791
+ isError: "is-error",
792
+ isShaking: "is-shaking"
793
+ };
794
+ var iconSwapStates = ["a", "b"];
795
+ var successCheckStates = ["out", "in"];
796
+ var dropdownOrigins = [
797
+ "top-left",
798
+ "top-center",
799
+ "top-right",
800
+ "bottom-left",
801
+ "bottom-center",
802
+ "bottom-right"
803
+ ];
804
+ var motionData = {
805
+ state: "data-state",
806
+ icon: "data-icon",
807
+ origin: "data-origin",
808
+ open: "data-open"
809
+ };
810
+
811
+ // src/motion/helpers.ts
812
+ var CHAT_ROOT = ".chat-root";
813
+ var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
814
+ var motionRoot = (node) => {
815
+ var _a;
816
+ return (_a = node == null ? void 0 : node.closest(CHAT_ROOT)) != null ? _a : typeof document !== "undefined" ? document.querySelector(CHAT_ROOT) : null;
817
+ };
818
+ var readRaw = (name, node) => {
819
+ const root = motionRoot(node);
820
+ if (!root) {
821
+ return "";
822
+ }
823
+ return getComputedStyle(root).getPropertyValue(name);
824
+ };
825
+ var readDurationMs = (name, fallback, node) => {
826
+ const value = parseFloat(readRaw(name, node));
827
+ return Number.isFinite(value) ? value : fallback;
828
+ };
829
+ var revertTimers = /* @__PURE__ */ new WeakMap();
830
+ var shakeTimers = /* @__PURE__ */ new WeakMap();
831
+ var swapText = (el, next, options) => {
832
+ var _a;
833
+ if (prefersReducedMotion()) {
834
+ el.textContent = next;
835
+ (_a = options == null ? void 0 : options.onSwap) == null ? void 0 : _a.call(options);
836
+ return;
837
+ }
838
+ const dur = readDurationMs("--text-swap-dur", 200, el);
839
+ el.classList.add(motion.isExit);
840
+ return window.setTimeout(() => {
841
+ var _a2;
842
+ el.classList.remove(motion.isExit);
843
+ el.classList.add(motion.isEnterStart);
844
+ el.textContent = next;
845
+ (_a2 = options == null ? void 0 : options.onSwap) == null ? void 0 : _a2.call(options);
846
+ void el.offsetHeight;
847
+ el.classList.remove(motion.isEnterStart);
848
+ }, dur);
849
+ };
850
+ var openDropdown = (el) => {
851
+ el.classList.remove(motion.isClosing);
852
+ el.classList.add(motion.isOpen);
853
+ };
854
+ var closeDropdown = (el, onClosed) => {
855
+ el.classList.remove(motion.isOpen);
856
+ el.classList.add(motion.isClosing);
857
+ const closeMs = prefersReducedMotion() ? 0 : readDurationMs("--dropdown-close-dur", 150, el);
858
+ return window.setTimeout(() => {
859
+ el.classList.remove(motion.isClosing);
860
+ onClosed == null ? void 0 : onClosed();
861
+ }, closeMs);
862
+ };
863
+ var triggerErrorShake = (wrap, options) => {
864
+ var _a;
865
+ const input = wrap.matches(`.${motion.input}`) ? wrap : (_a = wrap.querySelector(`.${motion.input}`)) != null ? _a : wrap;
866
+ wrap.classList.add(motion.isError);
867
+ input.classList.add(motion.isError);
868
+ input.classList.remove(motion.isShaking);
869
+ void input.offsetWidth;
870
+ input.classList.add(motion.isShaking);
871
+ const existingShake = shakeTimers.get(input);
872
+ if (existingShake) {
873
+ window.clearTimeout(existingShake);
874
+ }
875
+ const shakeMs = readDurationMs("--shake-dur-a", 80, wrap) * 2 + readDurationMs("--shake-dur-b", 60, wrap) * 2;
876
+ shakeTimers.set(
877
+ input,
878
+ window.setTimeout(() => {
879
+ input.classList.remove(motion.isShaking);
880
+ shakeTimers.delete(input);
881
+ }, shakeMs + 20)
882
+ );
883
+ const existingRevert = revertTimers.get(wrap);
884
+ if (existingRevert) {
885
+ window.clearTimeout(existingRevert);
886
+ revertTimers.delete(wrap);
887
+ }
888
+ if ((options == null ? void 0 : options.revert) === false) {
889
+ return;
890
+ }
891
+ const hold = readDurationMs("--revert-hold", 3e3, wrap);
892
+ revertTimers.set(
893
+ wrap,
894
+ window.setTimeout(() => {
895
+ revertTimers.delete(wrap);
896
+ wrap.classList.remove(motion.isError);
897
+ input.classList.remove(motion.isError);
898
+ }, shakeMs + hold)
899
+ );
900
+ };
901
+ var clearErrorShake = (wrap) => {
902
+ var _a;
903
+ const input = wrap.matches(`.${motion.input}`) ? wrap : (_a = wrap.querySelector(`.${motion.input}`)) != null ? _a : wrap;
904
+ const existingRevert = revertTimers.get(wrap);
905
+ if (existingRevert) {
906
+ window.clearTimeout(existingRevert);
907
+ revertTimers.delete(wrap);
908
+ }
909
+ const existingShake = shakeTimers.get(input);
910
+ if (existingShake) {
911
+ window.clearTimeout(existingShake);
912
+ shakeTimers.delete(input);
913
+ }
914
+ wrap.classList.remove(motion.isError);
915
+ input.classList.remove(motion.isError, motion.isShaking);
916
+ };
917
+ var calibrateCheckPath = (path) => {
918
+ const len = Math.ceil(path.getTotalLength());
919
+ path.style.strokeDasharray = String(len);
920
+ path.style.strokeDashoffset = String(len);
921
+ };
922
+
923
+ // src/motion/text-swap.tsx
924
+ import { useEffect, useRef as useRef3 } from "react";
925
+ import { jsx as jsx8 } from "react/jsx-runtime";
926
+ function TextSwap({ text, className, ...props }) {
927
+ const ref = useRef3(null);
928
+ const shownRef = useRef3(text);
929
+ useEffect(() => {
930
+ const el = ref.current;
931
+ if (!el || shownRef.current === text) {
932
+ return;
933
+ }
934
+ if (prefersReducedMotion()) {
935
+ shownRef.current = text;
936
+ el.textContent = text;
937
+ return;
938
+ }
939
+ const dur = readDurationMs("--text-swap-dur", 200, el);
940
+ el.classList.add(motion.isExit);
941
+ const timer = window.setTimeout(() => {
942
+ el.classList.remove(motion.isExit);
943
+ el.classList.add(motion.isEnterStart);
944
+ el.textContent = text;
945
+ shownRef.current = text;
946
+ void el.offsetHeight;
947
+ el.classList.remove(motion.isEnterStart);
948
+ }, dur);
949
+ return () => window.clearTimeout(timer);
950
+ }, [text]);
951
+ return /* @__PURE__ */ jsx8("span", { ref, className: cn(motion.textSwap, className), ...props, children: shownRef.current });
952
+ }
953
+
954
+ // src/motion/use-dropdown-presence.ts
955
+ import { useEffect as useEffect2, useRef as useRef4, useState as useState7 } from "react";
956
+ var resolveRoot = (root) => root && "current" in root ? root.current : root != null ? root : null;
957
+ function useDropdownPresence(open, root) {
958
+ const [mounted, setMounted] = useState7(open);
959
+ const [phase, setPhase] = useState7(open ? "open" : "rest");
960
+ const mountedRef = useRef4(open);
961
+ mountedRef.current = mounted;
962
+ useEffect2(() => {
963
+ const node = resolveRoot(root);
964
+ if (open) {
965
+ setMounted(true);
966
+ setPhase("rest");
967
+ const frame = window.requestAnimationFrame(() => {
968
+ window.requestAnimationFrame(() => setPhase("open"));
969
+ });
970
+ return () => window.cancelAnimationFrame(frame);
971
+ }
972
+ if (!mountedRef.current) {
973
+ return;
974
+ }
975
+ setPhase("closing");
976
+ const closeMs = prefersReducedMotion() ? 0 : readDurationMs("--dropdown-close-dur", 150, node);
977
+ const timer = window.setTimeout(() => {
978
+ setMounted(false);
979
+ setPhase("rest");
980
+ }, closeMs);
981
+ return () => window.clearTimeout(timer);
982
+ }, [open, root]);
983
+ const className = phase === "open" ? motion.isOpen : phase === "closing" ? motion.isClosing : void 0;
984
+ return { mounted, phase, className };
985
+ }
986
+
987
+ // src/components/Widget/widget.context.tsx
988
+ import { createContext, useContext, useMemo as useMemo2 } from "react";
989
+ import { jsx as jsx9 } from "react/jsx-runtime";
990
+ function defineWidget(name, component, options = {}) {
991
+ return {
992
+ name,
993
+ component,
994
+ shell: true,
995
+ ...options
996
+ };
997
+ }
998
+ function createWidgetRegistry(widgets = {}) {
999
+ if (Array.isArray(widgets)) {
1000
+ return Object.fromEntries(widgets.map((widget) => [widget.name, widget]));
1001
+ }
1002
+ return widgets;
1003
+ }
1004
+ function readWidgetValue(value, props, controls) {
1005
+ return typeof value === "function" ? value(props, controls) : value;
1006
+ }
1007
+ function getWidgetShellProps(definition, props, controls) {
1008
+ var _a;
1009
+ const title = readWidgetValue(definition.title, props, controls);
1010
+ return {
1011
+ label: (_a = readWidgetValue(definition.label, props, controls)) != null ? _a : definition.name.replace(/[-_]/g, " "),
1012
+ title: title != null ? title : definition.name,
1013
+ status: readWidgetValue(definition.status, props, controls),
1014
+ meta: readWidgetValue(definition.meta, props, controls),
1015
+ className: readWidgetValue(definition.className, props, controls)
1016
+ };
1017
+ }
1018
+ function isWidgetDefinition(entry) {
1019
+ return Boolean(entry && typeof entry === "object" && "component" in entry);
1020
+ }
1021
+ var WidgetContext = createContext(null);
1022
+ function WidgetProvider({
1023
+ widgets = {},
1024
+ respondToWidget,
1025
+ disabled = false,
1026
+ children
1027
+ }) {
1028
+ const registry = useMemo2(() => ({ ...prebuiltWidgets, ...createWidgetRegistry(widgets) }), [widgets]);
1029
+ const value = useMemo2(
1030
+ () => ({
1031
+ widgets: registry,
1032
+ respondToWidget,
1033
+ disabled
1034
+ }),
1035
+ [disabled, registry, respondToWidget]
1036
+ );
1037
+ return /* @__PURE__ */ jsx9(WidgetContext.Provider, { value, children });
1038
+ }
1039
+ function useWidgets() {
1040
+ const context = useContext(WidgetContext);
1041
+ if (!context) {
1042
+ return {
1043
+ widgets: prebuiltWidgets,
1044
+ respondToWidget: async () => void 0,
1045
+ disabled: true
1046
+ };
1047
+ }
1048
+ return context;
1049
+ }
1050
+ function resolveWidgetPart(part, registry) {
1051
+ var _a;
1052
+ return (_a = registry[part.name]) != null ? _a : null;
1053
+ }
1054
+
1055
+ // src/ui/button.tsx
1056
+ import { cva } from "class-variance-authority";
1057
+ import { Slot } from "radix-ui";
1058
+ import { jsx as jsx10 } from "react/jsx-runtime";
1059
+ var buttonVariants = cva(
1060
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1061
+ {
1062
+ variants: {
1063
+ variant: {
1064
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
1065
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1066
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
1067
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1068
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1069
+ link: "text-primary underline-offset-4 hover:underline"
1070
+ },
1071
+ size: {
1072
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
1073
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
1074
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
1075
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1076
+ icon: "size-9",
1077
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
1078
+ "icon-sm": "size-8",
1079
+ "icon-lg": "size-10"
1080
+ }
1081
+ },
1082
+ defaultVariants: {
1083
+ variant: "default",
1084
+ size: "default"
1085
+ }
1086
+ }
1087
+ );
1088
+ function Button({
1089
+ className,
1090
+ variant = "default",
1091
+ size = "default",
1092
+ asChild = false,
1093
+ skeleton = false,
1094
+ disabled,
1095
+ ...props
1096
+ }) {
1097
+ const Comp = asChild ? Slot.Root : "button";
1098
+ return /* @__PURE__ */ jsx10(
1099
+ Comp,
1100
+ {
1101
+ "data-slot": "button",
1102
+ "data-variant": variant,
1103
+ "data-size": size,
1104
+ "data-skeleton": skeleton ? "true" : void 0,
1105
+ "aria-busy": skeleton ? true : void 0,
1106
+ disabled: skeleton || disabled,
1107
+ className: cn(buttonVariants({ variant, size, className })),
1108
+ ...props
1109
+ }
1110
+ );
1111
+ }
1112
+
1113
+ // src/components/Message/message.skeleton.tsx
1114
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1115
+ var TextSkeleton = () => /* @__PURE__ */ jsxs8("div", { className: "chat-text-skeleton", role: "status", "aria-label": "Assistant is writing", children: [
1116
+ /* @__PURE__ */ jsx11("span", { className: "chat-skeleton-bar" }),
1117
+ /* @__PURE__ */ jsx11("span", { className: "chat-skeleton-bar" }),
1118
+ /* @__PURE__ */ jsx11("span", { className: "chat-skeleton-bar chat-skeleton-bar-short" })
1119
+ ] });
1120
+ var WidgetActionSkeleton = () => /* @__PURE__ */ jsxs8("div", { className: "chat-widget-action-skeleton", "aria-label": "Widget actions are loading", children: [
1121
+ /* @__PURE__ */ jsx11(Button, { skeleton: true, size: "sm", variant: "secondary", type: "button", tabIndex: -1, "aria-hidden": "true", children: "Confirm" }),
1122
+ /* @__PURE__ */ jsx11(Button, { skeleton: true, size: "sm", variant: "outline", type: "button", tabIndex: -1, "aria-hidden": "true", children: "Cancel" })
1123
+ ] });
1124
+
1125
+ // src/components/Widget/widget.renderer.tsx
1126
+ import { useRef as useRef5, useState as useState8 } from "react";
1127
+
1128
+ // src/components/Widget/base.widget.tsx
1129
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1130
+ function BaseWidget({
1131
+ label,
1132
+ title,
1133
+ status,
1134
+ meta,
1135
+ className,
1136
+ children
1137
+ }) {
1138
+ return /* @__PURE__ */ jsxs9("section", { className: cn("chat-widget-card", className), "aria-label": label, children: [
1139
+ /* @__PURE__ */ jsxs9("header", { className: "chat-widget-header", children: [
1140
+ /* @__PURE__ */ jsxs9("div", { className: "chat-widget-heading", children: [
1141
+ label ? /* @__PURE__ */ jsxs9("div", { className: "chat-widget-label-row", children: [
1142
+ /* @__PURE__ */ jsx12("span", { className: "chat-widget-status-dot", "aria-hidden": "true" }),
1143
+ /* @__PURE__ */ jsx12("span", { className: "chat-widget-label", children: label })
1144
+ ] }) : null,
1145
+ /* @__PURE__ */ jsx12("div", { className: "chat-widget-title", children: title })
1146
+ ] }),
1147
+ status ? /* @__PURE__ */ jsx12("div", { className: "chat-widget-status", children: status }) : null
1148
+ ] }),
1149
+ children ? /* @__PURE__ */ jsx12("div", { className: "chat-widget-body", children }) : null,
1150
+ meta ? /* @__PURE__ */ jsx12("div", { className: "chat-widget-meta", children: meta }) : null
1151
+ ] });
1152
+ }
1153
+
1154
+ // src/components/Widget/widget.renderer.tsx
1155
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1156
+ function WidgetRenderer({
1157
+ part,
1158
+ pending = false
1159
+ }) {
1160
+ const { widgets, respondToWidget, disabled } = useWidgets();
1161
+ const entry = resolveWidgetPart(part, widgets);
1162
+ const [submitted, setSubmitted] = useState8(false);
1163
+ const responsePending = useRef5(false);
1164
+ const widgetDisabled = disabled || submitted;
1165
+ const respond = async (value, label, actionId) => {
1166
+ if (!part.interactive || disabled || submitted || responsePending.current) {
1167
+ return;
1168
+ }
1169
+ responsePending.current = true;
1170
+ setSubmitted(true);
1171
+ try {
1172
+ await respondToWidget({
1173
+ widgetId: part.id,
1174
+ name: part.name,
1175
+ actionId,
1176
+ value,
1177
+ label
1178
+ });
1179
+ } catch (error) {
1180
+ setSubmitted(false);
1181
+ throw error;
1182
+ } finally {
1183
+ responsePending.current = false;
1184
+ }
1185
+ };
1186
+ const controls = {
1187
+ widgetId: part.id,
1188
+ widgetName: part.name,
1189
+ interactive: Boolean(part.interactive),
1190
+ disabled: widgetDisabled,
1191
+ respond,
1192
+ respondWith: async (response) => {
1193
+ await respond(response.value, response.label, response.actionId);
1194
+ }
1195
+ };
1196
+ const frame = (node) => /* @__PURE__ */ jsxs10("div", { className: "chat-widget", "data-pending": pending ? "true" : void 0, children: [
1197
+ node,
1198
+ pending ? /* @__PURE__ */ jsx13(WidgetActionSkeleton, {}) : null
1199
+ ] });
1200
+ if (!entry) {
1201
+ return frame(
1202
+ /* @__PURE__ */ jsx13(
1203
+ BaseWidget,
1204
+ {
1205
+ label: "Unhandled widget",
1206
+ title: part.name,
1207
+ status: "Raw props",
1208
+ className: "chat-widget-missing",
1209
+ children: /* @__PURE__ */ jsx13("pre", { className: "agent-tool-code", children: JSON.stringify(part.props, null, 2) })
1210
+ }
1211
+ )
1212
+ );
1213
+ }
1214
+ if (isWidgetDefinition(entry)) {
1215
+ const Component2 = entry.component;
1216
+ const content = /* @__PURE__ */ jsx13(Component2, { ...part.props, widget: controls });
1217
+ if (entry.shell === false) {
1218
+ return frame(content);
1219
+ }
1220
+ return frame(
1221
+ /* @__PURE__ */ jsx13(BaseWidget, { ...getWidgetShellProps(entry, part.props, controls), children: content })
1222
+ );
1223
+ }
1224
+ const Component = entry;
1225
+ return frame(
1226
+ /* @__PURE__ */ jsx13(
1227
+ Component,
1228
+ {
1229
+ id: part.id,
1230
+ name: part.name,
1231
+ props: part.props,
1232
+ interactive: part.interactive,
1233
+ disabled: widgetDisabled,
1234
+ onRespond: async (response) => {
1235
+ if (!part.interactive || disabled || submitted || responsePending.current) {
1236
+ return;
1237
+ }
1238
+ responsePending.current = true;
1239
+ setSubmitted(true);
1240
+ try {
1241
+ await respondToWidget(response);
1242
+ } catch (error) {
1243
+ setSubmitted(false);
1244
+ throw error;
1245
+ } finally {
1246
+ responsePending.current = false;
1247
+ }
1248
+ }
1249
+ }
1250
+ )
1251
+ );
1252
+ }
1253
+
1254
+ // src/components/Message/message.markdown.tsx
1255
+ import { Children, isValidElement } from "react";
1256
+ import { CodeMarkdown } from "@sarchauhan/code-markdown";
1257
+ import ReactMarkdown from "react-markdown";
1258
+ import remarkGfm from "remark-gfm";
1259
+
1260
+ // src/theme/theme.context.tsx
1261
+ import {
1262
+ createContext as createContext2,
1263
+ useCallback,
1264
+ useContext as useContext2,
1265
+ useEffect as useEffect3,
1266
+ useMemo as useMemo3,
1267
+ useState as useState9
1268
+ } from "react";
1269
+ import { jsx as jsx14 } from "react/jsx-runtime";
1270
+ var ThemeContext = createContext2(null);
1271
+ var STORAGE_KEY = "sarchauhan-chat-theme";
1272
+ var getSystemTheme = () => {
1273
+ if (typeof window === "undefined" || !window.matchMedia) {
1274
+ return "light";
1275
+ }
1276
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1277
+ };
1278
+ var resolveTheme = (theme) => theme === "system" ? getSystemTheme() : theme;
1279
+ function ThemeProvider({
1280
+ children,
1281
+ defaultTheme = "system",
1282
+ theme: controlledTheme,
1283
+ onThemeChange,
1284
+ storageKey = STORAGE_KEY
1285
+ }) {
1286
+ const [uncontrolledTheme, setUncontrolledTheme] = useState9(defaultTheme);
1287
+ const theme = controlledTheme != null ? controlledTheme : uncontrolledTheme;
1288
+ const [resolvedTheme, setResolvedTheme] = useState9(
1289
+ theme === "dark" ? "dark" : "light"
1290
+ );
1291
+ useEffect3(() => {
1292
+ if (controlledTheme !== void 0) {
1293
+ return;
1294
+ }
1295
+ const stored = window.localStorage.getItem(storageKey);
1296
+ if (stored === "light" || stored === "dark" || stored === "system") {
1297
+ setUncontrolledTheme(stored);
1298
+ }
1299
+ }, [controlledTheme, storageKey]);
1300
+ useEffect3(() => {
1301
+ setResolvedTheme(resolveTheme(theme));
1302
+ if (theme !== "system" || typeof window === "undefined") {
1303
+ return;
1304
+ }
1305
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
1306
+ const onChange = () => setResolvedTheme(getSystemTheme());
1307
+ media.addEventListener("change", onChange);
1308
+ return () => media.removeEventListener("change", onChange);
1309
+ }, [theme]);
1310
+ const setTheme = useCallback(
1311
+ (next) => {
1312
+ onThemeChange == null ? void 0 : onThemeChange(next);
1313
+ if (controlledTheme === void 0) {
1314
+ setUncontrolledTheme(next);
1315
+ if (typeof window !== "undefined") {
1316
+ window.localStorage.setItem(storageKey, next);
1317
+ }
1318
+ }
1319
+ },
1320
+ [controlledTheme, onThemeChange, storageKey]
1321
+ );
1322
+ const toggleTheme = useCallback(() => {
1323
+ const next = resolvedTheme === "dark" ? "light" : "dark";
1324
+ setTheme(next);
1325
+ }, [resolvedTheme, setTheme]);
1326
+ const value = useMemo3(
1327
+ () => ({
1328
+ theme,
1329
+ resolvedTheme,
1330
+ setTheme,
1331
+ toggleTheme
1332
+ }),
1333
+ [resolvedTheme, setTheme, theme, toggleTheme]
1334
+ );
1335
+ return /* @__PURE__ */ jsx14(ThemeContext.Provider, { value, children });
1336
+ }
1337
+ function useTheme() {
1338
+ const context = useContext2(ThemeContext);
1339
+ if (!context) {
1340
+ throw new Error("useTheme must be used inside ThemeProvider");
1341
+ }
1342
+ return context;
1343
+ }
1344
+
1345
+ // src/components/Message/message.markdown.theme.ts
1346
+ var chatCodeThemes = {
1347
+ light: {
1348
+ name: "chat-light",
1349
+ shikiTheme: "github-light",
1350
+ colors: {
1351
+ background: "#ffffff",
1352
+ foreground: "#0d0d0d",
1353
+ comment: "#6e6e80",
1354
+ keyword: "#9b174c",
1355
+ string: "#166534",
1356
+ function: "#1d4ed8",
1357
+ variable: "#0d0d0d",
1358
+ number: "#7c3aed",
1359
+ operator: "#9b174c",
1360
+ punctuation: "#6e6e80",
1361
+ type: "#0369a1",
1362
+ tag: "#166534",
1363
+ attribute: "#1d4ed8",
1364
+ constant: "#b45309",
1365
+ surface: "#f7f7f8",
1366
+ overlay: "#d8d8df",
1367
+ subtext: "#6e6e80"
1368
+ }
1369
+ },
1370
+ dark: {
1371
+ name: "chat-dark",
1372
+ shikiTheme: "github-dark",
1373
+ colors: {
1374
+ background: "#212121",
1375
+ foreground: "#ececec",
1376
+ comment: "#8f8f8f",
1377
+ keyword: "#ff7b72",
1378
+ string: "#a5d6a7",
1379
+ function: "#79c0ff",
1380
+ variable: "#ececec",
1381
+ number: "#d2a8ff",
1382
+ operator: "#ff7b72",
1383
+ punctuation: "#b4b4b4",
1384
+ type: "#79c0ff",
1385
+ tag: "#7ee787",
1386
+ attribute: "#79c0ff",
1387
+ constant: "#ffa657",
1388
+ surface: "#2f2f2f",
1389
+ overlay: "#454545",
1390
+ subtext: "#b4b4b4"
1391
+ }
1392
+ }
1393
+ };
1394
+
1395
+ // src/components/Message/message.markdown.tsx
1396
+ import { jsx as jsx15 } from "react/jsx-runtime";
1397
+ var markdownPlugins = {
1398
+ remark: [remarkGfm],
1399
+ rehype: []
1400
+ };
1401
+ var SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
1402
+ "c",
1403
+ "h",
1404
+ "cpp",
1405
+ "cxx",
1406
+ "cc",
1407
+ "hpp",
1408
+ "hxx",
1409
+ "go",
1410
+ "javascript",
1411
+ "js",
1412
+ "jsx",
1413
+ "mjs",
1414
+ "python",
1415
+ "py",
1416
+ "rust",
1417
+ "rs",
1418
+ "typescript",
1419
+ "ts",
1420
+ "tsx"
1421
+ ]);
1422
+ var isSupportedLanguage = (value) => SUPPORTED_LANGUAGES.has(value);
1423
+ var getCodeLanguage = (className) => {
1424
+ var _a;
1425
+ const match = className == null ? void 0 : className.match(/language-([\w-]+)/);
1426
+ const language = (_a = match == null ? void 0 : match[1]) == null ? void 0 : _a.toLowerCase();
1427
+ return language && isSupportedLanguage(language) ? language : "typescript";
1428
+ };
1429
+ var getTextContent = (value) => {
1430
+ if (typeof value === "string" || typeof value === "number") {
1431
+ return String(value);
1432
+ }
1433
+ if (Array.isArray(value)) {
1434
+ return value.map(getTextContent).join("");
1435
+ }
1436
+ return "";
1437
+ };
1438
+ var MarkdownCodeBlock = ({
1439
+ code,
1440
+ language
1441
+ }) => {
1442
+ const { resolvedTheme } = useTheme();
1443
+ return /* @__PURE__ */ jsx15(
1444
+ CodeMarkdown,
1445
+ {
1446
+ className: "chat-code-markdown",
1447
+ language,
1448
+ theme: chatCodeThemes[resolvedTheme],
1449
+ showCopyButton: true,
1450
+ showLineNumbers: true,
1451
+ showLanguage: true,
1452
+ children: code
1453
+ }
1454
+ );
1455
+ };
1456
+ var MarkdownPre = ({ className, children }) => {
1457
+ const child = Children.toArray(children)[0];
1458
+ const code = isValidElement(child) ? getTextContent(child.props.children) : getTextContent(children);
1459
+ const codeClassName = isValidElement(child) ? child.props.className : void 0;
1460
+ return /* @__PURE__ */ jsx15("div", { className: cn("my-5", className), children: /* @__PURE__ */ jsx15(
1461
+ MarkdownCodeBlock,
1462
+ {
1463
+ language: getCodeLanguage(codeClassName),
1464
+ code: code.replace(/\n$/, "")
1465
+ }
1466
+ ) });
1467
+ };
1468
+ var markdownComponents = {
1469
+ a: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1470
+ "a",
1471
+ {
1472
+ className: cn(
1473
+ "font-medium text-primary underline underline-offset-2 decoration-primary/30 decoration-1 hover:decoration-primary/70",
1474
+ className
1475
+ ),
1476
+ target: "_blank",
1477
+ rel: "noreferrer noopener",
1478
+ ...props
1479
+ }
1480
+ ),
1481
+ p: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1482
+ "p",
1483
+ {
1484
+ className: cn("my-[0.85em] text-inherit chat-text-body first:mt-0 last:mb-0", className),
1485
+ style: { fontSize: "var(--chat-body-size)", lineHeight: "var(--chat-body-leading)" },
1486
+ ...props
1487
+ }
1488
+ ),
1489
+ h1: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1490
+ "h1",
1491
+ {
1492
+ className: cn("mt-8 mb-3 chat-text-heading-1", className),
1493
+ style: {
1494
+ fontSize: "var(--chat-heading-1-size)",
1495
+ lineHeight: "var(--chat-heading-1-leading)",
1496
+ fontWeight: "var(--chat-heading-1-weight)",
1497
+ letterSpacing: "var(--chat-heading-1-tracking)"
1498
+ },
1499
+ ...props
1500
+ }
1501
+ ),
1502
+ h2: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1503
+ "h2",
1504
+ {
1505
+ className: cn("mt-8 mb-3", className),
1506
+ style: {
1507
+ fontSize: "var(--chat-fs-18)",
1508
+ lineHeight: "var(--chat-leading-tight)",
1509
+ fontWeight: "var(--chat-weight-semibold)",
1510
+ letterSpacing: "var(--chat-tracking-tight)"
1511
+ },
1512
+ ...props
1513
+ }
1514
+ ),
1515
+ h3: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1516
+ "h3",
1517
+ {
1518
+ className: cn("mt-6 mb-2", className),
1519
+ style: {
1520
+ fontSize: "var(--chat-fs-16)",
1521
+ lineHeight: "var(--chat-leading-tight)",
1522
+ fontWeight: "var(--chat-weight-semibold)",
1523
+ letterSpacing: "var(--chat-tracking-tight)"
1524
+ },
1525
+ ...props
1526
+ }
1527
+ ),
1528
+ h4: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1529
+ "h4",
1530
+ {
1531
+ className: cn("mt-6 mb-2", className),
1532
+ style: {
1533
+ fontSize: "var(--chat-fs-15)",
1534
+ lineHeight: "var(--chat-leading-tight)",
1535
+ fontWeight: "var(--chat-weight-semibold)",
1536
+ letterSpacing: "var(--chat-tracking-normal)"
1537
+ },
1538
+ ...props
1539
+ }
1540
+ ),
1541
+ ul: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1542
+ "ul",
1543
+ {
1544
+ className: cn("my-4 list-disc space-y-2 pl-6 chat-text-body", className),
1545
+ style: { fontSize: "var(--chat-body-size)", lineHeight: "var(--chat-body-leading)" },
1546
+ ...props
1547
+ }
1548
+ ),
1549
+ ol: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1550
+ "ol",
1551
+ {
1552
+ className: cn("my-4 list-decimal space-y-2 pl-6 chat-text-body", className),
1553
+ style: { fontSize: "var(--chat-body-size)", lineHeight: "var(--chat-body-leading)" },
1554
+ ...props
1555
+ }
1556
+ ),
1557
+ li: ({ className, ...props }) => /* @__PURE__ */ jsx15(
1558
+ "li",
1559
+ {
1560
+ className: cn("pl-1", className),
1561
+ style: { lineHeight: "var(--chat-leading-normal)" },
1562
+ ...props
1563
+ }
1564
+ ),
1565
+ blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx15("blockquote", { className: cn("md-blockquote", className), ...props }),
1566
+ hr: ({ className, ...props }) => /* @__PURE__ */ jsx15("hr", { className: cn("my-6 h-px border-0 bg-current opacity-10", className), ...props }),
1567
+ table: ({ className, ...props }) => /* @__PURE__ */ jsx15("div", { className: "md-table-wrapper", children: /* @__PURE__ */ jsx15("table", { className: cn("md-table", className), ...props }) }),
1568
+ thead: ({ className, ...props }) => /* @__PURE__ */ jsx15("thead", { ...props }),
1569
+ tbody: ({ className, ...props }) => /* @__PURE__ */ jsx15("tbody", { ...props }),
1570
+ tr: ({ className, ...props }) => /* @__PURE__ */ jsx15("tr", { ...props }),
1571
+ th: ({ className, ...props }) => /* @__PURE__ */ jsx15("th", { ...props }),
1572
+ td: ({ className, ...props }) => /* @__PURE__ */ jsx15("td", { ...props }),
1573
+ pre: MarkdownPre,
1574
+ // React Markdown v10 no longer consistently provides the legacy `inline`
1575
+ // prop. Fenced code is handled by the custom `pre` renderer above, so a
1576
+ // standalone `code` element should always be rendered as inline code.
1577
+ code: ({ className, children, ...props }) => /* @__PURE__ */ jsx15("code", { className: cn("md-inline-code", className), ...props, children })
1578
+ };
1579
+ var MarkdownContent = ({ children, className }) => /* @__PURE__ */ jsx15("div", { className: cn("md-content break-words text-inherit", className), children: /* @__PURE__ */ jsx15(
1580
+ ReactMarkdown,
1581
+ {
1582
+ remarkPlugins: markdownPlugins.remark,
1583
+ rehypePlugins: markdownPlugins.rehype,
1584
+ components: markdownComponents,
1585
+ children
1586
+ }
1587
+ ) });
1588
+
1589
+ // src/components/Message/message.thinking.tsx
1590
+ import { ChevronDown as ChevronDown2, Sparkles } from "lucide-react";
1591
+ import { useEffect as useEffect5, useState as useState11 } from "react";
1592
+
1593
+ // src/components/Message/message.loading.tsx
1594
+ import { useEffect as useEffect4, useRef as useRef6, useState as useState10 } from "react";
1595
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1596
+ var useElapsedTime = (active) => {
1597
+ const startedAtRef = useRef6(0);
1598
+ const [elapsed, setElapsed] = useState10(0);
1599
+ useEffect4(() => {
1600
+ if (!active) {
1601
+ return;
1602
+ }
1603
+ startedAtRef.current = Date.now();
1604
+ setElapsed(0);
1605
+ const timer = window.setInterval(() => {
1606
+ setElapsed(Date.now() - startedAtRef.current);
1607
+ }, 100);
1608
+ return () => window.clearInterval(timer);
1609
+ }, [active]);
1610
+ return elapsed;
1611
+ };
1612
+ var formatElapsed = (milliseconds, showTenths = false) => {
1613
+ const totalSeconds = Math.max(0, milliseconds / 1e3);
1614
+ const minutes = Math.floor(totalSeconds / 60);
1615
+ const seconds = totalSeconds - minutes * 60;
1616
+ const formattedSeconds = showTenths ? seconds.toFixed(1) : String(Math.floor(seconds));
1617
+ return minutes > 0 ? `${minutes}m ${formattedSeconds}s` : `${formattedSeconds}s`;
1618
+ };
1619
+ var formatElapsedWords = (milliseconds) => {
1620
+ const seconds = Math.max(0, Math.floor(milliseconds / 1e3));
1621
+ if (seconds < 60) {
1622
+ return `${seconds} ${seconds === 1 ? "second" : "seconds"}`;
1623
+ }
1624
+ const minutes = Math.floor(seconds / 60);
1625
+ const remainder = seconds % 60;
1626
+ const minuteLabel = `${minutes} ${minutes === 1 ? "minute" : "minutes"}`;
1627
+ return remainder > 0 ? `${minuteLabel} ${remainder} ${remainder === 1 ? "second" : "seconds"}` : minuteLabel;
1628
+ };
1629
+ var PixelGrid = () => /* @__PURE__ */ jsx16("span", { className: "chat-pixel-grid", "aria-hidden": "true", children: Array.from({ length: 9 }, (_, index) => /* @__PURE__ */ jsx16("span", {}, index)) });
1630
+ var LoadingState = ({ label = "Churning", compact = false }) => {
1631
+ const elapsed = useElapsedTime(true);
1632
+ return /* @__PURE__ */ jsxs11("div", { className: `chat-loading${compact ? " chat-loading-inline" : ""}`, role: "status", "aria-live": "polite", children: [
1633
+ /* @__PURE__ */ jsx16(PixelGrid, {}),
1634
+ /* @__PURE__ */ jsx16(TextSwap, { className: "chat-loading-label", text: label }),
1635
+ /* @__PURE__ */ jsx16("span", { className: "chat-loading-timer", role: "timer", children: formatElapsed(elapsed, true) }),
1636
+ /* @__PURE__ */ jsx16("span", { className: "chat-visually-hidden", children: "Loading conversation" })
1637
+ ] });
1638
+ };
1639
+
1640
+ // src/components/Message/message.thinking.tsx
1641
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
1642
+ var ThinkingBlock = ({ isComplete, elapsedMs, children }) => {
1643
+ const [isOpen, setIsOpen] = useState11(true);
1644
+ const measuredElapsed = useElapsedTime(!isComplete);
1645
+ const elapsed = elapsedMs != null ? elapsedMs : measuredElapsed;
1646
+ useEffect5(() => {
1647
+ if (!isComplete) {
1648
+ setIsOpen(true);
1649
+ }
1650
+ }, [isComplete]);
1651
+ return /* @__PURE__ */ jsxs12(
1652
+ "details",
1653
+ {
1654
+ className: cn(
1655
+ "md-thinking",
1656
+ isComplete ? "md-thinking-complete" : "md-thinking-pending"
1657
+ ),
1658
+ open: isOpen,
1659
+ onToggle: (event) => setIsOpen(event.currentTarget.open),
1660
+ children: [
1661
+ /* @__PURE__ */ jsxs12("summary", { children: [
1662
+ /* @__PURE__ */ jsx17(Sparkles, { className: "md-thinking-sparkle", "aria-hidden": "true" }),
1663
+ /* @__PURE__ */ jsx17(
1664
+ TextSwap,
1665
+ {
1666
+ className: "md-thinking-label",
1667
+ text: isComplete ? `Thought for ${formatElapsedWords(elapsed)}` : "Thinking"
1668
+ }
1669
+ ),
1670
+ isComplete && /* @__PURE__ */ jsx17(ChevronDown2, { className: "md-thinking-chevron", "aria-hidden": "true" })
1671
+ ] }),
1672
+ children
1673
+ ]
1674
+ }
1675
+ );
1676
+ };
1677
+
1678
+ // src/components/Message/message.content.tsx
1679
+ import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1680
+ var isPropsRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1681
+ var formatJson = (value) => {
1682
+ if (typeof value === "string") {
1683
+ return value;
1684
+ }
1685
+ try {
1686
+ return JSON.stringify(value, null, 2);
1687
+ } catch {
1688
+ return String(value);
1689
+ }
1690
+ };
1691
+ var toolDetail = (part) => {
1692
+ var _a, _b, _c, _d, _e, _f;
1693
+ const source = (_a = part.output) != null ? _a : part.input;
1694
+ if (!isPropsRecord(source)) {
1695
+ return null;
1696
+ }
1697
+ const value = (_f = (_e = (_d = (_c = (_b = source.path) != null ? _b : source.file) != null ? _c : source.filename) != null ? _d : source.query) != null ? _e : source.command) != null ? _f : source.url;
1698
+ return typeof value === "string" && value.trim() ? value : null;
1699
+ };
1700
+ var toolStateLabel = (state) => {
1701
+ switch (state) {
1702
+ case "input-streaming":
1703
+ return "Preparing";
1704
+ case "input-available":
1705
+ return "Ready";
1706
+ case "approval-requested":
1707
+ return "Needs approval";
1708
+ case "approval-responded":
1709
+ return "Approval sent";
1710
+ case "output-available":
1711
+ return "Done";
1712
+ case "output-error":
1713
+ return "Error";
1714
+ case "output-denied":
1715
+ return "Denied";
1716
+ default:
1717
+ return state;
1718
+ }
1719
+ };
1720
+ var ReasoningBlock = ({ part }) => {
1721
+ const isComplete = part.state !== "streaming";
1722
+ return /* @__PURE__ */ jsx18(ThinkingBlock, { isComplete, children: !!part.text.trim() && /* @__PURE__ */ jsx18("div", { className: "md-thinking-body", children: /* @__PURE__ */ jsx18(MarkdownContent, { children: part.text }) }) });
1723
+ };
1724
+ var ToolStatusIcon = ({
1725
+ isPending,
1726
+ isDone
1727
+ }) => {
1728
+ const pathRef = useRef7(null);
1729
+ const [sawPending, setSawPending] = useState12(isPending);
1730
+ const iconState = isPending ? "a" : "b";
1731
+ const playCheck = Boolean(sawPending && isDone);
1732
+ const checkState = playCheck ? "in" : "out";
1733
+ if (isPending && !sawPending) {
1734
+ setSawPending(true);
1735
+ }
1736
+ useLayoutEffect(() => {
1737
+ if (pathRef.current) {
1738
+ calibrateCheckPath(pathRef.current);
1739
+ }
1740
+ }, [playCheck]);
1741
+ return /* @__PURE__ */ jsx18("span", { className: "agent-tool-dot", "aria-hidden": "true", children: /* @__PURE__ */ jsxs13("span", { className: motion.iconSwap, "data-state": iconState, children: [
1742
+ /* @__PURE__ */ jsx18("span", { className: motion.icon, "data-icon": "a", children: /* @__PURE__ */ jsx18("span", { className: "agent-tool-spinner" }) }),
1743
+ /* @__PURE__ */ jsx18("span", { className: motion.icon, "data-icon": "b", children: isDone ? playCheck ? /* @__PURE__ */ jsx18("span", { className: motion.successCheck, "data-state": checkState, children: /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", children: /* @__PURE__ */ jsx18(
1744
+ "path",
1745
+ {
1746
+ ref: pathRef,
1747
+ d: "M2.5 6.5 5 9l4.5-6",
1748
+ stroke: "currentColor",
1749
+ strokeWidth: "1.6",
1750
+ strokeLinecap: "round",
1751
+ strokeLinejoin: "round"
1752
+ }
1753
+ ) }) }) : /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", children: /* @__PURE__ */ jsx18(
1754
+ "path",
1755
+ {
1756
+ d: "M2.5 6.5 5 9l4.5-6",
1757
+ stroke: "currentColor",
1758
+ strokeWidth: "1.6",
1759
+ strokeLinecap: "round",
1760
+ strokeLinejoin: "round"
1761
+ }
1762
+ ) }) : /* @__PURE__ */ jsx18("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", children: /* @__PURE__ */ jsx18(
1763
+ "path",
1764
+ {
1765
+ d: "M3.2 3.2 8.8 8.8M8.8 3.2 3.2 8.8",
1766
+ stroke: "currentColor",
1767
+ strokeWidth: "1.6",
1768
+ strokeLinecap: "round"
1769
+ }
1770
+ ) }) })
1771
+ ] }) });
1772
+ };
1773
+ var ToolBlock = ({ part }) => {
1774
+ var _a;
1775
+ const wrapRef = useRef7(null);
1776
+ const isPending = part.state === "input-streaming" || part.state === "input-available" || part.state === "approval-requested";
1777
+ const isError = part.state === "output-error" || part.state === "output-denied";
1778
+ const isDone = !isPending && !isError;
1779
+ const detail = toolDetail(part);
1780
+ useEffect6(() => {
1781
+ if (!isError || !wrapRef.current) {
1782
+ return;
1783
+ }
1784
+ triggerErrorShake(wrapRef.current, { revert: false });
1785
+ }, [isError]);
1786
+ return /* @__PURE__ */ jsxs13(
1787
+ "details",
1788
+ {
1789
+ ref: wrapRef,
1790
+ className: cn(
1791
+ "agent-tool",
1792
+ motion.inputWrap,
1793
+ isPending ? "agent-tool-pending" : isError ? "agent-tool-error" : "agent-tool-complete"
1794
+ ),
1795
+ open: isPending || isError,
1796
+ children: [
1797
+ /* @__PURE__ */ jsxs13("summary", { className: cn(motion.input, isError && motion.isError), children: [
1798
+ /* @__PURE__ */ jsx18(ChevronDown3, { className: "agent-tool-chevron", "aria-hidden": "true" }),
1799
+ /* @__PURE__ */ jsx18(ToolStatusIcon, { isPending, isDone }),
1800
+ /* @__PURE__ */ jsx18("span", { className: "agent-tool-name", children: (_a = part.title) != null ? _a : part.toolName }),
1801
+ detail ? /* @__PURE__ */ jsx18("span", { className: "agent-tool-chip", children: detail }) : null,
1802
+ /* @__PURE__ */ jsx18(TextSwap, { className: "agent-tool-state", text: toolStateLabel(part.state) })
1803
+ ] }),
1804
+ /* @__PURE__ */ jsxs13("div", { className: "agent-tool-body", children: [
1805
+ part.input !== void 0 && /* @__PURE__ */ jsxs13("div", { className: "agent-tool-section", children: [
1806
+ /* @__PURE__ */ jsx18("div", { className: "agent-tool-section-label", children: "Input" }),
1807
+ /* @__PURE__ */ jsx18("pre", { className: "agent-tool-code", children: formatJson(part.input) })
1808
+ ] }),
1809
+ part.output !== void 0 && /* @__PURE__ */ jsxs13("div", { className: "agent-tool-section", children: [
1810
+ /* @__PURE__ */ jsx18("div", { className: "agent-tool-section-label", children: "Output" }),
1811
+ /* @__PURE__ */ jsx18("pre", { className: "agent-tool-code", children: formatJson(part.output) })
1812
+ ] }),
1813
+ part.errorText && /* @__PURE__ */ jsxs13("div", { className: "agent-tool-section", children: [
1814
+ /* @__PURE__ */ jsx18("div", { className: "agent-tool-section-label", children: "Error" }),
1815
+ /* @__PURE__ */ jsx18("pre", { className: "agent-tool-code agent-tool-error", children: part.errorText })
1816
+ ] })
1817
+ ] })
1818
+ ]
1819
+ }
1820
+ );
1821
+ };
1822
+ var SourceUrlBlock = ({ part }) => /* @__PURE__ */ jsxs13("a", { className: "agent-source", href: part.url, target: "_blank", rel: "noreferrer", children: [
1823
+ /* @__PURE__ */ jsx18("span", { className: "agent-source-label", children: "Source" }),
1824
+ /* @__PURE__ */ jsx18("span", { className: "agent-source-title", children: part.title || part.url })
1825
+ ] });
1826
+ var SourceDocumentBlock = ({ part }) => /* @__PURE__ */ jsxs13("div", { className: "agent-source agent-source-document", children: [
1827
+ /* @__PURE__ */ jsx18("span", { className: "agent-source-label", children: "Document" }),
1828
+ /* @__PURE__ */ jsx18("span", { className: "agent-source-title", children: part.title }),
1829
+ part.filename && /* @__PURE__ */ jsx18("span", { className: "agent-source-meta", children: part.filename })
1830
+ ] });
1831
+ var FileBlock = ({ part }) => /* @__PURE__ */ jsx18(ArtifactWidget, { artifact: {
1832
+ id: part.url,
1833
+ filename: part.filename || (part.mediaType.startsWith("image/") ? "Generated image" : "Generated file"),
1834
+ mediaType: part.mediaType,
1835
+ url: part.url,
1836
+ status: "ready"
1837
+ } });
1838
+ var AgentEventBlock = ({ part }) => /* @__PURE__ */ jsxs13("details", { className: "agent-tool agent-event", children: [
1839
+ /* @__PURE__ */ jsxs13("summary", { children: [
1840
+ /* @__PURE__ */ jsx18("span", { className: "agent-event-marker", "aria-hidden": "true" }),
1841
+ /* @__PURE__ */ jsxs13("span", { className: "agent-tool-name", children: [
1842
+ "data.",
1843
+ part.name
1844
+ ] })
1845
+ ] }),
1846
+ /* @__PURE__ */ jsx18("div", { className: "agent-tool-body", children: /* @__PURE__ */ jsx18("pre", { className: "agent-tool-code", children: formatJson(part.data) }) })
1847
+ ] });
1848
+ var TextWithLegacyThinking = ({ text, isUser }) => {
1849
+ var _a;
1850
+ const userReferenceMessage = isUser ? parseUserReferenceMessage(text) : null;
1851
+ const content = (_a = userReferenceMessage == null ? void 0 : userReferenceMessage.message) != null ? _a : text;
1852
+ const segments = splitThinkingSegments(content);
1853
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
1854
+ userReferenceMessage && /* @__PURE__ */ jsx18("div", { className: "chat-user-reference", children: userReferenceMessage.references.map((reference, index) => /* @__PURE__ */ jsxs13(
1855
+ "div",
1856
+ {
1857
+ className: "chat-user-reference-item",
1858
+ children: [
1859
+ /* @__PURE__ */ jsxs13("div", { className: "chat-reference-label", children: [
1860
+ "Reference ",
1861
+ index + 1
1862
+ ] }),
1863
+ /* @__PURE__ */ jsx18("div", { className: "chat-reference-text", children: reference })
1864
+ ]
1865
+ },
1866
+ `${index}-${reference.slice(0, 16)}`
1867
+ )) }),
1868
+ segments.map((segment, index) => {
1869
+ var _a2;
1870
+ if (!segment.content.trim() && segment.type !== "thinking") {
1871
+ return null;
1872
+ }
1873
+ if (segment.type === "thinking" && !isUser) {
1874
+ const isComplete = (_a2 = segment.isComplete) != null ? _a2 : true;
1875
+ return /* @__PURE__ */ jsx18(ThinkingBlock, { isComplete, children: !!segment.content.trim() && /* @__PURE__ */ jsx18("div", { className: "md-thinking-body", children: /* @__PURE__ */ jsx18(MarkdownContent, { children: segment.content }) }) }, `thinking-${index}`);
1876
+ }
1877
+ return /* @__PURE__ */ jsx18(MarkdownContent, { children: segment.content }, `md-${index}`);
1878
+ })
1879
+ ] });
1880
+ };
1881
+ var asWidgetFromTool = (part) => {
1882
+ const source = part.state === "output-available" && part.output !== void 0 ? part.output : part.input;
1883
+ if (!isPropsRecord(source)) {
1884
+ return null;
1885
+ }
1886
+ return {
1887
+ type: "widget",
1888
+ name: part.toolName,
1889
+ id: part.toolCallId,
1890
+ props: source,
1891
+ interactive: part.toolName === "question" || source.interactive === true
1892
+ };
1893
+ };
1894
+ var asWidgetFromData = (part) => ({
1895
+ type: "widget",
1896
+ name: part.name,
1897
+ id: part.id,
1898
+ props: isPropsRecord(part.data) ? part.data : { value: part.data },
1899
+ interactive: isPropsRecord(part.data) ? part.data.interactive === true : false
1900
+ });
1901
+ var PartView = ({ part, index, isUser }) => {
1902
+ const { widgets } = useWidgets();
1903
+ if (part.type === "widget") {
1904
+ return isUser ? null : /* @__PURE__ */ jsx18(WidgetRenderer, { part }, `widget-${part.id || index}`);
1905
+ }
1906
+ if (part.type === "tool" && !isUser) {
1907
+ const entry = widgets[part.toolName];
1908
+ const isCustom = entry && entry !== prebuiltWidgets[part.toolName];
1909
+ const widgetPart = isCustom ? asWidgetFromTool(part) : computeToolWidget(part);
1910
+ if (widgetPart) {
1911
+ return /* @__PURE__ */ jsx18(
1912
+ WidgetRenderer,
1913
+ {
1914
+ part: widgetPart,
1915
+ pending: part.state === "input-streaming"
1916
+ },
1917
+ `tool-widget-${part.toolCallId || index}`
1918
+ );
1919
+ }
1920
+ }
1921
+ if (part.type === "data" && !isUser && widgets[part.name]) {
1922
+ const builtin = widgets[part.name] === prebuiltWidgets[part.name];
1923
+ if (builtin && (part.name === "artifact" && !parseArtifact2(part.data) || part.name === "subagent" && !parseSubagent(part.data))) return /* @__PURE__ */ jsx18(AgentEventBlock, { part });
1924
+ return /* @__PURE__ */ jsx18(
1925
+ WidgetRenderer,
1926
+ {
1927
+ part: asWidgetFromData(part)
1928
+ },
1929
+ `data-widget-${part.name}-${part.id || index}`
1930
+ );
1931
+ }
1932
+ switch (part.type) {
1933
+ case "text":
1934
+ if (!isUser && part.state === "streaming" && part.text.trim().length === 0) {
1935
+ return /* @__PURE__ */ jsx18(TextSkeleton, {}, `text-${index}`);
1936
+ }
1937
+ return /* @__PURE__ */ jsx18(TextWithLegacyThinking, { text: part.text, isUser }, `text-${index}`);
1938
+ case "reasoning":
1939
+ return isUser ? null : /* @__PURE__ */ jsx18(ReasoningBlock, { part }, `reasoning-${index}`);
1940
+ case "tool":
1941
+ return isUser ? null : /* @__PURE__ */ jsx18(ToolBlock, { part }, `tool-${part.toolCallId || index}`);
1942
+ case "source-url":
1943
+ return /* @__PURE__ */ jsx18(SourceUrlBlock, { part }, `source-url-${part.sourceId || index}`);
1944
+ case "source-document":
1945
+ return /* @__PURE__ */ jsx18(SourceDocumentBlock, { part }, `source-doc-${part.sourceId || index}`);
1946
+ case "file":
1947
+ return /* @__PURE__ */ jsx18(FileBlock, { part }, `file-${index}`);
1948
+ case "data":
1949
+ if (part.name === "usage" || part.name === "context" || part.name === "context-warning") {
1950
+ return null;
1951
+ }
1952
+ return /* @__PURE__ */ jsx18(AgentEventBlock, { part }, `data-${part.name}-${part.id || index}`);
1953
+ case "unknown":
1954
+ return /* @__PURE__ */ jsxs13("details", { className: "agent-tool agent-event", children: [
1955
+ /* @__PURE__ */ jsxs13("summary", { children: [
1956
+ /* @__PURE__ */ jsx18("span", { className: "agent-event-marker", "aria-hidden": "true" }),
1957
+ /* @__PURE__ */ jsx18("span", { className: "agent-tool-name", children: part.rawType })
1958
+ ] }),
1959
+ /* @__PURE__ */ jsx18("div", { className: "agent-tool-body", children: /* @__PURE__ */ jsx18("pre", { className: "agent-tool-code", children: formatJson(part.raw) }) })
1960
+ ] }, `unknown-${index}`);
1961
+ default:
1962
+ return null;
1963
+ }
1964
+ };
1965
+ var MessageContent = ({ parts, isUser = false }) => {
1966
+ const agentParts = normalizeAgentParts(parts);
1967
+ return /* @__PURE__ */ jsx18(
1968
+ "div",
1969
+ {
1970
+ className: cn(
1971
+ "chat-message-content break-words",
1972
+ isUser ? "md-content-user" : "md-content-assistant"
1973
+ ),
1974
+ children: agentParts.map((part, index) => /* @__PURE__ */ jsx18(PartView, { part, index, isUser }, `${part.type}-${index}`))
1975
+ }
1976
+ );
1977
+ };
1978
+
1979
+ // src/widgets/subagent.tsx
1980
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1981
+ var Depth = createContext3(0);
1982
+ var labels = { queued: "Queued", running: "Working", completed: "Completed", failed: "Failed", cancelled: "Cancelled" };
1983
+ function SubagentWidget({ subagent, className = "" }) {
1984
+ const depth = useContext3(Depth);
1985
+ const { title, task, state, summary, progress, messages = [], artifacts = [], error } = subagent;
1986
+ const Icon = state === "completed" ? CircleCheck : state === "failed" ? CircleX : state === "running" ? LoaderCircle : Bot;
1987
+ const canInspect = depth < 3;
1988
+ const transcript = messages.filter((message) => message.role !== "system");
1989
+ return /* @__PURE__ */ jsxs14("section", { className: "chat-prebuilt chat-subagent " + className, "data-state": state, "aria-label": "Subagent: " + title, children: [
1990
+ /* @__PURE__ */ jsxs14("div", { className: "chat-subagent-heading", children: [
1991
+ /* @__PURE__ */ jsx19("span", { className: "chat-subagent-icon", children: /* @__PURE__ */ jsx19(Icon, { size: 18, "aria-hidden": "true" }) }),
1992
+ /* @__PURE__ */ jsxs14("div", { children: [
1993
+ /* @__PURE__ */ jsx19("strong", { children: title }),
1994
+ /* @__PURE__ */ jsx19("span", { className: "chat-subagent-state", role: "status", children: labels[state] })
1995
+ ] }),
1996
+ canInspect ? /* @__PURE__ */ jsx19(
1997
+ ResponsiveDetail,
1998
+ {
1999
+ title,
2000
+ description: task || "Subagent activity in this conversation",
2001
+ trigger: /* @__PURE__ */ jsxs14("button", { className: "chat-subagent-inspect", type: "button", "aria-label": "View activity for " + title, children: [
2002
+ "View activity",
2003
+ /* @__PURE__ */ jsx19(ArrowUpRight, { size: 14, "aria-hidden": "true" })
2004
+ ] }),
2005
+ children: /* @__PURE__ */ jsxs14(Depth.Provider, { value: depth + 1, children: [
2006
+ /* @__PURE__ */ jsxs14("div", { className: "chat-subagent-overview", children: [
2007
+ /* @__PURE__ */ jsx19("span", { className: "chat-subagent-state", children: labels[state] }),
2008
+ progress !== void 0 ? /* @__PURE__ */ jsxs14("span", { children: [
2009
+ Math.round(progress * 100),
2010
+ "%"
2011
+ ] }) : null
2012
+ ] }),
2013
+ summary ? /* @__PURE__ */ jsx19("p", { className: "chat-subagent-summary", children: summary }) : null,
2014
+ error ? /* @__PURE__ */ jsx19("p", { className: "chat-widget-error", role: "alert", children: error }) : null,
2015
+ transcript.length ? /* @__PURE__ */ jsx19("div", { className: "chat-subagent-transcript", children: transcript.map((message) => /* @__PURE__ */ jsxs14("article", { className: "chat-subagent-message", children: [
2016
+ /* @__PURE__ */ jsx19("div", { className: "chat-subagent-role", children: message.role === "user" ? "Task" : title }),
2017
+ /* @__PURE__ */ jsx19(MessageContent, { parts: message.parts, isUser: message.role === "user" })
2018
+ ] }, message.id)) }) : /* @__PURE__ */ jsx19("p", { className: "chat-widget-empty", children: state === "queued" ? "Waiting to start." : state === "running" ? "Activity will appear as the agent works." : "No activity was recorded." }),
2019
+ artifacts.length ? /* @__PURE__ */ jsxs14("div", { className: "chat-subagent-artifacts", children: [
2020
+ /* @__PURE__ */ jsx19("h4", { children: "Created files" }),
2021
+ /* @__PURE__ */ jsx19(ArtifactsWidget, { artifacts })
2022
+ ] }) : null
2023
+ ] })
2024
+ }
2025
+ ) : null
2026
+ ] }),
2027
+ task ? /* @__PURE__ */ jsx19("p", { className: "chat-subagent-task", children: task }) : null,
2028
+ summary ? /* @__PURE__ */ jsx19("p", { className: "chat-subagent-summary", children: summary }) : null,
2029
+ state === "running" && progress !== void 0 ? /* @__PURE__ */ jsx19("progress", { className: "chat-subagent-progress", value: progress, max: 1, "aria-label": "Progress for " + title }) : null,
2030
+ state === "failed" && error ? /* @__PURE__ */ jsx19("p", { className: "chat-widget-error", role: "alert", children: error }) : null
2031
+ ] });
2032
+ }
2033
+
2034
+ // src/widgets/registry.tsx
2035
+ import { jsx as jsx20 } from "react/jsx-runtime";
2036
+ var str = (value, fallback = "") => typeof value === "string" ? value : fallback;
2037
+ var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
2038
+ var common = (props) => ({
2039
+ state: props.state === "running" || props.state === "error" || props.state === "denied" || props.state === "approval" ? props.state : "complete",
2040
+ error: typeof props.error === "string" ? props.error : void 0
2041
+ });
2042
+ function Read(props) {
2043
+ return /* @__PURE__ */ jsx20(
2044
+ ReadFileWidget,
2045
+ {
2046
+ ...common(props),
2047
+ path: str(props.path, "File"),
2048
+ content: typeof props.content === "string" ? props.content : void 0,
2049
+ startLine: num(props.startLine),
2050
+ language: str(props.language)
2051
+ }
2052
+ );
2053
+ }
2054
+ function Patch(props) {
2055
+ return /* @__PURE__ */ jsx20(
2056
+ PatchWidget,
2057
+ {
2058
+ ...common(props),
2059
+ path: str(props.path, "File changes"),
2060
+ diff: typeof props.diff === "string" ? props.diff : void 0,
2061
+ before: typeof props.before === "string" ? props.before : void 0,
2062
+ after: typeof props.after === "string" ? props.after : void 0
2063
+ }
2064
+ );
2065
+ }
2066
+ function Search(props) {
2067
+ return /* @__PURE__ */ jsx20(
2068
+ SearchWidget,
2069
+ {
2070
+ ...common(props),
2071
+ query: str(props.query),
2072
+ results: normalizeSearchResults(props.results),
2073
+ total: num(props.total)
2074
+ }
2075
+ );
2076
+ }
2077
+ function Question(props) {
2078
+ const options = Array.isArray(props.options) ? props.options.flatMap((item) => {
2079
+ if (typeof item === "string") return [{ label: item, value: item }];
2080
+ if (!item || typeof item !== "object" || typeof item.label !== "string" || typeof item.value !== "string")
2081
+ return [];
2082
+ return [
2083
+ {
2084
+ label: item.label,
2085
+ value: item.value,
2086
+ description: str(item.description),
2087
+ recommended: item.recommended === true
2088
+ }
2089
+ ];
2090
+ }) : [];
2091
+ return /* @__PURE__ */ jsx20(
2092
+ QuestionWidget,
2093
+ {
2094
+ prompt: str(props.prompt, "Your answer"),
2095
+ description: str(props.description),
2096
+ options,
2097
+ multiple: props.multiple === true,
2098
+ allowCustom: props.allowCustom === true,
2099
+ disabled: !props.widget.interactive || props.widget.disabled,
2100
+ onSubmit: async (answer) => {
2101
+ const labels2 = answer.values.map(
2102
+ (value) => {
2103
+ var _a, _b;
2104
+ return (_b = (_a = options.find((option) => option.value === value)) == null ? void 0 : _a.label) != null ? _b : value;
2105
+ }
2106
+ );
2107
+ await props.widget.respond(
2108
+ answer,
2109
+ [...labels2, answer.text].filter(Boolean).join(", "),
2110
+ "answer"
2111
+ );
2112
+ }
2113
+ }
2114
+ );
2115
+ }
2116
+ function Artifact(props) {
2117
+ var _a;
2118
+ const artifact = parseArtifact3((_a = props.artifact) != null ? _a : props);
2119
+ return artifact ? /* @__PURE__ */ jsx20(ArtifactWidget, { artifact }) : /* @__PURE__ */ jsx20("p", { className: "chat-widget-empty", children: "File details are unavailable." });
2120
+ }
2121
+ function Artifacts(props) {
2122
+ const artifacts = Array.isArray(props.artifacts) ? props.artifacts.flatMap((value) => {
2123
+ const artifact = parseArtifact3(value);
2124
+ return artifact ? [artifact] : [];
2125
+ }) : [];
2126
+ return /* @__PURE__ */ jsx20(ArtifactsWidget, { artifacts });
2127
+ }
2128
+ function Subagent(props) {
2129
+ var _a;
2130
+ const subagent = parseSubagent2((_a = props.subagent) != null ? _a : props);
2131
+ return subagent ? /* @__PURE__ */ jsx20(SubagentWidget, { subagent }) : /* @__PURE__ */ jsx20("p", { className: "chat-widget-empty", children: "Agent details are unavailable." });
2132
+ }
2133
+ var prebuiltWidgets = {
2134
+ artifact: { name: "artifact", component: Artifact, shell: false },
2135
+ artifacts: { name: "artifacts", component: Artifacts, shell: false },
2136
+ subagent: { name: "subagent", component: Subagent, shell: false },
2137
+ "read-file": { name: "read-file", component: Read, shell: false },
2138
+ patch: { name: "patch", component: Patch, shell: false },
2139
+ search: { name: "search", component: Search, shell: false },
2140
+ question: { name: "question", component: Question, shell: false }
2141
+ };
2142
+
2143
+ // src/widgets/index.ts
2144
+ import { createArtifactData, createSubagentData, parseArtifact as parseArtifact4, parseSubagent as parseSubagent3 } from "@sarchauhan/protocol";
2145
+
2146
+ export {
2147
+ ThemeProvider,
2148
+ useTheme,
2149
+ cn,
2150
+ motion,
2151
+ iconSwapStates,
2152
+ successCheckStates,
2153
+ dropdownOrigins,
2154
+ motionData,
2155
+ prefersReducedMotion,
2156
+ motionRoot,
2157
+ readDurationMs,
2158
+ swapText,
2159
+ openDropdown,
2160
+ closeDropdown,
2161
+ triggerErrorShake,
2162
+ clearErrorShake,
2163
+ calibrateCheckPath,
2164
+ TextSwap,
2165
+ useDropdownPresence,
2166
+ getUserDisplayText,
2167
+ ResponsiveDetail,
2168
+ safeArtifactUrl,
2169
+ formatArtifactSize,
2170
+ ArtifactWidget,
2171
+ ArtifactsWidget,
2172
+ computeToolState,
2173
+ normalizeSearchResults,
2174
+ computeToolWidget,
2175
+ computeDiff,
2176
+ safeResultUrl,
2177
+ computeTextDiff,
2178
+ SubagentWidget,
2179
+ PatchWidget,
2180
+ QuestionWidget,
2181
+ ReadFileWidget,
2182
+ SearchWidget,
2183
+ prebuiltWidgets,
2184
+ defineWidget,
2185
+ createWidgetRegistry,
2186
+ WidgetProvider,
2187
+ useWidgets,
2188
+ TextSkeleton,
2189
+ BaseWidget,
2190
+ WidgetRenderer,
2191
+ formatElapsed,
2192
+ formatElapsedWords,
2193
+ LoadingState,
2194
+ ThinkingBlock,
2195
+ MessageContent,
2196
+ createArtifactData,
2197
+ createSubagentData,
2198
+ parseArtifact4 as parseArtifact,
2199
+ parseSubagent3 as parseSubagent
2200
+ };
2201
+ //# sourceMappingURL=chunk-5SNRYULC.mjs.map