@copilotz/chat-ui 0.8.5 → 0.8.6

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.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  useCallback as useCallback4,
4
4
  useEffect as useEffect10,
5
- useMemo as useMemo5,
5
+ useMemo as useMemo6,
6
6
  useRef as useRef6,
7
7
  useState as useState9
8
8
  } from "react";
@@ -79,6 +79,15 @@ var defaultChatConfig = {
79
79
  renameThread: "Rename",
80
80
  archiveThread: "Archive",
81
81
  unarchiveThread: "Unarchive",
82
+ manageTags: "Manage tags",
83
+ tags: "Tags",
84
+ addTag: "Add tag",
85
+ removeTag: "Remove tag",
86
+ tagNamePlaceholder: "Tag name",
87
+ untagged: "Untagged",
88
+ groupBy: "Group by",
89
+ groupByDate: "Date",
90
+ groupByTag: "Tag",
82
91
  today: "Today",
83
92
  yesterday: "Yesterday",
84
93
  createNewThread: "Create New Conversation",
@@ -112,6 +121,13 @@ var defaultChatConfig = {
112
121
  enableRegeneration: true,
113
122
  showActivity: true,
114
123
  showActivityDetails: true,
124
+ threadTags: {
125
+ enabled: false,
126
+ groupingEnabled: true,
127
+ defaultGroupBy: "date",
128
+ allowCreate: true,
129
+ allowDrag: true
130
+ },
115
131
  maxAttachments: 4,
116
132
  maxFileSize: 10 * 1024 * 1024
117
133
  // 10MB
@@ -144,7 +160,8 @@ var defaultChatConfig = {
144
160
  createProvider: void 0
145
161
  },
146
162
  customComponent: {},
147
- headerActions: null
163
+ headerActions: null,
164
+ headerMenuItems: []
148
165
  };
149
166
  function mergeConfig(_baseConfig, userConfig) {
150
167
  if (!userConfig) return defaultChatConfig;
@@ -178,7 +195,8 @@ function mergeConfig(_baseConfig, userConfig) {
178
195
  ...userConfig.agentSelector
179
196
  },
180
197
  customComponent: userConfig.customComponent || defaultChatConfig.customComponent,
181
- headerActions: userConfig.headerActions || defaultChatConfig.headerActions
198
+ headerActions: userConfig.headerActions || defaultChatConfig.headerActions,
199
+ headerMenuItems: userConfig.headerMenuItems || defaultChatConfig.headerMenuItems
182
200
  };
183
201
  }
184
202
 
