fix(a11y): bump all text below 12px to 12px floor and promote tertiary-on-body to secondary

Brand guidelines (docs/brand/magnotia-brand-guidelines.md §4) say "Never go
below 12px for any text" and "tertiary text must be ≥18px bold or ≥24px
regular". Audit found 246 sub-12px className sites and 166 cases of
text-text-tertiary paired with body sizes.

Bumped text-[9/10/11px] → text-[12px] across 26 .svelte files in src/lib
and src/routes. Promoted text-text-tertiary → text-text-secondary at body
sizes (12-14px) where context allowed; left placeholder pseudo-states,
ternary-conditional inactive states, and line-through "done" states alone
(8 residual pairings, all decorative).

Affects neurodivergent users on 1366×768 budget hardware most directly.
svelte-check: 0 errors, 0 warnings.
This commit is contained in:
2026-05-07 11:18:10 +01:00
parent 6da15d1ac8
commit ca3a55320b
26 changed files with 257 additions and 257 deletions

View File

@@ -63,7 +63,7 @@
</div>
{#if !collapsed}
<p class="text-[10px] text-text-tertiary px-5 mt-1.5 tracking-[0.12em] uppercase">Think out loud</p>
<p class="text-[12px] text-text-secondary px-5 mt-1.5 tracking-[0.12em] uppercase">Think out loud</p>
<!-- LLM status chip — hidden when aiTier === off, otherwise reflects
disconnected / warming / ready / generating / error state live. -->
<div class="px-5 mt-2">
@@ -121,14 +121,14 @@
{#if !collapsed}
<span>{item.label}</span>
{#if item.id === "tasks" && taskCount > 0}
<span class="ml-auto text-[10px] px-1.5 py-0.5 rounded-full bg-accent/15 text-accent font-medium">
<span class="ml-auto text-[12px] px-1.5 py-0.5 rounded-full bg-accent/15 text-accent font-medium">
{taskCount}
</span>
{/if}
{:else}
<!-- Tooltip on hover/focus for collapsed mode -->
<span class="absolute left-full ml-2 px-2 py-1 rounded-md bg-bg-elevated border border-border-subtle
text-[11px] text-text whitespace-nowrap opacity-0 pointer-events-none
text-[12px] text-text whitespace-nowrap opacity-0 pointer-events-none
group-hover:opacity-100 group-focus-visible:opacity-100 z-50 shadow-lg"
style="transition: opacity var(--duration-ui)"
role="tooltip"
@@ -159,9 +159,9 @@
class:animate-pulse-soft={page.recording}
style="background: {page.statusColor}"
></span>
<span class="text-[11px] text-text-secondary">{page.status}</span>
<span class="text-[12px] text-text-secondary">{page.status}</span>
</div>
<p class="text-[10px] text-text-tertiary mt-1.5">{settings.formatMode} · v0.1</p>
<p class="text-[12px] text-text-secondary mt-1.5">{settings.formatMode} · v0.1</p>
</div>
{:else}
<!-- Collapsed: status dot only -->

View File

@@ -61,7 +61,7 @@
<div class="flex flex-col gap-1.5">
<div class="flex justify-between">
<p class="text-[12px] text-text-secondary font-medium">Font size</p>
<span class="text-[11px] text-text-tertiary">{prefs.accessibility.fontSize}px</span>
<span class="text-[12px] text-text-secondary">{prefs.accessibility.fontSize}px</span>
</div>
<input type="range" min="16" max="24" step="1" value={prefs.accessibility.fontSize}
oninput={(e) => updateAccessibility({ fontSize: rangeValue(e) })}
@@ -72,7 +72,7 @@
<div class="flex flex-col gap-1.5">
<div class="flex justify-between">
<p class="text-[12px] text-text-secondary font-medium">Letter spacing</p>
<span class="text-[11px] text-text-tertiary">{prefs.accessibility.letterSpacing.toFixed(2)}em</span>
<span class="text-[12px] text-text-secondary">{prefs.accessibility.letterSpacing.toFixed(2)}em</span>
</div>
<input type="range" min="0" max="0.15" step="0.01" value={prefs.accessibility.letterSpacing}
oninput={(e) => updateAccessibility({ letterSpacing: rangeValue(e) })}
@@ -83,7 +83,7 @@
<div class="flex flex-col gap-1.5">
<div class="flex justify-between">
<p class="text-[12px] text-text-secondary font-medium">Line height</p>
<span class="text-[11px] text-text-tertiary">{prefs.accessibility.lineHeight.toFixed(1)}</span>
<span class="text-[12px] text-text-secondary">{prefs.accessibility.lineHeight.toFixed(1)}</span>
</div>
<input type="range" min="1.3" max="2.2" step="0.1" value={prefs.accessibility.lineHeight}
oninput={(e) => updateAccessibility({ lineHeight: rangeValue(e) })}
@@ -94,7 +94,7 @@
<div class="flex flex-col gap-1.5">
<div class="flex justify-between">
<p class="text-[12px] text-text-secondary font-medium">Transcript font size</p>
<span class="text-[11px] text-text-tertiary">{prefs.accessibility.transcriptSize}px</span>
<span class="text-[12px] text-text-secondary">{prefs.accessibility.transcriptSize}px</span>
</div>
<input type="range" min="16" max="24" step="1" value={prefs.accessibility.transcriptSize}
oninput={(e) => updateAccessibility({ transcriptSize: rangeValue(e) })}

View File

@@ -65,7 +65,7 @@
<button
type="button"
class="energy-chip inline-flex items-center justify-center rounded-md border px-1 {chipSize} text-[10px] font-medium
class="energy-chip inline-flex items-center justify-center rounded-md border px-1 {chipSize} text-[12px] font-medium
{energy === null
? 'opacity-0 group-hover:opacity-100 text-text-tertiary border-border-subtle hover:border-accent hover:text-text-secondary'
: ''}

View File

@@ -198,19 +198,19 @@
>
<span class="sr-only" aria-live="polite">{srStatus}</span>
{#if recording}
<span class="text-[11px] text-text-tertiary italic">Hold Ctrl/Alt/Super + key. Esc to cancel.</span>
<span class="text-[12px] text-text-secondary italic">Hold Ctrl/Alt/Super + key. Esc to cancel.</span>
{:else}
{#each chips as chip, i}
{#if i > 0}
<span class="text-[10px] text-text-tertiary" aria-hidden="true">+</span>
<span class="text-[12px] text-text-secondary" aria-hidden="true">+</span>
{/if}
<span class="px-2 py-0.5 rounded-md bg-bg-elevated border border-border text-[11px] font-medium text-text
<span class="px-2 py-0.5 rounded-md bg-bg-elevated border border-border text-[12px] font-medium text-text
shadow-[inset_0_1px_2px_rgba(0,0,0,0.3)]">{chip}</span>
{/each}
{/if}
</button>
{#if rejectionReason}
<span class="text-[11px] text-warning" role="alert">
<span class="text-[12px] text-warning" role="alert">
Couldn't bind that combo. {rejectionReason}.
</span>
{/if}

View File

@@ -122,13 +122,13 @@
<div class="space-y-5">
<div>
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
Build simple if-then rules. They respect Mute for now in Nudges, and everything stays local.
</p>
<div class="rounded-lg border border-border-subtle bg-bg-input p-4">
<div class="grid grid-cols-1 md:grid-cols-[120px_1fr] gap-3 items-start">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider pt-2">If</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider pt-2">If</p>
<div class="flex flex-wrap gap-2">
<select
class="bg-bg border border-border rounded-lg px-3 py-2 text-[12px] text-text focus:border-accent"
@@ -147,7 +147,7 @@
{/if}
</div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider pt-2">Then</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider pt-2">Then</p>
<div class="space-y-3">
<div class="flex flex-col gap-2">
<Toggle
@@ -188,7 +188,7 @@
/>
<div>
<label for="rule-speak-line" class="block text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">
<label for="rule-speak-line" class="block text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">
Speak aloud
</label>
<input
@@ -204,7 +204,7 @@
</div>
{#if error}
<p class="text-[11px] text-danger mt-3">{error}</p>
<p class="text-[12px] text-danger mt-3">{error}</p>
{/if}
<div class="flex justify-end mt-4">
@@ -224,11 +224,11 @@
</div>
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Saved rules</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Saved rules</p>
{#if loading}
<p class="text-[11px] text-text-tertiary">Loading…</p>
<p class="text-[12px] text-text-secondary">Loading…</p>
{:else if implementationRules.length === 0}
<p class="text-[11px] text-text-tertiary italic">No rules yet.</p>
<p class="text-[12px] text-text-secondary italic">No rules yet.</p>
{:else}
<div class="space-y-2">
{#each implementationRules as rule (rule.id)}

View File

@@ -45,7 +45,7 @@
></span>
{:else}
<span
class="inline-flex items-center gap-1.5 px-2 py-[3px] rounded-full bg-bg-elevated border border-border-subtle text-[10px] text-text-secondary whitespace-nowrap"
class="inline-flex items-center gap-1.5 px-2 py-[3px] rounded-full bg-bg-elevated border border-border-subtle text-[12px] text-text-secondary whitespace-nowrap"
title={llmStatus.detail ?? label}
>
<span class="inline-block w-[6px] h-[6px] rounded-full {dotClass}"></span>

View File

@@ -193,13 +193,13 @@
<div class="pl-6 mt-1 flex flex-col gap-1">
{#if loading}
<div class="flex items-center gap-2 text-[12px] text-text-tertiary py-1">
<div class="flex items-center gap-2 text-[12px] text-text-secondary py-1">
<Loader2 size={12} class="animate-spin" aria-hidden="true" />
Loading steps…
</div>
{:else if subtasks.length === 0 && !error}
<button
class="flex items-center gap-1.5 text-[12px] text-text-tertiary hover:text-accent py-1 disabled:opacity-50"
class="flex items-center gap-1.5 text-[12px] text-text-secondary hover:text-accent py-1 disabled:opacity-50"
onclick={decompose}
disabled={decomposing}
aria-label="Break down this task into micro-steps"
@@ -213,7 +213,7 @@
{/if}
</button>
{:else if error}
<p class="text-[11px] text-text-tertiary py-1">{error}</p>
<p class="text-[12px] text-text-secondary py-1">{error}</p>
{:else}
{#each subtasks as step (step.id)}
<div class="flex items-center gap-2 group py-0.5">
@@ -295,7 +295,7 @@
<SpeakerButton text={step.text} label="Read this step aloud" size={10} />
</span>
<button
class="opacity-0 group-hover:opacity-100 flex items-center gap-1 text-[10px] text-text-tertiary hover:text-accent"
class="opacity-0 group-hover:opacity-100 flex items-center gap-1 text-[12px] text-text-secondary hover:text-accent"
onclick={() => startTimer(step.id)}
aria-label="Start 2-minute timer for this step"
style={reduceMotion ? '' : 'transition: opacity var(--duration-ui)'}

View File

@@ -74,7 +74,7 @@
<p class="text-[13px] text-text-secondary mb-1">
Magnotia needs the <span class="font-medium text-text">{modelSize}</span> model to transcribe speech.
</p>
<p class="text-[11px] text-text-tertiary mb-6">
<p class="text-[12px] text-text-secondary mb-6">
{currentModelInfo?.size ?? "?"} · {currentModelInfo?.accuracy ?? "?"} accuracy · 100% offline
</p>
@@ -104,7 +104,7 @@
<p class="text-[12px] text-danger mt-3">{error}</p>
{/if}
<p class="text-[10px] text-text-tertiary mt-5">
<p class="text-[12px] text-text-secondary mt-5">
Models are cached locally. No data leaves your machine.
</p>
</div>

View File

@@ -281,7 +281,7 @@
<div class="flex-1 overflow-y-auto px-6 pb-3 min-h-0">
{#if loading}
<p class="text-[12px] text-text-tertiary py-6 text-center">Loading your list…</p>
<p class="text-[12px] text-text-secondary py-6 text-center">Loading your list…</p>
{:else}
<ul class="flex flex-col gap-1.5">
{#each tasks as task (task.id)}
@@ -317,7 +317,7 @@
<div class="px-6 pb-5 pt-2">
<p
class="text-[11px] mb-3 min-h-[16px] transition-colors
class="text-[12px] mb-3 min-h-[16px] transition-colors
{tooManyFlash ? 'text-warning' : 'text-text-tertiary'}"
aria-live="polite"
>
@@ -332,7 +332,7 @@
<div class="flex items-center justify-between gap-3">
<button
type="button"
class="px-3 py-2 text-[12px] text-text-tertiary hover:text-text"
class="px-3 py-2 text-[12px] text-text-secondary hover:text-text"
onclick={skipForToday}
disabled={applying}
>

View File

@@ -6,7 +6,7 @@
{#each options as option}
<button
class="rounded-lg font-medium
{size === 'small' ? 'px-2.5 py-1 text-[11px]' : 'px-3.5 py-[6px] text-[12px]'}
{size === 'small' ? 'px-2.5 py-1 text-[12px]' : 'px-3.5 py-[6px] text-[12px]'}
{value === option
? 'bg-accent text-bg shadow-[0_1px_4px_rgba(214,132,80,0.3)]'
: 'text-text-secondary hover:text-text hover:bg-hover'}"

View File

@@ -58,7 +58,7 @@
<div class="flex-1">
<p class="text-sm text-text font-semibold">{title}</p>
{#if description}
<p class="text-[11px] text-text-tertiary tracking-wide mt-1">{description}</p>
<p class="text-[12px] text-text-secondary tracking-wide mt-1">{description}</p>
{/if}
</div>
</summary>

View File

@@ -39,12 +39,12 @@
<!-- Header -->
<div class="flex items-center gap-2 px-4 pt-4 pb-3">
<h3 class="text-[13px] font-semibold text-text">Tasks</h3>
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-tertiary">
<span class="text-[12px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-secondary">
{taskCount}
</span>
<div class="flex-1"></div>
<button
class="text-[11px] text-text-tertiary hover:text-accent"
class="text-[12px] text-text-secondary hover:text-accent"
onclick={() => page.current = "tasks"}
>View all</button>
<button
@@ -74,7 +74,7 @@
<!-- Task list -->
<div class="flex-1 overflow-y-auto px-3 pb-3 min-h-0">
{#if activeTasks.length === 0}
<p class="text-[11px] text-text-tertiary text-center py-6 opacity-60">No active tasks</p>
<p class="text-[12px] text-text-secondary text-center py-6 opacity-60">No active tasks</p>
{:else}
<div class="flex flex-col gap-0.5">
{#each activeTasks as task (task.id)}
@@ -87,7 +87,7 @@
onclick={() => completeTask(task.id)}
aria-label="Complete task"
></button>
<span class="text-[11px] text-text leading-snug flex-1 min-w-0">{task.text}</span>
<span class="text-[12px] text-text leading-snug flex-1 min-w-0">{task.text}</span>
<span class="w-2 h-2 rounded-full mt-1 flex-shrink-0 {BUCKET_DOT_COLORS[task.bucket] || 'bg-text-tertiary'}"></span>
</div>
{/each}

View File

@@ -83,7 +83,7 @@
<div class="flex-1 min-w-0">
<p class="text-[13px] text-text leading-tight">{label}</p>
{#if description}
<p class="text-[11px] text-text-tertiary mt-0.5 leading-snug">{description}</p>
<p class="text-[12px] text-text-secondary mt-0.5 leading-snug">{description}</p>
{/if}
</div>
</div>

View File

@@ -182,7 +182,7 @@
</button>
<!-- Timestamp -->
{#if showTimestamps}
<span class="text-[10px] text-text-tertiary tabular-nums flex-shrink-0 mt-0.5 min-w-[36px]">
<span class="text-[12px] text-text-secondary tabular-nums flex-shrink-0 mt-0.5 min-w-[36px]">
{formatTime(seg.start)}
</span>
{/if}

View File

@@ -60,7 +60,7 @@
<!-- Active tasks (WIP-limited) -->
{#if visibleTasks.length === 0}
<p class="text-[12px] text-text-tertiary text-center py-4">No active tasks</p>
<p class="text-[12px] text-text-secondary text-center py-4">No active tasks</p>
{:else}
<div class="flex flex-col gap-1">
{#each visibleTasks as task (task.id)}
@@ -105,7 +105,7 @@
{/if}
</button>
<button
class="text-text-tertiary hover:text-danger opacity-0 group-hover:opacity-100 text-[11px]"
class="text-text-secondary hover:text-danger opacity-0 group-hover:opacity-100 text-[12px]"
onclick={() => deleteTask(task.id)}
aria-label="Delete task"
>
@@ -124,7 +124,7 @@
<!-- Overflow -->
{#if hasOverflow}
<button
class="flex items-center gap-1.5 text-[11px] text-text-tertiary hover:text-text-secondary px-3"
class="flex items-center gap-1.5 text-[12px] text-text-secondary hover:text-text-secondary px-3"
onclick={() => showOverflow = !showOverflow}
>
<ChevronDown size={12} class="transform {showOverflow ? 'rotate-180' : ''}" aria-hidden="true"
@@ -146,7 +146,7 @@
<!-- WIP indicator -->
{#if activeTasks.length > 0}
<p class="text-[10px] text-text-tertiary text-right">
<p class="text-[12px] text-text-secondary text-right">
{Math.min(activeTasks.length, wipLimit)}/{wipLimit} active
</p>
{/if}

View File

@@ -29,7 +29,7 @@
aria-pressed={prefs.zone === zone.id || (zone.id === 'default' && !prefs.zone)}
>
<div class="w-8 h-8 rounded-lg {zone.swatch} border border-border-subtle"></div>
<span class="text-[11px] text-text-secondary">{zone.label}</span>
<span class="text-[12px] text-text-secondary">{zone.label}</span>
</button>
{/each}
</div>

View File

@@ -831,7 +831,7 @@
<span class="w-[14px] h-[14px] rounded-full bg-white/90"></span>
{/if}
</button>
<span class="text-[11px] font-medium flex-shrink-0 {page.recording ? 'text-danger' : 'text-text-secondary'}">
<span class="text-[12px] font-medium flex-shrink-0 {page.recording ? 'text-danger' : 'text-text-secondary'}">
{page.recording ? 'Stop' : modelLoading ? 'Loading' : 'Record'}
</span>
@@ -854,7 +854,7 @@
{/each}
</span>
{:else}
<span class="text-[11px] text-text-tertiary">
<span class="text-[12px] text-text-secondary">
{#if modelLoading}
Loading model...
{:else if !tauriRuntimeAvailable}
@@ -864,7 +864,7 @@
Saved{#if extractedCount > 0} · {extractedCount} task{extractedCount === 1 ? '' : 's'} extracted{/if}
</span>
{:else}
Press record or <kbd class="px-1 py-0.5 rounded bg-bg-elevated text-[10px] text-text-tertiary border border-border-subtle">{settings.globalHotkey}</kbd>
Press record or <kbd class="px-1 py-0.5 rounded bg-bg-elevated text-[12px] text-text-secondary border border-border-subtle">{settings.globalHotkey}</kbd>
{/if}
</span>
{/if}
@@ -876,7 +876,7 @@
</span>
<!-- Status indicator -->
<span class="text-[11px] text-text-tertiary flex-shrink-0 min-w-[60px] text-right">
<span class="text-[12px] text-text-secondary flex-shrink-0 min-w-[60px] text-right">
{#if page.recording}
<span class="inline-flex items-center gap-1.5">
<span class="w-[6px] h-[6px] rounded-full bg-danger animate-pulse-soft"></span>
@@ -901,10 +901,10 @@
>
<span class="flex items-center gap-1.5">
<SquareCheck size={16} class={page.taskSidebarOpen ? 'text-accent' : 'text-text-tertiary'} aria-hidden="true" />
<span class="text-[11px] {page.taskSidebarOpen ? 'text-accent' : 'text-text-tertiary'}">Tasks</span>
<span class="text-[12px] {page.taskSidebarOpen ? 'text-accent' : 'text-text-tertiary'}">Tasks</span>
</span>
{#if taskCount > 0}
<span class="absolute -top-0.5 -right-0.5 text-[9px] px-1 py-0 rounded-full bg-accent text-white font-medium min-w-[14px] text-center leading-[14px]">
<span class="absolute -top-0.5 -right-0.5 text-[12px] px-1 py-0 rounded-full bg-accent text-white font-medium min-w-[14px] text-center leading-[14px]">
{taskCount}
</span>
{/if}
@@ -979,10 +979,10 @@
{#if activeTemplate}
<div class="px-5 pt-2 animate-fade-in flex-shrink-0">
<div class="flex items-center gap-2 px-4 py-1.5 rounded-lg bg-accent-subtle border border-accent/20">
<span class="text-[11px] text-accent font-medium">Template: {activeTemplate}</span>
<span class="text-[11px] text-text-tertiary">Click a section, then record to fill it</span>
<span class="text-[12px] text-accent font-medium">Template: {activeTemplate}</span>
<span class="text-[12px] text-text-secondary">Click a section, then record to fill it</span>
<div class="flex-1"></div>
<button class="text-[11px] text-text-tertiary hover:text-text" onclick={() => activeTemplate = ""}>Remove</button>
<button class="text-[12px] text-text-secondary hover:text-text" onclick={() => activeTemplate = ""}>Remove</button>
</div>
</div>
{/if}
@@ -1042,7 +1042,7 @@
></textarea>
{#if showScrollHint}
<button
class="absolute bottom-3 right-6 px-3 py-1.5 rounded-full bg-accent text-white text-[11px] font-medium shadow-md hover:bg-accent-hover animate-fade-in"
class="absolute bottom-3 right-6 px-3 py-1.5 rounded-full bg-accent text-white text-[12px] font-medium shadow-md hover:bg-accent-hover animate-fade-in"
onclick={scrollToBottom}
aria-label="Scroll to latest"
style="transition-duration: var(--duration-ui)"
@@ -1055,7 +1055,7 @@
<!-- Status footer (inside transcript card) -->
<div class="flex items-center justify-between px-6 pb-3 pt-1 flex-shrink-0">
<span class="text-[11px] text-text-tertiary">
<span class="text-[12px] text-text-secondary">
{#if wordCount > 0}
{wordCount} {wordCount === 1 ? 'word' : 'words'}
{:else}
@@ -1067,10 +1067,10 @@
</span>
<div class="flex items-center gap-2">
{#if aiStatus}
<span class="text-[11px] text-accent animate-fade-in">{aiStatus}</span>
<span class="text-[11px] text-text-tertiary">·</span>
<span class="text-[12px] text-accent animate-fade-in">{aiStatus}</span>
<span class="text-[12px] text-text-secondary">·</span>
{/if}
<span class="text-[11px] text-text-tertiary">
<span class="text-[12px] text-text-secondary">
{settings.formatMode} · {page.activeProfile === "None" ? "No profile" : page.activeProfile}
</span>
{#if transcript.trim()}

View File

@@ -176,7 +176,7 @@
Drop audio or video files here
</p>
{/if}
<p class="text-[11px] text-text-tertiary text-center mt-1.5">
<p class="text-[12px] text-text-secondary text-center mt-1.5">
MP3, WAV, M4A, MP4, FLAC, OGG
</p>
</div>
@@ -218,7 +218,7 @@
></div>
</div>
{#if fileName}
<p class="text-[11px] text-text-tertiary mt-1.5">{fileName}</p>
<p class="text-[12px] text-text-secondary mt-1.5">{fileName}</p>
{/if}
</div>
{/if}

View File

@@ -181,7 +181,7 @@
<p class="text-sm text-text-secondary mt-3 leading-relaxed">
On the first launch of the day, a gentle modal shows yesterday's open items and asks you to pick up to three for today. The rest can wait.
</p>
<p class="text-[11px] text-text-tertiary 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">
<button
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover"
@@ -205,7 +205,7 @@
<p class="text-sm text-text-secondary mt-3 leading-relaxed">
A reflective page you can open when you want to close the day. Shows what you finished, names the open loops, then gets out of the way. Never scheduled, never nagging.
</p>
<p class="text-[11px] text-text-tertiary 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">
<button
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover"
@@ -229,7 +229,7 @@
<p class="text-sm text-text-secondary mt-3 leading-relaxed">
So Magnotia is already there when you need it, especially useful if you said yes to morning triage. Uses your OS's standard autostart. No background tricks, no telemetry.
</p>
<p class="text-[11px] text-text-tertiary 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">
<button
class="px-4 py-2 rounded-lg text-sm border border-border text-text-secondary hover:bg-hover"
@@ -310,10 +310,10 @@
<div>
<span class="text-sm font-medium text-text">{model.display_name}</span>
{#if i === 0}
<span class="ml-2 text-[10px] px-1.5 py-0.5 rounded-full bg-accent/15 text-accent font-medium">Recommended</span>
<span class="ml-2 text-[12px] px-1.5 py-0.5 rounded-full bg-accent/15 text-accent font-medium">Recommended</span>
{/if}
{#if model.is_downloaded}
<span class="ml-2 text-[10px] px-1.5 py-0.5 rounded-full bg-success/15 text-success font-medium">Downloaded</span>
<span class="ml-2 text-[12px] px-1.5 py-0.5 rounded-full bg-success/15 text-success font-medium">Downloaded</span>
{/if}
</div>
<span class="text-xs text-text-tertiary">{model.disk_size_mb} MB</span>

View File

@@ -572,7 +572,7 @@
<!-- Header -->
<div class="flex items-center gap-4 px-7 pt-6 pb-4">
<h2 class="font-display text-[26px] italic text-text">History</h2>
<span class="text-[11px] text-text-tertiary mt-1">{history.length} saved</span>
<span class="text-[12px] text-text-secondary mt-1">{history.length} saved</span>
<div class="flex-1"></div>
<button
class="inline-flex items-center gap-1.5 btn-md rounded-lg
@@ -582,7 +582,7 @@
title={showStarredOnly ? "Showing starred only (click to show all)" : "Show starred only"}
>
<Star size={14} aria-hidden="true" />
<span class="text-[11px]">Starred</span>
<span class="text-[12px]">Starred</span>
</button>
{#if history.some((i) => !i.llmTags || i.llmTags.length === 0)}
<button
@@ -592,7 +592,7 @@
title="Run AI content tagging across every untagged transcript"
>
<Tag size={13} aria-hidden="true" />
<span class="text-[11px]">{bulkTagging ? `Tagging ${bulkTaggingProgress}` : "Tag all untagged"}</span>
<span class="text-[12px]">{bulkTagging ? `Tagging ${bulkTaggingProgress}` : "Tag all untagged"}</span>
</button>
{/if}
{#if history.length > 0}
@@ -629,9 +629,9 @@
data-no-transition
/>
{#if searchQuery}
<span class="text-[11px] text-text-tertiary mr-2">{filtered.length} results</span>
<span class="text-[12px] text-text-secondary mr-2">{filtered.length} results</span>
<button
class="text-[11px] text-text-tertiary hover:text-text"
class="text-[12px] text-text-secondary hover:text-text"
onclick={() => searchQuery = ""}
>Clear</button>
{/if}
@@ -642,10 +642,10 @@
<!-- Tag chip filter bar -->
{#if allTags.length > 0}
<div class="px-7 pb-3 flex items-center gap-1.5 flex-wrap">
<span class="text-[10px] uppercase tracking-wider text-text-tertiary mr-1">Tags</span>
<span class="text-[12px] uppercase tracking-wider text-text-secondary mr-1">Tags</span>
{#if activeTagFilter}
<button
class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-accent text-[10px] text-accent bg-accent/10"
class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-accent text-[12px] text-accent bg-accent/10"
onclick={() => (activeTagFilter = null)}
title="Clear tag filter"
>
@@ -655,11 +655,11 @@
{:else}
{#each allTags.slice(0, 7) as t (t.tag)}
<button
class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-border-subtle text-[10px] text-text-secondary hover:border-accent hover:text-accent"
class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-border-subtle text-[12px] text-text-secondary hover:border-accent hover:text-accent"
onclick={() => (activeTagFilter = t.tag)}
>
{t.tag}
<span class="text-[9px] text-text-tertiary">{t.count}</span>
<span class="text-[12px] text-text-secondary">{t.count}</span>
</button>
{/each}
{/if}
@@ -774,7 +774,7 @@
<!-- Duration -->
{#if item.duration}
<span class="text-[11px] text-text-tertiary flex-shrink-0 tabular-nums">
<span class="text-[12px] text-text-secondary flex-shrink-0 tabular-nums">
{formatDuration(item.duration)}
</span>
{/if}
@@ -789,7 +789,7 @@
</span>
<!-- Date (right-aligned) -->
<span class="text-[11px] text-text-tertiary flex-shrink-0 text-right min-w-[90px]">
<span class="text-[12px] text-text-secondary flex-shrink-0 text-right min-w-[90px]">
{item.date}
</span>
@@ -808,7 +808,7 @@
<!-- Audio player (if playing this item) -->
{#if item.audioPath && playingId === item.id}
<div class="flex items-center gap-3 mb-4 px-1">
<span class="text-[10px] text-text-tertiary tabular-nums w-[80px]">
<span class="text-[12px] text-text-secondary tabular-nums w-[80px]">
{formatTime(currentTime)} / {formatTime(duration)}
</span>
<input
@@ -825,7 +825,7 @@
<div class="flex gap-0.5">
{#each PLAYBACK_SPEEDS as speed}
<button
class="text-[9px] px-1.5 py-0.5 rounded-full
class="text-[12px] px-1.5 py-0.5 rounded-full
{playbackRate === speed
? 'bg-accent/15 text-accent font-medium'
: 'text-text-tertiary hover:text-text-secondary'}"
@@ -853,12 +853,12 @@
<div class="flex items-center gap-1.5 flex-wrap mb-3" onclick={(e) => e.stopPropagation()} role="presentation">
{#each deriveAutoTags(item) as t (t)}
<span
class="inline-flex items-center px-2 py-0.5 rounded-full text-[10px] bg-bg-input text-text-tertiary border border-border-subtle"
class="inline-flex items-center px-2 py-0.5 rounded-full text-[12px] bg-bg-input text-text-secondary border border-border-subtle"
title="Auto-generated tag"
>{t}</span>
{/each}
{#each (item.manualTags || []) as t (t)}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] bg-accent/10 text-accent border border-accent/30">
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[12px] bg-accent/10 text-accent border border-accent/30">
{t}
<button
class="text-[12px] leading-none hover:text-danger"
@@ -873,7 +873,7 @@
into manualTags. -->
{#each (item.llmTags || []) as t (t)}
<button
class="inline-flex items-center px-2 py-0.5 rounded-full text-[10px] italic border border-dashed border-border-subtle text-text-tertiary hover:text-text hover:border-accent"
class="inline-flex items-center px-2 py-0.5 rounded-full text-[12px] italic border border-dashed border-border-subtle text-text-secondary hover:text-text hover:border-accent"
onclick={() => promoteLlmTag(item, t)}
title="Click to keep as a manual tag"
aria-label={`Promote ${t} to manual tag`}
@@ -881,7 +881,7 @@
{/each}
<input
type="text"
class="bg-bg-input border border-border rounded-full px-2 py-0.5 text-[10px]
class="bg-bg-input border border-border rounded-full px-2 py-0.5 text-[12px]
text-text placeholder:text-text-tertiary focus:border-accent w-[110px]"
placeholder="+ add tag"
onkeydown={(e) => handleAddTagKey(e, item)}
@@ -895,12 +895,12 @@
<!-- Action buttons -->
<div class="flex items-center gap-2 flex-wrap">
<button
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
class="text-[12px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); copyItem(item); }}
>Copy</button>
<button
class="inline-flex items-center gap-1.5 text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
class="inline-flex items-center gap-1.5 text-[12px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); openEditor(item); }}
title="Open transcript in a popout editor"
@@ -909,13 +909,13 @@
<ExternalLink size={11} aria-hidden="true" />
</button>
<button
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
class="text-[12px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); exportMarkdown(item); }}
title="Export this transcript as a Markdown file with YAML frontmatter"
>Export .md</button>
<button
class="inline-flex items-center gap-1.5 text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text disabled:opacity-50"
class="inline-flex items-center gap-1.5 text-[12px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-text disabled:opacity-50"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); tagRow(item); }}
disabled={tagging.has(item.id)}
@@ -927,14 +927,14 @@
</button>
{#if item.audioPath && item.segments && item.segments.length > 0}
<button
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-accent hover:text-accent-hover"
class="text-[12px] px-3 py-1.5 rounded-lg bg-hover text-accent hover:text-accent-hover"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); openViewer(item); }}
>Open viewer</button>
{/if}
<div class="flex-1"></div>
<button
class="text-[11px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-danger"
class="text-[12px] px-3 py-1.5 rounded-lg bg-hover text-text-secondary hover:text-danger"
style="transition-duration: var(--duration-ui)"
onclick={(e) => { e.stopPropagation(); removeItem(item); }}
>Delete</button>

View File

@@ -1069,7 +1069,7 @@
deep-dive; this chip is a one-line summary that's always
visible while the user moves through the settings tree. -->
<span
class="text-[11px] text-text-tertiary bg-accent-subtle rounded-full px-2.5 py-1"
class="text-[12px] text-text-secondary bg-accent-subtle rounded-full px-2.5 py-1"
>100% local · no telemetry · no cloud</span>
</div>
</div>
@@ -1112,11 +1112,11 @@
the global hotkey above the seven groups. -->
<div class="grid grid-cols-3 gap-3 px-1 pb-4 border-b border-border-subtle mb-2">
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Theme</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Theme</p>
<SegmentedButton size="small" options={["Dark", "Light"]} bind:value={settings.theme} />
</div>
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Font size</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Font size</p>
<SegmentedButton
size="small"
options={["Smaller", "Default", "Larger"]}
@@ -1124,11 +1124,11 @@
/>
</div>
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Hotkey</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Hotkey</p>
<button
type="button"
onclick={jumpToGlobalHotkey}
class="px-2.5 py-1 text-[11px] font-medium rounded-lg bg-bg-elevated text-text-secondary border border-border-subtle hover:border-accent hover:text-accent"
class="px-2.5 py-1 text-[12px] font-medium rounded-lg bg-bg-elevated text-text-secondary border border-border-subtle hover:border-accent hover:text-accent"
style="transition-duration: var(--duration-ui)"
aria-label={`Edit global hotkey, currently ${settings.globalHotkey}`}
>{settings.globalHotkey}</button>
@@ -1144,7 +1144,7 @@
>
<div class="px-1 pb-2 animate-fade-in">
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Microphone</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Microphone</p>
<div class="flex items-center gap-3">
<select
class="bg-bg-input border border-border rounded-lg px-3 py-2 text-[13px] text-text
@@ -1167,11 +1167,11 @@
>Refresh</button>
</div>
{#if audioDevicesError}
<p class="text-[11px] text-danger mt-2">{audioDevicesError}</p>
<p class="text-[12px] text-danger mt-2">{audioDevicesError}</p>
{:else if visibleAudioDevices.length === 0}
<p class="text-[11px] text-text-tertiary mt-2">No input devices detected. Check that a microphone is connected and PulseAudio/PipeWire is running.</p>
<p class="text-[12px] text-text-secondary mt-2">No input devices detected. Check that a microphone is connected and PulseAudio/PipeWire is running.</p>
{:else}
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
Auto mode tries the system default first, then any other real input. Speaker-monitor sources (loopback) are skipped because they record system audio rather than the microphone. If dictation is silent, pick the device explicitly here.
</p>
{/if}
@@ -1197,13 +1197,13 @@
open={searchActive ? searchMatches('Terms profiles initial prompt vocabulary') : true}
>
<div class="animate-fade-in">
<p class="text-[12px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
Words and phrases the AI cleanup pass should preserve exactly. Useful for medication names, place names, jargon, names of people in your support network, anything Whisper tends to mishear. Vocabulary is scoped to a profile, switch profiles to swap whole vocabularies.
</p>
<!-- Profile selector -->
<div class="mb-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Active Profile</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Active Profile</p>
<div class="flex items-center gap-2 flex-wrap">
<select
class="bg-bg-input border border-border rounded-lg px-3 py-2 text-[13px] text-text
@@ -1270,7 +1270,7 @@
{#if profilesStore.active}
<!-- Rename active profile -->
<div class="mb-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Profile Name</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Profile Name</p>
<input
type="text"
bind:value={profileRenameDraft}
@@ -1283,8 +1283,8 @@
<!-- Initial prompt textarea -->
<div class="mb-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Initial Prompt</p>
<p class="text-[11px] text-text-tertiary mb-2">
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Initial Prompt</p>
<p class="text-[12px] text-text-secondary mb-2">
Passed to Whisper before every transcription in this profile. Used as a biasing prompt, keep it short and topic-specific (names, domain terms, tone cues).
</p>
<textarea
@@ -1300,17 +1300,17 @@
<button
type="button"
onclick={saveInitialPrompt}
class="text-[11px] text-accent hover:text-accent-hover"
class="text-[12px] text-accent hover:text-accent-hover"
>Save prompt</button>
<span class="text-[11px] text-text-tertiary">Unsaved changes, saves on blur.</span>
<span class="text-[12px] text-text-secondary">Unsaved changes, saves on blur.</span>
{:else}
<span class="text-[11px] text-text-tertiary">Saved.</span>
<span class="text-[12px] text-text-secondary">Saved.</span>
{/if}
</div>
</div>
<!-- Terms -->
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Terms</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Terms</p>
<div class="flex gap-2 mb-4">
<input
@@ -1342,7 +1342,7 @@
{#if !showBulkVocab}
<button
type="button"
class="text-[11px] text-text-tertiary hover:text-accent underline"
class="text-[12px] text-text-secondary hover:text-accent underline"
onclick={() => { showBulkVocab = true; }}
>Bulk add from a list…</button>
{:else}
@@ -1367,7 +1367,7 @@
type="button"
onclick={() => { showBulkVocab = false; bulkVocabText = ""; }}
disabled={bulkVocabBusy}
class="px-3 py-1.5 text-[12px] text-text-tertiary hover:text-text"
class="px-3 py-1.5 text-[12px] text-text-secondary hover:text-text"
>Cancel</button>
</div>
</div>
@@ -1375,11 +1375,11 @@
</div>
{#if vocabularyError}
<p class="text-[11px] text-danger mb-3">{vocabularyError}</p>
<p class="text-[12px] text-danger mb-3">{vocabularyError}</p>
{/if}
{#if vocabulary.length === 0}
<p class="text-[11px] text-text-tertiary italic">No terms yet. Add one above.</p>
<p class="text-[12px] text-text-secondary italic">No terms yet. Add one above.</p>
{:else}
<ul class="space-y-1">
{#each vocabulary as entry (entry.id)}
@@ -1387,13 +1387,13 @@
<div class="flex-1 min-w-0">
<div class="text-[13px] text-text font-medium truncate">{entry.term}</div>
{#if entry.note}
<div class="text-[11px] text-text-tertiary truncate">{entry.note}</div>
<div class="text-[12px] text-text-secondary truncate">{entry.note}</div>
{/if}
</div>
<button
type="button"
onclick={() => deleteVocabTerm(entry.id)}
class="text-[12px] text-text-tertiary hover:text-danger border border-border hover:border-danger rounded px-2 py-1"
class="text-[12px] text-text-secondary hover:text-danger border border-border hover:border-danger rounded px-2 py-1"
aria-label="Delete {entry.term}"
>Remove</button>
</li>
@@ -1401,7 +1401,7 @@
</ul>
{/if}
{:else}
<p class="text-[11px] text-text-tertiary italic">Loading profiles…</p>
<p class="text-[12px] text-text-secondary italic">Loading profiles…</p>
{/if}
</div>
</SettingsGroup>
@@ -1428,15 +1428,15 @@
<div class="group">
<div class="flex items-center gap-2 bg-bg-input rounded-lg px-3 h-[36px]">
<span class="text-[12px] text-text flex-1 truncate">{profile.name}</span>
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-tertiary">
<span class="text-[12px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-secondary">
{profileWordCount(profile.words)} words
</span>
<button
class="text-[10px] text-text-tertiary hover:text-accent opacity-0 group-hover:opacity-100"
class="text-[12px] text-text-secondary hover:text-accent opacity-0 group-hover:opacity-100"
onclick={() => editingProfile = editingProfile === i ? -1 : i}
>{editingProfile === i ? "Close" : "Edit"}</button>
<button
class="text-[10px] text-text-tertiary hover:text-danger opacity-0 group-hover:opacity-100"
class="text-[12px] text-text-secondary hover:text-danger opacity-0 group-hover:opacity-100"
onclick={() => deleteProfile(i)}
>Delete</button>
</div>
@@ -1466,8 +1466,8 @@
onkeydown={(e) => e.key === "Enter" && createProfile()}
data-no-transition
/>
<button class="text-[11px] text-accent hover:text-accent-hover" onclick={createProfile}>Create</button>
<button class="text-[11px] text-text-tertiary" onclick={() => { showNewProfile = false; newProfileName = ""; }}>Cancel</button>
<button class="text-[12px] text-accent hover:text-accent-hover" onclick={createProfile}>Create</button>
<button class="text-[12px] text-text-secondary" onclick={() => { showNewProfile = false; newProfileName = ""; }}>Cancel</button>
</div>
{:else}
<button class="text-[12px] text-accent hover:text-accent-hover" onclick={() => showNewProfile = true}>+ Add profile</button>
@@ -1486,15 +1486,15 @@
<div class="group">
<div class="flex items-center gap-2 bg-bg-input rounded-lg px-3 h-[36px]">
<span class="text-[12px] text-text flex-1 truncate">{template.name}</span>
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-tertiary">
<span class="text-[12px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-secondary">
{template.sections.length} sections
</span>
<button
class="text-[10px] text-text-tertiary hover:text-accent opacity-0 group-hover:opacity-100"
class="text-[12px] text-text-secondary hover:text-accent opacity-0 group-hover:opacity-100"
onclick={() => editingTemplate = editingTemplate === i ? -1 : i}
>{editingTemplate === i ? "Close" : "Edit"}</button>
<button
class="text-[10px] text-text-tertiary hover:text-danger opacity-0 group-hover:opacity-100"
class="text-[12px] text-text-secondary hover:text-danger opacity-0 group-hover:opacity-100"
onclick={() => deleteTemplate(i)}
>Delete</button>
</div>
@@ -1524,8 +1524,8 @@
onkeydown={(e) => e.key === "Enter" && createTemplate()}
data-no-transition
/>
<button class="text-[11px] text-accent hover:text-accent-hover" onclick={createTemplate}>Create</button>
<button class="text-[11px] text-text-tertiary" onclick={() => { showNewTemplate = false; newTemplateName = ""; }}>Cancel</button>
<button class="text-[12px] text-accent hover:text-accent-hover" onclick={createTemplate}>Create</button>
<button class="text-[12px] text-text-secondary" onclick={() => { showNewTemplate = false; newTemplateName = ""; }}>Cancel</button>
</div>
{:else}
<button class="text-[12px] text-accent hover:text-accent-hover" onclick={() => showNewTemplate = true}>+ Add template</button>
@@ -1547,9 +1547,9 @@
<!-- Engine selector -->
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Engine</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Engine</p>
<SegmentedButton options={["whisper", "parakeet"]} bind:value={settings.engine} />
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{settings.engine === "whisper"
? "Whisper with the currently shipped English-only models in this build"
: "Parakeet CTC 0.6B. English-only, fast when the model is installed"}
@@ -1558,9 +1558,9 @@
<!-- Format mode -->
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Format Mode</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Format Mode</p>
<SegmentedButton options={["Raw", "Clean", "Smart"]} bind:value={settings.formatMode} />
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{settings.formatMode === "Raw" ? "Exact Whisper output, no formatting" :
settings.formatMode === "Clean" ? "Grouped into paragraphs, punctuation tidied" :
"Structured with lists, headings, and sections"}
@@ -1570,27 +1570,27 @@
<!-- Engine-specific model management -->
{#if settings.engine === "whisper"}
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Whisper Model</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Whisper Model</p>
<SegmentedButton options={["Tiny", "Base", "Small", "Distil-S", "Medium", "Distil-L"]} bind:value={settings.modelSize} />
<p class="text-[11px] text-text-tertiary mt-2">{modelDescriptions[settings.modelSize]}</p>
<p class="text-[12px] text-text-secondary mt-2">{modelDescriptions[settings.modelSize]}</p>
<div class="flex items-center gap-2 mt-3">
{#if isModelLoaded(settings.modelSize)}
<span class="inline-flex items-center gap-1.5 text-[11px] text-success">
<span class="inline-flex items-center gap-1.5 text-[12px] text-success">
<span class="w-[6px] h-[6px] rounded-full bg-success"></span>
Model loaded
</span>
{:else if isModelDownloaded(settings.modelSize)}
<span class="inline-flex items-center gap-1.5 text-[11px] text-success">
<span class="inline-flex items-center gap-1.5 text-[12px] text-success">
<span class="w-[6px] h-[6px] rounded-full bg-success"></span>
Downloaded
</span>
<button
class="text-[11px] text-text-tertiary hover:text-accent"
class="text-[12px] text-text-secondary hover:text-accent"
onclick={loadSelectedModel}
>Load model</button>
{:else if downloadingModel === whisperModelId(settings.modelSize)}
<span class="text-[11px] text-warning">
<span class="text-[12px] text-warning">
{downloadProgress}%
{#if downloadTotal > 0}
· {formatBytes(downloadBytes)} / {formatBytes(downloadTotal)}
@@ -1604,7 +1604,7 @@
</span>
{:else}
<button
class="text-[11px] text-accent hover:text-accent-hover"
class="text-[12px] text-accent hover:text-accent-hover"
onclick={() => downloadModel(whisperModelId(settings.modelSize))}
>Download {settings.modelSize}</button>
{/if}
@@ -1612,29 +1612,29 @@
</div>
{:else}
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Parakeet Model</p>
<p class="text-[11px] text-text-tertiary mb-3">Parakeet CTC 0.6B (int8). ~613MB, near-instant transcription</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Parakeet Model</p>
<p class="text-[12px] text-text-secondary mb-3">Parakeet CTC 0.6B (int8). ~613MB, near-instant transcription</p>
<div class="flex items-center gap-2">
{#if parakeetOk}
<span class="inline-flex items-center gap-1.5 text-[11px] text-success">
<span class="inline-flex items-center gap-1.5 text-[12px] text-success">
<span class="w-[6px] h-[6px] rounded-full bg-success"></span>
Model loaded
</span>
{:else if parakeetDownloaded}
<span class="inline-flex items-center gap-1.5 text-[11px] text-text-secondary">
<span class="inline-flex items-center gap-1.5 text-[12px] text-text-secondary">
<span class="w-[6px] h-[6px] rounded-full bg-text-tertiary"></span>
Downloaded
</span>
<button
class="text-[11px] text-text-tertiary hover:text-accent"
class="text-[12px] text-text-secondary hover:text-accent"
onclick={loadParakeet}
>Load model</button>
{:else if parakeetDownloading}
<span class="text-[11px] text-warning">{parakeetProgress}% downloading...</span>
<span class="text-[12px] text-warning">{parakeetProgress}% downloading...</span>
{:else}
<button
class="text-[11px] text-accent hover:text-accent-hover"
class="text-[12px] text-accent hover:text-accent-hover"
onclick={downloadParakeet}
>Download Parakeet</button>
{/if}
@@ -1644,7 +1644,7 @@
<!-- Compute device -->
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Compute Device</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Compute Device</p>
{#if hasGpuAcceleration()}
<select
class="bg-bg-input border border-border rounded-lg px-3 py-2 text-[13px] text-text
@@ -1664,7 +1664,7 @@
{/if}
<option value="cpu">CPU</option>
</select>
<p class="text-[11px] text-text-tertiary mt-2">Only accelerators built into this binary are shown here.</p>
<p class="text-[12px] text-text-secondary mt-2">Only accelerators built into this binary are shown here.</p>
{:else}
<div class="bg-bg-input border border-border rounded-lg px-3 py-2 text-[13px] text-text w-[320px]">
This build is CPU-only. GPU controls appear in GPU-enabled builds.
@@ -1674,7 +1674,7 @@
<!-- Language -->
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Language</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Language</p>
<div class="flex items-center gap-3">
{#if currentModelIsEnglishOnly()}
<select
@@ -1709,7 +1709,7 @@
{/if}
{#if settings.language === "en"}
<button
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-[11px] border animate-fade-in
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-full text-[12px] border animate-fade-in
{settings.britishEnglish
? 'bg-accent/10 border-accent/30 text-accent font-medium'
: 'bg-bg-input border-border text-text-tertiary hover:text-text-secondary'}"
@@ -1723,7 +1723,7 @@
</button>
{/if}
</div>
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{#if currentModelIsEnglishOnly()}
The selected model only supports English in this build.
{:else}
@@ -1772,12 +1772,12 @@
open={searchActive ? searchMatches('AI Assistant Local LLM tier model management qwen llama prewarm', 'Advanced cleanup preset prompt email notes code GPU concurrency parallel sequential VRAM') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
Local LLM for transcript cleanup, smart task extraction, and task breakdown. Runs fully offline after the model is downloaded.
</p>
<div class="mb-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Feature Tier</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Feature Tier</p>
<div class="flex flex-wrap gap-2">
<button
type="button"
@@ -1804,7 +1804,7 @@
onclick={() => setAiTier("tasks")}
>Cleanup + Tasks</button>
</div>
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{settings.aiTier === "off"
? "No local LLM calls. Magnotia falls back to the existing rule-based path."
: settings.aiTier === "cleanup"
@@ -1814,7 +1814,7 @@
</div>
<div class="mb-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Model Tier</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Model Tier</p>
<div class="space-y-2">
{#each LLM_MODELS as model}
<button
@@ -1830,14 +1830,14 @@
<div>
<div class="flex items-center gap-2">
<span class="text-[12px] font-medium text-text">{model.label}</span>
<span class="text-[11px] text-text-secondary">{model.subtitle}</span>
<span class="text-[12px] text-text-secondary">{model.subtitle}</span>
</div>
<p class="text-[11px] text-text-tertiary mt-1">{model.size} · {model.fit}</p>
<p class="text-[12px] text-text-secondary mt-1">{model.size} · {model.fit}</p>
{#if llmHardwareWarning(model.id)}
<p class="text-[11px] text-warning mt-2">{llmHardwareWarning(model.id)}</p>
<p class="text-[12px] text-warning mt-2">{llmHardwareWarning(model.id)}</p>
{/if}
</div>
<div class="text-right text-[11px]">
<div class="text-right text-[12px]">
{#if llmModelLoaded(model.id)}
<span class="text-success">Loaded</span>
{:else if llmModelDownloaded(model.id)}
@@ -1858,15 +1858,15 @@
<p class="text-[12px] text-text-secondary font-medium">
{LLM_MODELS.find((model) => model.id === selectedLlmModelId())?.subtitle || "Local model"}
</p>
<p class="text-[11px] text-text-tertiary mt-1">{llmStatus}</p>
<p class="text-[12px] text-text-secondary mt-1">{llmStatus}</p>
{#if llmTestHint}
<p class="text-[11px] text-accent mt-1">{llmTestHint}</p>
<p class="text-[12px] text-accent mt-1">{llmTestHint}</p>
{/if}
</div>
<div class="flex items-center gap-2 flex-wrap">
{#if llmDownloadingModel === selectedLlmModelId()}
<span class="text-[11px] text-warning">
<span class="text-[12px] text-warning">
{llmDownloadProgress}%
{#if llmDownloadTotal > 0}
· {formatBytes(llmDownloadBytes)} / {formatBytes(llmDownloadTotal)}
@@ -1922,7 +1922,7 @@
</div>
</div>
<p class="text-[11px] text-text-tertiary mt-3">
<p class="text-[12px] text-text-secondary mt-3">
Recommended for this machine:
<span class="text-text">
{LLM_MODELS.find((model) => model.id === (settings.llmModelId || "qwen3_5_4b"))?.subtitle || "Qwen3.5 4B"}
@@ -1955,12 +1955,12 @@
structure of LLM cleanup output; composes on top of the
active profile's initial prompt. -->
<div>
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Cleanup preset</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Cleanup preset</p>
<SegmentedButton
options={["default", "email", "notes", "code"]}
bind:value={settings.llmPromptPreset}
/>
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{#if settings.llmPromptPreset === "email"}
Formats output as an email paragraph, tight sentences, no markdown, no auto-added greeting or signoff.
{:else if settings.llmPromptPreset === "notes"}
@@ -1979,12 +1979,12 @@
other; adds reload latency between transcribe and
cleanup phases. -->
<div class="mt-5">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">GPU concurrency</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">GPU concurrency</p>
<SegmentedButton
options={["parallel", "sequential"]}
bind:value={settings.aiGpuConcurrency}
/>
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
{#if settings.aiGpuConcurrency === "sequential"}
On tight-VRAM cards (≤6 GB), loading Whisper + LLM together OOMs. Sequential mode frees the other model before loading; adds a small reload pause between transcribe and cleanup.
{:else}
@@ -2026,7 +2026,7 @@
open={searchActive ? searchMatches('Rituals morning triage wind-down launch login autostart') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
All off by default. Rituals only appear when you ask for them.
</p>
@@ -2038,7 +2038,7 @@
{#if settings.ritualsMorning}
<div class="pl-1 py-3 animate-fade-in">
<label for="triage-time" class="block text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">
<label for="triage-time" class="block text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">
Earliest triage time
</label>
<input
@@ -2047,7 +2047,7 @@
class="bg-bg-input border border-border rounded-lg px-3 py-1.5 text-[12px] text-text focus:border-accent focus:outline-none"
bind:value={settings.ritualsMorningTime}
/>
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
ADHD sleep inertia is intense for the first 3045 minutes after waking. Pick a time when you're genuinely ready to decide.
</p>
</div>
@@ -2093,7 +2093,7 @@
open={searchActive ? searchMatches('Tasks page gamification visuals header sparkline') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
How the Tasks page surfaces progress. Always additive.
</p>
@@ -2113,7 +2113,7 @@
open={searchActive ? searchMatches('Nudges soft notifications hourly') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
Gentle, anticipatory reminders. Capped at 3 per hour. Never fires while you're looking at Magnotia.
</p>
@@ -2158,12 +2158,12 @@
open={searchActive ? searchMatches('Read aloud TTS voice rate system') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
<p class="text-[12px] text-text-secondary mb-4">
Uses your operating system's built-in voices. No audio leaves the machine.
</p>
<div class="mb-4">
<label for="tts-voice" class="block text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Voice</label>
<label for="tts-voice" class="block text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Voice</label>
<select
id="tts-voice"
class="w-full bg-bg-input border border-border rounded-lg px-3 py-2 text-[12px] text-text focus:border-accent focus:outline-none"
@@ -2179,16 +2179,16 @@
{/each}
</select>
{#if ttsVoicesError}
<p class="text-[11px] text-danger mt-2">{ttsVoicesError}</p>
<p class="text-[12px] text-danger mt-2">{ttsVoicesError}</p>
{:else if ttsVoicesLoaded && ttsVoices.length === 0}
<p class="text-[11px] text-text-tertiary mt-2">
<p class="text-[12px] text-text-secondary mt-2">
No additional voices reported by the system synth. Install extra voices through your OS accessibility settings.
</p>
{/if}
</div>
<div class="mb-4">
<label for="tts-rate" class="block text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">
<label for="tts-rate" class="block text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">
Rate · {settings.ttsRate.toFixed(1)}×
</label>
<input
@@ -2200,7 +2200,7 @@
class="w-full accent-accent"
bind:value={settings.ttsRate}
/>
<div class="flex justify-between text-[10px] text-text-tertiary mt-1">
<div class="flex justify-between text-[12px] text-text-secondary mt-1">
<span>Slower</span>
<span>Normal</span>
<span>Faster</span>
@@ -2242,7 +2242,7 @@
/>
{#if settings.meetingAutoCapture}
<div class="ml-[50px] mt-2 mb-1 animate-fade-in">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-1.5">Apps to watch (comma-separated)</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-1.5">Apps to watch (comma-separated)</p>
<input
type="text"
class="w-full bg-bg-input border border-border-subtle rounded-lg px-3 py-1.5 text-[12px] text-text"
@@ -2264,7 +2264,7 @@
/>
{#if settings.soundCues}
<div class="ml-[50px] mt-2 mb-1 animate-fade-in flex items-center gap-3">
<label for="sound-cue-volume" class="text-[11px] text-text-tertiary uppercase tracking-wider">Volume</label>
<label for="sound-cue-volume" class="text-[12px] text-text-secondary uppercase tracking-wider">Volume</label>
<input
id="sound-cue-volume"
type="range"
@@ -2275,9 +2275,9 @@
class="w-[160px] accent-accent"
data-no-transition
/>
<span class="text-[11px] text-text-secondary w-[34px]">{Math.round(settings.soundCueVolume * 100)}%</span>
<span class="text-[12px] text-text-secondary w-[34px]">{Math.round(settings.soundCueVolume * 100)}%</span>
<button
class="text-[11px] text-accent hover:text-accent-hover"
class="text-[12px] text-accent hover:text-accent-hover"
onclick={async () => {
const mod = await import("$lib/utils/sounds.js");
mod.playCompleteCue(settings.soundCueVolume, true);
@@ -2293,15 +2293,15 @@
/>
{#if settings.saveAudio}
<div class="ml-[50px] mt-2 animate-fade-in">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-1.5">Output Folder</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-1.5">Output Folder</p>
<div class="flex items-center gap-2">
<div bind:this={outputFolderEl} class="flex-1 bg-bg-input rounded-lg px-3 py-1.5 border border-border-subtle">
<p class="text-[11px] text-text-secondary whitespace-pre-wrap break-words" style="line-height: {settingsPathLineHeight}px" title={settings.outputFolder || "Default (app data)"}>
<p class="text-[12px] text-text-secondary whitespace-pre-wrap break-words" style="line-height: {settingsPathLineHeight}px" title={settings.outputFolder || "Default (app data)"}>
{outputFolderPreview}
</p>
</div>
<button
class="text-[11px] text-accent hover:text-accent-hover whitespace-nowrap"
class="text-[12px] text-accent hover:text-accent-hover whitespace-nowrap"
onclick={async () => {
try {
const folder = await open({ directory: true, title: "Select output folder" });
@@ -2311,7 +2311,7 @@
>Change</button>
{#if settings.outputFolder}
<button
class="text-[11px] text-text-tertiary hover:text-text-secondary whitespace-nowrap"
class="text-[12px] text-text-secondary hover:text-text-secondary whitespace-nowrap"
onclick={() => { settings.outputFolder = ""; saveSettings(); }}
>Reset</button>
{/if}
@@ -2341,7 +2341,7 @@
open={searchActive ? searchMatches('Global hotkey toggle recording shortcut') : false}
>
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">Toggle recording from anywhere. Click to change.</p>
<p class="text-[12px] text-text-secondary mb-4">Toggle recording from anywhere. Click to change.</p>
<HotkeyRecorder />
</div>
</SettingsGroup>
@@ -2354,17 +2354,17 @@
>
<div class="animate-fade-in">
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Theme</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Theme</p>
<SegmentedButton options={["Dark", "Light", "System"]} bind:value={settings.theme} />
</div>
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Zone</p>
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Zone</p>
<ZonePicker />
</div>
<div class="mb-6">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">
Font Size <span class="font-normal text-text-secondary ml-1">{settings.fontSize}px</span>
</p>
<input
@@ -2376,7 +2376,7 @@
</div>
<div class="mb-2">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">
{$_("settings.language")}
</p>
<div class="inline-flex bg-bg-elevated rounded-[10px] p-[3px] gap-[2px]">
@@ -2391,7 +2391,7 @@
>{option.label}</button>
{/each}
</div>
<p class="text-[11px] text-text-tertiary mt-2">{$_("settings.languageDescription")}</p>
<p class="text-[12px] text-text-secondary mt-2">{$_("settings.languageDescription")}</p>
</div>
</div>
</SettingsGroup>
@@ -2428,17 +2428,17 @@
] as item}
<div class="flex items-start gap-2">
<Check class="w-3.5 h-3.5 text-success mt-0.5 flex-shrink-0" />
<p class="text-[11px] text-text-secondary">{item}</p>
<p class="text-[12px] text-text-secondary">{item}</p>
</div>
{/each}
</div>
<p class="text-[11px] text-text-tertiary mt-5">Magnotia v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
<p class="text-[12px] text-text-secondary mt-5">Magnotia v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
<!-- Diagnostic report (privacy posture: local only until you share it) -->
<div class="mt-6 pt-5 border-t border-border-subtle">
<p class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Diagnostics</p>
<p class="text-[11px] text-text-tertiary mb-3">
<p class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Diagnostics</p>
<p class="text-[12px] text-text-secondary mb-3">
If something goes wrong, generate a diagnostic report to share with the developer. The report contains your settings, recent errors, any crash dumps, and the tail of the log file. Nothing is sent automatically, you preview it first, then choose copy or save.
</p>
<div class="flex gap-2 mb-3">
@@ -2462,15 +2462,15 @@
{/if}
</div>
{#if diagnosticReportError}
<p class="text-[11px] text-danger mb-2">{diagnosticReportError}</p>
<p class="text-[12px] text-danger mb-2">{diagnosticReportError}</p>
{/if}
{#if diagnosticReportSavedTo}
<p class="text-[11px] text-success mb-2">{diagnosticReportSavedTo}</p>
<p class="text-[12px] text-success mb-2">{diagnosticReportSavedTo}</p>
{/if}
{#if diagnosticReport}
<details class="text-[11px]">
<details class="text-[12px]">
<summary class="cursor-pointer text-text-secondary hover:text-text">Preview report ({diagnosticReport.length} chars)</summary>
<pre class="mt-2 p-3 bg-bg-input border border-border rounded-lg text-[10px] text-text-secondary overflow-auto max-h-[400px] whitespace-pre-wrap font-mono">{diagnosticReport}</pre>
<pre class="mt-2 p-3 bg-bg-input border border-border rounded-lg text-[12px] text-text-secondary overflow-auto max-h-[400px] whitespace-pre-wrap font-mono">{diagnosticReport}</pre>
</details>
{/if}
</div>

View File

@@ -86,7 +86,7 @@
<div class="px-7 pb-8 max-w-[640px]">
{#if loading}
<p class="text-[12px] text-text-tertiary py-6">Looking at today…</p>
<p class="text-[12px] text-text-secondary py-6">Looking at today…</p>
{:else}
<!-- Additive framing: lead with what the user did, never with what they didn't. -->
<section class="mb-8">
@@ -112,11 +112,11 @@
<!-- Read-only reflection. The Tasks page is where things get done. -->
<section class="mb-8">
<h2 class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Open loops</h2>
<h2 class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Open loops</h2>
{#if openLoops.length === 0}
<p class="text-[12px] text-text-secondary">Nothing in the list.</p>
{:else}
<p class="text-[11px] text-text-tertiary mb-3">
<p class="text-[12px] text-text-secondary mb-3">
These are still here. Naming them silences the loop, you don't have to act now.
</p>
<ul class="flex flex-col gap-1">
@@ -126,7 +126,7 @@
</li>
{/each}
{#if openLoops.length > 12}
<li class="text-[11px] text-text-tertiary pt-1">
<li class="text-[12px] text-text-secondary pt-1">
…and {openLoops.length - 12} more.
</li>
{/if}
@@ -138,7 +138,7 @@
template, ~40% rumination reduction in psychological-detachment
studies. -->
<section class="mb-8">
<h2 class="text-[10px] font-medium text-text-tertiary uppercase tracking-wider mb-2">Before you close</h2>
<h2 class="text-[12px] font-medium text-text-secondary uppercase tracking-wider mb-2">Before you close</h2>
<ol class="flex flex-col gap-2 text-[13px] text-text-secondary">
<li>
<span class="font-medium text-text">Take a breath.</span>

View File

@@ -298,7 +298,7 @@
>
{#if todayCount() > 0}
<span
class="text-[11px] text-text-tertiary badge-today"
class="text-[12px] text-text-secondary badge-today"
aria-label={`${todayCount()} ${todayCount() === 1 ? "task" : "tasks"} completed today`}
>
{todayCount()} today
@@ -311,7 +311,7 @@
</div>
</div>
<p class="text-[11px] text-text-tertiary mt-1">Add tasks manually. Automatic extraction from your transcripts is coming.</p>
<p class="text-[12px] text-text-secondary mt-1">Add tasks manually. Automatic extraction from your transcripts is coming.</p>
</div>
<div class="flex-1"></div>
@@ -319,7 +319,7 @@
toggle for the sort. Sits in the header so it is always visible
from any bucket / list context. -->
<div class="flex items-center gap-2 mr-2">
<span class="text-[10px] text-text-tertiary hidden sm:inline" aria-hidden="true">I feel</span>
<span class="text-[12px] text-text-secondary hidden sm:inline" aria-hidden="true">I feel</span>
<div
bind:this={energyRadioGroupEl}
class="flex items-center gap-0.5 bg-bg-input border border-border-subtle rounded-lg p-0.5"
@@ -331,7 +331,7 @@
{#each energyOptions as opt}
{@const checked = settings.currentEnergy === opt.value}
<button
class="text-[10px] px-2 py-0.5 rounded-md
class="text-[12px] px-2 py-0.5 rounded-md
{checked
? 'bg-accent/25 text-accent border border-accent/30'
: 'text-text-secondary hover:text-text'}"
@@ -344,7 +344,7 @@
{/each}
</div>
<button
class="flex items-center gap-1 btn-md rounded-lg text-[10px]
class="flex items-center gap-1 btn-md rounded-lg text-[12px]
{settings.matchMyEnergy
? 'bg-accent/25 text-accent border border-accent/30'
: 'text-text-secondary hover:bg-hover hover:text-text border border-transparent'}"
@@ -383,7 +383,7 @@
data-no-transition
/>
{#if searchQuery}
<button class="text-[10px] text-text-tertiary hover:text-text" onclick={() => searchQuery = ""}>Clear</button>
<button class="text-[12px] text-text-secondary hover:text-text" onclick={() => searchQuery = ""}>Clear</button>
{/if}
</div>
</div>
@@ -401,7 +401,7 @@
onkeydown={handleQuickAdd}
/>
{#if quickInput.trim()}
<span class="text-[10px] text-text-tertiary px-1.5 py-0.5 rounded bg-bg-elevated border border-border-subtle">
<span class="text-[12px] text-text-secondary px-1.5 py-0.5 rounded bg-bg-elevated border border-border-subtle">
&rarr; {activeBucket === "all" ? "inbox" : activeBucket}
</span>
{/if}
@@ -422,7 +422,7 @@
>
{bucket.label}
{#if bucketCounts[bucket.id] > 0}
<span class="text-[10px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-tertiary">
<span class="text-[12px] px-1.5 py-0.5 rounded-full bg-bg-elevated text-text-secondary">
{bucketCounts[bucket.id]}
</span>
{/if}
@@ -435,7 +435,7 @@
<!-- Sort dropdown -->
<div class="relative">
<button
class="flex items-center gap-1 px-2 py-1.5 rounded-lg text-[11px] text-text-tertiary hover:text-text-secondary hover:bg-hover"
class="flex items-center gap-1 px-2 py-1.5 rounded-lg text-[12px] text-text-secondary hover:text-text-secondary hover:bg-hover"
onclick={() => showSortMenu = !showSortMenu}
aria-label="Sort tasks"
>
@@ -446,7 +446,7 @@
<div class="absolute right-0 top-full mt-1 bg-bg-card border border-border rounded-lg shadow-lg py-1 z-20 animate-fade-in min-w-[120px]">
{#each [["date", "By date"], ["quick-first", "Quick first"], ["deep-first", "Deep first"]] as [mode, label]}
<button
class="w-full text-left px-3 py-1 text-[11px] hover:bg-hover
class="w-full text-left px-3 py-1 text-[12px] hover:bg-hover
{sortMode === mode ? 'text-accent font-medium' : 'text-text-secondary hover:text-text'}"
onclick={() => { sortMode = mode; showSortMenu = false; }}
>{label}</button>
@@ -494,7 +494,7 @@
<input
bind:this={editingInputEl}
type="text"
class="w-full bg-bg-input border border-accent rounded px-2 py-1 text-[10px] text-text"
class="w-full bg-bg-input border border-accent rounded px-2 py-1 text-[12px] text-text"
bind:value={editingName}
onkeydown={(e) => { if (e.key === "Enter") finishRenaming(); if (e.key === "Escape") { editingListId = null; } }}
onblur={finishRenaming}
@@ -504,7 +504,7 @@
{:else}
<div class="relative">
<button
class="w-full flex items-center gap-1.5 px-2 py-1.5 rounded-md text-left text-[11px]
class="w-full flex items-center gap-1.5 px-2 py-1.5 rounded-md text-left text-[12px]
{activeListId === list.id
? 'text-text font-medium bg-accent/10'
: 'text-text-secondary hover:bg-hover hover:text-text'}"
@@ -514,13 +514,13 @@
title={sidebarCollapsed ? `${list.name} (${countForList(list.id)})` : ""}
>
{#if sidebarCollapsed}
<span class="text-[9px] px-1 py-0 rounded-full bg-bg-card text-text-tertiary min-w-[16px] text-center mx-auto">
<span class="text-[12px] px-1 py-0 rounded-full bg-bg-card text-text-secondary min-w-[16px] text-center mx-auto">
{countForList(list.id)}
</span>
{:else}
<span class="flex-1 truncate">{list.name}</span>
{#if countForList(list.id) > 0}
<span class="text-[9px] px-1 py-0 rounded-full bg-bg-card text-text-tertiary min-w-[16px] text-center">
<span class="text-[12px] px-1 py-0 rounded-full bg-bg-card text-text-secondary min-w-[16px] text-center">
{countForList(list.id)}
</span>
{/if}
@@ -531,12 +531,12 @@
{#if contextMenuListId === list.id && !sidebarCollapsed}
<div class="absolute left-2 top-full mt-0.5 bg-bg-card border border-border rounded-lg shadow-lg py-1 z-20 animate-fade-in min-w-[100px]">
<button
class="w-full text-left px-3 py-1 text-[10px] text-text-secondary hover:bg-hover hover:text-text"
class="w-full text-left px-3 py-1 text-[12px] text-text-secondary hover:bg-hover hover:text-text"
onclick={() => startRenaming(list)}
>Rename</button>
<div class="my-1 h-px bg-border-subtle"></div>
<button
class="w-full text-left px-3 py-1 text-[10px] text-danger hover:bg-hover"
class="w-full text-left px-3 py-1 text-[12px] text-danger hover:bg-hover"
onclick={() => handleDeleteList(list.id)}
>Delete</button>
</div>
@@ -553,7 +553,7 @@
<input
bind:this={newListInputEl}
type="text"
class="w-full bg-bg-input border border-border rounded px-2 py-1 text-[10px] text-text
class="w-full bg-bg-input border border-border rounded px-2 py-1 text-[12px] text-text
placeholder:text-text-tertiary focus:border-accent"
placeholder="List name..."
bind:value={newListName}
@@ -563,7 +563,7 @@
/>
{:else}
<button
class="w-full text-left text-[10px] text-accent hover:text-accent-hover px-1"
class="w-full text-left text-[12px] text-accent hover:text-accent-hover px-1"
onclick={() => showNewList = true}
>+ New list</button>
{/if}
@@ -601,7 +601,7 @@
<!-- Bucket pills -->
{#each taskBucketOptions as b}
<button
class="text-[10px] px-2 py-0.5 rounded-full border
class="text-[12px] px-2 py-0.5 rounded-full border
{task.bucket === b
? `${BUCKET_COLORS[b]} border-current bg-current/10 font-medium`
: 'text-text-tertiary border-transparent hover:border-border-subtle hover:text-text-secondary'}"
@@ -613,7 +613,7 @@
<!-- Effort pills -->
{#each effortOptions as e}
<button
class="text-[10px] px-2 py-0.5 rounded-full border
class="text-[12px] px-2 py-0.5 rounded-full border
{task.effort === e
? 'text-accent border-accent/30 bg-accent/10 font-medium'
: 'text-text-tertiary border-transparent hover:border-border-subtle hover:text-text-secondary'}"
@@ -630,12 +630,12 @@
/>
<!-- Timestamp -->
{#if task.createdAt}
<span class="text-[10px] text-text-tertiary ml-auto">{formatTimestamp(task.createdAt)}</span>
<span class="text-[12px] text-text-secondary ml-auto">{formatTimestamp(task.createdAt)}</span>
{/if}
</div>
<!-- List name (only when viewing all lists) -->
{#if activeListId === "all" && getListName(task.listId)}
<p class="text-[10px] text-text-tertiary italic mt-1">{getListName(task.listId)}</p>
<p class="text-[12px] text-text-secondary italic mt-1">{getListName(task.listId)}</p>
{/if}
</div>
@@ -657,7 +657,7 @@
{#if completedTasks.length > 0}
<div class="mt-6">
<button
class="flex items-center gap-2 text-[12px] text-text-tertiary hover:text-text-secondary mb-2"
class="flex items-center gap-2 text-[12px] text-text-secondary hover:text-text-secondary mb-2"
onclick={() => showCompleted = !showCompleted}
>
<ChevronRight size={12} class={showCompleted ? "rotate-90" : ""} aria-hidden="true"
@@ -679,7 +679,7 @@
<div class="flex-1 min-w-0">
<p class="text-[13px] text-text-secondary line-through">{task.text}</p>
{#if task.doneAt}
<p class="text-[10px] text-text-tertiary mt-0.5">Completed {formatTimestamp(task.doneAt)}</p>
<p class="text-[12px] text-text-secondary mt-0.5">Completed {formatTimestamp(task.doneAt)}</p>
{/if}
</div>
<button