@contractspec/module.ai-chat 4.1.5 → 4.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.
Files changed (30) hide show
  1. package/README.md +29 -3
  2. package/dist/browser/core/index.js +53 -17
  3. package/dist/browser/index.js +982 -426
  4. package/dist/browser/presentation/components/index.js +975 -419
  5. package/dist/browser/presentation/hooks/index.js +8 -1
  6. package/dist/browser/presentation/index.js +983 -427
  7. package/dist/core/create-chat-route.d.ts +14 -3
  8. package/dist/core/index.js +53 -17
  9. package/dist/core/message-types.d.ts +4 -0
  10. package/dist/index.js +982 -426
  11. package/dist/node/core/index.js +53 -17
  12. package/dist/node/index.js +982 -426
  13. package/dist/node/presentation/components/index.js +975 -419
  14. package/dist/node/presentation/hooks/index.js +8 -1
  15. package/dist/node/presentation/index.js +983 -427
  16. package/dist/presentation/components/ChainOfThought.d.ts +30 -0
  17. package/dist/presentation/components/ChatInput.d.ts +3 -4
  18. package/dist/presentation/components/ChatMessage.d.ts +6 -4
  19. package/dist/presentation/components/ChatWithExport.d.ts +14 -1
  20. package/dist/presentation/components/ChatWithSidebar.d.ts +12 -1
  21. package/dist/presentation/components/Reasoning.d.ts +24 -0
  22. package/dist/presentation/components/Sources.d.ts +22 -0
  23. package/dist/presentation/components/Suggestion.d.ts +12 -0
  24. package/dist/presentation/components/ToolResultRenderer.d.ts +20 -3
  25. package/dist/presentation/components/component-types.d.ts +52 -0
  26. package/dist/presentation/components/index.d.ts +6 -1
  27. package/dist/presentation/components/index.js +975 -419
  28. package/dist/presentation/hooks/index.js +8 -1
  29. package/dist/presentation/index.js +983 -427
  30. package/package.json +15 -15
@@ -80,8 +80,8 @@ function ChatContainer({
80
80
  });
81
81
  }
82
82
  // src/presentation/components/ChatMessage.tsx
83
- import * as React3 from "react";
84
- import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
83
+ import * as React4 from "react";
84
+ import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
85
85
  import { Avatar, AvatarFallback } from "@contractspec/lib.ui-kit-web/ui/avatar";
86
86
  import { Skeleton } from "@contractspec/lib.ui-kit-web/ui/skeleton";
87
87
  import {
@@ -90,7 +90,6 @@ import {
90
90
  AlertCircle,
91
91
  Copy as Copy2,
92
92
  Check as Check2,
93
- ExternalLink,
94
93
  Wrench,
95
94
  Pencil,
96
95
  X
@@ -244,22 +243,98 @@ function CodePreview({
244
243
  // src/presentation/components/ToolResultRenderer.tsx
245
244
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
246
245
  "use client";
246
+ function isUIMessageLike(result) {
247
+ return typeof result === "object" && result !== null && "parts" in result && Array.isArray(result.parts);
248
+ }
247
249
  function isPresentationToolResult(result) {
248
250
  return typeof result === "object" && result !== null && "presentationKey" in result && typeof result.presentationKey === "string";
249
251
  }
250
252
  function isFormToolResult(result) {
251
253
  return typeof result === "object" && result !== null && "formKey" in result && typeof result.formKey === "string";
252
254
  }
255
+ function isDataViewToolResult(result) {
256
+ return typeof result === "object" && result !== null && "dataViewKey" in result && typeof result.dataViewKey === "string";
257
+ }
258
+ function UIMessagePartRenderer({
259
+ part,
260
+ presentationRenderer,
261
+ formRenderer,
262
+ dataViewRenderer,
263
+ depth = 0
264
+ }) {
265
+ if (part === null || part === undefined)
266
+ return null;
267
+ const p = part;
268
+ if (p.type === "text" && typeof p.text === "string") {
269
+ return /* @__PURE__ */ jsx3("p", {
270
+ className: "text-sm whitespace-pre-wrap",
271
+ children: p.text
272
+ }, depth);
273
+ }
274
+ if (p.type && String(p.type).startsWith("tool-") && p.output) {
275
+ const output = p.output;
276
+ if (isUIMessageLike(output)) {
277
+ return /* @__PURE__ */ jsx3("div", {
278
+ className: "border-border ml-2 border-l-2 pl-2",
279
+ children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
280
+ parts: output.parts ?? [],
281
+ presentationRenderer,
282
+ formRenderer,
283
+ dataViewRenderer,
284
+ depth: depth + 1
285
+ })
286
+ }, depth);
287
+ }
288
+ return /* @__PURE__ */ jsx3("pre", {
289
+ className: "bg-background overflow-x-auto rounded p-2 text-xs",
290
+ children: typeof output === "object" ? JSON.stringify(output, null, 2) : String(output)
291
+ }, depth);
292
+ }
293
+ return null;
294
+ }
295
+ function UIMessagePartsRenderer({
296
+ parts,
297
+ presentationRenderer,
298
+ formRenderer,
299
+ dataViewRenderer,
300
+ depth = 0
301
+ }) {
302
+ if (parts.length === 0)
303
+ return null;
304
+ return /* @__PURE__ */ jsx3("div", {
305
+ className: "space-y-2",
306
+ children: parts.map((part, i) => /* @__PURE__ */ jsx3(UIMessagePartRenderer, {
307
+ part,
308
+ presentationRenderer,
309
+ formRenderer,
310
+ dataViewRenderer,
311
+ depth
312
+ }, `${depth}-${i}`))
313
+ });
314
+ }
253
315
  function ToolResultRenderer({
254
- toolName,
316
+ toolName: _toolName,
255
317
  result,
256
318
  presentationRenderer,
257
319
  formRenderer,
258
- showRawFallback = true
320
+ dataViewRenderer,
321
+ showRawFallback = true,
322
+ renderNestedUIMessage = true
259
323
  }) {
260
324
  if (result === undefined || result === null) {
261
325
  return null;
262
326
  }
327
+ if (renderNestedUIMessage && isUIMessageLike(result) && (result.parts?.length ?? 0) > 0) {
328
+ return /* @__PURE__ */ jsx3("div", {
329
+ className: "border-border bg-background/50 mt-2 rounded-md border p-3",
330
+ children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
331
+ parts: result.parts ?? [],
332
+ presentationRenderer,
333
+ formRenderer,
334
+ dataViewRenderer
335
+ })
336
+ });
337
+ }
263
338
  if (isPresentationToolResult(result) && presentationRenderer) {
264
339
  const rendered = presentationRenderer(result.presentationKey, result.data);
265
340
  if (rendered != null) {
@@ -278,6 +353,15 @@ function ToolResultRenderer({
278
353
  });
279
354
  }
280
355
  }
356
+ if (isDataViewToolResult(result) && dataViewRenderer) {
357
+ const rendered = dataViewRenderer(result.dataViewKey, result.items);
358
+ if (rendered != null) {
359
+ return /* @__PURE__ */ jsx3("div", {
360
+ className: "border-border bg-background/50 mt-2 rounded-md border p-3",
361
+ children: rendered
362
+ });
363
+ }
364
+ }
281
365
  if (!showRawFallback) {
282
366
  return null;
283
367
  }
@@ -295,8 +379,158 @@ function ToolResultRenderer({
295
379
  });
296
380
  }
297
381
 
382
+ // src/presentation/components/Reasoning.tsx
383
+ import * as React3 from "react";
384
+ import {
385
+ Collapsible,
386
+ CollapsibleContent,
387
+ CollapsibleTrigger
388
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
389
+ import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
390
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
391
+ "use client";
392
+ function Reasoning({
393
+ isStreaming = false,
394
+ open,
395
+ defaultOpen = false,
396
+ onOpenChange,
397
+ children,
398
+ className
399
+ }) {
400
+ const [internalOpen, setInternalOpen] = React3.useState(defaultOpen);
401
+ const prevStreamingRef = React3.useRef(isStreaming);
402
+ const isControlled = open !== undefined;
403
+ React3.useEffect(() => {
404
+ if (isStreaming) {
405
+ if (isControlled) {
406
+ onOpenChange?.(true);
407
+ } else {
408
+ setInternalOpen(true);
409
+ }
410
+ } else if (prevStreamingRef.current) {
411
+ if (isControlled) {
412
+ onOpenChange?.(false);
413
+ } else {
414
+ setInternalOpen(false);
415
+ }
416
+ }
417
+ prevStreamingRef.current = isStreaming;
418
+ }, [isStreaming, isControlled, onOpenChange]);
419
+ const handleOpenChange = React3.useCallback((next) => {
420
+ if (isControlled) {
421
+ onOpenChange?.(next);
422
+ } else {
423
+ setInternalOpen(next);
424
+ }
425
+ }, [isControlled, onOpenChange]);
426
+ return /* @__PURE__ */ jsx4(Collapsible, {
427
+ open: isControlled ? open : internalOpen,
428
+ onOpenChange: handleOpenChange,
429
+ className: cn3("w-full", className),
430
+ children
431
+ });
432
+ }
433
+ function ReasoningTrigger({
434
+ children,
435
+ isStreaming = false,
436
+ className
437
+ }) {
438
+ return /* @__PURE__ */ jsxs4(CollapsibleTrigger, {
439
+ className: cn3("text-muted-foreground hover:bg-muted hover:text-foreground flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors", className),
440
+ children: [
441
+ isStreaming && /* @__PURE__ */ jsx4("span", {
442
+ className: "bg-primary h-1.5 w-1.5 animate-pulse rounded-full",
443
+ "aria-hidden": true
444
+ }),
445
+ children ?? (isStreaming ? "Thinking..." : "View reasoning")
446
+ ]
447
+ });
448
+ }
449
+ function ReasoningContent({
450
+ children,
451
+ className
452
+ }) {
453
+ return /* @__PURE__ */ jsx4(CollapsibleContent, {
454
+ children: /* @__PURE__ */ jsx4("div", {
455
+ className: cn3("text-muted-foreground bg-muted mt-1 rounded-md p-2 text-sm", className),
456
+ children: /* @__PURE__ */ jsx4("p", {
457
+ className: "whitespace-pre-wrap",
458
+ children
459
+ })
460
+ })
461
+ });
462
+ }
463
+
464
+ // src/presentation/components/Sources.tsx
465
+ import {
466
+ Collapsible as Collapsible2,
467
+ CollapsibleContent as CollapsibleContent2,
468
+ CollapsibleTrigger as CollapsibleTrigger2
469
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
470
+ import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
471
+ import { ExternalLink } from "lucide-react";
472
+ import { jsx as jsx5, jsxs as jsxs5, Fragment } from "react/jsx-runtime";
473
+ "use client";
474
+ function Sources({ children, className }) {
475
+ return /* @__PURE__ */ jsx5(Collapsible2, {
476
+ className: cn4("mt-2", className),
477
+ defaultOpen: false,
478
+ children
479
+ });
480
+ }
481
+ function SourcesTrigger({
482
+ count,
483
+ children,
484
+ className
485
+ }) {
486
+ return /* @__PURE__ */ jsx5(CollapsibleTrigger2, {
487
+ className: cn4("text-muted-foreground hover:text-foreground hover:bg-muted inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-xs transition-colors", className),
488
+ children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
489
+ children: [
490
+ /* @__PURE__ */ jsx5(ExternalLink, {
491
+ className: "h-3 w-3"
492
+ }),
493
+ count,
494
+ " source",
495
+ count !== 1 ? "s" : ""
496
+ ]
497
+ })
498
+ });
499
+ }
500
+ function SourcesContent({ children, className }) {
501
+ return /* @__PURE__ */ jsx5(CollapsibleContent2, {
502
+ children: /* @__PURE__ */ jsx5("div", {
503
+ className: cn4("mt-2 flex flex-wrap gap-2", className),
504
+ children
505
+ })
506
+ });
507
+ }
508
+ function Source({
509
+ href,
510
+ title,
511
+ className,
512
+ children,
513
+ ...props
514
+ }) {
515
+ return /* @__PURE__ */ jsx5("a", {
516
+ href,
517
+ target: "_blank",
518
+ rel: "noopener noreferrer",
519
+ className: cn4("text-muted-foreground hover:text-foreground bg-muted inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs transition-colors", className),
520
+ ...props,
521
+ children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
522
+ children: [
523
+ /* @__PURE__ */ jsx5(ExternalLink, {
524
+ className: "h-3 w-3"
525
+ }),
526
+ title ?? href
527
+ ]
528
+ })
529
+ });
530
+ }
531
+
298
532
  // src/presentation/components/ChatMessage.tsx
299
- import { jsx as jsx4, jsxs as jsxs4, Fragment } from "react/jsx-runtime";
533
+ import { jsx as jsx6, jsxs as jsxs6, Fragment as Fragment2 } from "react/jsx-runtime";
300
534
  "use client";
