@contractspec/module.ai-chat 4.1.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -3
- package/dist/browser/core/index.js +53 -17
- package/dist/browser/index.js +982 -426
- package/dist/browser/presentation/components/index.js +975 -419
- package/dist/browser/presentation/hooks/index.js +8 -1
- package/dist/browser/presentation/index.js +983 -427
- package/dist/core/create-chat-route.d.ts +14 -3
- package/dist/core/index.js +53 -17
- package/dist/core/message-types.d.ts +4 -0
- package/dist/index.js +982 -426
- package/dist/node/core/index.js +53 -17
- package/dist/node/index.js +982 -426
- package/dist/node/presentation/components/index.js +975 -419
- package/dist/node/presentation/hooks/index.js +8 -1
- package/dist/node/presentation/index.js +983 -427
- package/dist/presentation/components/ChainOfThought.d.ts +30 -0
- package/dist/presentation/components/ChatInput.d.ts +3 -4
- package/dist/presentation/components/ChatMessage.d.ts +6 -4
- package/dist/presentation/components/ChatWithExport.d.ts +14 -1
- package/dist/presentation/components/ChatWithSidebar.d.ts +12 -1
- package/dist/presentation/components/Reasoning.d.ts +24 -0
- package/dist/presentation/components/Sources.d.ts +22 -0
- package/dist/presentation/components/Suggestion.d.ts +12 -0
- package/dist/presentation/components/ToolResultRenderer.d.ts +20 -3
- package/dist/presentation/components/component-types.d.ts +52 -0
- package/dist/presentation/components/index.d.ts +6 -1
- package/dist/presentation/components/index.js +975 -419
- package/dist/presentation/hooks/index.js +8 -1
- package/dist/presentation/index.js +983 -427
- package/package.json +13 -13
package/dist/browser/index.js
CHANGED
|
@@ -484,8 +484,8 @@ function ChatContainer({
|
|
|
484
484
|
});
|
|
485
485
|
}
|
|
486
486
|
// src/presentation/components/ChatMessage.tsx
|
|
487
|
-
import * as
|
|
488
|
-
import { cn as
|
|
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
|
-
|
|
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
|
|
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__ */
|
|
960
|
+
parts.push(/* @__PURE__ */ jsx6("span", {
|
|
727
961
|
children: text.slice(lastIndex, match.index)
|
|
728
962
|
}, key++));
|
|
729
963
|
}
|
|
730
|
-
parts.push(/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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] =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
809
|
-
const [editContent, setEditContent] =
|
|
810
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1073
|
+
const handleCancelEdit = React4.useCallback(() => {
|
|
825
1074
|
setEditContent(message.content);
|
|
826
1075
|
setIsEditing(false);
|
|
827
1076
|
}, [message.content]);
|
|
828
|
-
return /* @__PURE__ */
|
|
829
|
-
className:
|
|
1077
|
+
return /* @__PURE__ */ jsxs6("div", {
|
|
1078
|
+
className: cn5("group flex gap-3", isUser && "flex-row-reverse", className),
|
|
830
1079
|
children: [
|
|
831
|
-
selectable && /* @__PURE__ */
|
|
832
|
-
className:
|
|
833
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
1088
|
+
showAvatar && /* @__PURE__ */ jsx6(Avatar, {
|
|
840
1089
|
className: "h-8 w-8 shrink-0",
|
|
841
|
-
children: /* @__PURE__ */
|
|
842
|
-
className:
|
|
843
|
-
children: isUser ? /* @__PURE__ */
|
|
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__ */
|
|
1094
|
+
}) : /* @__PURE__ */ jsx6(Bot, {
|
|
846
1095
|
className: "h-4 w-4"
|
|
847
1096
|
})
|
|
848
1097
|
})
|
|
849
1098
|
}),
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
className:
|
|
1099
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
1100
|
+
className: cn5("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
|
|
852
1101
|
children: [
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
className:
|
|
855
|
-
children: isError && message.error ? /* @__PURE__ */
|
|
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__ */
|
|
1107
|
+
/* @__PURE__ */ jsx6(AlertCircle, {
|
|
859
1108
|
className: "text-destructive mt-0.5 h-4 w-4 shrink-0"
|
|
860
1109
|
}),
|
|
861
|
-
/* @__PURE__ */
|
|
1110
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
862
1111
|
children: [
|
|
863
|
-
/* @__PURE__ */
|
|
1112
|
+
/* @__PURE__ */ jsx6("p", {
|
|
864
1113
|
className: "text-destructive font-medium",
|
|
865
1114
|
children: message.error.code
|
|
866
1115
|
}),
|
|
867
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1123
|
+
}) : isEditing ? /* @__PURE__ */ jsxs6("div", {
|
|
875
1124
|
className: "flex flex-col gap-2",
|
|
876
1125
|
children: [
|
|
877
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
1132
|
+
"aria-label": "Edit message"
|
|
883
1133
|
}),
|
|
884
|
-
/* @__PURE__ */
|
|
1134
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
885
1135
|
className: "flex gap-2",
|
|
886
1136
|
children: [
|
|
887
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1143
|
+
/* @__PURE__ */ jsx6(Check2, {
|
|
894
1144
|
className: "h-3 w-3"
|
|
895
1145
|
}),
|
|
896
1146
|
"Save"
|
|
897
1147
|
]
|
|
898
1148
|
}),
|
|
899
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1164
|
+
}) : isStreaming && !message.content ? /* @__PURE__ */ jsxs6("div", {
|
|
915
1165
|
className: "flex flex-col gap-2",
|
|
916
1166
|
children: [
|
|
917
|
-
/* @__PURE__ */
|
|
1167
|
+
/* @__PURE__ */ jsx6(Skeleton, {
|
|
918
1168
|
className: "h-4 w-48"
|
|
919
1169
|
}),
|
|
920
|
-
/* @__PURE__ */
|
|
1170
|
+
/* @__PURE__ */ jsx6(Skeleton, {
|
|
921
1171
|
className: "h-4 w-32"
|
|
922
1172
|
})
|
|
923
1173
|
]
|
|
924
|
-
}) : /* @__PURE__ */
|
|
1174
|
+
}) : /* @__PURE__ */ jsx6(MessageContent, {
|
|
925
1175
|
content: message.content
|
|
926
1176
|
})
|
|
927
1177
|
}),
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
className:
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1199
|
+
children: copied ? /* @__PURE__ */ jsx6(Check2, {
|
|
950
1200
|
className: "h-3 w-3"
|
|
951
|
-
}) : /* @__PURE__ */
|
|
1201
|
+
}) : /* @__PURE__ */ jsx6(Copy2, {
|
|
952
1202
|
className: "h-3 w-3"
|
|
953
1203
|
})
|
|
954
1204
|
}),
|
|
955
|
-
editable && isUser && !isEditing && /* @__PURE__ */
|
|
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__ */
|
|
1211
|
+
children: /* @__PURE__ */ jsx6(Pencil, {
|
|
962
1212
|
className: "h-3 w-3"
|
|
963
1213
|
})
|
|
964
1214
|
})
|
|
965
1215
|
]
|
|
966
1216
|
}),
|
|
967
|
-
message.reasoning && /* @__PURE__ */
|
|
968
|
-
|
|
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__ */
|
|
971
|
-
|
|
972
|
-
children: "View reasoning"
|
|
1224
|
+
/* @__PURE__ */ jsx6(ReasoningTrigger, {
|
|
1225
|
+
isStreaming
|
|
973
1226
|
}),
|
|
974
|
-
/* @__PURE__ */
|
|
975
|
-
|
|
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 &&
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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__ */
|
|
992
|
-
|
|
1252
|
+
/* @__PURE__ */ jsx6(SourcesTrigger, {
|
|
1253
|
+
count: message.sources.length
|
|
993
1254
|
}),
|
|
994
|
-
|
|
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
|
-
}
|
|
997
|
-
}),
|
|
998
|
-
message.toolCalls && message.toolCalls.length > 0 &&
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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__ */
|
|
1007
|
-
className: "text-muted-foreground
|
|
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.
|
|
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__ */
|
|
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
|
-
}
|
|
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
|
|
1055
|
-
import { cn as
|
|
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 {
|
|
1059
|
-
|
|
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] =
|
|
1071
|
-
const [attachments, setAttachments] =
|
|
1072
|
-
const
|
|
1073
|
-
const
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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 =
|
|
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__ */
|
|
1126
|
-
className:
|
|
1487
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
1488
|
+
className: cn6("flex flex-col gap-2", className),
|
|
1127
1489
|
children: [
|
|
1128
|
-
attachments.length > 0 && /* @__PURE__ */
|
|
1490
|
+
attachments.length > 0 && /* @__PURE__ */ jsx7("div", {
|
|
1129
1491
|
className: "flex flex-wrap gap-2",
|
|
1130
|
-
children: attachments.map((attachment) => /* @__PURE__ */
|
|
1131
|
-
className:
|
|
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__ */
|
|
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__ */
|
|
1499
|
+
}) : /* @__PURE__ */ jsx7(FileText, {
|
|
1136
1500
|
className: "h-3.5 w-3.5"
|
|
1137
1501
|
}),
|
|
1138
|
-
/* @__PURE__ */
|
|
1502
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1139
1503
|
className: "max-w-[150px] truncate",
|
|
1140
1504
|
children: attachment.name
|
|
1141
1505
|
}),
|
|
1142
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1527
|
+
showAttachments && /* @__PURE__ */ jsxs7(Fragment3, {
|
|
1159
1528
|
children: [
|
|
1160
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1545
|
+
children: /* @__PURE__ */ jsx7(Paperclip, {
|
|
1177
1546
|
className: "h-4 w-4"
|
|
1178
1547
|
})
|
|
1179
1548
|
})
|
|
1180
1549
|
]
|
|
1181
1550
|
}),
|
|
1182
|
-
/* @__PURE__ */
|
|
1551
|
+
/* @__PURE__ */ jsx7("div", {
|
|
1183
1552
|
className: "relative flex-1",
|
|
1184
|
-
children: /* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
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__ */
|
|
1569
|
+
children: isLoading ? /* @__PURE__ */ jsx7(Loader2, {
|
|
1201
1570
|
className: "h-4 w-4 animate-spin"
|
|
1202
|
-
}) : /* @__PURE__ */
|
|
1571
|
+
}) : /* @__PURE__ */ jsx7(Send, {
|
|
1203
1572
|
className: "h-4 w-4"
|
|
1204
1573
|
})
|
|
1205
1574
|
})
|
|
1206
1575
|
]
|
|
1207
1576
|
}),
|
|
1208
|
-
/* @__PURE__ */
|
|
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
|
|
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
|
|
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] =
|
|
1444
|
-
const toExport =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
1464
|
-
const handleFork =
|
|
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__ */
|
|
1843
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1475
1844
|
className: "flex items-center gap-2",
|
|
1476
1845
|
children: [
|
|
1477
|
-
onCreateNew && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1880
|
+
onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs8(Fragment4, {
|
|
1512
1881
|
children: [
|
|
1513
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1898
|
+
/* @__PURE__ */ jsxs8(DropdownMenu, {
|
|
1530
1899
|
children: [
|
|
1531
|
-
/* @__PURE__ */
|
|
1900
|
+
/* @__PURE__ */ jsx8(DropdownMenuTrigger, {
|
|
1532
1901
|
asChild: true,
|
|
1533
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
1908
|
+
/* @__PURE__ */ jsx8(Download2, {
|
|
1540
1909
|
className: "h-4 w-4"
|
|
1541
1910
|
}),
|
|
1542
1911
|
"Export"
|
|
1543
1912
|
]
|
|
1544
1913
|
})
|
|
1545
1914
|
}),
|
|
1546
|
-
/* @__PURE__ */
|
|
1915
|
+
/* @__PURE__ */ jsxs8(DropdownMenuContent, {
|
|
1547
1916
|
align: "end",
|
|
1548
1917
|
children: [
|
|
1549
|
-
/* @__PURE__ */
|
|
1918
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1550
1919
|
onSelect: () => handleExport("markdown"),
|
|
1551
1920
|
disabled,
|
|
1552
1921
|
children: [
|
|
1553
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1928
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1560
1929
|
onSelect: () => handleExport("txt"),
|
|
1561
1930
|
disabled,
|
|
1562
1931
|
children: [
|
|
1563
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1938
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1570
1939
|
onSelect: () => handleExport("json"),
|
|
1571
1940
|
disabled,
|
|
1572
1941
|
children: [
|
|
1573
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1580
|
-
/* @__PURE__ */
|
|
1948
|
+
/* @__PURE__ */ jsx8(DropdownMenuSeparator, {}),
|
|
1949
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1581
1950
|
onSelect: () => handleCopy(),
|
|
1582
1951
|
disabled,
|
|
1583
1952
|
children: [
|
|
1584
|
-
copied ? /* @__PURE__ */
|
|
1953
|
+
copied ? /* @__PURE__ */ jsx8(Check3, {
|
|
1585
1954
|
className: "h-4 w-4 text-green-500"
|
|
1586
|
-
}) : /* @__PURE__ */
|
|
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
|
|
1969
|
+
import * as React10 from "react";
|
|
1601
1970
|
|
|
1602
1971
|
// src/presentation/components/ThinkingLevelPicker.tsx
|
|
1603
|
-
import * as
|
|
1604
|
-
import { cn as
|
|
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
|
|
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 =
|
|
2049
|
+
const handleChange = React7.useCallback((v) => {
|
|
1681
2050
|
onChange(v);
|
|
1682
2051
|
}, [onChange]);
|
|
1683
2052
|
if (compact) {
|
|
1684
|
-
return /* @__PURE__ */
|
|
2053
|
+
return /* @__PURE__ */ jsxs9(Select, {
|
|
1685
2054
|
value,
|
|
1686
2055
|
onValueChange: handleChange,
|
|
1687
2056
|
children: [
|
|
1688
|
-
/* @__PURE__ */
|
|
1689
|
-
className:
|
|
1690
|
-
children: /* @__PURE__ */
|
|
2057
|
+
/* @__PURE__ */ jsx9(SelectTrigger, {
|
|
2058
|
+
className: cn7("w-[140px]", className),
|
|
2059
|
+
children: /* @__PURE__ */ jsx9(SelectValue, {})
|
|
1691
2060
|
}),
|
|
1692
|
-
/* @__PURE__ */
|
|
1693
|
-
children: THINKING_LEVELS.map((level) => /* @__PURE__ */
|
|
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__ */
|
|
1702
|
-
className:
|
|
2070
|
+
return /* @__PURE__ */ jsxs9("div", {
|
|
2071
|
+
className: cn7("flex flex-col gap-1.5", className),
|
|
1703
2072
|
children: [
|
|
1704
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2078
|
+
/* @__PURE__ */ jsxs9(Select, {
|
|
1710
2079
|
name: "thinking-level-picker",
|
|
1711
2080
|
value,
|
|
1712
2081
|
onValueChange: handleChange,
|
|
1713
2082
|
children: [
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
children: /* @__PURE__ */
|
|
2083
|
+
/* @__PURE__ */ jsx9(SelectTrigger, {
|
|
2084
|
+
children: /* @__PURE__ */ jsx9(SelectValue, {
|
|
1716
2085
|
placeholder: "Select thinking level"
|
|
1717
2086
|
})
|
|
1718
2087
|
}),
|
|
1719
|
-
/* @__PURE__ */
|
|
1720
|
-
children: THINKING_LEVELS.map((level) => /* @__PURE__ */
|
|
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
|
|
2102
|
+
import * as React8 from "react";
|
|
1734
2103
|
"use client";
|
|
1735
2104
|
function useMessageSelection(messageIds) {
|
|
1736
|
-
const [selectedIds, setSelectedIds] =
|
|
1737
|
-
const idSet =
|
|
1738
|
-
|
|
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 =
|
|
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 =
|
|
2127
|
+
const selectAll = React8.useCallback(() => {
|
|
1759
2128
|
setSelectedIds(new Set(messageIds));
|
|
1760
2129
|
}, [messageIds.join(",")]);
|
|
1761
|
-
const clearSelection =
|
|
2130
|
+
const clearSelection = React8.useCallback(() => {
|
|
1762
2131
|
setSelectedIds(new Set);
|
|
1763
2132
|
}, []);
|
|
1764
|
-
const isSelected =
|
|
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
|
|
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 =
|
|
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__ */
|
|
2205
|
+
const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs10(Fragment5, {
|
|
1800
2206
|
children: [
|
|
1801
|
-
hasPicker && /* @__PURE__ */
|
|
2207
|
+
hasPicker && onThinkingLevelChange && /* @__PURE__ */ jsx11(ThinkingLevelPicker, {
|
|
1802
2208
|
value: thinkingLevel,
|
|
1803
2209
|
onChange: onThinkingLevelChange,
|
|
1804
2210
|
compact: true
|
|
1805
2211
|
}),
|
|
1806
|
-
hasToolbar && /* @__PURE__ */
|
|
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__ */
|
|
2226
|
+
return /* @__PURE__ */ jsxs10(ChatContainer, {
|
|
1821
2227
|
className,
|
|
1822
2228
|
headerContent,
|
|
1823
2229
|
showScrollButton,
|
|
1824
2230
|
children: [
|
|
1825
|
-
messages.map((msg) => /* @__PURE__ */
|
|
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
|
|
2267
|
+
import * as React12 from "react";
|
|
1841
2268
|
import { Plus as Plus2, Trash2, MessageSquare } from "lucide-react";
|
|
1842
|
-
import { Button as
|
|
1843
|
-
import { cn as
|
|
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
|
|
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] =
|
|
1851
|
-
const [isLoading, setIsLoading] =
|
|
1852
|
-
const refresh =
|
|
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
|
-
|
|
2293
|
+
React11.useEffect(() => {
|
|
1867
2294
|
refresh();
|
|
1868
2295
|
}, [refresh]);
|
|
1869
|
-
const deleteConversation =
|
|
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
|
|
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__ */
|
|
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:
|
|
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__ */
|
|
2346
|
+
/* @__PURE__ */ jsx12(MessageSquare, {
|
|
1920
2347
|
className: "text-muted-foreground h-4 w-4 shrink-0"
|
|
1921
2348
|
}),
|
|
1922
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsxs11("div", {
|
|
1923
2350
|
className: "min-w-0 flex-1",
|
|
1924
2351
|
children: [
|
|
1925
|
-
/* @__PURE__ */
|
|
2352
|
+
/* @__PURE__ */ jsx12("p", {
|
|
1926
2353
|
className: "truncate",
|
|
1927
2354
|
children: displayTitle
|
|
1928
2355
|
}),
|
|
1929
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2371
|
+
/* @__PURE__ */ jsx12("span", {
|
|
2372
|
+
role: "group",
|
|
1945
2373
|
onClick: (e) => e.stopPropagation(),
|
|
1946
|
-
|
|
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__ */
|
|
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,
|
|
1974
|
-
|
|
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__ */
|
|
1983
|
-
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__ */
|
|
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__ */
|
|
2422
|
+
/* @__PURE__ */ jsx12("span", {
|
|
1989
2423
|
className: "text-muted-foreground text-sm font-medium",
|
|
1990
2424
|
children: "Conversations"
|
|
1991
2425
|
}),
|
|
1992
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2432
|
+
children: /* @__PURE__ */ jsx12(Plus2, {
|
|
1999
2433
|
className: "h-4 w-4"
|
|
2000
2434
|
})
|
|
2001
2435
|
})
|
|
2002
2436
|
]
|
|
2003
2437
|
}),
|
|
2004
|
-
/* @__PURE__ */
|
|
2438
|
+
/* @__PURE__ */ jsx12("div", {
|
|
2005
2439
|
className: "flex-1 overflow-y-auto p-2",
|
|
2006
|
-
children: isLoading ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
2446
|
+
}) : /* @__PURE__ */ jsx12("div", {
|
|
2013
2447
|
className: "flex flex-col gap-1",
|
|
2014
|
-
children: conversations.map((conv) => /* @__PURE__ */
|
|
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__ */
|
|
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] =
|
|
2034
|
-
const [tagsStr, setTagsStr] =
|
|
2035
|
-
|
|
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 =
|
|
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__ */
|
|
2490
|
+
return /* @__PURE__ */ jsxs11("div", {
|
|
2057
2491
|
className: "border-border shrink-0 border-t p-2",
|
|
2058
2492
|
children: [
|
|
2059
|
-
/* @__PURE__ */
|
|
2493
|
+
/* @__PURE__ */ jsx12("p", {
|
|
2060
2494
|
className: "text-muted-foreground mb-1 text-xs font-medium",
|
|
2061
2495
|
children: "Project"
|
|
2062
2496
|
}),
|
|
2063
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2505
|
+
/* @__PURE__ */ jsx12("p", {
|
|
2072
2506
|
className: "text-muted-foreground mb-1 text-xs font-medium",
|
|
2073
2507
|
children: "Tags"
|
|
2074
2508
|
}),
|
|
2075
|
-
/* @__PURE__ */
|
|
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
|
|
2521
|
+
import * as React14 from "react";
|
|
2088
2522
|
|
|
2089
2523
|
// src/presentation/hooks/useChat.tsx
|
|
2090
|
-
import * as
|
|
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] =
|
|
3165
|
-
const [mcpTools, setMcpTools] =
|
|
3166
|
-
const mcpCleanupRef =
|
|
3167
|
-
const [conversation, setConversation] =
|
|
3168
|
-
const [isLoading, setIsLoading] =
|
|
3169
|
-
const [error, setError] =
|
|
3170
|
-
const [conversationId, setConversationId] =
|
|
3171
|
-
const abortControllerRef =
|
|
3172
|
-
const chatServiceRef =
|
|
3173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3485
|
-
|
|
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 =
|
|
3935
|
+
const stop = React13.useCallback(() => {
|
|
3495
3936
|
abortControllerRef.current?.abort();
|
|
3496
3937
|
setIsLoading(false);
|
|
3497
3938
|
}, []);
|
|
3498
3939
|
const createNewConversation = clearConversation;
|
|
3499
|
-
const editMessage =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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] =
|
|
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 =
|
|
4267
|
+
const handleSelectConversation = React14.useCallback((id) => {
|
|
3822
4268
|
setConversationId(id);
|
|
3823
4269
|
}, [setConversationId]);
|
|
3824
|
-
|
|
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__ */
|
|
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__ */
|
|
4290
|
+
/* @__PURE__ */ jsx13("div", {
|
|
3842
4291
|
className: "flex min-w-0 flex-1 flex-col",
|
|
3843
|
-
children: /* @__PURE__ */
|
|
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
|
-
|
|
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
|
|
3865
|
-
import { cn as
|
|
3866
|
-
import { Button as
|
|
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
|
|
4335
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3881
4336
|
"use client";
|
|
3882
4337
|
var PROVIDER_ICONS = {
|
|
3883
|
-
ollama: /* @__PURE__ */
|
|
4338
|
+
ollama: /* @__PURE__ */ jsx14(Cpu, {
|
|
3884
4339
|
className: "h-4 w-4"
|
|
3885
4340
|
}),
|
|
3886
|
-
openai: /* @__PURE__ */
|
|
4341
|
+
openai: /* @__PURE__ */ jsx14(Bot2, {
|
|
3887
4342
|
className: "h-4 w-4"
|
|
3888
4343
|
}),
|
|
3889
|
-
anthropic: /* @__PURE__ */
|
|
4344
|
+
anthropic: /* @__PURE__ */ jsx14(Sparkles, {
|
|
3890
4345
|
className: "h-4 w-4"
|
|
3891
4346
|
}),
|
|
3892
|
-
mistral: /* @__PURE__ */
|
|
4347
|
+
mistral: /* @__PURE__ */ jsx14(Cloud, {
|
|
3893
4348
|
className: "h-4 w-4"
|
|
3894
4349
|
}),
|
|
3895
|
-
gemini: /* @__PURE__ */
|
|
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 =
|
|
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 =
|
|
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__ */
|
|
3946
|
-
className:
|
|
4400
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
4401
|
+
className: cn10("flex items-center gap-2", className),
|
|
3947
4402
|
children: [
|
|
3948
|
-
/* @__PURE__ */
|
|
4403
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
3949
4404
|
value: value.provider,
|
|
3950
4405
|
onValueChange: handleProviderChange,
|
|
3951
4406
|
children: [
|
|
3952
|
-
/* @__PURE__ */
|
|
4407
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
3953
4408
|
className: "w-[140px]",
|
|
3954
|
-
children: /* @__PURE__ */
|
|
4409
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {})
|
|
3955
4410
|
}),
|
|
3956
|
-
/* @__PURE__ */
|
|
3957
|
-
children: providers.map((p) => /* @__PURE__ */
|
|
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__ */
|
|
4415
|
+
children: /* @__PURE__ */ jsxs13("div", {
|
|
3961
4416
|
className: "flex items-center gap-2",
|
|
3962
4417
|
children: [
|
|
3963
4418
|
PROVIDER_ICONS[p.provider],
|
|
3964
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4428
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
3974
4429
|
value: value.model,
|
|
3975
4430
|
onValueChange: handleModelChange,
|
|
3976
4431
|
children: [
|
|
3977
|
-
/* @__PURE__ */
|
|
4432
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
3978
4433
|
className: "w-[160px]",
|
|
3979
|
-
children: /* @__PURE__ */
|
|
4434
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {})
|
|
3980
4435
|
}),
|
|
3981
|
-
/* @__PURE__ */
|
|
3982
|
-
children: models.map((m) => /* @__PURE__ */
|
|
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__ */
|
|
3993
|
-
className:
|
|
4447
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
4448
|
+
className: cn10("flex flex-col gap-3", className),
|
|
3994
4449
|
children: [
|
|
3995
|
-
/* @__PURE__ */
|
|
4450
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3996
4451
|
className: "flex flex-col gap-1.5",
|
|
3997
4452
|
children: [
|
|
3998
|
-
/* @__PURE__ */
|
|
4453
|
+
/* @__PURE__ */ jsx14(Label2, {
|
|
3999
4454
|
htmlFor: "provider-selection",
|
|
4000
4455
|
className: "text-sm font-medium",
|
|
4001
4456
|
children: "Provider"
|
|
4002
4457
|
}),
|
|
4003
|
-
/* @__PURE__ */
|
|
4458
|
+
/* @__PURE__ */ jsx14("div", {
|
|
4004
4459
|
className: "flex flex-wrap gap-2",
|
|
4005
4460
|
id: "provider-selection",
|
|
4006
|
-
children: providers.map((p) => /* @__PURE__ */
|
|
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:
|
|
4466
|
+
className: cn10(!p.available && "opacity-50"),
|
|
4012
4467
|
children: [
|
|
4013
4468
|
PROVIDER_ICONS[p.provider],
|
|
4014
|
-
/* @__PURE__ */
|
|
4469
|
+
/* @__PURE__ */ jsx14("span", {
|
|
4015
4470
|
children: PROVIDER_NAMES[p.provider]
|
|
4016
4471
|
}),
|
|
4017
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4482
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
4028
4483
|
className: "flex flex-col gap-1.5",
|
|
4029
4484
|
children: [
|
|
4030
|
-
/* @__PURE__ */
|
|
4485
|
+
/* @__PURE__ */ jsx14(Label2, {
|
|
4031
4486
|
htmlFor: "model-picker",
|
|
4032
4487
|
className: "text-sm font-medium",
|
|
4033
4488
|
children: "Model"
|
|
4034
4489
|
}),
|
|
4035
|
-
/* @__PURE__ */
|
|
4490
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
4036
4491
|
name: "model-picker",
|
|
4037
4492
|
value: value.model,
|
|
4038
4493
|
onValueChange: handleModelChange,
|
|
4039
4494
|
children: [
|
|
4040
|
-
/* @__PURE__ */
|
|
4041
|
-
children: /* @__PURE__ */
|
|
4495
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
4496
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {
|
|
4042
4497
|
placeholder: "Select a model"
|
|
4043
4498
|
})
|
|
4044
4499
|
}),
|
|
4045
|
-
/* @__PURE__ */
|
|
4046
|
-
children: models.map((m) => /* @__PURE__ */
|
|
4500
|
+
/* @__PURE__ */ jsx14(SelectContent2, {
|
|
4501
|
+
children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
|
|
4047
4502
|
value: m.id,
|
|
4048
|
-
children: /* @__PURE__ */
|
|
4503
|
+
children: /* @__PURE__ */ jsxs13("div", {
|
|
4049
4504
|
className: "flex items-center gap-2",
|
|
4050
4505
|
children: [
|
|
4051
|
-
/* @__PURE__ */
|
|
4506
|
+
/* @__PURE__ */ jsx14("span", {
|
|
4052
4507
|
children: m.name
|
|
4053
4508
|
}),
|
|
4054
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
4534
|
+
selectedModel && /* @__PURE__ */ jsxs13("div", {
|
|
4080
4535
|
className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
|
|
4081
4536
|
children: [
|
|
4082
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4544
|
+
selectedModel.capabilities.vision && /* @__PURE__ */ jsx14("span", {
|
|
4090
4545
|
children: "• Vision"
|
|
4091
4546
|
}),
|
|
4092
|
-
selectedModel.capabilities.tools && /* @__PURE__ */
|
|
4547
|
+
selectedModel.capabilities.tools && /* @__PURE__ */ jsx14("span", {
|
|
4093
4548
|
children: "• Tools"
|
|
4094
4549
|
}),
|
|
4095
|
-
selectedModel.capabilities.reasoning && /* @__PURE__ */
|
|
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
|
|
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
|
|
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__ */
|
|
4123
|
-
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__ */
|
|
4580
|
+
/* @__PURE__ */ jsx15(Info, {
|
|
4126
4581
|
className: "h-4 w-4"
|
|
4127
4582
|
}),
|
|
4128
|
-
/* @__PURE__ */
|
|
4583
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4129
4584
|
children: "No workspace context"
|
|
4130
4585
|
})
|
|
4131
4586
|
]
|
|
4132
4587
|
});
|
|
4133
4588
|
}
|
|
4134
|
-
const content = /* @__PURE__ */
|
|
4135
|
-
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__ */
|
|
4592
|
+
/* @__PURE__ */ jsxs14(Badge2, {
|
|
4138
4593
|
variant: active ? "default" : "secondary",
|
|
4139
4594
|
className: "flex items-center gap-1",
|
|
4140
4595
|
children: [
|
|
4141
|
-
/* @__PURE__ */
|
|
4596
|
+
/* @__PURE__ */ jsx15(Zap, {
|
|
4142
4597
|
className: "h-3 w-3"
|
|
4143
4598
|
}),
|
|
4144
4599
|
"Context"
|
|
4145
4600
|
]
|
|
4146
4601
|
}),
|
|
4147
|
-
summary && showDetails && /* @__PURE__ */
|
|
4602
|
+
summary && showDetails && /* @__PURE__ */ jsxs14(Fragment7, {
|
|
4148
4603
|
children: [
|
|
4149
|
-
/* @__PURE__ */
|
|
4604
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
4150
4605
|
className: "flex items-center gap-1 text-xs",
|
|
4151
4606
|
children: [
|
|
4152
|
-
/* @__PURE__ */
|
|
4607
|
+
/* @__PURE__ */ jsx15(FolderOpen, {
|
|
4153
4608
|
className: "h-3.5 w-3.5"
|
|
4154
4609
|
}),
|
|
4155
|
-
/* @__PURE__ */
|
|
4610
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4156
4611
|
children: summary.name
|
|
4157
4612
|
})
|
|
4158
4613
|
]
|
|
4159
4614
|
}),
|
|
4160
|
-
/* @__PURE__ */
|
|
4615
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
4161
4616
|
className: "flex items-center gap-1 text-xs",
|
|
4162
4617
|
children: [
|
|
4163
|
-
/* @__PURE__ */
|
|
4618
|
+
/* @__PURE__ */ jsx15(FileCode, {
|
|
4164
4619
|
className: "h-3.5 w-3.5"
|
|
4165
4620
|
}),
|
|
4166
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4182
|
-
children: /* @__PURE__ */
|
|
4636
|
+
return /* @__PURE__ */ jsx15(TooltipProvider, {
|
|
4637
|
+
children: /* @__PURE__ */ jsxs14(Tooltip, {
|
|
4183
4638
|
children: [
|
|
4184
|
-
/* @__PURE__ */
|
|
4639
|
+
/* @__PURE__ */ jsx15(TooltipTrigger, {
|
|
4185
4640
|
asChild: true,
|
|
4186
4641
|
children: content
|
|
4187
4642
|
}),
|
|
4188
|
-
/* @__PURE__ */
|
|
4643
|
+
/* @__PURE__ */ jsx15(TooltipContent, {
|
|
4189
4644
|
side: "bottom",
|
|
4190
4645
|
className: "max-w-[300px]",
|
|
4191
|
-
children: /* @__PURE__ */
|
|
4646
|
+
children: /* @__PURE__ */ jsxs14("div", {
|
|
4192
4647
|
className: "flex flex-col gap-2 text-sm",
|
|
4193
4648
|
children: [
|
|
4194
|
-
/* @__PURE__ */
|
|
4649
|
+
/* @__PURE__ */ jsx15("div", {
|
|
4195
4650
|
className: "font-medium",
|
|
4196
4651
|
children: summary.name
|
|
4197
4652
|
}),
|
|
4198
|
-
/* @__PURE__ */
|
|
4653
|
+
/* @__PURE__ */ jsx15("div", {
|
|
4199
4654
|
className: "text-muted-foreground text-xs",
|
|
4200
4655
|
children: summary.path
|
|
4201
4656
|
}),
|
|
4202
|
-
/* @__PURE__ */
|
|
4657
|
+
/* @__PURE__ */ jsx15("div", {
|
|
4203
4658
|
className: "border-t pt-2",
|
|
4204
|
-
children: /* @__PURE__ */
|
|
4659
|
+
children: /* @__PURE__ */ jsxs14("div", {
|
|
4205
4660
|
className: "grid grid-cols-2 gap-1 text-xs",
|
|
4206
4661
|
children: [
|
|
4207
|
-
/* @__PURE__ */
|
|
4662
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4208
4663
|
children: "Commands:"
|
|
4209
4664
|
}),
|
|
4210
|
-
/* @__PURE__ */
|
|
4665
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4211
4666
|
className: "text-right",
|
|
4212
4667
|
children: summary.specs.commands
|
|
4213
4668
|
}),
|
|
4214
|
-
/* @__PURE__ */
|
|
4669
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4215
4670
|
children: "Queries:"
|
|
4216
4671
|
}),
|
|
4217
|
-
/* @__PURE__ */
|
|
4672
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4218
4673
|
className: "text-right",
|
|
4219
4674
|
children: summary.specs.queries
|
|
4220
4675
|
}),
|
|
4221
|
-
/* @__PURE__ */
|
|
4676
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4222
4677
|
children: "Events:"
|
|
4223
4678
|
}),
|
|
4224
|
-
/* @__PURE__ */
|
|
4679
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4225
4680
|
className: "text-right",
|
|
4226
4681
|
children: summary.specs.events
|
|
4227
4682
|
}),
|
|
4228
|
-
/* @__PURE__ */
|
|
4683
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4229
4684
|
children: "Presentations:"
|
|
4230
4685
|
}),
|
|
4231
|
-
/* @__PURE__ */
|
|
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__ */
|
|
4693
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
4239
4694
|
className: "border-t pt-2 text-xs",
|
|
4240
4695
|
children: [
|
|
4241
|
-
/* @__PURE__ */
|
|
4696
|
+
/* @__PURE__ */ jsxs14("span", {
|
|
4242
4697
|
children: [
|
|
4243
4698
|
summary.files.total,
|
|
4244
4699
|
" files"
|
|
4245
4700
|
]
|
|
4246
4701
|
}),
|
|
4247
|
-
/* @__PURE__ */
|
|
4702
|
+
/* @__PURE__ */ jsx15("span", {
|
|
4248
4703
|
className: "mx-1",
|
|
4249
4704
|
children: "•"
|
|
4250
4705
|
}),
|
|
4251
|
-
/* @__PURE__ */
|
|
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
|
|
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] =
|
|
4275
|
-
const [isLoading, setIsLoading] =
|
|
4276
|
-
const loadProviders =
|
|
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
|
-
|
|
4833
|
+
React16.useEffect(() => {
|
|
4292
4834
|
loadProviders();
|
|
4293
4835
|
}, [loadProviders]);
|
|
4294
|
-
const availableProviders =
|
|
4295
|
-
const isAvailable =
|
|
4296
|
-
const getModelsCallback =
|
|
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
|
};
|