@copilotz/chat-ui 0.8.4 → 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.cjs CHANGED
@@ -118,6 +118,15 @@ var defaultChatConfig = {
118
118
  renameThread: "Rename",
119
119
  archiveThread: "Archive",
120
120
  unarchiveThread: "Unarchive",
121
+ manageTags: "Manage tags",
122
+ tags: "Tags",
123
+ addTag: "Add tag",
124
+ removeTag: "Remove tag",
125
+ tagNamePlaceholder: "Tag name",
126
+ untagged: "Untagged",
127
+ groupBy: "Group by",
128
+ groupByDate: "Date",
129
+ groupByTag: "Tag",
121
130
  today: "Today",
122
131
  yesterday: "Yesterday",
123
132
  createNewThread: "Create New Conversation",
@@ -151,6 +160,13 @@ var defaultChatConfig = {
151
160
  enableRegeneration: true,
152
161
  showActivity: true,
153
162
  showActivityDetails: true,
163
+ threadTags: {
164
+ enabled: false,
165
+ groupingEnabled: true,
166
+ defaultGroupBy: "date",
167
+ allowCreate: true,
168
+ allowDrag: true
169
+ },
154
170
  maxAttachments: 4,
155
171
  maxFileSize: 10 * 1024 * 1024
156
172
  // 10MB
@@ -183,7 +199,8 @@ var defaultChatConfig = {
183
199
  createProvider: void 0
184
200
  },
185
201
  customComponent: {},
186
- headerActions: null
202
+ headerActions: null,
203
+ headerMenuItems: []
187
204
  };
188
205
  function mergeConfig(_baseConfig, userConfig) {
189
206
  if (!userConfig) return defaultChatConfig;
@@ -217,7 +234,8 @@ function mergeConfig(_baseConfig, userConfig) {
217
234
  ...userConfig.agentSelector
218
235
  },
219
236
  customComponent: userConfig.customComponent || defaultChatConfig.customComponent,
220
- headerActions: userConfig.headerActions || defaultChatConfig.headerActions
237
+ headerActions: userConfig.headerActions || defaultChatConfig.headerActions,
238
+ headerMenuItems: userConfig.headerMenuItems || defaultChatConfig.headerMenuItems
221
239
  };
222
240
  }
223
241
 
@@ -2442,6 +2460,37 @@ var ThreadInitialsIcon = ({ title }) => {
2442
2460
  const initials = title?.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase() || "?";
2443
2461
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center justify-center rounded bg-muted text-[10px] font-medium", children: initials });
2444
2462
  };
