@contractspec/module.ai-chat 4.1.4 → 4.2.0

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