agent: lumotia-rebrand — frontend strings (svelte + i18n + design-system)
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled

Phase 8 of the rebrand cascade. Every rendered string is now Lumotia;
no Magnotia surface visible in the UI.

Sweep replaced \bmagnotia\b -> lumotia and \bMagnotia\b -> Lumotia
across all .svelte / .ts / .js / .css / .html / .json (excluding
package-lock.json which regenerates, target/, build/, node_modules/).

Surfaces touched:
- src/app.css — design-token comment header and .magnotia-rh-* CSS
  resize-handle class selectors (also the consuming elements in
  components/ResizeHandles.svelte and src/routes/*/+layout.svelte).
- src/lib/i18n/locales/{en,de,es}.json — brand name in translations.
- src/lib/i18n/index.ts — header comment.
- src/lib/Sidebar.svelte and most pages under src/lib/pages/ +
  src/lib/components/ — title bars, document titles, default
  filenames (lumotia-YYYY-MM-DD.* etc), toast strings, error
  messages, dialog headers.
- src/routes/+layout.svelte, +page.svelte, viewer/, float/, preview/.
- src/app.html page <title>.
- src/lib/utils/settingsMigrations.ts — fallback toast copy.
- src/design-system/{colors_and_type.css,SKILL.md,README.md,
  ui_kits/{Sidebar.jsx,index.html}} — design-tokens, doc strings,
  preview wordmark in the kit.
- package.json — name + description.

NOT touched (deferred / immutable):
- package-lock.json — regenerates on next npm install.
- The two migration-call sites in stores reference the legacy magnotia
  keys deliberately; restored after the sweep clobbered them.
- docs/, README.md, HANDOVER.md — Phase 9 scope.

npm run check: 0 errors / 0 warnings (3958 files).
cargo test --workspace: 339 pass / 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 12:29:37 +01:00
parent 16081095e0
commit 681a9b26dc
37 changed files with 115 additions and 115 deletions

View File

@@ -47,17 +47,17 @@
<div class="flex items-center {collapsed ? 'justify-center px-0 pt-4 pb-1' : 'px-5 pt-4 pb-1'} relative">
{#if !collapsed}
<div class="flex items-center gap-2 flex-1 min-w-0">
<h1 class="font-display text-[26px] text-text tracking-tight leading-none italic">Magnotia</h1>
<h1 class="font-display text-[26px] text-text tracking-tight leading-none italic">Lumotia</h1>
<span
class="text-[18px] text-accent inline-block {page.recording ? 'animate-sinhala-spin' : ''}"
title="Magnotia"
title="Lumotia"
>&#3559;</span>
</div>
{:else}
<!-- Collapsed: just the decorative character centred -->
<span
class="text-[18px] text-accent inline-block {page.recording ? 'animate-sinhala-spin' : ''}"
title="Magnotia"
title="Lumotia"
>&#3559;</span>
{/if}
</div>

View File

@@ -5,7 +5,7 @@
// centre. Completion plays a gentle chime, flashes a success ring
// for 3 s, then disappears. Cancel button appears on hover.
//
// Mounted once in +layout.svelte. Listens for `magnotia:start-timer`
// Mounted once in +layout.svelte. Listens for `lumotia:start-timer`
// events from anywhere in the app (e.g. MicroSteps) and delegates
// to the focus-timer store.
//
@@ -63,7 +63,7 @@
return "var(--color-accent)";
});
// Event handler: start a timer when any component fires `magnotia:start-timer`.
// Event handler: start a timer when any component fires `lumotia:start-timer`.
// Payload shape from MicroSteps.svelte and task row buttons:
// { taskId?: string, seconds: number, label?: string }
function handleStartEvent(evt: Event) {

View File

@@ -26,7 +26,7 @@
]);
// Map KeyboardEvent.code (physical key, layout-independent, shift-independent)
// to the unshifted name the magnotia-hotkey evdev parser understands. Browser
// to the unshifted name the lumotia-hotkey evdev parser understands. Browser
// e.key returns "+" for Shift+Equal — parser wants "=". Same for | → \, etc.
const codeToParserKey: Record<string, string> = {
Equal: "=", Minus: "-",

View File

@@ -72,7 +72,7 @@
<h3 class="text-[18px] font-semibold text-text mb-2">Download Whisper Model</h3>
<p class="text-[13px] text-text-secondary mb-1">
Magnotia needs the <span class="font-medium text-text">{modelSize}</span> model to transcribe speech.
Lumotia needs the <span class="font-medium text-text">{modelSize}</span> model to transcribe speech.
</p>
<p class="text-[12px] text-text-secondary mb-6">
{currentModelInfo?.size ?? "?"} · {currentModelInfo?.accuracy ?? "?"} accuracy · 100% offline

View File

@@ -1,6 +1,6 @@
<script lang="ts">
// Invisible resize-handle overlays around each Magnotia window edge.
// Needed because Magnotia runs with decorations off, so the WM
// Invisible resize-handle overlays around each Lumotia window edge.
// Needed because Lumotia runs with decorations off, so the WM
// provides no resize affordance on KDE/GNOME Wayland.
//
// Architecture: eight fixed-position divs are the click target for
@@ -10,9 +10,9 @@
// a window move. This gives us a clean separation: resize wins on
// the edges, drag wins on the titlebar.
//
// Hit zone sizes come from CSS custom properties so every Magnotia window
// across the app stays identical. See the magnotia-resize-edge and
// magnotia-resize-corner custom properties in app.css.
// Hit zone sizes come from CSS custom properties so every Lumotia window
// across the app stays identical. See the lumotia-resize-edge and
// lumotia-resize-corner custom properties in app.css.
//
// Placement requirement: instances of this component MUST be mounted
// as a sibling of the animated/transformed layout root (not a child),
@@ -48,20 +48,20 @@
{#if enabled}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-n" onpointerdown={(e) => startResize(e,"North")}></div>
<div class="lumotia-rh lumotia-rh-n" onpointerdown={(e) => startResize(e,"North")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-s" onpointerdown={(e) => startResize(e,"South")}></div>
<div class="lumotia-rh lumotia-rh-s" onpointerdown={(e) => startResize(e,"South")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-w" onpointerdown={(e) => startResize(e,"West")}></div>
<div class="lumotia-rh lumotia-rh-w" onpointerdown={(e) => startResize(e,"West")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-e" onpointerdown={(e) => startResize(e,"East")}></div>
<div class="lumotia-rh lumotia-rh-e" onpointerdown={(e) => startResize(e,"East")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-nw" onpointerdown={(e) => startResize(e,"NorthWest")}></div>
<div class="lumotia-rh lumotia-rh-nw" onpointerdown={(e) => startResize(e,"NorthWest")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-ne" onpointerdown={(e) => startResize(e,"NorthEast")}></div>
<div class="lumotia-rh lumotia-rh-ne" onpointerdown={(e) => startResize(e,"NorthEast")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-sw" onpointerdown={(e) => startResize(e,"SouthWest")}></div>
<div class="lumotia-rh lumotia-rh-sw" onpointerdown={(e) => startResize(e,"SouthWest")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="magnotia-rh magnotia-rh-se" onpointerdown={(e) => startResize(e,"SouthEast")}></div>
<div class="lumotia-rh lumotia-rh-se" onpointerdown={(e) => startResize(e,"SouthEast")}></div>
{/if}

View File

@@ -2,7 +2,7 @@
"settings": {
"title": "Einstellungen",
"language": "Sprache",
"languageDescription": "Oberflächensprache von Magnotia. Wirkt sich nicht auf die Transkription aus."
"languageDescription": "Oberflächensprache von Lumotia. Wirkt sich nicht auf die Transkription aus."
},
"history": {
"title": "Verlauf",

View File

@@ -2,7 +2,7 @@
"settings": {
"title": "Settings",
"language": "Language",
"languageDescription": "Magnotia's interface language. Affects labels, not transcription."
"languageDescription": "Lumotia's interface language. Affects labels, not transcription."
},
"history": {
"title": "History",

View File

@@ -2,7 +2,7 @@
"settings": {
"title": "Ajustes",
"language": "Idioma",
"languageDescription": "Idioma de la interfaz de Magnotia. No afecta a la transcripción."
"languageDescription": "Idioma de la interfaz de Lumotia. No afecta a la transcripción."
},
"history": {
"title": "Historial",

View File

@@ -25,7 +25,7 @@
import { hasTauriRuntime } from '$lib/utils/runtime.js';
const prefs = getPreferences();
const tauriRuntimeAvailable = hasTauriRuntime();
const browserPreviewMessage = "You're viewing Magnotia in a normal browser. Local transcription only works in the Tauri desktop app window.";
const browserPreviewMessage = "You're viewing Lumotia in a normal browser. Local transcription only works in the Tauri desktop app window.";
let transcript = $state("");
let segments = $state([]);
@@ -199,7 +199,7 @@
lastLiveActivityAt = Date.now();
if (status.type === "overload" || status.type === "warning") {
liveWarning = status.message || "Magnotia is dropping older audio to stay responsive.";
liveWarning = status.message || "Lumotia is dropping older audio to stay responsive.";
return;
}
@@ -643,7 +643,7 @@
const content = exportTranscript(transcript, segments, format);
const extMap = { txt: "txt", vtt: "vtt", srt: "srt", md: "md", csv: "csv", html: "html" };
const ext = extMap[format] || "txt";
const defaultPath = `magnotia-${new Date().toISOString().slice(0, 10)}.${ext}`;
const defaultPath = `lumotia-${new Date().toISOString().slice(0, 10)}.${ext}`;
if (hasTauriRuntime()) {
try {

View File

@@ -140,7 +140,7 @@
const content = exportTranscript(fileTranscript, segments, format);
const extMap = { txt: "txt", vtt: "vtt", srt: "srt", md: "md", csv: "csv", html: "html" };
const ext = extMap[format] || "txt";
const defaultPath = `magnotia-file-${new Date().toISOString().slice(0, 10)}.${ext}`;
const defaultPath = `lumotia-file-${new Date().toISOString().slice(0, 10)}.${ext}`;
if (hasTauriRuntime()) {
try {

View File

@@ -225,9 +225,9 @@
{:else if ritualsStep === "autostart"}
<div class="w-full max-w-md mx-auto text-center">
<Play size={32} strokeWidth={1.5} class="text-accent mx-auto mb-3" />
<h2 class="text-xl font-medium text-text">Launch Magnotia at login?</h2>
<h2 class="text-xl font-medium text-text">Launch Lumotia at login?</h2>
<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.
So Lumotia 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-[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">
@@ -270,7 +270,7 @@
{:else}
<div class="w-full">
<h2 class="text-2xl font-medium text-text text-center">Welcome to Magnotia</h2>
<h2 class="text-2xl font-medium text-text text-center">Welcome to Lumotia</h2>
<p class="text-sm text-text-secondary text-center mt-2">Press the button. Start talking. That's it.</p>
{#if error}

View File

@@ -767,7 +767,7 @@
async function testReadAloudVoice() {
try {
await invoke("tts_speak", {
text: "This is Magnotia reading aloud.",
text: "This is Lumotia reading aloud.",
rate: settings.ttsRate,
voice: settings.ttsVoice ?? null,
});
@@ -1155,7 +1155,7 @@
bind:value={settings.microphoneDevice}
onfocus={refreshAudioDevices}
>
<option value="">Auto (recommended), let Magnotia pick the working mic</option>
<option value="">Auto (recommended), let Lumotia pick the working mic</option>
{#each visibleAudioDevices as dev}
<option value={dev.name} disabled={dev.is_likely_monitor}>
{friendlyLabel(dev)}{#if dev.is_default && dev.name !== "default"} (system default){/if}{#if dev.is_likely_monitor}, speaker monitor, skip{/if}
@@ -1808,7 +1808,7 @@
</div>
<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."
? "No local LLM calls. Lumotia falls back to the existing rule-based path."
: settings.aiTier === "cleanup"
? "Use the local model for transcript cleanup and formatting."
: "Use the local model for cleanup, task extraction, and task breakdown."}
@@ -2080,8 +2080,8 @@
about whether autostart is registered. -->
<Toggle
bind:checked={settings.launchAtLogin}
label="Launch Magnotia at login"
description="So Magnotia is already there at the start of the day. Uses your OS's standard autostart, no background tricks."
label="Launch Lumotia at login"
description="So Lumotia is already there at the start of the day. Uses your OS's standard autostart, no background tricks."
onChange={setLaunchAtLogin}
/>
</div>
@@ -2116,7 +2116,7 @@
>
<div class="animate-fade-in">
<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.
Gentle, anticipatory reminders. Capped at 3 per hour. Never fires while you're looking at Lumotia.
</p>
<Toggle
@@ -2234,13 +2234,13 @@
/>
<Toggle
bind:checked={settings.transcriptionPreview}
label="Floating preview when Magnotia is unfocused"
label="Floating preview when Lumotia is unfocused"
description="Shows a small always-on-top window with the raw transcription as you dictate, then the final formatted text. Only opens when the main window is unfocused or hidden."
/>
<Toggle
bind:checked={settings.meetingAutoCapture}
label="Remind me when a meeting starts"
description="Toasts when a matching app appears in the process list. You still hit the hotkey. Magnotia never records on its own."
description="Toasts when a matching app appears in the process list. You still hit the hotkey. Lumotia never records on its own."
/>
{#if settings.meetingAutoCapture}
<div class="ml-[50px] mt-2 mb-1 animate-fade-in">
@@ -2435,7 +2435,7 @@
{/each}
</div>
<p class="text-[12px] text-text-secondary mt-5">Magnotia v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
<p class="text-[12px] text-text-secondary mt-5">Lumotia 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">

View File

@@ -3,10 +3,10 @@
//
// Refresh triggers:
// - module load (first time)
// - magnotia:task-completed (from page.svelte.ts completeTask)
// - magnotia:step-completed (from MicroSteps.svelte)
// - magnotia:task-uncompleted (new event, emitted by uncompleteTask in Task 10)
// - magnotia:task-deleted (new event, emitted by deleteTask in Task 10)
// - lumotia:task-completed (from page.svelte.ts completeTask)
// - lumotia:step-completed (from MicroSteps.svelte)
// - lumotia:task-uncompleted (new event, emitted by uncompleteTask in Task 10)
// - lumotia:task-deleted (new event, emitted by deleteTask in Task 10)
// - window focus (for day rollover while the app stayed open
// past midnight)
//

View File

@@ -1,5 +1,5 @@
// Phase 6 Margot nudge bus. Frontend-owned, subscribes to in-app
// signals Magnotia already produces, applies suppression, and fans out
// signals Lumotia already produces, applies suppression, and fans out
// to OS notification (via Rust's deliver_nudge) + optional TTS.
//
// Why frontend-only: OS-wide keyboard/window detection is fragile
@@ -191,7 +191,7 @@ async function checkPendingMorningTriage() {
triageNudgeFiredOnKey = today;
void deliver(
"A few things waiting",
"When you're ready, Magnotia has your morning list. No rush.",
"When you're ready, Lumotia has your morning list. No rush.",
);
}

View File

@@ -46,7 +46,7 @@ const TEMPLATES_KEY = "lumotia_templates";
// One-shot key rename from the magnotia era. Idempotent. Runs once at
// module load before any localStorage read below.
migrateLocalStorageKeys([
["lumotia_settings", SETTINGS_KEY],
["magnotia_settings", SETTINGS_KEY],
["magnotia_profiles", PROFILES_KEY],
["magnotia_task_lists", TASK_LISTS_KEY],
["magnotia_templates", TEMPLATES_KEY],
@@ -113,8 +113,8 @@ function loadSettings(): SettingsState {
queueMicrotask(() => {
toasts.warn(
"Settings reset",
"Your saved settings couldn't be read, so Magnotia fell back to defaults. "
+ "Earlier Magnotia builds can still read your old data.",
"Your saved settings couldn't be read, so Lumotia fell back to defaults. "
+ "Earlier Lumotia builds can still read your old data.",
);
});
}
@@ -128,7 +128,7 @@ export const settings = $state<SettingsState>(loadSettings());
export function saveSettings() {
if (!canUseStorage()) return;
// Versioned envelope {version, data}. Older Magnotia builds that used
// Versioned envelope {version, data}. Older Lumotia builds that used
// the bare-object layout can still spread the `data` key over
// their defaults — they will just silently drop fields they don't
// know about, which is the same behaviour as today.

View File

@@ -1,7 +1,7 @@
import type { ToastItem, ToastSeverity } from "$lib/types/app";
import { errorMessage } from "$lib/utils/errors.js";
// Minimal toast store. Roll-our-own (no svelte-french-toast etc) so Magnotia
// Minimal toast store. Roll-our-own (no svelte-french-toast etc) so Lumotia
// stays lean. Toasts auto-dismiss after `duration` ms unless duration is 0
// (sticky) or unless the user clicks the close button.
//

View File

@@ -103,7 +103,7 @@ export interface SettingsState {
ritualsMorningTime: string;
ritualsEvening: boolean;
/**
* Phase 5: register Magnotia as a login-time autostart entry. The
* Phase 5: register Lumotia as a login-time autostart entry. The
* actual OS-level state lives in `tauri-plugin-autostart`; this
* flag mirrors it so the Settings UI can render without a round-trip
* on every mount.

View File

@@ -70,7 +70,7 @@ function toHTML(text: string, segments: Segment[]): string {
<html lang="en">
<head>
<meta charset="utf-8">
<title>Magnotia Transcript</title>
<title>Lumotia Transcript</title>
<style>
body { font-family: "DM Sans", system-ui, sans-serif; max-width: 700px; margin: 2rem auto; padding: 0 1rem; color: #1a1816; line-height: 1.7; }
h1 { font-family: "Instrument Serif", Georgia, serif; font-style: italic; }

View File

@@ -43,7 +43,7 @@ function timeOfDayTag(dateStr: string): string | null {
if (!dateStr) return null;
let ts = Date.parse(dateStr);
if (Number.isNaN(ts)) {
// Try DD/MM/YYYY, HH:MM:SS (UK local format used by Magnotia history rows).
// Try DD/MM/YYYY, HH:MM:SS (UK local format used by Lumotia history rows).
const match = String(dateStr).match(
/(\d{1,2})\/(\d{1,2})\/(\d{4})[,\s]+(\d{1,2}):(\d{2})(?::(\d{2}))?/,
);
@@ -75,7 +75,7 @@ function sourceTag(source: string): string | null {
// already shown elsewhere in the History row, so chips would duplicate
// information and add cognitive load without improving retrieval. The
// function is kept as a hook for one future AI-derived content tag
// (`topic:*`) once magnotia-llm wires up real llama-cpp-2 in Phase 3.
// (`topic:*`) once lumotia-llm wires up real llama-cpp-2 in Phase 3.
export function deriveAutoTags(_item: TranscriptEntry): string[] {
return [];
}

View File

@@ -42,7 +42,7 @@ export interface HotkeyParts {
trigger: string; // e.g. "R" — may be empty if we're mid-record
}
/** Tokenise a `+`-joined hotkey the way the magnotia-hotkey parser does,
/** Tokenise a `+`-joined hotkey the way the lumotia-hotkey parser does,
* but without demanding a fully-valid combo (we validate next). The
* last part is always the trigger key; everything before is a
* modifier. An empty/whitespace combo returns empty parts. */

View File

@@ -1,5 +1,5 @@
/**
* Phase 7 of the magnotia -> lumotia rebrand cascade. Persisted UI state
* Phase 7 of the lumotia -> lumotia rebrand cascade. Persisted UI state
* survives the rename via one-shot key migrations run at module-load
* inside the stores that own each key.
*

View File

@@ -23,7 +23,7 @@ import { parseStoredJson } from "$lib/utils/storage";
export const CURRENT_SETTINGS_VERSION = 1;
/** Envelope written to localStorage. The raw blob is always kept
* separately so a downgrade (user jumps to an older Magnotia build) can
* separately so a downgrade (user jumps to an older Lumotia build) can
* still read v1 data even after we've bumped to v2. */
export interface VersionedSettings<T> {
version: number;