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:
co-authored by
Pascal
parent
91d2fc3875
commit
2beefef688
@@ -4,6 +4,7 @@
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import Label from '$lib/components/ui/label/label.svelte';
|
||||
import * as RadioGroup from '$lib/components/ui/radio-group';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { SETTING_CONFIG_INFO, SETTINGS_KEYS } from '$lib/constants';
|
||||
@@ -84,10 +85,7 @@
|
||||
type={field.isPositiveInteger ? 'number' : 'text'}
|
||||
{...field.isPositiveInteger ? { min: '1', step: '1' } : {}}
|
||||
value={currentValue}
|
||||
oninput={(e) => {
|
||||
// Update local config immediately for real-time badge feedback
|
||||
onConfigChange(field.key, e.currentTarget.value);
|
||||
}}
|
||||
oninput={(e) => onConfigChange(field.key, e.currentTarget.value)}
|
||||
placeholder={currentModelParams[field.key] != null
|
||||
? `Default: ${normalizeFloatingPoint(currentModelParams[field.key])}`
|
||||
: ''}
|
||||
@@ -236,6 +234,52 @@
|
||||
{field.help || SETTING_CONFIG_INFO[field.key]}
|
||||
</p>
|
||||
{/if}
|
||||
{:else if field.type === SettingsFieldType.RADIO && field.radioOptions}
|
||||
{@const radioOptions = field.radioOptions}
|
||||
{@const currentMode =
|
||||
radioOptions.find((o: { key: string }) => Boolean(localConfig[o.key]))?.value ??
|
||||
radioOptions[0].value}
|
||||
|
||||
<Label class="flex items-center gap-1.5 text-sm font-medium mb-4">
|
||||
{field.label}
|
||||
|
||||
{#if field.isExperimental}
|
||||
<FlaskConical class="h-3.5 w-3.5 text-muted-foreground" />
|
||||
{/if}
|
||||
</Label>
|
||||
|
||||
<RadioGroup.Root
|
||||
class="gap-4"
|
||||
value={currentMode}
|
||||
onValueChange={(value) => {
|
||||
for (const opt of radioOptions) {
|
||||
onConfigChange(opt.key, opt.value === value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#each radioOptions as opt (opt.value)}
|
||||
{@const itemId = `${field.key}-${opt.value}`}
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroup.Item value={opt.value} id={itemId} />
|
||||
<Label
|
||||
for={itemId}
|
||||
class="flex cursor-pointer items-center gap-1.5 text-sm font-normal"
|
||||
>
|
||||
{opt.label}
|
||||
|
||||
{#if opt.isExperimental}
|
||||
<FlaskConical class="h-3.5 w-3.5 text-muted-foreground" />
|
||||
{/if}
|
||||
</Label>
|
||||
</div>
|
||||
{/each}
|
||||
</RadioGroup.Root>
|
||||
|
||||
{#if field.help || SETTING_CONFIG_INFO[field.key]}
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{field.help || SETTING_CONFIG_INFO[field.key]}
|
||||
</p>
|
||||
{/if}
|
||||
{:else if field.type === SettingsFieldType.CHECKBOX}
|
||||
<div class="flex items-start space-x-3">
|
||||
<Checkbox
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import { toolsStore } from '$lib/stores/tools.svelte';
|
||||
import { permissionsStore } from '$lib/stores/permissions.svelte';
|
||||
import { mcpStore } from '$lib/stores/mcp.svelte';
|
||||
import { getBuiltinToolUi } from '$lib/constants/built-in-tools';
|
||||
import { ToolSource } from '$lib/enums/tools.enums';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
|
||||
let expandedGroups = new SvelteSet<string>();
|
||||
@@ -69,12 +71,23 @@
|
||||
|
||||
{#each group.tools as entry (entry.key)}
|
||||
{@const toolName = entry.definition.function.name}
|
||||
{@const builtinUi =
|
||||
entry.source === ToolSource.BUILTIN || entry.source === ToolSource.FRONTEND
|
||||
? getBuiltinToolUi(toolName)
|
||||
: null}
|
||||
{@const displayLabel = builtinUi?.label ?? toolName}
|
||||
{@const IconComponent = builtinUi?.icon ?? null}
|
||||
{@const isEnabled = toolsStore.isToolEnabled(entry.key)}
|
||||
{@const permissionKey = entry.key}
|
||||
{@const isAlwaysAllowed = permissionsStore.hasTool(permissionKey)}
|
||||
|
||||
<div class="flex items-center gap-2 rounded px-2 py-1.5 text-sm hover:bg-muted/50">
|
||||
<TruncatedText text={toolName} class="flex-1" showTooltip={true} />
|
||||
<span class="flex min-w-0 flex-1 items-center gap-1.5">
|
||||
{#if IconComponent}
|
||||
<IconComponent class={ICON_CLASS_DEFAULT} />
|
||||
{/if}
|
||||
<TruncatedText text={displayLabel} class="min-w-0" showTooltip={true} />
|
||||
</span>
|
||||
|
||||
<div class="flex w-16 shrink-0 justify-center">
|
||||
<Checkbox
|
||||
|
||||
Reference in New Issue
Block a user