Basalt
Basalt is a desktop knowledge base that reads and writes plain Markdown folders, fully compatible with Obsidian vaults, so a decade of notes never belongs to the tool that displays them. I built the whole stack: the Tauri 2 shell, the React front end, and a CodeMirror 6 editing core where live-preview Markdown is implemented extension by extension rather than borrowed from a prebuilt editor.
The problem
What happens to your notes when the app that holds them changes hands, changes terms, or just changes? Obsidian's format is plain files, which is the right idea, but the application itself is closed source. I wanted the same daily-driver experience in a codebase that can be read, audited, and kept alive by anyone.
The hard part
Live preview, where Markdown syntax melts away as the cursor moves through it, is the feature that makes or breaks a notes app, and there is no shortcut for it. Each construct gets its own CodeMirror extension built on decorations and state fields: headings, callouts with folding, embeds, footnotes, tables, math, frontmatter. The editor directory is a catalog of those small machines, each one responsible for hiding syntax without losing the cursor.

The live-preview editor in its browser test harness: frontmatter as a property panel, a folded callout, wikilinks, and no visible Markdown until the caret comes back for it.
Basalt is AGPL licensed and aims for parity with the core features people rely on, plus its own plugin API. It deliberately does not chase compatibility with Obsidian's plugin ecosystem; the point is a foundation worth extending, not a clone.
The moment it broke
Saving a note made its own line unclickable. Type a line, save, and the caret would skip right over it; click one word and the cursor landed at the end of a different one. My first theory was wrong in a useful way: the file watcher was remounting the entire editor on every disk change, which was a real bug worth fixing on its own, so external changes now reconcile into the live editor as a single transaction instead of destroying it. But the caret stayed misaligned. The actual culprit was one line of CSS. I had centered the editor's content element with a max-width and an auto margin, which shifts the text away from where CodeMirror's click-to-position mapping expects it to be, so every click was off by exactly that margin. The width constraint moved to the parent container, and clicks landed where they were aimed. Three rounds of adversarial code review had missed it; clicking around the running app found it in minutes, which is why I trust exercised software over reviewed software.
Proof
- The live-preview core, where syntax hiding meets cursor state
- The editor theme, where the caret bug lived
- The editor extension catalog, one file per Markdown construct
What it demonstrates
Desktop application architecture across a Rust/webview boundary, serious editor internals work, and API design for extensibility. Also a thesis I hold about software: files over silos, and tools that respect the person using them.