@contractspec/module.ai-chat 4.1.5 → 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
package/dist/index.js CHANGED
@@ -479,8 +479,8 @@ function ChatContainer({
479
479
  });
480
480
  }
481
481
  // src/presentation/components/ChatMessage.tsx
482
- import * as React3 from "react";
483
- import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
482
+ import * as React4 from "react";
483
+ import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
484
484
  import { Avatar, AvatarFallback } from "@contractspec/lib.ui-kit-web/ui/avatar";
485
485
  import { Skeleton } from "@contractspec/lib.ui-kit-web/ui/skeleton";
486
486
  import {
@@ -489,7 +489,6 @@ import {
489
489
  AlertCircle,
490
490
  Copy as Copy2,
491
491
  Check as Check2,
492
- ExternalLink,
493
492
  Wrench,
494
493
  Pencil,
495
494
  X
@@ -643,22 +642,98 @@ function CodePreview({
643
642
  // src/presentation/components/ToolResultRenderer.tsx
644
643
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
645
644
  "use client";
645
+ function isUIMessageLike(result) {
646
+ return typeof result === "object" && result !== null && "parts" in result && Array.isArray(result.parts);
647
+ }
646
648
  function isPresentationToolResult(result) {
647
649
  return typeof result === "object" && result !== null && "presentationKey" in result && typeof result.presentationKey === "string";
648
650
  }
649
651
  function isFormToolResult(result) {
650
652
  return typeof result === "object" && result !== null && "formKey" in result && typeof result.formKey === "string";
651
653
  }
654
+ function isDataViewToolResult(result) {
655
+ return typeof result === "object" && result !== null && "dataViewKey" in result && typeof result.dataViewKey === "string";
656
+ }
657
+ function UIMessagePartRenderer({
658
+ part,
659
+ presentationRenderer,
660
+ formRenderer,
661
+ dataViewRenderer,
662
+ depth = 0
663
+ }) {
664
+ if (part === null || part === undefined)
665
+ return null;
666
+ const p = part;
667
+ if (p.type === "text" && typeof p.text === "string") {
668
+ return /* @__PURE__ */ jsx3("p", {
669
+ className: "text-sm whitespace-pre-wrap",
670
+ children: p.text
671
+ }, depth);
672
+ }
673
+ if (p.type && String(p.type).startsWith("tool-") && p.output) {
674
+ const output = p.output;
675
+ if (isUIMessageLike(output)) {
676
+ return /* @__PURE__ */ jsx3("div", {
677
+ className: "border-border ml-2 border-l-2 pl-2",
678
+ children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
679
+ parts: output.parts ?? [],
680
+ presentationRenderer,
681
+ formRenderer,
682
+ dataViewRenderer,
683
+ depth: depth + 1
684
+ })
685
+ }, depth);
686
+ }
687
+ return /* @__PURE__ */ jsx3("pre", {
688
+ className: "bg-background overflow-x-auto rounded p-2 text-xs",
689
+ children: typeof output === "object" ? JSON.stringify(output, null, 2) : String(output)
690
+ }, depth);
691
+ }
692
+ return null;
693
+ }
694
+ function UIMessagePartsRenderer({
695
+ parts,
696
+ presentationRenderer,
697
+ formRenderer,
698
+ dataViewRenderer,
699
+ depth = 0
700
+ }) {
701
+ if (parts.length === 0)
702
+ return null;
703
+ return /* @__PURE__ */ jsx3("div", {
704
+ className: "space-y-2",
705
+ children: parts.map((part, i) => /* @__PURE__ */ jsx3(UIMessagePartRenderer, {
706
+ part,
707
+ presentationRenderer,
708
+ formRenderer,
709
+ dataViewRenderer,
710
+ depth
711
+ }, `${depth}-${i}`))
712
+ });
713
+ }
652
714
  function ToolResultRenderer({
653
- toolName,
715
+ toolName: _toolName,
654
716
  result,
655
717
  presentationRenderer,
656
718
  formRenderer,
657
- showRawFallback = true
719
+ dataViewRenderer,
720
+ showRawFallback = true,
721
+ renderNestedUIMessage = true
658
722
  }) {
659
723
  if (result === undefined || result === null) {
660
724
  return null;
661
725
  }
726
+ if (renderNestedUIMessage && isUIMessageLike(result) && (result.parts?.length ?? 0) > 0) {
727
+ return /* @__PURE__ */ jsx3("div", {
728
+ className: "border-border bg-background/50 mt-2 rounded-md border p-3",
729
+ children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
730
+ parts: result.parts ?? [],
731
+ presentationRenderer,
732
+ formRenderer,
733
+ dataViewRenderer
734
+ })
735
+ });
736
+ }
662
737
  if (isPresentationToolResult(result) && presentationRenderer) {
663
738
  const rendered = presentationRenderer(result.presentationKey, result.data);
664
739
  if (rendered != null) {
@@ -677,6 +752,15 @@ function ToolResultRenderer({
677
752
  });
678
753
  }
679
754
  }
755
+ if (isDataViewToolResult(result) && dataViewRenderer) {
756
+ const rendered = dataViewRenderer(result.dataViewKey, result.items);
757
+ if (rendered != null) {
758
+ return /* @__PURE__ */ jsx3("div", {
759
+ className: "border-border bg-background/50 mt-2 rounded-md border p-3",
760
+ children: rendered
761
+ });
762
+ }
763
+ }
680
764
  if (!showRawFallback) {
681
765
  return null;
682
766
  }
@@ -694,8 +778,158 @@ function ToolResultRenderer({
694
778
  });
695
779
  }
696
780
 
781
+ // src/presentation/components/Reasoning.tsx
782
+ import * as React3 from "react";
783
+ import {
784
+ Collapsible,
785
+ CollapsibleContent,
786
+ CollapsibleTrigger
787
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
788
+ import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
789
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
790
+ "use client";
791
+ function Reasoning({
792
+ isStreaming = false,
793
+ open,
794
+ defaultOpen = false,
795
+ onOpenChange,
796
+ children,
797
+ className
798
+ }) {
799
+ const [internalOpen, setInternalOpen] = React3.useState(defaultOpen);
800
+ const prevStreamingRef = React3.useRef(isStreaming);
801
+ const isControlled = open !== undefined;
802
+ React3.useEffect(() => {
803
+ if (isStreaming) {
804
+ if (isControlled) {
805
+ onOpenChange?.(true);
806
+ } else {
807
+ setInternalOpen(true);
808
+ }
809
+ } else if (prevStreamingRef.current) {
810
+ if (isControlled) {
811
+ onOpenChange?.(false);
812
+ } else {
813
+ setInternalOpen(false);
814
+ }
815
+ }
816
+ prevStreamingRef.current = isStreaming;
817
+ }, [isStreaming, isControlled, onOpenChange]);
818
+ const handleOpenChange = React3.useCallback((next) => {
819
+ if (isControlled) {
820
+ onOpenChange?.(next);
821
+ } else {
822
+ setInternalOpen(next);
823
+ }
824
+ }, [isControlled, onOpenChange]);
825
+ return /* @__PURE__ */ jsx4(Collapsible, {
826
+ open: isControlled ? open : internalOpen,
827
+ onOpenChange: handleOpenChange,
828
+ className: cn3("w-full", className),
829
+ children
830
+ });
831
+ }
832
+ function ReasoningTrigger({
833
+ children,
834
+ isStreaming = false,
835
+ className
836
+ }) {
837
+ return /* @__PURE__ */ jsxs4(CollapsibleTrigger, {
838
+ 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),
839
+ children: [
840
+ isStreaming && /* @__PURE__ */ jsx4("span", {
841
+ className: "bg-primary h-1.5 w-1.5 animate-pulse rounded-full",
842
+ "aria-hidden": true
843
+ }),
844
+ children ?? (isStreaming ? "Thinking..." : "View reasoning")
845
+ ]
846
+ });
847
+ }
848
+ function ReasoningContent({
849
+ children,
850
+ className
851
+ }) {
852
+ return /* @__PURE__ */ jsx4(CollapsibleContent, {
853
+ children: /* @__PURE__ */ jsx4("div", {
854
+ className: cn3("text-muted-foreground bg-muted mt-1 rounded-md p-2 text-sm", className),
855
+ children: /* @__PURE__ */ jsx4("p", {
856
+ className: "whitespace-pre-wrap",
857
+ children
858
+ })
859
+ })
860
+ });
861
+ }
862
+
863
+ // src/presentation/components/Sources.tsx
864
+ import {
865
+ Collapsible as Collapsible2,
866
+ CollapsibleContent as CollapsibleContent2,
867
+ CollapsibleTrigger as CollapsibleTrigger2
868
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
869
+ import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
870
+ import { ExternalLink } from "lucide-react";
871
+ import { jsx as jsx5, jsxs as jsxs5, Fragment } from "react/jsx-runtime";
872
+ "use client";
873
+ function Sources({ children, className }) {
874
+ return /* @__PURE__ */ jsx5(Collapsible2, {
875
+ className: cn4("mt-2", className),
876
+ defaultOpen: false,
877
+ children
878
+ });
879
+ }
880
+ function SourcesTrigger({
881
+ count,
882
+ children,
883
+ className
884
+ }) {
885
+ return /* @__PURE__ */ jsx5(CollapsibleTrigger2, {
886
+ 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),
887
+ children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
888
+ children: [
889
+ /* @__PURE__ */ jsx5(ExternalLink, {
890
+ className: "h-3 w-3"
891
+ }),
892
+ count,
893
+ " source",
894
+ count !== 1 ? "s" : ""
895
+ ]
896
+ })
897
+ });
898
+ }
899
+ function SourcesContent({ children, className }) {
900
+ return /* @__PURE__ */ jsx5(CollapsibleContent2, {
901
+ children: /* @__PURE__ */ jsx5("div", {
902
+ className: cn4("mt-2 flex flex-wrap gap-2", className),
903
+ children
904
+ })
905
+ });
906
+ }
907
+ function Source({
908
+ href,
909
+ title,
910
+ className,
911
+ children,
912
+ ...props
913
+ }) {
914
+ return /* @__PURE__ */ jsx5("a", {
915
+ href,
916
+ target: "_blank",
917
+ rel: "noopener noreferrer",
918
+ 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),
919
+ ...props,
920
+ children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
921
+ children: [
922
+ /* @__PURE__ */ jsx5(ExternalLink, {
923
+ className: "h-3 w-3"
924
+ }),
925
+ title ?? href
926
+ ]
927
+ })
928
+ });
929
+ }
930
+
697
931
  // src/presentation/components/ChatMessage.tsx
698
- import { jsx as jsx4, jsxs as jsxs4, Fragment } from "react/jsx-runtime";
932
+ import { jsx as jsx6, jsxs as jsxs6, Fragment as Fragment2 } from "react/jsx-runtime";
699
933
  "use client";
