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