@@ -1328,7 +1346,7 @@ var Message = memo2(({
1328
1346
  }, arePropsEqual);
1329
1347
 
1330
1348
  // src/components/chat/Sidebar.tsx
1331
- import { useEffect as useEffect7, useRef as useRef4, useState as useState5 } from "react";
1349
+ import { useEffect as useEffect7, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
1332
1350
 
1333
1351
  // src/components/ui/input.tsx
1334
1352
  import { jsx as jsx10 } from "react/jsx-runtime";
@@ -2214,10 +2232,12 @@ import {
2214
2232
  Bot,
2215
2233
  Edit2,
2216
2234
  Filter,
2235
+ Tag,
2217
2236
  MoreHorizontal,
2218
2237
  Plus,
2219
2238
  Search,
2220
- Trash2
2239
+ Trash2,
2240
+ X as X2
2221
2241
  } from "lucide-react";
2222
2242
 
2223
2243
  // src/components/chat/UserMenu.tsx
@@ -2434,6 +2454,37 @@ var ThreadInitialsIcon = ({ title }) => {
2434
2454
  const initials = title?.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase() || "?";
2435
2455
  return /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center justify-center rounded bg-muted text-[10px] font-medium", children: initials });
2436
2456
  };
2457
+ function slugTagName(name) {
2458
+ const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
2459
+ return slug || "tag";
2460
+ }
2461
+ function createThreadTag(name) {
2462
+ return {
2463
+ id: `tag_${slugTagName(name)}`,
2464
+ name: name.trim()
2465
+ };
2466
+ }
2467
+ function mergeThreadTags(tags, tag) {
2468
+ const nameKey = tag.name.trim().toLowerCase();
2469
+ if (!nameKey) return tags;
2470
+ if (tags.some(
2471
+ (existing) => existing.id === tag.id || existing.name.trim().toLowerCase() === nameKey
2472
+ )) {
2473
+ return tags;
2474
+ }
2475
+ return [...tags, tag];
2476
+ }
2477
+ function collectThreadTags(threads) {
2478
+ const tags = [];
2479
+ for (const thread of threads) {
2480
+ for (const tag of thread.tags ?? []) {
2481
+ if (!tags.some((existing) => existing.id === tag.id)) {
2482
+ tags.push(tag);
2483
+ }
2484
+ }
2485
+ }
2486
+ return tags.sort((a, b) => a.name.localeCompare(b.name));
2487
+ }
2437
2488
  var Sidebar2 = ({
2438
2489
  threads,
2439
2490
  currentThreadId,
@@ -2443,6 +2494,7 @@ var Sidebar2 = ({
2443
2494
  onRenameThread,
2444
2495
  onDeleteThread,
2445
2496
  onArchiveThread,
2497
+ onUpdateThreadTags,
2446
2498
  // User menu props
2447
2499
  user,
2448
2500
  userMenuCallbacks,
@@ -2457,7 +2509,20 @@ var Sidebar2 = ({
2457
2509
  const [deleteThreadId, setDeleteThreadId] = useState5(null);
2458
2510
  const [editingThreadId, setEditingThreadId] = useState5(null);
2459
2511
  const [editTitle, setEditTitle] = useState5("");
2512
+ const [tagDialogThreadId, setTagDialogThreadId] = useState5(
2513
+ null
2514
+ );
2515
+ const [newTagName, setNewTagName] = useState5("");
2516
+ const [draggingThreadId, setDraggingThreadId] = useState5(null);
2517
+ const [dragOverTagId, setDragOverTagId] = useState5(null);
2460
2518
  const inputRef = useRef4(null);
2519
+ const threadTagsConfig = config.features?.threadTags;
2520
+ const tagsEnabled = !!threadTagsConfig?.enabled;
2521
+ const canUpdateTags = tagsEnabled && !!onUpdateThreadTags;
2522
+ const canDragTags = canUpdateTags && threadTagsConfig?.allowDrag !== false;
2523
+ const [groupBy, setGroupBy] = useState5(
2524
+ threadTagsConfig?.defaultGroupBy === "tag" ? "tag" : "date"
2525
+ );
2461
2526
  const { setOpen } = useSidebar();
2462
2527
  useEffect7(() => {
2463
2528
  if (editingThreadId && inputRef.current) {
@@ -2467,38 +2532,98 @@ var Sidebar2 = ({
2467
2532
  }, [editingThreadId]);
2468
2533
  const filteredThreads = threads.filter((thread) => {
2469
2534
  const title = (thread.title ?? "").toString();
2470
- const matchesSearch = title.toLowerCase().includes(
2471
- searchQuery.toLowerCase()
2472
- );
2535
+ const matchesSearch = title.toLowerCase().includes(searchQuery.toLowerCase());
2473
2536
  const matchesArchiveFilter = showArchived || !thread.isArchived;
2474
2537
  return matchesSearch && matchesArchiveFilter;
2475
2538
  });
2476
- const groupedThreads = filteredThreads.reduce((groups, thread) => {
2477
- const date = new Date(thread.updatedAt);
2478
- const today = /* @__PURE__ */ new Date();
2479
- const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2480
- let groupKey;
2481
- if (date.toDateString() === today.toDateString()) {
2482
- groupKey = config.labels?.today || "Today";
2483
- } else if (date.toDateString() === yesterday.toDateString()) {
2484
- groupKey = config.labels?.yesterday || "Yesterday";
2485
- } else {
2486
- groupKey = date.toLocaleDateString("en-US", {
2487
- weekday: "long",
2488
- day: "2-digit",
2489
- month: "long"
2490
- });
2491
- }
2492
- if (!groups[groupKey]) {
2493
- groups[groupKey] = [];
2539
+ const allTags = useMemo3(() => collectThreadTags(threads), [threads]);
2540
+ const threadGroups = useMemo3(() => {
2541
+ if (tagsEnabled && groupBy === "tag") {
2542
+ const groups2 = allTags.map((tag) => ({
2543
+ key: tag.id,
2544
+ label: tag.name,
2545
+ tag,
2546
+ threads: filteredThreads.filter(
2547
+ (thread) => (thread.tags ?? []).some((threadTag) => threadTag.id === tag.id)
2548
+ )
2549
+ })).filter((group) => group.threads.length > 0);
2550
+ const untagged = filteredThreads.filter(
2551
+ (thread) => (thread.tags ?? []).length === 0
2552
+ );
2553
+ if (untagged.length > 0) {
2554
+ groups2.push({
2555
+ key: "untagged",
2556
+ label: config.labels?.untagged || "Untagged",
2557
+ threads: untagged
2558
+ });
2559
+ }
2560
+ return groups2;
2561
+ }
2562
+ const groups = [];
2563
+ const groupMap = /* @__PURE__ */ new Map();
2564
+ for (const thread of filteredThreads) {
2565
+ const date = new Date(thread.updatedAt);
2566
+ const today = /* @__PURE__ */ new Date();
2567
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2568
+ let groupKey;
2569
+ if (date.toDateString() === today.toDateString()) {
2570
+ groupKey = config.labels?.today || "Today";
2571
+ } else if (date.toDateString() === yesterday.toDateString()) {
2572
+ groupKey = config.labels?.yesterday || "Yesterday";
2573
+ } else {
2574
+ groupKey = date.toLocaleDateString("en-US", {
2575
+ weekday: "long",
2576
+ day: "2-digit",
2577
+ month: "long"
2578
+ });
2579
+ }
2580
+ const existing = groupMap.get(groupKey);
2581
+ if (existing) {
2582
+ existing.threads.push(thread);
2583
+ } else {
2584
+ const group = { key: groupKey, label: groupKey, threads: [thread] };
2585
+ groupMap.set(groupKey, group);
2586
+ groups.push(group);
2587
+ }
2494
2588
  }
2495
- groups[groupKey].push(thread);
2496
2589
  return groups;
2497
- }, {});
2590
+ }, [
2591
+ allTags,
2592
+ config.labels?.today,
2593
+ config.labels?.untagged,
2594
+ config.labels?.yesterday,
2595
+ filteredThreads,
2596
+ groupBy,
2597
+ tagsEnabled
2598
+ ]);
2599
+ const tagDialogThread = tagDialogThreadId ? threads.find((thread) => thread.id === tagDialogThreadId) ?? null : null;
2498
2600
  const handleDeleteThread = (threadId) => {
2499
2601
  onDeleteThread?.(threadId);
2500
2602
  setDeleteThreadId(null);
2501
2603
  };
2604
+ const updateThreadTags = (thread, tags) => {
2605
+ onUpdateThreadTags?.(thread.id, tags);
2606
+ };
2607
+ const addTagToThread = (thread, tag) => {
2608
+ updateThreadTags(thread, mergeThreadTags(thread.tags ?? [], tag));
2609
+ };
2610
+ const removeTagFromThread = (thread, tagId) => {
2611
+ updateThreadTags(
2612
+ thread,
2613
+ (thread.tags ?? []).filter((tag) => tag.id !== tagId)
2614
+ );
2615
+ };
2616
+ const handleCreateTag = () => {
2617
+ if (!tagDialogThread || !newTagName.trim()) return;
2618
+ addTagToThread(tagDialogThread, createThreadTag(newTagName));
2619
+ setNewTagName("");
2620
+ };
2621
+ const handleDropOnTag = (tag) => {
2622
+ const thread = draggingThreadId ? threads.find((candidate) => candidate.id === draggingThreadId) : null;
2623
+ if (thread) addTagToThread(thread, tag);
2624
+ setDraggingThreadId(null);
2625
+ setDragOverTagId(null);
2626
+ };
2502
2627
  const startEditing = (thread) => {
2503
2628
  setEditingThreadId(thread.id);
2504
2629
  setEditTitle(thread.title || "");
@@ -2586,92 +2711,175 @@ var Sidebar2 = ({
2586
2711
  ]
2587
2712
  }
2588
2713
  ) }),
2589
- Object.keys(groupedThreads).length === 0 ? /* @__PURE__ */ jsxs11("div", { className: "px-4 py-8 text-center text-muted-foreground group-data-[collapsible=icon]:hidden", children: [
2590
- /* @__PURE__ */ jsx18("div", { className: "mx-auto h-8 w-8 mb-2 flex items-center justify-center rounded-full bg-muted/50", children: /* @__PURE__ */ jsx18(Plus, { className: "h-4 w-4 opacity-50" }) }),
2591
- /* @__PURE__ */ jsx18("p", { className: "text-xs", children: searchQuery ? config.labels?.noThreadsFound || "No conversations found" : config.labels?.noThreadsYet || "No conversations yet" })
2592
- ] }) : Object.entries(groupedThreads).map(([group, groupThreads]) => /* @__PURE__ */ jsxs11(SidebarGroup, { className: "mt-2", children: [
2593
- /* @__PURE__ */ jsx18(SidebarGroupLabel, { className: "group-data-[collapsible=icon]:hidden", children: group }),
2594
- /* @__PURE__ */ jsx18(SidebarGroupContent, { children: /* @__PURE__ */ jsx18(SidebarMenu, { children: groupThreads.map((thread) => /* @__PURE__ */ jsxs11(SidebarMenuItem, { children: [
2595
- editingThreadId === thread.id ? /* @__PURE__ */ jsx18("div", { className: "flex items-center gap-1 px-2 py-1", children: /* @__PURE__ */ jsx18(
2596
- Input,
2714
+ tagsEnabled && threadTagsConfig?.groupingEnabled !== false && /* @__PURE__ */ jsx18("div", { className: "px-4 py-2 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between rounded-md border border-sidebar-border bg-sidebar-accent/40 p-1", children: [
2715
+ /* @__PURE__ */ jsx18("span", { className: "px-2 text-[11px] text-muted-foreground", children: config.labels?.groupBy || "Group by" }),
2716
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1", children: [
2717
+ /* @__PURE__ */ jsx18(
2718
+ Button,
2597
2719
  {
2598
- ref: inputRef,
2599
- value: editTitle,
2600
- onChange: (e) => setEditTitle(e.target.value),
2601
- onKeyDown: (e) => {
2602
- if (e.key === "Enter") {
2603
- saveEdit();
2604
- }
2605
- if (e.key === "Escape") {
2606
- cancelEdit();
2607
- }
2608
- },
2609
- onBlur: saveEdit,
2610
- className: "h-7 text-sm"
2720
+ variant: groupBy === "date" ? "secondary" : "ghost",
2721
+ size: "sm",
2722
+ onClick: () => setGroupBy("date"),
2723
+ className: "h-6 px-2 text-xs",
2724
+ children: config.labels?.groupByDate || "Date"
2611
2725
  }
2612
- ) }) : /* @__PURE__ */ jsxs11(
2613
- SidebarMenuButton,
2726
+ ),
2727
+ /* @__PURE__ */ jsx18(
2728
+ Button,
2614
2729
  {
2615
- isActive: currentThreadId === thread.id,
2616
- onClick: () => onSelectThread?.(thread.id),
2617
- tooltip: thread.title,
2618
- children: [
2619
- /* @__PURE__ */ jsx18(ThreadInitialsIcon, { title: thread.title || "?" }),
2620
- /* @__PURE__ */ jsx18("div", { className: "flex flex-col items-start gap-0.5 flex-1 min-w-0 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ jsx18("span", { className: "truncate w-full", children: thread.title || "New Chat" }) }),
2621
- thread.isArchived && /* @__PURE__ */ jsx18(Archive, { className: "ml-auto h-3 w-3 opacity-50 group-data-[collapsible=icon]:hidden" })
2622
- ]
2730
+ variant: groupBy === "tag" ? "secondary" : "ghost",
2731
+ size: "sm",
2732
+ onClick: () => setGroupBy("tag"),
2733
+ className: "h-6 px-2 text-xs",
2734
+ children: config.labels?.groupByTag || "Tag"
2623
2735
  }
2624
- ),
2625
- !editingThreadId && /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
2626
- /* @__PURE__ */ jsx18(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(SidebarMenuAction, { showOnHover: true, children: [
2627
- /* @__PURE__ */ jsx18(MoreHorizontal, {}),
2628
- /* @__PURE__ */ jsx18("span", { className: "sr-only", children: "More" })
2629
- ] }) }),
2736
+ )
2737
+ ] })
2738
+ ] }) }),
2739
+ threadGroups.length === 0 ? /* @__PURE__ */ jsxs11("div", { className: "px-4 py-8 text-center text-muted-foreground group-data-[collapsible=icon]:hidden", children: [
2740
+ /* @__PURE__ */ jsx18("div", { className: "mx-auto h-8 w-8 mb-2 flex items-center justify-center rounded-full bg-muted/50", children: /* @__PURE__ */ jsx18(Plus, { className: "h-4 w-4 opacity-50" }) }),
2741
+ /* @__PURE__ */ jsx18("p", { className: "text-xs", children: searchQuery ? config.labels?.noThreadsFound || "No conversations found" : config.labels?.noThreadsYet || "No conversations yet" })
2742
+ ] }) : threadGroups.map((group) => /* @__PURE__ */ jsxs11(
2743
+ SidebarGroup,
2744
+ {
2745
+ className: "mt-2",
2746
+ onDragOver: (event) => {
2747
+ if (!canDragTags || !group.tag) return;
2748
+ event.preventDefault();
2749
+ setDragOverTagId(group.tag.id);
2750
+ },
2751
+ onDragLeave: () => {
2752
+ if (dragOverTagId === group.tag?.id) setDragOverTagId(null);
2753
+ },
2754
+ onDrop: (event) => {
2755
+ if (!canDragTags || !group.tag) return;
2756
+ event.preventDefault();
2757
+ handleDropOnTag(group.tag);
2758
+ },
2759
+ children: [
2630
2760
  /* @__PURE__ */ jsxs11(
2631
- DropdownMenuContent,
2761
+ SidebarGroupLabel,
2632
2762
  {
2633
- className: "w-48",
2634
- side: "right",
2635
- align: "start",
2763
+ className: `group-data-[collapsible=icon]:hidden ${dragOverTagId === group.tag?.id ? "rounded-md bg-sidebar-accent text-sidebar-accent-foreground" : ""}`,
2636
2764
  children: [
2637
- /* @__PURE__ */ jsxs11(
2638
- DropdownMenuItem,
2639
- {
2640
- onClick: () => startEditing(thread),
2641
- children: [
2642
- /* @__PURE__ */ jsx18(Edit2, { className: "mr-2 h-4 w-4" }),
2643
- /* @__PURE__ */ jsx18("span", { children: config.labels?.renameThread || "Rename" })
2644
- ]
2645
- }
2646
- ),
2647
- /* @__PURE__ */ jsxs11(
2648
- DropdownMenuItem,
2649
- {
2650
- onClick: () => onArchiveThread?.(thread.id),
2651
- children: [
2652
- /* @__PURE__ */ jsx18(Archive, { className: "mr-2 h-4 w-4" }),
2653
- /* @__PURE__ */ jsx18("span", { children: thread.isArchived ? config.labels?.unarchiveThread || "Unarchive" : config.labels?.archiveThread || "Archive" })
2654
- ]
2655
- }
2656
- ),
2657
- /* @__PURE__ */ jsx18(DropdownMenuSeparator, {}),
2658
- /* @__PURE__ */ jsxs11(
2659
- DropdownMenuItem,
2660
- {
2661
- onClick: () => setDeleteThreadId(thread.id),
2662
- className: "text-destructive focus:text-destructive",
2663
- children: [
2664
- /* @__PURE__ */ jsx18(Trash2, { className: "mr-2 h-4 w-4" }),
2665
- /* @__PURE__ */ jsx18("span", { children: config.labels?.deleteThread || "Delete" })
2666
- ]
2667
- }
2668
- )
2765
+ group.tag && /* @__PURE__ */ jsx18(Tag, { className: "mr-1 h-3 w-3" }),
2766
+ group.label
2669
2767
  ]
2670
2768
  }
2671
- )
2672
- ] })
2673
- ] }, thread.id)) }) })
2674
- ] }, group))
2769
+ ),
2770
+ /* @__PURE__ */ jsx18(SidebarGroupContent, { children: /* @__PURE__ */ jsx18(SidebarMenu, { children: group.threads.map((thread) => /* @__PURE__ */ jsxs11(SidebarMenuItem, { children: [
2771
+ editingThreadId === thread.id ? /* @__PURE__ */ jsx18("div", { className: "flex items-center gap-1 px-2 py-1", children: /* @__PURE__ */ jsx18(
2772
+ Input,
2773
+ {
2774
+ ref: inputRef,
2775
+ value: editTitle,
2776
+ onChange: (e) => setEditTitle(e.target.value),
2777
+ onKeyDown: (e) => {
2778
+ if (e.key === "Enter") {
2779
+ saveEdit();
2780
+ }
2781
+ if (e.key === "Escape") {
2782
+ cancelEdit();
2783
+ }
2784
+ },
2785
+ onBlur: saveEdit,
2786
+ className: "h-7 text-sm"
2787
+ }
2788
+ ) }) : /* @__PURE__ */ jsxs11(
2789
+ SidebarMenuButton,
2790
+ {
2791
+ isActive: currentThreadId === thread.id,
2792
+ onClick: () => onSelectThread?.(thread.id),
2793
+ tooltip: thread.title,
2794
+ draggable: canDragTags,
2795
+ onDragStart: () => setDraggingThreadId(thread.id),
2796
+ onDragEnd: () => {
2797
+ setDraggingThreadId(null);
2798
+ setDragOverTagId(null);
2799
+ },
2800
+ children: [
2801
+ /* @__PURE__ */ jsx18(ThreadInitialsIcon, { title: thread.title || "?" }),
2802
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-start gap-0.5 flex-1 min-w-0 group-data-[collapsible=icon]:hidden", children: [
2803
+ /* @__PURE__ */ jsx18("span", { className: "truncate w-full", children: thread.title || "New Chat" }),
2804
+ tagsEnabled && (thread.tags ?? []).length > 0 && /* @__PURE__ */ jsx18("span", { className: "flex max-w-full flex-wrap gap-1", children: (thread.tags ?? []).slice(0, 2).map((tag) => /* @__PURE__ */ jsx18(
2805
+ "span",
2806
+ {
2807
+ className: "max-w-20 truncate rounded bg-sidebar-accent px-1.5 py-0.5 text-[10px] text-muted-foreground",
2808
+ children: tag.name
2809
+ },
2810
+ tag.id
2811
+ )) })
2812
+ ] }),
2813
+ thread.isArchived && /* @__PURE__ */ jsx18(Archive, { className: "ml-auto h-3 w-3 opacity-50 group-data-[collapsible=icon]:hidden" })
2814
+ ]
2815
+ }
2816
+ ),
2817
+ !editingThreadId && /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
2818
+ /* @__PURE__ */ jsx18(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(SidebarMenuAction, { showOnHover: true, children: [
2819
+ /* @__PURE__ */ jsx18(MoreHorizontal, {}),
2820
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: "More" })
2821
+ ] }) }),
2822
+ /* @__PURE__ */ jsxs11(
2823
+ DropdownMenuContent,
2824
+ {
2825
+ className: "w-48",
2826
+ side: "right",
2827
+ align: "start",
2828
+ children: [
2829
+ /* @__PURE__ */ jsxs11(
2830
+ DropdownMenuItem,
2831
+ {
2832
+ onClick: () => startEditing(thread),
2833
+ children: [
2834
+ /* @__PURE__ */ jsx18(Edit2, { className: "mr-2 h-4 w-4" }),
2835
+ /* @__PURE__ */ jsx18("span", { children: config.labels?.renameThread || "Rename" })
2836
+ ]
2837
+ }
2838
+ ),
2839
+ /* @__PURE__ */ jsxs11(
2840
+ DropdownMenuItem,
2841
+ {
2842
+ onClick: () => onArchiveThread?.(thread.id),
2843
+ children: [
2844
+ /* @__PURE__ */ jsx18(Archive, { className: "mr-2 h-4 w-4" }),
2845
+ /* @__PURE__ */ jsx18("span", { children: thread.isArchived ? config.labels?.unarchiveThread || "Unarchive" : config.labels?.archiveThread || "Archive" })
2846
+ ]
2847
+ }
2848
+ ),
2849
+ canUpdateTags && /* @__PURE__ */ jsxs11(
2850
+ DropdownMenuItem,
2851
+ {
2852
+ onClick: () => {
2853
+ setTagDialogThreadId(thread.id);
2854
+ setNewTagName("");
2855
+ },
2856
+ children: [
2857
+ /* @__PURE__ */ jsx18(Tag, { className: "mr-2 h-4 w-4" }),
2858
+ /* @__PURE__ */ jsx18("span", { children: config.labels?.manageTags || "Manage tags" })
2859
+ ]
2860
+ }
2861
+ ),
2862
+ /* @__PURE__ */ jsx18(DropdownMenuSeparator, {}),
2863
+ /* @__PURE__ */ jsxs11(
2864
+ DropdownMenuItem,
2865
+ {
2866
+ onClick: () => setDeleteThreadId(thread.id),
2867
+ className: "text-destructive focus:text-destructive",
2868
+ children: [
2869
+ /* @__PURE__ */ jsx18(Trash2, { className: "mr-2 h-4 w-4" }),
2870
+ /* @__PURE__ */ jsx18("span", { children: config.labels?.deleteThread || "Delete" })
2871
+ ]
2872
+ }
2873
+ )
2874
+ ]
2875
+ }
2876
+ )
2877
+ ] })
2878
+ ] }, thread.id)) }) })
2879
+ ]
2880
+ },
2881
+ group.key
2882
+ ))
2675
2883
  ] }),