2463
+ function slugTagName(name) {
2464
+ const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
2465
+ return slug || "tag";
2466
+ }
2467
+ function createThreadTag(name) {
2468
+ return {
2469
+ id: `tag_${slugTagName(name)}`,
2470
+ name: name.trim()
2471
+ };
2472
+ }
2473
+ function mergeThreadTags(tags, tag) {
2474
+ const nameKey = tag.name.trim().toLowerCase();
2475
+ if (!nameKey) return tags;
2476
+ if (tags.some(
2477
+ (existing) => existing.id === tag.id || existing.name.trim().toLowerCase() === nameKey
2478
+ )) {
2479
+ return tags;
2480
+ }
2481
+ return [...tags, tag];
2482
+ }
2483
+ function collectThreadTags(threads) {
2484
+ const tags = [];
2485
+ for (const thread of threads) {
2486
+ for (const tag of thread.tags ?? []) {
2487
+ if (!tags.some((existing) => existing.id === tag.id)) {
2488
+ tags.push(tag);
2489
+ }
2490
+ }
2491
+ }
2492
+ return tags.sort((a, b) => a.name.localeCompare(b.name));
2493
+ }
2445
2494
  var Sidebar2 = ({
2446
2495
  threads,
2447
2496
  currentThreadId,
@@ -2451,6 +2500,7 @@ var Sidebar2 = ({
2451
2500
  onRenameThread,
2452
2501
  onDeleteThread,
2453
2502
  onArchiveThread,
2503
+ onUpdateThreadTags,
2454
2504
  // User menu props
2455
2505
  user,
2456
2506
  userMenuCallbacks,
@@ -2465,7 +2515,20 @@ var Sidebar2 = ({
2465
2515
  const [deleteThreadId, setDeleteThreadId] = (0, import_react4.useState)(null);
2466
2516
  const [editingThreadId, setEditingThreadId] = (0, import_react4.useState)(null);
2467
2517
  const [editTitle, setEditTitle] = (0, import_react4.useState)("");
2518
+ const [tagDialogThreadId, setTagDialogThreadId] = (0, import_react4.useState)(
2519
+ null
2520
+ );
2521
+ const [newTagName, setNewTagName] = (0, import_react4.useState)("");
2522
+ const [draggingThreadId, setDraggingThreadId] = (0, import_react4.useState)(null);
2523
+ const [dragOverTagId, setDragOverTagId] = (0, import_react4.useState)(null);
2468
2524
  const inputRef = (0, import_react4.useRef)(null);
2525
+ const threadTagsConfig = config.features?.threadTags;
2526
+ const tagsEnabled = !!threadTagsConfig?.enabled;
2527
+ const canUpdateTags = tagsEnabled && !!onUpdateThreadTags;
2528
+ const canDragTags = canUpdateTags && threadTagsConfig?.allowDrag !== false;
2529
+ const [groupBy, setGroupBy] = (0, import_react4.useState)(
2530
+ threadTagsConfig?.defaultGroupBy === "tag" ? "tag" : "date"
2531
+ );
2469
2532
  const { setOpen } = useSidebar();
2470
2533
  (0, import_react4.useEffect)(() => {
2471
2534
  if (editingThreadId && inputRef.current) {
@@ -2475,38 +2538,98 @@ var Sidebar2 = ({
2475
2538
  }, [editingThreadId]);
2476
2539
  const filteredThreads = threads.filter((thread) => {
2477
2540
  const title = (thread.title ?? "").toString();
2478
- const matchesSearch = title.toLowerCase().includes(
2479
- searchQuery.toLowerCase()
2480
- );
2541
+ const matchesSearch = title.toLowerCase().includes(searchQuery.toLowerCase());
2481
2542
  const matchesArchiveFilter = showArchived || !thread.isArchived;
2482
2543
  return matchesSearch && matchesArchiveFilter;
2483
2544
  });
2484
- const groupedThreads = filteredThreads.reduce((groups, thread) => {
2485
- const date = new Date(thread.updatedAt);
2486
- const today = /* @__PURE__ */ new Date();
2487
- const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2488
- let groupKey;
2489
- if (date.toDateString() === today.toDateString()) {
2490
- groupKey = config.labels?.today || "Today";
2491
- } else if (date.toDateString() === yesterday.toDateString()) {
2492
- groupKey = config.labels?.yesterday || "Yesterday";
2493
- } else {
2494
- groupKey = date.toLocaleDateString("en-US", {
2495
- weekday: "long",
2496
- day: "2-digit",
2497
- month: "long"
2498
- });
2499
- }
2500
- if (!groups[groupKey]) {
2501
- groups[groupKey] = [];
2545
+ const allTags = (0, import_react4.useMemo)(() => collectThreadTags(threads), [threads]);
2546
+ const threadGroups = (0, import_react4.useMemo)(() => {
2547
+ if (tagsEnabled && groupBy === "tag") {
2548
+ const groups2 = allTags.map((tag) => ({
2549
+ key: tag.id,
2550
+ label: tag.name,
2551
+ tag,
2552
+ threads: filteredThreads.filter(
2553
+ (thread) => (thread.tags ?? []).some((threadTag) => threadTag.id === tag.id)
2554
+ )
2555
+ })).filter((group) => group.threads.length > 0);
2556
+ const untagged = filteredThreads.filter(
2557
+ (thread) => (thread.tags ?? []).length === 0
2558
+ );
2559
+ if (untagged.length > 0) {
2560
+ groups2.push({
2561
+ key: "untagged",
2562
+ label: config.labels?.untagged || "Untagged",
2563
+ threads: untagged
2564
+ });
2565
+ }
2566
+ return groups2;
2567
+ }
2568
+ const groups = [];
2569
+ const groupMap = /* @__PURE__ */ new Map();
2570
+ for (const thread of filteredThreads) {
2571
+ const date = new Date(thread.updatedAt);
2572
+ const today = /* @__PURE__ */ new Date();
2573
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2574
+ let groupKey;
2575
+ if (date.toDateString() === today.toDateString()) {
2576
+ groupKey = config.labels?.today || "Today";
2577
+ } else if (date.toDateString() === yesterday.toDateString()) {
2578
+ groupKey = config.labels?.yesterday || "Yesterday";
2579
+ } else {
2580
+ groupKey = date.toLocaleDateString("en-US", {
2581
+ weekday: "long",
2582
+ day: "2-digit",
2583
+ month: "long"
2584
+ });
2585
+ }
2586
+ const existing = groupMap.get(groupKey);
2587
+ if (existing) {
2588
+ existing.threads.push(thread);
2589
+ } else {
2590
+ const group = { key: groupKey, label: groupKey, threads: [thread] };
2591
+ groupMap.set(groupKey, group);
2592
+ groups.push(group);
2593
+ }
2502
2594
  }
2503
- groups[groupKey].push(thread);
2504
2595
  return groups;
2505
- }, {});
2596
+ }, [
2597
+ allTags,
2598
+ config.labels?.today,
2599
+ config.labels?.untagged,
2600
+ config.labels?.yesterday,
2601
+ filteredThreads,
2602
+ groupBy,
2603
+ tagsEnabled
2604
+ ]);
2605
+ const tagDialogThread = tagDialogThreadId ? threads.find((thread) => thread.id === tagDialogThreadId) ?? null : null;
2506
2606
  const handleDeleteThread = (threadId) => {
2507
2607
  onDeleteThread?.(threadId);
2508
2608
  setDeleteThreadId(null);
2509
2609
  };
2610
+ const updateThreadTags = (thread, tags) => {
2611
+ onUpdateThreadTags?.(thread.id, tags);
2612
+ };
2613
+ const addTagToThread = (thread, tag) => {
2614
+ updateThreadTags(thread, mergeThreadTags(thread.tags ?? [], tag));
2615
+ };
2616
+ const removeTagFromThread = (thread, tagId) => {
2617
+ updateThreadTags(
2618
+ thread,
2619
+ (thread.tags ?? []).filter((tag) => tag.id !== tagId)
2620
+ );
2621
+ };
2622
+ const handleCreateTag = () => {
2623
+ if (!tagDialogThread || !newTagName.trim()) return;
2624
+ addTagToThread(tagDialogThread, createThreadTag(newTagName));
2625
+ setNewTagName("");
2626
+ };
2627
+ const handleDropOnTag = (tag) => {
2628
+ const thread = draggingThreadId ? threads.find((candidate) => candidate.id === draggingThreadId) : null;
2629
+ if (thread) addTagToThread(thread, tag);
2630
+ setDraggingThreadId(null);
2631
+ setDragOverTagId(null);
2632
+ };
2510
2633
  const startEditing = (thread) => {
2511
2634
  setEditingThreadId(thread.id);
2512
2635
  setEditTitle(thread.title || "");
@@ -2594,92 +2717,175 @@ var Sidebar2 = ({
2594
2717
  ]
2595
2718
  }
2596
2719
  ) }),
2597
- Object.keys(groupedThreads).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "px-4 py-8 text-center text-muted-foreground group-data-[collapsible=icon]:hidden", children: [
2598
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mx-auto h-8 w-8 mb-2 flex items-center justify-center rounded-full bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Plus, { className: "h-4 w-4 opacity-50" }) }),
2599
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-xs", children: searchQuery ? config.labels?.noThreadsFound || "No conversations found" : config.labels?.noThreadsYet || "No conversations yet" })
2600
- ] }) : Object.entries(groupedThreads).map(([group, groupThreads]) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(SidebarGroup, { className: "mt-2", children: [
2601
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarGroupLabel, { className: "group-data-[collapsible=icon]:hidden", children: group }),
2602
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarGroupContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarMenu, { children: groupThreads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(SidebarMenuItem, { children: [
2603
- editingThreadId === thread.id ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center gap-1 px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2604
- Input,
2720
+ tagsEnabled && threadTagsConfig?.groupingEnabled !== false && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "px-4 py-2 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center justify-between rounded-md border border-sidebar-border bg-sidebar-accent/40 p-1", children: [
2721
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "px-2 text-[11px] text-muted-foreground", children: config.labels?.groupBy || "Group by" }),
2722
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex gap-1", children: [
2723
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2724
+ Button,
2605
2725
  {
2606
- ref: inputRef,
2607
- value: editTitle,
2608
- onChange: (e) => setEditTitle(e.target.value),
2609
- onKeyDown: (e) => {
2610
- if (e.key === "Enter") {
2611
- saveEdit();
2612
- }
2613
- if (e.key === "Escape") {
2614
- cancelEdit();
2615
- }
2616
- },
2617
- onBlur: saveEdit,
2618
- className: "h-7 text-sm"
2726
+ variant: groupBy === "date" ? "secondary" : "ghost",
2727
+ size: "sm",
2728
+ onClick: () => setGroupBy("date"),
2729
+ className: "h-6 px-2 text-xs",
2730
+ children: config.labels?.groupByDate || "Date"
2619
2731
  }
2620
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2621
- SidebarMenuButton,
2732
+ ),
2733
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2734
+ Button,
2622
2735
  {
2623
- isActive: currentThreadId === thread.id,
2624
- onClick: () => onSelectThread?.(thread.id),
2625
- tooltip: thread.title,
2626
- children: [
2627
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ThreadInitialsIcon, { title: thread.title || "?" }),
2628
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-col items-start gap-0.5 flex-1 min-w-0 group-data-[collapsible=icon]:hidden", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate w-full", children: thread.title || "New Chat" }) }),
2629
- thread.isArchived && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Archive, { className: "ml-auto h-3 w-3 opacity-50 group-data-[collapsible=icon]:hidden" })
2630
- ]
2736
+ variant: groupBy === "tag" ? "secondary" : "ghost",
2737
+ size: "sm",
2738
+ onClick: () => setGroupBy("tag"),
2739
+ className: "h-6 px-2 text-xs",
2740
+ children: config.labels?.groupByTag || "Tag"
2631
2741
  }
2632
- ),
2633
- !editingThreadId && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DropdownMenu, { children: [
2634
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(SidebarMenuAction, { showOnHover: true, children: [
2635
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.MoreHorizontal, {}),
2636
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sr-only", children: "More" })
2637
- ] }) }),
2742
+ )
2743
+ ] })
2744
+ ] }) }),
2745
+ threadGroups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "px-4 py-8 text-center text-muted-foreground group-data-[collapsible=icon]:hidden", children: [
2746
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mx-auto h-8 w-8 mb-2 flex items-center justify-center rounded-full bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Plus, { className: "h-4 w-4 opacity-50" }) }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-xs", children: searchQuery ? config.labels?.noThreadsFound || "No conversations found" : config.labels?.noThreadsYet || "No conversations yet" })
2748
+ ] }) : threadGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2749
+ SidebarGroup,
2750
+ {
2751
+ className: "mt-2",
2752
+ onDragOver: (event) => {
2753
+ if (!canDragTags || !group.tag) return;
2754
+ event.preventDefault();
2755
+ setDragOverTagId(group.tag.id);
2756
+ },
2757
+ onDragLeave: () => {
2758
+ if (dragOverTagId === group.tag?.id) setDragOverTagId(null);
2759
+ },
2760
+ onDrop: (event) => {
2761
+ if (!canDragTags || !group.tag) return;
2762
+ event.preventDefault();
2763
+ handleDropOnTag(group.tag);
2764
+ },
2765
+ children: [
2638
2766
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2639
- DropdownMenuContent,
2767
+ SidebarGroupLabel,
2640
2768
  {
2641
- className: "w-48",
2642
- side: "right",
2643
- align: "start",
2769
+ className: `group-data-[collapsible=icon]:hidden ${dragOverTagId === group.tag?.id ? "rounded-md bg-sidebar-accent text-sidebar-accent-foreground" : ""}`,
2644
2770
  children: [
2645
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2646
- DropdownMenuItem,
2647
- {
2648
- onClick: () => startEditing(thread),
2649
- children: [
2650
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Edit2, { className: "mr-2 h-4 w-4" }),
2651
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: config.labels?.renameThread || "Rename" })
2652
- ]
2653
- }
2654
- ),
2655
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2656
- DropdownMenuItem,
2657
- {
2658
- onClick: () => onArchiveThread?.(thread.id),
2659
- children: [
2660
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Archive, { className: "mr-2 h-4 w-4" }),
2661
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: thread.isArchived ? config.labels?.unarchiveThread || "Unarchive" : config.labels?.archiveThread || "Archive" })
2662
- ]
2663
- }
2664
- ),
2665
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DropdownMenuSeparator, {}),
2666
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2667
- DropdownMenuItem,
2668
- {
2669
- onClick: () => setDeleteThreadId(thread.id),
2670
- className: "text-destructive focus:text-destructive",
2671
- children: [
2672
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Trash2, { className: "mr-2 h-4 w-4" }),
2673
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: config.labels?.deleteThread || "Delete" })
2674
- ]
2675
- }
2676
- )
2771
+ group.tag && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Tag, { className: "mr-1 h-3 w-3" }),
2772
+ group.label
2677
2773
  ]
