diff --git a/docs/release/v0.3-tactile-quietware.md b/docs/release/v0.3-tactile-quietware.md index 19c77df..a7d79b5 100644 --- a/docs/release/v0.3-tactile-quietware.md +++ b/docs/release/v0.3-tactile-quietware.md @@ -220,6 +220,30 @@ Architectural finding: both `LumotiaStatusPill` and `LumotiaNotice` already ship - All four tones (info / caution / danger / success) verified by the pattern itself: the visible signal is the left bar, which is `border-l-{tone}` at 100% opacity, working at any colour intensity. Caution remains a fill-only convention on cream by design. +### Phase 4a — Settings tab shell. Landed 2026-05-15. + +Architectural finding: the v0.2 `SettingsPage.svelte` (2 891 LOC after Phase 4a) was already structured as eight numbered sections that align almost 1:1 with the v0.3 plan. The cheapest path to "one section visible at a time" is therefore a tab-shell overlay rather than a file split. Behaviour stays identical to v0.2 when quietware is off; with quietware on, only the active tab's section renders. + +- New `LumotiaSettingsTabs.svelte` ([src/lib/ui/LumotiaSettingsTabs.svelte](../../src/lib/ui/LumotiaSettingsTabs.svelte)) implements the WAI-ARIA tabs (automatic activation) pattern. Arrow / Home / End keyboard navigation. Active tab uses the brand left-bar accent (`border-l-caution`) so it carries the same grammar as the LumotiaNotice refactor. + +- `SettingsPage.svelte` gained a small reactive `isQuietware` flag, a `MutationObserver` watching ``, and 8 `{#if !isQuietware || activeTab === 'X'}` wrappers around the existing eight top-level `` sections. The settings tree is unchanged; only its top-level rendering gate changed. + +- v0.2 fallback behaviour: when `data-design="quietware"` is absent the tablist does not render and all eight sections stack as before. The search filter still spans every section in v0.2 mode. This is the smallest-possible behavioural change for "one section at a time". + +- Tab IDs in this commit map 1:1 to existing v0.2 section names (start-here / transcription / models / tasks / accessibility / privacy / advanced / help). Phase 4b will restructure to match the plan's canonical names (carving Output from Advanced and Vocabulary from Transcription). + +### Phase 4b — Settings restructure to canonical tab names. Pending. + +- Carve **Output** out of `7. Advanced > Output & Capture` (currently at line 2398 in the pre-Phase-4a file) into its own top-level tab. +- Carve **Vocabulary** out of `2. Transcription > Custom vocabulary` (line 1472 in the pre-Phase-4a file) into its own top-level tab. +- Rename the remainder of `2. Transcription` to be subsumed by **Output** + **Vocabulary**, or kept as legacy section pending content audit. +- Final tab order: Start Here / Models / Output / Vocabulary / Tasks / Accessibility / Privacy / Advanced (eight tabs, matching the plan). + +### Phase 4c — Texture-opacity slider + high-contrast toggle. Pending. + +- Texture-opacity slider in the Accessibility tab, range 0.05–0.08, default 0.06. Persists to existing preferences store. +- High-contrast toggle in the Accessibility tab. Toggle overrides system `prefers-contrast: more` query when set. + --- ## Regression diary diff --git a/src/lib/pages/SettingsPage.svelte b/src/lib/pages/SettingsPage.svelte index 96a4b5f..e794b5d 100644 --- a/src/lib/pages/SettingsPage.svelte +++ b/src/lib/pages/SettingsPage.svelte @@ -10,6 +10,7 @@ import HotkeyRecorder from "$lib/components/HotkeyRecorder.svelte"; import ImplementationRulesEditor from "$lib/components/ImplementationRulesEditor.svelte"; import SettingsGroup from "$lib/ui/LumotiaSettingsGroup.svelte"; + import SettingsTabs from "$lib/ui/LumotiaSettingsTabs.svelte"; import StatusPill from "$lib/ui/LumotiaStatusPill.svelte"; import ZonePicker from "$lib/components/ZonePicker.svelte"; import AccessibilityControls from "$lib/components/AccessibilityControls.svelte"; @@ -1212,6 +1213,42 @@ saveTemplates(); editingTemplate = -1; } + + // v0.3 Phase 4a — sectional tab nav. The full 2 791 LOC settings tree + // stays in this file; quietware mode shows ONE top-level section at a + // time via {#if !isQuietware || activeTab === 'X'} wrappers below. + // Without quietware (data-design unset on ) the page renders + // exactly as v0.2 did (all eight sections stacked, search filter + // active across all of them). + let isQuietware = $state(false); + let activeTab = $state("start-here"); + const settingsTabs = [ + { id: "start-here", label: "Start Here" }, + { id: "transcription", label: "Transcription" }, + { id: "models", label: "Models" }, + { id: "tasks", label: "Tasks" }, + { id: "accessibility", label: "Accessibility" }, + { id: "privacy", label: "Privacy" }, + { id: "advanced", label: "Advanced" }, + { id: "help", label: "Help" }, + ]; + let quietwareObserver: MutationObserver | null = null; + onMount(() => { + if (typeof document === "undefined") return; + const sync = () => { + isQuietware = document.documentElement.dataset.design === "quietware"; + }; + sync(); + quietwareObserver = new MutationObserver(sync); + quietwareObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-design"], + }); + }); + onDestroy(() => { + quietwareObserver?.disconnect(); + quietwareObserver = null; + });
@@ -1253,6 +1290,18 @@ {/if}
+ {#if isQuietware} + +
+ (activeTab = id)} + /> +
+ {/if}
@@ -1298,6 +1347,7 @@
+ {#if !isQuietware || activeTab === 'start-here'} @@ -1429,7 +1479,9 @@
+ {/if} + {#if !isQuietware || activeTab === 'transcription'} @@ -1837,7 +1889,9 @@ + {/if} + {#if !isQuietware || activeTab === 'models'} @@ -2055,7 +2109,9 @@ + {/if} + {#if !isQuietware || activeTab === 'tasks'} @@ -2099,7 +2155,9 @@ + {/if} + {#if !isQuietware || activeTab === 'accessibility'} @@ -2112,7 +2170,9 @@ + {/if} + {#if !isQuietware || activeTab === 'privacy'} @@ -2197,7 +2257,9 @@ + {/if} + {#if !isQuietware || activeTab === 'advanced'} @@ -2743,8 +2805,10 @@ + {/if} + {#if !isQuietware || activeTab === 'help'} + {/if} diff --git a/src/lib/ui/LumotiaSettingsTabs.svelte b/src/lib/ui/LumotiaSettingsTabs.svelte new file mode 100644 index 0000000..78e8696 --- /dev/null +++ b/src/lib/ui/LumotiaSettingsTabs.svelte @@ -0,0 +1,93 @@ + + +
+ {#each tabs as tab, i (tab.id)} + {@const isActive = tab.id === activeId} + + {/each} +