@copilotz/chat-ui 0.3.3 → 0.3.5

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.d.cts CHANGED
@@ -186,6 +186,8 @@ interface ChatConfig {
186
186
  inputHelpText?: string;
187
187
  thinking?: string;
188
188
  defaultThreadName?: string;
189
+ loadOlderMessages?: string;
190
+ loadingOlderMessages?: string;
189
191
  showMoreMessage?: string;
190
192
  showLessMessage?: string;
191
193
  };
@@ -267,7 +269,10 @@ interface ChatV2Props {
267
269
  sidebar?: ReactNode;
268
270
  isGenerating?: boolean;
269
271
  isMessagesLoading?: boolean;
272
+ isLoadingOlderMessages?: boolean;
273
+ hasMoreMessagesBefore?: boolean;
270
274
  callbacks?: ChatCallbacks;
275
+ onLoadOlderMessages?: () => void;
271
276
  user?: {
272
277
  id: string;
273
278
  name?: string;
package/dist/index.d.ts CHANGED
@@ -186,6 +186,8 @@ interface ChatConfig {
186
186
  inputHelpText?: string;
187
187
  thinking?: string;
188
188
  defaultThreadName?: string;
189
+ loadOlderMessages?: string;
190
+ loadingOlderMessages?: string;
189
191
  showMoreMessage?: string;
190
192
  showLessMessage?: string;
191
193
  };
@@ -267,7 +269,10 @@ interface ChatV2Props {
267
269
  sidebar?: ReactNode;
268
270
  isGenerating?: boolean;
269
271
  isMessagesLoading?: boolean;
272
+ isLoadingOlderMessages?: boolean;
273
+ hasMoreMessagesBefore?: boolean;
270
274
  callbacks?: ChatCallbacks;
275
+ onLoadOlderMessages?: () => void;
271
276
  user?: {
272
277
  id: string;
273
278
  name?: string;
package/dist/index.js CHANGED
@@ -85,6 +85,8 @@ var defaultChatConfig = {
85
85
  inputHelpText: "Press Enter to send, Shift+Enter to add a new line.",
86
86
  thinking: "Thinking...",
87
87
  defaultThreadName: "Main Thread",
88
+ loadOlderMessages: "Load older messages",
89
+ loadingOlderMessages: "Loading older messages...",
88
90
  showMoreMessage: "Show more",
89
91
  showLessMessage: "Show less"
90
92
  },
@@ -5084,7 +5086,10 @@ var ChatUI = ({
5084
5086
  sidebar: _sidebar,
5085
5087
  isGenerating = false,
5086
5088
  isMessagesLoading = false,
5089
+ isLoadingOlderMessages = false,
5090
+ hasMoreMessagesBefore = false,
5087
5091
  callbacks = {},
5092
+ onLoadOlderMessages,
5088
5093
  user,
5089
5094
  assistant,
5090
5095
  suggestions = [],
@@ -5151,6 +5156,7 @@ var ChatUI = ({
5151
5156
  }
5152
5157
  }, [initialInput]);
5153
5158
  const scrollAreaRef = useRef6(null);
5159
+ const prependSnapshotRef = useRef6(null);
5154
5160
  const stateRef = useRef6(state);
5155
5161
  const inputValueRef = useRef6(inputValue);
5156
5162
  const attachmentsRef = useRef6(attachments);
@@ -5208,6 +5214,10 @@ var ChatUI = ({
5208
5214
  prevMessageCountRef.current = 0;
5209
5215
  return;
5210
5216
  }
5217
+ if (prependSnapshotRef.current) {
5218
+ prevMessageCountRef.current = messages.length;
5219
+ return;
5220
+ }
5211
5221
  const wasEmpty = prevMessageCountRef.current === 0;
5212
5222
  prevMessageCountRef.current = messages.length;
5213
5223
  if (wasEmpty) {
@@ -5232,6 +5242,46 @@ var ChatUI = ({
5232
5242
  useEffect10(() => {
5233
5243
  virtualizer.measure();
5234
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]);
5235
5285
  useEffect10(() => {
5236
5286
  const validMessageIds = new Set(messages.map((message) => message.id));
5237
5287
  setExpandedMessageIds((prev) => {
@@ -5250,11 +5300,15 @@ var ChatUI = ({
5250
5300
  const handleScroll = useCallback4((e) => {
5251
5301
  const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
5252
5302
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
5303
+ const isNearTop = scrollTop < 120;
5304
+ if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
5305
+ requestOlderMessages();
5306
+ }
5253
5307
  setState((prev) => {
5254
5308
  if (prev.isAtBottom === isAtBottom) return prev;
5255
5309
  return { ...prev, isAtBottom };
5256
5310
  });
5257
- }, []);
5311
+ }, [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]);
5258
5312
  const handleSendMessage = useCallback4((content, messageAttachments = []) => {
5259
5313
  if (!content.trim() && messageAttachments.length === 0) return;
5260
5314
  callbacks.onSendMessage?.(content, messageAttachments, createStateCallback());
@@ -5510,48 +5564,59 @@ var ChatUI = ({
5510
5564
  viewportClassName: "p-4 overscroll-contain",
5511
5565
  onScrollCapture: handleScroll,
5512
5566
  style: { contain: "strict" },
5513
- children: /* @__PURE__ */ jsx26("div", { className: "max-w-4xl mx-auto pb-4", children: isMessagesLoading ? renderMessageLoadingSkeleton() : messages.length === 0 ? renderSuggestions() : /* @__PURE__ */ jsx26(
5514
- "div",
5515
- {
5516
- style: {
5517
- height: `${virtualizer.getTotalSize()}px`,
5518
- width: "100%",
5519
- position: "relative"
5520
- },
5521
- children: virtualizer.getVirtualItems().map((virtualRow) => {
5522
- const message = messages[virtualRow.index];
5523
- const prevMessage = virtualRow.index > 0 ? messages[virtualRow.index - 1] : null;
5524
- const isGrouped = prevMessage !== null && prevMessage.role === message.role && getMessageSpeakerKey(prevMessage) === getMessageSpeakerKey(message);
5525
- return /* @__PURE__ */ jsx26(
5526
- "div",
5527
- {
5528
- "data-index": virtualRow.index,
5529
- ref: virtualizer.measureElement,
5530
- style: {
5531
- position: "absolute",
5532
- top: 0,
5533
- left: 0,
5534
- width: "100%",
5535
- transform: `translateY(${virtualRow.start}px)`
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
+ ] })
5536
5613
  },
5537
- children: /* @__PURE__ */ jsxs16("div", { className: virtualRow.index === 0 ? "" : isGrouped ? "pt-2" : "pt-4", children: [
5538
- /* @__PURE__ */ jsx26(
5539
- Message,
5540
- {
5541
- message,
5542
- ...messageProps,
5543
- isGrouped,
5544
- isExpanded: Boolean(expandedMessageIds[message.id])
5545
- }
5546
- ),
5547
- message.role === "assistant" && renderInlineSuggestions(message.id)
5548
- ] })
5549
- },
5550
- message.id
5551
- );
5552
- })
5553
- }
5554
- ) })
5614
+ message.id
5615
+ );
5616
+ })
5617
+ }
5618
+ )
5619
+ ] })
5555
5620
  }
5556
5621
  ),
5557
5622
  /* @__PURE__ */ jsxs16("div", { className: "bg-background pb-[env(safe-area-inset-bottom)]", children: [