@aphrody/m3-ai 3.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.js ADDED
@@ -0,0 +1,2227 @@
1
+ 'use client';
2
+ import { forwardRef, useState, useMemo, useRef, useEffect, useId, useCallback } from 'react';
3
+ import { clsx } from 'clsx';
4
+ import { AnimatePresence, motion } from 'motion/react';
5
+ import { usePopover, Popover, SelectPill, Icon, CircleButton, Button, DropdownMenu, Collapsible, CollapsibleTrigger, CollapsibleContent, Select, SelectOption, Input, TextArea } from '@aphrody/m3-primitives';
6
+ import { m3Easings, m3Durations } from '@aphrody/m3-motion';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import ReactMarkdown from 'react-markdown';
9
+ import remarkGfm from 'remark-gfm';
10
+ import rehypeRaw from 'rehype-raw';
11
+
12
+ // src/types.ts
13
+ var TASK_STATUS_ORDER = [
14
+ "in_progress",
15
+ "ready",
16
+ "pending_approval",
17
+ "backlog",
18
+ "done"
19
+ ];
20
+ var TASK_STATUS_LABEL = {
21
+ in_progress: "In Progress",
22
+ ready: "Ready",
23
+ pending_approval: "Pending Approval",
24
+ backlog: "Backlog",
25
+ done: "Done"
26
+ };
27
+ var TASK_PRIORITY_LABEL = {
28
+ critical: "Critical",
29
+ high: "High",
30
+ medium: "Medium",
31
+ low: "Low"
32
+ };
33
+ function tryParseJson(text) {
34
+ if (!text || text.trim().length === 0) return null;
35
+ try {
36
+ const parsed = JSON.parse(text);
37
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
38
+ return parsed;
39
+ }
40
+ return null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ function isErrorResult(text, parsed) {
46
+ if (parsed?.error) return true;
47
+ if (parsed?.status === "error") return true;
48
+ if (parsed?.success === false) return true;
49
+ if (typeof parsed?.exit_code === "number" && parsed.exit_code !== 0) return true;
50
+ const lower = text.toLowerCase();
51
+ return lower.startsWith("error:") || lower.startsWith("error -") || lower.startsWith("failed:") || lower.startsWith("toolset error:");
52
+ }
53
+ function pairTranscriptSteps(steps) {
54
+ const items = [];
55
+ const resultsById = /* @__PURE__ */ new Map();
56
+ for (const step of steps) {
57
+ if (step.type === "tool_result") {
58
+ resultsById.set(step.call_id, { name: step.name, text: step.text });
59
+ }
60
+ }
61
+ for (const step of steps) {
62
+ if (step.type === "action") {
63
+ for (const content of step.content) {
64
+ if (content.type === "text") {
65
+ items.push({ kind: "text", text: content.text });
66
+ } else if (content.type === "tool_call") {
67
+ const result = resultsById.get(content.id);
68
+ const parsedArgs = tryParseJson(content.args);
69
+ const parsedResult = result ? tryParseJson(result.text) : null;
70
+ const hasError = result ? isErrorResult(result.text, parsedResult) : false;
71
+ items.push({
72
+ kind: "tool",
73
+ pair: {
74
+ id: content.id,
75
+ name: content.name,
76
+ argsRaw: content.args,
77
+ args: parsedArgs,
78
+ resultRaw: result?.text ?? null,
79
+ result: parsedResult,
80
+ status: result ? hasError ? "error" : "completed" : "running"
81
+ }
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ return items;
88
+ }
89
+ var focusRing = "outline-none focus-visible:outline-3 focus-visible:outline-secondary focus-visible:outline-offset-2";
90
+ var expandMotion = {
91
+ initial: { height: 0, opacity: 0 },
92
+ animate: {
93
+ height: "auto",
94
+ opacity: 1,
95
+ transition: { duration: m3Durations.medium2, ease: m3Easings.emphasizedDecelerate }
96
+ },
97
+ exit: {
98
+ height: 0,
99
+ opacity: 0,
100
+ transition: { duration: m3Durations.short4, ease: m3Easings.emphasizedAccelerate }
101
+ }
102
+ };
103
+ function CircularIndicator({
104
+ size = 16,
105
+ className,
106
+ label = "Loading"
107
+ }) {
108
+ const stroke = size <= 16 ? 2 : size <= 24 ? 3 : 4;
109
+ const r = (size - stroke) / 2;
110
+ const c = 2 * Math.PI * r;
111
+ return /* @__PURE__ */ jsx(
112
+ "span",
113
+ {
114
+ role: "progressbar",
115
+ "aria-label": label,
116
+ className: clsx("m3-circular-indicator inline-flex shrink-0 text-primary", className),
117
+ style: { width: size, height: size },
118
+ children: /* @__PURE__ */ jsx(
119
+ "svg",
120
+ {
121
+ width: size,
122
+ height: size,
123
+ viewBox: `0 0 ${size} ${size}`,
124
+ className: "animate-spin",
125
+ "aria-hidden": "true",
126
+ children: /* @__PURE__ */ jsx(
127
+ "circle",
128
+ {
129
+ cx: size / 2,
130
+ cy: size / 2,
131
+ r,
132
+ fill: "none",
133
+ stroke: "currentColor",
134
+ strokeWidth: stroke,
135
+ strokeLinecap: "round",
136
+ strokeDasharray: `${c * 0.7} ${c}`
137
+ }
138
+ )
139
+ }
140
+ )
141
+ }
142
+ );
143
+ }
144
+ var chipTones = {
145
+ primary: "bg-primary-container text-on-primary-container",
146
+ success: "bg-success-container text-on-success-container",
147
+ error: "bg-error-container text-on-error-container",
148
+ warning: "bg-warning-container text-on-warning-container",
149
+ neutral: "border border-outline-variant text-on-surface-variant"
150
+ };
151
+ function StatusChip({
152
+ tone,
153
+ icon,
154
+ children,
155
+ className
156
+ }) {
157
+ return /* @__PURE__ */ jsxs(
158
+ "span",
159
+ {
160
+ className: clsx(
161
+ "inline-flex h-6 shrink-0 items-center gap-1 rounded-small px-2 text-label-medium",
162
+ chipTones[tone],
163
+ className
164
+ ),
165
+ children: [
166
+ icon,
167
+ children
168
+ ]
169
+ }
170
+ );
171
+ }
172
+ var toolRenderers = {
173
+ browser_launch: {
174
+ summary(pair) {
175
+ const headless = pair.args?.headless;
176
+ return headless === false ? "Launch browser (visible)" : "Launch browser";
177
+ },
178
+ resultView(pair) {
179
+ if (!pair.resultRaw) return null;
180
+ return /* @__PURE__ */ jsx(ResultLine, { text: "Browser session started" });
181
+ }
182
+ },
183
+ browser_navigate: {
184
+ summary(pair) {
185
+ const url = pair.args?.url;
186
+ return url ? truncate(String(url), 60) : null;
187
+ },
188
+ resultView(pair) {
189
+ if (!pair.result) return /* @__PURE__ */ jsx(ResultText, { text: pair.resultRaw });
190
+ const title = pair.result.title;
191
+ const url = pair.result.url;
192
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 px-3 py-2", children: [
193
+ title && /* @__PURE__ */ jsxs("p", { className: "text-body-small text-on-surface-variant", children: [
194
+ /* @__PURE__ */ jsx("span", { className: "text-outline", children: "Title: " }),
195
+ title
196
+ ] }),
197
+ url && /* @__PURE__ */ jsx("p", { className: "font-code text-body-small text-outline", children: truncate(url, 80) })
198
+ ] });
199
+ }
200
+ },
201
+ browser_snapshot: {
202
+ summary(pair) {
203
+ if (!pair.resultRaw) return "Taking snapshot...";
204
+ const matches = pair.resultRaw.match(/\[\d+\]/g);
205
+ const count = matches?.length ?? 0;
206
+ return count > 0 ? `${count} interactive element${count !== 1 ? "s" : ""}` : "Page snapshot";
207
+ },
208
+ resultView(pair) {
209
+ if (!pair.resultRaw) return null;
210
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
211
+ }
212
+ },
213
+ browser_click: {
214
+ summary(pair) {
215
+ const index = pair.args?.index;
216
+ return index !== void 0 ? `Click element [${index}]` : "Click";
217
+ },
218
+ resultView(pair) {
219
+ if (!pair.resultRaw) return null;
220
+ return /* @__PURE__ */ jsx(ResultLine, { text: pair.resultRaw });
221
+ }
222
+ },
223
+ browser_type: {
224
+ summary(pair) {
225
+ const index = pair.args?.index;
226
+ const hasSecret = pair.args?.secret !== void 0;
227
+ const text = pair.args?.text;
228
+ if (hasSecret) {
229
+ return index !== void 0 ? `Type secret into [${index}]` : "Type secret";
230
+ }
231
+ if (text) {
232
+ const display = truncate(String(text), 30);
233
+ return index !== void 0 ? `Type "${display}" into [${index}]` : `Type "${display}"`;
234
+ }
235
+ return index !== void 0 ? `Type into [${index}]` : "Type";
236
+ },
237
+ resultView(pair) {
238
+ if (!pair.resultRaw) return null;
239
+ return /* @__PURE__ */ jsx(ResultLine, { text: pair.resultRaw });
240
+ }
241
+ },
242
+ browser_press_key: {
243
+ summary(pair) {
244
+ const key = pair.args?.key;
245
+ return key ? `Press ${key}` : "Press key";
246
+ },
247
+ resultView(pair) {
248
+ if (!pair.resultRaw) return null;
249
+ return /* @__PURE__ */ jsx(ResultLine, { text: pair.resultRaw });
250
+ }
251
+ },
252
+ browser_screenshot: {
253
+ summary() {
254
+ return "Capture screenshot";
255
+ },
256
+ resultView(pair) {
257
+ if (!pair.resultRaw) return null;
258
+ if (pair.result?.base64) {
259
+ const mimeType = pair.result.mime_type ?? "image/png";
260
+ return /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsx(
261
+ "img",
262
+ {
263
+ src: `data:${mimeType};base64,${pair.result.base64}`,
264
+ alt: "Browser screenshot",
265
+ className: "max-h-60 rounded-extra-small border border-outline-variant object-contain"
266
+ }
267
+ ) });
268
+ }
269
+ return /* @__PURE__ */ jsx(ResultLine, { text: truncate(pair.resultRaw, 100) });
270
+ }
271
+ },
272
+ browser_evaluate: {
273
+ summary(pair) {
274
+ const expression = pair.args?.expression;
275
+ return expression ? truncate(String(expression), 50) : "Evaluate JS";
276
+ },
277
+ argsView(pair) {
278
+ const expression = pair.args?.expression;
279
+ if (!expression) return null;
280
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
281
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-body-small font-medium text-outline", children: "JavaScript" }),
282
+ /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-on-surface-variant", children: String(expression) })
283
+ ] });
284
+ }
285
+ },
286
+ browser_tab_open: {
287
+ summary(pair) {
288
+ const url = pair.args?.url;
289
+ return url ? `Open tab: ${truncate(String(url), 50)}` : "Open new tab";
290
+ }
291
+ },
292
+ browser_tab_list: {
293
+ summary() {
294
+ return "List tabs";
295
+ }
296
+ },
297
+ browser_tab_close: {
298
+ summary(pair) {
299
+ const tabId = pair.args?.tab_id;
300
+ return tabId !== void 0 ? `Close tab ${tabId}` : "Close tab";
301
+ }
302
+ },
303
+ browser_close: {
304
+ summary() {
305
+ return "Close browser";
306
+ },
307
+ resultView(pair) {
308
+ if (!pair.resultRaw) return null;
309
+ return /* @__PURE__ */ jsx(ResultLine, { text: "Browser session closed" });
310
+ }
311
+ },
312
+ shell: {
313
+ summary(pair) {
314
+ const command = pair.args?.command;
315
+ if (!command) return null;
316
+ if (pair.result && typeof pair.result.exit_code === "number") {
317
+ const code = pair.result.exit_code;
318
+ const cmdStr = truncate(String(command), 50);
319
+ return code === 0 ? cmdStr : `${cmdStr} (exit ${code})`;
320
+ }
321
+ return truncate(String(command), 60);
322
+ },
323
+ argsView(pair) {
324
+ const command = pair.args?.command;
325
+ if (!command) return null;
326
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsxs("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-on-surface-variant", children: [
327
+ /* @__PURE__ */ jsx("span", { className: "select-none text-outline", children: "$ " }),
328
+ String(command)
329
+ ] }) });
330
+ },
331
+ resultView(pair) {
332
+ if (!pair.resultRaw) return null;
333
+ return /* @__PURE__ */ jsx(ShellResultView, { pair });
334
+ }
335
+ },
336
+ file_read: {
337
+ summary(pair) {
338
+ if (pair.title) return pair.title;
339
+ const path = pair.args?.path;
340
+ return path ? truncate(String(path), 60) : null;
341
+ },
342
+ argsView(pair) {
343
+ const path = pair.args?.path;
344
+ if (!path) return null;
345
+ const offset = pair.args?.offset;
346
+ const limit = pair.args?.limit;
347
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsxs("p", { className: "font-code text-body-small text-on-surface-variant", children: [
348
+ String(path),
349
+ offset ? ` (from line ${offset})` : "",
350
+ limit ? ` (${limit} lines)` : ""
351
+ ] }) });
352
+ },
353
+ resultView(pair) {
354
+ if (!pair.resultRaw) return null;
355
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
356
+ }
357
+ },
358
+ file_write: {
359
+ summary(pair) {
360
+ if (pair.title) return pair.title;
361
+ const path = pair.args?.path;
362
+ return path ? truncate(String(path), 60) : null;
363
+ },
364
+ argsView(pair) {
365
+ const path = pair.args?.path;
366
+ const content = pair.args?.content;
367
+ if (!path && !content) return null;
368
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
369
+ !!path && /* @__PURE__ */ jsx("p", { className: "mb-1 font-code text-body-small text-on-surface-variant", children: String(path) }),
370
+ !!content && /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-outline", children: truncate(String(content), 2e3) })
371
+ ] });
372
+ },
373
+ resultView(pair) {
374
+ if (!pair.resultRaw) return null;
375
+ return /* @__PURE__ */ jsx(ResultLine, { text: truncate(pair.resultRaw, 100) });
376
+ }
377
+ },
378
+ file_edit: {
379
+ summary(pair) {
380
+ if (pair.title) return pair.title;
381
+ const path = pair.args?.path;
382
+ return path ? truncate(String(path), 60) : null;
383
+ },
384
+ argsView(pair) {
385
+ const path = pair.args?.path;
386
+ const oldStr = pair.args?.old_string;
387
+ const newStr = pair.args?.new_string;
388
+ if (!path) return null;
389
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
390
+ /* @__PURE__ */ jsx("p", { className: "mb-1 font-code text-body-small text-on-surface-variant", children: String(path) }),
391
+ !!oldStr && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
392
+ /* @__PURE__ */ jsx("p", { className: "text-body-small font-medium text-error", children: "Old" }),
393
+ /* @__PURE__ */ jsx("pre", { className: "max-h-20 overflow-auto font-code text-body-small text-error", children: truncate(String(oldStr), 500) })
394
+ ] }),
395
+ !!newStr && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
396
+ /* @__PURE__ */ jsx("p", { className: "text-body-small font-medium text-success", children: "New" }),
397
+ /* @__PURE__ */ jsx("pre", { className: "max-h-20 overflow-auto font-code text-body-small text-success", children: truncate(String(newStr), 500) })
398
+ ] })
399
+ ] });
400
+ },
401
+ resultView(pair) {
402
+ if (!pair.resultRaw) return null;
403
+ return /* @__PURE__ */ jsx(ResultLine, { text: truncate(pair.resultRaw, 100) });
404
+ }
405
+ },
406
+ file_list: {
407
+ summary(pair) {
408
+ if (pair.title) return pair.title;
409
+ const path = pair.args?.path;
410
+ return path ? truncate(String(path), 60) : null;
411
+ },
412
+ resultView(pair) {
413
+ if (!pair.resultRaw) return null;
414
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
415
+ }
416
+ },
417
+ exec: {
418
+ summary(pair) {
419
+ const program = pair.args?.program;
420
+ const cmdArgs = pair.args?.args;
421
+ if (!program) return null;
422
+ const parts = [String(program)];
423
+ if (Array.isArray(cmdArgs)) {
424
+ for (const arg of cmdArgs) parts.push(String(arg));
425
+ }
426
+ const full = parts.join(" ");
427
+ if (pair.result && typeof pair.result.exit_code === "number") {
428
+ const code = pair.result.exit_code;
429
+ const cmdStr = truncate(full, 50);
430
+ return code === 0 ? cmdStr : `${cmdStr} (exit ${code})`;
431
+ }
432
+ return truncate(full, 60);
433
+ },
434
+ argsView(pair) {
435
+ const program = pair.args?.program;
436
+ if (!program) return null;
437
+ const parts = [String(program)];
438
+ const cmdArgs = pair.args?.args;
439
+ if (Array.isArray(cmdArgs)) {
440
+ for (const arg of cmdArgs) parts.push(String(arg));
441
+ }
442
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsxs("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-on-surface-variant", children: [
443
+ /* @__PURE__ */ jsx("span", { className: "select-none text-outline", children: "$ " }),
444
+ parts.join(" ")
445
+ ] }) });
446
+ },
447
+ resultView(pair) {
448
+ if (!pair.resultRaw) return null;
449
+ return /* @__PURE__ */ jsx(ShellResultView, { pair });
450
+ }
451
+ },
452
+ set_status: {
453
+ summary(pair) {
454
+ const kind = pair.args?.kind;
455
+ const message = pair.args?.message;
456
+ if (kind === "outcome") {
457
+ return message ? `Outcome: ${truncate(String(message), 50)}` : "Outcome set";
458
+ }
459
+ return message ? truncate(String(message), 60) : null;
460
+ },
461
+ resultView() {
462
+ return null;
463
+ }
464
+ },
465
+ // OpenCode tools
466
+ read: {
467
+ summary(pair) {
468
+ if (pair.title) return pair.title;
469
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
470
+ return filePath ? truncate(String(filePath), 60) : null;
471
+ },
472
+ argsView(pair) {
473
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
474
+ if (!filePath) return null;
475
+ const offset = pair.args?.offset;
476
+ const limit = pair.args?.limit;
477
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsxs("p", { className: "font-code text-body-small text-on-surface-variant", children: [
478
+ String(filePath),
479
+ offset ? ` (from line ${offset})` : "",
480
+ limit ? ` (${limit} lines)` : ""
481
+ ] }) });
482
+ },
483
+ resultView(pair) {
484
+ if (!pair.resultRaw) return null;
485
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
486
+ }
487
+ },
488
+ write: {
489
+ summary(pair) {
490
+ if (pair.title) return pair.title;
491
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
492
+ return filePath ? truncate(String(filePath), 60) : null;
493
+ },
494
+ argsView(pair) {
495
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
496
+ const content = pair.args?.content;
497
+ if (!filePath && !content) return null;
498
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
499
+ !!filePath && /* @__PURE__ */ jsx("p", { className: "mb-1 font-code text-body-small text-on-surface-variant", children: String(filePath) }),
500
+ !!content && /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-outline", children: truncate(String(content), 2e3) })
501
+ ] });
502
+ },
503
+ resultView(pair) {
504
+ if (!pair.resultRaw) return null;
505
+ return /* @__PURE__ */ jsx(ResultLine, { text: truncate(pair.resultRaw, 100) });
506
+ }
507
+ },
508
+ edit: {
509
+ summary(pair) {
510
+ if (pair.title) return pair.title;
511
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
512
+ return filePath ? truncate(String(filePath), 60) : null;
513
+ },
514
+ argsView(pair) {
515
+ const filePath = pair.args?.filePath ?? pair.args?.file_path;
516
+ const oldStr = pair.args?.oldString ?? pair.args?.old_string;
517
+ const newStr = pair.args?.newString ?? pair.args?.new_string;
518
+ if (!filePath) return null;
519
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
520
+ /* @__PURE__ */ jsx("p", { className: "mb-1 font-code text-body-small text-on-surface-variant", children: String(filePath) }),
521
+ !!oldStr && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
522
+ /* @__PURE__ */ jsx("p", { className: "text-body-small font-medium text-error", children: "Old" }),
523
+ /* @__PURE__ */ jsx("pre", { className: "max-h-20 overflow-auto font-code text-body-small text-error", children: truncate(String(oldStr), 500) })
524
+ ] }),
525
+ !!newStr && /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
526
+ /* @__PURE__ */ jsx("p", { className: "text-body-small font-medium text-success", children: "New" }),
527
+ /* @__PURE__ */ jsx("pre", { className: "max-h-20 overflow-auto font-code text-body-small text-success", children: truncate(String(newStr), 500) })
528
+ ] })
529
+ ] });
530
+ },
531
+ resultView(pair) {
532
+ if (!pair.resultRaw) return null;
533
+ return /* @__PURE__ */ jsx(ResultLine, { text: truncate(pair.resultRaw, 100) });
534
+ }
535
+ },
536
+ bash: {
537
+ summary(pair) {
538
+ if (pair.title) return pair.title;
539
+ const command = pair.args?.command;
540
+ if (!command) return null;
541
+ if (pair.result && typeof pair.result.exit_code === "number") {
542
+ const code = pair.result.exit_code;
543
+ const cmdStr = truncate(String(command), 50);
544
+ return code === 0 ? cmdStr : `${cmdStr} (exit ${code})`;
545
+ }
546
+ return truncate(String(command), 60);
547
+ },
548
+ argsView(pair) {
549
+ const command = pair.args?.command;
550
+ if (!command) return null;
551
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsxs("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-on-surface-variant", children: [
552
+ /* @__PURE__ */ jsx("span", { className: "select-none text-outline", children: "$ " }),
553
+ String(command)
554
+ ] }) });
555
+ },
556
+ resultView(pair) {
557
+ if (!pair.resultRaw) return null;
558
+ return /* @__PURE__ */ jsx(ShellResultView, { pair });
559
+ }
560
+ },
561
+ glob: {
562
+ summary(pair) {
563
+ if (pair.title) return pair.title;
564
+ const pattern = pair.args?.pattern;
565
+ return pattern ? truncate(String(pattern), 60) : null;
566
+ },
567
+ resultView(pair) {
568
+ if (!pair.resultRaw) return null;
569
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
570
+ }
571
+ },
572
+ grep: {
573
+ summary(pair) {
574
+ if (pair.title) return pair.title;
575
+ const pattern = pair.args?.pattern;
576
+ const include = pair.args?.include;
577
+ if (pattern && include) {
578
+ return `/${pattern}/ in ${include}`;
579
+ }
580
+ return pattern ? `/${truncate(String(pattern), 40)}/` : null;
581
+ },
582
+ resultView(pair) {
583
+ if (!pair.resultRaw) return null;
584
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
585
+ }
586
+ },
587
+ webfetch: {
588
+ summary(pair) {
589
+ if (pair.title) return pair.title;
590
+ const url = pair.args?.url;
591
+ return url ? truncate(String(url), 60) : null;
592
+ },
593
+ resultView(pair) {
594
+ if (!pair.resultRaw) return null;
595
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
596
+ }
597
+ },
598
+ read_skill: {
599
+ summary(pair) {
600
+ if (pair.title) return pair.title;
601
+ const name = pair.args?.name;
602
+ return name ? String(name) : null;
603
+ },
604
+ resultView(pair) {
605
+ if (!pair.resultRaw) return null;
606
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
607
+ }
608
+ },
609
+ web_search: {
610
+ summary(pair) {
611
+ if (pair.title) return pair.title;
612
+ const query = pair.args?.query;
613
+ const resultCount = pair.result?.result_count ?? (Array.isArray(pair.result?.results) ? pair.result.results.length : null);
614
+ const queryStr = query ? truncate(String(query), 50) : null;
615
+ if (queryStr && resultCount != null) {
616
+ return `${queryStr} (${resultCount} result${resultCount !== 1 ? "s" : ""})`;
617
+ }
618
+ return queryStr;
619
+ },
620
+ argsView(pair) {
621
+ const query = pair.args?.query;
622
+ if (!query) return null;
623
+ const count = pair.args?.count;
624
+ const freshness = pair.args?.freshness;
625
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
626
+ /* @__PURE__ */ jsxs("p", { className: "text-body-small text-on-surface-variant", children: [
627
+ /* @__PURE__ */ jsx("span", { className: "select-none text-outline", children: "Search: " }),
628
+ String(query)
629
+ ] }),
630
+ !!(count || freshness) && /* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-body-small text-outline", children: [
631
+ count ? `${count} results` : "",
632
+ count && freshness ? " \xB7 " : "",
633
+ freshness ? `${freshness}` : ""
634
+ ] })
635
+ ] });
636
+ },
637
+ resultView(pair) {
638
+ if (!pair.resultRaw) return null;
639
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
640
+ }
641
+ },
642
+ spacebot_docs: {
643
+ summary(pair) {
644
+ if (pair.title) return pair.title;
645
+ const action = pair.args?.action;
646
+ const docId = pair.args?.doc_id;
647
+ if (action === "read" && docId) {
648
+ return truncate(String(docId), 50);
649
+ }
650
+ return action ? String(action) : "list";
651
+ },
652
+ argsView(pair) {
653
+ const action = pair.args?.action;
654
+ const docId = pair.args?.doc_id;
655
+ const query = pair.args?.query;
656
+ if (!action && !docId) return null;
657
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: [
658
+ !!docId && /* @__PURE__ */ jsx("p", { className: "font-code text-body-small text-on-surface-variant", children: String(docId) }),
659
+ !!query && /* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-body-small text-outline", children: [
660
+ "filter: ",
661
+ String(query)
662
+ ] })
663
+ ] });
664
+ },
665
+ resultView(pair) {
666
+ if (!pair.resultRaw) return null;
667
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
668
+ }
669
+ },
670
+ todowrite: {
671
+ summary(pair) {
672
+ if (pair.title) return pair.title;
673
+ return "Update tasks";
674
+ },
675
+ resultView() {
676
+ return null;
677
+ }
678
+ },
679
+ task: {
680
+ summary(pair) {
681
+ if (pair.title) return pair.title;
682
+ const description = pair.args?.description;
683
+ return description ? truncate(String(description), 60) : null;
684
+ },
685
+ resultView(pair) {
686
+ if (!pair.resultRaw) return null;
687
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
688
+ }
689
+ }
690
+ };
691
+ var defaultRenderer = {
692
+ summary(pair) {
693
+ if (!pair.argsRaw || pair.argsRaw === "{}") return null;
694
+ return truncate(pair.argsRaw, 60);
695
+ }
696
+ };
697
+ function getRenderer(name) {
698
+ return toolRenderers[name] ?? defaultRenderer;
699
+ }
700
+ function ResultLine({ text }) {
701
+ return /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-body-small text-on-surface-variant", children: text });
702
+ }
703
+ function ResultText({ text }) {
704
+ if (!text) return null;
705
+ return /* @__PURE__ */ jsx("pre", { className: "max-h-60 overflow-auto whitespace-pre-wrap px-3 py-2 font-code text-body-small text-on-surface-variant", children: text });
706
+ }
707
+ function CollapsiblePre({ text, maxLines = 20 }) {
708
+ const [expanded, setExpanded] = useState(false);
709
+ const lines = text.split("\n");
710
+ const needsCollapse = lines.length > maxLines;
711
+ const displayText = needsCollapse && !expanded ? lines.slice(0, maxLines).join("\n") + "\n..." : text;
712
+ return /* @__PURE__ */ jsxs("div", { children: [
713
+ /* @__PURE__ */ jsx("pre", { className: "max-h-80 overflow-auto whitespace-pre-wrap px-3 py-2 font-code text-body-small text-on-surface-variant", children: displayText }),
714
+ needsCollapse && /* @__PURE__ */ jsxs(
715
+ "button",
716
+ {
717
+ type: "button",
718
+ onClick: () => setExpanded(!expanded),
719
+ className: clsx(
720
+ "flex h-8 w-full items-center justify-center gap-1 border-t border-outline-variant px-3 text-label-medium text-primary",
721
+ "transition-colors duration-200 ease-standard hover:bg-primary/8 active:bg-primary/10",
722
+ focusRing
723
+ ),
724
+ children: [
725
+ /* @__PURE__ */ jsx(
726
+ Icon,
727
+ {
728
+ name: "expand_more",
729
+ size: 18,
730
+ className: clsx(
731
+ "transition-transform duration-200 ease-standard",
732
+ expanded && "rotate-180"
733
+ )
734
+ }
735
+ ),
736
+ expanded ? "Show less" : `Show all ${lines.length} lines`
737
+ ]
738
+ }
739
+ )
740
+ ] });
741
+ }
742
+ function ShellResultView({ pair }) {
743
+ const r = pair.result;
744
+ if (!r || typeof r.exit_code !== "number") {
745
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 30 });
746
+ }
747
+ const exitCode = r.exit_code;
748
+ const stdout = typeof r.stdout === "string" ? r.stdout : "";
749
+ const stderr = typeof r.stderr === "string" ? r.stderr : "";
750
+ const hasStdout = stdout.trim().length > 0;
751
+ const hasStderr = stderr.trim().length > 0;
752
+ const isError = exitCode !== 0;
753
+ if (!hasStdout && !hasStderr && exitCode === 0) {
754
+ return /* @__PURE__ */ jsx(ResultLine, { text: "Completed with no output" });
755
+ }
756
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
757
+ isError && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 border-b border-outline-variant px-3 py-1.5", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex h-6 items-center rounded-small bg-error px-2 font-code text-label-medium text-on-error", children: [
758
+ "exit ",
759
+ exitCode
760
+ ] }) }),
761
+ hasStdout && /* @__PURE__ */ jsx("div", { className: hasStderr ? "border-b border-outline-variant" : "", children: /* @__PURE__ */ jsx(CollapsiblePre, { text: stdout.replace(/\n$/, ""), maxLines: 30 }) }),
762
+ hasStderr && /* @__PURE__ */ jsxs("div", { children: [
763
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 px-3 pt-2 pb-0.5", children: /* @__PURE__ */ jsx("span", { className: clsx("text-label-medium", isError ? "text-error" : "text-warning"), children: "stderr" }) }),
764
+ /* @__PURE__ */ jsx(
765
+ "pre",
766
+ {
767
+ className: clsx(
768
+ "max-h-40 overflow-auto whitespace-pre-wrap px-3 pb-2 font-code text-body-small",
769
+ isError ? "text-error" : "text-warning"
770
+ ),
771
+ children: stderr.replace(/\n$/, "")
772
+ }
773
+ )
774
+ ] })
775
+ ] });
776
+ }
777
+ var STATUS_ICONS = {
778
+ running: "pending",
779
+ completed: "check_circle",
780
+ error: "error"
781
+ };
782
+ var STATUS_COLORS = {
783
+ running: "text-primary",
784
+ completed: "text-success",
785
+ error: "text-error"
786
+ };
787
+ var STATUS_LABELS = {
788
+ running: "Running",
789
+ completed: "Completed",
790
+ error: "Error"
791
+ };
792
+ function toolIcon(name) {
793
+ if (name.startsWith("browser_")) return "language";
794
+ if (name.startsWith("file_") || name === "read" || name === "write" || name === "edit") {
795
+ return "description";
796
+ }
797
+ if (name === "shell" || name === "exec" || name === "bash") return "terminal";
798
+ if (name === "glob" || name === "grep" || name === "web_search") return "search";
799
+ if (name === "webfetch") return "language";
800
+ if (name === "set_status") return "schedule";
801
+ return "build";
802
+ }
803
+ function formatToolName(name) {
804
+ const overrides = {
805
+ webfetch: "Web Fetch",
806
+ todowrite: "Todo",
807
+ read_skill: "Read Skill",
808
+ web_search: "Web Search",
809
+ spacebot_docs: "Docs"
810
+ };
811
+ if (overrides[name]) return overrides[name];
812
+ const stripped = name.replace(/^browser_/, "").replace(/^file_/, "").replace(/^tab_/, "Tab ");
813
+ return stripped.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
814
+ }
815
+ function toolCategory(name) {
816
+ if (name.startsWith("browser_")) return "Browser";
817
+ if (name.startsWith("file_")) return "File";
818
+ return null;
819
+ }
820
+ function ToolCall({ pair, defaultExpanded = false, className }) {
821
+ const [expanded, setExpanded] = useState(defaultExpanded);
822
+ const bodyId = useId();
823
+ const renderer = getRenderer(pair.name);
824
+ const summary = renderer.summary(pair);
825
+ const category = toolCategory(pair.name);
826
+ const displayName = formatToolName(pair.name);
827
+ const isError = pair.status === "error";
828
+ const args = expanded ? renderArgs(pair, renderer) : null;
829
+ const result = expanded ? renderResult(pair, renderer) : null;
830
+ return /* @__PURE__ */ jsxs(
831
+ "div",
832
+ {
833
+ "data-status": pair.status,
834
+ className: clsx(
835
+ "m3-tool-call min-w-0 overflow-hidden rounded-medium border bg-surface-container-low text-on-surface",
836
+ isError ? "border-error" : "border-outline-variant",
837
+ className
838
+ ),
839
+ children: [
840
+ /* @__PURE__ */ jsxs(
841
+ "button",
842
+ {
843
+ type: "button",
844
+ onClick: () => setExpanded(!expanded),
845
+ "aria-expanded": expanded,
846
+ "aria-controls": bodyId,
847
+ className: clsx(
848
+ "flex min-h-10 w-full items-center gap-2 rounded-medium px-3 py-2 text-left",
849
+ "transition-colors duration-200 ease-standard hover:bg-on-surface/8 active:bg-on-surface/10",
850
+ focusRing
851
+ ),
852
+ children: [
853
+ /* @__PURE__ */ jsx(
854
+ "span",
855
+ {
856
+ className: clsx(
857
+ "flex size-5 shrink-0 items-center justify-center",
858
+ STATUS_COLORS[pair.status]
859
+ ),
860
+ children: pair.status === "running" ? /* @__PURE__ */ jsx(CircularIndicator, { size: 16, label: "Running" }) : /* @__PURE__ */ jsx(
861
+ Icon,
862
+ {
863
+ name: STATUS_ICONS[pair.status],
864
+ size: 18,
865
+ filled: true,
866
+ label: STATUS_LABELS[pair.status]
867
+ }
868
+ )
869
+ }
870
+ ),
871
+ /* @__PURE__ */ jsx(Icon, { name: toolIcon(pair.name), size: 18, className: "shrink-0 text-on-surface-variant" }),
872
+ category && /* @__PURE__ */ jsx("span", { className: "text-label-medium text-outline", children: category }),
873
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-label-large text-on-surface", children: displayName }),
874
+ summary && !expanded ? /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-body-small text-on-surface-variant", children: summary }) : /* @__PURE__ */ jsx("span", { className: "flex-1" }),
875
+ isError && /* @__PURE__ */ jsx(StatusChip, { tone: "error", className: "h-5 px-1.5 text-label-small", children: "error" }),
876
+ /* @__PURE__ */ jsx(
877
+ Icon,
878
+ {
879
+ name: "expand_more",
880
+ size: 20,
881
+ className: clsx(
882
+ "shrink-0 text-on-surface-variant transition-transform duration-300 ease-emphasized",
883
+ expanded && "rotate-180"
884
+ )
885
+ }
886
+ )
887
+ ]
888
+ }
889
+ ),
890
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: expanded ? /* @__PURE__ */ jsx(motion.div, { id: bodyId, ...expandMotion, className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 border-t border-outline-variant px-3 py-3", children: [
891
+ args,
892
+ result != null && result !== false ? /* @__PURE__ */ jsx(
893
+ "div",
894
+ {
895
+ className: clsx(
896
+ "m3-tool-result overflow-hidden rounded-small",
897
+ isError ? "bg-error-container text-on-error-container [&_.text-on-surface-variant]:text-on-error-container [&_.text-outline]:text-on-error-container/80" : "bg-surface-container-highest text-on-surface-variant"
898
+ ),
899
+ children: result
900
+ }
901
+ ) : null
902
+ ] }) }, "body") : null })
903
+ ]
904
+ }
905
+ );
906
+ }
907
+ function renderArgs(pair, renderer) {
908
+ if (renderer.argsView) {
909
+ const custom = renderer.argsView(pair);
910
+ if (custom) return custom;
911
+ }
912
+ if (pair.args && Object.keys(pair.args).length > 0) {
913
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: Object.entries(pair.args).map(([key, value]) => /* @__PURE__ */ jsxs("p", { className: "font-code text-body-small", children: [
914
+ /* @__PURE__ */ jsxs("span", { className: "text-outline", children: [
915
+ key,
916
+ ": "
917
+ ] }),
918
+ /* @__PURE__ */ jsx("span", { className: "text-on-surface-variant", children: formatArgValue(key, value) })
919
+ ] }, key)) }) });
920
+ }
921
+ if (pair.argsRaw && pair.argsRaw !== "{}" && pair.argsRaw.trim().length > 0) {
922
+ return /* @__PURE__ */ jsx("div", { className: "rounded-small bg-surface-container-highest px-3 py-2", children: /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto font-code text-body-small text-on-surface-variant", children: pair.argsRaw }) });
923
+ }
924
+ return null;
925
+ }
926
+ function renderResult(pair, renderer) {
927
+ if (pair.status === "running") {
928
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-body-small text-on-surface-variant", children: [
929
+ /* @__PURE__ */ jsx(CircularIndicator, { size: 14, label: "Running" }),
930
+ "Running..."
931
+ ] });
932
+ }
933
+ if (renderer.resultView) {
934
+ const custom = renderer.resultView(pair);
935
+ if (custom !== void 0) return custom;
936
+ }
937
+ if (!pair.resultRaw) return null;
938
+ if (pair.result && Object.keys(pair.result).length > 0) {
939
+ return /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: Object.entries(pair.result).map(([key, value]) => /* @__PURE__ */ jsxs("p", { className: "font-code text-body-small", children: [
940
+ /* @__PURE__ */ jsxs("span", { className: "text-outline", children: [
941
+ key,
942
+ ": "
943
+ ] }),
944
+ /* @__PURE__ */ jsx("span", { className: "text-on-surface-variant", children: typeof value === "string" ? truncate(value, 200) : JSON.stringify(value) })
945
+ ] }, key)) }) });
946
+ }
947
+ return /* @__PURE__ */ jsx(CollapsiblePre, { text: pair.resultRaw, maxLines: 20 });
948
+ }
949
+ function formatArgValue(key, value) {
950
+ if (key === "secret" && typeof value === "string") {
951
+ return "***";
952
+ }
953
+ if (typeof value === "string") {
954
+ return truncate(value, 100);
955
+ }
956
+ if (typeof value === "boolean" || typeof value === "number") {
957
+ return String(value);
958
+ }
959
+ return JSON.stringify(value);
960
+ }
961
+ function truncate(text, maxLen) {
962
+ if (text.length <= maxLen) return text;
963
+ return text.slice(0, maxLen) + "...";
964
+ }
965
+ var bodyClass = {
966
+ small: "text-body-small",
967
+ medium: "text-body-medium",
968
+ large: "text-body-large"
969
+ };
970
+ function m3Components(size) {
971
+ const body = bodyClass[size];
972
+ return {
973
+ h1: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
974
+ "h1",
975
+ {
976
+ className: clsx("mt-4 mb-2 text-headline-small text-on-surface first:mt-0", className),
977
+ ...props,
978
+ children
979
+ }
980
+ ),
981
+ h2: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
982
+ "h2",
983
+ {
984
+ className: clsx("mt-4 mb-2 text-title-large text-on-surface first:mt-0", className),
985
+ ...props,
986
+ children
987
+ }
988
+ ),
989
+ h3: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
990
+ "h3",
991
+ {
992
+ className: clsx("mt-3 mb-1.5 text-title-medium text-on-surface first:mt-0", className),
993
+ ...props,
994
+ children
995
+ }
996
+ ),
997
+ h4: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
998
+ "h4",
999
+ {
1000
+ className: clsx("mt-3 mb-1 text-title-small text-on-surface first:mt-0", className),
1001
+ ...props,
1002
+ children
1003
+ }
1004
+ ),
1005
+ h5: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
1006
+ "h5",
1007
+ {
1008
+ className: clsx("mt-3 mb-1 text-title-small text-on-surface-variant first:mt-0", className),
1009
+ ...props,
1010
+ children
1011
+ }
1012
+ ),
1013
+ h6: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
1014
+ "h6",
1015
+ {
1016
+ className: clsx("mt-3 mb-1 text-label-large text-on-surface-variant first:mt-0", className),
1017
+ ...props,
1018
+ children
1019
+ }
1020
+ ),
1021
+ p: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("p", { className: clsx("my-2 first:mt-0 last:mb-0", body, className), ...props }),
1022
+ a: ({ node: _node, className, children, ...props }) => /* @__PURE__ */ jsx(
1023
+ "a",
1024
+ {
1025
+ className: clsx(
1026
+ "text-primary underline underline-offset-2 hover:decoration-2",
1027
+ "rounded-extra-small outline-none focus-visible:outline-3 focus-visible:outline-secondary focus-visible:outline-offset-2",
1028
+ className
1029
+ ),
1030
+ ...props,
1031
+ children
1032
+ }
1033
+ ),
1034
+ strong: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("strong", { className: clsx("font-semibold text-on-surface", className), ...props }),
1035
+ ul: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1036
+ "ul",
1037
+ {
1038
+ className: clsx("my-2 list-disc pl-6 marker:text-on-surface-variant", body, className),
1039
+ ...props
1040
+ }
1041
+ ),
1042
+ ol: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1043
+ "ol",
1044
+ {
1045
+ className: clsx("my-2 list-decimal pl-6 marker:text-on-surface-variant", body, className),
1046
+ ...props
1047
+ }
1048
+ ),
1049
+ li: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("li", { className: clsx("my-1", className), ...props }),
1050
+ blockquote: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1051
+ "blockquote",
1052
+ {
1053
+ className: clsx(
1054
+ "my-3 border-l-4 border-outline-variant pl-4 text-on-surface-variant",
1055
+ className
1056
+ ),
1057
+ ...props
1058
+ }
1059
+ ),
1060
+ code: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1061
+ "code",
1062
+ {
1063
+ className: clsx(
1064
+ "rounded-extra-small bg-surface-container-highest px-1 py-0.5 font-code text-[0.9em] text-on-surface",
1065
+ className
1066
+ ),
1067
+ ...props
1068
+ }
1069
+ ),
1070
+ pre: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1071
+ "pre",
1072
+ {
1073
+ className: clsx(
1074
+ "my-3 overflow-x-auto rounded-small bg-surface-container-highest p-3 font-code text-body-small text-on-surface",
1075
+ "[&_code]:bg-transparent [&_code]:p-0 [&_code]:text-[length:inherit]",
1076
+ className
1077
+ ),
1078
+ ...props
1079
+ }
1080
+ ),
1081
+ hr: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("hr", { className: clsx("my-4 border-outline-variant", className), ...props }),
1082
+ table: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("div", { className: "my-3 overflow-x-auto rounded-small border border-outline-variant", children: /* @__PURE__ */ jsx("table", { className: clsx("w-full border-collapse", body, className), ...props }) }),
1083
+ thead: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("thead", { className: clsx("bg-surface-container", className), ...props }),
1084
+ th: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1085
+ "th",
1086
+ {
1087
+ className: clsx(
1088
+ "border-b border-outline-variant px-3 py-2 text-left text-title-small text-on-surface",
1089
+ className
1090
+ ),
1091
+ ...props
1092
+ }
1093
+ ),
1094
+ td: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx(
1095
+ "td",
1096
+ {
1097
+ className: clsx("border-t border-outline-variant px-3 py-2 text-on-surface", className),
1098
+ ...props
1099
+ }
1100
+ ),
1101
+ img: ({ node: _node, className, alt, ...props }) => /* @__PURE__ */ jsx(
1102
+ "img",
1103
+ {
1104
+ className: clsx("my-2 max-w-full rounded-medium", className),
1105
+ alt: alt ?? "",
1106
+ ...props
1107
+ }
1108
+ ),
1109
+ input: ({ node: _node, className, ...props }) => /* @__PURE__ */ jsx("input", { className: clsx("mr-2 accent-primary", className), ...props })
1110
+ };
1111
+ }
1112
+ var Markdown = forwardRef(
1113
+ ({ content, className, size = "medium", allowHtml = false }, ref) => {
1114
+ return /* @__PURE__ */ jsx(
1115
+ "div",
1116
+ {
1117
+ ref,
1118
+ className: clsx("markdown min-w-0 text-on-surface", bodyClass[size], className),
1119
+ children: /* @__PURE__ */ jsx(
1120
+ ReactMarkdown,
1121
+ {
1122
+ remarkPlugins: [remarkGfm],
1123
+ rehypePlugins: allowHtml ? [rehypeRaw] : void 0,
1124
+ components: m3Components(size),
1125
+ children: content
1126
+ }
1127
+ )
1128
+ }
1129
+ );
1130
+ }
1131
+ );
1132
+ Markdown.displayName = "Markdown";
1133
+ function MessageBubble({
1134
+ content,
1135
+ isUser,
1136
+ isStreaming = false,
1137
+ onCopy,
1138
+ className
1139
+ }) {
1140
+ return /* @__PURE__ */ jsxs(
1141
+ "div",
1142
+ {
1143
+ className: clsx("group flex flex-col py-2", isUser ? "items-end" : "items-start", className),
1144
+ children: [
1145
+ /* @__PURE__ */ jsx(
1146
+ "div",
1147
+ {
1148
+ className: clsx(
1149
+ "min-w-0",
1150
+ isUser ? "max-w-[80%] rounded-large rounded-br-extra-small bg-secondary-container px-4 py-2.5 text-body-large text-on-secondary-container" : "w-full max-w-full bg-transparent text-body-large text-on-surface"
1151
+ ),
1152
+ "aria-busy": isStreaming || void 0,
1153
+ children: isUser ? /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap break-words", children: content }) : /* @__PURE__ */ jsx(Markdown, { content, size: "large", className: "break-words" })
1154
+ }
1155
+ ),
1156
+ !isUser && onCopy ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex opacity-0 transition-opacity duration-200 ease-standard group-focus-within:opacity-100 group-hover:opacity-100", children: /* @__PURE__ */ jsx(
1157
+ CircleButton,
1158
+ {
1159
+ icon: "content_copy",
1160
+ variant: "standard",
1161
+ size: "sm",
1162
+ onClick: () => onCopy(content),
1163
+ "aria-label": isStreaming ? "Copy Streaming Message" : "Copy Message",
1164
+ title: isStreaming ? "Copy streaming message" : "Copy message"
1165
+ }
1166
+ ) }) : null
1167
+ ]
1168
+ }
1169
+ );
1170
+ }
1171
+ function stripExcessWhitespace(text) {
1172
+ return text.replace(/ {3,}/g, " ");
1173
+ }
1174
+ function statusTone(status) {
1175
+ switch (status) {
1176
+ case "running":
1177
+ return "primary";
1178
+ case "completed":
1179
+ return "success";
1180
+ case "failed":
1181
+ case "error":
1182
+ case "cancelled":
1183
+ return "error";
1184
+ default:
1185
+ return "neutral";
1186
+ }
1187
+ }
1188
+ function InlineWorkerCard({
1189
+ title,
1190
+ status,
1191
+ toolCallCount,
1192
+ liveStatus,
1193
+ transcript,
1194
+ isTranscriptLoading,
1195
+ onCopyLogs,
1196
+ onCancel,
1197
+ className,
1198
+ defaultExpanded = false
1199
+ }) {
1200
+ const [expanded, setExpanded] = useState(defaultExpanded);
1201
+ const contentId = useId();
1202
+ const items = useMemo(() => pairTranscriptSteps(transcript), [transcript]);
1203
+ const isRunning = status === "running";
1204
+ const isDone = status === "completed";
1205
+ const tone = statusTone(status);
1206
+ return /* @__PURE__ */ jsxs("div", { className: clsx("group flex min-w-0 flex-col items-start", className), children: [
1207
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-medium bg-surface-container text-on-surface", children: [
1208
+ /* @__PURE__ */ jsxs(
1209
+ "button",
1210
+ {
1211
+ type: "button",
1212
+ onClick: () => setExpanded((v) => !v),
1213
+ "aria-expanded": expanded,
1214
+ "aria-controls": contentId,
1215
+ className: clsx(
1216
+ "flex w-full items-start gap-3 rounded-medium px-4 py-3 text-left transition-colors duration-200 ease-standard",
1217
+ "hover:bg-on-surface/8 active:bg-on-surface/10",
1218
+ focusRing
1219
+ ),
1220
+ children: [
1221
+ /* @__PURE__ */ jsx("div", { className: "flex size-8 shrink-0 items-center justify-center", children: isRunning ? /* @__PURE__ */ jsx(CircularIndicator, { size: 20, label: "Worker running" }) : isDone ? /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-success-container text-on-success-container", children: /* @__PURE__ */ jsx(Icon, { name: "check_circle", size: 18, filled: true }) }) : tone === "error" ? /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-error-container text-on-error-container", children: /* @__PURE__ */ jsx(Icon, { name: "error", size: 18, filled: true }) }) : /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-surface-container-highest text-on-surface-variant", children: /* @__PURE__ */ jsx(Icon, { name: "build", size: 18 }) }) }),
1222
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1223
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1224
+ /* @__PURE__ */ jsx("div", { className: "line-clamp-2 min-w-0 flex-1 text-title-small text-on-surface", children: title }),
1225
+ /* @__PURE__ */ jsx(StatusChip, { tone, children: status })
1226
+ ] }),
1227
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2 text-body-small text-on-surface-variant", children: [
1228
+ /* @__PURE__ */ jsxs("span", { children: [
1229
+ toolCallCount,
1230
+ " tool calls"
1231
+ ] }),
1232
+ liveStatus ? /* @__PURE__ */ jsx("span", { className: "truncate text-outline", children: liveStatus }) : null
1233
+ ] })
1234
+ ] }),
1235
+ /* @__PURE__ */ jsx(
1236
+ Icon,
1237
+ {
1238
+ name: "expand_more",
1239
+ size: 20,
1240
+ className: clsx(
1241
+ "mt-1.5 shrink-0 text-on-surface-variant transition-transform duration-300 ease-emphasized",
1242
+ expanded ? "rotate-180" : ""
1243
+ )
1244
+ }
1245
+ )
1246
+ ]
1247
+ }
1248
+ ),
1249
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: expanded ? /* @__PURE__ */ jsx(
1250
+ motion.div,
1251
+ {
1252
+ id: contentId,
1253
+ ...expandMotion,
1254
+ className: "overflow-hidden",
1255
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 border-t border-outline-variant px-4 py-3", children: isTranscriptLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-body-small text-on-surface-variant", children: [
1256
+ /* @__PURE__ */ jsx(CircularIndicator, { size: 16, label: "Loading transcript" }),
1257
+ "Loading worker transcript..."
1258
+ ] }) : items.length > 0 ? items.map(
1259
+ (item) => item.kind === "tool" ? /* @__PURE__ */ jsx(ToolCall, { pair: item.pair }, item.pair.id) : /* @__PURE__ */ jsx(
1260
+ Markdown,
1261
+ {
1262
+ content: stripExcessWhitespace(item.text),
1263
+ size: "small",
1264
+ className: "text-on-surface-variant"
1265
+ },
1266
+ `text-${item.text}`
1267
+ )
1268
+ ) : /* @__PURE__ */ jsx("div", { className: "text-body-small text-on-surface-variant", children: "No tool calls yet." }) })
1269
+ },
1270
+ "transcript"
1271
+ ) : null })
1272
+ ] }),
1273
+ (onCopyLogs || onCancel && isRunning) && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1 opacity-0 transition-opacity duration-200 ease-standard group-focus-within:opacity-100 group-hover:opacity-100", children: [
1274
+ onCopyLogs && /* @__PURE__ */ jsxs(Button, { variant: "text", size: "xs", onClick: onCopyLogs, title: "Copy logs", children: [
1275
+ /* @__PURE__ */ jsx(Icon, { name: "content_copy", size: 18 }),
1276
+ "Copy"
1277
+ ] }),
1278
+ onCancel && isRunning && /* @__PURE__ */ jsxs(Button, { variant: "text", size: "xs", onClick: onCancel, title: "Cancel", children: [
1279
+ /* @__PURE__ */ jsx(Icon, { name: "stop", size: 18 }),
1280
+ "Cancel"
1281
+ ] })
1282
+ ] })
1283
+ ] });
1284
+ }
1285
+ function InlineBranchCard({
1286
+ description,
1287
+ completedAt,
1288
+ conclusion,
1289
+ className,
1290
+ defaultExpanded = false
1291
+ }) {
1292
+ const [expanded, setExpanded] = useState(defaultExpanded);
1293
+ const isRunning = !completedAt;
1294
+ const contentId = useId();
1295
+ const canExpand = !!conclusion;
1296
+ return /* @__PURE__ */ jsx("div", { className: clsx("group flex min-w-0 flex-col items-start", className), children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-medium bg-surface-container text-on-surface", children: [
1297
+ /* @__PURE__ */ jsxs(
1298
+ "button",
1299
+ {
1300
+ type: "button",
1301
+ onClick: () => canExpand && setExpanded((v) => !v),
1302
+ "aria-expanded": canExpand ? expanded : void 0,
1303
+ "aria-controls": canExpand ? contentId : void 0,
1304
+ className: clsx(
1305
+ "flex w-full items-start gap-3 rounded-medium px-4 py-3 text-left transition-colors duration-200 ease-standard",
1306
+ canExpand && "hover:bg-on-surface/8 active:bg-on-surface/10",
1307
+ focusRing
1308
+ ),
1309
+ children: [
1310
+ /* @__PURE__ */ jsx("div", { className: "flex size-8 shrink-0 items-center justify-center", children: isRunning ? /* @__PURE__ */ jsx(CircularIndicator, { size: 20, label: "Thinking" }) : /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded-full bg-primary-container text-on-primary-container", children: /* @__PURE__ */ jsx(Icon, { name: "alt_route", size: 18 }) }) }),
1311
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 self-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1312
+ /* @__PURE__ */ jsx("div", { className: "line-clamp-2 min-w-0 flex-1 text-title-small text-on-surface", children: description }),
1313
+ /* @__PURE__ */ jsx(StatusChip, { tone: isRunning ? "primary" : "success", children: isRunning ? "thinking" : "done" })
1314
+ ] }) }),
1315
+ canExpand && /* @__PURE__ */ jsx(
1316
+ Icon,
1317
+ {
1318
+ name: "expand_more",
1319
+ size: 20,
1320
+ className: clsx(
1321
+ "mt-1.5 shrink-0 text-on-surface-variant transition-transform duration-300 ease-emphasized",
1322
+ expanded ? "rotate-180" : ""
1323
+ )
1324
+ }
1325
+ )
1326
+ ]
1327
+ }
1328
+ ),
1329
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: expanded && conclusion ? /* @__PURE__ */ jsx(
1330
+ motion.div,
1331
+ {
1332
+ id: contentId,
1333
+ ...expandMotion,
1334
+ className: "overflow-hidden",
1335
+ children: /* @__PURE__ */ jsx("div", { className: "border-t border-outline-variant px-4 py-3", children: /* @__PURE__ */ jsx(Markdown, { content: conclusion, size: "small", className: "text-on-surface-variant" }) })
1336
+ },
1337
+ "conclusion"
1338
+ ) : null })
1339
+ ] }) });
1340
+ }
1341
+ function groupModelsByProvider(models, search = "") {
1342
+ const q = search.trim().toLowerCase();
1343
+ const filtered = q ? models.filter((m) => m.name.toLowerCase().includes(q) || m.provider.toLowerCase().includes(q)) : models;
1344
+ return filtered.reduce(
1345
+ (acc, model) => {
1346
+ if (!acc[model.provider]) acc[model.provider] = [];
1347
+ acc[model.provider].push(model);
1348
+ return acc;
1349
+ },
1350
+ {}
1351
+ );
1352
+ }
1353
+ function ModelOptionList({ grouped, value, onSelect, className }) {
1354
+ const providers = Object.entries(grouped);
1355
+ return /* @__PURE__ */ jsxs(
1356
+ "div",
1357
+ {
1358
+ role: "listbox",
1359
+ "aria-label": "Models",
1360
+ className: clsx("flex flex-col", className),
1361
+ children: [
1362
+ providers.map(([provider, providerModels], groupIndex) => /* @__PURE__ */ jsxs(
1363
+ "div",
1364
+ {
1365
+ role: "group",
1366
+ "aria-label": provider,
1367
+ className: clsx(groupIndex > 0 && "mt-1 border-t border-outline-variant pt-1"),
1368
+ children: [
1369
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-2 pb-1 text-title-small text-on-surface-variant", children: provider }),
1370
+ providerModels.map((model) => {
1371
+ const selected = model.id === value;
1372
+ return /* @__PURE__ */ jsxs(
1373
+ "button",
1374
+ {
1375
+ type: "button",
1376
+ role: "option",
1377
+ "aria-selected": selected,
1378
+ onClick: () => onSelect(model.id),
1379
+ className: clsx(
1380
+ "flex min-h-12 w-full items-center gap-3 px-3 py-2 text-left",
1381
+ "transition-colors duration-200 ease-standard",
1382
+ selected ? "bg-secondary-container text-on-secondary-container" : "text-on-surface hover:bg-on-surface/8 active:bg-on-surface/10",
1383
+ focusRing,
1384
+ "focus-visible:outline-offset-[-3px]"
1385
+ ),
1386
+ children: [
1387
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1388
+ /* @__PURE__ */ jsx("div", { className: "truncate text-label-large", children: model.name }),
1389
+ model.context_window || model.capabilities?.length ? /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-1", children: [
1390
+ model.context_window ? /* @__PURE__ */ jsxs(
1391
+ "span",
1392
+ {
1393
+ className: clsx(
1394
+ "text-body-small",
1395
+ selected ? "text-on-secondary-container" : "text-on-surface-variant"
1396
+ ),
1397
+ children: [
1398
+ model.context_window.toLocaleString(),
1399
+ " tokens"
1400
+ ]
1401
+ }
1402
+ ) : null,
1403
+ model.capabilities?.map((capability) => /* @__PURE__ */ jsx(
1404
+ "span",
1405
+ {
1406
+ className: clsx(
1407
+ "inline-flex h-5 items-center rounded-small border border-outline-variant px-1.5 text-label-small",
1408
+ selected ? "text-on-secondary-container" : "text-on-surface-variant"
1409
+ ),
1410
+ children: capability
1411
+ },
1412
+ capability
1413
+ ))
1414
+ ] }) : null
1415
+ ] }),
1416
+ selected && /* @__PURE__ */ jsx(Icon, { name: "check", size: 20, className: "shrink-0" })
1417
+ ]
1418
+ },
1419
+ model.id
1420
+ );
1421
+ })
1422
+ ]
1423
+ },
1424
+ provider
1425
+ )),
1426
+ providers.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-3 text-center text-body-small text-on-surface-variant", children: "No models found" })
1427
+ ]
1428
+ }
1429
+ );
1430
+ }
1431
+ function ModelSearchField({
1432
+ value,
1433
+ onChange,
1434
+ placeholder
1435
+ }) {
1436
+ const inputRef = useRef(null);
1437
+ useEffect(() => {
1438
+ inputRef.current?.focus();
1439
+ }, []);
1440
+ return /* @__PURE__ */ jsx("div", { className: "px-2 pb-2", children: /* @__PURE__ */ jsxs("label", { className: "flex h-10 items-center gap-2 rounded-full bg-surface-container-high px-3 text-on-surface-variant focus-within:bg-surface-container-highest", children: [
1441
+ /* @__PURE__ */ jsx(Icon, { name: "search", size: 20, className: "shrink-0" }),
1442
+ /* @__PURE__ */ jsx(
1443
+ "input",
1444
+ {
1445
+ ref: inputRef,
1446
+ value,
1447
+ onChange: (event) => onChange(event.target.value),
1448
+ placeholder,
1449
+ "aria-label": placeholder,
1450
+ className: "min-w-0 flex-1 bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant"
1451
+ }
1452
+ )
1453
+ ] }) });
1454
+ }
1455
+ var ModelSelector = forwardRef(
1456
+ ({
1457
+ models,
1458
+ value,
1459
+ onChange,
1460
+ placeholder = "Select model...",
1461
+ searchPlaceholder = "Search models...",
1462
+ variant,
1463
+ size,
1464
+ popover: externalPopover,
1465
+ className,
1466
+ disabled
1467
+ }, ref) => {
1468
+ const internalPopover = usePopover();
1469
+ const popover = externalPopover ?? internalPopover;
1470
+ const [search, setSearch] = useState("");
1471
+ const selectedModel = models.find((m) => m.id === value);
1472
+ const grouped = useMemo(() => groupModelsByProvider(models, search), [models, search]);
1473
+ return /* @__PURE__ */ jsxs(
1474
+ Popover.Root,
1475
+ {
1476
+ open: popover.open,
1477
+ onOpenChange: (open) => {
1478
+ popover.setOpen(open);
1479
+ if (!open) setSearch("");
1480
+ },
1481
+ children: [
1482
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1483
+ SelectPill,
1484
+ {
1485
+ ref,
1486
+ variant,
1487
+ size,
1488
+ disabled,
1489
+ "aria-haspopup": "listbox",
1490
+ className: clsx("w-full", className),
1491
+ children: selectedModel?.name ?? placeholder
1492
+ }
1493
+ ) }),
1494
+ /* @__PURE__ */ jsxs(
1495
+ Popover.Content,
1496
+ {
1497
+ align: "end",
1498
+ sideOffset: 8,
1499
+ style: { width: 280 },
1500
+ className: "overflow-hidden rounded-extra-small! py-2",
1501
+ children: [
1502
+ /* @__PURE__ */ jsx(ModelSearchField, { value: search, onChange: setSearch, placeholder: searchPlaceholder }),
1503
+ /* @__PURE__ */ jsx("div", { className: "max-h-[320px] overflow-y-auto", children: /* @__PURE__ */ jsx(
1504
+ ModelOptionList,
1505
+ {
1506
+ grouped,
1507
+ value,
1508
+ onSelect: (id) => {
1509
+ onChange(id);
1510
+ popover.setOpen(false);
1511
+ setSearch("");
1512
+ }
1513
+ }
1514
+ ) })
1515
+ ]
1516
+ }
1517
+ )
1518
+ ]
1519
+ }
1520
+ );
1521
+ }
1522
+ );
1523
+ ModelSelector.displayName = "ModelSelector";
1524
+ var iconButtonEnter = {
1525
+ initial: { scale: 0.6, opacity: 0 },
1526
+ animate: {
1527
+ scale: 1,
1528
+ opacity: 1,
1529
+ transition: { duration: m3Durations.short4, ease: m3Easings.emphasizedDecelerate }
1530
+ },
1531
+ exit: {
1532
+ scale: 0.6,
1533
+ opacity: 0,
1534
+ transition: { duration: m3Durations.short3, ease: m3Easings.emphasizedAccelerate }
1535
+ }
1536
+ };
1537
+ function ChatComposer({
1538
+ draft,
1539
+ onDraftChange,
1540
+ onSend,
1541
+ placeholder = "Ask something...",
1542
+ heading,
1543
+ isSending = false,
1544
+ projectSelector,
1545
+ modelSelector,
1546
+ onOpenVoice,
1547
+ toolbarExtra,
1548
+ onStop,
1549
+ className
1550
+ }) {
1551
+ const canSend = !isSending && draft.trim().length > 0;
1552
+ const showStop = isSending && !!onStop;
1553
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1554
+ heading && /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2 px-1 text-title-small text-on-surface-variant", children: [
1555
+ /* @__PURE__ */ jsx(Icon, { name: "auto_awesome", size: 18, filled: true, className: "text-primary" }),
1556
+ heading
1557
+ ] }),
1558
+ /* @__PURE__ */ jsxs(
1559
+ "div",
1560
+ {
1561
+ className: clsx(
1562
+ "m3-chat-composer rounded-extra-large bg-surface-container-high px-4 pt-3 pb-2 text-on-surface",
1563
+ "transition-colors duration-200 ease-standard focus-within:bg-surface-container-highest",
1564
+ className
1565
+ ),
1566
+ children: [
1567
+ /* @__PURE__ */ jsx(
1568
+ "textarea",
1569
+ {
1570
+ value: draft,
1571
+ onChange: (event) => onDraftChange(event.target.value),
1572
+ onKeyDown: (event) => {
1573
+ if (event.key === "Enter" && !event.shiftKey) {
1574
+ event.preventDefault();
1575
+ onSend();
1576
+ }
1577
+ },
1578
+ placeholder,
1579
+ "aria-label": placeholder,
1580
+ rows: 2,
1581
+ className: "block w-full resize-none border-0 bg-transparent px-1 text-body-large text-on-surface caret-primary outline-none placeholder:text-on-surface-variant focus:outline-none focus:ring-0"
1582
+ }
1583
+ ),
1584
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [
1585
+ projectSelector ? /* @__PURE__ */ jsx("div", { className: "w-[210px]", children: /* @__PURE__ */ jsxs(
1586
+ Popover.Root,
1587
+ {
1588
+ open: projectSelector.popover.open,
1589
+ onOpenChange: projectSelector.popover.setOpen,
1590
+ children: [
1591
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(SelectPill, { className: "w-full", "aria-haspopup": "listbox", children: projectSelector.value }) }),
1592
+ /* @__PURE__ */ jsx(
1593
+ Popover.Content,
1594
+ {
1595
+ align: "start",
1596
+ sideOffset: 8,
1597
+ className: "min-w-[210px] overflow-hidden rounded-extra-small! py-2",
1598
+ children: /* @__PURE__ */ jsx(
1599
+ "div",
1600
+ {
1601
+ role: "listbox",
1602
+ "aria-label": "Projects",
1603
+ children: projectSelector.options.map((option) => {
1604
+ const selected = option === projectSelector.value;
1605
+ return /* @__PURE__ */ jsxs(
1606
+ "button",
1607
+ {
1608
+ type: "button",
1609
+ role: "option",
1610
+ "aria-selected": selected,
1611
+ onClick: () => {
1612
+ projectSelector.onChange(option);
1613
+ projectSelector.popover.setOpen(false);
1614
+ },
1615
+ className: clsx(
1616
+ "flex h-12 w-full items-center gap-3 px-3 text-left text-label-large",
1617
+ "transition-colors duration-200 ease-standard",
1618
+ selected ? "bg-secondary-container text-on-secondary-container" : "text-on-surface hover:bg-on-surface/8",
1619
+ focusRing,
1620
+ "focus-visible:outline-offset-[-3px]"
1621
+ ),
1622
+ children: [
1623
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: option }),
1624
+ selected && /* @__PURE__ */ jsx(Icon, { name: "check", size: 20 })
1625
+ ]
1626
+ },
1627
+ option
1628
+ );
1629
+ })
1630
+ }
1631
+ )
1632
+ }
1633
+ )
1634
+ ]
1635
+ }
1636
+ ) }) : /* @__PURE__ */ jsx("div", {}),
1637
+ /* @__PURE__ */ jsxs(motion.div, { layout: true, className: "flex items-center gap-1", children: [
1638
+ modelSelector && /* @__PURE__ */ jsx("div", { className: "w-[180px]", children: /* @__PURE__ */ jsx(
1639
+ ModelSelector,
1640
+ {
1641
+ models: modelSelector.options,
1642
+ value: modelSelector.value,
1643
+ onChange: modelSelector.onChange,
1644
+ size: "md"
1645
+ }
1646
+ ) }),
1647
+ toolbarExtra,
1648
+ onOpenVoice && /* @__PURE__ */ jsx(
1649
+ CircleButton,
1650
+ {
1651
+ icon: "mic",
1652
+ variant: "standard",
1653
+ onClick: onOpenVoice,
1654
+ disabled: isSending,
1655
+ "aria-label": "Open Voice Input"
1656
+ }
1657
+ ),
1658
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "popLayout", children: showStop ? /* @__PURE__ */ jsx(
1659
+ motion.button,
1660
+ {
1661
+ type: "button",
1662
+ ...iconButtonEnter,
1663
+ onClick: onStop,
1664
+ "aria-label": "Stop",
1665
+ className: clsx(
1666
+ "flex size-10 shrink-0 items-center justify-center rounded-full bg-primary text-on-primary",
1667
+ "transition-colors duration-200 ease-standard hover:bg-primary/92 active:bg-primary/90",
1668
+ focusRing
1669
+ ),
1670
+ children: /* @__PURE__ */ jsx(Icon, { name: "stop", size: 24, filled: true })
1671
+ },
1672
+ "stop"
1673
+ ) : canSend ? /* @__PURE__ */ jsx(
1674
+ motion.button,
1675
+ {
1676
+ type: "button",
1677
+ ...iconButtonEnter,
1678
+ onClick: onSend,
1679
+ "aria-label": "Send",
1680
+ className: clsx(
1681
+ "flex size-10 shrink-0 items-center justify-center rounded-full bg-primary text-on-primary",
1682
+ "transition-colors duration-200 ease-standard hover:bg-primary/92 active:bg-primary/90",
1683
+ focusRing
1684
+ ),
1685
+ children: /* @__PURE__ */ jsx(Icon, { name: "arrow_upward", size: 24 })
1686
+ },
1687
+ "send"
1688
+ ) : null })
1689
+ ] })
1690
+ ] })
1691
+ ]
1692
+ }
1693
+ )
1694
+ ] });
1695
+ }
1696
+ var config = {
1697
+ done: { icon: "check_circle", filled: true, color: "text-success" },
1698
+ in_progress: { icon: "clock_loader_40", filled: true, color: "text-primary" },
1699
+ ready: { icon: "radio_button_checked", filled: false, color: "text-primary" },
1700
+ pending_approval: { icon: "schedule", filled: false, color: "text-warning" },
1701
+ backlog: { icon: "radio_button_unchecked", filled: false, color: "text-outline" }
1702
+ };
1703
+ function TaskStatusIcon({ status, size = 16, className, label }) {
1704
+ const { icon, filled, color } = config[status];
1705
+ return /* @__PURE__ */ jsx(
1706
+ Icon,
1707
+ {
1708
+ name: icon,
1709
+ size,
1710
+ filled,
1711
+ label: label ?? TASK_STATUS_LABEL[status],
1712
+ "data-status": status,
1713
+ className: clsx(color, className)
1714
+ }
1715
+ );
1716
+ }
1717
+ var config2 = {
1718
+ critical: { icon: "priority_high", filled: true, color: "text-error" },
1719
+ high: { icon: "keyboard_double_arrow_up", filled: false, color: "text-warning" },
1720
+ medium: { icon: "drag_handle", filled: false, color: "text-primary" },
1721
+ low: { icon: "keyboard_arrow_down", filled: false, color: "text-outline" }
1722
+ };
1723
+ function TaskPriorityIcon({ priority, size = 14, className, label }) {
1724
+ const { icon, filled, color } = config2[priority];
1725
+ return /* @__PURE__ */ jsx(
1726
+ Icon,
1727
+ {
1728
+ name: icon,
1729
+ size,
1730
+ filled,
1731
+ weight: 600,
1732
+ label: label ?? `${TASK_PRIORITY_LABEL[priority]} priority`,
1733
+ "data-priority": priority,
1734
+ className: clsx(color, className)
1735
+ }
1736
+ );
1737
+ }
1738
+ var TASK_ROW_GRID = "20px 18px 72px 1fr 56px 96px 40px";
1739
+ var TASK_ROW_HEIGHT = 48;
1740
+ function TaskRow({
1741
+ task,
1742
+ onClick,
1743
+ isActive,
1744
+ resolveAgentName,
1745
+ onStatusChange,
1746
+ onDelete,
1747
+ className
1748
+ }) {
1749
+ const completedSubtasks = task.subtasks.filter((s) => s.completed).length;
1750
+ const assigneeName = resolveAgentName?.(task.assigned_agent_id) ?? task.assigned_agent_id;
1751
+ const hasActions = onStatusChange || onDelete;
1752
+ const isRowInteractive = typeof onClick === "function";
1753
+ const handleRowKeyDown = (e) => {
1754
+ if (e.repeat) return;
1755
+ if (e.key === "Enter") {
1756
+ e.preventDefault();
1757
+ onClick?.(task);
1758
+ }
1759
+ };
1760
+ const handleRowKeyUp = (e) => {
1761
+ if (e.repeat) return;
1762
+ if (e.key === " ") {
1763
+ e.preventDefault();
1764
+ onClick?.(task);
1765
+ }
1766
+ };
1767
+ const rowClassName = clsx(
1768
+ "task-row group grid w-full items-center border-b border-outline-variant text-left",
1769
+ "transition-colors duration-200 ease-standard",
1770
+ focusRing,
1771
+ "focus-visible:outline-offset-[-3px]",
1772
+ isActive ? "bg-secondary-container text-on-secondary-container" : "bg-transparent text-on-surface hover:bg-on-surface/8",
1773
+ isRowInteractive && !isActive && "active:bg-on-surface/10",
1774
+ className
1775
+ );
1776
+ const rowStyle = {
1777
+ gridTemplateColumns: TASK_ROW_GRID,
1778
+ minHeight: TASK_ROW_HEIGHT,
1779
+ paddingLeft: 16,
1780
+ paddingRight: 8,
1781
+ gap: 12
1782
+ };
1783
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
1784
+ /* @__PURE__ */ jsx(TaskStatusIcon, { status: task.status, size: 20 }),
1785
+ /* @__PURE__ */ jsx(TaskPriorityIcon, { priority: task.priority, size: 18 }),
1786
+ /* @__PURE__ */ jsxs(
1787
+ "span",
1788
+ {
1789
+ className: clsx(
1790
+ "truncate font-code text-body-small",
1791
+ isActive ? "text-on-secondary-container" : "text-on-surface-variant"
1792
+ ),
1793
+ children: [
1794
+ "SPC-",
1795
+ task.task_number
1796
+ ]
1797
+ }
1798
+ ),
1799
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-body-large", children: task.title }),
1800
+ /* @__PURE__ */ jsx(
1801
+ "span",
1802
+ {
1803
+ className: clsx(
1804
+ "text-right text-body-small",
1805
+ isActive ? "text-on-secondary-container" : "text-on-surface-variant"
1806
+ ),
1807
+ children: task.subtasks.length > 0 ? `${completedSubtasks}/${task.subtasks.length}` : ""
1808
+ }
1809
+ ),
1810
+ /* @__PURE__ */ jsx(
1811
+ "span",
1812
+ {
1813
+ className: clsx(
1814
+ "truncate text-right text-body-small",
1815
+ isActive ? "text-on-secondary-container" : "text-on-surface-variant"
1816
+ ),
1817
+ children: assigneeName
1818
+ }
1819
+ ),
1820
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center", children: hasActions ? /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
1821
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1822
+ "button",
1823
+ {
1824
+ type: "button",
1825
+ "aria-label": "Task Actions",
1826
+ onClick: (e) => e.stopPropagation(),
1827
+ onKeyDown: (e) => {
1828
+ if (e.key === "Enter" || e.key === " ") e.stopPropagation();
1829
+ },
1830
+ className: clsx(
1831
+ "flex size-8 items-center justify-center rounded-full text-on-surface-variant",
1832
+ "opacity-0 transition-[opacity,background-color] duration-200 ease-standard",
1833
+ "hover:bg-on-surface/8 active:bg-on-surface/10",
1834
+ "group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 data-popup-open:opacity-100",
1835
+ focusRing
1836
+ ),
1837
+ children: /* @__PURE__ */ jsx(Icon, { name: "more_vert", size: 20 })
1838
+ }
1839
+ ) }),
1840
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", sideOffset: 4, children: [
1841
+ onStatusChange && TASK_STATUS_ORDER.filter((s) => s !== task.status).map((s) => /* @__PURE__ */ jsxs(
1842
+ DropdownMenu.Item,
1843
+ {
1844
+ onClick: (e) => {
1845
+ e.stopPropagation();
1846
+ onStatusChange(task, s);
1847
+ },
1848
+ children: [
1849
+ /* @__PURE__ */ jsx(TaskStatusIcon, { status: s, size: 20, label: "" }),
1850
+ /* @__PURE__ */ jsx("span", { children: TASK_STATUS_LABEL[s] })
1851
+ ]
1852
+ },
1853
+ s
1854
+ )),
1855
+ onStatusChange && onDelete && /* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
1856
+ onDelete && /* @__PURE__ */ jsxs(
1857
+ DropdownMenu.Item,
1858
+ {
1859
+ onClick: (e) => {
1860
+ e.stopPropagation();
1861
+ onDelete(task);
1862
+ },
1863
+ className: "text-error",
1864
+ children: [
1865
+ /* @__PURE__ */ jsx(Icon, { name: "delete", size: 20 }),
1866
+ /* @__PURE__ */ jsx("span", { children: "Delete" })
1867
+ ]
1868
+ }
1869
+ )
1870
+ ] })
1871
+ ] }) : null })
1872
+ ] });
1873
+ if (!isRowInteractive) {
1874
+ return /* @__PURE__ */ jsx("div", { "aria-current": isActive ? "true" : void 0, className: rowClassName, style: rowStyle, children: content });
1875
+ }
1876
+ return /* @__PURE__ */ jsx(
1877
+ "div",
1878
+ {
1879
+ role: "button",
1880
+ tabIndex: 0,
1881
+ "aria-current": isActive ? "true" : void 0,
1882
+ onClick: () => onClick(task),
1883
+ onKeyDown: handleRowKeyDown,
1884
+ onKeyUp: handleRowKeyUp,
1885
+ className: rowClassName,
1886
+ style: rowStyle,
1887
+ children: content
1888
+ }
1889
+ );
1890
+ }
1891
+ var GRID_STYLE = {
1892
+ gridTemplateColumns: TASK_ROW_GRID,
1893
+ paddingLeft: 16,
1894
+ paddingRight: 8,
1895
+ gap: 12
1896
+ };
1897
+ function TaskList({
1898
+ tasks,
1899
+ groups = TASK_STATUS_ORDER,
1900
+ collapsedGroups,
1901
+ onToggleGroup,
1902
+ activeTaskId,
1903
+ onTaskClick,
1904
+ onStatusChange,
1905
+ onDelete,
1906
+ resolveAgentName,
1907
+ className
1908
+ }) {
1909
+ const grouped = useMemo(() => {
1910
+ const map = /* @__PURE__ */ new Map();
1911
+ for (const status of groups) map.set(status, []);
1912
+ for (const task of tasks) {
1913
+ const bucket = map.get(task.status);
1914
+ if (bucket) bucket.push(task);
1915
+ }
1916
+ return map;
1917
+ }, [tasks, groups]);
1918
+ return /* @__PURE__ */ jsxs("div", { className: clsx("flex flex-col bg-surface text-on-surface", className), children: [
1919
+ /* @__PURE__ */ jsxs(
1920
+ "div",
1921
+ {
1922
+ className: "grid items-center border-b border-outline-variant text-label-medium text-on-surface-variant",
1923
+ style: { ...GRID_STYLE, height: 32 },
1924
+ children: [
1925
+ /* @__PURE__ */ jsx("span", {}),
1926
+ /* @__PURE__ */ jsx("span", {}),
1927
+ /* @__PURE__ */ jsx("span", { children: "ID" }),
1928
+ /* @__PURE__ */ jsx("span", { children: "Title" }),
1929
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Subs" }),
1930
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Assignee" }),
1931
+ /* @__PURE__ */ jsx("span", {})
1932
+ ]
1933
+ }
1934
+ ),
1935
+ groups.map((status) => {
1936
+ const items = grouped.get(status) ?? [];
1937
+ const isOpen = !collapsedGroups?.has(status);
1938
+ return /* @__PURE__ */ jsxs(Collapsible, { open: isOpen, onOpenChange: () => onToggleGroup?.(status), children: [
1939
+ /* @__PURE__ */ jsxs(
1940
+ CollapsibleTrigger,
1941
+ {
1942
+ className: clsx(
1943
+ "grid w-full items-center rounded-none border-b border-outline-variant bg-surface-container-low",
1944
+ "text-title-small text-on-surface transition-colors duration-200 ease-standard",
1945
+ "hover:bg-on-surface/8 active:bg-on-surface/10",
1946
+ focusRing,
1947
+ "focus-visible:outline-offset-[-3px]"
1948
+ ),
1949
+ style: { ...GRID_STYLE, height: 40 },
1950
+ children: [
1951
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center text-on-surface-variant", children: /* @__PURE__ */ jsx(
1952
+ Icon,
1953
+ {
1954
+ name: "chevron_right",
1955
+ size: 20,
1956
+ className: clsx(
1957
+ "shrink-0 transition-transform duration-300 ease-emphasized",
1958
+ isOpen && "rotate-90"
1959
+ )
1960
+ }
1961
+ ) }),
1962
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(TaskStatusIcon, { status, size: 18, label: "" }) }),
1963
+ /* @__PURE__ */ jsxs("span", { className: "col-span-5 flex items-center gap-1.5 text-left", children: [
1964
+ TASK_STATUS_LABEL[status],
1965
+ /* @__PURE__ */ jsxs("span", { className: "text-body-small text-on-surface-variant", children: [
1966
+ "(",
1967
+ items.length,
1968
+ ")"
1969
+ ] })
1970
+ ] })
1971
+ ]
1972
+ }
1973
+ ),
1974
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: items.length > 0 ? items.map((task) => /* @__PURE__ */ jsx(
1975
+ TaskRow,
1976
+ {
1977
+ task,
1978
+ isActive: task.id === activeTaskId,
1979
+ onClick: onTaskClick,
1980
+ onStatusChange,
1981
+ onDelete,
1982
+ resolveAgentName
1983
+ },
1984
+ task.id
1985
+ )) : /* @__PURE__ */ jsx("div", { className: "border-b border-outline-variant px-4 py-3 text-body-small text-on-surface-variant", children: "No tasks" }) })
1986
+ ] }, status);
1987
+ })
1988
+ ] });
1989
+ }
1990
+ function formatDate(iso) {
1991
+ return new Date(iso).toLocaleDateString(void 0, {
1992
+ year: "numeric",
1993
+ month: "short",
1994
+ day: "numeric",
1995
+ hour: "2-digit",
1996
+ minute: "2-digit"
1997
+ });
1998
+ }
1999
+ function Property({ label, children }) {
2000
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
2001
+ /* @__PURE__ */ jsx("span", { className: "text-label-medium text-on-surface-variant", children: label }),
2002
+ /* @__PURE__ */ jsx("span", { className: "flex min-h-8 items-center gap-1.5 text-body-medium text-on-surface", children })
2003
+ ] });
2004
+ }
2005
+ function TaskDetail({
2006
+ task,
2007
+ resolveAgentName,
2008
+ onStatusChange,
2009
+ onSubtaskToggle,
2010
+ onDelete,
2011
+ onClose,
2012
+ className
2013
+ }) {
2014
+ const ownerName = resolveAgentName?.(task.owner_agent_id) ?? task.owner_agent_id;
2015
+ const assigneeName = resolveAgentName?.(task.assigned_agent_id) ?? task.assigned_agent_id;
2016
+ const completedCount = task.subtasks.filter((s) => s.completed).length;
2017
+ return /* @__PURE__ */ jsxs("div", { className: clsx("flex flex-col gap-6 bg-surface p-6 text-on-surface", className), children: [
2018
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
2019
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2020
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
2021
+ /* @__PURE__ */ jsx(TaskStatusIcon, { status: task.status, size: 20 }),
2022
+ /* @__PURE__ */ jsx(TaskPriorityIcon, { priority: task.priority, size: 18 }),
2023
+ /* @__PURE__ */ jsxs("span", { className: "font-code text-body-small text-on-surface-variant", children: [
2024
+ "SPC-",
2025
+ task.task_number
2026
+ ] })
2027
+ ] }),
2028
+ onClose && /* @__PURE__ */ jsx(CircleButton, { icon: "close", variant: "standard", onClick: onClose, "aria-label": "Close" })
2029
+ ] }),
2030
+ /* @__PURE__ */ jsx("h2", { className: "text-headline-small text-on-surface", children: task.title })
2031
+ ] }),
2032
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-6 gap-y-4 medium:grid-cols-4", children: [
2033
+ /* @__PURE__ */ jsx(Property, { label: "Status", children: onStatusChange ? /* @__PURE__ */ jsx(
2034
+ Select,
2035
+ {
2036
+ value: task.status,
2037
+ size: "md",
2038
+ onChange: (value) => onStatusChange(task, value),
2039
+ children: TASK_STATUS_ORDER.map((s) => /* @__PURE__ */ jsx(SelectOption, { value: s, children: TASK_STATUS_LABEL[s] }, s))
2040
+ }
2041
+ ) : TASK_STATUS_LABEL[task.status] }),
2042
+ /* @__PURE__ */ jsxs(Property, { label: "Priority", children: [
2043
+ /* @__PURE__ */ jsx(TaskPriorityIcon, { priority: task.priority, size: 18, label: "" }),
2044
+ TASK_PRIORITY_LABEL[task.priority]
2045
+ ] }),
2046
+ /* @__PURE__ */ jsx(Property, { label: "Owner", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: ownerName }) }),
2047
+ /* @__PURE__ */ jsx(Property, { label: "Assigned", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: assigneeName }) })
2048
+ ] }),
2049
+ task.subtasks.length > 0 && /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-2", children: [
2050
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2051
+ /* @__PURE__ */ jsx("h3", { className: "text-title-small text-on-surface", children: "Subtasks" }),
2052
+ /* @__PURE__ */ jsxs("span", { className: "text-body-small text-on-surface-variant", children: [
2053
+ completedCount,
2054
+ "/",
2055
+ task.subtasks.length
2056
+ ] })
2057
+ ] }),
2058
+ /* @__PURE__ */ jsx("ul", { className: "m-0 list-none overflow-hidden rounded-medium border border-outline-variant p-0", children: task.subtasks.map((subtask, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
2059
+ "button",
2060
+ {
2061
+ type: "button",
2062
+ role: "checkbox",
2063
+ "aria-checked": subtask.completed,
2064
+ onClick: () => onSubtaskToggle?.(task, i, !subtask.completed),
2065
+ className: clsx(
2066
+ "flex min-h-12 w-full items-center gap-4 px-4 py-2 text-left text-body-large",
2067
+ "transition-colors duration-200 ease-standard hover:bg-on-surface/8 active:bg-on-surface/10",
2068
+ focusRing,
2069
+ "focus-visible:outline-offset-[-3px]",
2070
+ i > 0 && "border-t border-outline-variant"
2071
+ ),
2072
+ children: [
2073
+ /* @__PURE__ */ jsx(
2074
+ "span",
2075
+ {
2076
+ className: clsx(
2077
+ "flex size-[18px] shrink-0 items-center justify-center rounded-[2px] transition-colors duration-200 ease-standard",
2078
+ subtask.completed ? "bg-primary text-on-primary" : "border-2 border-on-surface-variant"
2079
+ ),
2080
+ children: subtask.completed && /* @__PURE__ */ jsx(Icon, { name: "check", size: 16, weight: 700 })
2081
+ }
2082
+ ),
2083
+ /* @__PURE__ */ jsx(
2084
+ "span",
2085
+ {
2086
+ className: clsx(
2087
+ "min-w-0 flex-1",
2088
+ subtask.completed ? "text-on-surface-variant line-through" : "text-on-surface"
2089
+ ),
2090
+ children: subtask.title
2091
+ }
2092
+ )
2093
+ ]
2094
+ }
2095
+ ) }, subtask.title)) })
2096
+ ] }),
2097
+ /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-2", children: [
2098
+ /* @__PURE__ */ jsx("h3", { className: "text-title-small text-on-surface", children: "Description" }),
2099
+ task.description ? /* @__PURE__ */ jsx(Markdown, { content: task.description, size: "medium" }) : /* @__PURE__ */ jsx("p", { className: "text-body-medium text-on-surface-variant", children: "No description" })
2100
+ ] }),
2101
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-body-small text-on-surface-variant", children: [
2102
+ /* @__PURE__ */ jsxs("span", { children: [
2103
+ "Created ",
2104
+ formatDate(task.created_at)
2105
+ ] }),
2106
+ /* @__PURE__ */ jsxs("span", { children: [
2107
+ "Updated ",
2108
+ formatDate(task.updated_at)
2109
+ ] }),
2110
+ task.completed_at && /* @__PURE__ */ jsxs("span", { children: [
2111
+ "Completed ",
2112
+ formatDate(task.completed_at)
2113
+ ] })
2114
+ ] }),
2115
+ (onStatusChange || onDelete) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
2116
+ onStatusChange && task.status === "pending_approval" && /* @__PURE__ */ jsx(Button, { variant: "filled", size: "lg", onClick: () => onStatusChange(task, "ready"), children: "Approve" }),
2117
+ onStatusChange && task.status === "backlog" && /* @__PURE__ */ jsx(Button, { variant: "filled", size: "lg", onClick: () => onStatusChange(task, "in_progress"), children: "Execute" }),
2118
+ onStatusChange && (task.status === "in_progress" || task.status === "ready") && /* @__PURE__ */ jsx(Button, { variant: "filled", size: "lg", onClick: () => onStatusChange(task, "done"), children: "Mark Done" }),
2119
+ onStatusChange && task.status === "done" && /* @__PURE__ */ jsx(Button, { variant: "text", size: "lg", onClick: () => onStatusChange(task, "ready"), children: "Reopen" }),
2120
+ onDelete && /* @__PURE__ */ jsxs(
2121
+ "button",
2122
+ {
2123
+ type: "button",
2124
+ onClick: () => onDelete(task),
2125
+ className: clsx(
2126
+ "inline-flex h-10 items-center justify-center gap-2 rounded-full px-3 text-label-large text-error",
2127
+ "transition-colors duration-200 ease-standard hover:bg-error/8 active:bg-error/10",
2128
+ focusRing
2129
+ ),
2130
+ children: [
2131
+ /* @__PURE__ */ jsx(Icon, { name: "delete", size: 18 }),
2132
+ "Delete"
2133
+ ]
2134
+ }
2135
+ )
2136
+ ] })
2137
+ ] });
2138
+ }
2139
+ var PRIORITIES = ["critical", "high", "medium", "low"];
2140
+ function TaskCreateForm({
2141
+ onSubmit,
2142
+ onCancel,
2143
+ defaultPriority = "medium",
2144
+ isSubmitting,
2145
+ className
2146
+ }) {
2147
+ const [title, setTitle] = useState("");
2148
+ const [description, setDescription] = useState("");
2149
+ const [priority, setPriority] = useState(defaultPriority);
2150
+ const titleInputId = useId();
2151
+ const descriptionInputId = useId();
2152
+ const titleInputRef = useRef(null);
2153
+ useEffect(() => {
2154
+ titleInputRef.current?.focus();
2155
+ }, []);
2156
+ const canSubmit = title.trim().length > 0 && !isSubmitting;
2157
+ const handleSubmit = useCallback(() => {
2158
+ if (!canSubmit) return;
2159
+ onSubmit({ title: title.trim(), description: description.trim(), priority });
2160
+ setTitle("");
2161
+ setDescription("");
2162
+ setPriority(defaultPriority);
2163
+ }, [canSubmit, onSubmit, title, description, priority, defaultPriority]);
2164
+ return /* @__PURE__ */ jsxs(
2165
+ "div",
2166
+ {
2167
+ className: clsx(
2168
+ "flex flex-col gap-3 rounded-medium bg-surface-container-low p-4 text-on-surface",
2169
+ className
2170
+ ),
2171
+ children: [
2172
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2173
+ /* @__PURE__ */ jsx("label", { htmlFor: titleInputId, className: "sr-only", children: "Task Title" }),
2174
+ /* @__PURE__ */ jsx(
2175
+ Input,
2176
+ {
2177
+ ref: titleInputRef,
2178
+ id: titleInputId,
2179
+ type: "text",
2180
+ size: "lg",
2181
+ value: title,
2182
+ onChange: (e) => setTitle(e.target.value),
2183
+ onKeyDown: (e) => {
2184
+ if (e.key === "Enter") handleSubmit();
2185
+ if (e.key === "Escape") onCancel?.();
2186
+ },
2187
+ placeholder: "Task title...",
2188
+ className: "min-w-0 flex-1",
2189
+ disabled: isSubmitting
2190
+ }
2191
+ ),
2192
+ /* @__PURE__ */ jsx(
2193
+ Select,
2194
+ {
2195
+ value: priority,
2196
+ size: "lg",
2197
+ onChange: (value) => setPriority(value),
2198
+ disabled: isSubmitting,
2199
+ children: PRIORITIES.map((p) => /* @__PURE__ */ jsx(SelectOption, { value: p, children: TASK_PRIORITY_LABEL[p] }, p))
2200
+ }
2201
+ )
2202
+ ] }),
2203
+ /* @__PURE__ */ jsx("label", { htmlFor: descriptionInputId, className: "sr-only", children: "Description" }),
2204
+ /* @__PURE__ */ jsx(
2205
+ TextArea,
2206
+ {
2207
+ id: descriptionInputId,
2208
+ value: description,
2209
+ onChange: (e) => setDescription(e.target.value),
2210
+ placeholder: "Description (optional)",
2211
+ rows: 2,
2212
+ className: "w-full resize-none",
2213
+ disabled: isSubmitting
2214
+ }
2215
+ ),
2216
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
2217
+ onCancel && /* @__PURE__ */ jsx(Button, { variant: "text", size: "lg", onClick: onCancel, disabled: isSubmitting, children: "Cancel" }),
2218
+ /* @__PURE__ */ jsx(Button, { variant: "filled", size: "lg", disabled: !canSubmit, onClick: handleSubmit, children: "Create" })
2219
+ ] })
2220
+ ]
2221
+ }
2222
+ );
2223
+ }
2224
+
2225
+ export { ChatComposer, InlineBranchCard, InlineWorkerCard, Markdown, MessageBubble, ModelOptionList, ModelSelector, TASK_PRIORITY_LABEL, TASK_STATUS_LABEL, TASK_STATUS_ORDER, TaskCreateForm, TaskDetail, TaskList, TaskPriorityIcon, TaskRow, TaskStatusIcon, ToolCall, groupModelsByProvider, isErrorResult, pairTranscriptSteps, tryParseJson };
2226
+ //# sourceMappingURL=index.js.map
2227
+ //# sourceMappingURL=index.js.map