diff options
| author | Adam Malczewski <[email protected]> | 2026-05-22 15:24:13 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-22 15:24:13 +0900 |
| commit | 9ecaabd87c0e51b8a7408dabb0133a9344586859 (patch) | |
| tree | 4b5809ab23948a5e3f558f3aa34c52d5038f4e19 /packages/frontend/src/lib/components/ConfigPanel.svelte | |
| parent | 8f1dd855f0c4c877bff8d3a4ba193432b268b1c2 (diff) | |
| download | dispatch-9ecaabd87c0e51b8a7408dabb0133a9344586859.tar.gz dispatch-9ecaabd87c0e51b8a7408dabb0133a9344586859.zip | |
feat: agent builder, CWD support, auto-save, UI polish, unavailable tool handling
- Agent Builder: full CRUD with card grid, drag-and-drop model reorder, edit/delete
- Auto-save on edit with 600ms debounce, AbortController for concurrency, fieldset disabled until name entered
- Agent definitions stored as TOML with cwd field, loaded from global/project dirs
- Working directory: per-tab CWD override in Chat Settings, agent default CWD, auto-create on first message
- CWD validation: check-dir endpoint with ~ expansion, real-time validity indicator
- Subagent CWD validated against parent's effective CWD using path.relative
- Unavailable tool calls: caught gracefully, shown as tool call with error badge, model retries
- UI: tab bar border radius, sidebar border removed, chat input ghost style, scroll-to-bottom rectangle
- Skills dir collapse uses CSS rotation, Model Choice renamed to Chat Settings, System Prompt view removed
- Reusable SkillsBrowser/ToolPermissions with external mode for Agent Builder
- ModelSelector: Agent/Manual toggle, agent list, Agent Settings link
- Page router, skills recursive scanning, bin/up gopass removed, docker volume mounts
Diffstat (limited to 'packages/frontend/src/lib/components/ConfigPanel.svelte')
| -rw-r--r-- | packages/frontend/src/lib/components/ConfigPanel.svelte | 177 |
1 files changed, 92 insertions, 85 deletions
diff --git a/packages/frontend/src/lib/components/ConfigPanel.svelte b/packages/frontend/src/lib/components/ConfigPanel.svelte index 25cbe98..f517d28 100644 --- a/packages/frontend/src/lib/components/ConfigPanel.svelte +++ b/packages/frontend/src/lib/components/ConfigPanel.svelte @@ -1,94 +1,101 @@ <script lang="ts"> - interface AgentTemplate { - name: string; - description?: string; - model_tag?: string; - tools?: string[]; +interface AgentTemplate { + name: string; + description?: string; + model_tag?: string; + tools?: string[]; +} + +interface ModelEntry { + id: string; + provider?: string; + tags?: string[]; +} + +interface KeyEntry { + id: string; + provider?: string; + status?: string; + lastError?: string; + exhaustedAt?: string; +} + +interface ConfigData { + agents?: Record<string, AgentTemplate>; + models?: Record<string, { provider?: string; tags?: string[] }>; + keys?: Record<string, { env?: string }>; + fallback?: string[]; + permissions?: Record<string, unknown>; +} + +interface ModelsData { + models?: ModelEntry[]; + tags?: Record<string, string[]>; + keys?: KeyEntry[]; +} + +const { apiBase }: { apiBase: string } = $props(); + +let configData = $state<ConfigData | null>(null); +let modelsData = $state<ModelsData | null>(null); +let error = $state<string | null>(null); +let loading = $state(false); + +async function fetchData() { + loading = true; + error = null; + try { + const [configRes, modelsRes] = await Promise.all([ + fetch(`${apiBase}/config`), + fetch(`${apiBase}/models`), + ]); + if (!configRes.ok) throw new Error(`/config returned ${configRes.status}`); + if (!modelsRes.ok) throw new Error(`/models returned ${modelsRes.status}`); + const configJson = await configRes.json(); + const modelsJson = await modelsRes.json(); + configData = configJson.config ?? configJson; + modelsData = modelsJson; + } catch (e) { + error = e instanceof Error ? e.message : String(e); + } finally { + loading = false; } +} - interface ModelEntry { - id: string; - provider?: string; - tags?: string[]; - } - - interface KeyEntry { - id: string; - provider?: string; - status?: string; - lastError?: string; - exhaustedAt?: string; - } - - interface ConfigData { - agents?: Record<string, AgentTemplate>; - models?: Record<string, { provider?: string; tags?: string[] }>; - keys?: Record<string, { env?: string }>; - fallback?: string[]; - permissions?: Record<string, unknown>; - } - - interface ModelsData { - models?: ModelEntry[]; - tags?: Record<string, string[]>; - keys?: KeyEntry[]; - } - - const { apiBase }: { apiBase: string } = $props(); - - let configData = $state<ConfigData | null>(null); - let modelsData = $state<ModelsData | null>(null); - let error = $state<string | null>(null); - let loading = $state(false); - - async function fetchData() { - loading = true; - error = null; - try { - const [configRes, modelsRes] = await Promise.all([ - fetch(`${apiBase}/config`), - fetch(`${apiBase}/models`), - ]); - if (!configRes.ok) throw new Error(`/config returned ${configRes.status}`); - if (!modelsRes.ok) throw new Error(`/models returned ${modelsRes.status}`); - const configJson = await configRes.json(); - const modelsJson = await modelsRes.json(); - configData = configJson.config ?? configJson; - modelsData = modelsJson; - } catch (e) { - error = e instanceof Error ? e.message : String(e); - } finally { - loading = false; - } - } - - $effect(() => { - fetchData(); - }); - - const modelCount = $derived(modelsData?.models?.length ?? 0); - const keyCount = $derived(modelsData?.keys?.length ?? 0); - - function formatDate(iso: string | undefined): string { - if (!iso) return ""; - try { - return new Date(iso).toLocaleString(); - } catch { - return iso; - } - } - - function permissionEntries(permissions: Record<string, unknown>): Array<{ name: string; value: unknown }> { - return Object.entries(permissions).map(([name, value]) => ({ name, value })); - } +$effect(() => { + fetchData(); +}); - function isSimpleRule(value: unknown): value is { action: string } { - return typeof value === "object" && value !== null && "action" in value && Object.keys(value).length === 1; - } +const modelCount = $derived(modelsData?.models?.length ?? 0); +const keyCount = $derived(modelsData?.keys?.length ?? 0); - function isPatternRule(value: unknown): value is Record<string, { action: string }> { - return typeof value === "object" && value !== null && !("action" in value); +function formatDate(iso: string | undefined): string { + if (!iso) return ""; + try { + return new Date(iso).toLocaleString(); + } catch { + return iso; } +} + +function permissionEntries( + permissions: Record<string, unknown>, +): Array<{ name: string; value: unknown }> { + return Object.entries(permissions).map(([name, value]) => ({ name, value })); +} + +function isSimpleRule(value: unknown): value is { action: string } { + return ( + typeof value === "object" && + value !== null && + "action" in value && + Object.keys(value).length === 1 + ); +} + +function isPatternRule(value: unknown): value is Record<string, { action: string }> { + return typeof value === "object" && value !== null && !("action" in value); +} </script> <details class="collapse collapse-arrow bg-base-200 mt-2"> |
