@copilotz/chat-ui 0.1.8 → 0.1.10
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 +205 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +210 -24
- package/dist/index.js.map +1 -1
- package/dist/styles.css +21 -5
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -117,6 +117,8 @@ interface ChatConfig {
|
|
|
117
117
|
inputHelpText?: string;
|
|
118
118
|
thinking?: string;
|
|
119
119
|
defaultThreadName?: string;
|
|
120
|
+
showMoreMessage?: string;
|
|
121
|
+
showLessMessage?: string;
|
|
120
122
|
};
|
|
121
123
|
features?: {
|
|
122
124
|
enableThreads?: boolean;
|
|
@@ -135,6 +137,11 @@ interface ChatConfig {
|
|
|
135
137
|
showAvatars?: boolean;
|
|
136
138
|
compactMode?: boolean;
|
|
137
139
|
showWordCount?: boolean;
|
|
140
|
+
collapseLongMessages?: boolean;
|
|
141
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
142
|
+
longMessagePreviewChars?: number;
|
|
143
|
+
longMessageChunkChars?: number;
|
|
144
|
+
renderUserMarkdown?: boolean;
|
|
138
145
|
};
|
|
139
146
|
customComponent?: {
|
|
140
147
|
label?: string;
|
|
@@ -359,6 +366,15 @@ interface MessageProps {
|
|
|
359
366
|
className?: string;
|
|
360
367
|
toolUsedLabel?: string;
|
|
361
368
|
thinkingLabel?: string;
|
|
369
|
+
showMoreLabel?: string;
|
|
370
|
+
showLessLabel?: string;
|
|
371
|
+
collapseLongMessages?: boolean;
|
|
372
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
373
|
+
longMessagePreviewChars?: number;
|
|
374
|
+
longMessageChunkChars?: number;
|
|
375
|
+
renderUserMarkdown?: boolean;
|
|
376
|
+
isExpanded?: boolean;
|
|
377
|
+
onToggleExpanded?: (messageId: string) => void;
|
|
362
378
|
/** When true, hides the avatar and name (for grouped consecutive messages from same sender) */
|
|
363
379
|
isGrouped?: boolean;
|
|
364
380
|
}
|
|
@@ -565,6 +581,11 @@ declare const configUtils: {
|
|
|
565
581
|
showAvatars?: boolean;
|
|
566
582
|
compactMode?: boolean;
|
|
567
583
|
showWordCount?: boolean;
|
|
584
|
+
collapseLongMessages?: boolean;
|
|
585
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
586
|
+
longMessagePreviewChars?: number;
|
|
587
|
+
longMessageChunkChars?: number;
|
|
588
|
+
renderUserMarkdown?: boolean;
|
|
568
589
|
};
|
|
569
590
|
};
|
|
570
591
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -117,6 +117,8 @@ interface ChatConfig {
|
|
|
117
117
|
inputHelpText?: string;
|
|
118
118
|
thinking?: string;
|
|
119
119
|
defaultThreadName?: string;
|
|
120
|
+
showMoreMessage?: string;
|
|
121
|
+
showLessMessage?: string;
|
|
120
122
|
};
|
|
121
123
|
features?: {
|
|
122
124
|
enableThreads?: boolean;
|
|
@@ -135,6 +137,11 @@ interface ChatConfig {
|
|
|
135
137
|
showAvatars?: boolean;
|
|
136
138
|
compactMode?: boolean;
|
|
137
139
|
showWordCount?: boolean;
|
|
140
|
+
collapseLongMessages?: boolean;
|
|
141
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
142
|
+
longMessagePreviewChars?: number;
|
|
143
|
+
longMessageChunkChars?: number;
|
|
144
|
+
renderUserMarkdown?: boolean;
|
|
138
145
|
};
|
|
139
146
|
customComponent?: {
|
|
140
147
|
label?: string;
|
|
@@ -359,6 +366,15 @@ interface MessageProps {
|
|
|
359
366
|
className?: string;
|
|
360
367
|
toolUsedLabel?: string;
|
|
361
368
|
thinkingLabel?: string;
|
|
369
|
+
showMoreLabel?: string;
|
|
370
|
+
showLessLabel?: string;
|
|
371
|
+
collapseLongMessages?: boolean;
|
|
372
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
373
|
+
longMessagePreviewChars?: number;
|
|
374
|
+
longMessageChunkChars?: number;
|
|
375
|
+
renderUserMarkdown?: boolean;
|
|
376
|
+
isExpanded?: boolean;
|
|
377
|
+
onToggleExpanded?: (messageId: string) => void;
|
|
362
378
|
/** When true, hides the avatar and name (for grouped consecutive messages from same sender) */
|
|
363
379
|
isGrouped?: boolean;
|
|
364
380
|
}
|
|
@@ -565,6 +581,11 @@ declare const configUtils: {
|
|
|
565
581
|
showAvatars?: boolean;
|
|
566
582
|
compactMode?: boolean;
|
|
567
583
|
showWordCount?: boolean;
|
|
584
|
+
collapseLongMessages?: boolean;
|
|
585
|
+
collapseLongMessagesForUserOnly?: boolean;
|
|
586
|
+
longMessagePreviewChars?: number;
|
|
587
|
+
longMessageChunkChars?: number;
|
|
588
|
+
renderUserMarkdown?: boolean;
|
|
568
589
|
};
|
|
569
590
|
};
|
|
570
591
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/chat/ChatUI.tsx
|
|
2
|
-
import { useState as useState8, useEffect as useEffect9, useRef as useRef7, useCallback as useCallback4, useMemo as
|
|
2
|
+
import { useState as useState8, useEffect as useEffect9, useRef as useRef7, useCallback as useCallback4, useMemo as useMemo4 } from "react";
|
|
3
3
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4
4
|
|
|
5
5
|
// src/config/chatConfig.ts
|
|
@@ -64,7 +64,9 @@ var defaultChatConfig = {
|
|
|
64
64
|
daysAgo: "days ago",
|
|
65
65
|
inputHelpText: "Press Enter to send, Shift+Enter to add a new line.",
|
|
66
66
|
thinking: "Thinking...",
|
|
67
|
-
defaultThreadName: "Main Thread"
|
|
67
|
+
defaultThreadName: "Main Thread",
|
|
68
|
+
showMoreMessage: "Show more",
|
|
69
|
+
showLessMessage: "Show less"
|
|
68
70
|
},
|
|
69
71
|
features: {
|
|
70
72
|
enableThreads: true,
|
|
@@ -83,7 +85,12 @@ var defaultChatConfig = {
|
|
|
83
85
|
showTimestamps: false,
|
|
84
86
|
showAvatars: true,
|
|
85
87
|
compactMode: false,
|
|
86
|
-
showWordCount: false
|
|
88
|
+
showWordCount: false,
|
|
89
|
+
collapseLongMessages: false,
|
|
90
|
+
collapseLongMessagesForUserOnly: false,
|
|
91
|
+
longMessagePreviewChars: 4e3,
|
|
92
|
+
longMessageChunkChars: 12e3,
|
|
93
|
+
renderUserMarkdown: true
|
|
87
94
|
},
|
|
88
95
|
customComponent: {},
|
|
89
96
|
headerActions: null
|
|
@@ -238,7 +245,7 @@ var configUtils = {
|
|
|
238
245
|
};
|
|
239
246
|
|
|
240
247
|
// src/components/chat/Message.tsx
|
|
241
|
-
import { useState, useRef, memo } from "react";
|
|
248
|
+
import React, { useState, useRef, useMemo, memo } from "react";
|
|
242
249
|
import ReactMarkdown from "react-markdown";
|
|
243
250
|
import remarkGfm from "remark-gfm";
|
|
244
251
|
import rehypeHighlight from "rehype-highlight";
|
|
@@ -569,21 +576,89 @@ var markdownComponents = {
|
|
|
569
576
|
var remarkPluginsDefault = [remarkGfm];
|
|
570
577
|
var rehypePluginsDefault = [rehypeHighlight];
|
|
571
578
|
var rehypePluginsEmpty = [];
|
|
579
|
+
var getPlainTextChunks = (content, chunkSize) => {
|
|
580
|
+
if (chunkSize <= 0 || content.length <= chunkSize) {
|
|
581
|
+
return [content];
|
|
582
|
+
}
|
|
583
|
+
const chunks = [];
|
|
584
|
+
let start = 0;
|
|
585
|
+
while (start < content.length) {
|
|
586
|
+
let end = Math.min(start + chunkSize, content.length);
|
|
587
|
+
if (end < content.length) {
|
|
588
|
+
const splitAt = content.lastIndexOf("\n", end);
|
|
589
|
+
if (splitAt > start + Math.floor(chunkSize / 2)) {
|
|
590
|
+
end = splitAt + 1;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
chunks.push(content.slice(start, end));
|
|
594
|
+
start = end;
|
|
595
|
+
}
|
|
596
|
+
return chunks;
|
|
597
|
+
};
|
|
598
|
+
var hasCodeBlocks = (content) => /(^|\n)(```|~~~)/.test(content);
|
|
599
|
+
var getCollapsedPreview = (content, previewChars, previewOverride) => {
|
|
600
|
+
if (previewOverride && previewOverride.trim().length > 0) {
|
|
601
|
+
const normalizedPreview = previewOverride.trimEnd();
|
|
602
|
+
return normalizedPreview.endsWith("...") ? normalizedPreview : `${normalizedPreview}...`;
|
|
603
|
+
}
|
|
604
|
+
if (content.length <= previewChars) {
|
|
605
|
+
return content;
|
|
606
|
+
}
|
|
607
|
+
return `${content.slice(0, previewChars).trimEnd()}...`;
|
|
608
|
+
};
|
|
609
|
+
var LongContentShell = memo(function LongContentShell2({ children, className, style }) {
|
|
610
|
+
return /* @__PURE__ */ jsx7("div", { className, style, children });
|
|
611
|
+
});
|
|
612
|
+
var PlainTextContent = memo(function PlainTextContent2({
|
|
613
|
+
content,
|
|
614
|
+
className = "",
|
|
615
|
+
chunkSize = 12e3,
|
|
616
|
+
style
|
|
617
|
+
}) {
|
|
618
|
+
const chunks = useMemo(() => getPlainTextChunks(content, chunkSize), [content, chunkSize]);
|
|
619
|
+
return /* @__PURE__ */ jsx7(
|
|
620
|
+
LongContentShell,
|
|
621
|
+
{
|
|
622
|
+
className: `text-sm leading-6 whitespace-pre-wrap break-words ${className}`.trim(),
|
|
623
|
+
style,
|
|
624
|
+
children: chunks.map((chunk, index) => /* @__PURE__ */ jsx7(React.Fragment, { children: chunk }, index))
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
});
|
|
572
628
|
var StreamingText = memo(function StreamingText2({
|
|
573
629
|
content,
|
|
574
630
|
isStreaming = false,
|
|
575
631
|
thinkingLabel = "Thinking...",
|
|
576
|
-
className = ""
|
|
632
|
+
className = "",
|
|
633
|
+
renderMarkdown = true,
|
|
634
|
+
plainTextChunkChars = 12e3,
|
|
635
|
+
contentStyle
|
|
577
636
|
}) {
|
|
578
637
|
const hasContent = content.trim().length > 0;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
638
|
+
const enableSyntaxHighlight = renderMarkdown && !isStreaming && hasCodeBlocks(content);
|
|
639
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
640
|
+
hasContent ? renderMarkdown ? /* @__PURE__ */ jsx7(
|
|
641
|
+
LongContentShell,
|
|
642
|
+
{
|
|
643
|
+
className: `prose prose-sm max-w-none dark:prose-invert break-words ${className}`.trim(),
|
|
644
|
+
style: contentStyle,
|
|
645
|
+
children: /* @__PURE__ */ jsx7(
|
|
646
|
+
ReactMarkdown,
|
|
647
|
+
{
|
|
648
|
+
remarkPlugins: remarkPluginsDefault,
|
|
649
|
+
rehypePlugins: enableSyntaxHighlight ? rehypePluginsDefault : rehypePluginsEmpty,
|
|
650
|
+
components: markdownComponents,
|
|
651
|
+
children: content
|
|
652
|
+
}
|
|
653
|
+
)
|
|
654
|
+
}
|
|
655
|
+
) : /* @__PURE__ */ jsx7(
|
|
656
|
+
PlainTextContent,
|
|
582
657
|
{
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
658
|
+
content,
|
|
659
|
+
className,
|
|
660
|
+
chunkSize: plainTextChunkChars,
|
|
661
|
+
style: contentStyle
|
|
587
662
|
}
|
|
588
663
|
) : isStreaming ? (
|
|
589
664
|
// Show thinking indicator while waiting for first token
|
|
@@ -753,6 +828,15 @@ var arePropsEqual = (prevProps, nextProps) => {
|
|
|
753
828
|
if (prevProps.className !== nextProps.className) return false;
|
|
754
829
|
if (prevProps.toolUsedLabel !== nextProps.toolUsedLabel) return false;
|
|
755
830
|
if (prevProps.thinkingLabel !== nextProps.thinkingLabel) return false;
|
|
831
|
+
if (prevProps.showMoreLabel !== nextProps.showMoreLabel) return false;
|
|
832
|
+
if (prevProps.showLessLabel !== nextProps.showLessLabel) return false;
|
|
833
|
+
if (prevProps.collapseLongMessages !== nextProps.collapseLongMessages) return false;
|
|
834
|
+
if (prevProps.collapseLongMessagesForUserOnly !== nextProps.collapseLongMessagesForUserOnly) return false;
|
|
835
|
+
if (prevProps.longMessagePreviewChars !== nextProps.longMessagePreviewChars) return false;
|
|
836
|
+
if (prevProps.longMessageChunkChars !== nextProps.longMessageChunkChars) return false;
|
|
837
|
+
if (prevProps.renderUserMarkdown !== nextProps.renderUserMarkdown) return false;
|
|
838
|
+
if (prevProps.isExpanded !== nextProps.isExpanded) return false;
|
|
839
|
+
if (prevProps.onToggleExpanded !== nextProps.onToggleExpanded) return false;
|
|
756
840
|
if (prevProps.isGrouped !== nextProps.isGrouped) return false;
|
|
757
841
|
if (prevProps.assistantAvatar !== nextProps.assistantAvatar) return false;
|
|
758
842
|
return true;
|
|
@@ -775,6 +859,15 @@ var Message = memo(({
|
|
|
775
859
|
className = "",
|
|
776
860
|
toolUsedLabel,
|
|
777
861
|
thinkingLabel = "Thinking...",
|
|
862
|
+
showMoreLabel = "Show more",
|
|
863
|
+
showLessLabel = "Show less",
|
|
864
|
+
collapseLongMessages = false,
|
|
865
|
+
collapseLongMessagesForUserOnly = false,
|
|
866
|
+
longMessagePreviewChars = 4e3,
|
|
867
|
+
longMessageChunkChars = 12e3,
|
|
868
|
+
renderUserMarkdown = true,
|
|
869
|
+
isExpanded = false,
|
|
870
|
+
onToggleExpanded,
|
|
778
871
|
isGrouped = false
|
|
779
872
|
}) => {
|
|
780
873
|
const [isEditing, setIsEditing] = useState(false);
|
|
@@ -784,6 +877,18 @@ var Message = memo(({
|
|
|
784
877
|
const messageIsUser = isUser ?? message.role === "user";
|
|
785
878
|
const canEdit = enableEdit && messageIsUser;
|
|
786
879
|
const canRegenerate = enableRegenerate && !messageIsUser;
|
|
880
|
+
const normalizedPreviewChars = Math.max(longMessagePreviewChars, 1);
|
|
881
|
+
const normalizedChunkChars = Math.max(longMessageChunkChars, 1);
|
|
882
|
+
const previewOverride = typeof message.metadata?.previewContent === "string" ? message.metadata.previewContent : void 0;
|
|
883
|
+
const canCollapseMessage = collapseLongMessages && !message.isStreaming && message.content.length > normalizedPreviewChars && (!collapseLongMessagesForUserOnly || messageIsUser);
|
|
884
|
+
const isCollapsed = canCollapseMessage && !isExpanded;
|
|
885
|
+
const contentToRender = isCollapsed ? getCollapsedPreview(message.content, normalizedPreviewChars, previewOverride) : message.content;
|
|
886
|
+
const shouldRenderMarkdown = !isCollapsed && (!messageIsUser || renderUserMarkdown);
|
|
887
|
+
const shouldApplyLargeContentContainment = !isCollapsed && message.content.length > normalizedChunkChars;
|
|
888
|
+
const contentStyle = shouldApplyLargeContentContainment ? {
|
|
889
|
+
contentVisibility: "auto",
|
|
890
|
+
containIntrinsicSize: "1px 400px"
|
|
891
|
+
} : void 0;
|
|
787
892
|
const handleCopy = async () => {
|
|
788
893
|
try {
|
|
789
894
|
await navigator.clipboard.writeText(message.content);
|
|
@@ -812,6 +917,9 @@ var Message = memo(({
|
|
|
812
917
|
const handleRegenerate = () => {
|
|
813
918
|
onAction?.({ action: "regenerate", messageId: message.id });
|
|
814
919
|
};
|
|
920
|
+
const handleToggleExpanded = () => {
|
|
921
|
+
onToggleExpanded?.(message.id);
|
|
922
|
+
};
|
|
815
923
|
const formatTime = (timestamp) => {
|
|
816
924
|
return new Date(timestamp).toLocaleTimeString("pt-BR", {
|
|
817
925
|
hour: "2-digit",
|
|
@@ -836,7 +944,7 @@ var Message = memo(({
|
|
|
836
944
|
message.isEdited && /* @__PURE__ */ jsx7(Badge, { variant: "outline", className: "text-xs", children: "editado" })
|
|
837
945
|
] })
|
|
838
946
|
] }),
|
|
839
|
-
/* @__PURE__ */ jsx7("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"} ${isGrouped && showAvatar && !messageIsUser ? compactMode ? "ml-9" : "ml-11" : ""} ${isGrouped && showAvatar && messageIsUser ? compactMode ? "mr-9" : "mr-11" : ""}`, children: /* @__PURE__ */ jsxs2("div", { className: `relative inline-flex flex-col overflow-hidden ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-full"}`, children: [
|
|
947
|
+
/* @__PURE__ */ jsx7("div", { className: `flex-1 min-w-0 ${messageIsUser ? "text-right" : "text-left"} ${isGrouped && showAvatar && !messageIsUser ? compactMode ? "ml-9" : "ml-11" : ""} ${isGrouped && showAvatar && messageIsUser ? compactMode ? "mr-9" : "mr-11" : ""}`, children: /* @__PURE__ */ jsxs2("div", { className: `relative inline-flex flex-col overflow-hidden text-left ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-full"}`, children: [
|
|
840
948
|
isEditing ? /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
841
949
|
/* @__PURE__ */ jsx7(
|
|
842
950
|
Textarea,
|
|
@@ -862,12 +970,26 @@ var Message = memo(({
|
|
|
862
970
|
/* @__PURE__ */ jsx7(
|
|
863
971
|
StreamingText,
|
|
864
972
|
{
|
|
865
|
-
content:
|
|
973
|
+
content: contentToRender,
|
|
866
974
|
isStreaming: message.isStreaming,
|
|
867
975
|
thinkingLabel,
|
|
868
|
-
|
|
976
|
+
renderMarkdown: shouldRenderMarkdown,
|
|
977
|
+
plainTextChunkChars: normalizedChunkChars,
|
|
978
|
+
contentStyle
|
|
869
979
|
}
|
|
870
980
|
),
|
|
981
|
+
canCollapseMessage && /* @__PURE__ */ jsx7("div", { className: "mt-3", children: /* @__PURE__ */ jsx7(
|
|
982
|
+
Button,
|
|
983
|
+
{
|
|
984
|
+
type: "button",
|
|
985
|
+
variant: "ghost",
|
|
986
|
+
size: "sm",
|
|
987
|
+
className: "h-auto px-0 text-xs font-medium text-current hover:bg-transparent hover:opacity-80",
|
|
988
|
+
"aria-expanded": !isCollapsed,
|
|
989
|
+
onClick: handleToggleExpanded,
|
|
990
|
+
children: isCollapsed ? showMoreLabel : showLessLabel
|
|
991
|
+
}
|
|
992
|
+
) }),
|
|
871
993
|
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mt-3 space-y-2", children: message.attachments.map((attachment, index) => /* @__PURE__ */ jsx7(MediaRenderer, { attachment }, index)) })
|
|
872
994
|
] }),
|
|
873
995
|
!isEditing && (showActions || copied) && /* @__PURE__ */ jsxs2("div", { className: `absolute -top-2 flex gap-1 ${messageIsUser ? "-left-2" : "-right-2"}`, children: [
|
|
@@ -2589,7 +2711,7 @@ var ChatHeader = ({
|
|
|
2589
2711
|
import { useState as useState6, useRef as useRef6, useCallback as useCallback3, useEffect as useEffect8, memo as memo2 } from "react";
|
|
2590
2712
|
|
|
2591
2713
|
// src/components/chat/UserContext.tsx
|
|
2592
|
-
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect7, useMemo as
|
|
2714
|
+
import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect7, useMemo as useMemo3, useState as useState5 } from "react";
|
|
2593
2715
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2594
2716
|
var Ctx = createContext2(void 0);
|
|
2595
2717
|
var ChatUserContextProvider = ({ children, initial }) => {
|
|
@@ -2611,7 +2733,7 @@ var ChatUserContextProvider = ({ children, initial }) => {
|
|
|
2611
2733
|
return { ...prev, ...partial, updatedAt: Date.now() };
|
|
2612
2734
|
});
|
|
2613
2735
|
}, []);
|
|
2614
|
-
const value =
|
|
2736
|
+
const value = useMemo3(() => ({
|
|
2615
2737
|
context: ctx,
|
|
2616
2738
|
setContext: setPartial,
|
|
2617
2739
|
resetContext: () => setCtx({ updatedAt: Date.now() })
|
|
@@ -3736,7 +3858,7 @@ var ChatUI = ({
|
|
|
3736
3858
|
initialInput,
|
|
3737
3859
|
onInitialInputConsumed
|
|
3738
3860
|
}) => {
|
|
3739
|
-
const config =
|
|
3861
|
+
const config = useMemo4(
|
|
3740
3862
|
() => mergeConfig(defaultChatConfig, userConfig),
|
|
3741
3863
|
[userConfig]
|
|
3742
3864
|
);
|
|
@@ -3757,6 +3879,7 @@ var ChatUI = ({
|
|
|
3757
3879
|
};
|
|
3758
3880
|
const [inputValue, setInputValue] = useState8("");
|
|
3759
3881
|
const [attachments, setAttachments] = useState8([]);
|
|
3882
|
+
const [expandedMessageIds, setExpandedMessageIds] = useState8({});
|
|
3760
3883
|
const [state, setState] = useState8({
|
|
3761
3884
|
isRecording: false,
|
|
3762
3885
|
selectedThreadId: currentThreadId,
|
|
@@ -3834,8 +3957,23 @@ var ChatUI = ({
|
|
|
3834
3957
|
return () => clearTimeout(t);
|
|
3835
3958
|
}
|
|
3836
3959
|
}, [state.showSidebar, isMobile, config.customComponent]);
|
|
3960
|
+
const prevMessageCountRef = useRef7(0);
|
|
3837
3961
|
useEffect9(() => {
|
|
3838
|
-
if (
|
|
3962
|
+
if (messages.length === 0) {
|
|
3963
|
+
prevMessageCountRef.current = 0;
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
const wasEmpty = prevMessageCountRef.current === 0;
|
|
3967
|
+
prevMessageCountRef.current = messages.length;
|
|
3968
|
+
if (wasEmpty) {
|
|
3969
|
+
requestAnimationFrame(() => {
|
|
3970
|
+
requestAnimationFrame(() => {
|
|
3971
|
+
virtualizer.scrollToIndex(messages.length - 1, { align: "end" });
|
|
3972
|
+
});
|
|
3973
|
+
});
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
if (!state.isAtBottom) return;
|
|
3839
3977
|
requestAnimationFrame(() => {
|
|
3840
3978
|
const viewport = scrollAreaRef.current;
|
|
3841
3979
|
if (!viewport) return;
|
|
@@ -3845,7 +3983,25 @@ var ChatUI = ({
|
|
|
3845
3983
|
viewport.scrollTop = viewport.scrollHeight;
|
|
3846
3984
|
}
|
|
3847
3985
|
});
|
|
3848
|
-
}, [messages, state.isAtBottom]);
|
|
3986
|
+
}, [messages, state.isAtBottom, virtualizer]);
|
|
3987
|
+
useEffect9(() => {
|
|
3988
|
+
virtualizer.measure();
|
|
3989
|
+
}, [expandedMessageIds, virtualizer]);
|
|
3990
|
+
useEffect9(() => {
|
|
3991
|
+
const validMessageIds = new Set(messages.map((message) => message.id));
|
|
3992
|
+
setExpandedMessageIds((prev) => {
|
|
3993
|
+
const activeIds = Object.keys(prev);
|
|
3994
|
+
const staleIds = activeIds.filter((messageId) => !validMessageIds.has(messageId));
|
|
3995
|
+
if (staleIds.length === 0) {
|
|
3996
|
+
return prev;
|
|
3997
|
+
}
|
|
3998
|
+
const next = { ...prev };
|
|
3999
|
+
staleIds.forEach((messageId) => {
|
|
4000
|
+
delete next[messageId];
|
|
4001
|
+
});
|
|
4002
|
+
return next;
|
|
4003
|
+
});
|
|
4004
|
+
}, [messages]);
|
|
3849
4005
|
const handleScroll = useCallback4((e) => {
|
|
3850
4006
|
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
|
3851
4007
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
|
|
@@ -3883,6 +4039,19 @@ var ChatUI = ({
|
|
|
3883
4039
|
break;
|
|
3884
4040
|
}
|
|
3885
4041
|
}, [callbacks, createStateCallback]);
|
|
4042
|
+
const handleToggleMessageExpansion = useCallback4((messageId) => {
|
|
4043
|
+
setExpandedMessageIds((prev) => {
|
|
4044
|
+
if (prev[messageId]) {
|
|
4045
|
+
const next = { ...prev };
|
|
4046
|
+
delete next[messageId];
|
|
4047
|
+
return next;
|
|
4048
|
+
}
|
|
4049
|
+
return {
|
|
4050
|
+
...prev,
|
|
4051
|
+
[messageId]: true
|
|
4052
|
+
};
|
|
4053
|
+
});
|
|
4054
|
+
}, []);
|
|
3886
4055
|
const handleCreateThread = useCallback4((title) => {
|
|
3887
4056
|
callbacks.onCreateThread?.(title, createStateCallback());
|
|
3888
4057
|
}, [callbacks, createStateCallback]);
|
|
@@ -3973,7 +4142,7 @@ var ChatUI = ({
|
|
|
3973
4142
|
`message-skeleton-${index}`
|
|
3974
4143
|
);
|
|
3975
4144
|
}) });
|
|
3976
|
-
const messageProps =
|
|
4145
|
+
const messageProps = useMemo4(() => ({
|
|
3977
4146
|
userAvatar: user?.avatar,
|
|
3978
4147
|
userName: user?.name,
|
|
3979
4148
|
assistantAvatar: assistant?.avatar,
|
|
@@ -3987,7 +4156,15 @@ var ChatUI = ({
|
|
|
3987
4156
|
compactMode: config.ui.compactMode,
|
|
3988
4157
|
onAction: handleMessageAction,
|
|
3989
4158
|
toolUsedLabel: config.labels.toolUsed,
|
|
3990
|
-
thinkingLabel: config.labels.thinking
|
|
4159
|
+
thinkingLabel: config.labels.thinking,
|
|
4160
|
+
showMoreLabel: config.labels.showMoreMessage,
|
|
4161
|
+
showLessLabel: config.labels.showLessMessage,
|
|
4162
|
+
collapseLongMessages: config.ui.collapseLongMessages,
|
|
4163
|
+
collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
|
|
4164
|
+
longMessagePreviewChars: config.ui.longMessagePreviewChars,
|
|
4165
|
+
longMessageChunkChars: config.ui.longMessageChunkChars,
|
|
4166
|
+
renderUserMarkdown: config.ui.renderUserMarkdown,
|
|
4167
|
+
onToggleExpanded: handleToggleMessageExpansion
|
|
3991
4168
|
}), [
|
|
3992
4169
|
user?.avatar,
|
|
3993
4170
|
user?.name,
|
|
@@ -4002,7 +4179,15 @@ var ChatUI = ({
|
|
|
4002
4179
|
config.features.enableToolCallsDisplay,
|
|
4003
4180
|
config.labels.toolUsed,
|
|
4004
4181
|
config.labels.thinking,
|
|
4005
|
-
|
|
4182
|
+
config.labels.showMoreMessage,
|
|
4183
|
+
config.labels.showLessMessage,
|
|
4184
|
+
config.ui.collapseLongMessages,
|
|
4185
|
+
config.ui.collapseLongMessagesForUserOnly,
|
|
4186
|
+
config.ui.longMessagePreviewChars,
|
|
4187
|
+
config.ui.longMessageChunkChars,
|
|
4188
|
+
config.ui.renderUserMarkdown,
|
|
4189
|
+
handleMessageAction,
|
|
4190
|
+
handleToggleMessageExpansion
|
|
4006
4191
|
]);
|
|
4007
4192
|
const shouldShowAgentSelector = Boolean(
|
|
4008
4193
|
config.agentSelector?.enabled && onSelectAgent && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1)
|
|
@@ -4094,7 +4279,8 @@ var ChatUI = ({
|
|
|
4094
4279
|
{
|
|
4095
4280
|
message,
|
|
4096
4281
|
...messageProps,
|
|
4097
|
-
isGrouped
|
|
4282
|
+
isGrouped,
|
|
4283
|
+
isExpanded: Boolean(expandedMessageIds[message.id])
|
|
4098
4284
|
}
|
|
4099
4285
|
),
|
|
4100
4286
|
message.role === "assistant" && renderInlineSuggestions(message.id)
|