301
535
  function extractCodeBlocks(content) {
302
536
  const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
@@ -319,11 +553,11 @@ function renderInlineMarkdown(text) {
319
553
  let key = 0;
320
554
  while ((match = linkRegex.exec(text)) !== null) {
321
555
  if (match.index > lastIndex) {
322
- parts.push(/* @__PURE__ */ jsx4("span", {
556
+ parts.push(/* @__PURE__ */ jsx6("span", {
323
557
  children: text.slice(lastIndex, match.index)
324
558
  }, key++));
325
559
  }
326
- parts.push(/* @__PURE__ */ jsx4("a", {
560
+ parts.push(/* @__PURE__ */ jsx6("a", {
327
561
  href: match[2],
328
562
  target: "_blank",
329
563
  rel: "noopener noreferrer",
@@ -333,7 +567,7 @@ function renderInlineMarkdown(text) {
333
567
  lastIndex = match.index + match[0].length;
334
568
  }
335
569
  if (lastIndex < text.length) {
336
- parts.push(/* @__PURE__ */ jsx4("span", {
570
+ parts.push(/* @__PURE__ */ jsx6("span", {
337
571
  children: text.slice(lastIndex)
338
572
  }, key++));
339
573
  }
@@ -342,7 +576,7 @@ function renderInlineMarkdown(text) {
342
576
  function MessageContent({ content }) {
343
577
  const codeBlocks = extractCodeBlocks(content);
344
578
  if (codeBlocks.length === 0) {
345
- return /* @__PURE__ */ jsx4("p", {
579
+ return /* @__PURE__ */ jsx6("p", {
346
580
  className: "whitespace-pre-wrap",
347
581
  children: renderInlineMarkdown(content)
348
582
  });
@@ -353,12 +587,12 @@ function MessageContent({ content }) {
353
587
  for (const block of codeBlocks) {
354
588
  const [before, after] = remaining.split(block.raw);
355
589
  if (before) {
356
- parts.push(/* @__PURE__ */ jsx4("p", {
590
+ parts.push(/* @__PURE__ */ jsx6("p", {
357
591
  className: "whitespace-pre-wrap",
358
592
  children: renderInlineMarkdown(before.trim())
359
593
  }, key++));
360
594
  }
361
- parts.push(/* @__PURE__ */ jsx4(CodePreview, {
595
+ parts.push(/* @__PURE__ */ jsx6(CodePreview, {
362
596
  code: block.code,
363
597
  language: block.language,
364
598
  className: "my-2"
@@ -366,15 +600,22 @@ function MessageContent({ content }) {
366
600
  remaining = after ?? "";
367
601
  }
368
602
  if (remaining.trim()) {
369
- parts.push(/* @__PURE__ */ jsx4("p", {
603
+ parts.push(/* @__PURE__ */ jsx6("p", {
370
604
  className: "whitespace-pre-wrap",
371
605
  children: renderInlineMarkdown(remaining.trim())
372
606
  }, key++));
373
607
  }
374
- return /* @__PURE__ */ jsx4(Fragment, {
608
+ return /* @__PURE__ */ jsx6(Fragment2, {
375
609
  children: parts
376
610
  });
377
611
  }
612
+ function toolStatusToCotStatus(status) {
613
+ if (status === "completed")
614
+ return "complete";
615
+ if (status === "running")
616
+ return "active";
617
+ return "pending";
618
+ }
378
619
  function ChatMessage({
379
620
  message,
380
621
  className,
@@ -386,119 +627,128 @@ function ChatMessage({
386
627
  editable = false,
387
628
  onEdit,
388
629
  presentationRenderer,
389
- formRenderer
630
+ formRenderer,
631
+ dataViewRenderer,
632
+ components: comps
390
633
  }) {
391
- const [copied, setCopied] = React3.useState(false);
634
+ const [copied, setCopied] = React4.useState(false);
392
635
  const isUser = message.role === "user";
393
636
  const isError = message.status === "error";
394
637
  const isStreaming = message.status === "streaming";
395
- const handleCopy = React3.useCallback(async () => {
638
+ const handleCopy = React4.useCallback(async () => {
396
639
  await navigator.clipboard.writeText(message.content);
397
640
  setCopied(true);
398
641
  setTimeout(() => setCopied(false), 2000);
399
642
  }, [message.content]);
400
- const handleSelectChange = React3.useCallback((checked) => {
643
+ const handleSelectChange = React4.useCallback((checked) => {
401
644
  if (checked !== "indeterminate")
402
645
  onSelect?.(message.id);
403
646
  }, [message.id, onSelect]);
404
- const [isEditing, setIsEditing] = React3.useState(false);
405
- const [editContent, setEditContent] = React3.useState(message.content);
406
- React3.useEffect(() => {
647
+ const [isEditing, setIsEditing] = React4.useState(false);
648
+ const [editContent, setEditContent] = React4.useState(message.content);
649
+ const editTextareaRef = React4.useRef(null);
650
+ React4.useEffect(() => {
407
651
  setEditContent(message.content);
408
652
  }, [message.content]);
409
- const handleStartEdit = React3.useCallback(() => {
653
+ React4.useEffect(() => {
654
+ if (isEditing) {
655
+ editTextareaRef.current?.focus();
656
+ }
657
+ }, [isEditing]);
658
+ const handleStartEdit = React4.useCallback(() => {
410
659
  setEditContent(message.content);
411
660
  setIsEditing(true);
412
661
  }, [message.content]);
413
- const handleSaveEdit = React3.useCallback(async () => {
662
+ const handleSaveEdit = React4.useCallback(async () => {
414
663
  const trimmed = editContent.trim();
415
664
  if (trimmed !== message.content) {
416
665
  await onEdit?.(message.id, trimmed);
417
666
  }
418
667
  setIsEditing(false);
419
668
  }, [editContent, message.id, message.content, onEdit]);
420
- const handleCancelEdit = React3.useCallback(() => {
669
+ const handleCancelEdit = React4.useCallback(() => {
421
670
  setEditContent(message.content);
422
671
  setIsEditing(false);
423
672
  }, [message.content]);
424
- return /* @__PURE__ */ jsxs4("div", {
425
- className: cn3("group flex gap-3", isUser && "flex-row-reverse", className),
673
+ return /* @__PURE__ */ jsxs6("div", {
674
+ className: cn5("group flex gap-3", isUser && "flex-row-reverse", className),
426
675
  children: [
427
- selectable && /* @__PURE__ */ jsx4("div", {
428
- className: cn3("flex shrink-0 items-start pt-1", "opacity-0 transition-opacity group-hover:opacity-100"),
429
- children: /* @__PURE__ */ jsx4(Checkbox, {
676
+ selectable && /* @__PURE__ */ jsx6("div", {
677
+ className: cn5("flex shrink-0 items-start pt-1", "opacity-0 transition-opacity group-hover:opacity-100"),
678
+ children: /* @__PURE__ */ jsx6(Checkbox, {
430
679
  checked: selected,
431
680
  onCheckedChange: handleSelectChange,
432
681
  "aria-label": selected ? "Deselect message" : "Select message"
433
682
  })
434
683
  }),
435
- showAvatar && /* @__PURE__ */ jsx4(Avatar, {
684
+ showAvatar && /* @__PURE__ */ jsx6(Avatar, {
436
685
  className: "h-8 w-8 shrink-0",
437
- children: /* @__PURE__ */ jsx4(AvatarFallback, {
438
- className: cn3(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
439
- children: isUser ? /* @__PURE__ */ jsx4(User, {
686
+ children: /* @__PURE__ */ jsx6(AvatarFallback, {
687
+ className: cn5(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
688
+ children: isUser ? /* @__PURE__ */ jsx6(User, {
440
689
  className: "h-4 w-4"
441
- }) : /* @__PURE__ */ jsx4(Bot, {
690
+ }) : /* @__PURE__ */ jsx6(Bot, {
442
691
  className: "h-4 w-4"
443
692
  })
444
693
  })
445
694
  }),
446
- /* @__PURE__ */ jsxs4("div", {
447
- className: cn3("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
695
+ /* @__PURE__ */ jsxs6("div", {
696
+ className: cn5("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
448
697
  children: [
449
- /* @__PURE__ */ jsx4("div", {
450
- className: cn3("rounded-2xl px-4 py-2", isUser ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", isError && "border-destructive bg-destructive/10 border"),
451
- children: isError && message.error ? /* @__PURE__ */ jsxs4("div", {
698
+ /* @__PURE__ */ jsx6("div", {
699
+ className: cn5("rounded-2xl px-4 py-2", isUser ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", isError && "border-destructive bg-destructive/10 border"),
700
+ children: isError && message.error ? /* @__PURE__ */ jsxs6("div", {
452
701
  className: "flex items-start gap-2",
453
702
  children: [
454
- /* @__PURE__ */ jsx4(AlertCircle, {
703
+ /* @__PURE__ */ jsx6(AlertCircle, {
455
704
  className: "text-destructive mt-0.5 h-4 w-4 shrink-0"
456
705
  }),
457
- /* @__PURE__ */ jsxs4("div", {
706
+ /* @__PURE__ */ jsxs6("div", {
458
707
  children: [
459
- /* @__PURE__ */ jsx4("p", {
708
+ /* @__PURE__ */ jsx6("p", {
460
709
  className: "text-destructive font-medium",
461
710
  children: message.error.code
462
711
  }),
463
- /* @__PURE__ */ jsx4("p", {
712
+ /* @__PURE__ */ jsx6("p", {
464
713
  className: "text-muted-foreground text-sm",
465
714
  children: message.error.message
466
715
  })
467
716
  ]
468
717
  })
469
718
  ]
470
- }) : isEditing ? /* @__PURE__ */ jsxs4("div", {
719
+ }) : isEditing ? /* @__PURE__ */ jsxs6("div", {
471
720
  className: "flex flex-col gap-2",
472
721
  children: [
473
- /* @__PURE__ */ jsx4("textarea", {
722
+ /* @__PURE__ */ jsx6("textarea", {
723
+ ref: editTextareaRef,
474
724
  value: editContent,
475
725
  onChange: (e) => setEditContent(e.target.value),
476
726
  className: "bg-background/50 min-h-[80px] w-full resize-y rounded-md border px-3 py-2 text-sm",
477
727
  rows: 4,
478
- autoFocus: true
728
+ "aria-label": "Edit message"
479
729
  }),
480
- /* @__PURE__ */ jsxs4("div", {
730
+ /* @__PURE__ */ jsxs6("div", {
481
731
  className: "flex gap-2",
482
732
  children: [
483
- /* @__PURE__ */ jsxs4(Button2, {
733
+ /* @__PURE__ */ jsxs6(Button2, {
484
734
  variant: "default",
485
735
  size: "sm",
486
736
  onPress: handleSaveEdit,
487
737
  "aria-label": "Save edit",
488
738
  children: [
489
- /* @__PURE__ */ jsx4(Check2, {
739
+ /* @__PURE__ */ jsx6(Check2, {
490
740
  className: "h-3 w-3"
491
741
  }),
492
742
  "Save"
493
743
  ]
494
744
  }),
495
- /* @__PURE__ */ jsxs4(Button2, {
745
+ /* @__PURE__ */ jsxs6(Button2, {
496
746
  variant: "ghost",
497
747
  size: "sm",
498
748
  onPress: handleCancelEdit,
499
749
  "aria-label": "Cancel edit",
500
750
  children: [
501
- /* @__PURE__ */ jsx4(X, {
751
+ /* @__PURE__ */ jsx6(X, {
502
752
  className: "h-3 w-3"
503
753
  }),
504
754
  "Cancel"
@@ -507,153 +757,256 @@ function ChatMessage({
507
757
  ]
508
758
  })
509
759
  ]
510
- }) : isStreaming && !message.content ? /* @__PURE__ */ jsxs4("div", {
760
+ }) : isStreaming && !message.content ? /* @__PURE__ */ jsxs6("div", {
511
761
  className: "flex flex-col gap-2",
512
762
  children: [
513
- /* @__PURE__ */ jsx4(Skeleton, {
763
+ /* @__PURE__ */ jsx6(Skeleton, {
514
764
  className: "h-4 w-48"
515
765
  }),
516
- /* @__PURE__ */ jsx4(Skeleton, {
766
+ /* @__PURE__ */ jsx6(Skeleton, {
517
767
  className: "h-4 w-32"
518
768
  })
519
769
  ]
520
- }) : /* @__PURE__ */ jsx4(MessageContent, {
770
+ }) : /* @__PURE__ */ jsx6(MessageContent, {
521
771
  content: message.content
522
772
  })
523
773
  }),
524
- /* @__PURE__ */ jsxs4("div", {
525
- className: cn3("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
774
+ /* @__PURE__ */ jsxs6("div", {
775
+ className: cn5("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
526
776
  children: [
527
- /* @__PURE__ */ jsx4("span", {
777
+ /* @__PURE__ */ jsx6("span", {
528
778
  children: new Date(message.createdAt).toLocaleTimeString([], {
529
779
  hour: "2-digit",
530
780
  minute: "2-digit"
531
781
  })
532
782
  }),
533
- message.usage && /* @__PURE__ */ jsxs4("span", {
783
+ message.usage && /* @__PURE__ */ jsxs6("span", {
534
784
  children: [
535
785
  message.usage.inputTokens + message.usage.outputTokens,
536
786
  " tokens"
537
787
  ]
538
788
  }),
539
- showCopy && !isUser && message.content && /* @__PURE__ */ jsx4(Button2, {
789
+ showCopy && !isUser && message.content && /* @__PURE__ */ jsx6(Button2, {
540
790
  variant: "ghost",
541
791
  size: "sm",
542
792
  className: "h-6 w-6 p-0",
543
793
  onPress: handleCopy,
544
794
  "aria-label": copied ? "Copied" : "Copy message",
545
- children: copied ? /* @__PURE__ */ jsx4(Check2, {
795
+ children: copied ? /* @__PURE__ */ jsx6(Check2, {
546
796
  className: "h-3 w-3"
547
- }) : /* @__PURE__ */ jsx4(Copy2, {
797
+ }) : /* @__PURE__ */ jsx6(Copy2, {
548
798
  className: "h-3 w-3"
549
799
  })
550
800
  }),
551
- editable && isUser && !isEditing && /* @__PURE__ */ jsx4(Button2, {
801
+ editable && isUser && !isEditing && /* @__PURE__ */ jsx6(Button2, {
552
802
  variant: "ghost",
553
803
  size: "sm",
554
804
  className: "h-6 w-6 p-0",
555
805
  onPress: handleStartEdit,
556
806
  "aria-label": "Edit message",
557
- children: /* @__PURE__ */ jsx4(Pencil, {
807
+ children: /* @__PURE__ */ jsx6(Pencil, {
558
808
  className: "h-3 w-3"
559
809
  })
560
810
  })
561
811
  ]
562
812
  }),
563
- message.reasoning && /* @__PURE__ */ jsxs4("details", {
564
- className: "text-muted-foreground mt-2 text-sm",
813
+ message.reasoning && (comps?.Reasoning ? /* @__PURE__ */ jsx6(comps.Reasoning, {
814
+ isStreaming: isStreaming && !!message.reasoning,
815
+ children: message.reasoning
816
+ }) : /* @__PURE__ */ jsxs6(Reasoning, {
817
+ isStreaming: isStreaming && !!message.reasoning,
818
+ className: "mt-2",
565
819
  children: [
566
- /* @__PURE__ */ jsx4("summary", {
567
- className: "cursor-pointer hover:underline",
568
- children: "View reasoning"
820
+ /* @__PURE__ */ jsx6(ReasoningTrigger, {
821
+ isStreaming
569
822
  }),
570
- /* @__PURE__ */ jsx4("div", {
571
- className: "bg-muted mt-1 rounded-md p-2",
572
- children: /* @__PURE__ */ jsx4("p", {
573
- className: "whitespace-pre-wrap",
574
- children: message.reasoning
575
- })
823
+ /* @__PURE__ */ jsx6(ReasoningContent, {
824
+ children: message.reasoning
576
825
  })
577
826
  ]
578
- }),
579
- message.sources && message.sources.length > 0 && /* @__PURE__ */ jsx4("div", {
580
- className: "mt-2 flex flex-wrap gap-2",
581
- children: message.sources.map((source) => /* @__PURE__ */ jsxs4("a", {
582
- href: source.url ?? "#",
583
- target: "_blank",
584
- rel: "noopener noreferrer",
585
- className: "text-muted-foreground hover:text-foreground bg-muted inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs transition-colors",
827
+ })),
828
+ message.sources && message.sources.length > 0 && (() => {
829
+ const SourcesComp = comps?.Sources;
830
+ const SourcesTriggerComp = comps?.SourcesTrigger;
831
+ const SourceComp = comps?.Source;
832
+ if (SourcesComp && SourcesTriggerComp && SourceComp) {
833
+ return /* @__PURE__ */ jsxs6(SourcesComp, {
834
+ children: [
835
+ /* @__PURE__ */ jsx6(SourcesTriggerComp, {
836
+ count: message.sources.length
837
+ }),
838
+ message.sources.map((source) => /* @__PURE__ */ jsx6(SourceComp, {
839
+ href: source.url ?? "#",
840
+ title: source.title || source.url || source.id
841
+ }, source.id))
842
+ ]
843
+ });
844
+ }
845
+ return /* @__PURE__ */ jsxs6(Sources, {
846
+ className: "mt-2",
586
847
  children: [
587
- /* @__PURE__ */ jsx4(ExternalLink, {
588
- className: "h-3 w-3"
848
+ /* @__PURE__ */ jsx6(SourcesTrigger, {
849
+ count: message.sources.length
589
850
  }),
590
- source.title || source.url || source.id
851
+ /* @__PURE__ */ jsx6(SourcesContent, {
852
+ children: message.sources.map((source) => /* @__PURE__ */ jsx6(Source, {
853
+ href: source.url ?? "#",
854
+ title: source.title || source.url || source.id
855
+ }, source.id))
856
+ })
591
857
  ]
592
- }, source.id))
593
- }),
594
- message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx4("div", {
595
- className: "mt-2 space-y-2",
596
- children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs4("details", {
597
- className: "bg-muted border-border rounded-md border",
598
- children: [
599
- /* @__PURE__ */ jsxs4("summary", {
600
- className: "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm font-medium",
601
- children: [
602
- /* @__PURE__ */ jsx4(Wrench, {
603
- className: "text-muted-foreground h-4 w-4"
604
- }),
605
- tc.name,
606
- /* @__PURE__ */ jsx4("span", {
607
- className: cn3("ml-auto rounded px-1.5 py-0.5 text-xs", tc.status === "completed" && "bg-green-500/20 text-green-700 dark:text-green-400", tc.status === "error" && "bg-destructive/20 text-destructive", tc.status === "running" && "bg-blue-500/20 text-blue-700 dark:text-blue-400"),
608
- children: tc.status
609
- })
610
- ]
611
- }),
612
- /* @__PURE__ */ jsxs4("div", {
613
- className: "border-border border-t px-3 py-2 text-xs",
858
+ });
859
+ })(),
860
+ message.toolCalls && message.toolCalls.length > 0 && (() => {
861
+ const CotComp = comps?.ChainOfThought;
862
+ const CotStepComp = comps?.ChainOfThoughtStep;
863
+ if (CotComp && CotStepComp) {
864
+ return /* @__PURE__ */ jsx6(CotComp, {
865
+ defaultOpen: false,
866
+ className: "mt-2",
867
+ children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6(CotStepComp, {
868
+ label: tc.name,
869
+ description: Object.keys(tc.args).length > 0 ? `Input: ${JSON.stringify(tc.args)}` : undefined,
870
+ status: toolStatusToCotStatus(tc.status),
614
871
  children: [
615
- Object.keys(tc.args).length > 0 && /* @__PURE__ */ jsxs4("div", {
616
- className: "mb-2",
617
- children: [
618
- /* @__PURE__ */ jsx4("span", {
619
- className: "text-muted-foreground font-medium",
620
- children: "Input:"
621
- }),
622
- /* @__PURE__ */ jsx4("pre", {
623
- className: "bg-background mt-1 overflow-x-auto rounded p-2",
624
- children: JSON.stringify(tc.args, null, 2)
625
- })
626
- ]
872
+ tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
873
+ className: "text-muted-foreground mt-1 text-xs",
874
+ children: "Running\u2026"
627
875
  }),
628
- tc.result !== undefined && /* @__PURE__ */ jsx4(ToolResultRenderer, {
876
+ (tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
629
877
  toolName: tc.name,
630
- result: tc.result,
878
+ result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
631
879
  presentationRenderer,
632
880
  formRenderer,
881
+ dataViewRenderer,
633
882
  showRawFallback: true
634
883
  }),
635
- tc.error && /* @__PURE__ */ jsx4("p", {
636
- className: "text-destructive mt-1",
884
+ tc.error && /* @__PURE__ */ jsx6("p", {
885
+ className: "text-destructive mt-1 text-xs",
637
886
  children: tc.error
638
887
  })
639
888
  ]
640
- })
641
- ]
642
- }, tc.id))
643
- })
889
+ }, tc.id))
890
+ });
891
+ }
892
+ return /* @__PURE__ */ jsx6("div", {
893
+ className: "mt-2 space-y-2",
894
+ children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6("details", {
895
+ className: "bg-muted border-border rounded-md border",
896
+ children: [
897
+ /* @__PURE__ */ jsxs6("summary", {
898
+ className: "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm font-medium",
899
+ children: [
900
+ /* @__PURE__ */ jsx6(Wrench, {
901
+ className: "text-muted-foreground h-4 w-4"
902
+ }),
903
+ tc.name,
904
+ /* @__PURE__ */ jsx6("span", {
905
+ className: cn5("ml-auto rounded px-1.5 py-0.5 text-xs", tc.status === "completed" && "bg-green-500/20 text-green-700 dark:text-green-400", tc.status === "error" && "bg-destructive/20 text-destructive", tc.status === "running" && "bg-blue-500/20 text-blue-700 dark:text-blue-400"),
906
+ children: tc.status
907
+ })
908
+ ]
909
+ }),
910
+ /* @__PURE__ */ jsxs6("div", {
911
+ className: "border-border border-t px-3 py-2 text-xs",
912
+ children: [
913
+ Object.keys(tc.args).length > 0 && /* @__PURE__ */ jsxs6("div", {
914
+ className: "mb-2",
915
+ children: [
916
+ /* @__PURE__ */ jsx6("span", {
917
+ className: "text-muted-foreground font-medium",
918
+ children: "Input:"
919
+ }),
920
+ /* @__PURE__ */ jsx6("pre", {
921
+ className: "bg-background mt-1 overflow-x-auto rounded p-2",
922
+ children: JSON.stringify(tc.args, null, 2)
923
+ })
924
+ ]
925
+ }),
926
+ tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
927
+ className: "text-muted-foreground mt-1 text-xs",
928
+ children: "Running\u2026"
929
+ }),
930
+ (tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
931
+ toolName: tc.name,
932
+ result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
933
+ presentationRenderer,
934
+ formRenderer,
935
+ dataViewRenderer,
936
+ showRawFallback: true
937
+ }),
938
+ tc.error && /* @__PURE__ */ jsx6("p", {
939
+ className: "text-destructive mt-1",
940
+ children: tc.error
941
+ })
942
+ ]
943
+ })
944
+ ]
945
+ }, tc.id))
946
+ });
947
+ })()
644
948
  ]
645
949
  })
646
950
  ]
647
951
  });
648
952
  }
649
953
  // src/presentation/components/ChatInput.tsx
650
- import * as React4 from "react";
651
- import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
954
+ import * as React5 from "react";
955
+ import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
652
956
  import { Textarea } from "@contractspec/lib.design-system";
653
957
  import { Button as Button3 } from "@contractspec/lib.design-system";
654
- import { Send, Paperclip, X as X2, Loader2, FileText, Code } from "lucide-react";
655
- import { jsx as jsx5, jsxs as jsxs5, Fragment as Fragment2 } from "react/jsx-runtime";
958
+ import {
959
+ Send,
960
+ Paperclip,
961
+ X as X2,
962
+ Loader2,
963
+ FileText,
964
+ Code,
965
+ ImageIcon
966
+ } from "lucide-react";
967
+ import { jsx as jsx7, jsxs as jsxs7, Fragment as Fragment3 } from "react/jsx-runtime";
656
968
  "use client";
969
+ var DEFAULT_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
970
+ var CODE_EXTENSIONS = [
971
+ "ts",
972
+ "tsx",
973
+ "js",
974
+ "jsx",
975
+ "py",
976
+ "go",
977
+ "rs",
978
+ "java",
979
+ "json",
980
+ "md",
981
+ "txt",
982
+ "yaml",
983
+ "yml"
984
+ ];
985
+ function readFileAsContent(file) {
986
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
987
+ const isCode = CODE_EXTENSIONS.includes(extension);
988
+ const isImage = file.type.startsWith("image/");
989
+ if (isImage) {
990
+ return new Promise((resolve, reject) => {
991
+ const reader = new FileReader;
992
+ reader.onload = () => {
993
+ const result = reader.result;
994
+ resolve({
995
+ content: typeof result === "string" ? result : new TextDecoder().decode(result ?? new ArrayBuffer(0)),
996
+ type: "image"
997
+ });
998
+ };
999
+ reader.onerror = () => reject(new Error("Could not read file"));
1000
+ reader.readAsDataURL(file);
1001
+ });
1002
+ }
1003
+ return file.text().then((content) => ({
1004
+ content,
1005
+ type: isCode ? "code" : "file"
1006
+ })).catch(() => {
1007
+ throw new Error("Could not read file");
1008
+ });
1009
+ }
657
1010
  function ChatInput({
658
1011
  onSend,
659
1012
  disabled = false,
@@ -661,147 +1014,163 @@ function ChatInput({
661
1014
  placeholder = "Type a message...",
662
1015
  className,
663
1016
  showAttachments = true,
664
- maxAttachments = 5
1017
+ maxAttachments = 5,
1018
+ maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES
665
1019
  }) {
666
- const [content, setContent] = React4.useState("");
667
- const [attachments, setAttachments] = React4.useState([]);
668
- const textareaRef = React4.useRef(null);
669
- const fileInputRef = React4.useRef(null);
1020
+ const [content, setContent] = React5.useState("");
1021
+ const [attachments, setAttachments] = React5.useState([]);
1022
+ const [fileError, setFileError] = React5.useState(null);
1023
+ const textareaRef = React5.useRef(null);
1024
+ const fileInputRef = React5.useRef(null);
670
1025
  const canSend = content.trim().length > 0 || attachments.length > 0;
671
- const handleSubmit = React4.useCallback((e) => {
1026
+ const handleSubmit = React5.useCallback((e) => {
672
1027
  e?.preventDefault();
673
1028
  if (!canSend || disabled || isLoading)
674
1029
  return;
675
1030
  onSend(content.trim(), attachments.length > 0 ? attachments : undefined);
676
1031
  setContent("");
677
1032
  setAttachments([]);
1033
+ setFileError(null);
678
1034
  textareaRef.current?.focus();
679
1035
  }, [canSend, content, attachments, disabled, isLoading, onSend]);
680
- const handleKeyDown = React4.useCallback((e) => {
1036
+ const handleKeyDown = React5.useCallback((e) => {
681
1037
  if (e.key === "Enter" && !e.shiftKey) {
682
1038
  e.preventDefault();
683
1039
  handleSubmit();
684
1040
  }
685
1041
  }, [handleSubmit]);
686
- const handleFileSelect = React4.useCallback(async (e) => {
1042
+ const handleFileSelect = React5.useCallback(async (e) => {
687
1043
  const files = e.target.files;
688
1044
  if (!files)
689
1045
  return;
1046
+ setFileError(null);
690
1047
  const newAttachments = [];
1048
+ const errors = [];
691
1049
  for (const file of Array.from(files)) {
692
- if (attachments.length + newAttachments.length >= maxAttachments)
1050
+ if (attachments.length + newAttachments.length >= maxAttachments) {
1051
+ errors.push(`Maximum ${maxAttachments} attachments allowed`);
693
1052
  break;
694
- const content2 = await file.text();
695
- const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
696
- const isCode = [
697
- "ts",
698
- "tsx",
699
- "js",
700
- "jsx",
701
- "py",
702
- "go",
703
- "rs",
704
- "java"
705
- ].includes(extension);
706
- newAttachments.push({
707
- id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
708
- type: isCode ? "code" : "file",
709
- name: file.name,
710
- content: content2,
711
- mimeType: file.type,
712
- size: file.size
713
- });
1053
+ }
1054
+ if (file.size > maxFileSizeBytes) {
1055
+ errors.push(`${file.name} exceeds ${Math.round(maxFileSizeBytes / 1024 / 1024)}MB limit`);
1056
+ continue;
1057
+ }
1058
+ try {
1059
+ const { content: fileContent, type } = await readFileAsContent(file);
1060
+ newAttachments.push({
1061
+ id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
1062
+ type,
1063
+ name: file.name,
1064
+ content: fileContent,
1065
+ mimeType: file.type,
1066
+ size: file.size
1067
+ });
1068
+ } catch {
1069
+ errors.push(`Could not read ${file.name}`);
1070
+ }
1071
+ }
1072
+ if (errors.length > 0) {
1073
+ setFileError(errors[0] ?? "Could not add file");
1074
+ }
1075
+ if (newAttachments.length > 0) {
1076
+ setAttachments((prev) => [...prev, ...newAttachments]);
714
1077
  }
715
- setAttachments((prev) => [...prev, ...newAttachments]);
716
1078
  e.target.value = "";
717
- }, [attachments.length, maxAttachments]);
718
- const removeAttachment = React4.useCallback((id) => {
1079
+ }, [attachments.length, maxAttachments, maxFileSizeBytes]);
1080
+ const removeAttachment = React5.useCallback((id) => {
719
1081
  setAttachments((prev) => prev.filter((a) => a.id !== id));
720
1082
  }, []);
721
- return /* @__PURE__ */ jsxs5("div", {
722
- className: cn4("flex flex-col gap-2", className),
1083
+ return /* @__PURE__ */ jsxs7("div", {
1084
+ className: cn6("flex flex-col gap-2", className),
723
1085
  children: [
724
- attachments.length > 0 && /* @__PURE__ */ jsx5("div", {
1086
+ attachments.length > 0 && /* @__PURE__ */ jsx7("div", {
725
1087
  className: "flex flex-wrap gap-2",
726
- children: attachments.map((attachment) => /* @__PURE__ */ jsxs5("div", {
727
- className: cn4("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
1088
+ children: attachments.map((attachment) => /* @__PURE__ */ jsxs7("div", {
1089
+ className: cn6("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
728
1090
  children: [
729
- attachment.type === "code" ? /* @__PURE__ */ jsx5(Code, {
1091
+ attachment.type === "code" ? /* @__PURE__ */ jsx7(Code, {
730
1092
  className: "h-3.5 w-3.5"
731
- }) : /* @__PURE__ */ jsx5(FileText, {
1093
+ }) : attachment.type === "image" ? /* @__PURE__ */ jsx7(ImageIcon, {
1094
+ className: "h-3.5 w-3.5"
1095
+ }) : /* @__PURE__ */ jsx7(FileText, {
732
1096
  className: "h-3.5 w-3.5"
733
1097
  }),
734
- /* @__PURE__ */ jsx5("span", {
1098
+ /* @__PURE__ */ jsx7("span", {
735
1099
  className: "max-w-[150px] truncate",
736
1100
  children: attachment.name
737
1101
  }),
738
- /* @__PURE__ */ jsx5("button", {
1102
+ /* @__PURE__ */ jsx7("button", {
739
1103
  type: "button",
740
1104
  onClick: () => removeAttachment(attachment.id),
741
1105
  className: "hover:text-foreground",
742
1106
  "aria-label": `Remove ${attachment.name}`,
743
- children: /* @__PURE__ */ jsx5(X2, {
1107
+ children: /* @__PURE__ */ jsx7(X2, {
744
1108
  className: "h-3.5 w-3.5"
745
1109
  })
746
1110
  })
747
1111
  ]
748
1112
  }, attachment.id))
749
1113
  }),
750
- /* @__PURE__ */ jsxs5("form", {
1114
+ fileError && /* @__PURE__ */ jsx7("p", {
1115
+ className: "text-destructive text-xs",
1116
+ role: "alert",
1117
+ children: fileError
1118
+ }),
1119
+ /* @__PURE__ */ jsxs7("form", {
751
1120
  onSubmit: handleSubmit,
752
1121
  className: "flex items-end gap-2",
753
1122
  children: [
754
- showAttachments && /* @__PURE__ */ jsxs5(Fragment2, {
1123
+ showAttachments && /* @__PURE__ */ jsxs7(Fragment3, {
755
1124
  children: [
756
- /* @__PURE__ */ jsx5("input", {
1125
+ /* @__PURE__ */ jsx7("input", {
757
1126
  ref: fileInputRef,
758
1127
  type: "file",
759
1128
  multiple: true,
760
- accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
1129
+ accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml,image/*",
761
1130
  onChange: handleFileSelect,
762
1131
  className: "hidden",
763
1132
  "aria-label": "Attach files"
764
1133
  }),
765
- /* @__PURE__ */ jsx5(Button3, {
1134
+ /* @__PURE__ */ jsx7(Button3, {
766
1135
  type: "button",
767
1136
  variant: "ghost",
768
1137
  size: "sm",
769
1138
  onPress: () => fileInputRef.current?.click(),
770
1139
  disabled: disabled || attachments.length >= maxAttachments,
771
1140
  "aria-label": "Attach files",
772
- children: /* @__PURE__ */ jsx5(Paperclip, {
1141
+ children: /* @__PURE__ */ jsx7(Paperclip, {
773
1142
  className: "h-4 w-4"
774
1143
  })
775
1144
  })
776
1145
  ]
777
1146
  }),
778
- /* @__PURE__ */ jsx5("div", {
1147
+ /* @__PURE__ */ jsx7("div", {
779
1148
  className: "relative flex-1",
780
- children: /* @__PURE__ */ jsx5(Textarea, {
1149
+ children: /* @__PURE__ */ jsx7(Textarea, {
781
1150
  value: content,
782
1151
  onChange: (e) => setContent(e.target.value),
783
1152
  onKeyDown: handleKeyDown,
784
1153
  placeholder,
785
1154
  disabled,
786
- className: cn4("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
1155
+ className: cn6("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
787
1156
  rows: 1,
788
1157
  "aria-label": "Chat message"
789
1158
  })
790
1159
  }),
791
- /* @__PURE__ */ jsx5(Button3, {
1160
+ /* @__PURE__ */ jsx7(Button3, {
792
1161
  type: "submit",
793
1162
  disabled: !canSend || disabled || isLoading,
794
1163
  size: "sm",
795
1164
  "aria-label": isLoading ? "Sending..." : "Send message",
796
- children: isLoading ? /* @__PURE__ */ jsx5(Loader2, {
1165
+ children: isLoading ? /* @__PURE__ */ jsx7(Loader2, {
797
1166
  className: "h-4 w-4 animate-spin"
798
- }) : /* @__PURE__ */ jsx5(Send, {
1167
+ }) : /* @__PURE__ */ jsx7(Send, {
799
1168
  className: "h-4 w-4"
800
1169
  })
801
1170
  })
802
1171
  ]
803
1172
  }),
804
- /* @__PURE__ */ jsx5("p", {
1173
+ /* @__PURE__ */ jsx7("p", {
805
1174
  className: "text-muted-foreground text-xs",
806
1175
  children: "Press Enter to send, Shift+Enter for new line"
807
1176
  })
@@ -809,7 +1178,7 @@ function ChatInput({
809
1178
  });
810
1179
  }
811
1180
  // src/presentation/components/ChatExportToolbar.tsx
812
- import * as React5 from "react";
1181
+ import * as React6 from "react";
813
1182
  import { Download as Download2, FileText as FileText2, Copy as Copy3, Check as Check3, Plus, GitFork } from "lucide-react";
814
1183
  import { Button as Button4 } from "@contractspec/lib.design-system";
815
1184
  import {
@@ -1021,7 +1390,7 @@ function exportToFile(messages, format, conversation) {
1021
1390
  }
1022
1391
 
1023
1392
  // src/presentation/components/ChatExportToolbar.tsx
1024
- import { jsx as jsx6, jsxs as jsxs6, Fragment as Fragment3 } from "react/jsx-runtime";
1393
+ import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
1025
1394
  "use client";
1026
1395
  function ChatExportToolbar({
1027
1396
  messages,
@@ -1036,19 +1405,19 @@ function ChatExportToolbar({
1036
1405
  onCreateNew,
1037
1406
  onFork
1038
1407
  }) {
1039
- const [copied, setCopied] = React5.useState(false);
1040
- const toExport = React5.useMemo(() => {
1408
+ const [copied, setCopied] = React6.useState(false);
1409
+ const toExport = React6.useMemo(() => {
1041
1410
  if (selectedIds.size > 0) {
1042
1411
  const idSet = selectedIds;
1043
1412
  return messages.filter((m) => idSet.has(m.id));
1044
1413
  }
1045
1414
  return messages;
1046
1415
  }, [messages, selectedIds]);
1047
- const handleExport = React5.useCallback((format) => {
1416
+ const handleExport = React6.useCallback((format) => {
1048
1417
  exportToFile(toExport, format, conversation);
1049
1418
  onExported?.(format, toExport.length);
1050
1419
  }, [toExport, conversation, onExported]);
1051
- const handleCopy = React5.useCallback(async () => {
1420
+ const handleCopy = React6.useCallback(async () => {
1052
1421
  const content = formatMessagesAsMarkdown(toExport);
1053
1422
  await navigator.clipboard.writeText(content);
1054
1423
  setCopied(true);
@@ -1056,8 +1425,8 @@ function ChatExportToolbar({
1056
1425
  onExported?.("markdown", toExport.length);
1057
1426
  }, [toExport, onExported]);
1058
1427
  const disabled = messages.length === 0;
1059
- const [forking, setForking] = React5.useState(false);
1060
- const handleFork = React5.useCallback(async (upToMessageId) => {
1428
+ const [forking, setForking] = React6.useState(false);
1429
+ const handleFork = React6.useCallback(async (upToMessageId) => {
1061
1430
  if (!onFork)
1062
1431
  return;
1063
1432
  setForking(true);
@@ -1067,35 +1436,35 @@ function ChatExportToolbar({
1067
1436
  setForking(false);
1068
1437
  }
1069
1438
  }, [onFork]);
1070
- return /* @__PURE__ */ jsxs6("div", {
1439
+ return /* @__PURE__ */ jsxs8("div", {
1071
1440
  className: "flex items-center gap-2",
1072
1441
  children: [
1073
- onCreateNew && /* @__PURE__ */ jsxs6(Button4, {
1442
+ onCreateNew && /* @__PURE__ */ jsxs8(Button4, {
1074
1443
  variant: "outline",
1075
1444
  size: "sm",
1076
1445
  onPress: onCreateNew,
1077
1446
  "aria-label": "New conversation",
1078
1447
  children: [
1079
- /* @__PURE__ */ jsx6(Plus, {
1448
+ /* @__PURE__ */ jsx8(Plus, {
1080
1449
  className: "h-4 w-4"
1081
1450
  }),
1082
1451
  "New"
1083
1452
  ]
1084
1453
  }),
1085
- onFork && messages.length > 0 && /* @__PURE__ */ jsxs6(Button4, {
1454
+ onFork && messages.length > 0 && /* @__PURE__ */ jsxs8(Button4, {
1086
1455
  variant: "outline",
1087
1456
  size: "sm",
1088
1457
  disabled: forking,
1089
1458
  onPress: () => handleFork(),
1090
1459
  "aria-label": "Fork conversation",
1091
1460
  children: [
1092
- /* @__PURE__ */ jsx6(GitFork, {
1461
+ /* @__PURE__ */ jsx8(GitFork, {
1093
1462
  className: "h-4 w-4"
1094
1463
  }),
1095
1464
  "Fork"
1096
1465
  ]
1097
1466
  }),
1098
- showSelectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs6("span", {
1467
+ showSelectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs8("span", {
1099
1468
  className: "text-muted-foreground text-sm",
1100
1469
  children: [
1101
1470
  selectedCount,
@@ -1104,16 +1473,16 @@ function ChatExportToolbar({
1104
1473
  " selected"
1105
1474
  ]
1106
1475
  }),
1107
- onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs6(Fragment3, {
1476
+ onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs8(Fragment4, {
1108
1477
  children: [
1109
- /* @__PURE__ */ jsx6(Button4, {
1478
+ /* @__PURE__ */ jsx8(Button4, {
1110
1479
  variant: "ghost",
1111
1480
  size: "sm",
1112
1481
  onPress: onSelectAll,
1113
1482
  className: "text-xs",
1114
1483
  children: "Select all"
1115
1484
  }),
1116
- selectedCount > 0 && /* @__PURE__ */ jsx6(Button4, {
1485
+ selectedCount > 0 && /* @__PURE__ */ jsx8(Button4, {
1117
1486
  variant: "ghost",
1118
1487
  size: "sm",
1119
1488
  onPress: onClearSelection,
@@ -1122,64 +1491,64 @@ function ChatExportToolbar({
1122
1491
  })
1123
1492
  ]
1124
1493
  }),
1125
- /* @__PURE__ */ jsxs6(DropdownMenu, {
1494
+ /* @__PURE__ */ jsxs8(DropdownMenu, {
1126
1495
  children: [
1127
- /* @__PURE__ */ jsx6(DropdownMenuTrigger, {
1496
+ /* @__PURE__ */ jsx8(DropdownMenuTrigger, {
1128
1497
  asChild: true,
1129
- children: /* @__PURE__ */ jsxs6(Button4, {
1498
+ children: /* @__PURE__ */ jsxs8(Button4, {
1130
1499
  variant: "outline",
1131
1500
  size: "sm",
1132
1501
  disabled,
1133
1502
  "aria-label": selectedCount > 0 ? "Export selected messages" : "Export conversation",
1134
1503
  children: [
1135
- /* @__PURE__ */ jsx6(Download2, {
1504
+ /* @__PURE__ */ jsx8(Download2, {
1136
1505
  className: "h-4 w-4"
1137
1506
  }),
1138
1507
  "Export"
1139
1508
  ]
1140
1509
  })
1141
1510
  }),
1142
- /* @__PURE__ */ jsxs6(DropdownMenuContent, {
1511
+ /* @__PURE__ */ jsxs8(DropdownMenuContent, {
1143
1512
  align: "end",
1144
1513
  children: [
1145
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1514
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1146
1515
  onSelect: () => handleExport("markdown"),
1147
1516
  disabled,
1148
1517
  children: [
1149
- /* @__PURE__ */ jsx6(FileText2, {
1518
+ /* @__PURE__ */ jsx8(FileText2, {
1150
1519
  className: "h-4 w-4"
1151
1520
  }),
1152
1521
  "Export as Markdown (.md)"
1153
1522
  ]
1154
1523
  }),
1155
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1524
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1156
1525
  onSelect: () => handleExport("txt"),
1157
1526
  disabled,
1158
1527
  children: [
1159
- /* @__PURE__ */ jsx6(FileText2, {
1528
+ /* @__PURE__ */ jsx8(FileText2, {
1160
1529
  className: "h-4 w-4"
1161
1530
  }),
1162
1531
  "Export as Plain Text (.txt)"
1163
1532
  ]
1164
1533
  }),
1165
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1534
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1166
1535
  onSelect: () => handleExport("json"),
1167
1536
  disabled,
1168
1537
  children: [
1169
- /* @__PURE__ */ jsx6(FileText2, {
1538
+ /* @__PURE__ */ jsx8(FileText2, {
1170
1539
  className: "h-4 w-4"
1171
1540
  }),
1172
1541
  "Export as JSON (.json)"
1173
1542
  ]
1174
1543
  }),
1175
- /* @__PURE__ */ jsx6(DropdownMenuSeparator, {}),
1176
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1544
+ /* @__PURE__ */ jsx8(DropdownMenuSeparator, {}),
1545
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1177
1546
  onSelect: () => handleCopy(),
1178
1547
  disabled,
1179
1548
  children: [
1180
- copied ? /* @__PURE__ */ jsx6(Check3, {
1549
+ copied ? /* @__PURE__ */ jsx8(Check3, {
1181
1550
  className: "h-4 w-4 text-green-500"
1182
- }) : /* @__PURE__ */ jsx6(Copy3, {
1551
+ }) : /* @__PURE__ */ jsx8(Copy3, {
1183
1552
  className: "h-4 w-4"
1184
1553
  }),
1185
1554
  copied ? "Copied to clipboard" : "Copy to clipboard"
@@ -1193,11 +1562,11 @@ function ChatExportToolbar({
1193
1562
  });
1194
1563
  }
1195
1564
  // src/presentation/components/ChatWithExport.tsx
1196
- import * as React8 from "react";
1565
+ import * as React10 from "react";
1197
1566
 
1198
1567
  // src/presentation/components/ThinkingLevelPicker.tsx
1199
- import * as React6 from "react";
1200
- import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
1568
+ import * as React7 from "react";
1569
+ import { cn as cn7 } from "@contractspec/lib.ui-kit-web/ui/utils";
1201
1570
  import {
1202
1571
  Select,
1203
1572
  SelectContent,
@@ -1259,7 +1628,7 @@ function getProviderOptions(level, providerName) {
1259
1628
  }
1260
1629
 
1261
1630
  // src/presentation/components/ThinkingLevelPicker.tsx
1262
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1631
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1263
1632
  "use client";
1264
1633
  var THINKING_LEVELS = [
1265
1634
  "instant",
@@ -1273,20 +1642,20 @@ function ThinkingLevelPicker({
1273
1642
  className,
1274
1643
  compact = false
1275
1644
  }) {
1276
- const handleChange = React6.useCallback((v) => {
1645
+ const handleChange = React7.useCallback((v) => {
1277
1646
  onChange(v);
1278
1647
  }, [onChange]);
1279
1648
  if (compact) {
1280
- return /* @__PURE__ */ jsxs7(Select, {
1649
+ return /* @__PURE__ */ jsxs9(Select, {
1281
1650
  value,
1282
1651
  onValueChange: handleChange,
1283
1652
  children: [
1284
- /* @__PURE__ */ jsx7(SelectTrigger, {
1285
- className: cn5("w-[140px]", className),
1286
- children: /* @__PURE__ */ jsx7(SelectValue, {})
1653
+ /* @__PURE__ */ jsx9(SelectTrigger, {
1654
+ className: cn7("w-[140px]", className),
1655
+ children: /* @__PURE__ */ jsx9(SelectValue, {})
1287
1656
  }),
1288
- /* @__PURE__ */ jsx7(SelectContent, {
1289
- children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx7(SelectItem, {
1657
+ /* @__PURE__ */ jsx9(SelectContent, {
1658
+ children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
1290
1659
  value: level,
1291
1660
  children: THINKING_LEVEL_LABELS[level]
1292
1661
  }, level))
@@ -1294,26 +1663,26 @@ function ThinkingLevelPicker({
1294
1663
  ]
1295
1664
  });
1296
1665
  }
1297
- return /* @__PURE__ */ jsxs7("div", {
1298
- className: cn5("flex flex-col gap-1.5", className),
1666
+ return /* @__PURE__ */ jsxs9("div", {
1667
+ className: cn7("flex flex-col gap-1.5", className),
1299
1668
  children: [
1300
- /* @__PURE__ */ jsx7(Label, {
1669
+ /* @__PURE__ */ jsx9(Label, {
1301
1670
  htmlFor: "thinking-level-picker",
1302
1671
  className: "text-sm font-medium",
1303
1672
  children: "Thinking Level"
1304
1673
  }),
1305
- /* @__PURE__ */ jsxs7(Select, {
1674
+ /* @__PURE__ */ jsxs9(Select, {
1306
1675
  name: "thinking-level-picker",
1307
1676
  value,
1308
1677
  onValueChange: handleChange,
1309
1678
  children: [
1310
- /* @__PURE__ */ jsx7(SelectTrigger, {
1311
- children: /* @__PURE__ */ jsx7(SelectValue, {
1679
+ /* @__PURE__ */ jsx9(SelectTrigger, {
1680
+ children: /* @__PURE__ */ jsx9(SelectValue, {
1312
1681
  placeholder: "Select thinking level"
1313
1682
  })
1314
1683
  }),
1315
- /* @__PURE__ */ jsx7(SelectContent, {
1316
- children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx7(SelectItem, {
1684
+ /* @__PURE__ */ jsx9(SelectContent, {
1685
+ children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
1317
1686
  value: level,
1318
1687
  title: THINKING_LEVEL_DESCRIPTIONS[level],
1319
1688
  children: THINKING_LEVEL_LABELS[level]
@@ -1326,12 +1695,12 @@ function ThinkingLevelPicker({
1326
1695
  }
1327
1696
 
1328
1697
  // src/presentation/hooks/useMessageSelection.ts
1329
- import * as React7 from "react";
1698
+ import * as React8 from "react";
1330
1699
  "use client";
1331
1700
  function useMessageSelection(messageIds) {
1332
- const [selectedIds, setSelectedIds] = React7.useState(() => new Set);
1333
- const idSet = React7.useMemo(() => new Set(messageIds), [messageIds.join(",")]);
1334
- React7.useEffect(() => {
1701
+ const [selectedIds, setSelectedIds] = React8.useState(() => new Set);
1702
+ const idSet = React8.useMemo(() => new Set(messageIds), [messageIds.join(",")]);
1703
+ React8.useEffect(() => {
1335
1704
  setSelectedIds((prev) => {
1336
1705
  const next = new Set;
1337
1706
  for (const id of prev) {
@@ -1341,7 +1710,7 @@ function useMessageSelection(messageIds) {
1341
1710
  return next.size === prev.size ? prev : next;
1342
1711
  });
1343
1712
  }, [idSet]);
1344
- const toggle = React7.useCallback((id) => {
1713
+ const toggle = React8.useCallback((id) => {
1345
1714
  setSelectedIds((prev) => {
1346
1715
  const next = new Set(prev);
1347
1716
  if (next.has(id))
@@ -1351,13 +1720,13 @@ function useMessageSelection(messageIds) {
1351
1720
  return next;
1352
1721
  });
1353
1722
  }, []);
1354
- const selectAll = React7.useCallback(() => {
1723
+ const selectAll = React8.useCallback(() => {
1355
1724
  setSelectedIds(new Set(messageIds));
1356
1725
  }, [messageIds.join(",")]);
1357
- const clearSelection = React7.useCallback(() => {
1726
+ const clearSelection = React8.useCallback(() => {
1358
1727
  setSelectedIds(new Set);
1359
1728
  }, []);
1360
- const isSelected = React7.useCallback((id) => selectedIds.has(id), [selectedIds]);
1729
+ const isSelected = React8.useCallback((id) => selectedIds.has(id), [selectedIds]);
1361
1730
  const selectedCount = selectedIds.size;
1362
1731
  return {
1363
1732
  selectedIds,
@@ -1369,8 +1738,38 @@ function useMessageSelection(messageIds) {
1369
1738
  };
1370
1739
  }
1371
1740
 
1741
+ // src/presentation/components/Suggestion.tsx
1742
+ import * as React9 from "react";
1743
+ import { Button as Button5 } from "@contractspec/lib.design-system";
1744
+ import { cn as cn8 } from "@contractspec/lib.ui-kit-web/ui/utils";
1745
+ import { jsx as jsx10 } from "react/jsx-runtime";
1746
+ "use client";
1747
+ function Suggestions({ children, className }) {
1748
+ return /* @__PURE__ */ jsx10("div", {
1749
+ className: cn8("flex flex-wrap gap-2", className),
1750
+ children
1751
+ });
1752
+ }
1753
+ function Suggestion({
1754
+ suggestion,
1755
+ onClick,
1756
+ className
1757
+ }) {
1758
+ const handleClick = React9.useCallback(() => {
1759
+ onClick?.(suggestion);
1760
+ }, [suggestion, onClick]);
1761
+ return /* @__PURE__ */ jsx10(Button5, {
1762
+ type: "button",
1763
+ variant: "outline",
1764
+ size: "sm",
1765
+ onPress: handleClick,
1766
+ className: cn8("text-muted-foreground hover:text-foreground", className),
1767
+ children: suggestion
1768
+ });
1769
+ }
1770
+
1372
1771
  // src/presentation/components/ChatWithExport.tsx
1373
- import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
1772
+ import { jsx as jsx11, jsxs as jsxs10, Fragment as Fragment5 } from "react/jsx-runtime";
1374
1773
  "use client";
1375
1774
  function ChatWithExport({
1376
1775
  messages,
@@ -1386,20 +1785,27 @@ function ChatWithExport({
1386
1785
  thinkingLevel = "thinking",
1387
1786
  onThinkingLevelChange,
1388
1787
  presentationRenderer,
1389
- formRenderer
1788
+ formRenderer,
1789
+ dataViewRenderer,
1790
+ components,
1791
+ suggestions,
1792
+ onSuggestionClick,
1793
+ suggestionComponents: suggestionComps,
1794
+ showSuggestionsWhenEmpty = true
1390
1795
  }) {
1391
- const messageIds = React8.useMemo(() => messages.map((m) => m.id), [messages]);
1796
+ const messageIds = React10.useMemo(() => messages.map((m) => m.id), [messages]);
1392
1797
  const selection = useMessageSelection(messageIds);
1798
+ const showSuggestions = suggestions && suggestions.length > 0 && (messages.length === 0 || showSuggestionsWhenEmpty);
1393
1799
  const hasToolbar = showExport || showMessageSelection;
1394
1800
  const hasPicker = Boolean(onThinkingLevelChange);
1395
- const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs8(Fragment4, {
1801
+ const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs10(Fragment5, {
1396
1802
  children: [
1397
- hasPicker && /* @__PURE__ */ jsx8(ThinkingLevelPicker, {
1803
+ hasPicker && onThinkingLevelChange && /* @__PURE__ */ jsx11(ThinkingLevelPicker, {
1398
1804
  value: thinkingLevel,
1399
1805
  onChange: onThinkingLevelChange,
1400
1806
  compact: true
1401
1807
  }),
1402
- hasToolbar && /* @__PURE__ */ jsx8(ChatExportToolbar, {
1808
+ hasToolbar && /* @__PURE__ */ jsx11(ChatExportToolbar, {
1403
1809
  messages,
1404
1810
  conversation,
1405
1811
  selectedIds: selection.selectedIds,
@@ -1413,12 +1819,12 @@ function ChatWithExport({
1413
1819
  })
1414
1820
  ]
1415
1821
  }) : null;
1416
- return /* @__PURE__ */ jsxs8(ChatContainer, {
1822
+ return /* @__PURE__ */ jsxs10(ChatContainer, {
1417
1823
  className,
1418
1824
  headerContent,
1419
1825
  showScrollButton,
1420
1826
  children: [
1421
- messages.map((msg) => /* @__PURE__ */ jsx8(ChatMessage, {
1827
+ messages.map((msg) => /* @__PURE__ */ jsx11(ChatMessage, {
1422
1828
  message: msg,
1423
1829
  selectable: showMessageSelection,
1424
1830
  selected: selection.isSelected(msg.id),
@@ -1426,26 +1832,47 @@ function ChatWithExport({
1426
1832
  editable: msg.role === "user" && !!onEditMessage,
1427
1833
  onEdit: onEditMessage,
1428
1834
  presentationRenderer,
1429
- formRenderer
1835
+ formRenderer,
1836
+ dataViewRenderer,
1837
+ components
1430
1838
  }, msg.id)),
1839
+ showSuggestions && (() => {
1840
+ const SuggestionsComp = suggestionComps?.Suggestions;
1841
+ const SuggestionComp = suggestionComps?.Suggestion;
1842
+ if (SuggestionsComp && SuggestionComp) {
1843
+ return /* @__PURE__ */ jsx11(SuggestionsComp, {
1844
+ children: suggestions.map((s) => /* @__PURE__ */ jsx11(SuggestionComp, {
1845
+ suggestion: s,
1846
+ onClick: onSuggestionClick
1847
+ }, s))
1848
+ });
1849
+ }
1850
+ return /* @__PURE__ */ jsx11(Suggestions, {
1851
+ className: "mb-4",
1852
+ children: suggestions.map((s) => /* @__PURE__ */ jsx11(Suggestion, {
1853
+ suggestion: s,
1854
+ onClick: onSuggestionClick
1855
+ }, s))
1856
+ });
1857
+ })(),
1431
1858
  children
1432
1859
  ]
1433
1860
  });
1434
1861
  }
1435
1862
  // src/presentation/components/ChatSidebar.tsx
1436
- import * as React10 from "react";
1863
+ import * as React12 from "react";
1437
1864
  import { Plus as Plus2, Trash2, MessageSquare } from "lucide-react";
1438
- import { Button as Button5 } from "@contractspec/lib.design-system";
1439
- import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
1865
+ import { Button as Button6 } from "@contractspec/lib.design-system";
1866
+ import { cn as cn9 } from "@contractspec/lib.ui-kit-web/ui/utils";
1440
1867
 
1441
1868
  // src/presentation/hooks/useConversations.ts
1442
- import * as React9 from "react";
1869
+ import * as React11 from "react";
1443
1870
  "use client";
1444
1871
  function useConversations(options) {
1445
1872
  const { store, projectId, tags, limit = 50 } = options;
1446
- const [conversations, setConversations] = React9.useState([]);
1447
- const [isLoading, setIsLoading] = React9.useState(true);
1448
- const refresh = React9.useCallback(async () => {
1873
+ const [conversations, setConversations] = React11.useState([]);
1874
+ const [isLoading, setIsLoading] = React11.useState(true);
1875
+ const refresh = React11.useCallback(async () => {
1449
1876
  setIsLoading(true);
1450
1877
  try {
1451
1878
  const list = await store.list({
@@ -1459,10 +1886,10 @@ function useConversations(options) {
1459
1886
  setIsLoading(false);
1460
1887
  }
1461
1888
  }, [store, projectId, tags, limit]);
1462
- React9.useEffect(() => {
1889
+ React11.useEffect(() => {
1463
1890
  refresh();
1464
1891
  }, [refresh]);
1465
- const deleteConversation = React9.useCallback(async (id) => {
1892
+ const deleteConversation = React11.useCallback(async (id) => {
1466
1893
  const ok = await store.delete(id);
1467
1894
  if (ok) {
1468
1895
  setConversations((prev) => prev.filter((c) => c.id !== id));
@@ -1478,7 +1905,7 @@ function useConversations(options) {
1478
1905
  }
1479
1906
 
1480
1907
  // src/presentation/components/ChatSidebar.tsx
1481
- import { jsx as jsx9, jsxs as jsxs9, Fragment as Fragment5 } from "react/jsx-runtime";
1908
+ import { jsx as jsx12, jsxs as jsxs11, Fragment as Fragment6 } from "react/jsx-runtime";
1482
1909
  "use client";
1483
1910
  function formatDate(date) {
1484
1911
  const d = new Date(date);
@@ -1500,7 +1927,7 @@ function ConversationItem({
1500
1927
  }) {
1501
1928
  const title = conversation.title ?? conversation.messages[0]?.content?.slice(0, 50) ?? "New chat";
1502
1929
  const displayTitle = title.length > 40 ? `${title.slice(0, 40)}\u2026` : title;
1503
- return /* @__PURE__ */ jsxs9("div", {
1930
+ return /* @__PURE__ */ jsxs11("div", {
1504
1931
  role: "button",
1505
1932
  tabIndex: 0,
1506
1933
  onClick: onSelect,
@@ -1510,24 +1937,24 @@ function ConversationItem({
1510
1937
  onSelect();
1511
1938
  }
1512
1939
  },
1513
- className: cn6("group flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors", selected ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"),
1940
+ className: cn9("group flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors", selected ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"),
1514
1941
  children: [
1515
- /* @__PURE__ */ jsx9(MessageSquare, {
1942
+ /* @__PURE__ */ jsx12(MessageSquare, {
1516
1943
  className: "text-muted-foreground h-4 w-4 shrink-0"
1517
1944
  }),
1518
- /* @__PURE__ */ jsxs9("div", {
1945
+ /* @__PURE__ */ jsxs11("div", {
1519
1946
  className: "min-w-0 flex-1",
1520
1947
  children: [
1521
- /* @__PURE__ */ jsx9("p", {
1948
+ /* @__PURE__ */ jsx12("p", {
1522
1949
  className: "truncate",
1523
1950
  children: displayTitle
1524
1951
  }),
1525
- /* @__PURE__ */ jsxs9("p", {
1952
+ /* @__PURE__ */ jsxs11("p", {
1526
1953
  className: "text-muted-foreground text-xs",
1527
1954
  children: [
1528
1955
  formatDate(conversation.updatedAt),
1529
1956
  conversation.projectName && ` \xB7 ${conversation.projectName}`,
1530
- conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */ jsxs9(Fragment5, {
1957
+ conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */ jsxs11(Fragment6, {
1531
1958
  children: [
1532
1959
  " \xB7 ",
1533
1960
  conversation.tags.slice(0, 2).join(", ")
@@ -1537,15 +1964,17 @@ function ConversationItem({
1537
1964
  })
1538
1965
  ]
1539
1966
  }),
1540
- /* @__PURE__ */ jsx9("span", {
1967
+ /* @__PURE__ */ jsx12("span", {
1968
+ role: "group",
1541
1969
  onClick: (e) => e.stopPropagation(),
1542
- children: /* @__PURE__ */ jsx9(Button5, {
1970
+ onKeyDown: (e) => e.stopPropagation(),
1971
+ children: /* @__PURE__ */ jsx12(Button6, {
1543
1972
  variant: "ghost",
1544
1973
  size: "sm",
1545
1974
  className: "h-6 w-6 shrink-0 p-0 opacity-0 group-hover:opacity-100",
1546
1975
  onPress: onDelete,
1547
1976
  "aria-label": "Delete conversation",
1548
- children: /* @__PURE__ */ jsx9(Trash2, {
1977
+ children: /* @__PURE__ */ jsx12(Trash2, {
1549
1978
  className: "h-3 w-3"
1550
1979
  })
1551
1980
  })
@@ -1566,8 +1995,13 @@ function ChatSidebar({
1566
1995
  onUpdateConversation,
1567
1996
  selectedConversation
1568
1997
  }) {
1569
- const { conversations, isLoading, refresh, deleteConversation } = useConversations({ store, projectId, tags, limit });
1570
- const handleDelete = React10.useCallback(async (id) => {
1998
+ const { conversations, isLoading, deleteConversation } = useConversations({
1999
+ store,
2000
+ projectId,
2001
+ tags,
2002
+ limit
2003
+ });
2004
+ const handleDelete = React12.useCallback(async (id) => {
1571
2005
  const ok = await deleteConversation(id);
1572
2006
  if (ok && selectedConversationId === id) {
1573
2007
  onSelectConversation(null);
@@ -1575,39 +2009,39 @@ function ChatSidebar({
1575
2009
  }, [deleteConversation, selectedConversationId, onSelectConversation]);
1576
2010
  if (collapsed)
1577
2011
  return null;
1578
- return /* @__PURE__ */ jsxs9("div", {
1579
- className: cn6("border-border flex w-64 shrink-0 flex-col border-r", className),
2012
+ return /* @__PURE__ */ jsxs11("div", {
2013
+ className: cn9("border-border flex w-64 shrink-0 flex-col border-r", className),
1580
2014
  children: [
1581
- /* @__PURE__ */ jsxs9("div", {
2015
+ /* @__PURE__ */ jsxs11("div", {
1582
2016
  className: "border-border flex shrink-0 items-center justify-between border-b p-2",
1583
2017
  children: [
1584
- /* @__PURE__ */ jsx9("span", {
2018
+ /* @__PURE__ */ jsx12("span", {
1585
2019
  className: "text-muted-foreground text-sm font-medium",
1586
2020
  children: "Conversations"
1587
2021
  }),
1588
- /* @__PURE__ */ jsx9(Button5, {
2022
+ /* @__PURE__ */ jsx12(Button6, {
1589
2023
  variant: "ghost",
1590
2024
  size: "sm",
1591
2025
  className: "h-8 w-8 p-0",
1592
2026
  onPress: onCreateNew,
1593
2027
  "aria-label": "New conversation",
1594
- children: /* @__PURE__ */ jsx9(Plus2, {
2028
+ children: /* @__PURE__ */ jsx12(Plus2, {
1595
2029
  className: "h-4 w-4"
1596
2030
  })
1597
2031
  })
1598
2032
  ]
1599
2033
  }),
1600
- /* @__PURE__ */ jsx9("div", {
2034
+ /* @__PURE__ */ jsx12("div", {
1601
2035
  className: "flex-1 overflow-y-auto p-2",
1602
- children: isLoading ? /* @__PURE__ */ jsx9("div", {
2036
+ children: isLoading ? /* @__PURE__ */ jsx12("div", {
1603
2037
  className: "text-muted-foreground py-4 text-center text-sm",
1604
2038
  children: "Loading\u2026"
1605
- }) : conversations.length === 0 ? /* @__PURE__ */ jsx9("div", {
2039
+ }) : conversations.length === 0 ? /* @__PURE__ */ jsx12("div", {
1606
2040
  className: "text-muted-foreground py-4 text-center text-sm",
1607
2041
  children: "No conversations yet"
1608
- }) : /* @__PURE__ */ jsx9("div", {
2042
+ }) : /* @__PURE__ */ jsx12("div", {
1609
2043
  className: "flex flex-col gap-1",
1610
- children: conversations.map((conv) => /* @__PURE__ */ jsx9(ConversationItem, {
2044
+ children: conversations.map((conv) => /* @__PURE__ */ jsx12(ConversationItem, {
1611
2045
  conversation: conv,
1612
2046
  selected: conv.id === selectedConversationId,
1613
2047
  onSelect: () => onSelectConversation(conv.id),
@@ -1615,7 +2049,7 @@ function ChatSidebar({
1615
2049
  }, conv.id))
1616
2050
  })
1617
2051
  }),
1618
- selectedConversation && onUpdateConversation && /* @__PURE__ */ jsx9(ConversationMeta, {
2052
+ selectedConversation && onUpdateConversation && /* @__PURE__ */ jsx12(ConversationMeta, {
1619
2053
  conversation: selectedConversation,
1620
2054
  onUpdate: onUpdateConversation
1621
2055
  })
@@ -1626,13 +2060,13 @@ function ConversationMeta({
1626
2060
  conversation,
1627
2061
  onUpdate
1628
2062
  }) {
1629
- const [projectName, setProjectName] = React10.useState(conversation.projectName ?? "");
1630
- const [tagsStr, setTagsStr] = React10.useState(conversation.tags?.join(", ") ?? "");
1631
- React10.useEffect(() => {
2063
+ const [projectName, setProjectName] = React12.useState(conversation.projectName ?? "");
2064
+ const [tagsStr, setTagsStr] = React12.useState(conversation.tags?.join(", ") ?? "");
2065
+ React12.useEffect(() => {
1632
2066
  setProjectName(conversation.projectName ?? "");
1633
2067
  setTagsStr(conversation.tags?.join(", ") ?? "");
1634
2068
  }, [conversation.id, conversation.projectName, conversation.tags]);
1635
- const handleBlur = React10.useCallback(() => {
2069
+ const handleBlur = React12.useCallback(() => {
1636
2070
  const tags = tagsStr.split(",").map((t) => t.trim()).filter(Boolean);
1637
2071
  if (projectName !== (conversation.projectName ?? "") || JSON.stringify(tags) !== JSON.stringify(conversation.tags ?? [])) {
1638
2072
  onUpdate(conversation.id, {
@@ -1649,14 +2083,14 @@ function ConversationMeta({
1649
2083
  tagsStr,
1650
2084
  onUpdate
1651
2085
  ]);
1652
- return /* @__PURE__ */ jsxs9("div", {
2086
+ return /* @__PURE__ */ jsxs11("div", {
1653
2087
  className: "border-border shrink-0 border-t p-2",
1654
2088
  children: [
1655
- /* @__PURE__ */ jsx9("p", {
2089
+ /* @__PURE__ */ jsx12("p", {
1656
2090
  className: "text-muted-foreground mb-1 text-xs font-medium",
1657
2091
  children: "Project"
1658
2092
  }),
1659
- /* @__PURE__ */ jsx9("input", {
2093
+ /* @__PURE__ */ jsx12("input", {
1660
2094
  type: "text",
1661
2095
  value: projectName,
1662
2096
  onChange: (e) => setProjectName(e.target.value),
@@ -1664,11 +2098,11 @@ function ConversationMeta({
1664
2098
  placeholder: "Project name",
1665
2099
  className: "border-input bg-background mb-2 w-full rounded px-2 py-1 text-xs"
1666
2100
  }),
1667
- /* @__PURE__ */ jsx9("p", {
2101
+ /* @__PURE__ */ jsx12("p", {
1668
2102
  className: "text-muted-foreground mb-1 text-xs font-medium",
1669
2103
  children: "Tags"
1670
2104
  }),
1671
- /* @__PURE__ */ jsx9("input", {
2105
+ /* @__PURE__ */ jsx12("input", {
1672
2106
  type: "text",
1673
2107
  value: tagsStr,
1674
2108
  onChange: (e) => setTagsStr(e.target.value),
@@ -1680,10 +2114,10 @@ function ConversationMeta({
1680
2114
  });
1681
2115
  }
1682
2116
  // src/presentation/components/ChatWithSidebar.tsx
1683
- import * as React12 from "react";
2117
+ import * as React14 from "react";
1684
2118
 
1685
2119
  // src/presentation/hooks/useChat.tsx
1686
- import * as React11 from "react";
2120
+ import * as React13 from "react";
1687
2121
  import { tool as tool4 } from "ai";
1688
2122
  import { z as z4 } from "zod";
1689
2123
 
@@ -2757,16 +3191,16 @@ function useChat(options = {}) {
2757
3191
  mcpServers,
2758
3192
  agentMode
2759
3193
  } = options;
2760
- const [messages, setMessages] = React11.useState([]);
2761
- const [mcpTools, setMcpTools] = React11.useState(null);
2762
- const mcpCleanupRef = React11.useRef(null);
2763
- const [conversation, setConversation] = React11.useState(null);
2764
- const [isLoading, setIsLoading] = React11.useState(false);
2765
- const [error, setError] = React11.useState(null);
2766
- const [conversationId, setConversationId] = React11.useState(initialConversationId ?? null);
2767
- const abortControllerRef = React11.useRef(null);
2768
- const chatServiceRef = React11.useRef(null);
2769
- React11.useEffect(() => {
3194
+ const [messages, setMessages] = React13.useState([]);
3195
+ const [mcpTools, setMcpTools] = React13.useState(null);
3196
+ const mcpCleanupRef = React13.useRef(null);
3197
+ const [conversation, setConversation] = React13.useState(null);
3198
+ const [isLoading, setIsLoading] = React13.useState(false);
3199
+ const [error, setError] = React13.useState(null);
3200
+ const [conversationId, setConversationId] = React13.useState(initialConversationId ?? null);
3201
+ const abortControllerRef = React13.useRef(null);
3202
+ const chatServiceRef = React13.useRef(null);
3203
+ React13.useEffect(() => {
2770
3204
  if (!mcpServers?.length) {
2771
3205
  setMcpTools(null);
2772
3206
  return;
@@ -2798,7 +3232,7 @@ function useChat(options = {}) {
2798
3232
  setMcpTools(null);
2799
3233
  };
2800
3234
  }, [mcpServers]);
2801
- React11.useEffect(() => {
3235
+ React13.useEffect(() => {
2802
3236
  const chatProvider = createProvider({
2803
3237
  provider,
2804
3238
  model,
@@ -2835,7 +3269,7 @@ function useChat(options = {}) {
2835
3269
  surfacePlanConfig,
2836
3270
  mcpTools
2837
3271
  ]);
2838
- React11.useEffect(() => {
3272
+ React13.useEffect(() => {
2839
3273
  if (!conversationId || !chatServiceRef.current)
2840
3274
  return;
2841
3275
  const loadConversation = async () => {
@@ -2849,7 +3283,7 @@ function useChat(options = {}) {
2849
3283
  };
2850
3284
  loadConversation().catch(console.error);
2851
3285
  }, [conversationId]);
2852
- const sendMessage = React11.useCallback(async (content, attachments, opts) => {
3286
+ const sendMessage = React13.useCallback(async (content, attachments, opts) => {
2853
3287
  if (agentMode?.agent) {
2854
3288
  setIsLoading(true);
2855
3289
  setError(null);
@@ -3071,14 +3505,21 @@ function useChat(options = {}) {
3071
3505
  agentMode,
3072
3506
  store
3073
3507
  ]);
3074
- const clearConversation = React11.useCallback(() => {
3508
+ const clearConversation = React13.useCallback(() => {
3075
3509
  setMessages([]);
3076
3510
  setConversation(null);
3077
3511
  setConversationId(null);
3078
3512
  setError(null);
3079
3513
  }, []);
3080
- const regenerate = React11.useCallback(async () => {
3081
- const lastUserMessageIndex = messages.findLastIndex((m) => m.role === "user");
3514
+ const regenerate = React13.useCallback(async () => {
3515
+ let lastUserMessageIndex = -1;
3516
+ for (let i = messages.length - 1;i >= 0; i--) {
3517
+ const m = messages[i];
3518
+ if (m?.role === "user") {
3519
+ lastUserMessageIndex = i;
3520
+ break;
3521
+ }
3522
+ }
3082
3523
  if (lastUserMessageIndex === -1)
3083
3524
  return;
3084
3525
  const lastUserMessage = messages[lastUserMessageIndex];
@@ -3087,12 +3528,12 @@ function useChat(options = {}) {
3087
3528
  setMessages((prev) => prev.slice(0, lastUserMessageIndex + 1));
3088
3529
  await sendMessage(lastUserMessage.content, lastUserMessage.attachments);
3089
3530
  }, [messages, sendMessage]);
3090
- const stop = React11.useCallback(() => {
3531
+ const stop = React13.useCallback(() => {
3091
3532
  abortControllerRef.current?.abort();
3092
3533
  setIsLoading(false);
3093
3534
  }, []);
3094
3535
  const createNewConversation = clearConversation;
3095
- const editMessage = React11.useCallback(async (messageId, newContent) => {
3536
+ const editMessage = React13.useCallback(async (messageId, newContent) => {
3096
3537
  if (!chatServiceRef.current || !conversationId)
3097
3538
  return;
3098
3539
  const msg = messages.find((m) => m.id === messageId);
@@ -3107,7 +3548,7 @@ function useChat(options = {}) {
3107
3548
  }
3108
3549
  await sendMessage(newContent, undefined, { skipUserAppend: true });
3109
3550
  }, [conversationId, messages, sendMessage]);
3110
- const forkConversation = React11.useCallback(async (upToMessageId) => {
3551
+ const forkConversation = React13.useCallback(async (upToMessageId) => {
3111
3552
  if (!chatServiceRef.current)
3112
3553
  return null;
3113
3554
  const idToFork = conversationId ?? conversation?.id;
@@ -3123,7 +3564,7 @@ function useChat(options = {}) {
3123
3564
  return null;
3124
3565
  }
3125
3566
  }, [conversationId, conversation]);
3126
- const updateConversationFn = React11.useCallback(async (updates) => {
3567
+ const updateConversationFn = React13.useCallback(async (updates) => {
3127
3568
  if (!chatServiceRef.current || !conversationId)
3128
3569
  return null;
3129
3570
  const updated = await chatServiceRef.current.updateConversation(conversationId, updates);
@@ -3131,7 +3572,7 @@ function useChat(options = {}) {
3131
3572
  setConversation(updated);
3132
3573
  return updated;
3133
3574
  }, [conversationId]);
3134
- const addToolApprovalResponse = React11.useCallback((_toolCallId, _result) => {
3575
+ const addToolApprovalResponse = React13.useCallback((_toolCallId, _result) => {
3135
3576
  throw new Error(`addToolApprovalResponse: Tool approval requires server route with toUIMessageStreamResponse. ` + `Use createChatRoute and @ai-sdk/react useChat for tools with requireApproval.`);
3136
3577
  }, []);
3137
3578
  const hasApprovalTools = toolsDefs?.some((t) => t.requireApproval) ?? false;
@@ -3382,7 +3823,7 @@ function createLocalStorageConversationStore(storageKey) {
3382
3823
  }
3383
3824
 
3384
3825
  // src/presentation/components/ChatWithSidebar.tsx
3385
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3826
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3386
3827
  "use client";
3387
3828
  var defaultStore = createLocalStorageConversationStore();
3388
3829
  function ChatWithSidebar({
@@ -3393,10 +3834,15 @@ function ChatWithSidebar({
3393
3834
  thinkingLevel: initialThinkingLevel = "thinking",
3394
3835
  presentationRenderer,
3395
3836
  formRenderer,
3837
+ dataViewRenderer,
3838
+ components,
3839
+ suggestions,
3840
+ suggestionComponents,
3841
+ showSuggestionsWhenEmpty = false,
3396
3842
  ...useChatOptions
3397
3843
  }) {
3398
3844
  const effectiveStore = store;
3399
- const [thinkingLevel, setThinkingLevel] = React12.useState(initialThinkingLevel);
3845
+ const [thinkingLevel, setThinkingLevel] = React14.useState(initialThinkingLevel);
3400
3846
  const chat = useChat({
3401
3847
  ...useChatOptions,
3402
3848
  store: effectiveStore,
@@ -3414,13 +3860,16 @@ function ChatWithSidebar({
3414
3860
  updateConversation
3415
3861
  } = chat;
3416
3862
  const selectedConversationId = conversation?.id ?? null;
3417
- const handleSelectConversation = React12.useCallback((id) => {
3863
+ const handleSelectConversation = React14.useCallback((id) => {
3418
3864
  setConversationId(id);
3419
3865
  }, [setConversationId]);
3420
- return /* @__PURE__ */ jsxs10("div", {
3866
+ const handleSuggestionClick = React14.useCallback((suggestion) => {
3867
+ sendMessage(suggestion);
3868
+ }, [sendMessage]);
3869
+ return /* @__PURE__ */ jsxs12("div", {
3421
3870
  className: className ?? "flex h-full w-full",
3422
3871
  children: [
3423
- /* @__PURE__ */ jsx10(ChatSidebar, {
3872
+ /* @__PURE__ */ jsx13(ChatSidebar, {
3424
3873
  store: effectiveStore,
3425
3874
  selectedConversationId,
3426
3875
  onSelectConversation: handleSelectConversation,
@@ -3434,9 +3883,9 @@ function ChatWithSidebar({
3434
3883
  }
3435
3884
  } : undefined
3436
3885
  }),
3437
- /* @__PURE__ */ jsx10("div", {
3886
+ /* @__PURE__ */ jsx13("div", {
3438
3887
  className: "flex min-w-0 flex-1 flex-col",
3439
- children: /* @__PURE__ */ jsx10(ChatWithExport, {
3888
+ children: /* @__PURE__ */ jsx13(ChatWithExport, {
3440
3889
  messages,
3441
3890
  conversation,
3442
3891
  onCreateNew: createNewConversation,
@@ -3446,7 +3895,13 @@ function ChatWithSidebar({
3446
3895
  onThinkingLevelChange: setThinkingLevel,
3447
3896
  presentationRenderer,
3448
3897
  formRenderer,
3449
- children: /* @__PURE__ */ jsx10(ChatInput, {
3898
+ dataViewRenderer,
3899
+ components,
3900
+ suggestions,
3901
+ onSuggestionClick: handleSuggestionClick,
3902
+ suggestionComponents,
3903
+ showSuggestionsWhenEmpty,
3904
+ children: /* @__PURE__ */ jsx13(ChatInput, {
3450
3905
  onSend: (content, att) => sendMessage(content, att),
3451
3906
  disabled: isLoading,
3452
3907
  isLoading
@@ -3457,9 +3912,9 @@ function ChatWithSidebar({
3457
3912
  });
3458
3913
  }
3459
3914
  // src/presentation/components/ModelPicker.tsx
3460
- import * as React13 from "react";
3461
- import { cn as cn7 } from "@contractspec/lib.ui-kit-web/ui/utils";
3462
- import { Button as Button6 } from "@contractspec/lib.design-system";
3915
+ import * as React15 from "react";
3916
+ import { cn as cn10 } from "@contractspec/lib.ui-kit-web/ui/utils";
3917
+ import { Button as Button7 } from "@contractspec/lib.design-system";
3463
3918
  import {
3464
3919
  Select as Select2,
3465
3920
  SelectContent as SelectContent2,
@@ -3473,22 +3928,22 @@ import { Bot as Bot2, Cloud, Cpu, Sparkles } from "lucide-react";
3473
3928
  import {
3474
3929
  getModelsForProvider
3475
3930
  } from "@contractspec/lib.ai-providers";
3476
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3931
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3477
3932
  "use client";
3478
3933
  var PROVIDER_ICONS = {
3479
- ollama: /* @__PURE__ */ jsx11(Cpu, {
3934
+ ollama: /* @__PURE__ */ jsx14(Cpu, {
3480
3935
  className: "h-4 w-4"
3481
3936
  }),
3482
- openai: /* @__PURE__ */ jsx11(Bot2, {
3937
+ openai: /* @__PURE__ */ jsx14(Bot2, {
3483
3938
  className: "h-4 w-4"
3484
3939
  }),
3485
- anthropic: /* @__PURE__ */ jsx11(Sparkles, {
3940
+ anthropic: /* @__PURE__ */ jsx14(Sparkles, {
3486
3941
  className: "h-4 w-4"
3487
3942
  }),
3488
- mistral: /* @__PURE__ */ jsx11(Cloud, {
3943
+ mistral: /* @__PURE__ */ jsx14(Cloud, {
3489
3944
  className: "h-4 w-4"
3490
3945
  }),
3491
- gemini: /* @__PURE__ */ jsx11(Sparkles, {
3946
+ gemini: /* @__PURE__ */ jsx14(Sparkles, {
3492
3947
  className: "h-4 w-4"
3493
3948
  })
3494
3949
  };
@@ -3520,7 +3975,7 @@ function ModelPicker({
3520
3975
  ];
3521
3976
  const models = getModelsForProvider(value.provider);
3522
3977
  const selectedModel = models.find((m) => m.id === value.model);
3523
- const handleProviderChange = React13.useCallback((providerName) => {
3978
+ const handleProviderChange = React15.useCallback((providerName) => {
3524
3979
  const provider = providerName;
3525
3980
  const providerInfo = providers.find((p) => p.provider === provider);
3526
3981
  const providerModels = getModelsForProvider(provider);
@@ -3531,33 +3986,33 @@ function ModelPicker({
3531
3986
  mode: providerInfo?.mode ?? "byok"
3532
3987
  });
3533
3988
  }, [onChange, providers]);
3534
- const handleModelChange = React13.useCallback((modelId) => {
3989
+ const handleModelChange = React15.useCallback((modelId) => {
3535
3990
  onChange({
3536
3991
  ...value,
3537
3992
  model: modelId
3538
3993
  });
3539
3994
  }, [onChange, value]);
3540
3995
  if (compact) {
3541
- return /* @__PURE__ */ jsxs11("div", {
3542
- className: cn7("flex items-center gap-2", className),
3996
+ return /* @__PURE__ */ jsxs13("div", {
3997
+ className: cn10("flex items-center gap-2", className),
3543
3998
  children: [
3544
- /* @__PURE__ */ jsxs11(Select2, {
3999
+ /* @__PURE__ */ jsxs13(Select2, {
3545
4000
  value: value.provider,
3546
4001
  onValueChange: handleProviderChange,
3547
4002
  children: [
3548
- /* @__PURE__ */ jsx11(SelectTrigger2, {
4003
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
3549
4004
  className: "w-[140px]",
3550
- children: /* @__PURE__ */ jsx11(SelectValue2, {})
4005
+ children: /* @__PURE__ */ jsx14(SelectValue2, {})
3551
4006
  }),
3552
- /* @__PURE__ */ jsx11(SelectContent2, {
3553
- children: providers.map((p) => /* @__PURE__ */ jsx11(SelectItem2, {
4007
+ /* @__PURE__ */ jsx14(SelectContent2, {
4008
+ children: providers.map((p) => /* @__PURE__ */ jsx14(SelectItem2, {
3554
4009
  value: p.provider,
3555
4010
  disabled: !p.available,
3556
- children: /* @__PURE__ */ jsxs11("div", {
4011
+ children: /* @__PURE__ */ jsxs13("div", {
3557
4012
  className: "flex items-center gap-2",
3558
4013
  children: [
3559
4014
  PROVIDER_ICONS[p.provider],
3560
- /* @__PURE__ */ jsx11("span", {
4015
+ /* @__PURE__ */ jsx14("span", {
3561
4016
  children: PROVIDER_NAMES[p.provider]
3562
4017
  })
3563
4018
  ]
@@ -3566,16 +4021,16 @@ function ModelPicker({
3566
4021
  })
3567
4022
  ]
3568
4023
  }),
3569
- /* @__PURE__ */ jsxs11(Select2, {
4024
+ /* @__PURE__ */ jsxs13(Select2, {
3570
4025
  value: value.model,
3571
4026
  onValueChange: handleModelChange,
3572
4027
  children: [
3573
- /* @__PURE__ */ jsx11(SelectTrigger2, {
4028
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
3574
4029
  className: "w-[160px]",
3575
- children: /* @__PURE__ */ jsx11(SelectValue2, {})
4030
+ children: /* @__PURE__ */ jsx14(SelectValue2, {})
3576
4031
  }),
3577
- /* @__PURE__ */ jsx11(SelectContent2, {
3578
- children: models.map((m) => /* @__PURE__ */ jsx11(SelectItem2, {
4032
+ /* @__PURE__ */ jsx14(SelectContent2, {
4033
+ children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
3579
4034
  value: m.id,
3580
4035
  children: m.name
3581
4036
  }, m.id))
@@ -3585,32 +4040,32 @@ function ModelPicker({
3585
4040
  ]
3586
4041
  });
3587
4042
  }
3588
- return /* @__PURE__ */ jsxs11("div", {
3589
- className: cn7("flex flex-col gap-3", className),
4043
+ return /* @__PURE__ */ jsxs13("div", {
4044
+ className: cn10("flex flex-col gap-3", className),
3590
4045
  children: [
3591
- /* @__PURE__ */ jsxs11("div", {
4046
+ /* @__PURE__ */ jsxs13("div", {
3592
4047
  className: "flex flex-col gap-1.5",
3593
4048
  children: [
3594
- /* @__PURE__ */ jsx11(Label2, {
4049
+ /* @__PURE__ */ jsx14(Label2, {
3595
4050
  htmlFor: "provider-selection",
3596
4051
  className: "text-sm font-medium",
3597
4052
  children: "Provider"
3598
4053
  }),
3599
- /* @__PURE__ */ jsx11("div", {
4054
+ /* @__PURE__ */ jsx14("div", {
3600
4055
  className: "flex flex-wrap gap-2",
3601
4056
  id: "provider-selection",
3602
- children: providers.map((p) => /* @__PURE__ */ jsxs11(Button6, {
4057
+ children: providers.map((p) => /* @__PURE__ */ jsxs13(Button7, {
3603
4058
  variant: value.provider === p.provider ? "default" : "outline",
3604
4059
  size: "sm",
3605
4060
  onPress: () => p.available && handleProviderChange(p.provider),
3606
4061
  disabled: !p.available,
3607
- className: cn7(!p.available && "opacity-50"),
4062
+ className: cn10(!p.available && "opacity-50"),
3608
4063
  children: [
3609
4064
  PROVIDER_ICONS[p.provider],
3610
- /* @__PURE__ */ jsx11("span", {
4065
+ /* @__PURE__ */ jsx14("span", {
3611
4066
  children: PROVIDER_NAMES[p.provider]
3612
4067
  }),
3613
- /* @__PURE__ */ jsx11(Badge, {
4068
+ /* @__PURE__ */ jsx14(Badge, {
3614
4069
  variant: MODE_BADGES[p.mode].variant,
3615
4070
  className: "ml-1",
3616
4071
  children: MODE_BADGES[p.mode].label
@@ -3620,46 +4075,46 @@ function ModelPicker({
3620
4075
  })
3621
4076
  ]
3622
4077
  }),
3623
- /* @__PURE__ */ jsxs11("div", {
4078
+ /* @__PURE__ */ jsxs13("div", {
3624
4079
  className: "flex flex-col gap-1.5",
3625
4080
  children: [
3626
- /* @__PURE__ */ jsx11(Label2, {
4081
+ /* @__PURE__ */ jsx14(Label2, {
3627
4082
  htmlFor: "model-picker",
3628
4083
  className: "text-sm font-medium",
3629
4084
  children: "Model"
3630
4085
  }),
3631
- /* @__PURE__ */ jsxs11(Select2, {
4086
+ /* @__PURE__ */ jsxs13(Select2, {
3632
4087
  name: "model-picker",
3633
4088
  value: value.model,
3634
4089
  onValueChange: handleModelChange,
3635
4090
  children: [
3636
- /* @__PURE__ */ jsx11(SelectTrigger2, {
3637
- children: /* @__PURE__ */ jsx11(SelectValue2, {
4091
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
4092
+ children: /* @__PURE__ */ jsx14(SelectValue2, {
3638
4093
  placeholder: "Select a model"
3639
4094
  })
3640
4095
  }),
3641
- /* @__PURE__ */ jsx11(SelectContent2, {
3642
- children: models.map((m) => /* @__PURE__ */ jsx11(SelectItem2, {
4096
+ /* @__PURE__ */ jsx14(SelectContent2, {
4097
+ children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
3643
4098
  value: m.id,
3644
- children: /* @__PURE__ */ jsxs11("div", {
4099
+ children: /* @__PURE__ */ jsxs13("div", {
3645
4100
  className: "flex items-center gap-2",
3646
4101
  children: [
3647
- /* @__PURE__ */ jsx11("span", {
4102
+ /* @__PURE__ */ jsx14("span", {
3648
4103
  children: m.name
3649
4104
  }),
3650
- /* @__PURE__ */ jsxs11("span", {
4105
+ /* @__PURE__ */ jsxs13("span", {
3651
4106
  className: "text-muted-foreground text-xs",
3652
4107
  children: [
3653
4108
  Math.round(m.contextWindow / 1000),
3654
4109
  "K"
3655
4110
  ]
3656
4111
  }),
3657
- m.capabilities.vision && /* @__PURE__ */ jsx11(Badge, {
4112
+ m.capabilities.vision && /* @__PURE__ */ jsx14(Badge, {
3658
4113
  variant: "outline",
3659
4114
  className: "text-xs",
3660
4115
  children: "Vision"
3661
4116
  }),
3662
- m.capabilities.reasoning && /* @__PURE__ */ jsx11(Badge, {
4117
+ m.capabilities.reasoning && /* @__PURE__ */ jsx14(Badge, {
3663
4118
  variant: "outline",
3664
4119
  className: "text-xs",
3665
4120
  children: "Reasoning"
@@ -3672,23 +4127,23 @@ function ModelPicker({
3672
4127
  })
3673
4128
  ]
3674
4129
  }),
3675
- selectedModel && /* @__PURE__ */ jsxs11("div", {
4130
+ selectedModel && /* @__PURE__ */ jsxs13("div", {
3676
4131
  className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
3677
4132
  children: [
3678
- /* @__PURE__ */ jsxs11("span", {
4133
+ /* @__PURE__ */ jsxs13("span", {
3679
4134
  children: [
3680
4135
  "Context: ",
3681
4136
  Math.round(selectedModel.contextWindow / 1000),
3682
4137
  "K tokens"
3683
4138
  ]
3684
4139
  }),
3685
- selectedModel.capabilities.vision && /* @__PURE__ */ jsx11("span", {
4140
+ selectedModel.capabilities.vision && /* @__PURE__ */ jsx14("span", {
3686
4141
  children: "\u2022 Vision"
3687
4142
  }),
3688
- selectedModel.capabilities.tools && /* @__PURE__ */ jsx11("span", {
4143
+ selectedModel.capabilities.tools && /* @__PURE__ */ jsx14("span", {
3689
4144
  children: "\u2022 Tools"
3690
4145
  }),
3691
- selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx11("span", {
4146
+ selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx14("span", {
3692
4147
  children: "\u2022 Reasoning"
3693
4148
  })
3694
4149
  ]
@@ -3697,7 +4152,7 @@ function ModelPicker({
3697
4152
  });
3698
4153
  }
3699
4154
  // src/presentation/components/ContextIndicator.tsx
3700
- import { cn as cn8 } from "@contractspec/lib.ui-kit-web/ui/utils";
4155
+ import { cn as cn11 } from "@contractspec/lib.ui-kit-web/ui/utils";
3701
4156
  import { Badge as Badge2 } from "@contractspec/lib.ui-kit-web/ui/badge";
3702
4157
  import {
3703
4158
  Tooltip,
@@ -3706,7 +4161,7 @@ import {
3706
4161
  TooltipTrigger
3707
4162
  } from "@contractspec/lib.ui-kit-web/ui/tooltip";
3708
4163
  import { FolderOpen, FileCode, Zap, Info } from "lucide-react";
3709
- import { jsx as jsx12, jsxs as jsxs12, Fragment as Fragment6 } from "react/jsx-runtime";
4164
+ import { jsx as jsx15, jsxs as jsxs14, Fragment as Fragment7 } from "react/jsx-runtime";
3710
4165
  "use client";
3711
4166
  function ContextIndicator({
3712
4167
  summary,
@@ -3715,51 +4170,51 @@ function ContextIndicator({
3715
4170
  showDetails = true
3716
4171
  }) {
3717
4172
  if (!summary && !active) {
3718
- return /* @__PURE__ */ jsxs12("div", {
3719
- className: cn8("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
4173
+ return /* @__PURE__ */ jsxs14("div", {
4174
+ className: cn11("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
3720
4175
  children: [
3721
- /* @__PURE__ */ jsx12(Info, {
4176
+ /* @__PURE__ */ jsx15(Info, {
3722
4177
  className: "h-4 w-4"
3723
4178
  }),
3724
- /* @__PURE__ */ jsx12("span", {
4179
+ /* @__PURE__ */ jsx15("span", {
3725
4180
  children: "No workspace context"
3726
4181
  })
3727
4182
  ]
3728
4183
  });
3729
4184
  }
3730
- const content = /* @__PURE__ */ jsxs12("div", {
3731
- className: cn8("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
4185
+ const content = /* @__PURE__ */ jsxs14("div", {
4186
+ className: cn11("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
3732
4187
  children: [
3733
- /* @__PURE__ */ jsxs12(Badge2, {
4188
+ /* @__PURE__ */ jsxs14(Badge2, {
3734
4189
  variant: active ? "default" : "secondary",
3735
4190
  className: "flex items-center gap-1",
3736
4191
  children: [
3737
- /* @__PURE__ */ jsx12(Zap, {
4192
+ /* @__PURE__ */ jsx15(Zap, {
3738
4193
  className: "h-3 w-3"
3739
4194
  }),
3740
4195
  "Context"
3741
4196
  ]
3742
4197
  }),
3743
- summary && showDetails && /* @__PURE__ */ jsxs12(Fragment6, {
4198
+ summary && showDetails && /* @__PURE__ */ jsxs14(Fragment7, {
3744
4199
  children: [
3745
- /* @__PURE__ */ jsxs12("div", {
4200
+ /* @__PURE__ */ jsxs14("div", {
3746
4201
  className: "flex items-center gap-1 text-xs",
3747
4202
  children: [
3748
- /* @__PURE__ */ jsx12(FolderOpen, {
4203
+ /* @__PURE__ */ jsx15(FolderOpen, {
3749
4204
  className: "h-3.5 w-3.5"
3750
4205
  }),
3751
- /* @__PURE__ */ jsx12("span", {
4206
+ /* @__PURE__ */ jsx15("span", {
3752
4207
  children: summary.name
3753
4208
  })
3754
4209
  ]
3755
4210
  }),
3756
- /* @__PURE__ */ jsxs12("div", {
4211
+ /* @__PURE__ */ jsxs14("div", {
3757
4212
  className: "flex items-center gap-1 text-xs",
3758
4213
  children: [
3759
- /* @__PURE__ */ jsx12(FileCode, {
4214
+ /* @__PURE__ */ jsx15(FileCode, {
3760
4215
  className: "h-3.5 w-3.5"
3761
4216
  }),
3762
- /* @__PURE__ */ jsxs12("span", {
4217
+ /* @__PURE__ */ jsxs14("span", {
3763
4218
  children: [
3764
4219
  summary.specs.total,
3765
4220
  " specs"
@@ -3774,77 +4229,77 @@ function ContextIndicator({
3774
4229
  if (!summary) {
3775
4230
  return content;
3776
4231
  }
3777
- return /* @__PURE__ */ jsx12(TooltipProvider, {
3778
- children: /* @__PURE__ */ jsxs12(Tooltip, {
4232
+ return /* @__PURE__ */ jsx15(TooltipProvider, {
4233
+ children: /* @__PURE__ */ jsxs14(Tooltip, {
3779
4234
  children: [
3780
- /* @__PURE__ */ jsx12(TooltipTrigger, {
4235
+ /* @__PURE__ */ jsx15(TooltipTrigger, {
3781
4236
  asChild: true,
3782
4237
  children: content
3783
4238
  }),
3784
- /* @__PURE__ */ jsx12(TooltipContent, {
4239
+ /* @__PURE__ */ jsx15(TooltipContent, {
3785
4240
  side: "bottom",
3786
4241
  className: "max-w-[300px]",
3787
- children: /* @__PURE__ */ jsxs12("div", {
4242
+ children: /* @__PURE__ */ jsxs14("div", {
3788
4243
  className: "flex flex-col gap-2 text-sm",
3789
4244
  children: [
3790
- /* @__PURE__ */ jsx12("div", {
4245
+ /* @__PURE__ */ jsx15("div", {
3791
4246
  className: "font-medium",
3792
4247
  children: summary.name
3793
4248
  }),
3794
- /* @__PURE__ */ jsx12("div", {
4249
+ /* @__PURE__ */ jsx15("div", {
3795
4250
  className: "text-muted-foreground text-xs",
3796
4251
  children: summary.path
3797
4252
  }),
3798
- /* @__PURE__ */ jsx12("div", {
4253
+ /* @__PURE__ */ jsx15("div", {
3799
4254
  className: "border-t pt-2",
3800
- children: /* @__PURE__ */ jsxs12("div", {
4255
+ children: /* @__PURE__ */ jsxs14("div", {
3801
4256
  className: "grid grid-cols-2 gap-1 text-xs",
3802
4257
  children: [
3803
- /* @__PURE__ */ jsx12("span", {
4258
+ /* @__PURE__ */ jsx15("span", {
3804
4259
  children: "Commands:"
3805
4260
  }),
3806
- /* @__PURE__ */ jsx12("span", {
4261
+ /* @__PURE__ */ jsx15("span", {
3807
4262
  className: "text-right",
3808
4263
  children: summary.specs.commands
3809
4264
  }),
3810
- /* @__PURE__ */ jsx12("span", {
4265
+ /* @__PURE__ */ jsx15("span", {
3811
4266
  children: "Queries:"
3812
4267
  }),
3813
- /* @__PURE__ */ jsx12("span", {
4268
+ /* @__PURE__ */ jsx15("span", {
3814
4269
  className: "text-right",
3815
4270
  children: summary.specs.queries
3816
4271
  }),
3817
- /* @__PURE__ */ jsx12("span", {
4272
+ /* @__PURE__ */ jsx15("span", {
3818
4273
  children: "Events:"
3819
4274
  }),
3820
- /* @__PURE__ */ jsx12("span", {
4275
+ /* @__PURE__ */ jsx15("span", {
3821
4276
  className: "text-right",
3822
4277
  children: summary.specs.events
3823
4278
  }),
3824
- /* @__PURE__ */ jsx12("span", {
4279
+ /* @__PURE__ */ jsx15("span", {
3825
4280
  children: "Presentations:"
3826
4281
  }),
3827
- /* @__PURE__ */ jsx12("span", {
4282
+ /* @__PURE__ */ jsx15("span", {
3828
4283
  className: "text-right",
3829
4284
  children: summary.specs.presentations
3830
4285
  })
3831
4286
  ]
3832
4287
  })
3833
4288
  }),
3834
- /* @__PURE__ */ jsxs12("div", {
4289
+ /* @__PURE__ */ jsxs14("div", {
3835
4290
  className: "border-t pt-2 text-xs",
3836
4291
  children: [
3837
- /* @__PURE__ */ jsxs12("span", {
4292
+ /* @__PURE__ */ jsxs14("span", {
3838
4293
  children: [
3839
4294
  summary.files.total,
3840
4295
  " files"
3841
4296
  ]
3842
4297
  }),
3843
- /* @__PURE__ */ jsx12("span", {
4298
+ /* @__PURE__ */ jsx15("span", {
3844
4299
  className: "mx-1",
3845
4300
  children: "\u2022"
3846
4301
  }),
3847
- /* @__PURE__ */ jsxs12("span", {
4302
+ /* @__PURE__ */ jsxs14("span", {
3848
4303
  children: [
3849
4304
  summary.files.specFiles,
3850
4305
  " spec files"
@@ -3859,11 +4314,108 @@ function ContextIndicator({
3859
4314
  })
3860
4315
  });
3861
4316
  }
4317
+ // src/presentation/components/ChainOfThought.tsx
4318
+ import {
4319
+ Collapsible as Collapsible3,
4320
+ CollapsibleContent as CollapsibleContent3,
4321
+ CollapsibleTrigger as CollapsibleTrigger3
4322
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
4323
+ import { cn as cn12 } from "@contractspec/lib.ui-kit-web/ui/utils";
4324
+ import { ChevronDown, Dot } from "lucide-react";
4325
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
4326
+ "use client";
4327
+ function ChainOfThought({
4328
+ children,
4329
+ open,
4330
+ defaultOpen = false,
4331
+ onOpenChange,
4332
+ className
4333
+ }) {
4334
+ return /* @__PURE__ */ jsx16(Collapsible3, {
4335
+ open,
4336
+ defaultOpen,
4337
+ onOpenChange,
4338
+ className: cn12("group/cot mt-2", className),
4339
+ children
4340
+ });
4341
+ }
4342
+ function ChainOfThoughtHeader({
4343
+ children = "Chain of Thought",
4344
+ className
4345
+ }) {
4346
+ return /* @__PURE__ */ jsxs15(CollapsibleTrigger3, {
4347
+ className: cn12("hover:bg-muted flex w-full cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors", className),
4348
+ children: [
4349
+ /* @__PURE__ */ jsx16(ChevronDown, {
4350
+ className: "text-muted-foreground h-4 w-4 shrink-0 transition-transform group-data-[state=open]/cot:rotate-180"
4351
+ }),
4352
+ children
4353
+ ]
4354
+ });
4355
+ }
4356
+ function ChainOfThoughtStep({
4357
+ label,
4358
+ description,
4359
+ status = "complete",
4360
+ icon: Icon = Dot,
4361
+ children,
4362
+ className
4363
+ }) {
4364
+ return /* @__PURE__ */ jsxs15("div", {
4365
+ className: cn12("border-border flex gap-3 border-b py-2 last:border-b-0", className),
4366
+ children: [
4367
+ /* @__PURE__ */ jsx16("div", {
4368
+ className: cn12("mt-1.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full", status === "complete" && "bg-green-500/20 text-green-700 dark:text-green-400", status === "active" && "bg-blue-500/20 text-blue-700 dark:text-blue-400", status === "pending" && "bg-muted text-muted-foreground"),
4369
+ children: /* @__PURE__ */ jsx16(Icon, {
4370
+ className: "h-3 w-3"
4371
+ })
4372
+ }),
4373
+ /* @__PURE__ */ jsxs15("div", {
4374
+ className: "min-w-0 flex-1",
4375
+ children: [
4376
+ /* @__PURE__ */ jsx16("p", {
4377
+ className: "font-medium",
4378
+ children: label
4379
+ }),
4380
+ description && /* @__PURE__ */ jsx16("p", {
4381
+ className: "text-muted-foreground mt-0.5 text-xs",
4382
+ children: description
4383
+ }),
4384
+ children && /* @__PURE__ */ jsx16("div", {
4385
+ className: "mt-2",
4386
+ children
4387
+ })
4388
+ ]
4389
+ })
4390
+ ]
4391
+ });
4392
+ }
4393
+ function ChainOfThoughtContent({
4394
+ children,
4395
+ className
4396
+ }) {
4397
+ return /* @__PURE__ */ jsx16(CollapsibleContent3, {
4398
+ children: /* @__PURE__ */ jsx16("div", {
4399
+ className: cn12("bg-muted border-border mt-1 rounded-md border px-3 py-2", className),
4400
+ children
4401
+ })
4402
+ });
4403
+ }
3862
4404
  export {
3863
4405
  isPresentationToolResult,
3864
4406
  isFormToolResult,
4407
+ isDataViewToolResult,
3865
4408
  ToolResultRenderer,
3866
4409
  ThinkingLevelPicker,
4410
+ Suggestions,
4411
+ Suggestion,
4412
+ SourcesTrigger,
4413
+ SourcesContent,
4414
+ Sources,
4415
+ Source,
4416
+ ReasoningTrigger,
4417
+ ReasoningContent,
4418
+ Reasoning,
3867
4419
  ModelPicker,
3868
4420
  ContextIndicator,
3869
4421
  CodePreview,
@@ -3873,5 +4425,9 @@ export {
3873
4425
  ChatMessage,
3874
4426
  ChatInput,
3875
4427
  ChatExportToolbar,
3876
- ChatContainer
4428
+ ChatContainer,
4429
+ ChainOfThoughtStep,
4430
+ ChainOfThoughtHeader,
4431
+ ChainOfThoughtContent,
4432
+ ChainOfThought
3877
4433
  };