2678
2774
  }
2679
- )
2680
- ] })
2681
- ] }, thread.id)) }) })
2682
- ] }, group))
2775
+ ),
2776
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarGroupContent, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarMenu, { children: group.threads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(SidebarMenuItem, { children: [
2777
+ editingThreadId === thread.id ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center gap-1 px-2 py-1", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2778
+ Input,
2779
+ {
2780
+ ref: inputRef,
2781
+ value: editTitle,
2782
+ onChange: (e) => setEditTitle(e.target.value),
2783
+ onKeyDown: (e) => {
2784
+ if (e.key === "Enter") {
2785
+ saveEdit();
2786
+ }
2787
+ if (e.key === "Escape") {
2788
+ cancelEdit();
2789
+ }
2790
+ },
2791
+ onBlur: saveEdit,
2792
+ className: "h-7 text-sm"
2793
+ }
2794
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2795
+ SidebarMenuButton,
2796
+ {
2797
+ isActive: currentThreadId === thread.id,
2798
+ onClick: () => onSelectThread?.(thread.id),
2799
+ tooltip: thread.title,
2800
+ draggable: canDragTags,
2801
+ onDragStart: () => setDraggingThreadId(thread.id),
2802
+ onDragEnd: () => {
2803
+ setDraggingThreadId(null);
2804
+ setDragOverTagId(null);
2805
+ },
2806
+ children: [
2807
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ThreadInitialsIcon, { title: thread.title || "?" }),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col items-start gap-0.5 flex-1 min-w-0 group-data-[collapsible=icon]:hidden", children: [
2809
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate w-full", children: thread.title || "New Chat" }),
2810
+ tagsEnabled && (thread.tags ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex max-w-full flex-wrap gap-1", children: (thread.tags ?? []).slice(0, 2).map((tag) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2811
+ "span",
2812
+ {
2813
+ className: "max-w-20 truncate rounded bg-sidebar-accent px-1.5 py-0.5 text-[10px] text-muted-foreground",
2814
+ children: tag.name
2815
+ },
2816
+ tag.id
2817
+ )) })
2818
+ ] }),
2819
+ thread.isArchived && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Archive, { className: "ml-auto h-3 w-3 opacity-50 group-data-[collapsible=icon]:hidden" })
2820
+ ]
2821
+ }
2822
+ ),
2823
+ !editingThreadId && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DropdownMenu, { children: [
2824
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(SidebarMenuAction, { showOnHover: true, children: [
2825
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.MoreHorizontal, {}),
2826
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "sr-only", children: "More" })
2827
+ ] }) }),
2828
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2829
+ DropdownMenuContent,
2830
+ {
2831
+ className: "w-48",
2832
+ side: "right",
2833
+ align: "start",
2834
+ children: [
2835
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2836
+ DropdownMenuItem,
2837
+ {
2838
+ onClick: () => startEditing(thread),
2839
+ children: [
2840
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Edit2, { className: "mr-2 h-4 w-4" }),
2841
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: config.labels?.renameThread || "Rename" })
2842
+ ]
2843
+ }
2844
+ ),
2845
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2846
+ DropdownMenuItem,
2847
+ {
2848
+ onClick: () => onArchiveThread?.(thread.id),
2849
+ children: [
2850
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Archive, { className: "mr-2 h-4 w-4" }),
2851
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: thread.isArchived ? config.labels?.unarchiveThread || "Unarchive" : config.labels?.archiveThread || "Archive" })
2852
+ ]
2853
+ }
2854
+ ),
2855
+ canUpdateTags && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2856
+ DropdownMenuItem,
2857
+ {
2858
+ onClick: () => {
2859
+ setTagDialogThreadId(thread.id);
2860
+ setNewTagName("");
2861
+ },
2862
+ children: [
2863
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Tag, { className: "mr-2 h-4 w-4" }),
2864
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: config.labels?.manageTags || "Manage tags" })
2865
+ ]
2866
+ }
2867
+ ),
2868
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DropdownMenuSeparator, {}),
2869
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2870
+ DropdownMenuItem,
2871
+ {
2872
+ onClick: () => setDeleteThreadId(thread.id),
2873
+ className: "text-destructive focus:text-destructive",
2874
+ children: [
2875
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Trash2, { className: "mr-2 h-4 w-4" }),
2876
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: config.labels?.deleteThread || "Delete" })
2877
+ ]
2878
+ }
2879
+ )
2880
+ ]
2881
+ }
2882
+ )
2883
+ ] })
2884
+ ] }, thread.id)) }) })
2885
+ ]
2886
+ },
2887
+ group.key
2888
+ ))
2683
2889
  ] }),
