v0.2 Phase 7.3: FirstRunPage — wrapper sweep

Migrated the onboarding step cluster's ad-hoc button pairs to the new
grammar. Skip-link tertiary buttons stay as plain <button> with underline
because they're intentionally low-emphasis (text-only).

  - Step CTA buttons (primary + secondary) → LumotiaButton variants
  - Autostart "Saving…" pair → LumotiaButton loading + disabled
  - Error notice → LumotiaNotice tone=danger with body content snippet
  - Download progress bar → LumotiaProgress

Bespoke surfaces left verbatim: model-pick cards (rich content tiles
with Recommended/Downloaded pills), UnicodeSpinner, and the
test-recording quote-block.

Per-page gate: npm run check (0/0/5704 files). e2e baseline untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 08:55:59 +01:00
parent d812410039
commit cb69772f4e

View File

@@ -6,6 +6,9 @@
import UnicodeSpinner from "$lib/components/UnicodeSpinner.svelte"; import UnicodeSpinner from "$lib/components/UnicodeSpinner.svelte";
import { toasts } from "$lib/stores/toasts.svelte.js"; import { toasts } from "$lib/stores/toasts.svelte.js";
import { Download, CheckCircle, Sunrise, Moon, Play, Mic } from 'lucide-svelte'; import { Download, CheckCircle, Sunrise, Moon, Play, Mic } from 'lucide-svelte';
import LumotiaButton from "$lib/ui/LumotiaButton.svelte";
import LumotiaNotice from "$lib/ui/LumotiaNotice.svelte";
import LumotiaProgress from "$lib/ui/LumotiaProgress.svelte";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Onboarding event helpers (best-effort — failures are logged, never blocking) // Onboarding event helpers (best-effort — failures are logged, never blocking)
@@ -261,11 +264,9 @@
The main dictation screen is just a button press away. Try your first real recording there — it's the same experience you'll use every day. The main dictation screen is just a button press away. Try your first real recording there — it's the same experience you'll use every day.
</p> </p>
<div class="flex items-center justify-center gap-3 mt-6"> <div class="flex items-center justify-center gap-3 mt-6">
<button <LumotiaButton variant="primary" onclick={deferTestToMainUi}>
class="px-4 py-2 rounded-lg text-sm bg-accent btn-filled-text hover:bg-accent-hover" Open the main app and try recording there
style="transition-duration: var(--duration-ui)" </LumotiaButton>
onclick={deferTestToMainUi}
>Open the main app and try recording there</button>
</div> </div>
<button <button
class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline" class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline"
@@ -290,14 +291,8 @@
</p> </p>
<p class="text-[12px] text-text-secondary mt-3">Off by default. You can change your mind any time in Settings.</p> <p class="text-[12px] text-text-secondary mt-3">Off by default. You can change your mind any time in Settings.</p>
<div class="flex items-center justify-center gap-3 mt-6"> <div class="flex items-center justify-center gap-3 mt-6">
<button <LumotiaButton variant="secondary" onclick={() => answerMorning(false)}>No thanks</LumotiaButton>
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover" <LumotiaButton variant="primary" onclick={() => answerMorning(true)}>Yes, turn it on</LumotiaButton>
onclick={() => answerMorning(false)}
>No thanks</button>
<button
class="px-4 py-2 rounded-lg text-sm bg-accent btn-filled-text hover:bg-accent-hover"
onclick={() => answerMorning(true)}
>Yes, turn it on</button>
</div> </div>
<button <button
class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline" class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline"
@@ -314,14 +309,8 @@
</p> </p>
<p class="text-[12px] text-text-secondary mt-3">Off by default. Always opt-in.</p> <p class="text-[12px] text-text-secondary mt-3">Off by default. Always opt-in.</p>
<div class="flex items-center justify-center gap-3 mt-6"> <div class="flex items-center justify-center gap-3 mt-6">
<button <LumotiaButton variant="secondary" onclick={() => answerEvening(false)}>No thanks</LumotiaButton>
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover" <LumotiaButton variant="primary" onclick={() => answerEvening(true)}>Yes, turn it on</LumotiaButton>
onclick={() => answerEvening(false)}
>No thanks</button>
<button
class="px-4 py-2 rounded-lg text-sm bg-accent btn-filled-text hover:bg-accent-hover"
onclick={() => answerEvening(true)}
>Yes, turn it on</button>
</div> </div>
<button <button
class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline" class="mt-5 text-xs text-text-tertiary hover:text-text-secondary underline"
@@ -338,16 +327,17 @@
</p> </p>
<p class="text-[12px] text-text-secondary mt-3">You can change this any time in Settings.</p> <p class="text-[12px] text-text-secondary mt-3">You can change this any time in Settings.</p>
<div class="flex items-center justify-center gap-3 mt-6"> <div class="flex items-center justify-center gap-3 mt-6">
<button <LumotiaButton
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover" variant="secondary"
disabled={autostartApplying}
onclick={() => answerAutostart(false)} onclick={() => answerAutostart(false)}
>No thanks</LumotiaButton>
<LumotiaButton
variant="primary"
loading={autostartApplying}
disabled={autostartApplying} disabled={autostartApplying}
>No thanks</button>
<button
class="px-4 py-2 rounded-lg text-sm bg-accent text-white hover:bg-accent-hover disabled:opacity-60"
onclick={() => answerAutostart(true)} onclick={() => answerAutostart(true)}
disabled={autostartApplying} >{autostartApplying ? 'Saving…' : 'Yes, launch at login'}</LumotiaButton>
>{autostartApplying ? 'Saving…' : 'Yes, launch at login'}</button>
</div> </div>
</div> </div>
@@ -363,11 +353,8 @@
<Download size={32} strokeWidth={1.5} class="text-accent mx-auto mb-3" /> <Download size={32} strokeWidth={1.5} class="text-accent mx-auto mb-3" />
<h2 class="text-xl font-medium text-text">Downloading model</h2> <h2 class="text-xl font-medium text-text">Downloading model</h2>
<p class="text-sm text-text-secondary mt-2">{downloadingModel}</p> <p class="text-sm text-text-secondary mt-2">{downloadingModel}</p>
<div class="w-full bg-bg-input rounded-full h-2 mt-6"> <div class="mt-6">
<div <LumotiaProgress value={downloadProgress} max={100} ariaLabel="Model download progress" />
class="bg-accent h-2 rounded-full"
style="width: {downloadProgress}%; transition-duration: var(--duration-ui)"
></div>
</div> </div>
<p class="text-xs text-text-tertiary mt-2">{downloadProgress}%</p> <p class="text-xs text-text-tertiary mt-2">{downloadProgress}%</p>
{#if estimatedMinutes > 2} {#if estimatedMinutes > 2}
@@ -383,21 +370,18 @@
{#if error} {#if error}
<!-- Sub-task 5: failure recovery — plain-English error + Try again / Skip --> <!-- Sub-task 5: failure recovery — plain-English error + Try again / Skip -->
<div class="mt-4 p-3 rounded-lg bg-danger/10 border border-danger/20"> <div class="mt-4">
<p class="text-sm text-danger font-medium mb-1">Something went wrong</p> <LumotiaNotice tone="danger" title="Something went wrong">
<p class="text-xs text-danger/80 whitespace-pre-line leading-relaxed">{error}</p> <p class="whitespace-pre-line leading-relaxed">{error}</p>
<div class="flex gap-2 mt-3"> <div class="flex gap-2 mt-3">
<button <LumotiaButton variant="destructive" size="sm" onclick={() => { error = ""; probe(); }}>
class="px-3 py-1.5 rounded-md text-xs bg-danger btn-filled-text hover:bg-danger/80" Try again
style="transition-duration: var(--duration-ui)" </LumotiaButton>
onclick={() => { error = ""; probe(); }} <LumotiaButton variant="tertiary" size="sm" onclick={skipSetup}>
>Try again</button> Skip this step
<button </LumotiaButton>
class="px-3 py-1.5 rounded-md text-xs border border-danger/40 text-danger hover:bg-danger/10"
style="transition-duration: var(--duration-ui)"
onclick={skipSetup}
>Skip this step</button>
</div> </div>
</LumotiaNotice>
</div> </div>
{/if} {/if}