feat: add preset configurations with built-in and custom presets
Add preset store with three built-in presets (Strict mode, Research only, Full access) and localStorage persistence for custom presets. Integrate preset selector into ConfigSidebar with load, save, and delete actions. Built-in presets cannot be deleted. Closes #14 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import type { SessionConfig } from '$lib/proto/llm_multiverse/v1/orchestrator_pb';
|
||||
import { SessionConfigSchema } from '$lib/proto/llm_multiverse/v1/orchestrator_pb';
|
||||
import { create } from '@bufbuild/protobuf';
|
||||
import { presetStore } from '$lib/stores/presets.svelte';
|
||||
|
||||
let {
|
||||
config,
|
||||
@@ -10,6 +11,9 @@
|
||||
}: { config: SessionConfig; onConfigChange: (config: SessionConfig) => void } = $props();
|
||||
|
||||
let newPermission = $state('');
|
||||
let newPresetName = $state('');
|
||||
let showSavePreset = $state(false);
|
||||
let saveError = $state('');
|
||||
|
||||
const toolTypes = [
|
||||
{ value: ToolType.MEMORY_READ, label: 'Memory Read' },
|
||||
@@ -79,6 +83,36 @@
|
||||
function resetConfig() {
|
||||
onConfigChange(create(SessionConfigSchema, { overrideLevel: OverrideLevel.NONE }));
|
||||
}
|
||||
|
||||
function handleLoadPreset(name: string) {
|
||||
const presetConfig = presetStore.loadPreset(name);
|
||||
if (!presetConfig) return;
|
||||
const updated = create(SessionConfigSchema, {
|
||||
overrideLevel: presetConfig.overrideLevel,
|
||||
disabledTools: [...presetConfig.disabledTools],
|
||||
grantedPermissions: [...presetConfig.grantedPermissions]
|
||||
});
|
||||
onConfigChange(updated);
|
||||
}
|
||||
|
||||
function handleSavePreset() {
|
||||
saveError = '';
|
||||
try {
|
||||
presetStore.savePreset(newPresetName, {
|
||||
overrideLevel: config.overrideLevel,
|
||||
disabledTools: [...config.disabledTools],
|
||||
grantedPermissions: [...config.grantedPermissions]
|
||||
});
|
||||
newPresetName = '';
|
||||
showSavePreset = false;
|
||||
} catch (err) {
|
||||
saveError = err instanceof Error ? err.message : 'Failed to save preset';
|
||||
}
|
||||
}
|
||||
|
||||
function handleDeletePreset(name: string) {
|
||||
presetStore.deletePreset(name);
|
||||
}
|
||||
</script>
|
||||
|
||||
<aside class="flex h-full w-72 flex-col border-l border-gray-200 bg-white">
|
||||
@@ -99,6 +133,73 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-4 space-y-5">
|
||||
<!-- Presets -->
|
||||
<div>
|
||||
<p class="mb-2 text-xs font-medium text-gray-700">Presets</p>
|
||||
<div class="space-y-1">
|
||||
{#each presetStore.getAllPresets() as preset (preset.name)}
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handleLoadPreset(preset.name)}
|
||||
class="flex-1 rounded-lg px-3 py-1.5 text-left text-sm text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
{preset.name}
|
||||
{#if preset.builtIn}
|
||||
<span class="text-[10px] text-gray-400">built-in</span>
|
||||
{/if}
|
||||
</button>
|
||||
{#if !preset.builtIn}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handleDeletePreset(preset.name)}
|
||||
class="rounded px-1.5 py-1 text-xs text-gray-400 hover:text-red-500"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if showSavePreset}
|
||||
<div class="mt-2 flex gap-1">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={newPresetName}
|
||||
onkeydown={(e) => { if (e.key === 'Enter') handleSavePreset(); }}
|
||||
placeholder="Preset name"
|
||||
class="flex-1 rounded-lg border border-gray-300 px-2 py-1.5 text-xs focus:border-blue-500 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onclick={handleSavePreset}
|
||||
disabled={!newPresetName.trim()}
|
||||
class="rounded-lg bg-blue-50 px-2 py-1.5 text-xs font-medium text-blue-700 hover:bg-blue-100 disabled:opacity-50"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { showSavePreset = false; saveError = ''; newPresetName = ''; }}
|
||||
class="rounded-lg px-1.5 py-1.5 text-xs text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
{#if saveError}
|
||||
<p class="mt-1 text-xs text-red-500">{saveError}</p>
|
||||
{/if}
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (showSavePreset = true)}
|
||||
class="mt-2 w-full rounded-lg border border-dashed border-gray-300 px-3 py-1.5 text-xs text-gray-500 hover:border-gray-400 hover:text-gray-700"
|
||||
>
|
||||
Save current as preset
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Override Level -->
|
||||
<div>
|
||||
<p class="mb-2 text-xs font-medium text-gray-700">Override Level</p>
|
||||
|
||||
Reference in New Issue
Block a user