2684
2890
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarFooter, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2685
2891
  UserMenu,
@@ -2694,6 +2900,94 @@ var Sidebar2 = ({
2694
2900
  }
2695
2901
  ) }),
2696
2902
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SidebarRail, {}),
2903
+ tagDialogThread && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2904
+ Dialog,
2905
+ {
2906
+ open: !!tagDialogThread,
2907
+ onOpenChange: (open) => {
2908
+ if (!open) {
2909
+ setTagDialogThreadId(null);
2910
+ setNewTagName("");
2911
+ }
2912
+ },
2913
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogContent, { children: [
2914
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogHeader, { children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { children: config.labels?.manageTags || "Manage tags" }),
2916
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogDescription, { children: tagDialogThread.title || "New Chat" })
2917
+ ] }),
2918
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-4", children: [
2919
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-2", children: [
2920
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-sm font-medium", children: config.labels?.tags || "Tags" }),
2921
+ (tagDialogThread.tags ?? []).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-muted-foreground", children: config.labels?.untagged || "Untagged" }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-wrap gap-2", children: (tagDialogThread.tags ?? []).map((tag) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2922
+ "span",
2923
+ {
2924
+ className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-sm",
2925
+ children: [
2926
+ tag.name,
2927
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2928
+ "button",
2929
+ {
2930
+ type: "button",
2931
+ className: "rounded hover:bg-background",
2932
+ onClick: () => removeTagFromThread(tagDialogThread, tag.id),
2933
+ "aria-label": config.labels?.removeTag || "Remove tag",
2934
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { className: "h-3 w-3" })
2935
+ }
2936
+ )
2937
+ ]
2938
+ },
2939
+ tag.id
2940
+ )) })
2941
+ ] }),
2942
+ allTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-2", children: [
2943
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-sm font-medium", children: config.labels?.addTag || "Add tag" }),
2944
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex flex-wrap gap-2", children: allTags.map((tag) => {
2945
+ const assigned = (tagDialogThread.tags ?? []).some(
2946
+ (threadTag) => threadTag.id === tag.id
2947
+ );
2948
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2949
+ Button,
2950
+ {
2951
+ type: "button",
2952
+ variant: assigned ? "secondary" : "outline",
2953
+ size: "sm",
2954
+ disabled: assigned,
2955
+ onClick: () => addTagToThread(tagDialogThread, tag),
2956
+ children: tag.name
2957
+ },
2958
+ tag.id
2959
+ );
2960
+ }) })
2961
+ ] }),
2962
+ threadTagsConfig?.allowCreate !== false && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex gap-2", children: [
2963
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2964
+ Input,
2965
+ {
2966
+ value: newTagName,
2967
+ onChange: (event) => setNewTagName(event.target.value),
2968
+ placeholder: config.labels?.tagNamePlaceholder || "Tag name",
2969
+ onKeyDown: (event) => {
2970
+ if (event.key === "Enter") {
2971
+ event.preventDefault();
2972
+ handleCreateTag();
2973
+ }
2974
+ }
2975
+ }
2976
+ ),
2977
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2978
+ Button,
2979
+ {
2980
+ type: "button",
2981
+ onClick: handleCreateTag,
2982
+ disabled: !newTagName.trim(),
2983
+ children: config.labels?.addTag || "Add tag"
2984
+ }
2985
+ )
2986
+ ] })
2987
+ ] })
2988
+ ] })
2989
+ }
2990
+ ),
2697
2991
  deleteThreadId && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2698
