v0.2 Phase 7.2: FilesPage — wrapper sweep

Migrated FilesPage chrome to the new grammar; the drop-zone affordance
and transcript textarea (the page's identity surfaces) stay verbatim.

  - Card import → LumotiaCard
  - EmptyState import → LumotiaEmptyState
  - "Browse Files" filled button → LumotiaButton variant=primary size=lg
  - Bottom "Copy" / "Export" toolbar → LumotiaButton variant=tertiary
  - Custom export dropdown → LumotiaMenu (Bits UI DropdownMenu)
  - Inline danger error → LumotiaNotice tone=danger
  - Custom progress bar → LumotiaProgress

Banks the LumotiaField + LumotiaNotice patterns the plan called out;
Field stays on the textarea (transcript surface is intentionally
naked inside the card per brand spec).

Per-page gate: npm run check (0/0/5704 files). Vitest / browser-mode /
e2e baselines unchanged (no behaviour change to the smoke surface).

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

View File

@@ -5,8 +5,12 @@
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { settings, addToHistory } from "$lib/stores/page.svelte.js"; import { settings, addToHistory } from "$lib/stores/page.svelte.js";
import { profilesStore } from "$lib/stores/profiles.svelte.ts"; import { profilesStore } from "$lib/stores/profiles.svelte.ts";
import Card from "$lib/components/Card.svelte"; import LumotiaCard from "$lib/ui/LumotiaCard.svelte";
import EmptyState from "$lib/components/EmptyState.svelte"; import LumotiaEmptyState from "$lib/ui/LumotiaEmptyState.svelte";
import LumotiaButton from "$lib/ui/LumotiaButton.svelte";
import LumotiaNotice from "$lib/ui/LumotiaNotice.svelte";
import LumotiaProgress from "$lib/ui/LumotiaProgress.svelte";
import LumotiaMenu from "$lib/ui/LumotiaMenu.svelte";
import { exportTranscript } from "$lib/utils/export.js"; import { exportTranscript } from "$lib/utils/export.js";
import { hasTauriRuntime } from "$lib/utils/runtime"; import { hasTauriRuntime } from "$lib/utils/runtime";
import { toasts } from "$lib/stores/toasts.svelte.js"; import { toasts } from "$lib/stores/toasts.svelte.js";
@@ -190,7 +194,7 @@
aria-label="Audio file drop zone" aria-label="Audio file drop zone"
> >
{#if !fileTranscript && !transcribing} {#if !fileTranscript && !transcribing}
<EmptyState <LumotiaEmptyState
icon={Upload} icon={Upload}
message="Drop an audio file or click to browse" message="Drop an audio file or click to browse"
/> />
@@ -210,14 +214,9 @@
<!-- Browse buttons + progress --> <!-- Browse buttons + progress -->
<div class="flex items-center gap-2 px-7 pb-3"> <div class="flex items-center gap-2 px-7 pb-3">
<button <LumotiaButton variant="primary" size="lg" disabled={transcribing} onclick={handleBrowse}>
class="btn-lg rounded-lg font-medium btn-filled-text bg-accent hover:bg-accent-hover
shadow-[var(--shadow-accent-raised)]"
onclick={handleBrowse}
disabled={transcribing}
>
Browse Files Browse Files
</button> </LumotiaButton>
<div class="flex-1"></div> <div class="flex-1"></div>
{#if progressText} {#if progressText}
<span class="text-[12px] text-text-secondary animate-fade-in">{progressText}</span> <span class="text-[12px] text-text-secondary animate-fade-in">{progressText}</span>
@@ -227,21 +226,14 @@
<!-- Error --> <!-- Error -->
{#if error} {#if error}
<div class="px-7 pb-2 animate-fade-in"> <div class="px-7 pb-2 animate-fade-in">
<div class="px-4 py-2 rounded-lg bg-danger/10 border border-danger/20 text-[12px] text-danger"> <LumotiaNotice tone="danger">{error}</LumotiaNotice>
{error}
</div>
</div> </div>
{/if} {/if}
<!-- Progress bar --> <!-- Progress bar -->
{#if progress > 0} {#if progress > 0}
<div class="px-7 pb-3 animate-fade-in"> <div class="px-7 pb-3 animate-fade-in">
<div class="h-[3px] bg-bg-elevated rounded-full overflow-hidden"> <LumotiaProgress value={progress} max={100} ariaLabel="Transcription progress" />
<div
class="h-full bg-accent rounded-full shadow-[var(--shadow-accent-glow)]"
style="width: {progress}%; transition-duration: var(--duration-ui)"
></div>
</div>
{#if fileName} {#if fileName}
<p class="text-[12px] text-text-secondary mt-1.5">{fileName}</p> <p class="text-[12px] text-text-secondary mt-1.5">{fileName}</p>
{/if} {/if}
@@ -250,7 +242,7 @@
<!-- File transcript --> <!-- File transcript -->
<div class="flex-1 px-7 pb-3 min-h-0"> <div class="flex-1 px-7 pb-3 min-h-0">
<Card classes="h-full flex flex-col"> <LumotiaCard classes="h-full flex flex-col">
<textarea <textarea
class="font-transcript flex-1 w-full bg-transparent text-text p-6 class="font-transcript flex-1 w-full bg-transparent text-text p-6
resize-none placeholder:text-text-tertiary" resize-none placeholder:text-text-tertiary"
@@ -260,27 +252,26 @@
aria-label="File transcript" aria-label="File transcript"
aria-live="polite" aria-live="polite"
></textarea> ></textarea>
</Card> </LumotiaCard>
</div> </div>
<!-- Bottom actions --> <!-- Bottom actions -->
<div class="flex gap-0.5 px-7 pb-4"> <div class="flex gap-1 px-7 pb-4">
<button class="btn-md rounded-lg text-text-secondary hover:bg-hover hover:text-text" onclick={copyAll}>Copy</button> <LumotiaButton variant="tertiary" onclick={copyAll}>Copy</LumotiaButton>
<div class="relative"> <LumotiaMenu
<button align="start"
class="btn-md rounded-lg text-text-secondary hover:bg-hover hover:text-text" items={[
onclick={() => showExportMenu = !showExportMenu} { value: "txt", label: "Plain Text", onSelect: () => handleExport("txt") },
>Export</button> { value: "md", label: "Markdown", onSelect: () => handleExport("md") },
{#if showExportMenu} { value: "csv", label: "CSV", onSelect: () => handleExport("csv") },
<div class="absolute left-0 bottom-full mb-1 bg-bg-card border border-border rounded-lg shadow-lg py-1 z-10 animate-fade-in min-w-[120px]"> { value: "html", label: "HTML", onSelect: () => handleExport("html") },
{#each [["txt", "Plain Text"], ["md", "Markdown"], ["csv", "CSV"], ["html", "HTML"], ["srt", "SRT Subtitles"], ["vtt", "WebVTT"]] as [fmt, label]} { value: "srt", label: "SRT Subtitles", onSelect: () => handleExport("srt") },
<button { value: "vtt", label: "WebVTT", onSelect: () => handleExport("vtt") },
class="w-full text-left btn-md text-text-secondary hover:bg-hover hover:text-text" ]}
onclick={() => handleExport(fmt)} >
>{label}</button> {#snippet trigger()}
{/each} <LumotiaButton variant="tertiary">Export</LumotiaButton>
</div> {/snippet}
{/if} </LumotiaMenu>
</div>
</div> </div>
</div> </div>