Skip to content

Commit 9d3973f

Browse files
committed
feat: Enable shift-click multiple conversation items selection
1 parent 37b4bc3 commit 9d3973f

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

tools/server/webui/src/lib/components/app/chat/ChatSettings/ConversationSelectionDialog.svelte

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
2828
let searchQuery = $state('');
2929
let selectedIds = $state.raw<SvelteSet<string>>(new SvelteSet(conversations.map((c) => c.id)));
30+
let lastClickedId = $state<string | null>(null);
3031
3132
let filteredConversations = $derived(
3233
conversations.filter((conv) => {
@@ -44,14 +45,40 @@
4445
filteredConversations.some((conv) => selectedIds.has(conv.id)) && !allSelected
4546
);
4647
47-
function toggleConversation(id: string) {
48+
function toggleConversation(id: string, shiftKey: boolean = false) {
4849
const newSet = new SvelteSet(selectedIds);
50+
51+
if (shiftKey && lastClickedId !== null) {
52+
const lastIndex = filteredConversations.findIndex((c) => c.id === lastClickedId);
53+
const currentIndex = filteredConversations.findIndex((c) => c.id === id);
54+
55+
if (lastIndex !== -1 && currentIndex !== -1) {
56+
const start = Math.min(lastIndex, currentIndex);
57+
const end = Math.max(lastIndex, currentIndex);
58+
59+
const shouldSelect = !newSet.has(id);
60+
61+
for (let i = start; i <= end; i++) {
62+
if (shouldSelect) {
63+
newSet.add(filteredConversations[i].id);
64+
} else {
65+
newSet.delete(filteredConversations[i].id);
66+
}
67+
}
68+
69+
selectedIds = newSet;
70+
return;
71+
}
72+
}
73+
4974
if (newSet.has(id)) {
5075
newSet.delete(id);
5176
} else {
5277
newSet.add(id);
5378
}
79+
5480
selectedIds = newSet;
81+
lastClickedId = id;
5582
}
5683
5784
function toggleAll() {
@@ -76,6 +103,7 @@
76103
function handleCancel() {
77104
selectedIds = new SvelteSet(conversations.map((c) => c.id));
78105
searchQuery = '';
106+
lastClickedId = null;
79107
80108
onCancel();
81109
}
@@ -86,6 +114,7 @@
86114
if (open && !previousOpen) {
87115
selectedIds = new SvelteSet(conversations.map((c) => c.id));
88116
searchQuery = '';
117+
lastClickedId = null;
89118
} else if (!open && previousOpen) {
90119
onCancel();
91120
}
@@ -174,12 +203,16 @@
174203
{#each filteredConversations as conv (conv.id)}
175204
<tr
176205
class="cursor-pointer border-b transition-colors hover:bg-muted/50"
177-
onclick={() => toggleConversation(conv.id)}
206+
onclick={(e) => toggleConversation(conv.id, e.shiftKey)}
178207
>
179-
<td class="p-3" onclick={(e) => e.stopPropagation()}>
208+
<td class="p-3">
180209
<Checkbox
181210
checked={selectedIds.has(conv.id)}
182-
onCheckedChange={() => toggleConversation(conv.id)}
211+
onclick={(e) => {
212+
e.preventDefault();
213+
e.stopPropagation();
214+
toggleConversation(conv.id, e.shiftKey);
215+
}}
183216
/>
184217
</td>
185218

0 commit comments

Comments
 (0)