@copilotz/chat-ui 0.1.7 → 0.1.9

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 CHANGED
@@ -55,6 +55,7 @@ module.exports = __toCommonJS(index_exports);
55
55
 
56
56
  // src/components/chat/ChatUI.tsx
57
57
  var import_react7 = require("react");
58
+ var import_react_virtual = require("@tanstack/react-virtual");
58
59
 
59
60
  // src/config/chatConfig.ts
60
61
  var defaultChatConfig = {
@@ -616,10 +617,11 @@ var rehypePluginsEmpty = [];
616
617
  var StreamingText = (0, import_react.memo)(function StreamingText2({
617
618
  content,
618
619
  isStreaming = false,
619
- thinkingLabel = "Thinking..."
620
+ thinkingLabel = "Thinking...",
621
+ className = ""
620
622
  }) {
621
623
  const hasContent = content.trim().length > 0;
622
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "prose prose-sm max-w-none dark:prose-invert", children: [
624
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `prose prose-sm max-w-none dark:prose-invert break-words ${className}`, children: [
623
625
  hasContent ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
624
626
  import_react_markdown.default,
625
627
  {
@@ -861,7 +863,7 @@ var Message = (0, import_react.memo)(({
861
863
  minute: "2-digit"
862
864
  });
863
865
  };
864
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
866
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
865
867
  "div",
866
868
  {
867
869
  className: `flex w-full flex-col ${className} max-w-[800px] mx-auto`,
@@ -879,7 +881,7 @@ var Message = (0, import_react.memo)(({
879
881
  message.isEdited && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Badge, { variant: "outline", className: "text-xs", children: "editado" })
880
882
  ] })
881
883
  ] }),
882
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `relative inline-flex flex-col ${messageIsUser ? "rounded-lg p-3 bg-primary text-primary-foreground ml-auto max-w-[85%]" : "max-w-[85%]"}`, children: [
884
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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__ */ (0, import_jsx_runtime7.jsxs)("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: [
883
885
  isEditing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
884
886
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
885
887
  Textarea,
@@ -956,7 +958,7 @@ var Message = (0, import_react.memo)(({
956
958
  ] }) })
957
959
  ]
958
960
  }
959
- ) });
961
+ );
960
962
  }, arePropsEqual);
961
963
 
962
964
  // src/components/chat/Sidebar.tsx
@@ -3184,7 +3186,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3184
3186
  onChange: (e) => onChange(e.target.value),
3185
3187
  onKeyDown: handleKeyDown,
3186
3188
  placeholder,
3187
- disabled: disabled || isGenerating,
3189
+ disabled,
3188
3190
  className: "max-h-[120px] resize-none border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0",
3189
3191
  rows: 1
3190
3192
  }
@@ -3757,7 +3759,6 @@ var ChatUI = ({
3757
3759
  initialInputApplied.current = true;
3758
3760
  }
3759
3761
  }, [initialInput]);
3760
- const messagesEndRef = (0, import_react7.useRef)(null);
3761
3762
  const scrollAreaRef = (0, import_react7.useRef)(null);
3762
3763
  const stateRef = (0, import_react7.useRef)(state);
3763
3764
  const inputValueRef = (0, import_react7.useRef)(inputValue);
@@ -3773,6 +3774,12 @@ var ChatUI = ({
3773
3774
  }, [attachments]);
3774
3775
  const [isCustomMounted, setIsCustomMounted] = (0, import_react7.useState)(false);
3775
3776
  const [isCustomVisible, setIsCustomVisible] = (0, import_react7.useState)(false);
