chore: rebrand from Kon/Corbie to Magnotia

Replace all instances of the legacy product names "Kon" and "Corbie" with
"Magnotia" across user-facing copy, code identifiers, package names, bundle
ids, file paths, and documentation. Preserves the unrelated "konsole" (KDE
terminal) reference and the parent CORBEL company name.

- Renames 10 Rust crates (kon-* → magnotia-*) and the tauri binary
- Updates package.json, tauri.conf.json (productName + identifier)
- Renames CSS classes (kon-rh-* → magnotia-rh-*) and animations
- Renames brand and roadmap docs
- Regenerates Cargo.lock and package-lock.json

Verified: svelte-check passes; pure-rust crates compile under new names.
This commit is contained in:
Claude
2026-04-30 13:06:55 +00:00
parent 749403697a
commit 89c63891fa
186 changed files with 1297 additions and 1297 deletions

View File

@@ -37,7 +37,7 @@
@import "tailwindcss";
/* === Kon Design Tokens === */
/* === Magnotia Design Tokens === */
@theme {
/* Surfaces — warm layered depth */
--color-bg: #0f0e0c;
@@ -94,9 +94,9 @@
--duration-decorative: 300ms;
/* Window resize hit zones — consumed by ResizeHandles.svelte. One source
of truth so every Kon window feels identical. */
--kon-resize-edge: 12px;
--kon-resize-corner: 20px;
of truth so every Magnotia window feels identical. */
--magnotia-resize-edge: 12px;
--magnotia-resize-corner: 20px;
}
/* === Button Component Classes === */
@@ -430,7 +430,7 @@ textarea, input, [data-no-transition] {
parse it as a per-component virtual CSS module, which triggers an
"Invalid declaration" error on plain CSS inputs in some Tailwind v4
+ Svelte 5 combinations. */
.kon-rh {
.magnotia-rh {
position: fixed;
z-index: 2147483646;
background: transparent;
@@ -440,67 +440,67 @@ textarea, input, [data-no-transition] {
-webkit-user-select: none;
}
.kon-rh-nw,
.kon-rh-ne,
.kon-rh-sw,
.kon-rh-se {
.magnotia-rh-nw,
.magnotia-rh-ne,
.magnotia-rh-sw,
.magnotia-rh-se {
z-index: 2147483647;
}
.kon-rh-n {
.magnotia-rh-n {
top: 0;
left: var(--kon-resize-corner);
right: var(--kon-resize-corner);
height: var(--kon-resize-edge);
left: var(--magnotia-resize-corner);
right: var(--magnotia-resize-corner);
height: var(--magnotia-resize-edge);
cursor: n-resize;
}
.kon-rh-s {
.magnotia-rh-s {
bottom: 0;
left: var(--kon-resize-corner);
right: var(--kon-resize-corner);
height: var(--kon-resize-edge);
left: var(--magnotia-resize-corner);
right: var(--magnotia-resize-corner);
height: var(--magnotia-resize-edge);
cursor: s-resize;
}
.kon-rh-w {
top: var(--kon-resize-corner);
bottom: var(--kon-resize-corner);
.magnotia-rh-w {
top: var(--magnotia-resize-corner);
bottom: var(--magnotia-resize-corner);
left: 0;
width: var(--kon-resize-edge);
width: var(--magnotia-resize-edge);
cursor: w-resize;
}
.kon-rh-e {
top: var(--kon-resize-corner);
bottom: var(--kon-resize-corner);
.magnotia-rh-e {
top: var(--magnotia-resize-corner);
bottom: var(--magnotia-resize-corner);
right: 0;
width: var(--kon-resize-edge);
width: var(--magnotia-resize-edge);
cursor: e-resize;
}
.kon-rh-nw {
.magnotia-rh-nw {
top: 0;
left: 0;
width: var(--kon-resize-corner);
height: var(--kon-resize-corner);
width: var(--magnotia-resize-corner);
height: var(--magnotia-resize-corner);
cursor: nw-resize;
}
.kon-rh-ne {
.magnotia-rh-ne {
top: 0;
right: 0;
width: var(--kon-resize-corner);
height: var(--kon-resize-corner);
width: var(--magnotia-resize-corner);
height: var(--magnotia-resize-corner);
cursor: ne-resize;
}
.kon-rh-sw {
.magnotia-rh-sw {
bottom: 0;
left: 0;
width: var(--kon-resize-corner);
height: var(--kon-resize-corner);
width: var(--magnotia-resize-corner);
height: var(--magnotia-resize-corner);
cursor: sw-resize;
}
.kon-rh-se {
.magnotia-rh-se {
bottom: 0;
right: 0;
width: var(--kon-resize-corner);
height: var(--kon-resize-corner);
width: var(--magnotia-resize-corner);
height: var(--magnotia-resize-corner);
cursor: se-resize;
}

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Kon</title>
<title>Magnotia</title>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View File

@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 80" width="240" height="80">
<text x="12" y="54" font-family="&#39;Instrument Serif&#39;, Georgia, serif" font-style="italic" font-weight="400" font-size="48" fill="#e8a87c" letter-spacing="-0.01em">Kon</text>
<text x="12" y="54" font-family="&#39;Instrument Serif&#39;, Georgia, serif" font-style="italic" font-weight="400" font-size="48" fill="#e8a87c" letter-spacing="-0.01em">Magnotia</text>
<text x="92" y="46" font-family="&#39;Instrument Serif&#39;, Georgia, serif" font-size="28" fill="#e8a87c"></text>
<text x="12" y="70" font-family="&#39;Lexend&#39;, sans-serif" font-size="9" fill="#716b60" letter-spacing="1.4" text-transform="uppercase" style="text-transform:uppercase">THINK OUT LOUD</text>
</svg>

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -1,16 +1,16 @@
# Kon — Design System
# Magnotia — Design System
> **Think out loud.** A voice-first dictation app built for neurodivergent minds, by CORBEL.
## What is Kon?
## What is Magnotia?
Kon is a local-first dictation + thought-capture app. You press a hotkey, speak, and Kon transcribes, cleans, and — optionally — extracts tasks from what you said. Everything runs on your machine (Whisper / Parakeet models); nothing is sent anywhere.
Magnotia is a local-first dictation + thought-capture app. You press a hotkey, speak, and Magnotia transcribes, cleans, and — optionally — extracts tasks from what you said. Everything runs on your machine (Whisper / Parakeet models); nothing is sent anywhere.
**Company:** CORBEL — an AI consultancy and technology integrator that removes friction from day-to-day life and work.
**Product essence:** *Clarity without friction.* Every design decision is measured against it. If something adds cognitive load, it doesn't ship.
**Archetypes:** Sage (primary) + Magician (secondary). Kon listens more than it speaks.
**Archetypes:** Sage (primary) + Magician (secondary). Magnotia listens more than it speaks.
**Visual reference points:** The Barbican, Muji, Amsterdam urban design, Nujabes album art — warm brutalism, honest materials, structural clarity. **Anti-references:** Notion-style feature density, generic blue/white SaaS, FAANG aesthetics.
@@ -18,9 +18,9 @@ Kon is a local-first dictation + thought-capture app. You press a hotkey, speak,
## Sources
- **Repo:** `github.com/jakejars/kon` — Tauri + Svelte 5 + Tailwind v4 desktop app. Imported into `kon-source/` (lib components + routes).
- **Repo:** `github.com/jakejars/magnotia` — Tauri + Svelte 5 + Tailwind v4 desktop app. Imported into `magnotia-source/` (lib components + routes).
- **Design brief:** Pasted in the initial message — complete token system, type scale, motion guidelines, ideology.
- **Key reads in `kon-source/`:**
- **Key reads in `magnotia-source/`:**
- `app.css`@theme tokens, global base, zone + motion system
- `lib/Sidebar.svelte` — navigation shell
- `lib/pages/DictationPage.svelte` — record / transcribe (the hero screen)
@@ -41,8 +41,8 @@ Kon is a local-first dictation + thought-capture app. You press a hotkey, speak,
| `SKILL.md` | Agent-skill manifest (cross-compatible with Claude Code Agent Skills). |
| `assets/` | Logos, the sinhala wordmark glyph, grain texture, icon references |
| `preview/` | HTML spec cards shown in the Design System tab (swatches, specimens, components) |
| `ui_kits/kon-app/` | UI kit — pixel-level recreation of the Kon desktop app with interactive click-thru |
| `kon-source/` | Imported Svelte source — the ground truth for component structure |
| `ui_kits/magnotia-app/` | UI kit — pixel-level recreation of the Magnotia desktop app with interactive click-thru |
| `magnotia-source/` | Imported Svelte source — the ground truth for component structure |
No slide template was attached, so `slides/` is not created.
@@ -52,12 +52,12 @@ No slide template was attached, so `slides/` is not created.
> **Voice:** A calm friend who has already done the anxious part for you.
**Person.** Addresses the user as *you*; never refers to itself as *I*. Brand name "Kon" appears sparingly — the product speaks, it doesn't narrate itself. *(Settings labels: "Save recordings", "British English" — never "I'll save your recordings".)*
**Person.** Addresses the user as *you*; never refers to itself as *I*. Brand name "Magnotia" appears sparingly — the product speaks, it doesn't narrate itself. *(Settings labels: "Save recordings", "British English" — never "I'll save your recordings".)*
**Sentence shape.** Short. Direct. Rarely more than ~12 words per line of UI copy. Never exclaims. Never hypes. Full stops inside UI labels are dropped; in prose (empty states, toasts) they're used naturally.
**Tone.**
- **Ambient, not demanding.** Empty states are *"Your words will appear here…"* — not *"Record your first transcript!"* Blank screens trigger freeze; Kon offers presence instead of prompting.
- **Ambient, not demanding.** Empty states are *"Your words will appear here…"* — not *"Record your first transcript!"* Blank screens trigger freeze; Magnotia offers presence instead of prompting.
- **Calm on failure.** Failed transcription copy leads with *"Your audio is saved"* — the anxiety reducer, not the apology. Never red all-caps.
- **No guilt, no streaks.** No streak-shaming, no *"keep it up!"*, no urgency.
- **British English.** "Minimise", "finalising", "customise". `britishEnglish` is a first-class setting.
@@ -65,7 +65,7 @@ No slide template was attached, so `slides/` is not created.
**Casing.**
- Sentence case for all headings and buttons. *"Start recording"*, *"Clear all"*, *"Sensory zone"* — never *Start Recording* or *START RECORDING*.
- Eyebrow labels (10px, uppercase, 0.12em tracking) are the **only** place all-caps appears — e.g. *THINK OUT LOUD* under the sidebar wordmark. Never apply uppercase to sentences.
- Brand name is always `Kon` (capital K, one lowercase letter).
- Brand name is always `Magnotia` (capital K, one lowercase letter).
- Product tagline — *"Think out loud"* — always sentence case.
**Buttons.** Verbs. *Record, Stop, Save, Copy, Clear, Export, Extract Tasks, Open viewer.* The one exception is *Tasks* (noun) as a sidebar toggle — consistent with its icon label.
@@ -147,7 +147,7 @@ Lucide icons, 2px stroke, rounded terminals. 16px in nav, 20px in features, 24px
## Iconography
**Library:** [Lucide Icons](https://lucide.dev) (MIT). Every Lucide glyph Kon uses is listed in `kon-source/docs/icon-mapping.md`.
**Library:** [Lucide Icons](https://lucide.dev) (MIT). Every Lucide glyph Magnotia uses is listed in `magnotia-source/docs/icon-mapping.md`.
**Stroke weight:** 2px default · 2.5 on active sidebar items · 1 for empty-state illustrations (48px, opacity 40%).
@@ -165,7 +165,7 @@ Lucide icons, 2px stroke, rounded terminals. 16px in nav, 20px in features, 24px
- `--danger` for destructive and the recording state indicator
- `--success` for completion checks
**Core Kon set (from the audit):** `Mic` (Dictation), `FileText` (Files), `SquareCheck` (Tasks), `Clock` (History), `Settings`, `Circle`/`Square` (record/stop), `Play`/`Pause`, `Search`, `Upload`, `Download`, `Copy`, `ChevronDown`, `ChevronLeft`, `ChevronRight`, `X`, `Check`, `Plus`, `Pin`, `User`, `AlignLeft`, `AlertTriangle`, `Loader2`.
**Core Magnotia set (from the audit):** `Mic` (Dictation), `FileText` (Files), `SquareCheck` (Tasks), `Clock` (History), `Settings`, `Circle`/`Square` (record/stop), `Play`/`Pause`, `Search`, `Upload`, `Download`, `Copy`, `ChevronDown`, `ChevronLeft`, `ChevronRight`, `X`, `Check`, `Plus`, `Pin`, `User`, `AlignLeft`, `AlertTriangle`, `Loader2`.
**Brand glyph:** The Sinhala letter **ක** (U+0D9A) sits next to the wordmark in `--accent`. Decorative only. During recording it spins slowly (2s linear infinite, `sinhala-spin`). A Unicode codepoint, not an SVG — scales perfectly, no asset needed.

View File

@@ -1,6 +1,6 @@
---
name: kon-design
description: Use this skill to generate well-branded interfaces and assets for Kon (by CORBEL), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, assets, and UI kit components for prototyping.
name: magnotia-design
description: Use this skill to generate well-branded interfaces and assets for Magnotia (by CORBEL), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, assets, and UI kit components for prototyping.
user-invocable: true
---
@@ -21,6 +21,6 @@ If the user invokes this skill without any other guidance, ask them what they wa
- `colors_and_type.css` — import this for all tokens + semantic type.
- `README.md` — full brand + content + visual foundations.
- `assets/` — logos, glyph, grain, icon samples.
- `ui_kits/kon-app/` — component recreations of the desktop app.
- `ui_kits/magnotia-app/` — component recreations of the desktop app.
- `preview/` — spec cards (swatches, specimens, components).
- `kon-source/` — ground-truth Svelte source from the Kon repo.
- `magnotia-source/` — ground-truth Svelte source from the Magnotia repo.

View File

@@ -1,10 +1,10 @@
/* ============================================================
Kon — Colors & Type
Magnotia — Colors & Type
Single source of truth for CSS variables + semantic type.
Import this into any Kon design artefact.
Import this into any Magnotia design artefact.
============================================================ */
/* --- Brand Webfonts (local woff2, shipped with Kon) --- */
/* --- Brand Webfonts (local woff2, shipped with Magnotia) --- */
@font-face {
font-family: 'Lexend';
src: url('fonts/lexend-variable.woff2') format('woff2-variations'),
@@ -240,7 +240,7 @@ a:hover { color: var(--accent-hover); }
}
:root[data-theme="light"] .grain::after { opacity: 0.015; }
/* Kon sinhala glyph ක (U+0D9A) — wordmark companion */
/* Magnotia sinhala glyph ක (U+0D9A) — wordmark companion */
.k-glyph::after { content: '\0D9A'; color: var(--accent); margin-left: 0.25em; display: inline-block; }
@media (prefers-reduced-motion: reduce) {

View File

@@ -12,7 +12,7 @@
</style></head><body>
<div class="wrap">
<div>
<div class="k">Kon</div>
<div class="k">Magnotia</div>
<div class="tag">Think out loud</div>
</div>
<div class="notes">

View File

@@ -14,6 +14,6 @@
</style></head><body>
<div class="eb">Toasts · bottom-right · sticky on error</div>
<div class="toast err"><div class="dot err"></div><div><div class="title">Could not start recording</div><div class="body">Audio device disconnected. Plug it back in and try again.</div></div></div>
<div class="toast"><div class="dot warn"></div><div><div class="title">Dropped 2s of older audio</div><div class="body">Kon is keeping up in real time.</div></div></div>
<div class="toast"><div class="dot warn"></div><div><div class="title">Dropped 2s of older audio</div><div class="body">Magnotia is keeping up in real time.</div></div></div>
<div class="toast ok"><div class="dot ok"></div><div><div class="title">Saved · 3 tasks extracted</div><div class="body">Your audio is saved. Open Tasks to review.</div></div></div>
</body></html>

View File

@@ -4,7 +4,7 @@
.row{display:flex;align-items:flex-start;gap:18px;margin-bottom:14px}
.l{font:400 11px var(--font-mono);color:var(--text-tertiary);min-width:110px;padding-top:4px}
</style></head><body>
<div class="row"><div class="l">Body · 16/1.5</div><div style="max-width:56ch">Kon listens more than it speaks. Press the hotkey, speak your thought, and your words appear — saved, searchable, left exactly where you put them.</div></div>
<div class="row"><div class="l">Body · 16/1.5</div><div style="max-width:56ch">Magnotia listens more than it speaks. Press the hotkey, speak your thought, and your words appear — saved, searchable, left exactly where you put them.</div></div>
<div class="row"><div class="l">Body large · 18/1.6</div><div style="font-size:18px;line-height:1.6;max-width:52ch;color:var(--text-secondary)">A calm friend who has already done the anxious part for you.</div></div>
<div class="row"><div class="l">Small · 13</div><div style="font-size:13px;color:var(--text-secondary)">Press record or Ctrl+Shift+R</div></div>
<div class="row"><div class="l">Caption · 12</div><div style="font-size:12px;color:var(--text-tertiary)">12 words · 00:47 · live</div></div>

View File

@@ -8,6 +8,6 @@
</style></head><body>
<div class="eb">Transcript · 1624px · 1.85 · +0.01em · liga + kern</div>
<div class="tr">So the thing I wanted to capture is — if we move the onboarding to the second screen instead of the first, the friction around first launch basically disappears. That feels right.</div>
<div class="mono">~/Library/Application Support/Kon/transcripts.db</div>
<div class="mono">~/Library/Application Support/Magnotia/transcripts.db</div>
<div style="margin-top:14px"><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>R</kbd><span style="font-size:12px;color:var(--text-tertiary)">Global record toggle</span></div>
</body></html>

View File

@@ -8,7 +8,7 @@ function RecordButton({ recording, onToggle }) {
transition:'all 150ms cubic-bezier(.2,.8,.2,1)',
background: recording ? 'var(--danger)' : 'var(--accent)',
boxShadow: recording ? '0 0 0 0 rgba(232,113,113,.4)' : '0 4px 20px rgba(232,168,124,.3)',
animation: recording ? 'konPulse 2s ease-in-out infinite' : 'none',
animation: recording ? 'magnotiaPulse 2s ease-in-out infinite' : 'none',
};
return (
<button onClick={onToggle} style={style} aria-label={recording ? 'Stop recording' : 'Start recording'}>
@@ -29,7 +29,7 @@ function Waveform({ recording }) {
<span key={i} style={{
width:2, borderRadius:999, background:'rgba(232,113,113,.7)',
height:`${h*100}%`,
animation:`konBar 1.2s ease-in-out ${i*60}ms infinite`,
animation:`magnotiaBar 1.2s ease-in-out ${i*60}ms infinite`,
}}/>
))}
</span>

View File

@@ -1,4 +1,4 @@
# Kon app · UI kit
# Magnotia app · UI kit
Desktop transcription app — local-first, neurodivergent-first, amber on warm black.
@@ -12,4 +12,4 @@ Desktop transcription app — local-first, neurodivergent-first, amber on warm b
Open `index.html`. Click nav items to switch. Click the amber circle to toggle "recording" — the button pulses and the waveform bars animate. Check tasks off on the Tasks page.
## Notes on fidelity
Recreated from the Svelte source in `kon-source/`. The visual language, component shapes, copy tone and icon stroke weights match the app. Local transcription, model loading, Tauri IPC, and the real audio pipeline are stubbed — this kit is for visual + interaction reference.
Recreated from the Svelte source in `magnotia-source/`. The visual language, component shapes, copy tone and icon stroke weights match the app. Local transcription, model loading, Tauri IPC, and the real audio pipeline are stubbed — this kit is for visual + interaction reference.

View File

@@ -32,7 +32,7 @@ function Sidebar({ current, onNav, taskCount = 0, status = 'Ready', statusColor
return (
<aside style={{width:210, minWidth:210, background:'var(--sidebar)', borderRight:'1px solid var(--border-subtle)', display:'flex', flexDirection:'column', height:'100%'}}>
<div style={{display:'flex', alignItems:'baseline', gap:8, padding:'16px 20px 0'}}>
<h1 style={{font:'italic 400 26px/1 var(--font-display)', color:'var(--text)', margin:0, letterSpacing:'-.01em'}}>Kon</h1>
<h1 style={{font:'italic 400 26px/1 var(--font-display)', color:'var(--text)', margin:0, letterSpacing:'-.01em'}}>Magnotia</h1>
<span style={{fontSize:18, color:'var(--accent)'}}>&#3559;</span>
</div>
<p style={{font:'500 10px/1.3 var(--font-body)', color:'var(--text-tertiary)', margin:'6px 20px 0', letterSpacing:'.12em', textTransform:'uppercase'}}>Think out loud</p>

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Kon · UI kit</title>
<title>Magnotia · UI kit</title>
<link rel="stylesheet" href="../../colors_and_type.css">
<style>
html, body { margin:0; height:100%; background:#0b0a08; font-family:var(--font-body); color:var(--text); }
@@ -42,16 +42,16 @@
background-size: 200px 200px; opacity: .025; mix-blend-mode: overlay;
}
@keyframes konPulse {
@keyframes magnotiaPulse {
0%,100% { box-shadow: 0 0 0 0 rgba(232,113,113,.4); }
50% { box-shadow: 0 0 0 8px rgba(232,113,113,0); }
}
@keyframes konBar {
@keyframes magnotiaBar {
0%,100% { transform: scaleY(.5); opacity:.6; }
50% { transform: scaleY(1); opacity:1; }
}
@keyframes konFade { from { opacity: 0 } to { opacity: 1 } }
[data-fade] { animation: konFade 300ms cubic-bezier(.2,.8,.2,1); }
@keyframes magnotiaFade { from { opacity: 0 } to { opacity: 1 } }
[data-fade] { animation: magnotiaFade 300ms cubic-bezier(.2,.8,.2,1); }
button:focus-visible, input:focus-visible, textarea:focus-visible {
outline: none; box-shadow: 0 0 0 3px var(--accent-glow);
@@ -75,7 +75,7 @@ const { useState, useEffect, useRef } = React;
function pad(n){ return String(n).padStart(2,'0'); }
function KonApp() {
function MagnotiaApp() {
const [current, setCurrent] = useState('dictation');
const [recording, setRecording] = useState(false);
const [seconds, setSeconds] = useState(0);
@@ -131,7 +131,7 @@ function KonApp() {
<div className="frame">
<div className="titlebar">
<div className="lights"><div className="light lc"/><div className="light la"/><div className="light lg"/></div>
<div className="title">Kon</div>
<div className="title">Magnotia</div>
<div style={{width:54}}/>
</div>
<div className="shell">
@@ -142,7 +142,7 @@ function KonApp() {
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<KonApp/>);
ReactDOM.createRoot(document.getElementById('root')).render(<MagnotiaApp/>);
</script>
</body>
</html>

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">Kon</h1>
<h1 class="font-display text-[26px] text-text tracking-tight leading-none italic">Magnotia</h1>
<span
class="text-[18px] text-accent inline-block {page.recording ? 'animate-sinhala-spin' : ''}"
title="Kon"
title="Magnotia"
>&#3559;</span>
</div>
{:else}
<!-- Collapsed: just the decorative character centred -->
<span
class="text-[18px] text-accent inline-block {page.recording ? 'animate-sinhala-spin' : ''}"
title="Kon"
title="Magnotia"
>&#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 `kon:start-timer`
// Mounted once in +layout.svelte. Listens for `magnotia: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 `kon:start-timer`.
// Event handler: start a timer when any component fires `magnotia:start-timer`.
// Payload shape from MicroSteps.svelte and task row buttons:
// { taskId?: string, seconds: number, label?: string }
function handleStartEvent(evt: Event) {
@@ -77,13 +77,13 @@
}
onMount(() => {
window.addEventListener("kon:start-timer", handleStartEvent);
window.addEventListener("magnotia:start-timer", handleStartEvent);
// Rehydrate any in-flight timer that survived a window close.
focusTimer.rehydrate();
});
onDestroy(() => {
window.removeEventListener("kon:start-timer", handleStartEvent);
window.removeEventListener("magnotia:start-timer", handleStartEvent);
});
function handleCancel() {

View File

@@ -26,7 +26,7 @@
]);
// Map KeyboardEvent.code (physical key, layout-independent, shift-independent)
// to the unshifted name the kon-hotkey evdev parser understands. Browser
// to the unshifted name the magnotia-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

@@ -58,7 +58,7 @@
// is completed in that window, a gentle "still with that one?"
// nudge fires.
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('kon:microstep-generated', {
window.dispatchEvent(new CustomEvent('magnotia:microstep-generated', {
detail: { parentTaskId },
}));
}
@@ -78,7 +78,7 @@
// micro-step-idle timer for this parent task — the breakdown
// is demonstrably getting worked, no nudge needed.
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('kon:step-completed', {
window.dispatchEvent(new CustomEvent('magnotia:step-completed', {
detail: { id: subtaskId, parentTaskId },
}));
}
@@ -86,7 +86,7 @@
}
function startTimer(subtaskId: string) {
window.dispatchEvent(new CustomEvent('kon:start-timer', {
window.dispatchEvent(new CustomEvent('magnotia:start-timer', {
detail: { taskId: subtaskId, seconds: 120 }
}));
}

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">
Kon needs the <span class="font-medium text-text">{modelSize}</span> model to transcribe speech.
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">
{currentModelInfo?.size ?? "?"} · {currentModelInfo?.accuracy ?? "?"} accuracy · 100% offline

View File

@@ -67,7 +67,7 @@
function dispatchTriageFinished(mode: 'empty' | 'skipped' | 'picked') {
if (typeof window === 'undefined') return;
window.dispatchEvent(new CustomEvent('kon:morning-triage-finished', {
window.dispatchEvent(new CustomEvent('magnotia:morning-triage-finished', {
detail: { date: todayKey(), mode },
}));
}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
// Invisible resize-handle overlays around each Kon window edge.
// Needed because Kon runs with decorations off, so the WM
// Invisible resize-handle overlays around each Magnotia window edge.
// Needed because Magnotia 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 Kon window
// across the app stays identical. See the kon-resize-edge and
// kon-resize-corner custom properties in app.css.
// 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.
//
// 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="kon-rh kon-rh-n" onpointerdown={(e) => startResize(e,"North")}></div>
<div class="magnotia-rh magnotia-rh-n" onpointerdown={(e) => startResize(e,"North")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-s" onpointerdown={(e) => startResize(e,"South")}></div>
<div class="magnotia-rh magnotia-rh-s" onpointerdown={(e) => startResize(e,"South")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-w" onpointerdown={(e) => startResize(e,"West")}></div>
<div class="magnotia-rh magnotia-rh-w" onpointerdown={(e) => startResize(e,"West")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-e" onpointerdown={(e) => startResize(e,"East")}></div>
<div class="magnotia-rh magnotia-rh-e" onpointerdown={(e) => startResize(e,"East")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-nw" onpointerdown={(e) => startResize(e,"NorthWest")}></div>
<div class="magnotia-rh magnotia-rh-nw" onpointerdown={(e) => startResize(e,"NorthWest")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-ne" onpointerdown={(e) => startResize(e,"NorthEast")}></div>
<div class="magnotia-rh magnotia-rh-ne" onpointerdown={(e) => startResize(e,"NorthEast")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-sw" onpointerdown={(e) => startResize(e,"SouthWest")}></div>
<div class="magnotia-rh magnotia-rh-sw" onpointerdown={(e) => startResize(e,"SouthWest")}></div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="kon-rh kon-rh-se" onpointerdown={(e) => startResize(e,"SouthEast")}></div>
<div class="magnotia-rh magnotia-rh-se" onpointerdown={(e) => startResize(e,"SouthEast")}></div>
{/if}

View File

@@ -5,7 +5,7 @@
import { ChevronDown, ChevronRight, Timer } from 'lucide-svelte';
function startFocusTimer(task: { id: string; text: string }) {
window.dispatchEvent(new CustomEvent('kon:start-timer', {
window.dispatchEvent(new CustomEvent('magnotia:start-timer', {
detail: { taskId: task.id, seconds: 300, label: task.text }
}));
}

View File

@@ -19,7 +19,7 @@ export const SUPPORTED_LOCALES: { code: Locale; label: string }[] = [
{ code: "de", label: "Deutsch" },
];
const STORAGE_KEY = "kon_locale";
const STORAGE_KEY = "magnotia_locale";
register("en", () => import("./locales/en.json"));
register("es", () => import("./locales/es.json"));

View File

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

View File

@@ -2,7 +2,7 @@
"settings": {
"title": "Settings",
"language": "Language",
"languageDescription": "Kon's interface language. Affects labels, not transcription."
"languageDescription": "Magnotia'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 Kon. No afecta a la transcripción."
"languageDescription": "Idioma de la interfaz de Magnotia. 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 Kon in a normal browser. Local transcription only works in the Tauri desktop app window.";
const browserPreviewMessage = "You're viewing Magnotia in a normal browser. Local transcription only works in the Tauri desktop app window.";
let transcript = $state("");
let segments = $state([]);
@@ -65,7 +65,7 @@
let hotkeyHandler = () => toggleRecording();
onMount(async () => {
window.addEventListener("kon:toggle-recording", hotkeyHandler);
window.addEventListener("magnotia:toggle-recording", hotkeyHandler);
if (!tauriRuntimeAvailable) {
error = browserPreviewMessage;
@@ -77,7 +77,7 @@
});
onDestroy(() => {
window.removeEventListener("kon:toggle-recording", hotkeyHandler);
window.removeEventListener("magnotia:toggle-recording", hotkeyHandler);
clearInterval(timerInterval);
if (page.recording) {
page.recording = false;
@@ -199,7 +199,7 @@
lastLiveActivityAt = Date.now();
if (status.type === "overload" || status.type === "warning") {
liveWarning = status.message || "Kon is dropping older audio to stay responsive.";
liveWarning = status.message || "Magnotia is dropping older audio to stay responsive.";
return;
}
@@ -647,7 +647,7 @@
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `kon-${new Date().toISOString().slice(0, 10)}.${ext}`;
a.download = `magnotia-${new Date().toISOString().slice(0, 10)}.${ext}`;
a.click();
URL.revokeObjectURL(url);
}

View File

@@ -142,7 +142,7 @@
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `kon-file-${new Date().toISOString().slice(0, 10)}.${ext}`;
a.download = `magnotia-file-${new Date().toISOString().slice(0, 10)}.${ext}`;
a.click();
URL.revokeObjectURL(url);
}

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 Corbie at login?</h2>
<h2 class="text-xl font-medium text-text">Launch Magnotia at login?</h2>
<p class="text-sm text-text-secondary mt-3 leading-relaxed">
So Corbie 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 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>
<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 Kon</h2>
<h2 class="text-2xl font-medium text-text text-center">Welcome to Magnotia</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

@@ -342,12 +342,12 @@
// script.
const handoff = JSON.stringify({ id: String(item.id) });
try {
localStorage.setItem("kon_viewer_item", handoff);
localStorage.setItem("kon_viewer_mode", "view");
localStorage.setItem("magnotia_viewer_item", handoff);
localStorage.setItem("magnotia_viewer_mode", "view");
await invoke("open_viewer_window");
} catch {
localStorage.setItem("kon_viewer_item", handoff);
localStorage.setItem("kon_viewer_mode", "view");
localStorage.setItem("magnotia_viewer_item", handoff);
localStorage.setItem("magnotia_viewer_mode", "view");
window.open("/viewer", "_blank", "width=600,height=700");
}
}
@@ -509,12 +509,12 @@
// (see openViewer above for the rationale).
const handoff = JSON.stringify({ id: String(item.id) });
try {
localStorage.setItem("kon_viewer_item", handoff);
localStorage.setItem("kon_viewer_mode", "edit");
localStorage.setItem("magnotia_viewer_item", handoff);
localStorage.setItem("magnotia_viewer_mode", "edit");
await invoke("open_viewer_window");
} catch {
localStorage.setItem("kon_viewer_item", handoff);
localStorage.setItem("kon_viewer_mode", "edit");
localStorage.setItem("magnotia_viewer_item", handoff);
localStorage.setItem("magnotia_viewer_mode", "edit");
window.open("/viewer", "_blank", "width=600,height=700");
}
}

View File

@@ -675,7 +675,7 @@
async function testReadAloudVoice() {
try {
await invoke("tts_speak", {
text: "This is Corbie reading aloud.",
text: "This is Magnotia reading aloud.",
rate: settings.ttsRate,
voice: settings.ttsVoice ?? null,
});
@@ -771,7 +771,7 @@
downloadProgress = event.payload.percent || event.payload.progress || 0;
});
unlistenLlm = await listen("kon:llm-download-progress", (event) => {
unlistenLlm = await listen("magnotia:llm-download-progress", (event) => {
llmDownloadProgress = event.payload.percent || 0;
llmDownloadingModel = event.payload.modelId || llmDownloadingModel;
});
@@ -984,7 +984,7 @@
bind:value={settings.microphoneDevice}
onfocus={refreshAudioDevices}
>
<option value="">Auto (recommended) — let Kon pick the working mic</option>
<option value="">Auto (recommended) — let Magnotia 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}
@@ -1624,7 +1624,7 @@
</div>
<p class="text-[11px] text-text-tertiary mt-2">
{settings.aiTier === "off"
? "No local LLM calls. Kon falls back to the existing rule-based path."
? "No local LLM calls. Magnotia 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."}
@@ -1871,7 +1871,7 @@
disabled={autostartSyncing}
role="switch"
aria-checked={settings.launchAtLogin}
aria-label="Launch Corbie at login"
aria-label="Launch Magnotia at login"
>
<span
class="absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white shadow-sm
@@ -1880,9 +1880,9 @@
></span>
</button>
<div class="flex-1 min-w-0">
<p class="text-[13px] text-text leading-tight">Launch Corbie at login</p>
<p class="text-[13px] text-text leading-tight">Launch Magnotia at login</p>
<p class="text-[11px] text-text-tertiary mt-0.5 leading-snug">
So Corbie is already there at the start of the day. Uses your OS's standard autostart — no background tricks.
So Magnotia is already there at the start of the day. Uses your OS's standard autostart — no background tricks.
</p>
{#if autostartSyncing}
<p class="text-[11px] text-text-tertiary mt-1">Updating…</p>
@@ -1913,7 +1913,7 @@
<SettingsGroup title="Nudges" description="Soft notifications, capped per hour.">
<div class="animate-fade-in">
<p class="text-[11px] text-text-tertiary mb-4">
Gentle, anticipatory reminders. Capped at 3 per hour. Never fires while you're looking at Corbie.
Gentle, anticipatory reminders. Capped at 3 per hour. Never fires while you're looking at Magnotia.
</p>
<Toggle
@@ -2020,13 +2020,13 @@
/>
<Toggle
bind:checked={settings.transcriptionPreview}
label="Floating preview when Kon is unfocused"
label="Floating preview when Magnotia 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 — Kon never records on its own."
description="Toasts when a matching app appears in the process list. You still hit the hotkey — Magnotia never records on its own."
/>
{#if settings.meetingAutoCapture}
<div class="ml-[50px] mt-2 mb-1 animate-fade-in">
@@ -2201,7 +2201,7 @@
{/each}
</div>
<p class="text-[11px] text-text-tertiary mt-5">Kon v1.0 · Powered by whisper.cpp · Built by CORBEL Ltd</p>
<p class="text-[11px] text-text-tertiary 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">

View File

@@ -3,10 +3,10 @@
//
// Refresh triggers:
// - module load (first time)
// - kon:task-completed (from page.svelte.ts completeTask)
// - kon:step-completed (from MicroSteps.svelte)
// - kon:task-uncompleted (new event, emitted by uncompleteTask in Task 10)
// - kon:task-deleted (new event, emitted by deleteTask in Task 10)
// - 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)
// - window focus (for day rollover while the app stayed open
// past midnight)
//
@@ -47,10 +47,10 @@ if (typeof window !== "undefined") {
const handler = () => {
refresh().catch(() => {});
};
window.addEventListener("kon:task-completed", handler);
window.addEventListener("kon:step-completed", handler);
window.addEventListener("kon:task-uncompleted", handler);
window.addEventListener("kon:task-deleted", handler);
window.addEventListener("magnotia:task-completed", handler);
window.addEventListener("magnotia:step-completed", handler);
window.addEventListener("magnotia:task-uncompleted", handler);
window.addEventListener("magnotia:task-deleted", handler);
window.addEventListener("focus", handler);
if (hasTauriRuntime()) {

View File

@@ -12,7 +12,7 @@
// so closing the window mid-timer still gets you the "done" signal
// on next launch.
const STORAGE_KEY = "kon.focusTimer.v1";
const STORAGE_KEY = "magnotia.focusTimer.v1";
const TICK_INTERVAL_MS = 250;
export type FocusTimerPersisted = {
@@ -114,7 +114,7 @@ function createFocusTimerStore() {
writePersisted(null);
stopTick();
if (wasActive && typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("kon:focus-timer-cancelled"));
window.dispatchEvent(new CustomEvent("magnotia:focus-timer-cancelled"));
}
}
@@ -172,7 +172,7 @@ function createFocusTimerStore() {
osc.onended = () => ctx.close().catch(() => {});
} catch { /* audio is a nicety; never fatal */ }
window.dispatchEvent(new CustomEvent("kon:focus-timer-complete", {
window.dispatchEvent(new CustomEvent("magnotia:focus-timer-complete", {
detail: { taskId, label },
}));
}

View File

@@ -13,7 +13,7 @@ import { page, settings, tasks } from "$lib/stores/page.svelte.js";
import { toasts } from "$lib/stores/toasts.svelte.js";
const TIME_RULE_POLL_MS = 30_000;
const RULES_CHANGED_EVENT = "kon:implementation-rules-changed";
const RULES_CHANGED_EVENT = "magnotia:implementation-rules-changed";
export const implementationRules = $state<ImplementationRule[]>([]);
@@ -157,7 +157,7 @@ async function executeAction(action: ImplementationRuleAction): Promise<void> {
return;
}
if (action.kind === "start_timer") {
window.dispatchEvent(new CustomEvent("kon:start-timer", {
window.dispatchEvent(new CustomEvent("magnotia:start-timer", {
detail: {
taskId: action.taskId ?? null,
seconds: 300,
@@ -239,8 +239,8 @@ export function startImplementationIntentions(): void {
void loadImplementationRules(true)
.then(() => checkTimeRules())
.catch(() => {});
window.addEventListener("kon:task-completed", onTaskCompleted);
window.addEventListener("kon:morning-triage-finished", onMorningTriageFinished);
window.addEventListener("magnotia:task-completed", onTaskCompleted);
window.addEventListener("magnotia:morning-triage-finished", onMorningTriageFinished);
window.addEventListener(RULES_CHANGED_EVENT, onRulesChanged);
timePollHandle = setInterval(() => {
void checkTimeRules().catch(() => {});
@@ -250,8 +250,8 @@ export function startImplementationIntentions(): void {
export function stopImplementationIntentions(): void {
if (!started) return;
started = false;
window.removeEventListener("kon:task-completed", onTaskCompleted);
window.removeEventListener("kon:morning-triage-finished", onMorningTriageFinished);
window.removeEventListener("magnotia:task-completed", onTaskCompleted);
window.removeEventListener("magnotia:morning-triage-finished", onMorningTriageFinished);
window.removeEventListener(RULES_CHANGED_EVENT, onRulesChanged);
if (timePollHandle !== null) {
clearInterval(timePollHandle);

View File

@@ -1,5 +1,5 @@
// Phase 6 Margot nudge bus. Frontend-owned, subscribes to in-app
// signals Corbie already produces, applies suppression, and fans out
// signals Magnotia 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, Corbie has your morning list. No rush.",
"When you're ready, Magnotia has your morning list. No rush.",
);
}
@@ -236,12 +236,12 @@ export function startNudgeBus(): void {
if (typeof window === "undefined") return;
started = true;
window.addEventListener("kon:start-timer", onTimerStart);
window.addEventListener("kon:focus-timer-complete", resetTimerState);
window.addEventListener("kon:focus-timer-cancelled", resetTimerState);
window.addEventListener("kon:microstep-generated", onMicroStepGenerated);
window.addEventListener("kon:step-completed", onStepOrTaskCompleted);
window.addEventListener("kon:task-completed", onStepOrTaskCompleted);
window.addEventListener("magnotia:start-timer", onTimerStart);
window.addEventListener("magnotia:focus-timer-complete", resetTimerState);
window.addEventListener("magnotia:focus-timer-cancelled", resetTimerState);
window.addEventListener("magnotia:microstep-generated", onMicroStepGenerated);
window.addEventListener("magnotia:step-completed", onStepOrTaskCompleted);
window.addEventListener("magnotia:task-completed", onStepOrTaskCompleted);
window.addEventListener("focus", onFocus);
window.addEventListener("blur", onBlur);
@@ -265,12 +265,12 @@ export function stopNudgeBus(): void {
if (!started) return;
started = false;
window.removeEventListener("kon:start-timer", onTimerStart);
window.removeEventListener("kon:focus-timer-complete", resetTimerState);
window.removeEventListener("kon:focus-timer-cancelled", resetTimerState);
window.removeEventListener("kon:microstep-generated", onMicroStepGenerated);
window.removeEventListener("kon:step-completed", onStepOrTaskCompleted);
window.removeEventListener("kon:task-completed", onStepOrTaskCompleted);
window.removeEventListener("magnotia:start-timer", onTimerStart);
window.removeEventListener("magnotia:focus-timer-complete", resetTimerState);
window.removeEventListener("magnotia:focus-timer-cancelled", resetTimerState);
window.removeEventListener("magnotia:microstep-generated", onMicroStepGenerated);
window.removeEventListener("magnotia:step-completed", onStepOrTaskCompleted);
window.removeEventListener("magnotia:task-completed", onStepOrTaskCompleted);
window.removeEventListener("focus", onFocus);
window.removeEventListener("blur", onBlur);

View File

@@ -36,10 +36,10 @@ export const page = $state<PageState>({
taskSidebarOpen: false,
});
const SETTINGS_KEY = "kon_settings";
const PROFILES_KEY = "kon_profiles";
const TASK_LISTS_KEY = "kon_task_lists";
const TEMPLATES_KEY = "kon_templates";
const SETTINGS_KEY = "magnotia_settings";
const PROFILES_KEY = "magnotia_profiles";
const TASK_LISTS_KEY = "magnotia_task_lists";
const TEMPLATES_KEY = "magnotia_templates";
const defaults: SettingsState = {
engine: "whisper",
@@ -102,8 +102,8 @@ function loadSettings(): SettingsState {
queueMicrotask(() => {
toasts.warn(
"Settings reset",
"Your saved settings couldn't be read, so Kon fell back to defaults. "
+ "Earlier Kon builds can still read your old data.",
"Your saved settings couldn't be read, so Magnotia fell back to defaults. "
+ "Earlier Magnotia builds can still read your old data.",
);
});
}
@@ -117,7 +117,7 @@ export const settings = $state<SettingsState>(loadSettings());
export function saveSettings() {
if (!canUseStorage()) return;
// Versioned envelope {version, data}. Older Kon builds that used
// Versioned envelope {version, data}. Older Magnotia 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.
@@ -459,7 +459,7 @@ export async function deleteTask(id: string) {
tasks.splice(idx, 1);
broadcastTasks();
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("kon:task-deleted", { detail: { id } }));
window.dispatchEvent(new CustomEvent("magnotia:task-deleted", { detail: { id } }));
}
}
} catch (err) {
@@ -477,7 +477,7 @@ export async function completeTask(id: string) {
// to this to clear micro-step-idle timers for the completed task
// and, later, to drive Phase 7 "after a task completes" rules.
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("kon:task-completed", { detail: { id } }));
window.dispatchEvent(new CustomEvent("magnotia:task-completed", { detail: { id } }));
}
} catch (err) {
toasts.error("Couldn't complete task", errorMessage(err));
@@ -491,7 +491,7 @@ export async function uncompleteTask(id: string) {
await invoke("uncomplete_task_cmd", { id });
applyLocalTaskUpdate(id, { done: false, doneAt: null });
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent("kon:task-uncompleted", { detail: { id } }));
window.dispatchEvent(new CustomEvent("magnotia:task-uncompleted", { detail: { id } }));
}
} catch (err) {
toasts.error("Couldn't uncomplete task", errorMessage(err));
@@ -504,7 +504,7 @@ interface TaskChannelMessage {
}
const taskChannel = typeof BroadcastChannel !== "undefined"
? new BroadcastChannel("kon_task_sync")
? new BroadcastChannel("magnotia_task_sync")
: null;
if (taskChannel) {
@@ -654,7 +654,7 @@ interface TaskListChannelMessage {
}
const taskListChannel = typeof BroadcastChannel !== "undefined"
? new BroadcastChannel("kon_task_lists")
? new BroadcastChannel("magnotia_task_lists")
: null;
if (taskListChannel) {

View File

@@ -6,7 +6,7 @@ import type { AccessibilityPreferences, Preferences } from "$lib/types/app";
import { errorMessage } from "$lib/utils/errors.js";
import { toasts } from "./toasts.svelte.ts";
export const PREFERENCES_CHANGED_EVENT = "kon:preferences-changed";
export const PREFERENCES_CHANGED_EVENT = "magnotia:preferences-changed";
type FontFamilies = Record<AccessibilityPreferences["fontFamily"], string>;

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 Kon
// Minimal toast store. Roll-our-own (no svelte-french-toast etc) so Magnotia
// stays lean. Toasts auto-dismiss after `duration` ms unless duration is 0
// (sticky) or unless the user clicks the close button.
//

View File

@@ -99,7 +99,7 @@ export interface SettingsState {
ritualsMorningTime: string;
ritualsEvening: boolean;
/**
* Phase 5: register Corbie as a login-time autostart entry. The
* Phase 5: register Magnotia 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>Kon Transcript</title>
<title>Magnotia 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 Kon history rows).
// Try DD/MM/YYYY, HH:MM:SS (UK local format used by Magnotia 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 kon-llm wires up real llama-cpp-2 in Phase 3.
// (`topic:*`) once magnotia-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 kon-hotkey parser does,
/** Tokenise a `+`-joined hotkey the way the magnotia-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,7 +1,7 @@
/**
* Forward-compatible, versioned settings migration.
*
* Historically, `localStorage["kon_settings"]` was a bare JSON object
* Historically, `localStorage["magnotia_settings"]` was a bare JSON object
* (whatever `SettingsState` looked like at write time). That shape
* tolerates new fields cleanly — spread over `defaults` — but does not
* survive:
@@ -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 Kon build) can
* separately so a downgrade (user jumps to an older Magnotia build) can
* still read v1 data even after we've bumped to v2. */
export interface VersionedSettings<T> {
version: number;

View File

@@ -42,7 +42,7 @@
const prefs = getPreferences();
const tauriRuntimeAvailable = hasTauriRuntime();
// On Linux Kon uses native KWin/Mutter decorations (see
// On Linux Magnotia uses native KWin/Mutter decorations (see
// src-tauri/tauri.linux.conf.json and windows.rs). Frameless custom
// chrome stays for macOS and Windows. Default to false so Linux users
// don't see a flash of custom titlebar before loadOsInfo resolves.
@@ -68,7 +68,7 @@
});
// Global hotkey registration — dual backend
// Wayland: evdev via kon-hotkey crate (works without display server)
// Wayland: evdev via magnotia-hotkey crate (works without display server)
// X11/macOS/Windows: tauri-plugin-global-shortcut (native)
let registeredHotkey = null;
let hotkeyBackend = $state("unknown"); // "evdev" | "tauri" | "unavailable"
@@ -123,7 +123,7 @@
await mod.register(hotkey, () => {
if (page.current !== "dictation") page.current = "dictation";
requestAnimationFrame(() => {
window.dispatchEvent(new CustomEvent("kon:toggle-recording"));
window.dispatchEvent(new CustomEvent("magnotia:toggle-recording"));
});
});
registeredHotkey = hotkey;
@@ -159,13 +159,13 @@
async function setupEvdevListener() {
if (!tauriRuntimeAvailable) return;
const { listen } = await import("@tauri-apps/api/event");
unlistenEvdev = await listen("kon:hotkey-pressed", () => {
unlistenEvdev = await listen("magnotia:hotkey-pressed", () => {
const now = Date.now();
if (now - lastHotkeyAtMs < HOTKEY_DEBOUNCE_MS) return;
lastHotkeyAtMs = now;
if (page.current !== "dictation") page.current = "dictation";
requestAnimationFrame(() => {
window.dispatchEvent(new CustomEvent("kon:toggle-recording"));
window.dispatchEvent(new CustomEvent("magnotia:toggle-recording"));
});
});
}
@@ -217,7 +217,7 @@
let unlistenWindDown = null;
async function setupWindDownListener() {
if (!tauriRuntimeAvailable) return;
unlistenWindDown = await listen("kon:open-wind-down", () => {
unlistenWindDown = await listen("magnotia:open-wind-down", () => {
page.current = "shutdown";
});
}
@@ -325,7 +325,7 @@
invoke("check_for_update")
.then((version) => {
if (version) {
toasts.info(`Kon ${version} is available. Download and restart to update.`);
toasts.info(`Magnotia ${version} is available. Download and restart to update.`);
}
})
.catch(() => { /* update check failure must not affect the app */ });
@@ -442,7 +442,7 @@
in the bottom-right of the viewport. (Day 3 of the upgrade plan) -->
<ToastViewport />
<!-- Global focus-timer overlay. Renders nothing until a `kon:start-timer`
<!-- Global focus-timer overlay. Renders nothing until a `magnotia:start-timer`
event fires; then pins a shrinking colour ring to the top-right.
Phase 1 of the 2026-04-23 feature-complete roadmap — closes the
visual-time-representation gap from docs/brief/feature-set.md and

View File

@@ -14,7 +14,7 @@
});
</script>
<main class="h-full overflow-hidden" aria-label="Kon application content">
<main class="h-full overflow-hidden" aria-label="Magnotia application content">
{#if page.current === "first-run"}
<FirstRunPage />
{:else if page.current === "dictation"}

View File

@@ -35,7 +35,7 @@
// Listen for settings changes from main window
if (typeof window !== "undefined") {
window.addEventListener("storage", (e) => {
if (e.key === "kon_settings" && e.newValue) {
if (e.key === "magnotia_settings" && e.newValue) {
try {
Object.assign(settings, JSON.parse(e.newValue));
} catch {}

View File

@@ -185,7 +185,7 @@
onpointerdown={handleDragStart}
>
<span class="text-[12px] font-medium text-text-secondary tracking-wide">
Kon - To-do
Magnotia - To-do
</span>
<div class="flex-1"></div>

View File

@@ -28,7 +28,7 @@
if (typeof window !== "undefined") {
window.addEventListener("storage", (event) => {
if (event.key === "kon_settings" && event.newValue) {
if (event.key === "magnotia_settings" && event.newValue) {
try { Object.assign(settings, JSON.parse(event.newValue)); } catch {}
}
});

View File

@@ -32,7 +32,7 @@
// Sync settings from main window
if (typeof window !== "undefined") {
window.addEventListener("storage", (e) => {
if (e.key === "kon_settings" && e.newValue) {
if (e.key === "magnotia_settings" && e.newValue) {
try { Object.assign(settings, JSON.parse(e.newValue)); } catch {}
}
});

View File

@@ -92,8 +92,8 @@
}
onMount(() => {
loadFromHandoff(localStorage.getItem("kon_viewer_item"));
const mode = localStorage.getItem("kon_viewer_mode");
loadFromHandoff(localStorage.getItem("magnotia_viewer_item"));
const mode = localStorage.getItem("magnotia_viewer_mode");
if (mode === "edit" || mode === "view") viewerMode = mode;
// Listen for new items via storage events
@@ -139,7 +139,7 @@
}
function handleStorageChange(e: StorageEvent) {
if (e.key === "kon_viewer_item" && e.newValue) {
if (e.key === "magnotia_viewer_item" && e.newValue) {
void loadFromHandoff(e.newValue);
}
}
@@ -317,7 +317,7 @@
if (!item) return;
try {
localStorage.setItem(
"kon_viewer_item",
"magnotia_viewer_item",
JSON.stringify({ id: String(item.id), stamp: Date.now() }),
);
} catch {}
@@ -391,7 +391,7 @@
onpointerdown={handleDragStart}
>
<span class="text-[12px] font-medium text-text-secondary tracking-wide">
Kon - {viewerMode === "edit" ? "Editor" : "Viewer"}
Magnotia - {viewerMode === "edit" ? "Editor" : "Viewer"}
</span>
<div class="flex-1"></div>
</div>