2 import { Pin } from '@lucide/svelte';
3 import { buildConversationTree } from '$lib/stores/conversations.svelte';
4 import SidebarNavigationConversationItem from './SidebarNavigationConversationItem.svelte';
5 import SidebarNavigationSearchResults from './SidebarNavigationSearchResults.svelte';
6 import SidebarNavigationSelectionBar from './SidebarNavigationSelectionBar.svelte';
10 filteredConversations: DatabaseConversation[];
11 currentChatId: string | undefined;
12 isSearchModeActive: boolean;
14 isSelectionMode?: boolean;
15 selectedIds?: Set<string>;
16 onSelect: (id: string) => void;
17 onEdit: (id: string) => void;
18 onDelete: (id: string) => void;
19 onStop: (id: string) => void;
20 onToggleSelect?: (id: string) => void;
21 onEnterSelectionMode?: (id: string) => void;
22 onSelectionClick?: (id: string, options: { shiftKey: boolean }) => void;
23 onRowMouseDown?: (id: string, event: MouseEvent) => void;
25 allVisibleSelected: boolean;
26 someVisibleSelected: boolean;
27 allSelectedArePinned: boolean;
28 pinStateIsMixed: boolean;
29 onSelectAllToggle: () => void;
30 onBulkPinToggle: () => void;
31 onBulkExport: () => void;
32 onBulkDelete: () => void;
33 onCloseSelection: () => void;
38 filteredConversations,
42 isSelectionMode = false,
43 selectedIds = new Set<string>(),
64 let conversationTree = $derived(buildConversationTree(filteredConversations));
66 let pinnedConversations = $derived(
67 conversationTree.filter(({ conversation }) => conversation.pinned)
70 let unpinnedConversations = $derived(
71 conversationTree.filter(({ conversation }) => !conversation.pinned)
74 const recentEmptyMessage = $derived(
75 searchQuery.length > 0 ? 'No results found' : 'No conversations yet'
79 <div class="flex min-h-0 flex-1 flex-col">
80 {#if isSearchModeActive}
81 <SidebarNavigationSearchResults
84 {filteredConversations}
93 {onEnterSelectionMode}
98 {#if pinnedConversations.length > 0}
99 <div class="py-2 flex whitespace-nowrap {className}">
101 class="text-muted-foreground inline-flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium gap-1"
103 <Pin class="h-3.5 w-3.5" />
109 <ul class="flex w-full min-w-0 flex-col gap-4 md:gap-1 {className}">
110 {#each pinnedConversations as { conversation, depth } (conversation.id)}
111 <li class="group/item relative mb-1 p-0">
112 <SidebarNavigationConversationItem
115 name: conversation.name,
116 lastModified: conversation.lastModified,
117 currNode: conversation.currNode,
118 forkedFromConversationId: conversation.forkedFromConversationId,
119 pinned: conversation.pinned
122 isActive={currentChatId === conversation.id}
124 isSelected={selectedIds.has(conversation.id)}
130 {onEnterSelectionMode}
139 <div class="mt-2 flex min-h-0 flex-1 flex-col gap-4 md:gap-0 whitespace-nowrap {className}">
140 {#if filteredConversations.length > 0}
142 class="text-muted-foreground flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium"
148 <div class="min-h-0 flex-1 md:overflow-y-auto">
149 <ul class="flex w-full min-w-0 flex-col gap-4 md:gap-0">
150 {#each unpinnedConversations as { conversation, depth } (conversation.id)}
151 <li class="group/item relative mb-1 p-0">
152 <SidebarNavigationConversationItem
155 name: conversation.name,
156 lastModified: conversation.lastModified,
157 currNode: conversation.currNode,
158 forkedFromConversationId: conversation.forkedFromConversationId,
159 pinned: conversation.pinned
162 isActive={currentChatId === conversation.id}
164 isSelected={selectedIds.has(conversation.id)}
170 {onEnterSelectionMode}
177 {#if unpinnedConversations.length === 0}
178 <li class="px-2 py-4 text-center">
179 <p class="mb-4 p-4 text-sm text-muted-foreground">
188 {#if isSelectionMode}
189 <SidebarNavigationSelectionBar
190 class="sticky top-0 z-10 m-2 mt-0"
191 selectedCount={selectedIds.size}
194 {someVisibleSelected}
195 someSelectedPinned={allSelectedArePinned}
201 onClose={onCloseSelection}