v0.3 Phase 4a: Settings tab shell

Discovers that v0.2 SettingsPage.svelte was already structured as
eight numbered sections aligning 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.

Changes.

  - NEW: src/lib/ui/LumotiaSettingsTabs.svelte. Tab list following
    the WAI-ARIA "tabs (automatic activation)" pattern. Arrow / Home /
    End keyboard navigation. Active tab uses the brand left-bar
    accent (border-l-caution) — same grammar as the LumotiaNotice
    refactor from Phase 3.

  - MODIFIED: src/lib/pages/SettingsPage.svelte. Added an isQuietware
    reactive flag, a MutationObserver watching <html data-design>,
    an activeTab state, and eight {#if !isQuietware || activeTab ===
    'X'} wrappers around the existing top-level SettingsGroup
    sections. The settings tree is unchanged; only its top-level
    rendering gate changed.

  - v0.2 fallback: when data-design="quietware" is absent the
    tablist does not render and all eight sections stack as before.
    The search filter spans every section in v0.2 mode. 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 the plan's
canonical names (carving Output from Advanced and Vocabulary from
Transcription).

Phases 4b and 4c are logged as pending in the plan doc:
  4b. Restructure to canonical tab names (Output, Vocabulary).
  4c. Texture-opacity slider + high-contrast toggle in Accessibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 13:22:13 +01:00
parent db9c50c548
commit 091780086b
3 changed files with 182 additions and 0 deletions

View File

@@ -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 <html>) 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;
});
</script>
<div class="flex flex-col h-full overflow-y-auto animate-fade-in">
@@ -1253,6 +1290,18 @@
</button>
{/if}
</div>
{#if isQuietware}
<!-- v0.3 Phase 4a — tab nav renders only when quietware is active.
v0.2 behaviour (stacked sections) is unchanged when the flag
is off. -->
<div class="pt-3">
<SettingsTabs
tabs={settingsTabs}
activeId={activeTab}
onSelect={(id) => (activeTab = id)}
/>
</div>
{/if}
</div>
<div class="px-7 pt-5 pb-8">
@@ -1298,6 +1347,7 @@
</div>
</div>
{#if !isQuietware || activeTab === 'start-here'}
<!-- =====================================================
1. Start Here — microphone, language, engine
===================================================== -->
@@ -1429,7 +1479,9 @@
</div>
</div>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'transcription'}
<!-- =====================================================
2. Transcription — format mode, cleanup, vocabulary
===================================================== -->
@@ -1837,7 +1889,9 @@
</div>
</SettingsGroup>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'models'}
<!-- =====================================================
3. Models — download, switch, disk-space readout
===================================================== -->
@@ -2055,7 +2109,9 @@
</div>
</div>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'tasks'}
<!-- =====================================================
4. Tasks — WIP limit, energy-aware sequencing
===================================================== -->
@@ -2099,7 +2155,9 @@
</div>
</div>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'accessibility'}
<!-- =====================================================
5. Accessibility — motion, contrast, typography
===================================================== -->
@@ -2112,7 +2170,9 @@
<AccessibilityControls />
</div>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'privacy'}
<!-- =====================================================
6. Privacy — local-only badge, AI disclosure, activation log
===================================================== -->
@@ -2197,7 +2257,9 @@
</div>
</div>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'advanced'}
<!-- =====================================================
7. Advanced — everything else, collapsed by default
===================================================== -->
@@ -2743,8 +2805,10 @@
</div>
</SettingsGroup>
</SettingsGroup>
{/if}
{#if !isQuietware || activeTab === 'help'}
<!-- 8. Help — first-run tutorial replay + support links. -->
<SettingsGroup
title="Help"
@@ -2799,6 +2863,7 @@
</div>
</div>
</SettingsGroup>
{/if}
</Card>
</div>
</div>