cargo fmt --check ✓
cargo clippy --workspace --all-targets -- -D warnings ✓
cargo test --workspace ✓
cargo nextest run --workspace ✓ 435/435
npm run check ✓ 0/0/5704 files
npm test ✓ 13/13 (2 files)
npm run test:browser ✓ 3/3 in Chromium
npm run test:e2e ✓ 16/16 × 2 viewports
npm run analyze ✓ reports/bundle-stats.html (1.7 MB)
scripts/dogfood-rebrand-drill.sh ✓ 8/8 (sandbox)
npm run guard:no-skeleton ✓ clean
Two small Phase 8 corrections landed alongside the gate:
vite.config.js: the jsdom suite was picking up
`src/lib/ui/*.browser.test.ts`, which only works under the
@vitest/browser-playwright provider. Added the browser-suffix glob to
the jsdom suite's exclude list so the two runners stop double-running
the same files.
playwright.config.ts: bumped the global `expect.timeout` to 15 s. The
cold first-compile of the Vite SPA tree was exceeding Playwright's
default 5 s `toBeVisible` timeout on the 900x700 project on dogfood
runs. The 1440x900 project (which runs second after a warm cache)
never hit it.
Phase 8 closes Phase-7 page migrations. Branch is ready for the
finishing-a-development-branch handoff.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Combined commit for the three secondary windows. Each +layout@.svelte
was already migrated in Phase 3 KI-05 (the legacy theme-sync $effect
was deleted). The +page.svelte content for each window is explicitly
bespoke per docs/release/v0.2-frontend-overhaul.md §6.3:
/float — Lumotia-To-do panel: list pills, drag-and-drop between
lists, context menus, pin-on-top, custom titlebar drag
region. No Card/EmptyState/Toggle wrappers apply.
/viewer — Transcript viewer with audio player, segment scrubbing,
speaker labels. Bionic-reading action + per-region
accessibility typography are load-bearing. No wrappers.
/preview — Wayland-hardened transcription preview overlay.
WindowTypeHint::Utility, never steals focus, hidden
from Alt+Tab. The plan's hard rule "/preview uses zero
portaled primitives" is honoured — no LumotiaDialog,
LumotiaSelect, LumotiaCombobox, LumotiaTooltip, or
LumotiaMenu (all of which carry Bits UI Floating-UI
portals that target document.body).
Phase 7 closes here. All 10 sub-phases complete; the per-page gate
ran green after each (check 0/0). Full Phase 8 gate fires next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src/routes/+layout.svelte was 537 LOC of mixed runtime, chrome and
overlay concerns. Split into three single-purpose shells under
src/lib/shell/, with +layout.svelte reduced to ~28 LOC of pure
composition.
AppRuntime (no DOM beyond <svelte:window>):
- Global hotkey dual backend (evdev / tauri-plugin-global-shortcut)
- 120ms hotkey debounce (sacred behaviour §5 #2)
- PREFERENCES_CHANGED_EVENT listener (sacred §5 #4)
- KI-05 one-shot legacy-theme migration
- Sidebar hotkeys: [ toggle, Ctrl+K, Ctrl+, (sacred §5 #10)
- Wind-down tray listener
- Meeting auto-capture poller
- Global frontend error capture
- Nudge bus + implementation intentions lifecycle
- Font-size CSS var $effect
- Window resize → sidebar auto-collapse
- Onboarding/first-run check + update check + LLM status warm-up
AppChrome (the visual shell):
- Titlebar (OS-aware via customChrome helper)
- Sidebar (recording-state-aware — sacred §5 #1 stays in
Sidebar.svelte verbatim)
- Main slot
- TaskSidebar conditional rail
AppOverlays (mounted-once globals):
- ToastViewport
- FocusTimer
- MorningTriageModal
- ResizeHandles (OS-gated)
src/lib/utils/customChrome.svelte.ts holds the single source of truth
for useCustomChrome, a module-level $state both AppChrome and
AppOverlays subscribe to. Each only ever sees one loadOsInfo() call
between them.
Secondary windows still escape via their own +layout@.svelte; the
defensive isSecondaryWindow check in +layout.svelte stays so a
direct /float, /viewer, /preview navigation through the root layout
also drops the chrome.
Phase 6 per-page gate green: npm run check (0/0/5704 files),
npm test, npm run test:browser (3/3), npm run test:e2e (16/16).
No regressions in the Phase 1 smoke baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six thin alias wrappers, same prop APIs as the underlying components.
Lets pages migrate imports from $lib/components/* to $lib/ui/* one
file at a time without touching markup, and gives Phase 5+ a place
to tighten grammar without churning every call site.
LumotiaCard → Card.svelte
LumotiaStatusPill → StatusPill.svelte
LumotiaToggle → Toggle.svelte (forwards bind:checked, bind:loading)
LumotiaSettingsGroup → SettingsGroup.svelte (typed Props for svelte-check)
LumotiaEmptyState → EmptyState.svelte
LumotiaPostCaptureCard → PostCaptureCard.svelte
Per the plan, the underlying components in src/lib/components/ are
untouched. They get retired during the per-page migrations in Phase 7
once no consumer remains.
LumotiaSettingsGroup mirrors the underlying Props interface explicitly
because Svelte 5's spread-into-typed-component caught a real missing-
`title` error during svelte-check. The mirrored interface keeps call
sites type-safe when importing via $lib/ui/.
Phase 4 per-phase gate green: npm run check (0/0/4135 files),
npm test (all green), npm run test:e2e (16/16), npm run
guard:no-skeleton (clean).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Additive token grammar (no renames, no replacements):
- --color-caution (dark #e8be4a, light #a08a1f) becomes the canonical
name for tuned-amber notice surfaces in the new wrapper grammar
- --color-warning is kept as a CSS var() alias of --color-caution so
every existing text-warning / bg-warning call site stays valid
- --color-info (dark #7a9ec0, light #3d6a8a) is the soft blue-grey
signal for the new LumotiaNotice info variant
- --color-accent-environment (dark #8fae9a, light #4a7058) is an
optional sage/moss support token for empty-state illustrations
and environment-neutral status dots. NOT a brand swap — amber/
copper --color-accent stays primary
Mirrored in src/design-system/colors_and_type.css (the buildless
preview pages bypass Tailwind so the duplication is intentional).
KI-05 resolved in the same commit, per the plan:
- src/lib/types/app.ts: drop `theme` from SettingsState
- src/lib/stores/page.svelte.ts: drop `theme: "Dark"` from defaults
- src/routes/+layout.svelte: drop the migration $effect, add a
one-shot migrateLegacyTheme() on mount that copies any historical
lumotia_settings.theme into preferences.theme and strips the
legacy field. Idempotent — subsequent loads short-circuit
- src/routes/{float,viewer,preview}/+layout@.svelte: drop the same
$effect; secondary windows inherit theme via PREFERENCES_CHANGED_EVENT
- src/lib/pages/SettingsPage.svelte: both SegmentedButton bindings
(quick-settings row at :1272, Appearance group at :2606) now use
Svelte 5 function bindings ({ get, set }) backed by prefs.theme
and updatePreferences. No SegmentedButton API change
Phase 3 per-page gate green: npm run check (0/0/4129 files),
npm test (13/13), npm run test:e2e (16/16). No regressions in
the Phase 1 smoke baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 — docs/release/v0.2-frontend-overhaul.md as single source of
truth for the v0.2 frontend coherence pass. Records hard rules,
tooling pins, sacred-behaviour contract list, wrapper catalogue,
per-page migration order, verification matrix, KI-05 plan, and the
explicit "DO NOT add Skeleton" line.
Phase 1 — tooling baseline. All exact-pinned per the plan:
- @playwright/test@1.60.0 + playwright@1.60.0 + @axe-core/playwright@4.11.3
- rollup-plugin-visualizer@7.0.1 (wired behind ANALYZE=1)
- @vitest/browser@4.1.6 + @vitest/browser-playwright@4.1.6 (provider)
- vitest-browser-svelte@2.1.1 (runes-aware Svelte 5 bridge)
- cargo-nextest installed globally
New configs: playwright.config.ts (frontend-only, dev:frontend webServer,
900x700 + 1440x900 projects, visual baselines deferred), vitest.browser.config.js
(separate from jsdom suite). New scripts: test:e2e, test:e2e:ui,
test:browser, analyze, test:rust:fast, guard:no-skeleton.
guard-no-skeleton.mjs walks package.json + package-lock + src/ for any
@skeletonlabs reference and exits 1 if found — locks in the no-Skeleton
hard rule for any future agent.
Smoke baseline (tests/e2e/smoke.spec.ts): 16 tests passing across two
viewports — app loads without Tauri runtime, keyboard nav, axe scan
(color-contrast deferred to Phase 7 per docs/release/v0.1-contrast-audit.md),
light/dark theme cycle, all three sensory zones (cave/energy/reset).
10 screenshots emitted to test-results/ as non-failing artefacts.
Surfaced + fixed two browser-preview bugs while landing the baseline:
- src/lib/utils/osInfo.ts: FALLBACK_BROWSER_INFO was evaluated at SSR
module-load (navigator undefined → os: 'unknown'), and the UA check
ran before navigator.platform — so Playwright's Windows-UA Chromium
on a Linux runner detected as Windows, useCustomChrome went true,
Titlebar mounted and tripped Tauri-only APIs. Now lazy-built per
call; platform is the primary signal, UA only a fallback.
- src/lib/components/Titlebar.svelte: defensive hasTauriRuntime() guard
on every handler and the $effect. Titlebar should not crash if any
future code path mounts it without Tauri.
.gitignore: reports/, .playwright/, test-results/, playwright-report/.
Per-phase gate green: npm run check (0/0), npm test (0/0), npm run
test:e2e (16/16), npm run guard:no-skeleton (clean).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>