700
934
  function extractCodeBlocks(content) {
701
935
  const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
@@ -718,11 +952,11 @@ function renderInlineMarkdown(text) {
718
952
  let key = 0;
719
953
  while ((match = linkRegex.exec(text)) !== null) {
720
954
  if (match.index > lastIndex) {
721
- parts.push(/* @__PURE__ */ jsx4("span", {
955
+ parts.push(/* @__PURE__ */ jsx6("span", {
722
956
  children: text.slice(lastIndex, match.index)
723
957
  }, key++));
724
958
  }
725
- parts.push(/* @__PURE__ */ jsx4("a", {
959
+ parts.push(/* @__PURE__ */ jsx6("a", {
726
960
  href: match[2],
727
961
  target: "_blank",
728
962
  rel: "noopener noreferrer",
@@ -732,7 +966,7 @@ function renderInlineMarkdown(text) {
732
966
  lastIndex = match.index + match[0].length;
733
967
  }
734
968
  if (lastIndex < text.length) {
735
- parts.push(/* @__PURE__ */ jsx4("span", {
969
+ parts.push(/* @__PURE__ */ jsx6("span", {
736
970
  children: text.slice(lastIndex)
737
971
  }, key++));
738
972
  }
@@ -741,7 +975,7 @@ function renderInlineMarkdown(text) {
741
975
  function MessageContent({ content }) {
742
976
  const codeBlocks = extractCodeBlocks(content);
743
977
  if (codeBlocks.length === 0) {
744
- return /* @__PURE__ */ jsx4("p", {
978
+ return /* @__PURE__ */ jsx6("p", {
745
979
  className: "whitespace-pre-wrap",
746
980
  children: renderInlineMarkdown(content)
747
981
  });
@@ -752,12 +986,12 @@ function MessageContent({ content }) {
752
986
  for (const block of codeBlocks) {
753
987
  const [before, after] = remaining.split(block.raw);
754
988
  if (before) {
755
- parts.push(/* @__PURE__ */ jsx4("p", {
989
+ parts.push(/* @__PURE__ */ jsx6("p", {
756
990
  className: "whitespace-pre-wrap",
757
991
  children: renderInlineMarkdown(before.trim())
758
992
  }, key++));
759
993
  }
760
- parts.push(/* @__PURE__ */ jsx4(CodePreview, {
994
+ parts.push(/* @__PURE__ */ jsx6(CodePreview, {
761
995
  code: block.code,
762
996
  language: block.language,
763
997
  className: "my-2"
@@ -765,15 +999,22 @@ function MessageContent({ content }) {
765
999
  remaining = after ?? "";
766
1000
  }
767
1001
  if (remaining.trim()) {
768
- parts.push(/* @__PURE__ */ jsx4("p", {
1002
+ parts.push(/* @__PURE__ */ jsx6("p", {
769
1003
  className: "whitespace-pre-wrap",
770
1004
  children: renderInlineMarkdown(remaining.trim())
771
1005
  }, key++));
772
1006
  }
773
- return /* @__PURE__ */ jsx4(Fragment, {
1007
+ return /* @__PURE__ */ jsx6(Fragment2, {
774
1008
  children: parts
775
1009
  });
776
1010
  }
1011
+ function toolStatusToCotStatus(status) {
1012
+ if (status === "completed")
1013
+ return "complete";
1014
+ if (status === "running")
1015
+ return "active";
1016
+ return "pending";
1017
+ }
777
1018
  function ChatMessage({
778
1019
  message,
779
1020
  className,
@@ -785,119 +1026,128 @@ function ChatMessage({
785
1026
  editable = false,
786
1027
  onEdit,
787
1028
  presentationRenderer,
788
- formRenderer
1029
+ formRenderer,
1030
+ dataViewRenderer,
1031
+ components: comps
789
1032
  }) {
790
- const [copied, setCopied] = React3.useState(false);
1033
+ const [copied, setCopied] = React4.useState(false);
791
1034
  const isUser = message.role === "user";
792
1035
  const isError = message.status === "error";
793
1036
  const isStreaming = message.status === "streaming";
794
- const handleCopy = React3.useCallback(async () => {
1037
+ const handleCopy = React4.useCallback(async () => {
795
1038
  await navigator.clipboard.writeText(message.content);
796
1039
  setCopied(true);
797
1040
  setTimeout(() => setCopied(false), 2000);
798
1041
  }, [message.content]);
799
- const handleSelectChange = React3.useCallback((checked) => {
1042
+ const handleSelectChange = React4.useCallback((checked) => {
800
1043
  if (checked !== "indeterminate")
801
1044
  onSelect?.(message.id);
802
1045
  }, [message.id, onSelect]);
803
- const [isEditing, setIsEditing] = React3.useState(false);
804
- const [editContent, setEditContent] = React3.useState(message.content);
805
- React3.useEffect(() => {
1046
+ const [isEditing, setIsEditing] = React4.useState(false);
1047
+ const [editContent, setEditContent] = React4.useState(message.content);
1048
+ const editTextareaRef = React4.useRef(null);
1049
+ React4.useEffect(() => {
806
1050
  setEditContent(message.content);
807
1051
  }, [message.content]);
808
- const handleStartEdit = React3.useCallback(() => {
1052
+ React4.useEffect(() => {
1053
+ if (isEditing) {
1054
+ editTextareaRef.current?.focus();
1055
+ }
1056
+ }, [isEditing]);
1057
+ const handleStartEdit = React4.useCallback(() => {
809
1058
  setEditContent(message.content);
810
1059
  setIsEditing(true);
811
1060
  }, [message.content]);
812
- const handleSaveEdit = React3.useCallback(async () => {
1061
+ const handleSaveEdit = React4.useCallback(async () => {
813
1062
  const trimmed = editContent.trim();
814
1063
  if (trimmed !== message.content) {
815
1064
  await onEdit?.(message.id, trimmed);
816
1065
  }
817
1066
  setIsEditing(false);
818
1067
  }, [editContent, message.id, message.content, onEdit]);
819
- const handleCancelEdit = React3.useCallback(() => {
1068
+ const handleCancelEdit = React4.useCallback(() => {
820
1069
  setEditContent(message.content);
821
1070
  setIsEditing(false);
822
1071
  }, [message.content]);
823
- return /* @__PURE__ */ jsxs4("div", {
824
- className: cn3("group flex gap-3", isUser && "flex-row-reverse", className),
1072
+ return /* @__PURE__ */ jsxs6("div", {
1073
+ className: cn5("group flex gap-3", isUser && "flex-row-reverse", className),
825
1074
  children: [
826
- selectable && /* @__PURE__ */ jsx4("div", {
827
- className: cn3("flex shrink-0 items-start pt-1", "opacity-0 transition-opacity group-hover:opacity-100"),
828
- children: /* @__PURE__ */ jsx4(Checkbox, {
1075
+ selectable && /* @__PURE__ */ jsx6("div", {
1076
+ className: cn5("flex shrink-0 items-start pt-1", "opacity-0 transition-opacity group-hover:opacity-100"),
1077
+ children: /* @__PURE__ */ jsx6(Checkbox, {
829
1078
  checked: selected,
830
1079
  onCheckedChange: handleSelectChange,
831
1080
  "aria-label": selected ? "Deselect message" : "Select message"
832
1081
  })
833
1082
  }),
834
- showAvatar && /* @__PURE__ */ jsx4(Avatar, {
1083
+ showAvatar && /* @__PURE__ */ jsx6(Avatar, {
835
1084
  className: "h-8 w-8 shrink-0",
836
- children: /* @__PURE__ */ jsx4(AvatarFallback, {
837
- className: cn3(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
838
- children: isUser ? /* @__PURE__ */ jsx4(User, {
1085
+ children: /* @__PURE__ */ jsx6(AvatarFallback, {
1086
+ className: cn5(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
1087
+ children: isUser ? /* @__PURE__ */ jsx6(User, {
839
1088
  className: "h-4 w-4"
840
- }) : /* @__PURE__ */ jsx4(Bot, {
1089
+ }) : /* @__PURE__ */ jsx6(Bot, {
841
1090
  className: "h-4 w-4"
842
1091
  })
843
1092
  })
844
1093
  }),
845
- /* @__PURE__ */ jsxs4("div", {
846
- className: cn3("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
1094
+ /* @__PURE__ */ jsxs6("div", {
1095
+ className: cn5("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
847
1096
  children: [
848
- /* @__PURE__ */ jsx4("div", {
849
- 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"),
850
- children: isError && message.error ? /* @__PURE__ */ jsxs4("div", {
1097
+ /* @__PURE__ */ jsx6("div", {
1098
+ 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"),
1099
+ children: isError && message.error ? /* @__PURE__ */ jsxs6("div", {
851
1100
  className: "flex items-start gap-2",
852
1101
  children: [
853
- /* @__PURE__ */ jsx4(AlertCircle, {
1102
+ /* @__PURE__ */ jsx6(AlertCircle, {
854
1103
  className: "text-destructive mt-0.5 h-4 w-4 shrink-0"
855
1104
  }),
856
- /* @__PURE__ */ jsxs4("div", {
1105
+ /* @__PURE__ */ jsxs6("div", {
857
1106
  children: [
858
- /* @__PURE__ */ jsx4("p", {
1107
+ /* @__PURE__ */ jsx6("p", {
859
1108
  className: "text-destructive font-medium",
860
1109
  children: message.error.code
861
1110
  }),
862
- /* @__PURE__ */ jsx4("p", {
1111
+ /* @__PURE__ */ jsx6("p", {
863
1112
  className: "text-muted-foreground text-sm",
864
1113
  children: message.error.message
865
1114
  })
866
1115
  ]
867
1116
  })
868
1117
  ]
869
- }) : isEditing ? /* @__PURE__ */ jsxs4("div", {
1118
+ }) : isEditing ? /* @__PURE__ */ jsxs6("div", {
870
1119
  className: "flex flex-col gap-2",
871
1120
  children: [
872
- /* @__PURE__ */ jsx4("textarea", {
1121
+ /* @__PURE__ */ jsx6("textarea", {
1122
+ ref: editTextareaRef,
873
1123
  value: editContent,
874
1124
  onChange: (e) => setEditContent(e.target.value),
875
1125
  className: "bg-background/50 min-h-[80px] w-full resize-y rounded-md border px-3 py-2 text-sm",
876
1126
  rows: 4,
877
- autoFocus: true
1127
+ "aria-label": "Edit message"
878
1128
  }),
879
- /* @__PURE__ */ jsxs4("div", {
1129
+ /* @__PURE__ */ jsxs6("div", {
880
1130
  className: "flex gap-2",
881
1131
  children: [
882
- /* @__PURE__ */ jsxs4(Button2, {
1132
+ /* @__PURE__ */ jsxs6(Button2, {
883
1133
  variant: "default",
884
1134
  size: "sm",
885
1135
  onPress: handleSaveEdit,
886
1136
  "aria-label": "Save edit",
887
1137
  children: [
888
- /* @__PURE__ */ jsx4(Check2, {
1138
+ /* @__PURE__ */ jsx6(Check2, {
889
1139
  className: "h-3 w-3"
890
1140
  }),
891
1141
  "Save"
892
1142
  ]
893
1143
  }),
894
- /* @__PURE__ */ jsxs4(Button2, {
1144
+ /* @__PURE__ */ jsxs6(Button2, {
895
1145
  variant: "ghost",
896
1146
  size: "sm",
897
1147
  onPress: handleCancelEdit,
898
1148
  "aria-label": "Cancel edit",
899
1149
  children: [
900
- /* @__PURE__ */ jsx4(X, {
1150
+ /* @__PURE__ */ jsx6(X, {
901
1151
  className: "h-3 w-3"
902
1152
  }),
903
1153
  "Cancel"
@@ -906,153 +1156,256 @@ function ChatMessage({
906
1156
  ]
907
1157
  })
908
1158
  ]
909
- }) : isStreaming && !message.content ? /* @__PURE__ */ jsxs4("div", {
1159
+ }) : isStreaming && !message.content ? /* @__PURE__ */ jsxs6("div", {
910
1160
  className: "flex flex-col gap-2",
911
1161
  children: [
912
- /* @__PURE__ */ jsx4(Skeleton, {
1162
+ /* @__PURE__ */ jsx6(Skeleton, {
913
1163
  className: "h-4 w-48"
914
1164
  }),
915
- /* @__PURE__ */ jsx4(Skeleton, {
1165
+ /* @__PURE__ */ jsx6(Skeleton, {
916
1166
  className: "h-4 w-32"
917
1167
  })
918
1168
  ]
919
- }) : /* @__PURE__ */ jsx4(MessageContent, {
1169
+ }) : /* @__PURE__ */ jsx6(MessageContent, {
920
1170
  content: message.content
921
1171
  })
922
1172
  }),
923
- /* @__PURE__ */ jsxs4("div", {
924
- className: cn3("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
1173
+ /* @__PURE__ */ jsxs6("div", {
1174
+ className: cn5("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
925
1175
  children: [
926
- /* @__PURE__ */ jsx4("span", {
1176
+ /* @__PURE__ */ jsx6("span", {
927
1177
  children: new Date(message.createdAt).toLocaleTimeString([], {
928
1178
  hour: "2-digit",
929
1179
  minute: "2-digit"
930
1180
  })
931
1181
  }),
932
- message.usage && /* @__PURE__ */ jsxs4("span", {
1182
+ message.usage && /* @__PURE__ */ jsxs6("span", {
933
1183
  children: [
934
1184
  message.usage.inputTokens + message.usage.outputTokens,
935
1185
  " tokens"
936
1186
  ]
937
1187
  }),
938
- showCopy && !isUser && message.content && /* @__PURE__ */ jsx4(Button2, {
1188
+ showCopy && !isUser && message.content && /* @__PURE__ */ jsx6(Button2, {
939
1189
  variant: "ghost",
940
1190
  size: "sm",
941
1191
  className: "h-6 w-6 p-0",
942
1192
  onPress: handleCopy,
943
1193
  "aria-label": copied ? "Copied" : "Copy message",
944
- children: copied ? /* @__PURE__ */ jsx4(Check2, {
1194
+ children: copied ? /* @__PURE__ */ jsx6(Check2, {
945
1195
  className: "h-3 w-3"
946
- }) : /* @__PURE__ */ jsx4(Copy2, {
1196
+ }) : /* @__PURE__ */ jsx6(Copy2, {
947
1197
  className: "h-3 w-3"
948
1198
  })
949
1199
  }),
950
- editable && isUser && !isEditing && /* @__PURE__ */ jsx4(Button2, {
1200
+ editable && isUser && !isEditing && /* @__PURE__ */ jsx6(Button2, {
951
1201
  variant: "ghost",
952
1202
  size: "sm",
953
1203
  className: "h-6 w-6 p-0",
954
1204
  onPress: handleStartEdit,
955
1205
  "aria-label": "Edit message",
956
- children: /* @__PURE__ */ jsx4(Pencil, {
1206
+ children: /* @__PURE__ */ jsx6(Pencil, {
957
1207
  className: "h-3 w-3"
958
1208
  })
959
1209
  })
960
1210
  ]
961
1211
  }),
962
- message.reasoning && /* @__PURE__ */ jsxs4("details", {
963
- className: "text-muted-foreground mt-2 text-sm",
1212
+ message.reasoning && (comps?.Reasoning ? /* @__PURE__ */ jsx6(comps.Reasoning, {
1213
+ isStreaming: isStreaming && !!message.reasoning,
1214
+ children: message.reasoning
1215
+ }) : /* @__PURE__ */ jsxs6(Reasoning, {
1216
+ isStreaming: isStreaming && !!message.reasoning,
1217
+ className: "mt-2",
964
1218
  children: [
965
- /* @__PURE__ */ jsx4("summary", {
966
- className: "cursor-pointer hover:underline",
967
- children: "View reasoning"
1219
+ /* @__PURE__ */ jsx6(ReasoningTrigger, {
1220
+ isStreaming
968
1221
  }),
969
- /* @__PURE__ */ jsx4("div", {
970
- className: "bg-muted mt-1 rounded-md p-2",
971
- children: /* @__PURE__ */ jsx4("p", {
972
- className: "whitespace-pre-wrap",
973
- children: message.reasoning
974
- })
1222
+ /* @__PURE__ */ jsx6(ReasoningContent, {
1223
+ children: message.reasoning
975
1224
  })
976
1225
  ]
977
- }),
978
- message.sources && message.sources.length > 0 && /* @__PURE__ */ jsx4("div", {
979
- className: "mt-2 flex flex-wrap gap-2",
980
- children: message.sources.map((source) => /* @__PURE__ */ jsxs4("a", {
981
- href: source.url ?? "#",
982
- target: "_blank",
983
- rel: "noopener noreferrer",
984
- 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",
1226
+ })),
1227
+ message.sources && message.sources.length > 0 && (() => {
1228
+ const SourcesComp = comps?.Sources;
1229
+ const SourcesTriggerComp = comps?.SourcesTrigger;
1230
+ const SourceComp = comps?.Source;
1231
+ if (SourcesComp && SourcesTriggerComp && SourceComp) {
1232
+ return /* @__PURE__ */ jsxs6(SourcesComp, {
1233
+ children: [
1234
+ /* @__PURE__ */ jsx6(SourcesTriggerComp, {
1235
+ count: message.sources.length
1236
+ }),
1237
+ message.sources.map((source) => /* @__PURE__ */ jsx6(SourceComp, {
1238
+ href: source.url ?? "#",
1239
+ title: source.title || source.url || source.id
1240
+ }, source.id))
1241
+ ]
1242
+ });
1243
+ }
1244
+ return /* @__PURE__ */ jsxs6(Sources, {
1245
+ className: "mt-2",
985
1246
  children: [
986
- /* @__PURE__ */ jsx4(ExternalLink, {
987
- className: "h-3 w-3"
1247
+ /* @__PURE__ */ jsx6(SourcesTrigger, {
1248
+ count: message.sources.length
988
1249
  }),
989
- source.title || source.url || source.id
1250
+ /* @__PURE__ */ jsx6(SourcesContent, {
1251
+ children: message.sources.map((source) => /* @__PURE__ */ jsx6(Source, {
1252
+ href: source.url ?? "#",
1253
+ title: source.title || source.url || source.id
1254
+ }, source.id))
1255
+ })
990
1256
  ]
991
- }, source.id))
992
- }),
993
- message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx4("div", {
994
- className: "mt-2 space-y-2",
995
- children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs4("details", {
996
- className: "bg-muted border-border rounded-md border",
997
- children: [
998
- /* @__PURE__ */ jsxs4("summary", {
999
- className: "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm font-medium",
1257
+ });
1258
+ })(),
1259
+ message.toolCalls && message.toolCalls.length > 0 && (() => {
1260
+ const CotComp = comps?.ChainOfThought;
1261
+ const CotStepComp = comps?.ChainOfThoughtStep;
1262
+ if (CotComp && CotStepComp) {
1263
+ return /* @__PURE__ */ jsx6(CotComp, {
1264
+ defaultOpen: false,
1265
+ className: "mt-2",
1266
+ children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6(CotStepComp, {
1267
+ label: tc.name,
1268
+ description: Object.keys(tc.args).length > 0 ? `Input: ${JSON.stringify(tc.args)}` : undefined,
1269
+ status: toolStatusToCotStatus(tc.status),
1000
1270
  children: [
1001
- /* @__PURE__ */ jsx4(Wrench, {
1002
- className: "text-muted-foreground h-4 w-4"
1271
+ tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
1272
+ className: "text-muted-foreground mt-1 text-xs",
1273
+ children: "Running\u2026"
1003
1274
  }),
1004
- tc.name,
1005
- /* @__PURE__ */ jsx4("span", {
1006
- 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"),
1007
- children: tc.status
1008
- })
1009
- ]
1010
- }),
1011
- /* @__PURE__ */ jsxs4("div", {
1012
- className: "border-border border-t px-3 py-2 text-xs",
1013
- children: [
1014
- Object.keys(tc.args).length > 0 && /* @__PURE__ */ jsxs4("div", {
1015
- className: "mb-2",
1016
- children: [
1017
- /* @__PURE__ */ jsx4("span", {
1018
- className: "text-muted-foreground font-medium",
1019
- children: "Input:"
1020
- }),
1021
- /* @__PURE__ */ jsx4("pre", {
1022
- className: "bg-background mt-1 overflow-x-auto rounded p-2",
1023
- children: JSON.stringify(tc.args, null, 2)
1024
- })
1025
- ]
1026
- }),
1027
- tc.result !== undefined && /* @__PURE__ */ jsx4(ToolResultRenderer, {
1275
+ (tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
1028
1276
  toolName: tc.name,
1029
- result: tc.result,
1277
+ result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
1030
1278
  presentationRenderer,
1031
1279
  formRenderer,
1280
+ dataViewRenderer,
1032
1281
  showRawFallback: true
1033
1282
  }),
1034
- tc.error && /* @__PURE__ */ jsx4("p", {
1035
- className: "text-destructive mt-1",
1283
+ tc.error && /* @__PURE__ */ jsx6("p", {
1284
+ className: "text-destructive mt-1 text-xs",
1036
1285
  children: tc.error
1037
1286
  })
1038
1287
  ]
1039
- })
1040
- ]
1041
- }, tc.id))
1042
- })
1288
+ }, tc.id))
1289
+ });
1290
+ }
1291
+ return /* @__PURE__ */ jsx6("div", {
1292
+ className: "mt-2 space-y-2",
1293
+ children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6("details", {
1294
+ className: "bg-muted border-border rounded-md border",
1295
+ children: [
1296
+ /* @__PURE__ */ jsxs6("summary", {
1297
+ className: "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm font-medium",
1298
+ children: [
1299
+ /* @__PURE__ */ jsx6(Wrench, {
1300
+ className: "text-muted-foreground h-4 w-4"
1301
+ }),
1302
+ tc.name,
1303
+ /* @__PURE__ */ jsx6("span", {
1304
+ 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"),
1305
+ children: tc.status
1306
+ })
1307
+ ]
1308
+ }),
1309
+ /* @__PURE__ */ jsxs6("div", {
1310
+ className: "border-border border-t px-3 py-2 text-xs",
1311
+ children: [
1312
+ Object.keys(tc.args).length > 0 && /* @__PURE__ */ jsxs6("div", {
1313
+ className: "mb-2",
1314
+ children: [
1315
+ /* @__PURE__ */ jsx6("span", {
1316
+ className: "text-muted-foreground font-medium",
1317
+ children: "Input:"
1318
+ }),
1319
+ /* @__PURE__ */ jsx6("pre", {
1320
+ className: "bg-background mt-1 overflow-x-auto rounded p-2",
1321
+ children: JSON.stringify(tc.args, null, 2)
1322
+ })
1323
+ ]
1324
+ }),
1325
+ tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
1326
+ className: "text-muted-foreground mt-1 text-xs",
1327
+ children: "Running\u2026"
1328
+ }),
1329
+ (tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
1330
+ toolName: tc.name,
1331
+ result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
1332
+ presentationRenderer,
1333
+ formRenderer,
1334
+ dataViewRenderer,
1335
+ showRawFallback: true
1336
+ }),
1337
+ tc.error && /* @__PURE__ */ jsx6("p", {
1338
+ className: "text-destructive mt-1",
1339
+ children: tc.error
1340
+ })
1341
+ ]
1342
+ })
1343
+ ]
1344
+ }, tc.id))
1345
+ });
1346
+ })()
1043
1347
  ]
1044
1348
  })
1045
1349
  ]
1046
1350
  });
1047
1351
  }
1048
1352
  // src/presentation/components/ChatInput.tsx
1049
- import * as React4 from "react";
1050
- import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
1353
+ import * as React5 from "react";
1354
+ import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
1051
1355
  import { Textarea } from "@contractspec/lib.design-system";
1052
1356
  import { Button as Button3 } from "@contractspec/lib.design-system";
1053
- import { Send, Paperclip, X as X2, Loader2, FileText, Code } from "lucide-react";
1054
- import { jsx as jsx5, jsxs as jsxs5, Fragment as Fragment2 } from "react/jsx-runtime";
1357
+ import {
1358
+ Send,
1359
+ Paperclip,
1360
+ X as X2,
1361
+ Loader2,
1362
+ FileText,
1363
+ Code,
1364
+ ImageIcon
1365
+ } from "lucide-react";
1366
+ import { jsx as jsx7, jsxs as jsxs7, Fragment as Fragment3 } from "react/jsx-runtime";
1055
1367
  "use client";
1368
+ var DEFAULT_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
1369
+ var CODE_EXTENSIONS = [
1370
+ "ts",
1371
+ "tsx",
1372
+ "js",
1373
+ "jsx",
1374
+ "py",
1375
+ "go",
1376
+ "rs",
1377
+ "java",
1378
+ "json",
1379
+ "md",
1380
+ "txt",
1381
+ "yaml",
1382
+ "yml"
1383
+ ];
1384
+ function readFileAsContent(file) {
1385
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
1386
+ const isCode = CODE_EXTENSIONS.includes(extension);
1387
+ const isImage = file.type.startsWith("image/");
1388
+ if (isImage) {
1389
+ return new Promise((resolve, reject) => {
1390
+ const reader = new FileReader;
1391
+ reader.onload = () => {
1392
+ const result = reader.result;
1393
+ resolve({
1394
+ content: typeof result === "string" ? result : new TextDecoder().decode(result ?? new ArrayBuffer(0)),
1395
+ type: "image"
1396
+ });
1397
+ };
1398
+ reader.onerror = () => reject(new Error("Could not read file"));
1399
+ reader.readAsDataURL(file);
1400
+ });
1401
+ }
1402
+ return file.text().then((content) => ({
1403
+ content,
1404
+ type: isCode ? "code" : "file"
1405
+ })).catch(() => {
1406
+ throw new Error("Could not read file");
1407
+ });
1408
+ }
1056
1409
  function ChatInput({
1057
1410
  onSend,
1058
1411
  disabled = false,
@@ -1060,147 +1413,163 @@ function ChatInput({
1060
1413
  placeholder = "Type a message...",
1061
1414
  className,
1062
1415
  showAttachments = true,
1063
- maxAttachments = 5
1416
+ maxAttachments = 5,
1417
+ maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES
1064
1418
  }) {
1065
- const [content, setContent] = React4.useState("");
1066
- const [attachments, setAttachments] = React4.useState([]);
1067
- const textareaRef = React4.useRef(null);
1068
- const fileInputRef = React4.useRef(null);
1419
+ const [content, setContent] = React5.useState("");
1420
+ const [attachments, setAttachments] = React5.useState([]);
1421
+ const [fileError, setFileError] = React5.useState(null);
1422
+ const textareaRef = React5.useRef(null);
1423
+ const fileInputRef = React5.useRef(null);
1069
1424
  const canSend = content.trim().length > 0 || attachments.length > 0;
1070
- const handleSubmit = React4.useCallback((e) => {
1425
+ const handleSubmit = React5.useCallback((e) => {
1071
1426
  e?.preventDefault();
1072
1427
  if (!canSend || disabled || isLoading)
1073
1428
  return;
1074
1429
  onSend(content.trim(), attachments.length > 0 ? attachments : undefined);
1075
1430
  setContent("");
1076
1431
  setAttachments([]);
1432
+ setFileError(null);
1077
1433
  textareaRef.current?.focus();
1078
1434
  }, [canSend, content, attachments, disabled, isLoading, onSend]);
1079
- const handleKeyDown = React4.useCallback((e) => {
1435
+ const handleKeyDown = React5.useCallback((e) => {
1080
1436
  if (e.key === "Enter" && !e.shiftKey) {
1081
1437
  e.preventDefault();
1082
1438
  handleSubmit();
1083
1439
  }
1084
1440
  }, [handleSubmit]);
1085
- const handleFileSelect = React4.useCallback(async (e) => {
1441
+ const handleFileSelect = React5.useCallback(async (e) => {
1086
1442
  const files = e.target.files;
1087
1443
  if (!files)
1088
1444
  return;
1445
+ setFileError(null);
1089
1446
  const newAttachments = [];
1447
+ const errors = [];
1090
1448
  for (const file of Array.from(files)) {
1091
- if (attachments.length + newAttachments.length >= maxAttachments)
1449
+ if (attachments.length + newAttachments.length >= maxAttachments) {
1450
+ errors.push(`Maximum ${maxAttachments} attachments allowed`);
1092
1451
  break;
1093
- const content2 = await file.text();
1094
- const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
1095
- const isCode = [
1096
- "ts",
1097
- "tsx",
1098
- "js",
1099
- "jsx",
1100
- "py",
1101
- "go",
1102
- "rs",
1103
- "java"
1104
- ].includes(extension);
1105
- newAttachments.push({
1106
- id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
1107
- type: isCode ? "code" : "file",
1108
- name: file.name,
1109
- content: content2,
1110
- mimeType: file.type,
1111
- size: file.size
1112
- });
1452
+ }
1453
+ if (file.size > maxFileSizeBytes) {
1454
+ errors.push(`${file.name} exceeds ${Math.round(maxFileSizeBytes / 1024 / 1024)}MB limit`);
1455
+ continue;
1456
+ }
1457
+ try {
1458
+ const { content: fileContent, type } = await readFileAsContent(file);
1459
+ newAttachments.push({
1460
+ id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
1461
+ type,
1462
+ name: file.name,
1463
+ content: fileContent,
1464
+ mimeType: file.type,
1465
+ size: file.size
1466
+ });
1467
+ } catch {
1468
+ errors.push(`Could not read ${file.name}`);
1469
+ }
1470
+ }
1471
+ if (errors.length > 0) {
1472
+ setFileError(errors[0] ?? "Could not add file");
1473
+ }
1474
+ if (newAttachments.length > 0) {
1475
+ setAttachments((prev) => [...prev, ...newAttachments]);
1113
1476
  }
1114
- setAttachments((prev) => [...prev, ...newAttachments]);
1115
1477
  e.target.value = "";
1116
- }, [attachments.length, maxAttachments]);
1117
- const removeAttachment = React4.useCallback((id) => {
1478
+ }, [attachments.length, maxAttachments, maxFileSizeBytes]);
1479
+ const removeAttachment = React5.useCallback((id) => {
1118
1480
  setAttachments((prev) => prev.filter((a) => a.id !== id));
1119
1481
  }, []);
1120
- return /* @__PURE__ */ jsxs5("div", {
1121
- className: cn4("flex flex-col gap-2", className),
1482
+ return /* @__PURE__ */ jsxs7("div", {
1483
+ className: cn6("flex flex-col gap-2", className),
1122
1484
  children: [
1123
- attachments.length > 0 && /* @__PURE__ */ jsx5("div", {
1485
+ attachments.length > 0 && /* @__PURE__ */ jsx7("div", {
1124
1486
  className: "flex flex-wrap gap-2",
1125
- children: attachments.map((attachment) => /* @__PURE__ */ jsxs5("div", {
1126
- className: cn4("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
1487
+ children: attachments.map((attachment) => /* @__PURE__ */ jsxs7("div", {
1488
+ className: cn6("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
1127
1489
  children: [
1128
- attachment.type === "code" ? /* @__PURE__ */ jsx5(Code, {
1490
+ attachment.type === "code" ? /* @__PURE__ */ jsx7(Code, {
1491
+ className: "h-3.5 w-3.5"
1492
+ }) : attachment.type === "image" ? /* @__PURE__ */ jsx7(ImageIcon, {
1129
1493
  className: "h-3.5 w-3.5"
1130
- }) : /* @__PURE__ */ jsx5(FileText, {
1494
+ }) : /* @__PURE__ */ jsx7(FileText, {
1131
1495
  className: "h-3.5 w-3.5"
1132
1496
  }),
1133
- /* @__PURE__ */ jsx5("span", {
1497
+ /* @__PURE__ */ jsx7("span", {
1134
1498
  className: "max-w-[150px] truncate",
1135
1499
  children: attachment.name
1136
1500
  }),
1137
- /* @__PURE__ */ jsx5("button", {
1501
+ /* @__PURE__ */ jsx7("button", {
1138
1502
  type: "button",
1139
1503
  onClick: () => removeAttachment(attachment.id),
1140
1504
  className: "hover:text-foreground",
1141
1505
  "aria-label": `Remove ${attachment.name}`,
1142
- children: /* @__PURE__ */ jsx5(X2, {
1506
+ children: /* @__PURE__ */ jsx7(X2, {
1143
1507
  className: "h-3.5 w-3.5"
1144
1508
  })
1145
1509
  })
1146
1510
  ]
1147
1511
  }, attachment.id))
1148
1512
  }),
1149
- /* @__PURE__ */ jsxs5("form", {
1513
+ fileError && /* @__PURE__ */ jsx7("p", {
1514
+ className: "text-destructive text-xs",
1515
+ role: "alert",
1516
+ children: fileError
1517
+ }),
1518
+ /* @__PURE__ */ jsxs7("form", {
1150
1519
  onSubmit: handleSubmit,
1151
1520
  className: "flex items-end gap-2",
1152
1521
  children: [
1153
- showAttachments && /* @__PURE__ */ jsxs5(Fragment2, {
1522
+ showAttachments && /* @__PURE__ */ jsxs7(Fragment3, {
1154
1523
  children: [
1155
- /* @__PURE__ */ jsx5("input", {
1524
+ /* @__PURE__ */ jsx7("input", {
1156
1525
  ref: fileInputRef,
1157
1526
  type: "file",
1158
1527
  multiple: true,
1159
- accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
1528
+ accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml,image/*",
1160
1529
  onChange: handleFileSelect,
1161
1530
  className: "hidden",
1162
1531
  "aria-label": "Attach files"
1163
1532
  }),
1164
- /* @__PURE__ */ jsx5(Button3, {
1533
+ /* @__PURE__ */ jsx7(Button3, {
1165
1534
  type: "button",
1166
1535
  variant: "ghost",
1167
1536
  size: "sm",
1168
1537
  onPress: () => fileInputRef.current?.click(),
1169
1538
  disabled: disabled || attachments.length >= maxAttachments,
1170
1539
  "aria-label": "Attach files",
1171
- children: /* @__PURE__ */ jsx5(Paperclip, {
1540
+ children: /* @__PURE__ */ jsx7(Paperclip, {
1172
1541
  className: "h-4 w-4"
1173
1542
  })
1174
1543
  })
1175
1544
  ]
1176
1545
  }),
1177
- /* @__PURE__ */ jsx5("div", {
1546
+ /* @__PURE__ */ jsx7("div", {
1178
1547
  className: "relative flex-1",
1179
- children: /* @__PURE__ */ jsx5(Textarea, {
1548
+ children: /* @__PURE__ */ jsx7(Textarea, {
1180
1549
  value: content,
1181
1550
  onChange: (e) => setContent(e.target.value),
1182
1551
  onKeyDown: handleKeyDown,
1183
1552
  placeholder,
1184
1553
  disabled,
1185
- className: cn4("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
1554
+ className: cn6("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
1186
1555
  rows: 1,
1187
1556
  "aria-label": "Chat message"
1188
1557
  })
1189
1558
  }),
1190
- /* @__PURE__ */ jsx5(Button3, {
1559
+ /* @__PURE__ */ jsx7(Button3, {
1191
1560
  type: "submit",
1192
1561
  disabled: !canSend || disabled || isLoading,
1193
1562
  size: "sm",
1194
1563
  "aria-label": isLoading ? "Sending..." : "Send message",
1195
- children: isLoading ? /* @__PURE__ */ jsx5(Loader2, {
1564
+ children: isLoading ? /* @__PURE__ */ jsx7(Loader2, {
1196
1565
  className: "h-4 w-4 animate-spin"
1197
- }) : /* @__PURE__ */ jsx5(Send, {
1566
+ }) : /* @__PURE__ */ jsx7(Send, {
1198
1567
  className: "h-4 w-4"
1199
1568
  })
1200
1569
  })
1201
1570
  ]
1202
1571
  }),
1203
- /* @__PURE__ */ jsx5("p", {
1572
+ /* @__PURE__ */ jsx7("p", {
1204
1573
  className: "text-muted-foreground text-xs",
1205
1574
  children: "Press Enter to send, Shift+Enter for new line"
1206
1575
  })
@@ -1208,7 +1577,7 @@ function ChatInput({
1208
1577
  });
1209
1578
  }
1210
1579
  // src/presentation/components/ChatExportToolbar.tsx
1211
- import * as React5 from "react";
1580
+ import * as React6 from "react";
1212
1581
  import { Download as Download2, FileText as FileText2, Copy as Copy3, Check as Check3, Plus, GitFork } from "lucide-react";
1213
1582
  import { Button as Button4 } from "@contractspec/lib.design-system";
1214
1583
  import {
@@ -1420,7 +1789,7 @@ function exportToFile(messages, format, conversation) {
1420
1789
  }
1421
1790
 
1422
1791
  // src/presentation/components/ChatExportToolbar.tsx
1423
- import { jsx as jsx6, jsxs as jsxs6, Fragment as Fragment3 } from "react/jsx-runtime";
1792
+ import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
1424
1793
  "use client";
1425
1794
  function ChatExportToolbar({
1426
1795
  messages,
@@ -1435,19 +1804,19 @@ function ChatExportToolbar({
1435
1804
  onCreateNew,
1436
1805
  onFork
1437
1806
  }) {
1438
- const [copied, setCopied] = React5.useState(false);
1439
- const toExport = React5.useMemo(() => {
1807
+ const [copied, setCopied] = React6.useState(false);
1808
+ const toExport = React6.useMemo(() => {
1440
1809
  if (selectedIds.size > 0) {
1441
1810
  const idSet = selectedIds;
1442
1811
  return messages.filter((m) => idSet.has(m.id));
1443
1812
  }
1444
1813
  return messages;
1445
1814
  }, [messages, selectedIds]);
1446
- const handleExport = React5.useCallback((format) => {
1815
+ const handleExport = React6.useCallback((format) => {
1447
1816
  exportToFile(toExport, format, conversation);
1448
1817
  onExported?.(format, toExport.length);
1449
1818
  }, [toExport, conversation, onExported]);
1450
- const handleCopy = React5.useCallback(async () => {
1819
+ const handleCopy = React6.useCallback(async () => {
1451
1820
  const content = formatMessagesAsMarkdown(toExport);
1452
1821
  await navigator.clipboard.writeText(content);
1453
1822
  setCopied(true);
@@ -1455,8 +1824,8 @@ function ChatExportToolbar({
1455
1824
  onExported?.("markdown", toExport.length);
1456
1825
  }, [toExport, onExported]);
1457
1826
  const disabled = messages.length === 0;
1458
- const [forking, setForking] = React5.useState(false);
1459
- const handleFork = React5.useCallback(async (upToMessageId) => {
1827
+ const [forking, setForking] = React6.useState(false);
1828
+ const handleFork = React6.useCallback(async (upToMessageId) => {
1460
1829
  if (!onFork)
1461
1830
  return;
1462
1831
  setForking(true);
@@ -1466,35 +1835,35 @@ function ChatExportToolbar({
1466
1835
  setForking(false);
1467
1836
  }
1468
1837
  }, [onFork]);
1469
- return /* @__PURE__ */ jsxs6("div", {
1838
+ return /* @__PURE__ */ jsxs8("div", {
1470
1839
  className: "flex items-center gap-2",
1471
1840
  children: [
1472
- onCreateNew && /* @__PURE__ */ jsxs6(Button4, {
1841
+ onCreateNew && /* @__PURE__ */ jsxs8(Button4, {
1473
1842
  variant: "outline",
1474
1843
  size: "sm",
1475
1844
  onPress: onCreateNew,
1476
1845
  "aria-label": "New conversation",
1477
1846
  children: [
1478
- /* @__PURE__ */ jsx6(Plus, {
1847
+ /* @__PURE__ */ jsx8(Plus, {
1479
1848
  className: "h-4 w-4"
1480
1849
  }),
1481
1850
  "New"
1482
1851
  ]
1483
1852
  }),
1484
- onFork && messages.length > 0 && /* @__PURE__ */ jsxs6(Button4, {
1853
+ onFork && messages.length > 0 && /* @__PURE__ */ jsxs8(Button4, {
1485
1854
  variant: "outline",
1486
1855
  size: "sm",
1487
1856
  disabled: forking,
1488
1857
  onPress: () => handleFork(),
1489
1858
  "aria-label": "Fork conversation",
1490
1859
  children: [
1491
- /* @__PURE__ */ jsx6(GitFork, {
1860
+ /* @__PURE__ */ jsx8(GitFork, {
1492
1861
  className: "h-4 w-4"
1493
1862
  }),
1494
1863
  "Fork"
1495
1864
  ]
1496
1865
  }),
1497
- showSelectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs6("span", {
1866
+ showSelectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs8("span", {
1498
1867
  className: "text-muted-foreground text-sm",
1499
1868
  children: [
1500
1869
  selectedCount,
@@ -1503,16 +1872,16 @@ function ChatExportToolbar({
1503
1872
  " selected"
1504
1873
  ]
1505
1874
  }),
1506
- onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs6(Fragment3, {
1875
+ onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs8(Fragment4, {
1507
1876
  children: [
1508
- /* @__PURE__ */ jsx6(Button4, {
1877
+ /* @__PURE__ */ jsx8(Button4, {
1509
1878
  variant: "ghost",
1510
1879
  size: "sm",
1511
1880
  onPress: onSelectAll,
1512
1881
  className: "text-xs",
1513
1882
  children: "Select all"
1514
1883
  }),
1515
- selectedCount > 0 && /* @__PURE__ */ jsx6(Button4, {
1884
+ selectedCount > 0 && /* @__PURE__ */ jsx8(Button4, {
1516
1885
  variant: "ghost",
1517
1886
  size: "sm",
1518
1887
  onPress: onClearSelection,
@@ -1521,64 +1890,64 @@ function ChatExportToolbar({
1521
1890
  })
1522
1891
  ]
1523
1892
  }),
1524
- /* @__PURE__ */ jsxs6(DropdownMenu, {
1893
+ /* @__PURE__ */ jsxs8(DropdownMenu, {
1525
1894
  children: [
1526
- /* @__PURE__ */ jsx6(DropdownMenuTrigger, {
1895
+ /* @__PURE__ */ jsx8(DropdownMenuTrigger, {
1527
1896
  asChild: true,
1528
- children: /* @__PURE__ */ jsxs6(Button4, {
1897
+ children: /* @__PURE__ */ jsxs8(Button4, {
1529
1898
  variant: "outline",
1530
1899
  size: "sm",
1531
1900
  disabled,
1532
1901
  "aria-label": selectedCount > 0 ? "Export selected messages" : "Export conversation",
1533
1902
  children: [
1534
- /* @__PURE__ */ jsx6(Download2, {
1903
+ /* @__PURE__ */ jsx8(Download2, {
1535
1904
  className: "h-4 w-4"
1536
1905
  }),
1537
1906
  "Export"
1538
1907
  ]
1539
1908
  })
1540
1909
  }),
1541
- /* @__PURE__ */ jsxs6(DropdownMenuContent, {
1910
+ /* @__PURE__ */ jsxs8(DropdownMenuContent, {
1542
1911
  align: "end",
1543
1912
  children: [
1544
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1913
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1545
1914
  onSelect: () => handleExport("markdown"),
1546
1915
  disabled,
1547
1916
  children: [
1548
- /* @__PURE__ */ jsx6(FileText2, {
1917
+ /* @__PURE__ */ jsx8(FileText2, {
1549
1918
  className: "h-4 w-4"
1550
1919
  }),
1551
1920
  "Export as Markdown (.md)"
1552
1921
  ]
1553
1922
  }),
1554
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1923
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1555
1924
  onSelect: () => handleExport("txt"),
1556
1925
  disabled,
1557
1926
  children: [
1558
- /* @__PURE__ */ jsx6(FileText2, {
1927
+ /* @__PURE__ */ jsx8(FileText2, {
1559
1928
  className: "h-4 w-4"
1560
1929
  }),
1561
1930
  "Export as Plain Text (.txt)"
1562
1931
  ]
1563
1932
  }),
1564
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1933
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1565
1934
  onSelect: () => handleExport("json"),
1566
1935
  disabled,
1567
1936
  children: [
1568
- /* @__PURE__ */ jsx6(FileText2, {
1937
+ /* @__PURE__ */ jsx8(FileText2, {
1569
1938
  className: "h-4 w-4"
1570
1939
  }),
1571
1940
  "Export as JSON (.json)"
1572
1941
  ]
1573
1942
  }),
1574
- /* @__PURE__ */ jsx6(DropdownMenuSeparator, {}),
1575
- /* @__PURE__ */ jsxs6(DropdownMenuItem, {
1943
+ /* @__PURE__ */ jsx8(DropdownMenuSeparator, {}),
1944
+ /* @__PURE__ */ jsxs8(DropdownMenuItem, {
1576
1945
  onSelect: () => handleCopy(),
1577
1946
  disabled,
1578
1947
  children: [
1579
- copied ? /* @__PURE__ */ jsx6(Check3, {
1948
+ copied ? /* @__PURE__ */ jsx8(Check3, {
1580
1949
  className: "h-4 w-4 text-green-500"
1581
- }) : /* @__PURE__ */ jsx6(Copy3, {
1950
+ }) : /* @__PURE__ */ jsx8(Copy3, {
1582
1951
  className: "h-4 w-4"
1583
1952
  }),
1584
1953
  copied ? "Copied to clipboard" : "Copy to clipboard"
@@ -1592,11 +1961,11 @@ function ChatExportToolbar({
1592
1961
  });
1593
1962
  }
1594
1963
  // src/presentation/components/ChatWithExport.tsx
1595
- import * as React8 from "react";
1964
+ import * as React10 from "react";
1596
1965
 
1597
1966
  // src/presentation/components/ThinkingLevelPicker.tsx
1598
- import * as React6 from "react";
1599
- import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
1967
+ import * as React7 from "react";
1968
+ import { cn as cn7 } from "@contractspec/lib.ui-kit-web/ui/utils";
1600
1969
  import {
1601
1970
  Select,
1602
1971
  SelectContent,
@@ -1658,7 +2027,7 @@ function getProviderOptions(level, providerName) {
1658
2027
  }
1659
2028
 
1660
2029
  // src/presentation/components/ThinkingLevelPicker.tsx
1661
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2030
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1662
2031
  "use client";
1663
2032
  var THINKING_LEVELS = [
1664
2033
  "instant",
@@ -1672,20 +2041,20 @@ function ThinkingLevelPicker({
1672
2041
  className,
1673
2042
  compact = false
1674
2043
  }) {
1675
- const handleChange = React6.useCallback((v) => {
2044
+ const handleChange = React7.useCallback((v) => {
1676
2045
  onChange(v);
1677
2046
  }, [onChange]);
1678
2047
  if (compact) {
1679
- return /* @__PURE__ */ jsxs7(Select, {
2048
+ return /* @__PURE__ */ jsxs9(Select, {
1680
2049
  value,
1681
2050
  onValueChange: handleChange,
1682
2051
  children: [
1683
- /* @__PURE__ */ jsx7(SelectTrigger, {
1684
- className: cn5("w-[140px]", className),
1685
- children: /* @__PURE__ */ jsx7(SelectValue, {})
2052
+ /* @__PURE__ */ jsx9(SelectTrigger, {
2053
+ className: cn7("w-[140px]", className),
2054
+ children: /* @__PURE__ */ jsx9(SelectValue, {})
1686
2055
  }),
1687
- /* @__PURE__ */ jsx7(SelectContent, {
1688
- children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx7(SelectItem, {
2056
+ /* @__PURE__ */ jsx9(SelectContent, {
2057
+ children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
1689
2058
  value: level,
1690
2059
  children: THINKING_LEVEL_LABELS[level]
1691
2060
  }, level))
@@ -1693,26 +2062,26 @@ function ThinkingLevelPicker({
1693
2062
  ]
1694
2063
  });
1695
2064
  }
1696
- return /* @__PURE__ */ jsxs7("div", {
1697
- className: cn5("flex flex-col gap-1.5", className),
2065
+ return /* @__PURE__ */ jsxs9("div", {
2066
+ className: cn7("flex flex-col gap-1.5", className),
1698
2067
  children: [
1699
- /* @__PURE__ */ jsx7(Label, {
2068
+ /* @__PURE__ */ jsx9(Label, {
1700
2069
  htmlFor: "thinking-level-picker",
1701
2070
  className: "text-sm font-medium",
1702
2071
  children: "Thinking Level"
1703
2072
  }),
1704
- /* @__PURE__ */ jsxs7(Select, {
2073
+ /* @__PURE__ */ jsxs9(Select, {
1705
2074
  name: "thinking-level-picker",
1706
2075
  value,
1707
2076
  onValueChange: handleChange,
1708
2077
  children: [
1709
- /* @__PURE__ */ jsx7(SelectTrigger, {
1710
- children: /* @__PURE__ */ jsx7(SelectValue, {
2078
+ /* @__PURE__ */ jsx9(SelectTrigger, {
2079
+ children: /* @__PURE__ */ jsx9(SelectValue, {
1711
2080
  placeholder: "Select thinking level"
1712
2081
  })
1713
2082
  }),
1714
- /* @__PURE__ */ jsx7(SelectContent, {
1715
- children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx7(SelectItem, {
2083
+ /* @__PURE__ */ jsx9(SelectContent, {
2084
+ children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
1716
2085
  value: level,
1717
2086
  title: THINKING_LEVEL_DESCRIPTIONS[level],
1718
2087
  children: THINKING_LEVEL_LABELS[level]
@@ -1725,12 +2094,12 @@ function ThinkingLevelPicker({
1725
2094
  }
1726
2095
 
1727
2096
  // src/presentation/hooks/useMessageSelection.ts
1728
- import * as React7 from "react";
2097
+ import * as React8 from "react";
1729
2098
  "use client";
1730
2099
  function useMessageSelection(messageIds) {
1731
- const [selectedIds, setSelectedIds] = React7.useState(() => new Set);
1732
- const idSet = React7.useMemo(() => new Set(messageIds), [messageIds.join(",")]);
1733
- React7.useEffect(() => {
2100
+ const [selectedIds, setSelectedIds] = React8.useState(() => new Set);
2101
+ const idSet = React8.useMemo(() => new Set(messageIds), [messageIds.join(",")]);
2102
+ React8.useEffect(() => {
1734
2103
  setSelectedIds((prev) => {
1735
2104
  const next = new Set;
1736
2105
  for (const id of prev) {
@@ -1740,7 +2109,7 @@ function useMessageSelection(messageIds) {
1740
2109
  return next.size === prev.size ? prev : next;
1741
2110
  });
1742
2111
  }, [idSet]);
1743
- const toggle = React7.useCallback((id) => {
2112
+ const toggle = React8.useCallback((id) => {
1744
2113
  setSelectedIds((prev) => {
1745
2114
  const next = new Set(prev);
1746
2115
  if (next.has(id))
@@ -1750,13 +2119,13 @@ function useMessageSelection(messageIds) {
1750
2119
  return next;
1751
2120
  });
1752
2121
  }, []);
1753
- const selectAll = React7.useCallback(() => {
2122
+ const selectAll = React8.useCallback(() => {
1754
2123
  setSelectedIds(new Set(messageIds));
1755
2124
  }, [messageIds.join(",")]);
1756
- const clearSelection = React7.useCallback(() => {
2125
+ const clearSelection = React8.useCallback(() => {
1757
2126
  setSelectedIds(new Set);
1758
2127
  }, []);
1759
- const isSelected = React7.useCallback((id) => selectedIds.has(id), [selectedIds]);
2128
+ const isSelected = React8.useCallback((id) => selectedIds.has(id), [selectedIds]);
1760
2129
  const selectedCount = selectedIds.size;
1761
2130
  return {
1762
2131
  selectedIds,
@@ -1768,8 +2137,38 @@ function useMessageSelection(messageIds) {
1768
2137
  };
1769
2138
  }
1770
2139
 
2140
+ // src/presentation/components/Suggestion.tsx
2141
+ import * as React9 from "react";
2142
+ import { Button as Button5 } from "@contractspec/lib.design-system";
2143
+ import { cn as cn8 } from "@contractspec/lib.ui-kit-web/ui/utils";
2144
+ import { jsx as jsx10 } from "react/jsx-runtime";
2145
+ "use client";
2146
+ function Suggestions({ children, className }) {
2147
+ return /* @__PURE__ */ jsx10("div", {
2148
+ className: cn8("flex flex-wrap gap-2", className),
2149
+ children
2150
+ });
2151
+ }
2152
+ function Suggestion({
2153
+ suggestion,
2154
+ onClick,
2155
+ className
2156
+ }) {
2157
+ const handleClick = React9.useCallback(() => {
2158
+ onClick?.(suggestion);
2159
+ }, [suggestion, onClick]);
2160
+ return /* @__PURE__ */ jsx10(Button5, {
2161
+ type: "button",
2162
+ variant: "outline",
2163
+ size: "sm",
2164
+ onPress: handleClick,
2165
+ className: cn8("text-muted-foreground hover:text-foreground", className),
2166
+ children: suggestion
2167
+ });
2168
+ }
2169
+
1771
2170
  // src/presentation/components/ChatWithExport.tsx
1772
- import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
2171
+ import { jsx as jsx11, jsxs as jsxs10, Fragment as Fragment5 } from "react/jsx-runtime";
1773
2172
  "use client";
1774
2173
  function ChatWithExport({
1775
2174
  messages,
@@ -1785,20 +2184,27 @@ function ChatWithExport({
1785
2184
  thinkingLevel = "thinking",
1786
2185
  onThinkingLevelChange,
1787
2186
  presentationRenderer,
1788
- formRenderer
2187
+ formRenderer,
2188
+ dataViewRenderer,
2189
+ components,
2190
+ suggestions,
2191
+ onSuggestionClick,
2192
+ suggestionComponents: suggestionComps,
2193
+ showSuggestionsWhenEmpty = true
1789
2194
  }) {
1790
- const messageIds = React8.useMemo(() => messages.map((m) => m.id), [messages]);
2195
+ const messageIds = React10.useMemo(() => messages.map((m) => m.id), [messages]);
1791
2196
  const selection = useMessageSelection(messageIds);
2197
+ const showSuggestions = suggestions && suggestions.length > 0 && (messages.length === 0 || showSuggestionsWhenEmpty);
1792
2198
  const hasToolbar = showExport || showMessageSelection;
1793
2199
  const hasPicker = Boolean(onThinkingLevelChange);
1794
- const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs8(Fragment4, {
2200
+ const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs10(Fragment5, {
1795
2201
  children: [
1796
- hasPicker && /* @__PURE__ */ jsx8(ThinkingLevelPicker, {
2202
+ hasPicker && onThinkingLevelChange && /* @__PURE__ */ jsx11(ThinkingLevelPicker, {
1797
2203
  value: thinkingLevel,
1798
2204
  onChange: onThinkingLevelChange,
1799
2205
  compact: true
1800
2206
  }),
1801
- hasToolbar && /* @__PURE__ */ jsx8(ChatExportToolbar, {
2207
+ hasToolbar && /* @__PURE__ */ jsx11(ChatExportToolbar, {
1802
2208
  messages,
1803
2209
  conversation,
1804
2210
  selectedIds: selection.selectedIds,
@@ -1812,12 +2218,12 @@ function ChatWithExport({
1812
2218
  })
1813
2219
  ]
1814
2220
  }) : null;
1815
- return /* @__PURE__ */ jsxs8(ChatContainer, {
2221
+ return /* @__PURE__ */ jsxs10(ChatContainer, {
1816
2222
  className,
1817
2223
  headerContent,
1818
2224
  showScrollButton,
1819
2225
  children: [
1820
- messages.map((msg) => /* @__PURE__ */ jsx8(ChatMessage, {
2226
+ messages.map((msg) => /* @__PURE__ */ jsx11(ChatMessage, {
1821
2227
  message: msg,
1822
2228
  selectable: showMessageSelection,
1823
2229
  selected: selection.isSelected(msg.id),
@@ -1825,26 +2231,47 @@ function ChatWithExport({
1825
2231
  editable: msg.role === "user" && !!onEditMessage,
1826
2232
  onEdit: onEditMessage,
1827
2233
  presentationRenderer,
1828
- formRenderer
2234
+ formRenderer,
2235
+ dataViewRenderer,
2236
+ components
1829
2237
  }, msg.id)),
2238
+ showSuggestions && (() => {
2239
+ const SuggestionsComp = suggestionComps?.Suggestions;
2240
+ const SuggestionComp = suggestionComps?.Suggestion;
2241
+ if (SuggestionsComp && SuggestionComp) {
2242
+ return /* @__PURE__ */ jsx11(SuggestionsComp, {
2243
+ children: suggestions.map((s) => /* @__PURE__ */ jsx11(SuggestionComp, {
2244
+ suggestion: s,
2245
+ onClick: onSuggestionClick
2246
+ }, s))
2247
+ });
2248
+ }
2249
+ return /* @__PURE__ */ jsx11(Suggestions, {
2250
+ className: "mb-4",
2251
+ children: suggestions.map((s) => /* @__PURE__ */ jsx11(Suggestion, {
2252
+ suggestion: s,
2253
+ onClick: onSuggestionClick
2254
+ }, s))
2255
+ });
2256
+ })(),
1830
2257
  children
1831
2258
  ]
1832
2259
  });
1833
2260
  }
1834
2261
  // src/presentation/components/ChatSidebar.tsx
1835
- import * as React10 from "react";
2262
+ import * as React12 from "react";
1836
2263
  import { Plus as Plus2, Trash2, MessageSquare } from "lucide-react";
1837
- import { Button as Button5 } from "@contractspec/lib.design-system";
1838
- import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
2264
+ import { Button as Button6 } from "@contractspec/lib.design-system";
2265
+ import { cn as cn9 } from "@contractspec/lib.ui-kit-web/ui/utils";
1839
2266
 
1840
2267
  // src/presentation/hooks/useConversations.ts
1841
- import * as React9 from "react";
2268
+ import * as React11 from "react";
1842
2269
  "use client";
1843
2270
  function useConversations(options) {
1844
2271
  const { store, projectId, tags, limit = 50 } = options;
1845
- const [conversations, setConversations] = React9.useState([]);
1846
- const [isLoading, setIsLoading] = React9.useState(true);
1847
- const refresh = React9.useCallback(async () => {
2272
+ const [conversations, setConversations] = React11.useState([]);
2273
+ const [isLoading, setIsLoading] = React11.useState(true);
2274
+ const refresh = React11.useCallback(async () => {
1848
2275
  setIsLoading(true);
1849
2276
  try {
1850
2277
  const list = await store.list({
@@ -1858,10 +2285,10 @@ function useConversations(options) {
1858
2285
  setIsLoading(false);
1859
2286
  }
1860
2287
  }, [store, projectId, tags, limit]);
1861
- React9.useEffect(() => {
2288
+ React11.useEffect(() => {
1862
2289
  refresh();
1863
2290
  }, [refresh]);
1864
- const deleteConversation = React9.useCallback(async (id) => {
2291
+ const deleteConversation = React11.useCallback(async (id) => {
1865
2292
  const ok = await store.delete(id);
1866
2293
  if (ok) {
1867
2294
  setConversations((prev) => prev.filter((c) => c.id !== id));
@@ -1877,7 +2304,7 @@ function useConversations(options) {
1877
2304
  }
1878
2305
 
1879
2306
  // src/presentation/components/ChatSidebar.tsx
1880
- import { jsx as jsx9, jsxs as jsxs9, Fragment as Fragment5 } from "react/jsx-runtime";
2307
+ import { jsx as jsx12, jsxs as jsxs11, Fragment as Fragment6 } from "react/jsx-runtime";
1881
2308
  "use client";
1882
2309
  function formatDate(date) {
1883
2310
  const d = new Date(date);
@@ -1899,7 +2326,7 @@ function ConversationItem({
1899
2326
  }) {
1900
2327
  const title = conversation.title ?? conversation.messages[0]?.content?.slice(0, 50) ?? "New chat";
1901
2328
  const displayTitle = title.length > 40 ? `${title.slice(0, 40)}\u2026` : title;
1902
- return /* @__PURE__ */ jsxs9("div", {
2329
+ return /* @__PURE__ */ jsxs11("div", {
1903
2330
  role: "button",
1904
2331
  tabIndex: 0,
1905
2332
  onClick: onSelect,
@@ -1909,24 +2336,24 @@ function ConversationItem({
1909
2336
  onSelect();
1910
2337
  }
1911
2338
  },
1912
- 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"),
2339
+ 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"),
1913
2340
  children: [
1914
- /* @__PURE__ */ jsx9(MessageSquare, {
2341
+ /* @__PURE__ */ jsx12(MessageSquare, {
1915
2342
  className: "text-muted-foreground h-4 w-4 shrink-0"
1916
2343
  }),
1917
- /* @__PURE__ */ jsxs9("div", {
2344
+ /* @__PURE__ */ jsxs11("div", {
1918
2345
  className: "min-w-0 flex-1",
1919
2346
  children: [
1920
- /* @__PURE__ */ jsx9("p", {
2347
+ /* @__PURE__ */ jsx12("p", {
1921
2348
  className: "truncate",
1922
2349
  children: displayTitle
1923
2350
  }),
1924
- /* @__PURE__ */ jsxs9("p", {
2351
+ /* @__PURE__ */ jsxs11("p", {
1925
2352
  className: "text-muted-foreground text-xs",
1926
2353
  children: [
1927
2354
  formatDate(conversation.updatedAt),
1928
2355
  conversation.projectName && ` \xB7 ${conversation.projectName}`,
1929
- conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */ jsxs9(Fragment5, {
2356
+ conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */ jsxs11(Fragment6, {
1930
2357
  children: [
1931
2358
  " \xB7 ",
1932
2359
  conversation.tags.slice(0, 2).join(", ")
@@ -1936,15 +2363,17 @@ function ConversationItem({
1936
2363
  })
1937
2364
  ]
1938
2365
  }),
1939
- /* @__PURE__ */ jsx9("span", {
2366
+ /* @__PURE__ */ jsx12("span", {
2367
+ role: "group",
1940
2368
  onClick: (e) => e.stopPropagation(),
1941
- children: /* @__PURE__ */ jsx9(Button5, {
2369
+ onKeyDown: (e) => e.stopPropagation(),
2370
+ children: /* @__PURE__ */ jsx12(Button6, {
1942
2371
  variant: "ghost",
1943
2372
  size: "sm",
1944
2373
  className: "h-6 w-6 shrink-0 p-0 opacity-0 group-hover:opacity-100",
1945
2374
  onPress: onDelete,
1946
2375
  "aria-label": "Delete conversation",
1947
- children: /* @__PURE__ */ jsx9(Trash2, {
2376
+ children: /* @__PURE__ */ jsx12(Trash2, {
1948
2377
  className: "h-3 w-3"
1949
2378
  })
1950
2379
  })
@@ -1965,8 +2394,13 @@ function ChatSidebar({
1965
2394
  onUpdateConversation,
1966
2395
  selectedConversation
1967
2396
  }) {
1968
- const { conversations, isLoading, refresh, deleteConversation } = useConversations({ store, projectId, tags, limit });
1969
- const handleDelete = React10.useCallback(async (id) => {
2397
+ const { conversations, isLoading, deleteConversation } = useConversations({
2398
+ store,
2399
+ projectId,
2400
+ tags,
2401
+ limit
2402
+ });
2403
+ const handleDelete = React12.useCallback(async (id) => {
1970
2404
  const ok = await deleteConversation(id);
1971
2405
  if (ok && selectedConversationId === id) {
1972
2406
  onSelectConversation(null);
@@ -1974,39 +2408,39 @@ function ChatSidebar({
1974
2408
  }, [deleteConversation, selectedConversationId, onSelectConversation]);
1975
2409
  if (collapsed)
1976
2410
  return null;
1977
- return /* @__PURE__ */ jsxs9("div", {
1978
- className: cn6("border-border flex w-64 shrink-0 flex-col border-r", className),
2411
+ return /* @__PURE__ */ jsxs11("div", {
2412
+ className: cn9("border-border flex w-64 shrink-0 flex-col border-r", className),
1979
2413
  children: [
1980
- /* @__PURE__ */ jsxs9("div", {
2414
+ /* @__PURE__ */ jsxs11("div", {
1981
2415
  className: "border-border flex shrink-0 items-center justify-between border-b p-2",
1982
2416
  children: [
1983
- /* @__PURE__ */ jsx9("span", {
2417
+ /* @__PURE__ */ jsx12("span", {
1984
2418
  className: "text-muted-foreground text-sm font-medium",
1985
2419
  children: "Conversations"
1986
2420
  }),
1987
- /* @__PURE__ */ jsx9(Button5, {
2421
+ /* @__PURE__ */ jsx12(Button6, {
1988
2422
  variant: "ghost",
1989
2423
  size: "sm",
1990
2424
  className: "h-8 w-8 p-0",
1991
2425
  onPress: onCreateNew,
1992
2426
  "aria-label": "New conversation",
1993
- children: /* @__PURE__ */ jsx9(Plus2, {
2427
+ children: /* @__PURE__ */ jsx12(Plus2, {
1994
2428
  className: "h-4 w-4"
1995
2429
  })
1996
2430
  })
1997
2431
  ]
1998
2432
  }),
1999
- /* @__PURE__ */ jsx9("div", {
2433
+ /* @__PURE__ */ jsx12("div", {
2000
2434
  className: "flex-1 overflow-y-auto p-2",
2001
- children: isLoading ? /* @__PURE__ */ jsx9("div", {
2435
+ children: isLoading ? /* @__PURE__ */ jsx12("div", {
2002
2436
  className: "text-muted-foreground py-4 text-center text-sm",
2003
2437
  children: "Loading\u2026"
2004
- }) : conversations.length === 0 ? /* @__PURE__ */ jsx9("div", {
2438
+ }) : conversations.length === 0 ? /* @__PURE__ */ jsx12("div", {
2005
2439
  className: "text-muted-foreground py-4 text-center text-sm",
2006
2440
  children: "No conversations yet"
2007
- }) : /* @__PURE__ */ jsx9("div", {
2441
+ }) : /* @__PURE__ */ jsx12("div", {
2008
2442
  className: "flex flex-col gap-1",
2009
- children: conversations.map((conv) => /* @__PURE__ */ jsx9(ConversationItem, {
2443
+ children: conversations.map((conv) => /* @__PURE__ */ jsx12(ConversationItem, {
2010
2444
  conversation: conv,
2011
2445
  selected: conv.id === selectedConversationId,
2012
2446
  onSelect: () => onSelectConversation(conv.id),
@@ -2014,7 +2448,7 @@ function ChatSidebar({
2014
2448
  }, conv.id))
2015
2449
  })
2016
2450
  }),
2017
- selectedConversation && onUpdateConversation && /* @__PURE__ */ jsx9(ConversationMeta, {
2451
+ selectedConversation && onUpdateConversation && /* @__PURE__ */ jsx12(ConversationMeta, {
2018
2452
  conversation: selectedConversation,
2019
2453
  onUpdate: onUpdateConversation
2020
2454
  })
@@ -2025,13 +2459,13 @@ function ConversationMeta({
2025
2459
  conversation,
2026
2460
  onUpdate
2027
2461
  }) {
2028
- const [projectName, setProjectName] = React10.useState(conversation.projectName ?? "");
2029
- const [tagsStr, setTagsStr] = React10.useState(conversation.tags?.join(", ") ?? "");
2030
- React10.useEffect(() => {
2462
+ const [projectName, setProjectName] = React12.useState(conversation.projectName ?? "");
2463
+ const [tagsStr, setTagsStr] = React12.useState(conversation.tags?.join(", ") ?? "");
2464
+ React12.useEffect(() => {
2031
2465
  setProjectName(conversation.projectName ?? "");
2032
2466
  setTagsStr(conversation.tags?.join(", ") ?? "");
2033
2467
  }, [conversation.id, conversation.projectName, conversation.tags]);
2034
- const handleBlur = React10.useCallback(() => {
2468
+ const handleBlur = React12.useCallback(() => {
2035
2469
  const tags = tagsStr.split(",").map((t) => t.trim()).filter(Boolean);
2036
2470
  if (projectName !== (conversation.projectName ?? "") || JSON.stringify(tags) !== JSON.stringify(conversation.tags ?? [])) {
2037
2471
  onUpdate(conversation.id, {
@@ -2048,14 +2482,14 @@ function ConversationMeta({
2048
2482
  tagsStr,
2049
2483
  onUpdate
2050
2484
  ]);
2051
- return /* @__PURE__ */ jsxs9("div", {
2485
+ return /* @__PURE__ */ jsxs11("div", {
2052
2486
  className: "border-border shrink-0 border-t p-2",
2053
2487
  children: [
2054
- /* @__PURE__ */ jsx9("p", {
2488
+ /* @__PURE__ */ jsx12("p", {
2055
2489
  className: "text-muted-foreground mb-1 text-xs font-medium",
2056
2490
  children: "Project"
2057
2491
  }),
2058
- /* @__PURE__ */ jsx9("input", {
2492
+ /* @__PURE__ */ jsx12("input", {
2059
2493
  type: "text",
2060
2494
  value: projectName,
2061
2495
  onChange: (e) => setProjectName(e.target.value),
@@ -2063,11 +2497,11 @@ function ConversationMeta({
2063
2497
  placeholder: "Project name",
2064
2498
  className: "border-input bg-background mb-2 w-full rounded px-2 py-1 text-xs"
2065
2499
  }),
2066
- /* @__PURE__ */ jsx9("p", {
2500
+ /* @__PURE__ */ jsx12("p", {
2067
2501
  className: "text-muted-foreground mb-1 text-xs font-medium",
2068
2502
  children: "Tags"
2069
2503
  }),
2070
- /* @__PURE__ */ jsx9("input", {
2504
+ /* @__PURE__ */ jsx12("input", {
2071
2505
  type: "text",
2072
2506
  value: tagsStr,
2073
2507
  onChange: (e) => setTagsStr(e.target.value),
@@ -2079,10 +2513,10 @@ function ConversationMeta({
2079
2513
  });
2080
2514
  }
2081
2515
  // src/presentation/components/ChatWithSidebar.tsx
2082
- import * as React12 from "react";
2516
+ import * as React14 from "react";
2083
2517
 
2084
2518
  // src/presentation/hooks/useChat.tsx
2085
- import * as React11 from "react";
2519
+ import * as React13 from "react";
2086
2520
  import { tool as tool4 } from "ai";
2087
2521
  import { z as z4 } from "zod";
2088
2522
 
@@ -3156,16 +3590,16 @@ function useChat(options = {}) {
3156
3590
  mcpServers,
3157
3591
  agentMode
3158
3592
  } = options;
3159
- const [messages, setMessages] = React11.useState([]);
3160
- const [mcpTools, setMcpTools] = React11.useState(null);
3161
- const mcpCleanupRef = React11.useRef(null);
3162
- const [conversation, setConversation] = React11.useState(null);
3163
- const [isLoading, setIsLoading] = React11.useState(false);
3164
- const [error, setError] = React11.useState(null);
3165
- const [conversationId, setConversationId] = React11.useState(initialConversationId ?? null);
3166
- const abortControllerRef = React11.useRef(null);
3167
- const chatServiceRef = React11.useRef(null);
3168
- React11.useEffect(() => {
3593
+ const [messages, setMessages] = React13.useState([]);
3594
+ const [mcpTools, setMcpTools] = React13.useState(null);
3595
+ const mcpCleanupRef = React13.useRef(null);
3596
+ const [conversation, setConversation] = React13.useState(null);
3597
+ const [isLoading, setIsLoading] = React13.useState(false);
3598
+ const [error, setError] = React13.useState(null);
3599
+ const [conversationId, setConversationId] = React13.useState(initialConversationId ?? null);
3600
+ const abortControllerRef = React13.useRef(null);
3601
+ const chatServiceRef = React13.useRef(null);
3602
+ React13.useEffect(() => {
3169
3603
  if (!mcpServers?.length) {
3170
3604
  setMcpTools(null);
3171
3605
  return;
@@ -3197,7 +3631,7 @@ function useChat(options = {}) {
3197
3631
  setMcpTools(null);
3198
3632
  };
3199
3633
  }, [mcpServers]);
3200
- React11.useEffect(() => {
3634
+ React13.useEffect(() => {
3201
3635
  const chatProvider = createProvider({
3202
3636
  provider,
3203
3637
  model,
@@ -3234,7 +3668,7 @@ function useChat(options = {}) {
3234
3668
  surfacePlanConfig,
3235
3669
  mcpTools
3236
3670
  ]);
3237
- React11.useEffect(() => {
3671
+ React13.useEffect(() => {
3238
3672
  if (!conversationId || !chatServiceRef.current)
3239
3673
  return;
3240
3674
  const loadConversation = async () => {
@@ -3248,7 +3682,7 @@ function useChat(options = {}) {
3248
3682
  };
3249
3683
  loadConversation().catch(console.error);
3250
3684
  }, [conversationId]);
3251
- const sendMessage = React11.useCallback(async (content, attachments, opts) => {
3685
+ const sendMessage = React13.useCallback(async (content, attachments, opts) => {
3252
3686
  if (agentMode?.agent) {
3253
3687
  setIsLoading(true);
3254
3688
  setError(null);
@@ -3470,14 +3904,21 @@ function useChat(options = {}) {
3470
3904
  agentMode,
3471
3905
  store
3472
3906
  ]);
3473
- const clearConversation = React11.useCallback(() => {
3907
+ const clearConversation = React13.useCallback(() => {
3474
3908
  setMessages([]);
3475
3909
  setConversation(null);
3476
3910
  setConversationId(null);
3477
3911
  setError(null);
3478
3912
  }, []);
3479
- const regenerate = React11.useCallback(async () => {
3480
- const lastUserMessageIndex = messages.findLastIndex((m) => m.role === "user");
3913
+ const regenerate = React13.useCallback(async () => {
3914
+ let lastUserMessageIndex = -1;
3915
+ for (let i = messages.length - 1;i >= 0; i--) {
3916
+ const m = messages[i];
3917
+ if (m?.role === "user") {
3918
+ lastUserMessageIndex = i;
3919
+ break;
3920
+ }
3921
+ }
3481
3922
  if (lastUserMessageIndex === -1)
3482
3923
  return;
3483
3924
  const lastUserMessage = messages[lastUserMessageIndex];
@@ -3486,12 +3927,12 @@ function useChat(options = {}) {
3486
3927
  setMessages((prev) => prev.slice(0, lastUserMessageIndex + 1));
3487
3928
  await sendMessage(lastUserMessage.content, lastUserMessage.attachments);
3488
3929
  }, [messages, sendMessage]);
3489
- const stop = React11.useCallback(() => {
3930
+ const stop = React13.useCallback(() => {
3490
3931
  abortControllerRef.current?.abort();
3491
3932
  setIsLoading(false);
3492
3933
  }, []);
3493
3934
  const createNewConversation = clearConversation;
3494
- const editMessage = React11.useCallback(async (messageId, newContent) => {
3935
+ const editMessage = React13.useCallback(async (messageId, newContent) => {
3495
3936
  if (!chatServiceRef.current || !conversationId)
3496
3937
  return;
3497
3938
  const msg = messages.find((m) => m.id === messageId);
@@ -3506,7 +3947,7 @@ function useChat(options = {}) {
3506
3947
  }
3507
3948
  await sendMessage(newContent, undefined, { skipUserAppend: true });
3508
3949
  }, [conversationId, messages, sendMessage]);
3509
- const forkConversation = React11.useCallback(async (upToMessageId) => {
3950
+ const forkConversation = React13.useCallback(async (upToMessageId) => {
3510
3951
  if (!chatServiceRef.current)
3511
3952
  return null;
3512
3953
  const idToFork = conversationId ?? conversation?.id;
@@ -3522,7 +3963,7 @@ function useChat(options = {}) {
3522
3963
  return null;
3523
3964
  }
3524
3965
  }, [conversationId, conversation]);
3525
- const updateConversationFn = React11.useCallback(async (updates) => {
3966
+ const updateConversationFn = React13.useCallback(async (updates) => {
3526
3967
  if (!chatServiceRef.current || !conversationId)
3527
3968
  return null;
3528
3969
  const updated = await chatServiceRef.current.updateConversation(conversationId, updates);
@@ -3530,7 +3971,7 @@ function useChat(options = {}) {
3530
3971
  setConversation(updated);
3531
3972
  return updated;
3532
3973
  }, [conversationId]);
3533
- const addToolApprovalResponse = React11.useCallback((_toolCallId, _result) => {
3974
+ const addToolApprovalResponse = React13.useCallback((_toolCallId, _result) => {
3534
3975
  throw new Error(`addToolApprovalResponse: Tool approval requires server route with toUIMessageStreamResponse. ` + `Use createChatRoute and @ai-sdk/react useChat for tools with requireApproval.`);
3535
3976
  }, []);
3536
3977
  const hasApprovalTools = toolsDefs?.some((t) => t.requireApproval) ?? false;
@@ -3781,7 +4222,7 @@ function createLocalStorageConversationStore(storageKey) {
3781
4222
  }
3782
4223
 
3783
4224
  // src/presentation/components/ChatWithSidebar.tsx
3784
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
4225
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3785
4226
  "use client";
3786
4227
  var defaultStore = createLocalStorageConversationStore();
3787
4228
  function ChatWithSidebar({
@@ -3792,10 +4233,15 @@ function ChatWithSidebar({
3792
4233
  thinkingLevel: initialThinkingLevel = "thinking",
3793
4234
  presentationRenderer,
3794
4235
  formRenderer,
4236
+ dataViewRenderer,
4237
+ components,
4238
+ suggestions,
4239
+ suggestionComponents,
4240
+ showSuggestionsWhenEmpty = false,
3795
4241
  ...useChatOptions
3796
4242
  }) {
3797
4243
  const effectiveStore = store;
3798
- const [thinkingLevel, setThinkingLevel] = React12.useState(initialThinkingLevel);
4244
+ const [thinkingLevel, setThinkingLevel] = React14.useState(initialThinkingLevel);
3799
4245
  const chat = useChat({
3800
4246
  ...useChatOptions,
3801
4247
  store: effectiveStore,
@@ -3813,13 +4259,16 @@ function ChatWithSidebar({
3813
4259
  updateConversation
3814
4260
  } = chat;
3815
4261
  const selectedConversationId = conversation?.id ?? null;
3816
- const handleSelectConversation = React12.useCallback((id) => {
4262
+ const handleSelectConversation = React14.useCallback((id) => {
3817
4263
  setConversationId(id);
3818
4264
  }, [setConversationId]);
3819
- return /* @__PURE__ */ jsxs10("div", {
4265
+ const handleSuggestionClick = React14.useCallback((suggestion) => {
4266
+ sendMessage(suggestion);
4267
+ }, [sendMessage]);
4268
+ return /* @__PURE__ */ jsxs12("div", {
3820
4269
  className: className ?? "flex h-full w-full",
3821
4270
  children: [
3822
- /* @__PURE__ */ jsx10(ChatSidebar, {
4271
+ /* @__PURE__ */ jsx13(ChatSidebar, {
3823
4272
  store: effectiveStore,
3824
4273
  selectedConversationId,
3825
4274
  onSelectConversation: handleSelectConversation,
@@ -3833,9 +4282,9 @@ function ChatWithSidebar({
3833
4282
  }
3834
4283
  } : undefined
3835
4284
  }),
3836
- /* @__PURE__ */ jsx10("div", {
4285
+ /* @__PURE__ */ jsx13("div", {
3837
4286
  className: "flex min-w-0 flex-1 flex-col",
3838
- children: /* @__PURE__ */ jsx10(ChatWithExport, {
4287
+ children: /* @__PURE__ */ jsx13(ChatWithExport, {
3839
4288
  messages,
3840
4289
  conversation,
3841
4290
  onCreateNew: createNewConversation,
@@ -3845,7 +4294,13 @@ function ChatWithSidebar({
3845
4294
  onThinkingLevelChange: setThinkingLevel,
3846
4295
  presentationRenderer,
3847
4296
  formRenderer,
3848
- children: /* @__PURE__ */ jsx10(ChatInput, {
4297
+ dataViewRenderer,
4298
+ components,
4299
+ suggestions,
4300
+ onSuggestionClick: handleSuggestionClick,
4301
+ suggestionComponents,
4302
+ showSuggestionsWhenEmpty,
4303
+ children: /* @__PURE__ */ jsx13(ChatInput, {
3849
4304
  onSend: (content, att) => sendMessage(content, att),
3850
4305
  disabled: isLoading,
3851
4306
  isLoading
@@ -3856,9 +4311,9 @@ function ChatWithSidebar({
3856
4311
  });
3857
4312
  }
3858
4313
  // src/presentation/components/ModelPicker.tsx
3859
- import * as React13 from "react";
3860
- import { cn as cn7 } from "@contractspec/lib.ui-kit-web/ui/utils";
3861
- import { Button as Button6 } from "@contractspec/lib.design-system";
4314
+ import * as React15 from "react";
4315
+ import { cn as cn10 } from "@contractspec/lib.ui-kit-web/ui/utils";
4316
+ import { Button as Button7 } from "@contractspec/lib.design-system";
3862
4317
  import {
3863
4318
  Select as Select2,
3864
4319
  SelectContent as SelectContent2,
@@ -3872,22 +4327,22 @@ import { Bot as Bot2, Cloud, Cpu, Sparkles } from "lucide-react";
3872
4327
  import {
3873
4328
  getModelsForProvider
3874
4329
  } from "@contractspec/lib.ai-providers";
3875
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
4330
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3876
4331
  "use client";
3877
4332
  var PROVIDER_ICONS = {
3878
- ollama: /* @__PURE__ */ jsx11(Cpu, {
4333
+ ollama: /* @__PURE__ */ jsx14(Cpu, {
3879
4334
  className: "h-4 w-4"
3880
4335
  }),
3881
- openai: /* @__PURE__ */ jsx11(Bot2, {
4336
+ openai: /* @__PURE__ */ jsx14(Bot2, {
3882
4337
  className: "h-4 w-4"
3883
4338
  }),
3884
- anthropic: /* @__PURE__ */ jsx11(Sparkles, {
4339
+ anthropic: /* @__PURE__ */ jsx14(Sparkles, {
3885
4340
  className: "h-4 w-4"
3886
4341
  }),
3887
- mistral: /* @__PURE__ */ jsx11(Cloud, {
4342
+ mistral: /* @__PURE__ */ jsx14(Cloud, {
3888
4343
  className: "h-4 w-4"
3889
4344
  }),
3890
- gemini: /* @__PURE__ */ jsx11(Sparkles, {
4345
+ gemini: /* @__PURE__ */ jsx14(Sparkles, {
3891
4346
  className: "h-4 w-4"
3892
4347
  })
3893
4348
  };
@@ -3919,7 +4374,7 @@ function ModelPicker({
3919
4374
  ];
3920
4375
  const models = getModelsForProvider(value.provider);
3921
4376
  const selectedModel = models.find((m) => m.id === value.model);
3922
- const handleProviderChange = React13.useCallback((providerName) => {
4377
+ const handleProviderChange = React15.useCallback((providerName) => {
3923
4378
  const provider = providerName;
3924
4379
  const providerInfo = providers.find((p) => p.provider === provider);
3925
4380
  const providerModels = getModelsForProvider(provider);
@@ -3930,33 +4385,33 @@ function ModelPicker({
3930
4385
  mode: providerInfo?.mode ?? "byok"
3931
4386
  });
3932
4387
  }, [onChange, providers]);
3933
- const handleModelChange = React13.useCallback((modelId) => {
4388
+ const handleModelChange = React15.useCallback((modelId) => {
3934
4389
  onChange({
3935
4390
  ...value,
3936
4391
  model: modelId
3937
4392
  });
3938
4393
  }, [onChange, value]);
3939
4394
  if (compact) {
3940
- return /* @__PURE__ */ jsxs11("div", {
3941
- className: cn7("flex items-center gap-2", className),
4395
+ return /* @__PURE__ */ jsxs13("div", {
4396
+ className: cn10("flex items-center gap-2", className),
3942
4397
  children: [
3943
- /* @__PURE__ */ jsxs11(Select2, {
4398
+ /* @__PURE__ */ jsxs13(Select2, {
3944
4399
  value: value.provider,
3945
4400
  onValueChange: handleProviderChange,
3946
4401
  children: [
3947
- /* @__PURE__ */ jsx11(SelectTrigger2, {
4402
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
3948
4403
  className: "w-[140px]",
3949
- children: /* @__PURE__ */ jsx11(SelectValue2, {})
4404
+ children: /* @__PURE__ */ jsx14(SelectValue2, {})
3950
4405
  }),
3951
- /* @__PURE__ */ jsx11(SelectContent2, {
3952
- children: providers.map((p) => /* @__PURE__ */ jsx11(SelectItem2, {
4406
+ /* @__PURE__ */ jsx14(SelectContent2, {
4407
+ children: providers.map((p) => /* @__PURE__ */ jsx14(SelectItem2, {
3953
4408
  value: p.provider,
3954
4409
  disabled: !p.available,
3955
- children: /* @__PURE__ */ jsxs11("div", {
4410
+ children: /* @__PURE__ */ jsxs13("div", {
3956
4411
  className: "flex items-center gap-2",
3957
4412
  children: [
3958
4413
  PROVIDER_ICONS[p.provider],
3959
- /* @__PURE__ */ jsx11("span", {
4414
+ /* @__PURE__ */ jsx14("span", {
3960
4415
  children: PROVIDER_NAMES[p.provider]
3961
4416
  })
3962
4417
  ]
@@ -3965,16 +4420,16 @@ function ModelPicker({
3965
4420
  })
3966
4421
  ]
3967
4422
  }),
3968
- /* @__PURE__ */ jsxs11(Select2, {
4423
+ /* @__PURE__ */ jsxs13(Select2, {
3969
4424
  value: value.model,
3970
4425
  onValueChange: handleModelChange,
3971
4426
  children: [
3972
- /* @__PURE__ */ jsx11(SelectTrigger2, {
4427
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
3973
4428
  className: "w-[160px]",
3974
- children: /* @__PURE__ */ jsx11(SelectValue2, {})
4429
+ children: /* @__PURE__ */ jsx14(SelectValue2, {})
3975
4430
  }),
3976
- /* @__PURE__ */ jsx11(SelectContent2, {
3977
- children: models.map((m) => /* @__PURE__ */ jsx11(SelectItem2, {
4431
+ /* @__PURE__ */ jsx14(SelectContent2, {
4432
+ children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
3978
4433
  value: m.id,
3979
4434
  children: m.name
3980
4435
  }, m.id))
@@ -3984,32 +4439,32 @@ function ModelPicker({
3984
4439
  ]
3985
4440
  });
3986
4441
  }
3987
- return /* @__PURE__ */ jsxs11("div", {
3988
- className: cn7("flex flex-col gap-3", className),
4442
+ return /* @__PURE__ */ jsxs13("div", {
4443
+ className: cn10("flex flex-col gap-3", className),
3989
4444
  children: [
3990
- /* @__PURE__ */ jsxs11("div", {
4445
+ /* @__PURE__ */ jsxs13("div", {
3991
4446
  className: "flex flex-col gap-1.5",
3992
4447
  children: [
3993
- /* @__PURE__ */ jsx11(Label2, {
4448
+ /* @__PURE__ */ jsx14(Label2, {
3994
4449
  htmlFor: "provider-selection",
3995
4450
  className: "text-sm font-medium",
3996
4451
  children: "Provider"
3997
4452
  }),
3998
- /* @__PURE__ */ jsx11("div", {
4453
+ /* @__PURE__ */ jsx14("div", {
3999
4454
  className: "flex flex-wrap gap-2",
4000
4455
  id: "provider-selection",
4001
- children: providers.map((p) => /* @__PURE__ */ jsxs11(Button6, {
4456
+ children: providers.map((p) => /* @__PURE__ */ jsxs13(Button7, {
4002
4457
  variant: value.provider === p.provider ? "default" : "outline",
4003
4458
  size: "sm",
4004
4459
  onPress: () => p.available && handleProviderChange(p.provider),
4005
4460
  disabled: !p.available,
4006
- className: cn7(!p.available && "opacity-50"),
4461
+ className: cn10(!p.available && "opacity-50"),
4007
4462
  children: [
4008
4463
  PROVIDER_ICONS[p.provider],
4009
- /* @__PURE__ */ jsx11("span", {
4464
+ /* @__PURE__ */ jsx14("span", {
4010
4465
  children: PROVIDER_NAMES[p.provider]
4011
4466
  }),
4012
- /* @__PURE__ */ jsx11(Badge, {
4467
+ /* @__PURE__ */ jsx14(Badge, {
4013
4468
  variant: MODE_BADGES[p.mode].variant,
4014
4469
  className: "ml-1",
4015
4470
  children: MODE_BADGES[p.mode].label
@@ -4019,46 +4474,46 @@ function ModelPicker({
4019
4474
  })
4020
4475
  ]
4021
4476
  }),
4022
- /* @__PURE__ */ jsxs11("div", {
4477
+ /* @__PURE__ */ jsxs13("div", {
4023
4478
  className: "flex flex-col gap-1.5",
4024
4479
  children: [
4025
- /* @__PURE__ */ jsx11(Label2, {
4480
+ /* @__PURE__ */ jsx14(Label2, {
4026
4481
  htmlFor: "model-picker",
4027
4482
  className: "text-sm font-medium",
4028
4483
  children: "Model"
4029
4484
  }),
4030
- /* @__PURE__ */ jsxs11(Select2, {
4485
+ /* @__PURE__ */ jsxs13(Select2, {
4031
4486
  name: "model-picker",
4032
4487
  value: value.model,
4033
4488
  onValueChange: handleModelChange,
4034
4489
  children: [
4035
- /* @__PURE__ */ jsx11(SelectTrigger2, {
4036
- children: /* @__PURE__ */ jsx11(SelectValue2, {
4490
+ /* @__PURE__ */ jsx14(SelectTrigger2, {
4491
+ children: /* @__PURE__ */ jsx14(SelectValue2, {
4037
4492
  placeholder: "Select a model"
4038
4493
  })
4039
4494
  }),
4040
- /* @__PURE__ */ jsx11(SelectContent2, {
4041
- children: models.map((m) => /* @__PURE__ */ jsx11(SelectItem2, {
4495
+ /* @__PURE__ */ jsx14(SelectContent2, {
4496
+ children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
4042
4497
  value: m.id,
4043
- children: /* @__PURE__ */ jsxs11("div", {
4498
+ children: /* @__PURE__ */ jsxs13("div", {
4044
4499
  className: "flex items-center gap-2",
4045
4500
  children: [
4046
- /* @__PURE__ */ jsx11("span", {
4501
+ /* @__PURE__ */ jsx14("span", {
4047
4502
  children: m.name
4048
4503
  }),
4049
- /* @__PURE__ */ jsxs11("span", {
4504
+ /* @__PURE__ */ jsxs13("span", {
4050
4505
  className: "text-muted-foreground text-xs",
4051
4506
  children: [
4052
4507
  Math.round(m.contextWindow / 1000),
4053
4508
  "K"
4054
4509
  ]
4055
4510
  }),
4056
- m.capabilities.vision && /* @__PURE__ */ jsx11(Badge, {
4511
+ m.capabilities.vision && /* @__PURE__ */ jsx14(Badge, {
4057
4512
  variant: "outline",
4058
4513
  className: "text-xs",
4059
4514
  children: "Vision"
4060
4515
  }),
4061
- m.capabilities.reasoning && /* @__PURE__ */ jsx11(Badge, {
4516
+ m.capabilities.reasoning && /* @__PURE__ */ jsx14(Badge, {
4062
4517
  variant: "outline",
4063
4518
  className: "text-xs",
4064
4519
  children: "Reasoning"
@@ -4071,23 +4526,23 @@ function ModelPicker({
4071
4526
  })
4072
4527
  ]
4073
4528
  }),
4074
- selectedModel && /* @__PURE__ */ jsxs11("div", {
4529
+ selectedModel && /* @__PURE__ */ jsxs13("div", {
4075
4530
  className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
4076
4531
  children: [
4077
- /* @__PURE__ */ jsxs11("span", {
4532
+ /* @__PURE__ */ jsxs13("span", {
4078
4533
  children: [
4079
4534
  "Context: ",
4080
4535
  Math.round(selectedModel.contextWindow / 1000),
4081
4536
  "K tokens"
4082
4537
  ]
4083
4538
  }),
4084
- selectedModel.capabilities.vision && /* @__PURE__ */ jsx11("span", {
4539
+ selectedModel.capabilities.vision && /* @__PURE__ */ jsx14("span", {
4085
4540
  children: "\u2022 Vision"
4086
4541
  }),
4087
- selectedModel.capabilities.tools && /* @__PURE__ */ jsx11("span", {
4542
+ selectedModel.capabilities.tools && /* @__PURE__ */ jsx14("span", {
4088
4543
  children: "\u2022 Tools"
4089
4544
  }),
4090
- selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx11("span", {
4545
+ selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx14("span", {
4091
4546
  children: "\u2022 Reasoning"
4092
4547
  })
4093
4548
  ]
@@ -4096,7 +4551,7 @@ function ModelPicker({
4096
4551
  });
4097
4552
  }
4098
4553
  // src/presentation/components/ContextIndicator.tsx
4099
- import { cn as cn8 } from "@contractspec/lib.ui-kit-web/ui/utils";
4554
+ import { cn as cn11 } from "@contractspec/lib.ui-kit-web/ui/utils";
4100
4555
  import { Badge as Badge2 } from "@contractspec/lib.ui-kit-web/ui/badge";
4101
4556
  import {
4102
4557
  Tooltip,
@@ -4105,7 +4560,7 @@ import {
4105
4560
  TooltipTrigger
4106
4561
  } from "@contractspec/lib.ui-kit-web/ui/tooltip";
4107
4562
  import { FolderOpen, FileCode, Zap, Info } from "lucide-react";
4108
- import { jsx as jsx12, jsxs as jsxs12, Fragment as Fragment6 } from "react/jsx-runtime";
4563
+ import { jsx as jsx15, jsxs as jsxs14, Fragment as Fragment7 } from "react/jsx-runtime";
4109
4564
  "use client";
4110
4565
  function ContextIndicator({
4111
4566
  summary,
@@ -4114,51 +4569,51 @@ function ContextIndicator({
4114
4569
  showDetails = true
4115
4570
  }) {
4116
4571
  if (!summary && !active) {
4117
- return /* @__PURE__ */ jsxs12("div", {
4118
- className: cn8("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
4572
+ return /* @__PURE__ */ jsxs14("div", {
4573
+ className: cn11("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
4119
4574
  children: [
4120
- /* @__PURE__ */ jsx12(Info, {
4575
+ /* @__PURE__ */ jsx15(Info, {
4121
4576
  className: "h-4 w-4"
4122
4577
  }),
4123
- /* @__PURE__ */ jsx12("span", {
4578
+ /* @__PURE__ */ jsx15("span", {
4124
4579
  children: "No workspace context"
4125
4580
  })
4126
4581
  ]
4127
4582
  });
4128
4583
  }
4129
- const content = /* @__PURE__ */ jsxs12("div", {
4130
- className: cn8("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
4584
+ const content = /* @__PURE__ */ jsxs14("div", {
4585
+ className: cn11("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
4131
4586
  children: [
4132
- /* @__PURE__ */ jsxs12(Badge2, {
4587
+ /* @__PURE__ */ jsxs14(Badge2, {
4133
4588
  variant: active ? "default" : "secondary",
4134
4589
  className: "flex items-center gap-1",
4135
4590
  children: [
4136
- /* @__PURE__ */ jsx12(Zap, {
4591
+ /* @__PURE__ */ jsx15(Zap, {
4137
4592
  className: "h-3 w-3"
4138
4593
  }),
4139
4594
  "Context"
4140
4595
  ]
4141
4596
  }),
4142
- summary && showDetails && /* @__PURE__ */ jsxs12(Fragment6, {
4597
+ summary && showDetails && /* @__PURE__ */ jsxs14(Fragment7, {
4143
4598
  children: [
4144
- /* @__PURE__ */ jsxs12("div", {
4599
+ /* @__PURE__ */ jsxs14("div", {
4145
4600
  className: "flex items-center gap-1 text-xs",
4146
4601
  children: [
4147
- /* @__PURE__ */ jsx12(FolderOpen, {
4602
+ /* @__PURE__ */ jsx15(FolderOpen, {
4148
4603
  className: "h-3.5 w-3.5"
4149
4604
  }),
4150
- /* @__PURE__ */ jsx12("span", {
4605
+ /* @__PURE__ */ jsx15("span", {
4151
4606
  children: summary.name
4152
4607
  })
4153
4608
  ]
4154
4609
  }),
4155
- /* @__PURE__ */ jsxs12("div", {
4610
+ /* @__PURE__ */ jsxs14("div", {
4156
4611
  className: "flex items-center gap-1 text-xs",
4157
4612
  children: [
4158
- /* @__PURE__ */ jsx12(FileCode, {
4613
+ /* @__PURE__ */ jsx15(FileCode, {
4159
4614
  className: "h-3.5 w-3.5"
4160
4615
  }),
4161
- /* @__PURE__ */ jsxs12("span", {
4616
+ /* @__PURE__ */ jsxs14("span", {
4162
4617
  children: [
4163
4618
  summary.specs.total,
4164
4619
  " specs"
@@ -4173,77 +4628,77 @@ function ContextIndicator({
4173
4628
  if (!summary) {
4174
4629
  return content;
4175
4630
  }
4176
- return /* @__PURE__ */ jsx12(TooltipProvider, {
4177
- children: /* @__PURE__ */ jsxs12(Tooltip, {
4631
+ return /* @__PURE__ */ jsx15(TooltipProvider, {
4632
+ children: /* @__PURE__ */ jsxs14(Tooltip, {
4178
4633
  children: [
4179
- /* @__PURE__ */ jsx12(TooltipTrigger, {
4634
+ /* @__PURE__ */ jsx15(TooltipTrigger, {
4180
4635
  asChild: true,
4181
4636
  children: content
4182
4637
  }),
4183
- /* @__PURE__ */ jsx12(TooltipContent, {
4638
+ /* @__PURE__ */ jsx15(TooltipContent, {
4184
4639
  side: "bottom",
4185
4640
  className: "max-w-[300px]",
4186
- children: /* @__PURE__ */ jsxs12("div", {
4641
+ children: /* @__PURE__ */ jsxs14("div", {
4187
4642
  className: "flex flex-col gap-2 text-sm",
4188
4643
  children: [
4189
- /* @__PURE__ */ jsx12("div", {
4644
+ /* @__PURE__ */ jsx15("div", {
4190
4645
  className: "font-medium",
4191
4646
  children: summary.name
4192
4647
  }),
4193
- /* @__PURE__ */ jsx12("div", {
4648
+ /* @__PURE__ */ jsx15("div", {
4194
4649
  className: "text-muted-foreground text-xs",
4195
4650
  children: summary.path
4196
4651
  }),
4197
- /* @__PURE__ */ jsx12("div", {
4652
+ /* @__PURE__ */ jsx15("div", {
4198
4653
  className: "border-t pt-2",
4199
- children: /* @__PURE__ */ jsxs12("div", {
4654
+ children: /* @__PURE__ */ jsxs14("div", {
4200
4655
  className: "grid grid-cols-2 gap-1 text-xs",
4201
4656
  children: [
4202
- /* @__PURE__ */ jsx12("span", {
4657
+ /* @__PURE__ */ jsx15("span", {
4203
4658
  children: "Commands:"
4204
4659
  }),
4205
- /* @__PURE__ */ jsx12("span", {
4660
+ /* @__PURE__ */ jsx15("span", {
4206
4661
  className: "text-right",
4207
4662
  children: summary.specs.commands
4208
4663
  }),
4209
- /* @__PURE__ */ jsx12("span", {
4664
+ /* @__PURE__ */ jsx15("span", {
4210
4665
  children: "Queries:"
4211
4666
  }),
4212
- /* @__PURE__ */ jsx12("span", {
4667
+ /* @__PURE__ */ jsx15("span", {
4213
4668
  className: "text-right",
4214
4669
  children: summary.specs.queries
4215
4670
  }),
4216
- /* @__PURE__ */ jsx12("span", {
4671
+ /* @__PURE__ */ jsx15("span", {
4217
4672
  children: "Events:"
4218
4673
  }),
4219
- /* @__PURE__ */ jsx12("span", {
4674
+ /* @__PURE__ */ jsx15("span", {
4220
4675
  className: "text-right",
4221
4676
  children: summary.specs.events
4222
4677
  }),
4223
- /* @__PURE__ */ jsx12("span", {
4678
+ /* @__PURE__ */ jsx15("span", {
4224
4679
  children: "Presentations:"
4225
4680
  }),
4226
- /* @__PURE__ */ jsx12("span", {
4681
+ /* @__PURE__ */ jsx15("span", {
4227
4682
  className: "text-right",
4228
4683
  children: summary.specs.presentations
4229
4684
  })
4230
4685
  ]
4231
4686
  })
4232
4687
  }),
4233
- /* @__PURE__ */ jsxs12("div", {
4688
+ /* @__PURE__ */ jsxs14("div", {
4234
4689
  className: "border-t pt-2 text-xs",
4235
4690
  children: [
4236
- /* @__PURE__ */ jsxs12("span", {
4691
+ /* @__PURE__ */ jsxs14("span", {
4237
4692
  children: [
4238
4693
  summary.files.total,
4239
4694
  " files"
4240
4695
  ]
4241
4696
  }),
4242
- /* @__PURE__ */ jsx12("span", {
4697
+ /* @__PURE__ */ jsx15("span", {
4243
4698
  className: "mx-1",
4244
4699
  children: "\u2022"
4245
4700
  }),
4246
- /* @__PURE__ */ jsxs12("span", {
4701
+ /* @__PURE__ */ jsxs14("span", {
4247
4702
  children: [
4248
4703
  summary.files.specFiles,
4249
4704
  " spec files"
@@ -4258,17 +4713,104 @@ function ContextIndicator({
4258
4713
  })
4259
4714
  });
4260
4715
  }
4716
+ // src/presentation/components/ChainOfThought.tsx
4717
+ import {
4718
+ Collapsible as Collapsible3,
4719
+ CollapsibleContent as CollapsibleContent3,
4720
+ CollapsibleTrigger as CollapsibleTrigger3
4721
+ } from "@contractspec/lib.ui-kit-web/ui/collapsible";
4722
+ import { cn as cn12 } from "@contractspec/lib.ui-kit-web/ui/utils";
4723
+ import { ChevronDown, Dot } from "lucide-react";
4724
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
4725
+ "use client";
4726
+ function ChainOfThought({
4727
+ children,
4728
+ open,
4729
+ defaultOpen = false,
4730
+ onOpenChange,
4731
+ className
4732
+ }) {
4733
+ return /* @__PURE__ */ jsx16(Collapsible3, {
4734
+ open,
4735
+ defaultOpen,
4736
+ onOpenChange,
4737
+ className: cn12("group/cot mt-2", className),
4738
+ children
4739
+ });
4740
+ }
4741
+ function ChainOfThoughtHeader({
4742
+ children = "Chain of Thought",
4743
+ className
4744
+ }) {
4745
+ return /* @__PURE__ */ jsxs15(CollapsibleTrigger3, {
4746
+ 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),
4747
+ children: [
4748
+ /* @__PURE__ */ jsx16(ChevronDown, {
4749
+ className: "text-muted-foreground h-4 w-4 shrink-0 transition-transform group-data-[state=open]/cot:rotate-180"
4750
+ }),
4751
+ children
4752
+ ]
4753
+ });
4754
+ }
4755
+ function ChainOfThoughtStep({
4756
+ label,
4757
+ description,
4758
+ status = "complete",
4759
+ icon: Icon = Dot,
4760
+ children,
4761
+ className
4762
+ }) {
4763
+ return /* @__PURE__ */ jsxs15("div", {
4764
+ className: cn12("border-border flex gap-3 border-b py-2 last:border-b-0", className),
4765
+ children: [
4766
+ /* @__PURE__ */ jsx16("div", {
4767
+ 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"),
4768
+ children: /* @__PURE__ */ jsx16(Icon, {
4769
+ className: "h-3 w-3"
4770
+ })
4771
+ }),
4772
+ /* @__PURE__ */ jsxs15("div", {
4773
+ className: "min-w-0 flex-1",
4774
+ children: [
4775
+ /* @__PURE__ */ jsx16("p", {
4776
+ className: "font-medium",
4777
+ children: label
4778
+ }),
4779
+ description && /* @__PURE__ */ jsx16("p", {
4780
+ className: "text-muted-foreground mt-0.5 text-xs",
4781
+ children: description
4782
+ }),
4783
+ children && /* @__PURE__ */ jsx16("div", {
4784
+ className: "mt-2",
4785
+ children
4786
+ })
4787
+ ]
4788
+ })
4789
+ ]
4790
+ });
4791
+ }
4792
+ function ChainOfThoughtContent({
4793
+ children,
4794
+ className
4795
+ }) {
4796
+ return /* @__PURE__ */ jsx16(CollapsibleContent3, {
4797
+ children: /* @__PURE__ */ jsx16("div", {
4798
+ className: cn12("bg-muted border-border mt-1 rounded-md border px-3 py-2", className),
4799
+ children
4800
+ })
4801
+ });
4802
+ }
4261
4803
  // src/presentation/hooks/useProviders.tsx
4262
- import * as React14 from "react";
4804
+ import * as React16 from "react";
4263
4805
  import {
4264
4806
  getAvailableProviders,
4265
4807
  getModelsForProvider as getModelsForProvider2
4266
4808
  } from "@contractspec/lib.ai-providers";
4267
4809
  "use client";
4268
4810
  function useProviders() {
4269
- const [providers, setProviders] = React14.useState([]);
4270
- const [isLoading, setIsLoading] = React14.useState(true);
4271
- const loadProviders = React14.useCallback(async () => {
4811
+ const [providers, setProviders] = React16.useState([]);
4812
+ const [isLoading, setIsLoading] = React16.useState(true);
4813
+ const loadProviders = React16.useCallback(async () => {
4272
4814
  setIsLoading(true);
4273
4815
  try {
4274
4816
  const available = getAvailableProviders();
@@ -4283,12 +4825,12 @@ function useProviders() {
4283
4825
  setIsLoading(false);
4284
4826
  }
4285
4827
  }, []);
4286
- React14.useEffect(() => {
4828
+ React16.useEffect(() => {
4287
4829
  loadProviders();
4288
4830
  }, [loadProviders]);
4289
- const availableProviders = React14.useMemo(() => providers.filter((p) => p.available), [providers]);
4290
- const isAvailable = React14.useCallback((provider) => providers.some((p) => p.provider === provider && p.available), [providers]);
4291
- const getModelsCallback = React14.useCallback((provider) => providers.find((p) => p.provider === provider)?.models ?? [], [providers]);
4831
+ const availableProviders = React16.useMemo(() => providers.filter((p) => p.available), [providers]);
4832
+ const isAvailable = React16.useCallback((provider) => providers.some((p) => p.provider === provider && p.available), [providers]);
4833
+ const getModelsCallback = React16.useCallback((provider) => providers.find((p) => p.provider === provider)?.models ?? [], [providers]);
4292
4834
  return {
4293
4835
  providers,
4294
4836
  availableProviders,
@@ -4690,6 +5232,7 @@ export {
4690
5232
  isPresentationToolResult,
4691
5233
  isOllamaRunning,
4692
5234
  isFormToolResult,
5235
+ isDataViewToolResult,
4693
5236
  hasCredentials,
4694
5237
  getRecommendedModels,
4695
5238
  getModelsForProvider3 as getModelsForProvider,
@@ -4707,11 +5250,20 @@ export {
4707
5250
  WorkspaceContext,
4708
5251
  ToolResultRenderer,
4709
5252
  ThinkingLevelPicker,
5253
+ Suggestions,
5254
+ Suggestion,
4710
5255
  StreamMessageContract,
5256
+ SourcesTrigger,
5257
+ SourcesContent,
5258
+ Sources,
5259
+ Source,
4711
5260
  SendMessageOutputModel,
4712
5261
  SendMessageInputModel,
4713
5262
  SendMessageContract,
4714
5263
  ScanContextContract,
5264
+ ReasoningTrigger,
5265
+ ReasoningContent,
5266
+ Reasoning,
4715
5267
  ModelPicker,
4716
5268
  MessageSentEvent,
4717
5269
  MessageReceivedEvent,
@@ -4739,5 +5291,9 @@ export {
4739
5291
  ChatErrorEvent,
4740
5292
  ChatConversationModel,
4741
5293
  ChatContainer,
5294
+ ChainOfThoughtStep,
5295
+ ChainOfThoughtHeader,
5296
+ ChainOfThoughtContent,
5297
+ ChainOfThought,
4742
5298
  AiChatFeature
4743
5299
  };