feat(electrobun): wire EVERYTHING — all settings persist, theme editor, marketplace

All settings wired to SQLite persistence:
- AgentSettings: shell, CWD, permissions, providers (JSON blob)
- SecuritySettings: branch policies (JSON array)
- ProjectSettings: per-project via setProject RPC
- OrchestrationSettings: wake, anchors, notifications
- AdvancedSettings: logging, OTLP, plugins, import/export JSON

Theme Editor:
- 26 color pickers (14 Accents + 12 Neutrals)
- Live CSS var preview as you pick colors
- Save custom theme to SQLite, cancel reverts
- Import/export theme as JSON
- Custom themes in dropdown with delete button

Extensions Marketplace:
- 8-plugin demo catalog (Browse/Installed tabs)
- Search/filter by name or tag
- Install/uninstall with SQLite persistence
- Plugin cards with emoji icons, tags, version

Terminal font hot-swap:
- fontStore.onTermFontChange() → xterm.js options update + fitAddon.fit()
- Resize notification to PTY daemon after font change

All 7 settings categories functional. Every control persists and takes effect.
This commit is contained in:
Hibryda 2026-03-20 05:45:10 +01:00
parent 6002a379e4
commit 5032021915
20 changed files with 1005 additions and 271 deletions

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { appRpc } from '../main.ts';
import { PROVIDER_CAPABILITIES, type ProviderId } from '../provider-capabilities';
const ANCHOR_SCALES = ['small', 'medium', 'large', 'full'] as const;
@ -6,7 +8,6 @@
const PROVIDERS = Object.keys(PROVIDER_CAPABILITIES) as ProviderId[];
// Demo projects
interface ProjectConfig {
id: string;
name: string;
@ -21,26 +22,12 @@
let projects = $state<ProjectConfig[]>([
{
id: 'p1',
name: 'agent-orchestrator',
provider: 'claude',
model: 'claude-opus-4-5',
useWorktrees: false,
useSandbox: false,
stallThreshold: 15,
anchorScale: 'medium',
customContext: '',
id: 'p1', name: 'agent-orchestrator', provider: 'claude', model: 'claude-opus-4-5',
useWorktrees: false, useSandbox: false, stallThreshold: 15, anchorScale: 'medium', customContext: '',
},
{
id: 'p2',
name: 'quanta-discord-bot',
provider: 'claude',
model: 'claude-sonnet-4-5',
useWorktrees: false,
useSandbox: false,
stallThreshold: 15,
anchorScale: 'medium',
customContext: '',
id: 'p2', name: 'quanta-discord-bot', provider: 'claude', model: 'claude-sonnet-4-5',
useWorktrees: false, useSandbox: false, stallThreshold: 15, anchorScale: 'medium', customContext: '',
},
]);
@ -49,11 +36,26 @@
function updateProj(patch: Partial<ProjectConfig>) {
projects = projects.map(p => p.id === selectedId ? { ...p, ...patch } : p);
const updated = projects.find(p => p.id === selectedId)!;
appRpc?.request['settings.setProject']({
id: selectedId,
config: JSON.stringify(updated),
}).catch(console.error);
}
onMount(async () => {
if (!appRpc) return;
const res = await appRpc.request['settings.getProjects']({}).catch(() => ({ projects: [] }));
if (res.projects.length > 0) {
const loaded: ProjectConfig[] = res.projects.flatMap(({ config }) => {
try { return [JSON.parse(config) as ProjectConfig]; } catch { return []; }
});
if (loaded.length > 0) projects = loaded;
}
});
</script>
<div class="section">
<!-- Project selector -->
<h3 class="sh">Project</h3>
<div class="proj-tabs">
{#each projects as p}