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