3777
+ const virtualizer = (0, import_react_virtual.useVirtualizer)({
3778
+ count: messages.length,
3779
+ getScrollElement: () => scrollAreaRef.current,
3780
+ estimateSize: () => 100,
3781
+ overscan: 5
3782
+ });
3776
3783
  const createStateCallback = (0, import_react7.useCallback)(
3777
3784
  (setter) => ({
3778
3785
  setState: (newState) => setter?.(newState),
@@ -3804,21 +3811,40 @@ var ChatUI = ({
3804
3811
  return () => clearTimeout(t);
3805
3812
  }
3806
3813
  }, [state.showSidebar, isMobile, config.customComponent]);
3814
+ const prevMessageCountRef = (0, import_react7.useRef)(0);
3807
3815
  (0, import_react7.useEffect)(() => {
3808
- if (!state.isAtBottom) return;
3809
- const viewport = scrollAreaRef.current;
3810
- if (!viewport) return;
3811
- const target = viewport.scrollHeight;
3812
- try {
3813
- viewport.scrollTo({ top: target, behavior: "smooth" });
3814
- } catch {
3815
- viewport.scrollTop = target;
3816
+ if (messages.length === 0) {
3817
+ prevMessageCountRef.current = 0;
3818
+ return;
3816
3819
  }
3817
- }, [messages, state.isAtBottom]);
3820
+ const wasEmpty = prevMessageCountRef.current === 0;
3821
+ prevMessageCountRef.current = messages.length;
3822
+ if (wasEmpty) {
3823
+ requestAnimationFrame(() => {
3824
+ requestAnimationFrame(() => {
3825
+ virtualizer.scrollToIndex(messages.length - 1, { align: "end" });
3826
+ });
3827
+ });
3828
+ return;
3829
+ }
3830
+ if (!state.isAtBottom) return;
3831
+ requestAnimationFrame(() => {
3832
+ const viewport = scrollAreaRef.current;
3833
+ if (!viewport) return;
3834
+ try {
3835
+ viewport.scrollTo({ top: viewport.scrollHeight, behavior: "smooth" });
3836
+ } catch {
3837
+ viewport.scrollTop = viewport.scrollHeight;
3838
+ }
3839
+ });
3840
+ }, [messages, state.isAtBottom, virtualizer]);
3818
3841
  const handleScroll = (0, import_react7.useCallback)((e) => {
3819
3842
  const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
3820
3843
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
3821
- setState((prev) => ({ ...prev, isAtBottom }));
3844
+ setState((prev) => {
3845
+ if (prev.isAtBottom === isAtBottom) return prev;
3846
+ return { ...prev, isAtBottom };
3847
+ });
3822
3848
  }, []);
3823
3849
  const handleSendMessage = (0, import_react7.useCallback)((content, messageAttachments = []) => {
3824
3850
  if (!content.trim() && messageAttachments.length === 0) return;
@@ -3939,49 +3965,26 @@ var ChatUI = ({
3939
3965
  `message-skeleton-${index}`
3940
3966
  );
3941
3967
  }) });
