@copilotz/chat-ui 0.1.10 → 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.d.cts CHANGED
@@ -592,6 +592,7 @@ declare const configUtils: {
592
592
 
593
593
  declare function cn(...inputs: ClassValue[]): string;
594
594
  declare const formatDate: (timestamp: number, labels?: ChatConfig["labels"]) => string;
595
+ declare const createObjectUrlFromDataUrl: (dataUrl: string) => string | null;
595
596
 
596
597
  declare const chatUtils: {
597
598
  generateId: () => string;
@@ -602,4 +603,4 @@ declare const chatUtils: {
602
603
  generateThreadTitle: (firstMessage: string) => string;
603
604
  };
604
605
 
605
- export { type AgentOption, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, chatConfigPresets, chatUtils, cn, configUtils, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
606
+ export { type AgentOption, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
package/dist/index.d.ts CHANGED
@@ -592,6 +592,7 @@ declare const configUtils: {
592
592
 
593
593
  declare function cn(...inputs: ClassValue[]): string;
594
594
  declare const formatDate: (timestamp: number, labels?: ChatConfig["labels"]) => string;
595
+ declare const createObjectUrlFromDataUrl: (dataUrl: string) => string | null;
595
596
 
596
597
  declare const chatUtils: {
597
598
  generateId: () => string;
@@ -602,4 +603,4 @@ declare const chatUtils: {
602
603
  generateThreadTitle: (firstMessage: string) => string;
603
604
  };
604
605
 
605
- export { type AgentOption, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, chatConfigPresets, chatUtils, cn, configUtils, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
606
+ export { type AgentOption, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
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 useMemo4 } from "react";
2
+ import { useState as useState8, useEffect as useEffect10, useRef as useRef6, useCallback as useCallback4, useMemo as useMemo4 } from "react";
3
3
  import { useVirtualizer } from "@tanstack/react-virtual";
4
4
 
5
5
  // src/config/chatConfig.ts
@@ -245,7 +245,7 @@ var configUtils = {
245
245
  };
246
246
 
247
247
  // src/components/chat/Message.tsx
248
- import React, { useState, useRef, useMemo, memo } from "react";
248
+ import React, { useState, useMemo, useEffect, memo } from "react";
249
249
  import ReactMarkdown from "react-markdown";
250
250
  import remarkGfm from "remark-gfm";
251
251
  import rehypeHighlight from "rehype-highlight";
@@ -278,6 +278,24 @@ var formatDate = (timestamp, labels) => {
278
278
  });
279
279
  }
280
280
  };
281
+ var createObjectUrlFromDataUrl = (dataUrl) => {
282
+ const match = dataUrl.match(/^data:(.+?);base64,(.+)$/s);
283
+ if (!match) {
284
+ return null;
285
+ }
286
+ try {
287
+ const [, mimeType, base64] = match;
288
+ const binary = atob(base64);
289
+ const bytes = new Uint8Array(binary.length);
290
+ for (let i = 0; i < binary.length; i += 1) {
291
+ bytes[i] = binary.charCodeAt(i);
292
+ }
293
+ const blob = new Blob([bytes], { type: mimeType || "application/octet-stream" });
294
+ return URL.createObjectURL(blob);
295
+ } catch {
296
+ return null;
297
+ }
298
+ };
281
299
 
282
300
  // src/components/ui/button.tsx
283
301
  import { jsx } from "react/jsx-runtime";
@@ -668,26 +686,22 @@ var StreamingText = memo(function StreamingText2({
668
686
  ] });
669
687
  });
670
688
  var MediaRenderer = memo(function MediaRenderer2({ attachment }) {
671
- const [isPlaying, setIsPlaying] = useState(false);
672
- const audioRef = useRef(null);
673
- const videoRef = useRef(null);
674
- const togglePlayback = () => {
675
- if (attachment.kind === "audio" && audioRef.current) {
676
- if (isPlaying) {
677
- audioRef.current.pause();
678
- } else {
679
- audioRef.current.play();
680
- }
681
- setIsPlaying(!isPlaying);
682
- } else if (attachment.kind === "video" && videoRef.current) {
683
- if (isPlaying) {
684
- videoRef.current.pause();
685
- } else {
686
- videoRef.current.play();
687
- }
688
- setIsPlaying(!isPlaying);
689
+ const [audioPlaybackSrc, setAudioPlaybackSrc] = useState(attachment.dataUrl);
690
+ useEffect(() => {
691
+ if (attachment.kind !== "audio" || !attachment.dataUrl.startsWith("data:")) {
692
+ setAudioPlaybackSrc(attachment.dataUrl);
693
+ return;
689
694
  }
690
- };
695
+ const objectUrl = createObjectUrlFromDataUrl(attachment.dataUrl);
696
+ if (!objectUrl) {
697
+ setAudioPlaybackSrc(attachment.dataUrl);
698
+ return;
699
+ }
700
+ setAudioPlaybackSrc(objectUrl);
701
+ return () => {
702
+ URL.revokeObjectURL(objectUrl);
703
+ };
704
+ }, [attachment.kind, attachment.dataUrl]);
691
705
  const formatDuration = (ms) => {
692
706
  if (!ms) return "";
693
707
  const seconds = Math.floor(ms / 1e3);
@@ -712,13 +726,10 @@ var MediaRenderer = memo(function MediaRenderer2({ attachment }) {
712
726
  return /* @__PURE__ */ jsx7("div", { className: "flex w-full max-w-md py-0 min-w-64 items-center gap-3", children: /* @__PURE__ */ jsx7(
713
727
  "audio",
714
728
  {
715
- ref: audioRef,
716
- src: attachment.dataUrl,
717
- onPlay: () => setIsPlaying(true),
718
- onPause: () => setIsPlaying(false),
719
- onEnded: () => setIsPlaying(false),
720
729
  className: "w-full mt-2",
721
- controls: true
730
+ preload: "metadata",
731
+ controls: true,
732
+ children: /* @__PURE__ */ jsx7("source", { src: audioPlaybackSrc, type: attachment.mimeType })
722
733
  }
723
734
  ) });
724
735
  case "video":
@@ -726,14 +737,10 @@ var MediaRenderer = memo(function MediaRenderer2({ attachment }) {
726
737
  /* @__PURE__ */ jsx7(
727
738
  "video",
728
739
  {
729
- ref: videoRef,
730
740
  src: attachment.dataUrl,
731
741
  poster: attachment.poster,
732
742
  controls: true,
733
- className: "w-full h-auto",
734
- onPlay: () => setIsPlaying(true),
735
- onPause: () => setIsPlaying(false),
736
- onEnded: () => setIsPlaying(false)
743
+ className: "w-full h-auto"
737
744
  }
738
745
  ),
739
746
  attachment.fileName && /* @__PURE__ */ jsx7("div", { className: "absolute bottom-0 left-0 right-0 bg-black/50 text-white text-xs p-2", children: attachment.fileName })
@@ -1040,7 +1047,7 @@ var Message = memo(({
1040
1047
  }, arePropsEqual);
1041
1048
 
1042
1049
  // src/components/chat/Sidebar.tsx
1043
- import { useState as useState4, useRef as useRef5, useEffect as useEffect6 } from "react";
1050
+ import { useState as useState4, useRef as useRef4, useEffect as useEffect7 } from "react";
1044
1051
 
1045
1052
  // src/components/ui/input.tsx
1046
1053
  import { jsx as jsx8 } from "react/jsx-runtime";
@@ -2289,9 +2296,9 @@ var Sidebar2 = ({
2289
2296
  const [deleteThreadId, setDeleteThreadId] = useState4(null);
2290
2297
  const [editingThreadId, setEditingThreadId] = useState4(null);
2291
2298
  const [editTitle, setEditTitle] = useState4("");
2292
- const inputRef = useRef5(null);
2299
+ const inputRef = useRef4(null);
2293
2300
  const { setOpen } = useSidebar();
2294
- useEffect6(() => {
2301
+ useEffect7(() => {
2295
2302
  if (editingThreadId && inputRef.current) {
2296
2303
  inputRef.current.focus();
2297
2304
  inputRef.current.select();
@@ -2708,10 +2715,10 @@ var ChatHeader = ({
2708
2715
  };
2709
2716
 
2710
2717
  // src/components/chat/ChatInput.tsx
2711
- import { useState as useState6, useRef as useRef6, useCallback as useCallback3, useEffect as useEffect8, memo as memo2 } from "react";
2718
+ import { useState as useState6, useRef as useRef5, useCallback as useCallback3, useEffect as useEffect9, memo as memo2 } from "react";
2712
2719
 
2713
2720
  // src/components/chat/UserContext.tsx
2714
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect7, useMemo as useMemo3, useState as useState5 } from "react";
2721
+ import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect8, useMemo as useMemo3, useState as useState5 } from "react";
2715
2722
  import { jsx as jsx19 } from "react/jsx-runtime";
2716
2723
  var Ctx = createContext2(void 0);
2717
2724
  var ChatUserContextProvider = ({ children, initial }) => {
@@ -2719,7 +2726,7 @@ var ChatUserContextProvider = ({ children, initial }) => {
2719
2726
  updatedAt: Date.now(),
2720
2727
  ...initial ?? {}
2721
2728
  }));
2722
- useEffect7(() => {
2729
+ useEffect8(() => {
2723
2730
  if (!initial) return;
2724
2731
  setCtx((prev) => ({
2725
2732
  ...prev,
@@ -2785,8 +2792,8 @@ import {
2785
2792
  FileText,
2786
2793
  X as X2,
2787
2794
  Square,
2788
- Play as Play2,
2789
- Pause as Pause2,
2795
+ Play,
2796
+ Pause,
2790
2797
  Loader2
2791
2798
  } from "lucide-react";
2792
2799
  import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
@@ -2851,7 +2858,23 @@ var FileUploadItem = memo2(function FileUploadItem2({ file, progress, onCancel }
2851
2858
  });
2852
2859
  var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove }) {
2853
2860
  const [isPlaying, setIsPlaying] = useState6(false);
2854
- const audioRef = useRef6(null);
2861
+ const [audioPlaybackSrc, setAudioPlaybackSrc] = useState6(attachment.dataUrl);
2862
+ const audioRef = useRef5(null);
2863
+ useEffect9(() => {
2864
+ if (attachment.kind !== "audio" || !attachment.dataUrl.startsWith("data:")) {
2865
+ setAudioPlaybackSrc(attachment.dataUrl);
2866
+ return;
2867
+ }
2868
+ const objectUrl = createObjectUrlFromDataUrl(attachment.dataUrl);
2869
+ if (!objectUrl) {
2870
+ setAudioPlaybackSrc(attachment.dataUrl);
2871
+ return;
2872
+ }
2873
+ setAudioPlaybackSrc(objectUrl);
2874
+ return () => {
2875
+ URL.revokeObjectURL(objectUrl);
2876
+ };
2877
+ }, [attachment.kind, attachment.dataUrl]);
2855
2878
  const handlePlayPause = () => {
2856
2879
  if (audioRef.current) {
2857
2880
  if (isPlaying) {
@@ -2919,7 +2942,7 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
2919
2942
  size: "icon",
2920
2943
  className: "h-8 w-8",
2921
2944
  onClick: handlePlayPause,
2922
- children: isPlaying ? /* @__PURE__ */ jsx21(Pause2, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx21(Play2, { className: "h-3 w-3" })
2945
+ children: isPlaying ? /* @__PURE__ */ jsx21(Pause, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx21(Play, { className: "h-3 w-3" })
2923
2946
  }
2924
2947
  ),
2925
2948
  /* @__PURE__ */ jsxs11("div", { className: "flex-1", children: [
@@ -2930,10 +2953,11 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
2930
2953
  "audio",
2931
2954
  {
2932
2955
  ref: audioRef,
2933
- src: attachment.dataUrl,
2934
2956
  onPlay: () => setIsPlaying(true),
2935
2957
  onPause: () => setIsPlaying(false),
2936
- onEnded: () => setIsPlaying(false)
2958
+ onEnded: () => setIsPlaying(false),
2959
+ preload: "metadata",
2960
+ children: /* @__PURE__ */ jsx21("source", { src: audioPlaybackSrc, type: attachment.mimeType })
2937
2961
  }
2938
2962
  ),
2939
2963
  /* @__PURE__ */ jsx21(
@@ -3028,13 +3052,13 @@ var ChatInput = memo2(function ChatInput2({
3028
3052
  const { setContext } = useChatUserContext();
3029
3053
  const [recordingDuration, setRecordingDuration] = useState6(0);
3030
3054
  const [uploadProgress, setUploadProgress] = useState6(/* @__PURE__ */ new Map());
3031
- const textareaRef = useRef6(null);
3032
- const fileInputRef = useRef6(null);
3033
- const mediaRecorderRef = useRef6(null);
3034
- const recordingStartTime = useRef6(0);
3035
- const recordingInterval = useRef6(null);
3036
- const mediaStreamRef = useRef6(null);
3037
- useEffect8(() => {
3055
+ const textareaRef = useRef5(null);
3056
+ const fileInputRef = useRef5(null);
3057
+ const mediaRecorderRef = useRef5(null);
3058
+ const recordingStartTime = useRef5(0);
3059
+ const recordingInterval = useRef5(null);
3060
+ const mediaStreamRef = useRef5(null);
3061
+ useEffect9(() => {
3038
3062
  return () => {
3039
3063
  if (mediaStreamRef.current) {
3040
3064
  mediaStreamRef.current.getTracks().forEach((track) => track.stop());
@@ -3892,30 +3916,30 @@ var ChatUI = ({
3892
3916
  isSidebarCollapsed: false
3893
3917
  // No longer used for main sidebar
3894
3918
  });
3895
- useEffect9(() => {
3919
+ useEffect10(() => {
3896
3920
  if (currentThreadId !== state.selectedThreadId) {
3897
3921
  setState((prev) => ({ ...prev, selectedThreadId: currentThreadId }));
3898
3922
  }
3899
3923
  }, [currentThreadId]);
3900
- const initialInputApplied = useRef7(false);
3901
- const initialInputConsumedRef = useRef7(false);
3902
- useEffect9(() => {
3924
+ const initialInputApplied = useRef6(false);
3925
+ const initialInputConsumedRef = useRef6(false);
3926
+ useEffect10(() => {
3903
3927
  if (initialInput && !initialInputApplied.current) {
3904
3928
  setInputValue(initialInput);
3905
3929
  initialInputApplied.current = true;
3906
3930
  }
3907
3931
  }, [initialInput]);
3908
- const scrollAreaRef = useRef7(null);
3909
- const stateRef = useRef7(state);
3910
- const inputValueRef = useRef7(inputValue);
3911
- const attachmentsRef = useRef7(attachments);
3912
- useEffect9(() => {
3932
+ const scrollAreaRef = useRef6(null);
3933
+ const stateRef = useRef6(state);
3934
+ const inputValueRef = useRef6(inputValue);
3935
+ const attachmentsRef = useRef6(attachments);
3936
+ useEffect10(() => {
3913
3937
  stateRef.current = state;
3914
3938
  }, [state]);
3915
- useEffect9(() => {
3939
+ useEffect10(() => {
3916
3940
  inputValueRef.current = inputValue;
3917
3941
  }, [inputValue]);
3918
- useEffect9(() => {
3942
+ useEffect10(() => {
3919
3943
  attachmentsRef.current = attachments;
3920
3944
  }, [attachments]);
3921
3945
  const [isCustomMounted, setIsCustomMounted] = useState8(false);
@@ -3938,7 +3962,7 @@ var ChatUI = ({
3938
3962
  []
3939
3963
  // No dependencies - uses refs for latest state
3940
3964
  );
3941
- useEffect9(() => {
3965
+ useEffect10(() => {
3942
3966
  const checkMobile = () => {
3943
3967
  setIsMobile(globalThis.innerWidth < 1024);
3944
3968
  };
@@ -3946,7 +3970,7 @@ var ChatUI = ({
3946
3970
  globalThis.addEventListener("resize", checkMobile);
3947
3971
  return () => globalThis.removeEventListener("resize", checkMobile);
3948
3972
  }, []);
3949
- useEffect9(() => {
3973
+ useEffect10(() => {
3950
3974
  if (!isMobile || !config.customComponent?.component) return;
3951
3975
  if (state.showSidebar) {
3952
3976
  setIsCustomMounted(true);
@@ -3957,8 +3981,8 @@ var ChatUI = ({
3957
3981
  return () => clearTimeout(t);
3958
3982
  }
3959
3983
  }, [state.showSidebar, isMobile, config.customComponent]);
3960
- const prevMessageCountRef = useRef7(0);
3961
- useEffect9(() => {
3984
+ const prevMessageCountRef = useRef6(0);
3985
+ useEffect10(() => {
3962
3986
  if (messages.length === 0) {
3963
3987
  prevMessageCountRef.current = 0;
3964
3988
  return;
@@ -3984,10 +4008,10 @@ var ChatUI = ({
3984
4008
  }
3985
4009
  });
3986
4010
  }, [messages, state.isAtBottom, virtualizer]);
3987
- useEffect9(() => {
4011
+ useEffect10(() => {
3988
4012
  virtualizer.measure();
3989
4013
  }, [expandedMessageIds, virtualizer]);
3990
- useEffect9(() => {
4014
+ useEffect10(() => {
3991
4015
  const validMessageIds = new Set(messages.map((message) => message.id));
3992
4016
  setExpandedMessageIds((prev) => {
3993
4017
  const activeIds = Object.keys(prev);
@@ -4369,7 +4393,7 @@ var ChatUI = ({
4369
4393
  };
4370
4394
 
4371
4395
  // src/components/chat/ThreadManager.tsx
4372
- import { useState as useState9, useRef as useRef8, useEffect as useEffect10 } from "react";
4396
+ import { useState as useState9, useRef as useRef7, useEffect as useEffect11 } from "react";
4373
4397
  import {
4374
4398
  Plus as Plus4,
4375
4399
  MessageSquare as MessageSquare2,
@@ -4388,8 +4412,8 @@ import { Fragment as Fragment6, jsx as jsx25, jsxs as jsxs15 } from "react/jsx-r
4388
4412
  var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onArchive }) => {
4389
4413
  const [isEditing, setIsEditing] = useState9(false);
4390
4414
  const [editTitle, setEditTitle] = useState9(thread.title);
4391
- const inputRef = useRef8(null);
4392
- useEffect10(() => {
4415
+ const inputRef = useRef7(null);
4416
+ useEffect11(() => {
4393
4417
  if (isEditing && inputRef.current) {
4394
4418
  inputRef.current.focus();
4395
4419
  inputRef.current.select();
@@ -4683,6 +4707,7 @@ export {
4683
4707
  chatUtils,
4684
4708
  cn,
4685
4709
  configUtils,
4710
+ createObjectUrlFromDataUrl,
4686
4711
  defaultChatConfig,
4687
4712
  featureFlags,
4688
4713
  formatDate,