2992
  AlertDialog,
2699
2993
  {
@@ -3154,6 +3448,20 @@ var ChatHeader = ({
3154
3448
  ] }),
3155
3449
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuSeparator, {})
3156
3450
  ] }),
3451
+ config.headerMenuItems?.map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3452
+ DropdownMenuItem,
3453
+ {
3454
+ onClick: item.onSelect,
3455
+ disabled: item.disabled,
3456
+ variant: item.variant,
3457
+ children: [
3458
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "h-4 w-4 mr-2 flex items-center justify-center", children: item.icon }) : null,
3459
+ item.label
3460
+ ]
3461
+ },
3462
+ item.id
3463
+ )),
3464
+ config.headerMenuItems && config.headerMenuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuSeparator, {}),
3157
3465
  onExportData && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenuItem, { onClick: onExportData, children: [
3158
3466
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Download, { className: "h-4 w-4 mr-2" }),
3159
3467
  config.labels?.exportData || "Export Data"
@@ -5391,9 +5699,10 @@ var ChatUI = ({
5391
5699
  }, [attachments]);
5392
5700
  const [isCustomMounted, setIsCustomMounted] = (0, import_react10.useState)(false);
5393
5701
  const [isCustomVisible, setIsCustomVisible] = (0, import_react10.useState)(false);
5394
- const groupedMessages = (0, import_react10.useMemo)(() => groupMessagesForRender(messages), [
5395
- messages
5396
- ]);
5702
+ const groupedMessages = (0, import_react10.useMemo)(
5703
+ () => groupMessagesForRender(messages),
5704
+ [messages]
5705
+ );
5397
5706
  const virtualizer = (0, import_react_virtual.useVirtualizer)({
5398
5707
  count: groupedMessages.length,
5399
5708
  getScrollElement: () => scrollAreaRef.current,
@@ -5519,7 +5828,8 @@ var ChatUI = ({
5519
5828
  });
5520
5829
  }, [groupedMessages, isLoadingOlderMessages, virtualizer]);
5521
5830
  const requestOlderMessages = (0, import_react10.useCallback)(() => {
5522
- if (!onLoadOlderMessages || !hasMoreMessagesBefore || isLoadingOlderMessages) return;
5831
+ if (!onLoadOlderMessages || !hasMoreMessagesBefore || isLoadingOlderMessages)
5832
+ return;
5523
5833
  const viewport = scrollAreaRef.current;
5524
5834
  prependSnapshotRef.current = viewport ? {
5525
5835
  scrollHeight: viewport.scrollHeight,
@@ -5551,55 +5861,68 @@ var ChatUI = ({
5551
5861
  return next;
5552
5862
  });
5553
5863
  }, [groupedMessages]);
5554
- const handleScroll = (0, import_react10.useCallback)((e) => {
5555
- const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
5556
- const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
5557
- const isNearTop = scrollTop < 120;
5558
- if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
5559
- requestOlderMessages();
5560
- }
5561
- setState((prev) => {
5562
- if (prev.isAtBottom === isAtBottom) return prev;
5563
- return { ...prev, isAtBottom };
5564
- });
5565
- }, [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]);
5566
- const handleSendMessage = (0, import_react10.useCallback)((content, messageAttachments = []) => {
5567
- if (!content.trim() && messageAttachments.length === 0) return;
5568
- callbacks.onSendMessage?.(
5569
- content,
5570
- messageAttachments,
5571
- createStateCallback()
5572
- );
5573
- if (initialInputApplied.current && !initialInputConsumedRef.current) {
5574
- initialInputConsumedRef.current = true;
5575
- onInitialInputConsumed?.();
5576
- }
5577
- setInputValue("");
5578
- setAttachments([]);
5579
- }, [callbacks, createStateCallback, onInitialInputConsumed]);
5580
- const handleMessageAction = (0, import_react10.useCallback)((event) => {
5581
- const { action, messageId, content } = event;
5582
- switch (action) {
5583
- case "copy":
5584
- callbacks.onCopyMessage?.(
5585
- messageId,
5586
- content || "",
5587
- createStateCallback()
5588
- );
5589
- break;
5590
- case "edit":
5591
- if (content) {
5592
- callbacks.onEditMessage?.(messageId, content, createStateCallback());
5593
- }
5594
- break;
5595
- case "regenerate":
5596
- callbacks.onRegenerateMessage?.(messageId, createStateCallback());
5597
- break;
5598
- case "delete":
5599
- callbacks.onDeleteMessage?.(messageId, createStateCallback());
5600
- break;
5601
- }
5602
- }, [callbacks, createStateCallback]);
5864
+ const handleScroll = (0, import_react10.useCallback)(
5865
+ (e) => {
5866
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
5867
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 50;
5868
+ const isNearTop = scrollTop < 120;
5869
+ if (isNearTop && hasMoreMessagesBefore && !isLoadingOlderMessages) {
5870
+ requestOlderMessages();
5871
+ }
5872
+ setState((prev) => {
5873
+ if (prev.isAtBottom === isAtBottom) return prev;
5874
+ return { ...prev, isAtBottom };
5875
+ });
5876
+ },
5877
+ [hasMoreMessagesBefore, isLoadingOlderMessages, requestOlderMessages]
5878
+ );
5879
+ const handleSendMessage = (0, import_react10.useCallback)(
5880
+ (content, messageAttachments = []) => {
5881
+ if (!content.trim() && messageAttachments.length === 0) return;
5882
+ callbacks.onSendMessage?.(
5883
+ content,
5884
+ messageAttachments,
5885
+ createStateCallback()
5886
+ );
5887
+ if (initialInputApplied.current && !initialInputConsumedRef.current) {
5888
+ initialInputConsumedRef.current = true;
5889
+ onInitialInputConsumed?.();
5890
+ }
5891
+ setInputValue("");
5892
+ setAttachments([]);
5893
+ },
5894
+ [callbacks, createStateCallback, onInitialInputConsumed]
5895
+ );
5896
+ const handleMessageAction = (0, import_react10.useCallback)(
5897
+ (event) => {
5898
+ const { action, messageId, content } = event;
5899
+ switch (action) {
5900
+ case "copy":
5901
+ callbacks.onCopyMessage?.(
5902
+ messageId,
5903
+ content || "",
5904
+ createStateCallback()
5905
+ );
5906
+ break;
5907
+ case "edit":
5908
+ if (content) {
5909
+ callbacks.onEditMessage?.(
5910
+ messageId,
5911
+ content,
5912
+ createStateCallback()
5913
+ );
5914
+ }
5915
+ break;
5916
+ case "regenerate":
5917
+ callbacks.onRegenerateMessage?.(messageId, createStateCallback());
5918
+ break;
5919
+ case "delete":
5920
+ callbacks.onDeleteMessage?.(messageId, createStateCallback());
5921
+ break;
5922
+ }
5923
+ },
5924
+ [callbacks, createStateCallback]
5925
+ );
5603
5926
  const handleToggleMessageExpansion = (0, import_react10.useCallback)((messageId) => {
5604
5927
  setExpandedMessageIds((prev) => {
5605
5928
  if (prev[messageId]) {
@@ -5613,51 +5936,79 @@ var ChatUI = ({
5613
5936
  };
5614
5937
  });
5615
5938
  }, []);
5616
- const handleCreateThread = (0, import_react10.useCallback)((title) => {
5617
- callbacks.onCreateThread?.(title, createStateCallback());
5618
- }, [callbacks, createStateCallback]);
5619
- const handleSelectThread = (0, import_react10.useCallback)((threadId) => {
5620
- callbacks.onSelectThread?.(threadId, createStateCallback());
5621
- }, [callbacks, createStateCallback]);
5939
+ const handleCreateThread = (0, import_react10.useCallback)(
5940
+ (title) => {
5941
+ callbacks.onCreateThread?.(title, createStateCallback());
5942
+ },
5943
+ [callbacks, createStateCallback]
5944
+ );
5945
+ const handleSelectThread = (0, import_react10.useCallback)(
5946
+ (threadId) => {
5947
+ callbacks.onSelectThread?.(threadId, createStateCallback());
5948
+ },
5949
+ [callbacks, createStateCallback]
5950
+ );
5622
5951
  const handleRenameThread = (0, import_react10.useCallback)(
5623
5952
  (threadId, newTitle) => {
5624
5953
  callbacks.onRenameThread?.(threadId, newTitle, createStateCallback());
5625
5954
  },
5626
5955
  [callbacks, createStateCallback]
5627
5956
  );
5628
- const handleDeleteThread = (0, import_react10.useCallback)((threadId) => {
5629
- callbacks.onDeleteThread?.(threadId, createStateCallback());
5630
- }, [callbacks, createStateCallback]);
5631
- const handleArchiveThread = (0, import_react10.useCallback)((threadId) => {
5632
- callbacks.onArchiveThread?.(threadId, createStateCallback());
5633
- }, [callbacks, createStateCallback]);
5957
+ const handleDeleteThread = (0, import_react10.useCallback)(
5958
+ (threadId) => {
5959
+ callbacks.onDeleteThread?.(threadId, createStateCallback());
5960
+ },
5961
+ [callbacks, createStateCallback]
5962
+ );
5963
+ const handleArchiveThread = (0, import_react10.useCallback)(
5964
+ (threadId) => {
5965
+ callbacks.onArchiveThread?.(threadId, createStateCallback());
5966
+ },
5967
+ [callbacks, createStateCallback]
5968
+ );
5969
+ const handleUpdateThreadTags = (0, import_react10.useCallback)(
5970
+ (threadId, tags) => {
5971
+ callbacks.onUpdateThreadTags?.(
5972
+ threadId,
5973
+ tags ?? [],
5974
+ createStateCallback()
5975
+ );
5976
+ },
5977
+ [callbacks, createStateCallback]
5978
+ );
5634
5979
  const closeSidebar = (0, import_react10.useCallback)(() => {
5635
5980
  setState((prev) => ({ ...prev, showSidebar: false }));
5636
5981
  }, []);
5637
5982
  const handleCustomComponentToggle = (0, import_react10.useCallback)(() => {
5638
5983
  setState((prev) => ({ ...prev, showSidebar: !prev.showSidebar }));
5639
5984
  }, []);
5640
- const sidebarUser = (0, import_react10.useMemo)(() => user ? {
5641
- id: user.id,
5642
- name: user.name,
5643
- email: user.email,
5644
- avatar: user.avatar
5645
- } : null, [user?.id, user?.name, user?.email, user?.avatar]);
5985
+ const sidebarUser = (0, import_react10.useMemo)(
5986
+ () => user ? {
5987
+ id: user.id,
5988
+ name: user.name,
5989
+ email: user.email,
5990
+ avatar: user.avatar
5991
+ } : null,
5992
+ [user?.id, user?.name, user?.email, user?.avatar]
5993
+ );
5646
5994
  const handleViewProfile = (0, import_react10.useCallback)(() => {
5647
5995
  setIsUserProfileOpen(true);
5648
5996
  callbacks.onViewProfile?.();
5649
5997
  }, [callbacks.onViewProfile]);
5650
- const sidebarUserMenuCallbacks = (0, import_react10.useMemo)(() => ({
5651
- onViewProfile: handleViewProfile,
5652
- onOpenSettings: callbacks.onOpenSettings,
5653
- onThemeChange: callbacks.onThemeChange,
5654
- onLogout: callbacks.onLogout
5655
- }), [
5656
- handleViewProfile,
5657
- callbacks.onOpenSettings,
5658
- callbacks.onThemeChange,
5659
- callbacks.onLogout
5660
- ]);
5998
+ const sidebarUserMenuCallbacks = (0, import_react10.useMemo)(
5999
+ () => ({
6000
+ onViewProfile: handleViewProfile,
6001
+ onOpenSettings: callbacks.onOpenSettings,
6002
+ onThemeChange: callbacks.onThemeChange,
6003
+ onLogout: callbacks.onLogout
6004
+ }),
6005
+ [
6006
+ handleViewProfile,
6007
+ callbacks.onOpenSettings,
6008
+ callbacks.onThemeChange,
6009
+ callbacks.onLogout
6010
+ ]
6011
+ );
5661
6012
  const renderCustomComponent = (0, import_react10.useCallback)(() => {
5662
6013
  const component = config?.customComponent?.component;
5663
6014
  if (!component) return null;
@@ -5738,55 +6089,58 @@ var ChatUI = ({
5738
6089
  );
5739
6090
  }) });
5740
6091
  const isMultiAgentMode = config.agentSelector?.mode === "multi";
5741
- const messageProps = (0, import_react10.useMemo)(() => ({
5742
- userAvatar: user?.avatar,
5743
- userName: user?.name,
5744
- assistantAvatar: assistant?.avatar,
5745
- assistantName: assistant?.name,
5746
- showTimestamp: config.ui.showTimestamps,
5747
- showAvatar: config.ui.showAvatars,
5748
- enableCopy: config.features.enableMessageCopy,
5749
- enableEdit: config.features.enableMessageEditing,
5750
- enableRegenerate: config.features.enableRegeneration,
5751
- showActivity: config.features.showActivity,
5752
- showActivityDetails: config.features.showActivityDetails,
5753
- compactMode: config.ui.compactMode,
5754
- onAction: handleMessageAction,
5755
- labels: config.labels,
5756
- showMoreLabel: config.labels.showMoreMessage,
5757
- showLessLabel: config.labels.showLessMessage,
5758
- collapseLongMessages: config.ui.collapseLongMessages,
5759
- collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
5760
- longMessagePreviewChars: config.ui.longMessagePreviewChars,
5761
- longMessageChunkChars: config.ui.longMessageChunkChars,
5762
- renderUserMarkdown: config.ui.renderUserMarkdown,
5763
- markdown: config.markdown,
5764
- onToggleExpanded: handleToggleMessageExpansion
5765
- }), [
5766
- user?.avatar,
5767
- user?.name,
5768
- assistant?.avatar,
5769
- assistant?.name,
5770
- config.ui.showTimestamps,
5771
- config.ui.showAvatars,
5772
- config.ui.compactMode,
5773
- config.features.enableMessageCopy,
5774
- config.features.enableMessageEditing,
5775
- config.features.enableRegeneration,
5776
- config.features.showActivity,
5777
- config.features.showActivityDetails,
5778
- config.labels,
5779
- config.labels.showMoreMessage,
5780
- config.labels.showLessMessage,
5781
- config.ui.collapseLongMessages,
5782
- config.ui.collapseLongMessagesForUserOnly,
5783
- config.ui.longMessagePreviewChars,
5784
- config.ui.longMessageChunkChars,
5785
- config.ui.renderUserMarkdown,
5786
- config.markdown,
5787
- handleMessageAction,
5788
- handleToggleMessageExpansion
5789
- ]);
6092
+ const messageProps = (0, import_react10.useMemo)(
6093
+ () => ({
6094
+ userAvatar: user?.avatar,
6095
+ userName: user?.name,
6096
+ assistantAvatar: assistant?.avatar,
6097
+ assistantName: assistant?.name,
6098
+ showTimestamp: config.ui.showTimestamps,
6099
+ showAvatar: config.ui.showAvatars,
6100
+ enableCopy: config.features.enableMessageCopy,
6101
+ enableEdit: config.features.enableMessageEditing,
6102
+ enableRegenerate: config.features.enableRegeneration,
6103
+ showActivity: config.features.showActivity,
6104
+ showActivityDetails: config.features.showActivityDetails,
6105
+ compactMode: config.ui.compactMode,
6106
+ onAction: handleMessageAction,
6107
+ labels: config.labels,
6108
+ showMoreLabel: config.labels.showMoreMessage,
6109
+ showLessLabel: config.labels.showLessMessage,
6110
+ collapseLongMessages: config.ui.collapseLongMessages,
6111
+ collapseLongMessagesForUserOnly: config.ui.collapseLongMessagesForUserOnly,
6112
+ longMessagePreviewChars: config.ui.longMessagePreviewChars,
6113
+ longMessageChunkChars: config.ui.longMessageChunkChars,
6114
+ renderUserMarkdown: config.ui.renderUserMarkdown,
6115
+ markdown: config.markdown,
6116
+ onToggleExpanded: handleToggleMessageExpansion
6117
+ }),
6118
+ [
6119
+ user?.avatar,
6120
+ user?.name,
6121
+ assistant?.avatar,
6122
+ assistant?.name,
6123
+ config.ui.showTimestamps,
6124
+ config.ui.showAvatars,
6125
+ config.ui.compactMode,
6126
+ config.features.enableMessageCopy,
6127
+ config.features.enableMessageEditing,
6128
+ config.features.enableRegeneration,
6129
+ config.features.showActivity,
6130
+ config.features.showActivityDetails,
6131
+ config.labels,
6132
+ config.labels.showMoreMessage,
6133
+ config.labels.showLessMessage,
6134
+ config.ui.collapseLongMessages,
6135
+ config.ui.collapseLongMessagesForUserOnly,
6136
+ config.ui.longMessagePreviewChars,
6137
+ config.ui.longMessageChunkChars,
6138
+ config.ui.renderUserMarkdown,
6139
+ config.markdown,
6140
+ handleMessageAction,
6141
+ handleToggleMessageExpansion
6142
+ ]
6143
+ );
5790
6144
  const shouldShowAgentSelector = Boolean(
5791
6145
  config.agentSelector?.enabled && agentOptions.length > 0 && (!config.agentSelector?.hideIfSingle || agentOptions.length > 1) && (isMultiAgentMode ? onParticipantsChange : onSelectAgent)
5792
6146
  );
@@ -5806,6 +6160,7 @@ var ChatUI = ({
5806
6160
  onRenameThread: handleRenameThread,
5807
6161
  onDeleteThread: handleDeleteThread,
5808
6162
  onArchiveThread: handleArchiveThread,
6163
+ onUpdateThreadTags: handleUpdateThreadTags,
5809
6164
  user: sidebarUser,
5810
6165
  userMenuCallbacks: sidebarUserMenuCallbacks,
5811
6166
  currentTheme: config.ui.theme === "auto" ? "system" : config.ui.theme,
@@ -5819,9 +6174,7 @@ var ChatUI = ({
5819
6174
  ChatHeader,
5820
6175
  {
5821
6176
  config,
5822
- currentThreadTitle: threads.find(
5823
- (t) => t.id === state.selectedThreadId
5824
- )?.title,
6177
+ currentThreadTitle: threads.find((t) => t.id === state.selectedThreadId)?.title,
5825
6178
  isMobile,
5826
6179
  onCustomComponentToggle: handleCustomComponentToggle,
5827
6180
  onNewThread: handleCreateThread,