3942
- const renderedMessageList = (0, import_react7.useMemo)(() => {
3943
- if (isMessagesLoading) return renderMessageLoadingSkeleton();
3944
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
3945
- renderSuggestions(),
3946
- messages.map((message, index) => {
3947
- const prevMessage = index > 0 ? messages[index - 1] : null;
3948
- const isGrouped = prevMessage !== null && prevMessage.role === message.role;
3949
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: isGrouped ? "space-y-1 -mt-2" : "space-y-2", children: [
3950
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3951
- Message,
3952
- {
3953
- message,
3954
- userAvatar: user?.avatar,
3955
- userName: user?.name,
3956
- assistantAvatar: assistant?.avatar,
3957
- assistantName: assistant?.name,
3958
- showTimestamp: config.ui.showTimestamps,
3959
- showAvatar: config.ui.showAvatars,
3960
- enableCopy: config.features.enableMessageCopy,
3961
- enableEdit: config.features.enableMessageEditing,
3962
- enableRegenerate: config.features.enableRegeneration,
3963
- enableToolCallsDisplay: config.features.enableToolCallsDisplay,
3964
- compactMode: config.ui.compactMode,
3965
- onAction: handleMessageAction,
3966
- toolUsedLabel: config.labels.toolUsed,
3967
- thinkingLabel: config.labels.thinking,
3968
- isGrouped
3969
- }
3970
- ),
3971
- message.role === "assistant" && renderInlineSuggestions(message.id)
3972
- ] }, message.id);
3973
- })
3974
- ] });
3975
- }, [
3976
- isMessagesLoading,
3977
- messages,
3978
- handleSendMessage,
3968
+ const messageProps = (0, import_react7.useMemo)(() => ({
3969
+ userAvatar: user?.avatar,
3970
+ userName: user?.name,
3971
+ assistantAvatar: assistant?.avatar,
3972
+ assistantName: assistant?.name,
3973
+ showTimestamp: config.ui.showTimestamps,
3974
+ showAvatar: config.ui.showAvatars,
3975
+ enableCopy: config.features.enableMessageCopy,
3976
+ enableEdit: config.features.enableMessageEditing,
3977
+ enableRegenerate: config.features.enableRegeneration,
3978
+ enableToolCallsDisplay: config.features.enableToolCallsDisplay,
3979
+ compactMode: config.ui.compactMode,
3980
+ onAction: handleMessageAction,
3981
+ toolUsedLabel: config.labels.toolUsed,
3982
+ thinkingLabel: config.labels.thinking
3983
+ }), [
3979
3984
  user?.avatar,
3980
3985
  user?.name,
3981
3986
  assistant?.avatar,
3982
3987
  assistant?.name,
3983
- config.branding.title,
3984
- config.branding.subtitle,
3985
3988
  config.ui.showTimestamps,
3986
3989
  config.ui.showAvatars,
3987
3990
  config.ui.compactMode,
@@ -3991,9 +3994,7 @@ var ChatUI = ({
3991
3994
  config.features.enableToolCallsDisplay,
3992
3995
  config.labels.toolUsed,
3993
3996
  config.labels.thinking,
3994
- handleMessageAction,
3995
- messageSuggestions,
3996
- suggestions
3997
+ handleMessageAction
3997
3998
  ]);
3998
3999
  const shouldShowAgentSelector = Boolean(
3999
4000
  config.agentSelector?.enabled && onSelectAgent && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1)
@@ -4054,10 +4055,48 @@ var ChatUI = ({
4054
4055
  className: "flex-1 min-h-0",
4055
4056
  viewportClassName: "p-4 overscroll-contain",
4056
4057
  onScrollCapture: handleScroll,
4057
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "max-w-4xl mx-auto space-y-4 pb-4", children: [
4058
- renderedMessageList,
4059
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: messagesEndRef })
4060
- ] })
4058
+ style: { contain: "strict" },
4059
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "max-w-4xl mx-auto pb-4", children: isMessagesLoading ? renderMessageLoadingSkeleton() : messages.length === 0 ? renderSuggestions() : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4060
+ "div",
4061
+ {
4062
+ style: {
4063
+ height: `${virtualizer.getTotalSize()}px`,
4064
+ width: "100%",
4065
+ position: "relative"
4066
+ },
4067
+ children: virtualizer.getVirtualItems().map((virtualRow) => {
4068
+ const message = messages[virtualRow.index];
4069
+ const prevMessage = virtualRow.index > 0 ? messages[virtualRow.index - 1] : null;
4070
+ const isGrouped = prevMessage !== null && prevMessage.role === message.role;
4071
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4072
+ "div",
4073
+ {
4074
+ "data-index": virtualRow.index,
4075
+ ref: virtualizer.measureElement,
4076
+ style: {
4077
+ position: "absolute",
4078
+ top: 0,
4079
+ left: 0,
4080
+ width: "100%",
4081
+ transform: `translateY(${virtualRow.start}px)`
4082
+ },
4083
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: virtualRow.index === 0 ? "" : isGrouped ? "pt-2" : "pt-4", children: [
4084
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4085
+ Message,
4086
+ {
4087
+ message,
4088
+ ...messageProps,
4089
+ isGrouped
4090
+ }
4091
+ ),
4092
+ message.role === "assistant" && renderInlineSuggestions(message.id)
4093
+ ] })
4094
+ },
4095
+ message.id
4096
+ );
4097
+ })
4098
+ }
4099
+ ) })
4061
4100
  }
4062
4101
  ),
4063
4102
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "bg-background pb-[env(safe-area-inset-bottom)]", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(