2676
2884
  /* @__PURE__ */ jsx18(SidebarFooter, { children: /* @__PURE__ */ jsx18(
2677
2885
  UserMenu,
@@ -2686,6 +2894,94 @@ var Sidebar2 = ({
2686
2894
  }
2687
2895
  ) }),
2688
2896
  /* @__PURE__ */ jsx18(SidebarRail, {}),
2897
+ tagDialogThread && /* @__PURE__ */ jsx18(
2898
+ Dialog,
2899
+ {
2900
+ open: !!tagDialogThread,
2901
+ onOpenChange: (open) => {
2902
+ if (!open) {
2903
+ setTagDialogThreadId(null);
2904
+ setNewTagName("");
2905
+ }
2906
+ },
2907
+ children: /* @__PURE__ */ jsxs11(DialogContent, { children: [
2908
+ /* @__PURE__ */ jsxs11(DialogHeader, { children: [
2909
+ /* @__PURE__ */ jsx18(DialogTitle, { children: config.labels?.manageTags || "Manage tags" }),
2910
+ /* @__PURE__ */ jsx18(DialogDescription, { children: tagDialogThread.title || "New Chat" })
2911
+ ] }),
2912
+ /* @__PURE__ */ jsxs11("div", { className: "space-y-4", children: [
2913
+ /* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
2914
+ /* @__PURE__ */ jsx18("div", { className: "text-sm font-medium", children: config.labels?.tags || "Tags" }),
2915
+ (tagDialogThread.tags ?? []).length === 0 ? /* @__PURE__ */ jsx18("p", { className: "text-sm text-muted-foreground", children: config.labels?.untagged || "Untagged" }) : /* @__PURE__ */ jsx18("div", { className: "flex flex-wrap gap-2", children: (tagDialogThread.tags ?? []).map((tag) => /* @__PURE__ */ jsxs11(
2916
+ "span",
2917
+ {
2918
+ className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-sm",
2919
+ children: [
2920
+ tag.name,
2921
+ /* @__PURE__ */ jsx18(
2922
+ "button",
2923
+ {
2924
+ type: "button",
2925
+ className: "rounded hover:bg-background",
2926
+ onClick: () => removeTagFromThread(tagDialogThread, tag.id),
2927
+ "aria-label": config.labels?.removeTag || "Remove tag",
2928
+ children: /* @__PURE__ */ jsx18(X2, { className: "h-3 w-3" })
2929
+ }
2930
+ )
2931
+ ]
2932
+ },
2933
+ tag.id
2934
+ )) })
2935
+ ] }),
2936
+ allTags.length > 0 && /* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
2937
+ /* @__PURE__ */ jsx18("div", { className: "text-sm font-medium", children: config.labels?.addTag || "Add tag" }),
2938
+ /* @__PURE__ */ jsx18("div", { className: "flex flex-wrap gap-2", children: allTags.map((tag) => {
2939
+ const assigned = (tagDialogThread.tags ?? []).some(
2940
+ (threadTag) => threadTag.id === tag.id
2941
+ );
2942
+ return /* @__PURE__ */ jsx18(
2943
+ Button,
2944
+ {
2945
+ type: "button",
2946
+ variant: assigned ? "secondary" : "outline",
2947
+ size: "sm",
2948
+ disabled: assigned,
2949
+ onClick: () => addTagToThread(tagDialogThread, tag),
2950
+ children: tag.name
2951
+ },
2952
+ tag.id
2953
+ );
2954
+ }) })
2955
+ ] }),
2956
+ threadTagsConfig?.allowCreate !== false && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2", children: [
2957
+ /* @__PURE__ */ jsx18(
2958
+ Input,
2959
+ {
2960
+ value: newTagName,
2961
+ onChange: (event) => setNewTagName(event.target.value),
2962
+ placeholder: config.labels?.tagNamePlaceholder || "Tag name",
2963
+ onKeyDown: (event) => {
2964
+ if (event.key === "Enter") {
2965
+ event.preventDefault();
2966
+ handleCreateTag();
2967
+ }
2968
+ }
2969
+ }
2970
+ ),
2971
+ /* @__PURE__ */ jsx18(
2972
+ Button,
2973
+ {
2974
+ type: "button",
2975
+ onClick: handleCreateTag,
2976
+ disabled: !newTagName.trim(),
2977
+ children: config.labels?.addTag || "Add tag"
2978
+ }
2979
+ )
2980
+ ] })
2981
+ ] })
2982
+ ] })
2983
+ }
2984
+ ),
2689
2985
  deleteThreadId && /* @__PURE__ */ jsx18(
2690
2986
  AlertDialog,
2691
2987
  {
@@ -2771,8 +3067,8 @@ import {
2771
3067
  } from "lucide-react";
2772
3068
 
2773
3069
  // src/components/chat/AgentSelectors.tsx
2774
- import { memo as memo3, useMemo as useMemo3 } from "react";
2775
- import { Check as Check3, ChevronDown as ChevronDown2, Users, AtSign, X as X2 } from "lucide-react";
3070
+ import { memo as memo3, useMemo as useMemo4 } from "react";
3071
+ import { Check as Check3, ChevronDown as ChevronDown2, Users, AtSign, X as X3 } from "lucide-react";
2776
3072
  import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
2777
3073
  var ParticipantsSelector = memo3(({
2778
3074
  agents,
@@ -2782,8 +3078,8 @@ var ParticipantsSelector = memo3(({
2782
3078
  maxVisible = 3,
2783
3079
  disabled = false
2784
3080
  }) => {
2785
- const agentsWithColors = useMemo3(() => assignAgentColors(agents), [agents]);
2786
- const selectedAgents = useMemo3(
3081
+ const agentsWithColors = useMemo4(() => assignAgentColors(agents), [agents]);
3082
+ const selectedAgents = useMemo4(
2787
3083
  () => agentsWithColors.filter((a) => participantIds.includes(a.id)),
2788
3084
  [agentsWithColors, participantIds]
2789
3085
  );
@@ -2880,8 +3176,8 @@ var TargetAgentSelector = memo3(({
2880
3176
  placeholder = "Select agent",
2881
3177
  disabled = false
2882
3178
  }) => {
2883
- const agentsWithColors = useMemo3(() => assignAgentColors(agents), [agents]);
2884
- const selectedAgent = useMemo3(
3179
+ const agentsWithColors = useMemo4(() => assignAgentColors(agents), [agents]);
3180
+ const selectedAgent = useMemo4(
2885
3181
  () => agentsWithColors.find((a) => a.id === targetAgentId),
2886
3182
  [agentsWithColors, targetAgentId]
2887
3183
  );
@@ -2988,7 +3284,7 @@ var AgentBadge = memo3(({
2988
3284
  e.stopPropagation();
2989
3285
  onRemove();
2990
3286
  },
2991
- children: /* @__PURE__ */ jsx20(X2, { className: "h-3 w-3" })
3287
+ children: /* @__PURE__ */ jsx20(X3, { className: "h-3 w-3" })
2992
3288
  }
2993
3289
  )
2994
3290
  ]
@@ -3158,6 +3454,20 @@ var ChatHeader = ({
3158
3454
  ] }),
3159
3455
  /* @__PURE__ */ jsx21(DropdownMenuSeparator, {})
3160
3456
  ] }),
3457
+ config.headerMenuItems?.map((item) => /* @__PURE__ */ jsxs13(
3458
+ DropdownMenuItem,
3459
+ {
3460
+ onClick: item.onSelect,
3461
+ disabled: item.disabled,
3462
+ variant: item.variant,
3463
+ children: [
3464
+ item.icon ? /* @__PURE__ */ jsx21("span", { className: "h-4 w-4 mr-2 flex items-center justify-center", children: item.icon }) : null,
3465
+ item.label
3466
+ ]
3467
+ },
3468
+ item.id
3469
+ )),
3470
+ config.headerMenuItems && config.headerMenuItems.length > 0 && /* @__PURE__ */ jsx21(DropdownMenuSeparator, {}),
3161
3471
  onExportData && /* @__PURE__ */ jsxs13(DropdownMenuItem, { onClick: onExportData, children: [
3162
3472
  /* @__PURE__ */ jsx21(Download2, { className: "h-4 w-4 mr-2" }),
3163
3473
  config.labels?.exportData || "Export Data"
@@ -3200,7 +3510,7 @@ var ChatHeader = ({
3200
3510
  import React13, { useState as useState7, useRef as useRef5, useCallback as useCallback3, useEffect as useEffect9, memo as memo4 } from "react";
3201
3511
 
3202
3512
  // src/components/chat/UserContext.tsx
3203
- import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect8, useMemo as useMemo4, useState as useState6 } from "react";
3513
+ import { createContext as createContext2, useCallback as useCallback2, useContext as useContext2, useEffect as useEffect8, useMemo as useMemo5, useState as useState6 } from "react";
3204
3514
  import { jsx as jsx22 } from "react/jsx-runtime";
3205
3515
  var Ctx = createContext2(void 0);
3206
3516
  var ChatUserContextProvider = ({ children, initial }) => {
@@ -3223,7 +3533,7 @@ var ChatUserContextProvider = ({ children, initial }) => {
3223
3533
  return { ...prev, ...partial, updatedAt: Date.now() };
3224
3534
  });
3225
3535
  }, []);
3226
- const value = useMemo4(() => ({
3536
+ const value = useMemo5(() => ({
3227
3537
  context: ctx,
3228
3538
  setContext: setPartial,
3229
3539
  resetContext: () => setCtx({ updatedAt: Date.now() })
@@ -3590,7 +3900,7 @@ function Progress({
3590
3900
  }
3591
3901
 
3592
3902
  // src/components/chat/VoiceComposer.tsx
3593
- import { Keyboard, Loader2, Mic, Send, Square, Trash2 as Trash23, X as X3 } from "lucide-react";
3903
+ import { Keyboard, Loader2, Mic, Send, Square, Trash2 as Trash23, X as X4 } from "lucide-react";
3594
3904
  import { jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
3595
3905
  var formatDuration = (durationMs) => {
3596
3906
  const totalSeconds = Math.max(0, Math.floor(durationMs / 1e3));
@@ -3776,7 +4086,7 @@ var VoiceComposer = ({
3776
4086
  isAutoSendActive && autoSendDelayMs > 0 && /* @__PURE__ */ jsx24("div", { className: "mt-3 flex justify-center", children: /* @__PURE__ */ jsx24("div", { className: "inline-flex items-center rounded-full border bg-background px-3 py-1 text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) }) }),
3777
4087
  /* @__PURE__ */ jsxs14("div", { className: "mt-4 grid grid-cols-1 gap-2 sm:flex sm:items-center sm:justify-end", children: [
3778
4088
  isAutoSendActive && /* @__PURE__ */ jsxs14(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancelAutoSend, disabled, className: "w-full sm:w-auto", children: [
3779
- /* @__PURE__ */ jsx24(X3, { className: "h-4 w-4" }),
4089
+ /* @__PURE__ */ jsx24(X4, { className: "h-4 w-4" }),
3780
4090
  labels?.voiceCancel || "Cancel"
3781
4091
  ] }),
3782
4092
  /* @__PURE__ */ jsxs14(Button, { type: "button", size: "sm", onClick: onSendNow, disabled, className: "w-full sm:w-auto", children: [
@@ -3797,7 +4107,7 @@ import {
3797
4107
  Image as Image2,
3798
4108
  Video,
3799
4109
  FileText as FileText2,
3800
- X as X4,
4110
+ X as X5,
3801
4111
  Square as Square2,
3802
4112
  Play,
3803
4113
  Pause,
@@ -3881,7 +4191,7 @@ var FileUploadItem = memo4(function FileUploadItem2({ file, progress, onCancel }
3881
4191
  size: "icon",
3882
4192
  className: "h-6 w-6",
3883
4193
  onClick: onCancel,
3884
- children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
4194
+ children: /* @__PURE__ */ jsx25(X5, { className: "h-3 w-3" })
3885
4195
  }
3886
4196
  )
3887
4197
  ] }) }) });
@@ -3938,7 +4248,7 @@ var AttachmentPreview = memo4(function AttachmentPreview2({ attachment, onRemove
3938
4248
  size: "icon",
3939
4249
  className: "h-6 w-6",
3940
4250
  onClick: onRemove,
3941
- children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
4251
+ children: /* @__PURE__ */ jsx25(X5, { className: "h-3 w-3" })
3942
4252
  }
3943
4253
  ) })
3944
4254
  ] }),
@@ -3959,7 +4269,7 @@ var AttachmentPreview = memo4(function AttachmentPreview2({ attachment, onRemove
3959
4269
  size: "icon",
3960
4270
  className: "h-6 w-6",
3961
4271
  onClick: onRemove,
3962
- children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
4272
+ children: /* @__PURE__ */ jsx25(X5, { className: "h-3 w-3" })
3963
4273
  }
3964
4274
  ) }),
3965
4275
  /* @__PURE__ */ jsx25(Badge, { className: "absolute bottom-1 right-1 text-xs", children: formatDuration2(attachment.durationMs) })
@@ -3997,7 +4307,7 @@ var AttachmentPreview = memo4(function AttachmentPreview2({ attachment, onRemove
3997
4307
  size: "icon",
3998
4308
  className: "h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity",
3999
4309
  onClick: onRemove,
4000
- children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
4310
+ children: /* @__PURE__ */ jsx25(X5, { className: "h-3 w-3" })
4001
4311
  }
4002
4312
  )
4003
4313
  ] }),
@@ -4014,7 +4324,7 @@ var AttachmentPreview = memo4(function AttachmentPreview2({ attachment, onRemove
4014
4324
  size: "icon",
4015
4325
  className: "h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100",
4016
4326
  onClick: onRemove,
4017
- children: /* @__PURE__ */ jsx25(X4, { className: "h-3 w-3" })
4327
+ children: /* @__PURE__ */ jsx25(X5, { className: "h-3 w-3" })
4018
4328
  }
4019
4329
  )
4020
4330
  ] }),
@@ -4867,7 +5177,7 @@ import {
4867
5177
  Bot as Bot3,
4868
5178
  Pencil,
4869
5179
  Check as Check5,
4870
- X as X5
5180
+ X as X6
4871
5181
  } from "lucide-react";
4872
5182
  import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
4873
5183
  var getInitials2 = (name, email) => {
@@ -5167,7 +5477,7 @@ var UserProfile = ({
5167
5477
  className: "h-7 px-2",
5168
5478
  onClick: handleCancelEdit,
5169
5479
  children: [
5170
- /* @__PURE__ */ jsx27(X5, { className: "h-3.5 w-3.5 mr-1" }),
5480
+ /* @__PURE__ */ jsx27(X6, { className: "h-3.5 w-3.5 mr-1" }),
5171
5481
  "Cancelar"
5172
5482
  ]
5173
5483
  }
@@ -5377,7 +5687,7 @@ var ChatUI = ({
5377
5687
  initialInput,
5378
5688
  onInitialInputConsumed
5379
5689
  }) => {
5380
- const config = useMemo5(
5690
+ const config = useMemo6(
5381
5691
  () => mergeConfig(defaultChatConfig, userConfig),
5382
5692
  [userConfig]
5383
5693
  );
@@ -5440,9 +5750,10 @@ var ChatUI = ({
5440
5750
  }, [attachments]);
5441
5751
  const [isCustomMounted, setIsCustomMounted] = useState9(false);
5442
5752
  const [isCustomVisible, setIsCustomVisible] = useState9(false);
5443
- const groupedMessages = useMemo5(() => groupMessagesForRender(messages), [
5444
- messages
5445
- ]);
5753
+ const groupedMessages = useMemo6(
5754
+ () => groupMessagesForRender(messages),
5755
+ [messages]
5756
+ );
5446
5757
  const virtualizer = useVirtualizer({
5447
5758
  count: groupedMessages.length,
5448
5759
  getScrollElement: () => scrollAreaRef.current,
@@ -5568,7 +5879,8 @@ var ChatUI = ({
5568
5879
  });
5569
5880
  }, [groupedMessages, isLoadingOlderMessages, virtualizer]);
5570
5881
  const requestOlderMessages = useCallback4(() => {
5571
- if (!onLoadOlderMessages || !hasMoreMessagesBefore || isLoadingOlderMessages) return;
5882
+ if (!onLoadOlderMessages || !hasMoreMessagesBefore || isLoadingOlderMessages)
5883
+ return;
5572
5884
  const viewport = scrollAreaRef.current;
5573
5885
  prependSnapshotRef.current = viewport ? {
5574
5886
  scrollHeight: viewport.scrollHeight,
@@ -5600,55 +5912,68 @@ var ChatUI = ({
5600
5912
  return next;
5601
5913
  });
5602
5914
  }, [groupedMessages]);
5603
- const handleScroll = useCallback4((e) => {
5604
- const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
5605
- const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
5606
- const isNearTop = scrollTop < 120;
5607
- if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
5608
- requestOlderMessages();
5609
- }
5610
- setState((prev) => {
5611
- if (prev.isAtBottom === isAtBottom) return prev;
5612
- return { ...prev, isAtBottom };
5613
- });
5614
- }, [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]);
5615
- const handleSendMessage = useCallback4((content, messageAttachments = []) => {
5616
- if (!content.trim() && messageAttachments.length === 0) return;
5617
- callbacks.onSendMessage?.(
5618
- content,
5619
- messageAttachments,
5620
- createStateCallback()
5621
- );
5622
- if (initialInputApplied.current && !initialInputConsumedRef.current) {
5623
- initialInputConsumedRef.current = true;
5624
- onInitialInputConsumed?.();
5625
- }
5626
- setInputValue("");
5627
- setAttachments([]);
5628
- }, [callbacks, createStateCallback, onInitialInputConsumed]);
5629
- const handleMessageAction = useCallback4((event) => {
5630
- const { action, messageId, content } = event;
5631
- switch (action) {
5632
- case "copy":
5633
- callbacks.onCopyMessage?.(
5634
- messageId,
5635
- content || "",
5636
- createStateCallback()
5637
- );
5638
- break;
5639
- case "edit":
5640
- if (content) {
5641
- callbacks.onEditMessage?.(messageId, content, createStateCallback());
5642
- }
5643
- break;
5644
- case "regenerate":
5645
- callbacks.onRegenerateMessage?.(messageId, createStateCallback());
5646
- break;
5647
- case "delete":
5648
- callbacks.onDeleteMessage?.(messageId, createStateCallback());
5649
- break;
5650
- }
5651
- }, [callbacks, createStateCallback]);
5915
+ const handleScroll = useCallback4(
5916
+ (e) => {
5917
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
5918
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
5919
+ const isNearTop = scrollTop < 120;
5920
+ if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
5921
+ requestOlderMessages();
5922
+ }
5923
+ setState((prev) => {
5924
+ if (prev.isAtBottom === isAtBottom) return prev;
5925
+ return { ...prev, isAtBottom };
5926
+ });
5927
+ },
5928
+ [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]
5929
+ );
5930
+ const handleSendMessage = useCallback4(
5931
+ (content, messageAttachments = []) => {
5932
+ if (!content.trim() && messageAttachments.length === 0) return;
5933
+ callbacks.onSendMessage?.(
5934
+ content,
5935
+ messageAttachments,
5936
+ createStateCallback()
5937
+ );
5938
+ if (initialInputApplied.current && !initialInputConsumedRef.current) {
5939
+ initialInputConsumedRef.current = true;
5940
+ onInitialInputConsumed?.();
5941
+ }
5942
+ setInputValue("");
5943
+ setAttachments([]);
5944
+ },
5945
+ [callbacks, createStateCallback, onInitialInputConsumed]
5946
+ );
5947
+ const handleMessageAction = useCallback4(
5948
+ (event) => {
5949
+ const { action, messageId, content } = event;
5950
+ switch (action) {
5951
+ case "copy":
5952
+ callbacks.onCopyMessage?.(
5953
+ messageId,
5954
+ content || "",
5955
+ createStateCallback()
5956
+ );
5957
+ break;
5958
+ case "edit":
5959
+ if (content) {
5960
+ callbacks.onEditMessage?.(
5961
+ messageId,
5962
+ content,
5963
+ createStateCallback()
5964
+ );
5965
+ }
5966
+ break;
5967
+ case "regenerate":
5968
+ callbacks.onRegenerateMessage?.(messageId, createStateCallback());
5969
+ break;
5970
+ case "delete":
5971
+ callbacks.onDeleteMessage?.(messageId, createStateCallback());
5972
+ break;
5973
+ }
5974
+ },
5975
+ [callbacks, createStateCallback]
5976
+ );
5652
5977
  const handleToggleMessageExpansion = useCallback4((messageId) => {
5653
5978
  setExpandedMessageIds((prev) => {
5654
5979
  if (prev[messageId]) {
@@ -5662,51 +5987,79 @@ var ChatUI = ({
5662
5987
  };
5663
5988
  });
5664
5989
  }, []);
5665
- const handleCreateThread = useCallback4((title) => {
5666
- callbacks.onCreateThread?.(title, createStateCallback());
5667
- }, [callbacks, createStateCallback]);
5668
- const handleSelectThread = useCallback4((threadId) => {
5669
- callbacks.onSelectThread?.(threadId, createStateCallback());
5670
- }, [callbacks, createStateCallback]);
5990
+ const handleCreateThread = useCallback4(
5991
+ (title) => {
5992
+ callbacks.onCreateThread?.(title, createStateCallback());
5993
+ },
5994
+ [callbacks, createStateCallback]
5995
+ );
5996
+ const handleSelectThread = useCallback4(
5997
+ (threadId) => {
5998
+ callbacks.onSelectThread?.(threadId, createStateCallback());
5999
+ },
6000
+ [callbacks, createStateCallback]
6001
+ );
5671
6002
  const handleRenameThread = useCallback4(
5672
6003
  (threadId, newTitle) => {
5673
6004
  callbacks.onRenameThread?.(threadId, newTitle, createStateCallback());
5674
6005
  },
5675
6006
  [callbacks, createStateCallback]
5676
6007
  );
5677
- const handleDeleteThread = useCallback4((threadId) => {
5678
- callbacks.onDeleteThread?.(threadId, createStateCallback());
5679
- }, [callbacks, createStateCallback]);
5680
- const handleArchiveThread = useCallback4((threadId) => {
5681
- callbacks.onArchiveThread?.(threadId, createStateCallback());
5682
- }, [callbacks, createStateCallback]);
6008
+ const handleDeleteThread = useCallback4(
6009
+ (threadId) => {
6010
+ callbacks.onDeleteThread?.(threadId, createStateCallback());
6011
+ },
6012
+ [callbacks, createStateCallback]
6013
+ );
6014
+ const handleArchiveThread = useCallback4(
6015
+ (threadId) => {
6016
+ callbacks.onArchiveThread?.(threadId, createStateCallback());
6017
+ },
6018
+ [callbacks, createStateCallback]
6019
+ );
6020
+ const handleUpdateThreadTags = useCallback4(
6021
+ (threadId, tags) => {
6022
+ callbacks.onUpdateThreadTags?.(
6023
+ threadId,
6024
+ tags ?? [],
6025
+ createStateCallback()
6026
+ );
6027
+ },
6028
+ [callbacks, createStateCallback]
6029
+ );
5683
6030
  const closeSidebar = useCallback4(() => {
5684
6031
  setState((prev) => ({ ...prev, showSidebar: false }));
5685
6032
  }, []);
5686
6033
  const handleCustomComponentToggle = useCallback4(() => {
5687
6034
  setState((prev) => ({ ...prev, showSidebar: !prev.showSidebar }));
5688
6035
  }, []);
5689
- const sidebarUser = useMemo5(() => user ? {
5690
- id: user.id,
5691
- name: user.name,
5692
- email: user.email,
5693
- avatar: user.avatar
5694
- } : null, [user?.id, user?.name, user?.email, user?.avatar]);
6036
+ const sidebarUser = useMemo6(
6037
+ () => user ? {
6038
+ id: user.id,
6039
+ name: user.name,
6040
+ email: user.email,
6041
+ avatar: user.avatar
6042
+ } : null,
6043
+ [user?.id, user?.name, user?.email, user?.avatar]
6044
+ );
5695
6045
  const handleViewProfile = useCallback4(() => {
5696
6046
  setIsUserProfileOpen(true);
5697
6047
  callbacks.onViewProfile?.();
5698
6048
  }, [callbacks.onViewProfile]);
5699
- const sidebarUserMenuCallbacks = useMemo5(() => ({
5700
- onViewProfile: handleViewProfile,
5701
- onOpenSettings: callbacks.onOpenSettings,
5702
- onThemeChange: callbacks.onThemeChange,
5703
- onLogout: callbacks.onLogout
5704
- }), [
5705
- handleViewProfile,
5706
- callbacks.onOpenSettings,
5707
- callbacks.onThemeChange,
5708
- callbacks.onLogout
5709
- ]);
6049
+ const sidebarUserMenuCallbacks = useMemo6(
6050
+ () => ({
6051
+ onViewProfile: handleViewProfile,
6052
+ onOpenSettings: callbacks.onOpenSettings,
6053
+ onThemeChange: callbacks.onThemeChange,
6054
+ onLogout: callbacks.onLogout
6055
+ }),
6056
+ [
6057
+ handleViewProfile,
6058
+ callbacks.onOpenSettings,
6059
+ callbacks.onThemeChange,
6060
+ callbacks.onLogout
6061
+ ]
6062
+ );
5710
6063
  const renderCustomComponent = useCallback4(() => {
5711
6064
  const component = config?.customComponent?.component;
5712
6065
  if (!component) return null;
@@ -5787,55 +6140,58 @@ var ChatUI = ({
5787
6140
  );
5788
6141
  }) });
5789
6142
  const isMultiAgentMode = config.agentSelector?.mode === "multi";
5790
- const messageProps = useMemo5(() => ({
5791
- userAvatar: user?.avatar,
5792
- userName: user?.name,
5793
- assistantAvatar: assistant?.avatar,
5794
- assistantName: assistant?.name,
5795
- showTimestamp: config.ui.showTimestamps,
5796
- showAvatar: config.ui.showAvatars,
5797
- enableCopy: config.features.enableMessageCopy,
5798
- enableEdit: config.features.enableMessageEditing,
5799
- enableRegenerate: config.features.enableRegeneration,
5800
- showActivity: config.features.showActivity,
5801
- showActivityDetails: config.features.showActivityDetails,
5802
- compactMode: config.ui.compactMode,
5803
- onAction: handleMessageAction,
5804
- labels: config.labels,
5805
- showMoreLabel: config.labels.showMoreMessage,
5806
- showLessLabel: config.labels.showLessMessage,
5807
- collapseLongMessages: config.ui.collapseLongMessages,
5808
- collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
5809
- longMessagePreviewChars: config.ui.longMessagePreviewChars,
5810
- longMessageChunkChars: config.ui.longMessageChunkChars,
5811
- renderUserMarkdown: config.ui.renderUserMarkdown,
5812
- markdown: config.markdown,
5813
- onToggleExpanded: handleToggleMessageExpansion
5814
- }), [
5815
- user?.avatar,
5816
- user?.name,
5817
- assistant?.avatar,
5818
- assistant?.name,
5819
- config.ui.showTimestamps,
5820
- config.ui.showAvatars,
5821
- config.ui.compactMode,
5822
- config.features.enableMessageCopy,
5823
- config.features.enableMessageEditing,
5824
- config.features.enableRegeneration,
5825
- config.features.showActivity,
5826
- config.features.showActivityDetails,
5827
- config.labels,
5828
- config.labels.showMoreMessage,
5829
- config.labels.showLessMessage,
5830
- config.ui.collapseLongMessages,
5831
- config.ui.collapseLongMessagesForUserOnly,
5832
- config.ui.longMessagePreviewChars,
5833
- config.ui.longMessageChunkChars,
5834
- config.ui.renderUserMarkdown,
5835
- config.markdown,
5836
- handleMessageAction,
5837
- handleToggleMessageExpansion
5838
- ]);
6143
+ const messageProps = useMemo6(
6144
+ () => ({
6145
+ userAvatar: user?.avatar,
6146
+ userName: user?.name,
6147
+ assistantAvatar: assistant?.avatar,
6148
+ assistantName: assistant?.name,
6149
+ showTimestamp: config.ui.showTimestamps,
6150
+ showAvatar: config.ui.showAvatars,
6151
+ enableCopy: config.features.enableMessageCopy,
6152
+ enableEdit: config.features.enableMessageEditing,
6153
+ enableRegenerate: config.features.enableRegeneration,
6154
+ showActivity: config.features.showActivity,
6155
+ showActivityDetails: config.features.showActivityDetails,
6156
+ compactMode: config.ui.compactMode,
6157
+ onAction: handleMessageAction,
6158
+ labels: config.labels,
6159
+ showMoreLabel: config.labels.showMoreMessage,
6160
+ showLessLabel: config.labels.showLessMessage,
6161
+ collapseLongMessages: config.ui.collapseLongMessages,
6162
+ collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
6163
+ longMessagePreviewChars: config.ui.longMessagePreviewChars,
6164
+ longMessageChunkChars: config.ui.longMessageChunkChars,
6165
+ renderUserMarkdown: config.ui.renderUserMarkdown,
6166
+ markdown: config.markdown,
6167
+ onToggleExpanded: handleToggleMessageExpansion
6168
+ }),
6169
+ [
6170
+ user?.avatar,
6171
+ user?.name,
6172
+ assistant?.avatar,
6173
+ assistant?.name,
6174
+ config.ui.showTimestamps,
6175
+ config.ui.showAvatars,
6176
+ config.ui.compactMode,
6177
+ config.features.enableMessageCopy,
6178
+ config.features.enableMessageEditing,
6179
+ config.features.enableRegeneration,
6180
+ config.features.showActivity,
6181
+ config.features.showActivityDetails,
6182
+ config.labels,
6183
+ config.labels.showMoreMessage,
6184
+ config.labels.showLessMessage,
6185
+ config.ui.collapseLongMessages,
6186
+ config.ui.collapseLongMessagesForUserOnly,
6187
+ config.ui.longMessagePreviewChars,
6188
+ config.ui.longMessageChunkChars,
6189
+ config.ui.renderUserMarkdown,
6190
+ config.markdown,
6191
+ handleMessageAction,
6192
+ handleToggleMessageExpansion
6193
+ ]
6194
+ );
5839
6195
  const shouldShowAgentSelector = Boolean(
5840
6196
  config.agentSelector?.enabled && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1) && (isMultiAgentMode ? onParticipantsChange : onSelectAgent)
5841
6197
  );
@@ -5855,6 +6211,7 @@ var ChatUI = ({
5855
6211
  onRenameThread: handleRenameThread,
5856
6212
  onDeleteThread: handleDeleteThread,
5857
6213
  onArchiveThread: handleArchiveThread,
6214
+ onUpdateThreadTags: handleUpdateThreadTags,
5858
6215
  user: sidebarUser,
5859
6216
  userMenuCallbacks: sidebarUserMenuCallbacks,
5860
6217
  currentTheme: config.ui.theme === "auto" ? "system" : config.ui.theme,
@@ -5868,9 +6225,7 @@ var ChatUI = ({
5868
6225
  ChatHeader,
5869
6226
  {
5870
6227
  config,
5871
- currentThreadTitle: threads.find(
5872
- (t) => t.id === state.selectedThreadId
5873
- )?.title,
6228
+ currentThreadTitle: threads.find((t) => t.id === state.selectedThreadId)?.title,
5874
6229
  isMobile,
5875
6230
  onCustomComponentToggle: handleCustomComponentToggle,
5876
6231
  onNewThread: handleCreateThread,