@copilotz/chat-ui 0.3.2 → 0.3.4
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 +112 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +112 -206
- package/dist/index.js.map +1 -1
- package/dist/styles.css +0 -12
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -131,8 +131,6 @@ interface ChatConfig {
|
|
|
131
131
|
stopGenerationTooltip?: string;
|
|
132
132
|
attachFiles?: string;
|
|
133
133
|
attachFileTooltip?: string;
|
|
134
|
-
recordAudio?: string;
|
|
135
|
-
recordAudioTooltip?: string;
|
|
136
134
|
voiceEnter?: string;
|
|
137
135
|
voiceExit?: string;
|
|
138
136
|
voiceTitle?: string;
|
|
@@ -188,6 +186,8 @@ interface ChatConfig {
|
|
|
188
186
|
inputHelpText?: string;
|
|
189
187
|
thinking?: string;
|
|
190
188
|
defaultThreadName?: string;
|
|
189
|
+
loadOlderMessages?: string;
|
|
190
|
+
loadingOlderMessages?: string;
|
|
191
191
|
showMoreMessage?: string;
|
|
192
192
|
showLessMessage?: string;
|
|
193
193
|
};
|
|
@@ -216,7 +216,6 @@ interface ChatConfig {
|
|
|
216
216
|
};
|
|
217
217
|
markdown?: ChatMarkdownConfig;
|
|
218
218
|
voiceCompose?: {
|
|
219
|
-
enabled?: boolean;
|
|
220
219
|
defaultMode?: 'text' | 'voice';
|
|
221
220
|
reviewMode?: VoiceReviewMode;
|
|
222
221
|
autoSendDelayMs?: number;
|
|
@@ -270,7 +269,10 @@ interface ChatV2Props {
|
|
|
270
269
|
sidebar?: ReactNode;
|
|
271
270
|
isGenerating?: boolean;
|
|
272
271
|
isMessagesLoading?: boolean;
|
|
272
|
+
isLoadingOlderMessages?: boolean;
|
|
273
|
+
hasMoreMessagesBefore?: boolean;
|
|
273
274
|
callbacks?: ChatCallbacks;
|
|
275
|
+
onLoadOlderMessages?: () => void;
|
|
274
276
|
user?: {
|
|
275
277
|
id: string;
|
|
276
278
|
name?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -131,8 +131,6 @@ interface ChatConfig {
|
|
|
131
131
|
stopGenerationTooltip?: string;
|
|
132
132
|
attachFiles?: string;
|
|
133
133
|
attachFileTooltip?: string;
|
|
134
|
-
recordAudio?: string;
|
|
135
|
-
recordAudioTooltip?: string;
|
|
136
134
|
voiceEnter?: string;
|
|
137
135
|
voiceExit?: string;
|
|
138
136
|
voiceTitle?: string;
|
|
@@ -188,6 +186,8 @@ interface ChatConfig {
|
|
|
188
186
|
inputHelpText?: string;
|
|
189
187
|
thinking?: string;
|
|
190
188
|
defaultThreadName?: string;
|
|
189
|
+
loadOlderMessages?: string;
|
|
190
|
+
loadingOlderMessages?: string;
|
|
191
191
|
showMoreMessage?: string;
|
|
192
192
|
showLessMessage?: string;
|
|
193
193
|
};
|
|
@@ -216,7 +216,6 @@ interface ChatConfig {
|
|
|
216
216
|
};
|
|
217
217
|
markdown?: ChatMarkdownConfig;
|
|
218
218
|
voiceCompose?: {
|
|
219
|
-
enabled?: boolean;
|
|
220
219
|
defaultMode?: 'text' | 'voice';
|
|
221
220
|
reviewMode?: VoiceReviewMode;
|
|
222
221
|
autoSendDelayMs?: number;
|
|
@@ -270,7 +269,10 @@ interface ChatV2Props {
|
|
|
270
269
|
sidebar?: ReactNode;
|
|
271
270
|
isGenerating?: boolean;
|
|
272
271
|
isMessagesLoading?: boolean;
|
|
272
|
+
isLoadingOlderMessages?: boolean;
|
|
273
|
+
hasMoreMessagesBefore?: boolean;
|
|
273
274
|
callbacks?: ChatCallbacks;
|
|
275
|
+
onLoadOlderMessages?: () => void;
|
|
274
276
|
user?: {
|
|
275
277
|
id: string;
|
|
276
278
|
name?: string;
|
package/dist/index.js
CHANGED
|
@@ -28,8 +28,6 @@ var defaultChatConfig = {
|
|
|
28
28
|
stopGenerationTooltip: "Stop generation",
|
|
29
29
|
attachFiles: "Attach Files",
|
|
30
30
|
attachFileTooltip: "Attach file",
|
|
31
|
-
recordAudio: "Record Audio",
|
|
32
|
-
recordAudioTooltip: "Record audio",
|
|
33
31
|
voiceEnter: "Voice input",
|
|
34
32
|
voiceExit: "Use keyboard",
|
|
35
33
|
voiceTitle: "Voice",
|
|
@@ -87,6 +85,8 @@ var defaultChatConfig = {
|
|
|
87
85
|
inputHelpText: "Press Enter to send, Shift+Enter to add a new line.",
|
|
88
86
|
thinking: "Thinking...",
|
|
89
87
|
defaultThreadName: "Main Thread",
|
|
88
|
+
loadOlderMessages: "Load older messages",
|
|
89
|
+
loadingOlderMessages: "Loading older messages...",
|
|
90
90
|
showMoreMessage: "Show more",
|
|
91
91
|
showLessMessage: "Show less"
|
|
92
92
|
},
|
|
@@ -120,7 +120,6 @@ var defaultChatConfig = {
|
|
|
120
120
|
components: {}
|
|
121
121
|
},
|
|
122
122
|
voiceCompose: {
|
|
123
|
-
enabled: false,
|
|
124
123
|
defaultMode: "text",
|
|
125
124
|
reviewMode: "manual",
|
|
126
125
|
autoSendDelayMs: 5e3,
|
|
@@ -3845,61 +3844,6 @@ var AttachmentPreview = memo3(function AttachmentPreview2({ attachment, onRemove
|
|
|
3845
3844
|
attachment.fileName && attachment.kind !== "audio" && /* @__PURE__ */ jsx23("div", { className: "absolute bottom-0 left-0 right-0 bg-black/70 text-white text-xs p-1 rounded-b", children: /* @__PURE__ */ jsx23("p", { className: "truncate", children: attachment.fileName }) })
|
|
3846
3845
|
] }) });
|
|
3847
3846
|
});
|
|
3848
|
-
var AudioRecorder = memo3(function AudioRecorder2({ isRecording, onStartRecording, onStopRecording, onCancel, recordingDuration, config }) {
|
|
3849
|
-
const formatTime = (seconds) => {
|
|
3850
|
-
const mins = Math.floor(seconds / 60);
|
|
3851
|
-
const secs = seconds % 60;
|
|
3852
|
-
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
3853
|
-
};
|
|
3854
|
-
if (!isRecording) {
|
|
3855
|
-
return /* @__PURE__ */ jsxs13(Tooltip, { children: [
|
|
3856
|
-
/* @__PURE__ */ jsx23(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx23(
|
|
3857
|
-
Button,
|
|
3858
|
-
{
|
|
3859
|
-
variant: "outline",
|
|
3860
|
-
size: "icon",
|
|
3861
|
-
onClick: onStartRecording,
|
|
3862
|
-
className: "h-10 w-10",
|
|
3863
|
-
children: /* @__PURE__ */ jsx23(Mic2, { className: "h-4 w-4" })
|
|
3864
|
-
}
|
|
3865
|
-
) }),
|
|
3866
|
-
/* @__PURE__ */ jsx23(TooltipContent, { children: config?.labels?.recordAudioTooltip })
|
|
3867
|
-
] });
|
|
3868
|
-
}
|
|
3869
|
-
return /* @__PURE__ */ jsx23(Card, { className: "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950", children: /* @__PURE__ */ jsx23(CardContent, { className: "p-3", children: /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3", children: [
|
|
3870
|
-
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
|
|
3871
|
-
/* @__PURE__ */ jsx23("div", { className: "h-3 w-3 bg-red-500 rounded-full animate-pulse" }),
|
|
3872
|
-
/* @__PURE__ */ jsx23("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: config?.labels?.voiceListening || "Recording" })
|
|
3873
|
-
] }),
|
|
3874
|
-
/* @__PURE__ */ jsx23(Badge, { variant: "outline", className: "text-xs", children: formatTime(recordingDuration) }),
|
|
3875
|
-
/* @__PURE__ */ jsxs13("div", { className: "flex gap-1 ml-auto", children: [
|
|
3876
|
-
/* @__PURE__ */ jsxs13(
|
|
3877
|
-
Button,
|
|
3878
|
-
{
|
|
3879
|
-
variant: "outline",
|
|
3880
|
-
size: "sm",
|
|
3881
|
-
onClick: onCancel,
|
|
3882
|
-
children: [
|
|
3883
|
-
/* @__PURE__ */ jsx23(X4, { className: "h-3 w-3 mr-1" }),
|
|
3884
|
-
config?.labels?.cancel || "Cancel"
|
|
3885
|
-
]
|
|
3886
|
-
}
|
|
3887
|
-
),
|
|
3888
|
-
/* @__PURE__ */ jsxs13(
|
|
3889
|
-
Button,
|
|
3890
|
-
{
|
|
3891
|
-
variant: "default",
|
|
3892
|
-
size: "sm",
|
|
3893
|
-
onClick: onStopRecording,
|
|
3894
|
-
children: [
|
|
3895
|
-
/* @__PURE__ */ jsx23(Square2, { className: "h-3 w-3 mr-1" }),
|
|
3896
|
-
config?.labels?.voiceStop || "Stop"
|
|
3897
|
-
]
|
|
3898
|
-
}
|
|
3899
|
-
)
|
|
3900
|
-
] })
|
|
3901
|
-
] }) }) });
|
|
3902
|
-
});
|
|
3903
3847
|
var resolveVoiceErrorMessage = (error, config) => {
|
|
3904
3848
|
if (error instanceof DOMException && error.name === "NotAllowedError") {
|
|
3905
3849
|
return config?.labels?.voicePermissionDenied || "Microphone access was denied.";
|
|
@@ -3932,7 +3876,6 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
3932
3876
|
mentionAgents = [],
|
|
3933
3877
|
onTargetAgentChange
|
|
3934
3878
|
}) {
|
|
3935
|
-
const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
|
|
3936
3879
|
const voiceDefaultMode = config?.voiceCompose?.defaultMode ?? "text";
|
|
3937
3880
|
const voiceReviewMode = config?.voiceCompose?.reviewMode ?? "manual";
|
|
3938
3881
|
const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
|
|
@@ -3940,12 +3883,10 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
3940
3883
|
const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
|
|
3941
3884
|
const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
|
|
3942
3885
|
const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
|
|
3943
|
-
const [isRecording, setIsRecording] = useState6(false);
|
|
3944
3886
|
const { setContext } = useChatUserContext();
|
|
3945
|
-
const [recordingDuration, setRecordingDuration] = useState6(0);
|
|
3946
3887
|
const [uploadProgress, setUploadProgress] = useState6(/* @__PURE__ */ new Map());
|
|
3947
3888
|
const [isVoiceComposerOpen, setIsVoiceComposerOpen] = useState6(
|
|
3948
|
-
() =>
|
|
3889
|
+
() => enableAudioRecording && voiceDefaultMode === "voice"
|
|
3949
3890
|
);
|
|
3950
3891
|
const [voiceState, setVoiceState] = useState6("idle");
|
|
3951
3892
|
const [voiceDraft, setVoiceDraft] = useState6(null);
|
|
@@ -3959,10 +3900,6 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
3959
3900
|
const [activeMentionIndex, setActiveMentionIndex] = useState6(0);
|
|
3960
3901
|
const textareaRef = useRef5(null);
|
|
3961
3902
|
const fileInputRef = useRef5(null);
|
|
3962
|
-
const mediaRecorderRef = useRef5(null);
|
|
3963
|
-
const recordingStartTime = useRef5(0);
|
|
3964
|
-
const recordingInterval = useRef5(null);
|
|
3965
|
-
const mediaStreamRef = useRef5(null);
|
|
3966
3903
|
const voiceProviderRef = useRef5(null);
|
|
3967
3904
|
const voiceDraftRef = useRef5(null);
|
|
3968
3905
|
const voiceAppendBaseRef = useRef5(null);
|
|
@@ -3998,12 +3935,6 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
3998
3935
|
}, []);
|
|
3999
3936
|
useEffect9(() => {
|
|
4000
3937
|
return () => {
|
|
4001
|
-
if (mediaStreamRef.current) {
|
|
4002
|
-
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
|
4003
|
-
}
|
|
4004
|
-
if (recordingInterval.current) {
|
|
4005
|
-
clearInterval(recordingInterval.current);
|
|
4006
|
-
}
|
|
4007
3938
|
if (voiceProviderRef.current) {
|
|
4008
3939
|
void voiceProviderRef.current.destroy();
|
|
4009
3940
|
voiceProviderRef.current = null;
|
|
@@ -4176,73 +4107,6 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
4176
4107
|
const handleDragOver = useCallback3((e) => {
|
|
4177
4108
|
e.preventDefault();
|
|
4178
4109
|
}, []);
|
|
4179
|
-
const startRecording = async () => {
|
|
4180
|
-
try {
|
|
4181
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
4182
|
-
mediaStreamRef.current = stream;
|
|
4183
|
-
const mediaRecorder = new MediaRecorder(stream);
|
|
4184
|
-
mediaRecorderRef.current = mediaRecorder;
|
|
4185
|
-
const chunks = [];
|
|
4186
|
-
mediaRecorder.ondataavailable = (e) => {
|
|
4187
|
-
chunks.push(e.data);
|
|
4188
|
-
};
|
|
4189
|
-
mediaRecorder.onstop = async () => {
|
|
4190
|
-
const blob = new Blob(chunks, { type: "audio/webm" });
|
|
4191
|
-
const dataUrl = await new Promise((resolve, reject) => {
|
|
4192
|
-
const reader = new FileReader();
|
|
4193
|
-
reader.onload = () => resolve(reader.result);
|
|
4194
|
-
reader.onerror = reject;
|
|
4195
|
-
reader.readAsDataURL(blob);
|
|
4196
|
-
});
|
|
4197
|
-
const attachment = {
|
|
4198
|
-
kind: "audio",
|
|
4199
|
-
dataUrl,
|
|
4200
|
-
mimeType: blob.type,
|
|
4201
|
-
durationMs: recordingDuration * 1e3,
|
|
4202
|
-
fileName: `audio_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}.webm`,
|
|
4203
|
-
size: blob.size
|
|
4204
|
-
};
|
|
4205
|
-
onAttachmentsChange([...attachments, attachment]);
|
|
4206
|
-
if (mediaStreamRef.current) {
|
|
4207
|
-
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
|
4208
|
-
mediaStreamRef.current = null;
|
|
4209
|
-
}
|
|
4210
|
-
};
|
|
4211
|
-
recordingStartTime.current = Date.now();
|
|
4212
|
-
setRecordingDuration(0);
|
|
4213
|
-
setIsRecording(true);
|
|
4214
|
-
mediaRecorder.start();
|
|
4215
|
-
recordingInterval.current = setInterval(() => {
|
|
4216
|
-
const duration = Math.floor((Date.now() - recordingStartTime.current) / 1e3);
|
|
4217
|
-
setRecordingDuration(duration);
|
|
4218
|
-
}, 1e3);
|
|
4219
|
-
} catch (error) {
|
|
4220
|
-
console.error("Error starting recording:", error);
|
|
4221
|
-
alert(config?.labels?.voicePermissionDenied || "Microphone access was denied.");
|
|
4222
|
-
}
|
|
4223
|
-
};
|
|
4224
|
-
const stopRecording = () => {
|
|
4225
|
-
if (mediaRecorderRef.current && isRecording) {
|
|
4226
|
-
mediaRecorderRef.current.stop();
|
|
4227
|
-
setIsRecording(false);
|
|
4228
|
-
if (recordingInterval.current) {
|
|
4229
|
-
clearInterval(recordingInterval.current);
|
|
4230
|
-
}
|
|
4231
|
-
}
|
|
4232
|
-
};
|
|
4233
|
-
const cancelRecording = () => {
|
|
4234
|
-
if (mediaRecorderRef.current && isRecording) {
|
|
4235
|
-
mediaRecorderRef.current.stop();
|
|
4236
|
-
setIsRecording(false);
|
|
4237
|
-
if (recordingInterval.current) {
|
|
4238
|
-
clearInterval(recordingInterval.current);
|
|
4239
|
-
}
|
|
4240
|
-
if (mediaStreamRef.current) {
|
|
4241
|
-
mediaStreamRef.current.getTracks().forEach((track) => track.stop());
|
|
4242
|
-
mediaStreamRef.current = null;
|
|
4243
|
-
}
|
|
4244
|
-
}
|
|
4245
|
-
};
|
|
4246
4110
|
const resetVoiceComposerState = useCallback3((nextState = "idle") => {
|
|
4247
4111
|
setVoiceState(nextState);
|
|
4248
4112
|
setVoiceDraft(null);
|
|
@@ -4527,7 +4391,7 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
4527
4391
|
onAttachmentsChange(newAttachments);
|
|
4528
4392
|
};
|
|
4529
4393
|
const canAddMoreAttachments = attachments.length < maxAttachments;
|
|
4530
|
-
const showVoiceComposer =
|
|
4394
|
+
const showVoiceComposer = enableAudioRecording && isVoiceComposerOpen;
|
|
4531
4395
|
return /* @__PURE__ */ jsx23(TooltipProvider, { children: /* @__PURE__ */ jsx23("div", { className: `border-t py-0 bg-transparent ${className}`, children: /* @__PURE__ */ jsxs13("div", { className: "px-0 md:p-2 pb-1 space-y-4 bg-transparent", children: [
|
|
4532
4396
|
uploadProgress.size > 0 && /* @__PURE__ */ jsx23("div", { className: "space-y-2", children: Array.from(uploadProgress.entries()).map(([id, progress]) => /* @__PURE__ */ jsx23(
|
|
4533
4397
|
FileUploadItem,
|
|
@@ -4544,17 +4408,6 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
4544
4408
|
},
|
|
4545
4409
|
id
|
|
4546
4410
|
)) }),
|
|
4547
|
-
isRecording && /* @__PURE__ */ jsx23(
|
|
4548
|
-
AudioRecorder,
|
|
4549
|
-
{
|
|
4550
|
-
isRecording,
|
|
4551
|
-
onStartRecording: startRecording,
|
|
4552
|
-
onStopRecording: stopRecording,
|
|
4553
|
-
onCancel: cancelRecording,
|
|
4554
|
-
recordingDuration,
|
|
4555
|
-
config
|
|
4556
|
-
}
|
|
4557
|
-
),
|
|
4558
4411
|
attachments.length > 0 && /* @__PURE__ */ jsx23("div", { className: "grid grid-cols-4 gap-2", children: attachments.map((attachment, index) => /* @__PURE__ */ jsx23(
|
|
4559
4412
|
AttachmentPreview,
|
|
4560
4413
|
{
|
|
@@ -4684,7 +4537,7 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
4684
4537
|
agent.id
|
|
4685
4538
|
)) }) })
|
|
4686
4539
|
] }),
|
|
4687
|
-
enableAudioRecording &&
|
|
4540
|
+
enableAudioRecording && canAddMoreAttachments && !value.trim() && /* @__PURE__ */ jsxs13(Tooltip, { children: [
|
|
4688
4541
|
/* @__PURE__ */ jsx23(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx23(
|
|
4689
4542
|
Button,
|
|
4690
4543
|
{
|
|
@@ -4699,18 +4552,8 @@ var ChatInput = memo3(function ChatInput2({
|
|
|
4699
4552
|
children: /* @__PURE__ */ jsx23(Mic2, { className: "h-4 w-4" })
|
|
4700
4553
|
}
|
|
4701
4554
|
) }),
|
|
4702
|
-
/* @__PURE__ */ jsx23(TooltipContent, { children: config?.labels?.voiceEnter
|
|
4703
|
-
] })
|
|
4704
|
-
AudioRecorder,
|
|
4705
|
-
{
|
|
4706
|
-
isRecording,
|
|
4707
|
-
onStartRecording: startRecording,
|
|
4708
|
-
onStopRecording: stopRecording,
|
|
4709
|
-
onCancel: cancelRecording,
|
|
4710
|
-
recordingDuration,
|
|
4711
|
-
config
|
|
4712
|
-
}
|
|
4713
|
-
)),
|
|
4555
|
+
/* @__PURE__ */ jsx23(TooltipContent, { children: config?.labels?.voiceEnter })
|
|
4556
|
+
] }),
|
|
4714
4557
|
isGenerating ? /* @__PURE__ */ jsxs13(Tooltip, { children: [
|
|
4715
4558
|
/* @__PURE__ */ jsx23(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx23(
|
|
4716
4559
|
Button,
|
|
@@ -5243,7 +5086,10 @@ var ChatUI = ({
|
|
|
5243
5086
|
sidebar: _sidebar,
|
|
5244
5087
|
isGenerating = false,
|
|
5245
5088
|
isMessagesLoading = false,
|
|
5089
|
+
isLoadingOlderMessages = false,
|
|
5090
|
+
hasMoreMessagesBefore = false,
|
|
5246
5091
|
callbacks = {},
|
|
5092
|
+
onLoadOlderMessages,
|
|
5247
5093
|
user,
|
|
5248
5094
|
assistant,
|
|
5249
5095
|
suggestions = [],
|
|
@@ -5310,6 +5156,7 @@ var ChatUI = ({
|
|
|
5310
5156
|
}
|
|
5311
5157
|
}, [initialInput]);
|
|
5312
5158
|
const scrollAreaRef = useRef6(null);
|
|
5159
|
+
const prependSnapshotRef = useRef6(null);
|
|
5313
5160
|
const stateRef = useRef6(state);
|
|
5314
5161
|
const inputValueRef = useRef6(inputValue);
|
|
5315
5162
|
const attachmentsRef = useRef6(attachments);
|
|
@@ -5367,6 +5214,10 @@ var ChatUI = ({
|
|
|
5367
5214
|
prevMessageCountRef.current = 0;
|
|
5368
5215
|
return;
|
|
5369
5216
|
}
|
|
5217
|
+
if (prependSnapshotRef.current) {
|
|
5218
|
+
prevMessageCountRef.current = messages.length;
|
|
5219
|
+
return;
|
|
5220
|
+
}
|
|
5370
5221
|
const wasEmpty = prevMessageCountRef.current === 0;
|
|
5371
5222
|
prevMessageCountRef.current = messages.length;
|
|
5372
5223
|
if (wasEmpty) {
|
|
@@ -5391,6 +5242,46 @@ var ChatUI = ({
|
|
|
5391
5242
|
useEffect10(() => {
|
|
5392
5243
|
virtualizer.measure();
|
|
5393
5244
|
}, [expandedMessageIds, virtualizer]);
|
|
5245
|
+
useEffect10(() => {
|
|
5246
|
+
prependSnapshotRef.current = null;
|
|
5247
|
+
}, [currentThreadId]);
|
|
5248
|
+
useEffect10(() => {
|
|
5249
|
+
const snapshot = prependSnapshotRef.current;
|
|
5250
|
+
if (!snapshot) return;
|
|
5251
|
+
if (messages.length <= snapshot.messageCount) {
|
|
5252
|
+
if (!isLoadingOlderMessages) {
|
|
5253
|
+
prependSnapshotRef.current = null;
|
|
5254
|
+
}
|
|
5255
|
+
return;
|
|
5256
|
+
}
|
|
5257
|
+
if ((messages[0]?.id ?? null) === snapshot.firstMessageId) {
|
|
5258
|
+
if (!isLoadingOlderMessages) {
|
|
5259
|
+
prependSnapshotRef.current = null;
|
|
5260
|
+
}
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5263
|
+
requestAnimationFrame(() => {
|
|
5264
|
+
virtualizer.measure();
|
|
5265
|
+
requestAnimationFrame(() => {
|
|
5266
|
+
const viewport = scrollAreaRef.current;
|
|
5267
|
+
if (!viewport) return;
|
|
5268
|
+
const heightDelta = viewport.scrollHeight - snapshot.scrollHeight;
|
|
5269
|
+
viewport.scrollTop = snapshot.scrollTop + heightDelta;
|
|
5270
|
+
prependSnapshotRef.current = null;
|
|
5271
|
+
});
|
|
5272
|
+
});
|
|
5273
|
+
}, [messages, isLoadingOlderMessages, virtualizer]);
|
|
5274
|
+
const requestOlderMessages = useCallback4(() => {
|
|
5275
|
+
if (!onLoadOlderMessages || !hasMoreMessagesBefore || isLoadingOlderMessages) return;
|
|
5276
|
+
const viewport = scrollAreaRef.current;
|
|
5277
|
+
prependSnapshotRef.current = viewport ? {
|
|
5278
|
+
scrollHeight: viewport.scrollHeight,
|
|
5279
|
+
scrollTop: viewport.scrollTop,
|
|
5280
|
+
firstMessageId: messages[0]?.id ?? null,
|
|
5281
|
+
messageCount: messages.length
|
|
5282
|
+
} : null;
|
|
5283
|
+
onLoadOlderMessages();
|
|
5284
|
+
}, [hasMoreMessagesBefore, isLoadingOlderMessages, messages, onLoadOlderMessages]);
|
|
5394
5285
|
useEffect10(() => {
|
|
5395
5286
|
const validMessageIds = new Set(messages.map((message) => message.id));
|
|
5396
5287
|
setExpandedMessageIds((prev) => {
|
|
@@ -5409,11 +5300,15 @@ var ChatUI = ({
|
|
|
5409
5300
|
const handleScroll = useCallback4((e) => {
|
|
5410
5301
|
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
|
5411
5302
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
|
|
5303
|
+
const isNearTop = scrollTop < 120;
|
|
5304
|
+
if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
|
|
5305
|
+
requestOlderMessages();
|
|
5306
|
+
}
|
|
5412
5307
|
setState((prev) => {
|
|
5413
5308
|
if (prev.isAtBottom === isAtBottom) return prev;
|
|
5414
5309
|
return { ...prev, isAtBottom };
|
|
5415
5310
|
});
|
|
5416
|
-
}, []);
|
|
5311
|
+
}, [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]);
|
|
5417
5312
|
const handleSendMessage = useCallback4((content, messageAttachments = []) => {
|
|
5418
5313
|
if (!content.trim() && messageAttachments.length === 0) return;
|
|
5419
5314
|
callbacks.onSendMessage?.(content, messageAttachments, createStateCallback());
|
|
@@ -5669,48 +5564,59 @@ var ChatUI = ({
|
|
|
5669
5564
|
viewportClassName: "p-4 overscroll-contain",
|
|
5670
5565
|
onScrollCapture: handleScroll,
|
|
5671
5566
|
style: { contain: "strict" },
|
|
5672
|
-
children: /* @__PURE__ */
|
|
5673
|
-
"div",
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
{
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5567
|
+
children: /* @__PURE__ */ jsxs16("div", { className: "max-w-4xl mx-auto pb-4", children: [
|
|
5568
|
+
messages.length > 0 && /* @__PURE__ */ jsx26("div", { className: "flex justify-center py-2", children: isLoadingOlderMessages ? /* @__PURE__ */ jsx26("span", { className: "text-xs text-muted-foreground", children: config.labels.loadingOlderMessages }) : hasMoreMessagesBefore ? /* @__PURE__ */ jsx26(
|
|
5569
|
+
"button",
|
|
5570
|
+
{
|
|
5571
|
+
type: "button",
|
|
5572
|
+
onClick: requestOlderMessages,
|
|
5573
|
+
className: "text-xs text-muted-foreground transition-colors hover:text-foreground",
|
|
5574
|
+
children: config.labels.loadOlderMessages
|
|
5575
|
+
}
|
|
5576
|
+
) : null }),
|
|
5577
|
+
isMessagesLoading ? renderMessageLoadingSkeleton() : messages.length === 0 ? renderSuggestions() : /* @__PURE__ */ jsx26(
|
|
5578
|
+
"div",
|
|
5579
|
+
{
|
|
5580
|
+
style: {
|
|
5581
|
+
height: `${virtualizer.getTotalSize()}px`,
|
|
5582
|
+
width: "100%",
|
|
5583
|
+
position: "relative"
|
|
5584
|
+
},
|
|
5585
|
+
children: virtualizer.getVirtualItems().map((virtualRow) => {
|
|
5586
|
+
const message = messages[virtualRow.index];
|
|
5587
|
+
const prevMessage = virtualRow.index > 0 ? messages[virtualRow.index - 1] : null;
|
|
5588
|
+
const isGrouped = prevMessage !== null && prevMessage.role === message.role && getMessageSpeakerKey(prevMessage) === getMessageSpeakerKey(message);
|
|
5589
|
+
return /* @__PURE__ */ jsx26(
|
|
5590
|
+
"div",
|
|
5591
|
+
{
|
|
5592
|
+
"data-index": virtualRow.index,
|
|
5593
|
+
ref: virtualizer.measureElement,
|
|
5594
|
+
style: {
|
|
5595
|
+
position: "absolute",
|
|
5596
|
+
top: 0,
|
|
5597
|
+
left: 0,
|
|
5598
|
+
width: "100%",
|
|
5599
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
5600
|
+
},
|
|
5601
|
+
children: /* @__PURE__ */ jsxs16("div", { className: virtualRow.index === 0 ? "" : isGrouped ? "pt-2" : "pt-4", children: [
|
|
5602
|
+
/* @__PURE__ */ jsx26(
|
|
5603
|
+
Message,
|
|
5604
|
+
{
|
|
5605
|
+
message,
|
|
5606
|
+
...messageProps,
|
|
5607
|
+
isGrouped,
|
|
5608
|
+
isExpanded: Boolean(expandedMessageIds[message.id])
|
|
5609
|
+
}
|
|
5610
|
+
),
|
|
5611
|
+
message.role === "assistant" && renderInlineSuggestions(message.id)
|
|
5612
|
+
] })
|
|
5695
5613
|
},
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
isGrouped,
|
|
5703
|
-
isExpanded: Boolean(expandedMessageIds[message.id])
|
|
5704
|
-
}
|
|
5705
|
-
),
|
|
5706
|
-
message.role === "assistant" && renderInlineSuggestions(message.id)
|
|
5707
|
-
] })
|
|
5708
|
-
},
|
|
5709
|
-
message.id
|
|
5710
|
-
);
|
|
5711
|
-
})
|
|
5712
|
-
}
|
|
5713
|
-
) })
|
|
5614
|
+
message.id
|
|
5615
|
+
);
|
|
5616
|
+
})
|
|
5617
|
+
}
|
|
5618
|
+
)
|
|
5619
|
+
] })
|
|
5714
5620
|
}
|
|
5715
5621
|
),
|
|
5716
5622
|
/* @__PURE__ */ jsxs16("div", { className: "bg-background pb-[env(safe-area-inset-bottom)]", children: [
|