v0.2 Phase 4: wrapper alias layer (src/lib/ui/)

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>
This commit is contained in:
2026-05-15 08:38:23 +01:00
parent 66e25aa778
commit 8c9708a508
7 changed files with 96 additions and 7 deletions

View File

@@ -85,12 +85,12 @@ Filled progressively during Phases 45. Wrappers live in `src/lib/ui/`; bespok
| Wrapper | Wraps | Status | | Wrapper | Wraps | Status |
|---|---|---| |---|---|---|
| `LumotiaCard` | `Card.svelte` | pending | | `LumotiaCard` | `Card.svelte` | ✅ Phase 4 |
| `LumotiaStatusPill` | `StatusPill.svelte` | pending | | `LumotiaStatusPill` | `StatusPill.svelte` | ✅ Phase 4 |
| `LumotiaToggle` | `Toggle.svelte` | pending | | `LumotiaToggle` | `Toggle.svelte` | ✅ Phase 4 |
| `LumotiaSettingsGroup` | `SettingsGroup.svelte` | pending | | `LumotiaSettingsGroup` | `SettingsGroup.svelte` | ✅ Phase 4 |
| `LumotiaEmptyState` | `EmptyState.svelte` | pending | | `LumotiaEmptyState` | `EmptyState.svelte` | ✅ Phase 4 |
| `LumotiaPostCaptureCard` | `PostCaptureCard.svelte` | pending | | `LumotiaPostCaptureCard` | `PostCaptureCard.svelte` | ✅ Phase 4 |
### 6.2 Phase 5 new primitives ### 6.2 Phase 5 new primitives
@@ -201,7 +201,7 @@ Filled during execution. Each entry: phase #, date, what landed, what's next.
| 1 | ✅ complete | Tooling baseline green: `npm run check` clean, `npm test` clean, `npm run test:e2e` 16/16, `npm run guard:no-skeleton` clean, 10 screenshot artefacts. Browser-preview OS detection fixed (see Regression diary). | | 1 | ✅ complete | Tooling baseline green: `npm run check` clean, `npm test` clean, `npm run test:e2e` 16/16, `npm run guard:no-skeleton` clean, 10 screenshot artefacts. Browser-preview OS detection fixed (see Regression diary). |
| 2 | ✅ complete | bits-ui 2.18.1, formsnap 2.0.1, sveltekit-superforms 2.30.1, zod 4.4.3, @internationalized/date 3.12.1. `npm audit signatures` 273 verified + 93 attestations. | | 2 | ✅ complete | bits-ui 2.18.1, formsnap 2.0.1, sveltekit-superforms 2.30.1, zod 4.4.3, @internationalized/date 3.12.1. `npm audit signatures` 273 verified + 93 attestations. |
| 3 | ✅ complete | New tokens `--color-caution`, `--color-info`, `--color-accent-environment` (dark + light), `--color-warning` aliased to `var(--color-caution)`. KI-05 resolved: `theme` dropped from SettingsState type + defaults, all four route-layout migration `$effect`s deleted, two SettingsPage SegmentedButton bindings repointed to `preferences.theme` via Svelte 5 function bindings, one-shot legacy-theme migration on first mount strips the field after copying. Gate green (check 0/0, vitest 13/13, e2e 16/16). | | 3 | ✅ complete | New tokens `--color-caution`, `--color-info`, `--color-accent-environment` (dark + light), `--color-warning` aliased to `var(--color-caution)`. KI-05 resolved: `theme` dropped from SettingsState type + defaults, all four route-layout migration `$effect`s deleted, two SettingsPage SegmentedButton bindings repointed to `preferences.theme` via Svelte 5 function bindings, one-shot legacy-theme migration on first mount strips the field after copying. Gate green (check 0/0, vitest 13/13, e2e 16/16). |
| 4 | pending | Wrapper alias layer | | 4 | ✅ complete | Six wrapper aliases under `src/lib/ui/`: Lumotia{Card,StatusPill,Toggle,SettingsGroup,EmptyState,PostCaptureCard}. Same prop APIs, $bindable forwarded for Toggle. Underlying `src/lib/components/*.svelte` untouched. |
| 5 | pending | New primitives + design-system-v2 preview | | 5 | pending | New primitives + design-system-v2 preview |
| 6 | pending | Shell split | | 6 | pending | Shell split |
| 7.1 ShutdownRitualPage | pending | | | 7.1 ShutdownRitualPage | pending | |

