diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 14:55:45 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 14:55:45 +0900 |
| commit | 4b1b920e8bc557a592f505a4619062b1067a233e (patch) | |
| tree | 533a02dc7fca2611f01a6c4eb615c6945db0b176 /src/features/views/ui/ViewSidebar.svelte | |
| parent | ce1321eccf24e0923642ae2ef74f6220ecd7f75a (diff) | |
| download | dispatch-web-4b1b920e8bc557a592f505a4619062b1067a233e.tar.gz dispatch-web-4b1b920e8bc557a592f505a4619062b1067a233e.zip | |
feat: persist sidebar layout + open/closed state between refreshes
Sidebar panel layout (which views are open and their order) and the
sidebar open/closed toggle are now persisted to localStorage. Default
layout is just the Model view at the top.
- ViewSidebar accepts an onChange callback that reports panel kinds
- App.svelte creates two createLocalStore instances (dispatch.sidebar.views
+ dispatch.sidebar.open) using the store's storage adapter
- AppStore exposes its storage instance so the shell persists via the
same adapter (test-injectable, not globalThis.localStorage)
- Tests pre-populate fake storage with ["extensions"] for the 4 tests
that need the Extensions view visible
686 tests green. 0 svelte-check warnings (2 pre-existing errors from
missing transport-contract exports, unchanged).
Diffstat (limited to 'src/features/views/ui/ViewSidebar.svelte')
| -rw-r--r-- | src/features/views/ui/ViewSidebar.svelte | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/src/features/views/ui/ViewSidebar.svelte b/src/features/views/ui/ViewSidebar.svelte index c4b466f..e4a3ee6 100644 --- a/src/features/views/ui/ViewSidebar.svelte +++ b/src/features/views/ui/ViewSidebar.svelte @@ -17,6 +17,7 @@ kinds, content, initial, + onChange, }: { /** The view kinds offered in every panel's dropdown. */ kinds: readonly ViewKind[]; @@ -24,6 +25,8 @@ content: Snippet<[string]>; /** Optional seed of panel kinds; defaults to one panel of the first kind. */ initial?: readonly (string | null)[]; + /** Called whenever the panel layout changes (add/remove/select). */ + onChange?: (kinds: readonly (string | null)[]) => void; } = $props(); // Local UI composition state, owned by this unit and folded through the pure @@ -32,6 +35,10 @@ let state = $state<PanelsState>( untrack(() => initialPanels(initial ?? [kinds[0]?.id ?? null])), ); + + function notify(): void { + onChange?.(state.panels.map((p) => p.kind)); + } </script> <div class="flex min-h-0 flex-col gap-2"> @@ -45,6 +52,7 @@ onchange={(e) => { const v = e.currentTarget.value; state = selectKind(state, panel.id, v === "" ? null : v); + notify(); }} > <option value="" disabled>Select a view</option> @@ -59,6 +67,7 @@ aria-label="Remove view" onclick={() => { state = removePanel(state, panel.id); + notify(); }} > ✕ @@ -80,6 +89,7 @@ aria-label="Add view" onclick={() => { state = addPanel(state); + notify(); }} > + |
