summaryrefslogtreecommitdiffhomepage
path: root/src/features/views
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-22 14:55:45 +0900
committerAdam Malczewski <[email protected]>2026-06-22 14:55:45 +0900
commitb7ea4b7325c02bf29046ab232411c053b36a99bd (patch)
treee0fc862f03a20fe070e28831d59a3450e7963214 /src/features/views
parent0ab13155b0d32a6062797b3f3da1c093b30cc9f0 (diff)
downloaddispatch-web-b7ea4b7325c02bf29046ab232411c053b36a99bd.tar.gz
dispatch-web-b7ea4b7325c02bf29046ab232411c053b36a99bd.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')
-rw-r--r--src/features/views/ui/ViewSidebar.svelte10
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();
}}
>
+