View File

@@ -0,0 +1,13 @@
<script lang="ts">
// v0.2 Phase 4 alias wrapper around $lib/components/Card.svelte.
// Same prop API; exists so pages can migrate imports progressively
// and so Phase 5+ can tighten grammar without churning every call
// site. Tone (default | subtle | danger) and elevation (flat | raised)
// pass through unchanged.
import Card from "$lib/components/Card.svelte";
const { children, ...rest } = $props();
</script>
<Card {...rest}>
{#if children}{@render children()}{/if}
</Card>

View File

@@ -0,0 +1,9 @@
<script>
// v0.2 Phase 4 alias wrapper around $lib/components/EmptyState.svelte.
// Same props: icon (lucide-svelte component), headline, message,
// actionLabel, onAction.
import EmptyState from "$lib/components/EmptyState.svelte";
const props = $props();
</script>
<EmptyState {...props} />

View File

@@ -0,0 +1,11 @@
<script lang="ts">
// v0.2 Phase 4 alias wrapper around $lib/components/PostCaptureCard.svelte.
// Identity surface (raw + cleaned transcript, extracted tasks, MicroSteps
// reveal). NOT in the bespoke do-not-wrap list because the card's
// surrounding chrome — buttons, headers, notices — will migrate to the
// new grammar; the transcript+task surfaces inside it stay verbatim.
import PostCaptureCard from "$lib/components/PostCaptureCard.svelte";
const props = $props();
</script>
<PostCaptureCard {...props} />

View File

@@ -0,0 +1,21 @@
<script lang="ts">
// v0.2 Phase 4 alias wrapper around $lib/components/SettingsGroup.svelte.
// Props mirror the underlying component so call sites get the same
// type checking when they import from $lib/ui/.
import SettingsGroup from "$lib/components/SettingsGroup.svelte";
import type { Component, Snippet } from "svelte";
interface Props {
title: string;
description?: string;
open?: boolean;
onopen?: () => void;
icon?: Component<{ size?: number; class?: string; "aria-hidden"?: boolean | "true" | "false" }>;
children?: Snippet;
}
let { title, description, open, onopen, icon, children }: Props = $props();
</script>
<SettingsGroup {title} {description} {open} {onopen} {icon}>
{#if children}{@render children()}{/if}
</SettingsGroup>

View File

@@ -0,0 +1,11 @@
<script lang="ts">
// v0.2 Phase 4 alias wrapper around $lib/components/StatusPill.svelte.
// Same Status union (ready | recording | paused | transcribing |
// cleaning | extracting-tasks | saved | exported | needs-review |
// failed-safely). prefers-reduced-motion honoured by the underlying
// component.
import StatusPill from "$lib/components/StatusPill.svelte";
const props = $props();
</script>
<StatusPill {...props} />

View File

@@ -0,0 +1,24 @@
<script>
// v0.2 Phase 4 alias wrapper around $lib/components/Toggle.svelte.
// checked and loading are forwarded as $bindable so call sites
// keep their existing bind:checked / bind:loading patterns.
import Toggle from "$lib/components/Toggle.svelte";
let {
checked = $bindable(false),
loading = $bindable(false),
label = "",
description = "",
disabled = false,
onChange = undefined,
} = $props();
</script>
<Toggle
bind:checked
bind:loading
{label}
{description}
{disabled}
{onChange}
/>