ui: Sidebar Conversations Bulk Action + Improved Settings logic/UI (#25815)

* feat: WIP

* feat: Replace conversation rename flow with unified AlertDialog component

* feat: Add radio group component and consolidate title generation settings

* refactor: Remove JS Sandbox global toggle and migrate legacy user state

* chore: Formatting

* refactor: Cleanup

Co-authored-by: Aleksander Grygier <aleksander.grygier@gmail.com>

* refactor: Cleanup

* refactor: Marquee selection hook

* feat: UI improvements

* refactor: Bulk db operations

* fix: optimize bulk conversation deletion to handle ancestor chains

* refactor: remove pairedKey mechanism from settings system

* fix: remove redundant onclick handler from dialog cancel button

* chore: pin @lucide/svelte to exact version

* feat: Run JavaScript tool disabled by default

* fix: correct active conversation deletion tracking in bulk delete

* feat: improve shift-key multi-selection support in sidebar via keyboard

* refactor: Retrieve JS Tool enabling via Developer Settings

* nits: sync, dialog wording, cycle guard, and lockfile follow-ups

- Restore titleGenerationUseLLM registry entry so it syncs across devices again
- Mention fork cascade in the bulk delete confirmation dialog
- Clear newParent on cycle guard break so children never point at a deleted conversation
- Align @lucide/svelte in package-lock.json with the exact pin in package.json

---------

Co-authored-by: Pascal <admin@serveurperso.com>
This commit is contained in:
Aleksander Grygier
2026-07-20 23:40:08 +02:00
committed by GitHub
co-authored by Pascal
parent 91d2fc3875
commit 2beefef688
31 changed files with 1529 additions and 426 deletions
@@ -0,0 +1,85 @@
<script lang="ts">
import * as AlertDialog from '$lib/components/ui/alert-dialog';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Pencil } from '@lucide/svelte';
interface Props {
open: boolean;
currentTitle: string;
value: string;
onConfirm: () => void;
onCancel: () => void;
}
let {
open = $bindable(),
currentTitle,
value = $bindable(''),
onConfirm,
onCancel
}: Props = $props();
let inputRef = $state<HTMLInputElement | null>(null);
const canSubmit = $derived(value.trim().length > 0 && value.trim() !== currentTitle.trim());
$effect(() => {
if (open) {
value = currentTitle;
queueMicrotask(() => {
inputRef?.focus();
inputRef?.select();
});
}
});
function handleOpenChange(newOpen: boolean) {
if (!newOpen) {
onCancel();
}
}
function handleSubmit(event: Event) {
event.preventDefault();
if (!canSubmit) return;
value = value.trim();
onConfirm();
}
</script>
<AlertDialog.Root bind:open onOpenChange={handleOpenChange}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title class="flex items-center gap-2">
<Pencil class="h-5 w-5" />
Rename conversation
</AlertDialog.Title>
<AlertDialog.Description>Choose a new title for this conversation.</AlertDialog.Description>
</AlertDialog.Header>
<form onsubmit={handleSubmit} class="space-y-2 pt-2 pb-4">
<label for="conversation-rename-input" class="text-sm font-medium text-muted-foreground">
Conversation title
</label>
<Input
id="conversation-rename-input"
bind:ref={inputRef}
bind:value
placeholder="Conversation title"
maxlength={200}
autocomplete="off"
autocorrect="off"
spellcheck={false}
/>
</form>
<AlertDialog.Footer>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<Button type="button" onclick={handleSubmit} disabled={!canSubmit}>Save</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>