@copilotz/chat-ui 0.1.9 → 0.1.11
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/dist/index.cjs +246 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +284 -87
- package/dist/index.js.map +1 -1
- package/dist/styles.css +21 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
chatUtils: () => chatUtils,
|
|
44
44
|
cn: () => cn,
|
|
45
45
|
configUtils: () => configUtils,
|
|
46
|
+
createObjectUrlFromDataUrl: () => createObjectUrlFromDataUrl,
|
|
46
47
|
defaultChatConfig: () => defaultChatConfig,
|
|
47
48
|
featureFlags: () => featureFlags,
|
|
48
49
|
formatDate: () => formatDate,
|
|
@@ -119,7 +120,9 @@ var defaultChatConfig = {
|
|
|
119
120
|
daysAgo: "days ago",
|
|
120
121
|
inputHelpText: "Press Enter to send, Shift+Enter to add a new line.",
|
|
121
122
|
thinking: "Thinking...",
|
|
122
|
-
defaultThreadName: "Main Thread"
|
|
123
|
+
defaultThreadName: "Main Thread",
|
|
124
|
+
showMoreMessage: "Show more",
|
|
125
|
+
showLessMessage: "Show less"
|
|
123
126
|
},
|
|
124
127
|
features: {
|
|
125
128
|
enableThreads: true,
|
|
@@ -138,7 +141,12 @@ var defaultChatConfig = {
|
|
|
138
141
|
showTimestamps: false,
|
|
139
142
|
showAvatars: true,
|
|
140
143
|
compactMode: false,
|
|
141
|
-
showWordCount: false
|
|
144
|
+
showWordCount: false,
|
|
145
|
+
collapseLongMessages: false,
|
|
146
|
+
collapseLongMessagesForUserOnly: false,
|
|
147
|
+
longMessagePreviewChars: 4e3,
|
|
148
|
+
longMessageChunkChars: 12e3,
|
|
149
|
+
renderUserMarkdown: true
|
|
142
150
|
},
|
|
143
151
|
customComponent: {},
|
|
144
152
|
headerActions: null
|
|
@@ -293,7 +301,7 @@ var configUtils = {
|
|
|
293
301
|
};
|
|
294
302
|
|
|
295
303
|
// src/components/chat/Message.tsx
|
|
296
|
-
var import_react = require("react");
|
|
304
|
+
var import_react = __toESM(require("react"), 1);
|
|
297
305
|
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
298
306
|
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
299
307
|
var import_rehype_highlight = __toESM(require("rehype-highlight"), 1);
|
|
@@ -326,6 +334,24 @@ var formatDate = (timestamp, labels) => {
|
|
|
326
334
|
});
|
|
327
335
|
}
|
|
328
336
|
};
|
|
337
|
+
var createObjectUrlFromDataUrl = (dataUrl) => {
|
|
338
|
+
const match = dataUrl.match(/^data:(.+?);base64,(.+)$/s);
|
|
339
|
+
if (!match) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
const [, mimeType, base64] = match;
|
|
344
|
+
const binary = atob(base64);
|
|
345
|
+
const bytes = new Uint8Array(binary.length);
|
|
346
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
347
|
+
bytes[i] = binary.charCodeAt(i);
|
|
348
|
+
}
|
|
349
|
+
const blob = new Blob([bytes], { type: mimeType || "application/octet-stream" });
|
|
350
|
+
return URL.createObjectURL(blob);
|
|
351
|
+
} catch {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
329
355
|
|
|
330
356
|
// src/components/ui/button.tsx
|
|
331
357
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -614,21 +640,89 @@ var markdownComponents = {
|
|
|
614
640
|
var remarkPluginsDefault = [import_remark_gfm.default];
|
|
615
641
|
var rehypePluginsDefault = [import_rehype_highlight.default];
|
|
616
642
|
var rehypePluginsEmpty = [];
|
|
643
|
+
var getPlainTextChunks = (content, chunkSize) => {
|
|
644
|
+
if (chunkSize <= 0 || content.length <= chunkSize) {
|
|
645
|
+
return [content];
|
|
646
|
+
}
|
|
647
|
+
const chunks = [];
|
|
648
|
+
let start = 0;
|
|
649
|
+
while (start < content.length) {
|
|
650
|
+
let end = Math.min(start + chunkSize, content.length);
|
|
651
|
+
if (end < content.length) {
|
|
652
|
+
const splitAt = content.lastIndexOf("\n", end);
|
|
653
|
+
if (splitAt > start + Math.floor(chunkSize / 2)) {
|
|
654
|
+
end = splitAt + 1;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
chunks.push(content.slice(start, end));
|
|
658
|
+
start = end;
|
|
659
|
+
}
|
|
660
|
+
return chunks;
|
|
661
|
+
};
|
|
662
|
+
var hasCodeBlocks = (content) => /(^|\n)(```|~~~)/.test(content);
|
|
663
|
+
var getCollapsedPreview = (content, previewChars, previewOverride) => {
|
|
664
|
+
if (previewOverride && previewOverride.trim().length > 0) {
|
|
665
|
+
const normalizedPreview = previewOverride.trimEnd();
|
|
666
|
+
return normalizedPreview.endsWith("...") ? normalizedPreview : `${normalizedPreview}...`;
|
|
667
|
+
}
|
|
668
|
+
if (content.length <= previewChars) {
|
|
669
|
+
return content;
|
|
670
|
+
}
|
|
671
|
+
return `${content.slice(0, previewChars).trimEnd()}...`;
|
|
672
|
+
};
|
|
673
|
+
var LongContentShell = (0, import_react.memo)(function LongContentShell2({ children, className, style }) {
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className, style, children });
|
|
675
|
+
});
|
|
676
|
+
var PlainTextContent = (0, import_react.memo)(function PlainTextContent2({
|
|
677
|
+
content,
|
|
678
|
+
className = "",
|
|
679
|
+
chunkSize = 12e3,
|
|
680
|
+
style
|
|
681
|
+
}) {
|
|
682
|
+
const chunks = (0, import_react.useMemo)(() => getPlainTextChunks(content, chunkSize), [content, chunkSize]);
|
|
683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
684
|
+
LongContentShell,
|
|
685
|
+
{
|
|
686
|
+
className: `text-sm leading-6 whitespace-pre-wrap break-words ${className}`.trim(),
|
|
687
|
+
style,
|
|
688
|
+
children: chunks.map((chunk, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react.default.Fragment, { children: chunk }, index))
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
});
|
|
617
692
|
var StreamingText = (0, import_react.memo)(function StreamingText2({
|
|
618
693
|
content,
|
|
619
694
|
isStreaming = false,
|
|
620
695
|
thinkingLabel = "Thinking...",
|
|
621
|
-
className = ""
|
|
696
|
+
className = "",
|
|
697
|
+
renderMarkdown = true,
|
|
698
|
+
plainTextChunkChars = 12e3,
|
|
699
|
+
contentStyle
|
|
622
700
|
}) {
|
|
623
701
|
const hasContent = content.trim().length > 0;
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
702
|
+
const enableSyntaxHighlight = renderMarkdown && !isStreaming && hasCodeBlocks(content);
|
|
703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
704
|
+
hasContent ? renderMarkdown ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
705
|
+
LongContentShell,
|
|
706
|
+
{
|
|
707
|
+
className: `prose prose-sm max-w-none dark:prose-invert break-words ${className}`.trim(),
|
|
708
|
+
style: contentStyle,
|
|
709
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
710
|
+
import_react_markdown.default,
|
|
711
|
+
{
|
|
712
|
+
remarkPlugins: remarkPluginsDefault,
|
|
713
|
+
rehypePlugins: enableSyntaxHighlight ? rehypePluginsDefault : rehypePluginsEmpty,
|
|
714
|
+
components: markdownComponents,
|
|
715
|
+
children: content
|
|
716
|
+
}
|
|
717
|
+
)
|
|
718
|
+
}
|
|
719
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
720
|
+
PlainTextContent,
|
|
627
721
|
{
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
722
|
+
content,
|
|
723
|
+
className,
|
|
724
|
+
chunkSize: plainTextChunkChars,
|
|
725
|
+
style: contentStyle
|
|
632
726
|
}
|
|
633
727
|
) : isStreaming ? (
|
|
634
728
|
// Show thinking indicator while waiting for first token
|
|
@@ -638,26 +732,22 @@ var StreamingText = (0, import_react.memo)(function StreamingText2({
|
|
|
638
732
|
] });
|
|
639
733
|
});
|
|
640
734
|
var MediaRenderer = (0, import_react.memo)(function MediaRenderer2({ attachment }) {
|
|
641
|
-
const [
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (isPlaying) {
|
|
647
|
-
audioRef.current.pause();
|
|
648
|
-
} else {
|
|
649
|
-
audioRef.current.play();
|
|
650
|
-
}
|
|
651
|
-
setIsPlaying(!isPlaying);
|
|
652
|
-
} else if (attachment.kind === "video" && videoRef.current) {
|
|
653
|
-
if (isPlaying) {
|
|
654
|
-
videoRef.current.pause();
|
|
655
|
-
} else {
|
|
656
|
-
videoRef.current.play();
|
|
657
|
-
}
|
|
658
|
-
setIsPlaying(!isPlaying);
|
|
735
|
+
const [audioPlaybackSrc, setAudioPlaybackSrc] = (0, import_react.useState)(attachment.dataUrl);
|
|
736
|
+
(0, import_react.useEffect)(() => {
|
|
737
|
+
if (attachment.kind !== "audio" || !attachment.dataUrl.startsWith("data:")) {
|
|
738
|
+
setAudioPlaybackSrc(attachment.dataUrl);
|
|
739
|
+
return;
|
|
659
740
|
}
|
|
660
|
-
|
|
741
|
+
const objectUrl = createObjectUrlFromDataUrl(attachment.dataUrl);
|
|
742
|
+
if (!objectUrl) {
|
|
743
|
+
setAudioPlaybackSrc(attachment.dataUrl);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
setAudioPlaybackSrc(objectUrl);
|
|
747
|
+
return () => {
|
|
748
|
+
URL.revokeObjectURL(objectUrl);
|
|
749
|
+
};
|
|
750
|
+
}, [attachment.kind, attachment.dataUrl]);
|
|
661
751
|
const formatDuration = (ms) => {
|
|
662
752
|
if (!ms) return "";
|
|
663
753
|
const seconds = Math.floor(ms / 1e3);
|
|
@@ -682,13 +772,10 @@ var MediaRenderer = (0, import_react.memo)(function MediaRenderer2({ attachment
|
|
|
682
772
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex w-full max-w-md py-0 min-w-64 items-center gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
683
773
|
"audio",
|
|
684
774
|
{
|
|
685
|
-
ref: audioRef,
|
|
686
|
-
src: attachment.dataUrl,
|
|
687
|
-
onPlay: () => setIsPlaying(true),
|
|
688
|
-
onPause: () => setIsPlaying(false),
|
|
689
|
-
onEnded: () => setIsPlaying(false),
|
|
690
775
|
className: "w-full mt-2",
|
|
691
|
-
|
|
776
|
+
preload: "metadata",
|
|
777
|
+
controls: true,
|
|
778
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("source", { src: audioPlaybackSrc, type: attachment.mimeType })
|
|
692
779
|
}
|
|
693
780
|
) });
|
|
694
781
|
case "video":
|
|
@@ -696,14 +783,10 @@ var MediaRenderer = (0, import_react.memo)(function MediaRenderer2({ attachment
|
|
|
696
783
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
697
784
|
"video",
|
|
698
785
|
{
|
|
699
|
-
ref: videoRef,
|
|
700
786
|
src: attachment.dataUrl,
|
|
701
787
|
poster: attachment.poster,
|
|
702
788
|
controls: true,
|
|
703
|
-
className: "w-full h-auto"
|
|
704
|
-
onPlay: () => setIsPlaying(true),
|
|
705
|
-
onPause: () => setIsPlaying(false),
|
|
706
|
-
onEnded: () => setIsPlaying(false)
|
|
789
|
+
className: "w-full h-auto"
|
|
707
790
|
}
|
|
708
791
|
),
|
|
709
792
|
attachment.fileName && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-2", children: attachment.fileName })
|
|
@@ -798,6 +881,15 @@ var arePropsEqual = (prevProps, nextProps) => {
|
|
|
798
881
|
if (prevProps.className !== nextProps.className) return false;
|
|
799
882
|
if (prevProps.toolUsedLabel !== nextProps.toolUsedLabel) return false;
|
|
800
883
|
if (prevProps.thinkingLabel !== nextProps.thinkingLabel) return false;
|
|
884
|
+
if (prevProps.showMoreLabel !== nextProps.showMoreLabel) return false;
|
|
885
|
+
if (prevProps.showLessLabel !== nextProps.showLessLabel) return false;
|
|
886
|
+
if (prevProps.collapseLongMessages !== nextProps.collapseLongMessages) return false;
|
|
887
|
+
if (prevProps.collapseLongMessagesForUserOnly !== nextProps.collapseLongMessagesForUserOnly) return false;
|
|
888
|
+
if (prevProps.longMessagePreviewChars !== nextProps.longMessagePreviewChars) return false;
|
|
889
|
+
if (prevProps.longMessageChunkChars !== nextProps.longMessageChunkChars) return false;
|
|
890
|
+
if (prevProps.renderUserMarkdown !== nextProps.renderUserMarkdown) return false;
|
|
891
|
+
if (prevProps.isExpanded !== nextProps.isExpanded) return false;
|
|
892
|
+
if (prevProps.onToggleExpanded !== nextProps.onToggleExpanded) return false;
|
|
801
893
|
if (prevProps.isGrouped !== nextProps.isGrouped) return false;
|
|
802
894
|
if (prevProps.assistantAvatar !== nextProps.assistantAvatar) return false;
|
|
803
895
|
return true;
|
|
@@ -820,6 +912,15 @@ var Message = (0, import_react.memo)(({
|
|
|
820
912
|
className = "",
|
|
821
913
|
toolUsedLabel,
|
|
822
914
|
thinkingLabel = "Thinking...",
|
|
915
|
+
showMoreLabel = "Show more",
|
|
916
|
+
showLessLabel = "Show less",
|
|
917
|
+
collapseLongMessages = false,
|
|
918
|
+
collapseLongMessagesForUserOnly = false,
|
|
919
|
+
longMessagePreviewChars = 4e3,
|
|
920
|
+
longMessageChunkChars = 12e3,
|
|
921
|
+
renderUserMarkdown = true,
|
|
922
|
+
isExpanded = false,
|
|
923
|
+
onToggleExpanded,
|
|
823
924
|
isGrouped = false
|
|
824
925
|
}) => {
|
|
825
926
|
const [isEditing, setIsEditing] = (0, import_react.useState)(false);
|
|
@@ -829,6 +930,18 @@ var Message = (0, import_react.memo)(({
|
|
|
829
930
|
const messageIsUser = isUser ?? message.role === "user";
|
|
830
931
|
const canEdit = enableEdit && messageIsUser;
|
|
831
932
|
const canRegenerate = enableRegenerate && !messageIsUser;
|
|
933
|
+
const normalizedPreviewChars = Math.max(longMessagePreviewChars, 1);
|
|
934
|
+
const normalizedChunkChars = Math.max(longMessageChunkChars, 1);
|
|
935
|
+
const previewOverride = typeof message.metadata?.previewContent === "string" ? message.metadata.previewContent : void 0;
|
|
936
|
+
const canCollapseMessage = collapseLongMessages && !message.isStreaming && message.content.length > normalizedPreviewChars && (!collapseLongMessagesForUserOnly || messageIsUser);
|
|
937
|
+
const isCollapsed = canCollapseMessage && !isExpanded;
|
|
938
|
+
const contentToRender = isCollapsed ? getCollapsedPreview(message.content, normalizedPreviewChars, previewOverride) : message.content;
|
|
939
|
+
const shouldRenderMarkdown = !isCollapsed && (!messageIsUser || renderUserMarkdown);
|
|
940
|
+
const shouldApplyLargeContentContainment = !isCollapsed && message.content.length > normalizedChunkChars;
|
|
941
|
+
const contentStyle = shouldApplyLargeContentContainment ? {
|
|
942
|
+
contentVisibility: "auto",
|
|
943
|
+
containIntrinsicSize: "1px 400px"
|
|
944
|
+
} : void 0;
|
|
832
945
|
const handleCopy = async () => {
|
|
833
946
|
try {
|
|
834
947
|
await navigator.clipboard.writeText(message.content);
|
|
@@ -857,6 +970,9 @@ var Message = (0, import_react.memo)(({
|
|
|
857
970
|
const handleRegenerate = () => {
|
|
858
971
|
onAction?.({ action: "regenerate", messageId: message.id });
|
|
859
972
|
};
|
|
973
|
+
const handleToggleExpanded = () => {
|
|
974
|
+
onToggleExpanded?.(message.id);
|
|
975
|
+
};
|
|
860
976
|
const formatTime = (timestamp) => {
|
|
861
977
|
return new Date(timestamp).toLocaleTimeString("pt-BR", {
|
|
862
978
|
hour: "2-digit",
|
|
@@ -907,11 +1023,26 @@ var Message = (0, import_react.memo)(({
|
|
|
907
1023
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
908
1024
|
StreamingText,
|
|
909
1025
|
{
|
|
910
|
-
content:
|
|
1026
|
+
content: contentToRender,
|
|
911
1027
|
isStreaming: message.isStreaming,
|
|
912
|
-
thinkingLabel
|
|
1028
|
+
thinkingLabel,
|
|
1029
|
+
renderMarkdown: shouldRenderMarkdown,
|
|
1030
|
+
plainTextChunkChars: normalizedChunkChars,
|
|
1031
|
+
contentStyle
|
|
913
1032
|
}
|
|
914
1033
|
),
|
|
1034
|
+
canCollapseMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1035
|
+
Button,
|
|
1036
|
+
{
|
|
1037
|
+
type: "button",
|
|
1038
|
+
variant: "ghost",
|
|
1039
|
+
size: "sm",
|
|
1040
|
+
className: "h-auto px-0 text-xs font-medium text-current hover:bg-transparent hover:opacity-80",
|
|
1041
|
+
"aria-expanded": !isCollapsed,
|
|
1042
|
+
onClick: handleToggleExpanded,
|
|
1043
|
+
children: isCollapsed ? showMoreLabel : showLessLabel
|
|
1044
|
+
}
|
|
1045
|
+
) }),
|
|
915
1046
|
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-3 space-y-2", children: message.attachments.map((attachment, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MediaRenderer, { attachment }, index)) })
|
|
916
1047
|
] }),
|
|
917
1048
|
!isEditing && (showActions || copied) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `absolute -top-2 flex gap-1 ${messageIsUser ? "-left-2" : "-right-2"}`, children: [
|
|
@@ -2732,7 +2863,23 @@ var FileUploadItem = (0, import_react5.memo)(function FileUploadItem2({ file, pr
|
|
|
2732
2863
|
});
|
|
2733
2864
|
var AttachmentPreview = (0, import_react5.memo)(function AttachmentPreview2({ attachment, onRemove }) {
|
|
2734
2865
|
const [isPlaying, setIsPlaying] = (0, import_react5.useState)(false);
|
|
2866
|
+
const [audioPlaybackSrc, setAudioPlaybackSrc] = (0, import_react5.useState)(attachment.dataUrl);
|
|
2735
2867
|
const audioRef = (0, import_react5.useRef)(null);
|
|
2868
|
+
(0, import_react5.useEffect)(() => {
|
|
2869
|
+
if (attachment.kind !== "audio" || !attachment.dataUrl.startsWith("data:")) {
|
|
2870
|
+
setAudioPlaybackSrc(attachment.dataUrl);
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const objectUrl = createObjectUrlFromDataUrl(attachment.dataUrl);
|
|
2874
|
+
if (!objectUrl) {
|
|
2875
|
+
setAudioPlaybackSrc(attachment.dataUrl);
|
|
2876
|
+
return;
|
|
2877
|
+
}
|
|
2878
|
+
setAudioPlaybackSrc(objectUrl);
|
|
2879
|
+
return () => {
|
|
2880
|
+
URL.revokeObjectURL(objectUrl);
|
|
2881
|
+
};
|
|
2882
|
+
}, [attachment.kind, attachment.dataUrl]);
|
|
2736
2883
|
const handlePlayPause = () => {
|
|
2737
2884
|
if (audioRef.current) {
|
|
2738
2885
|
if (isPlaying) {
|
|
@@ -2811,10 +2958,11 @@ var AttachmentPreview = (0, import_react5.memo)(function AttachmentPreview2({ at
|
|
|
2811
2958
|
"audio",
|
|
2812
2959
|
{
|
|
2813
2960
|
ref: audioRef,
|
|
2814
|
-
src: attachment.dataUrl,
|
|
2815
2961
|
onPlay: () => setIsPlaying(true),
|
|
2816
2962
|
onPause: () => setIsPlaying(false),
|
|
2817
|
-
onEnded: () => setIsPlaying(false)
|
|
2963
|
+
onEnded: () => setIsPlaying(false),
|
|
2964
|
+
preload: "metadata",
|
|
2965
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("source", { src: audioPlaybackSrc, type: attachment.mimeType })
|
|
2818
2966
|
}
|
|
2819
2967
|
),
|
|
2820
2968
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
@@ -3734,6 +3882,7 @@ var ChatUI = ({
|
|
|
3734
3882
|
};
|
|
3735
3883
|
const [inputValue, setInputValue] = (0, import_react7.useState)("");
|
|
3736
3884
|
const [attachments, setAttachments] = (0, import_react7.useState)([]);
|
|
3885
|
+
const [expandedMessageIds, setExpandedMessageIds] = (0, import_react7.useState)({});
|
|
3737
3886
|
const [state, setState] = (0, import_react7.useState)({
|
|
3738
3887
|
isRecording: false,
|
|
3739
3888
|
selectedThreadId: currentThreadId,
|
|
@@ -3838,6 +3987,24 @@ var ChatUI = ({
|
|
|
3838
3987
|
}
|
|
3839
3988
|
});
|
|
3840
3989
|
}, [messages, state.isAtBottom, virtualizer]);
|
|
3990
|
+
(0, import_react7.useEffect)(() => {
|
|
3991
|
+
virtualizer.measure();
|
|
3992
|
+
}, [expandedMessageIds, virtualizer]);
|
|
3993
|
+
(0, import_react7.useEffect)(() => {
|
|
3994
|
+
const validMessageIds = new Set(messages.map((message) => message.id));
|
|
3995
|
+
setExpandedMessageIds((prev) => {
|
|
3996
|
+
const activeIds = Object.keys(prev);
|
|
3997
|
+
const staleIds = activeIds.filter((messageId) => !validMessageIds.has(messageId));
|
|
3998
|
+
if (staleIds.length === 0) {
|
|
3999
|
+
return prev;
|
|
4000
|
+
}
|
|
4001
|
+
const next = { ...prev };
|
|
4002
|
+
staleIds.forEach((messageId) => {
|
|
4003
|
+
delete next[messageId];
|
|
4004
|
+
});
|
|
4005
|
+
return next;
|
|
4006
|
+
});
|
|
4007
|
+
}, [messages]);
|
|
3841
4008
|
const handleScroll = (0, import_react7.useCallback)((e) => {
|
|
3842
4009
|
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
|
3843
4010
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
|
|
@@ -3875,6 +4042,19 @@ var ChatUI = ({
|
|
|
3875
4042
|
break;
|
|
3876
4043
|
}
|
|
3877
4044
|
}, [callbacks, createStateCallback]);
|
|
4045
|
+
const handleToggleMessageExpansion = (0, import_react7.useCallback)((messageId) => {
|
|
4046
|
+
setExpandedMessageIds((prev) => {
|
|
4047
|
+
if (prev[messageId]) {
|
|
4048
|
+
const next = { ...prev };
|
|
4049
|
+
delete next[messageId];
|
|
4050
|
+
return next;
|
|
4051
|
+
}
|
|
4052
|
+
return {
|
|
4053
|
+
...prev,
|
|
4054
|
+
[messageId]: true
|
|
4055
|
+
};
|
|
4056
|
+
});
|
|
4057
|
+
}, []);
|
|
3878
4058
|
const handleCreateThread = (0, import_react7.useCallback)((title) => {
|
|
3879
4059
|
callbacks.onCreateThread?.(title, createStateCallback());
|
|
3880
4060
|
}, [callbacks, createStateCallback]);
|
|
@@ -3979,7 +4159,15 @@ var ChatUI = ({
|
|
|
3979
4159
|
compactMode: config.ui.compactMode,
|
|
3980
4160
|
onAction: handleMessageAction,
|
|
3981
4161
|
toolUsedLabel: config.labels.toolUsed,
|
|
3982
|
-
thinkingLabel: config.labels.thinking
|
|
4162
|
+
thinkingLabel: config.labels.thinking,
|
|
4163
|
+
showMoreLabel: config.labels.showMoreMessage,
|
|
4164
|
+
showLessLabel: config.labels.showLessMessage,
|
|
4165
|
+
collapseLongMessages: config.ui.collapseLongMessages,
|
|
4166
|
+
collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
|
|
4167
|
+
longMessagePreviewChars: config.ui.longMessagePreviewChars,
|
|
4168
|
+
longMessageChunkChars: config.ui.longMessageChunkChars,
|
|
4169
|
+
renderUserMarkdown: config.ui.renderUserMarkdown,
|
|
4170
|
+
onToggleExpanded: handleToggleMessageExpansion
|
|
3983
4171
|
}), [
|
|
3984
4172
|
user?.avatar,
|
|
3985
4173
|
user?.name,
|
|
@@ -3994,7 +4182,15 @@ var ChatUI = ({
|
|
|
3994
4182
|
config.features.enableToolCallsDisplay,
|
|
3995
4183
|
config.labels.toolUsed,
|
|
3996
4184
|
config.labels.thinking,
|
|
3997
|
-
|
|
4185
|
+
config.labels.showMoreMessage,
|
|
4186
|
+
config.labels.showLessMessage,
|
|
4187
|
+
config.ui.collapseLongMessages,
|
|
4188
|
+
config.ui.collapseLongMessagesForUserOnly,
|
|
4189
|
+
config.ui.longMessagePreviewChars,
|
|
4190
|
+
config.ui.longMessageChunkChars,
|
|
4191
|
+
config.ui.renderUserMarkdown,
|
|
4192
|
+
handleMessageAction,
|
|
4193
|
+
handleToggleMessageExpansion
|
|
3998
4194
|
]);
|
|
3999
4195
|
const shouldShowAgentSelector = Boolean(
|
|
4000
4196
|
config.agentSelector?.enabled && onSelectAgent && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1)
|
|
@@ -4086,7 +4282,8 @@ var ChatUI = ({
|
|
|
4086
4282
|
{
|
|
4087
4283
|
message,
|
|
4088
4284
|
...messageProps,
|
|
4089
|
-
isGrouped
|
|
4285
|
+
isGrouped,
|
|
4286
|
+
isExpanded: Boolean(expandedMessageIds[message.id])
|
|
4090
4287
|
}
|
|
4091
4288
|
),
|
|
4092
4289
|
message.role === "assistant" && renderInlineSuggestions(message.id)
|
|
@@ -4477,6 +4674,7 @@ var chatUtils = {
|
|
|
4477
4674
|
chatUtils,
|
|
4478
4675
|
cn,
|
|
4479
4676
|
configUtils,
|
|
4677
|
+
createObjectUrlFromDataUrl,
|
|
4480
4678
|
defaultChatConfig,
|
|
4481
4679
|
featureFlags,
|
|
4482
4680
|
formatDate,
|