MD Kanban swaps its textarea for CodeMirror 6 with a toolbar and Vim keys, and replaces an unsanitized marked preview with a remark and rehype pipeline.
The biggest commit so far landed on September 10: 30 files and about 2,500 added lines. Most of it is two components, a new editor and a new preview renderer, plus a self-check script for each.
The hole in the preview
The old preview ran card markdown through marked and put the HTML straight into the page with no sanitizing. A card containing <img src=x onerror="..."> would run that script, and in the desktop build it would run inside the Electron app, which reads and writes files through its IPC bridge. Agents write cards as well as me, so whatever lands in a folder gets rendered. Tables also had no borders.
GitBasedDocs already had a remark and rehype pipeline with the Obsidian extensions I wanted, so I ported it into src/lib/render.ts instead of starting over. Raw HTML is parsed and then cut down by rehype-sanitize with GitHub's allowlist before any of my own transforms run. <details>, <kbd>, <sub> and <sup> survive. Scripts, iframes, forms and inline styles are removed.
Two more doors got closed in Electron. Links in a preview open in the system browser, and the app window refuses to navigate away from the board. Images in a card, either  or Obsidian's ![[img.png|300]], load through a new mdkanban:read-binary IPC call. It resolves the path inside the project root and only serves image extensions, so a card can't use an image tag to pull other files out of the folder.
What the preview renders now
- Code blocks through Shiki, with a file title (
title="lib/auth.ts"), marked lines ({2}),// [!code ++]and// [!code --]diff markers, and a copy button - Mermaid diagrams, redrawn when the theme flips; a broken diagram shows its error next to the source
- KaTeX for
$E = mc^2$and$$blocks - Every Obsidian callout type, with custom titles and
[!faq]-or[!faq]+folding - Tables with borders and a scroll box for wide ones, task lists, footnotes
- Wikilinks:
[[card]]opens the card with that title, file name or id, and unknown ones render dimmed ==highlight==, and%%comments%%hidden in the preview and in the excerpt on the board card- Heading anchors and an "On this page" list
Math needed one fix. remark-math reads "costs $5 a month, or $50" as a formula. Obsidian and Pandoc don't: inline math can't start or end with a space, and the closing $ can't be followed by a digit. A small remark plugin applies those two rules and turns bad matches back into text.
Comments follow Obsidian too. Fenced code keeps its %%, and an unclosed %% hides the rest of the file. bun src/lib/render.check.ts runs assertions over all of this.
The editor
The Markdown tab was a plain textarea. It is now CodeMirror 6 with markdown colouring, syntax colouring inside code fences, line wrap, search, and Enter continuing lists. I wanted a code editor over a rich text one because it leaves the file text exactly as written. An agent may be editing the same file, and a WYSIWYG round trip would reformat its work.
The toolbar covers H1 to H3, bold, italic, strikethrough, highlight, inline code, link, image, three list types, quote, code block, table, callout, math, mermaid and a divider. Cmd+B, Cmd+I and Cmd+E work as shortcuts. The commands in src/lib/md-commands.ts toggle, so bold on bold text removes the asterisks, and they have their own check file. Colours come from the app's CSS variables, so the editor follows the light and dark toggle.
The swap turned up two bugs. The global N (new card) and Escape (close) shortcuts skipped keys typed in a TEXTAREA. CodeMirror is a contenteditable div, so that check missed it and typing "n" in a card opened the New card dialog. The second bug was older. Autosave waits 500 ms after the last keystroke, and closing or switching cards inside that window dropped the last edit. The editor now flushes the pending save first.
Vim keys
Vim mode is a checkbox in the sidebar under Auto-refresh folders, saved with the other prefs. It uses @replit/codemirror-vim inside a CodeMirror compartment, so turning it on or off swaps the extension without rebuilding the editor, and it shows a --NORMAL-- or --INSERT-- mode line. It covers the card editor only. With Vim on, Escape never closes the card, because Escape is how you leave insert mode. :w is accepted even though cards autosave.
Keeping the bundle small
CodeMirror, Shiki, KaTeX and Mermaid are heavy. The editor and preview load on the first card open, and Mermaid only loads for a card that contains a diagram. The main bundle stays at 165 kB.
The same commit squared off the filter chips and badges. The theme has a radius of 0, but pills were still round. Colour dots stay round.
Still in review
Three checks are open: a full day of use in the desktop app, image embeds, links and Mermaid against a real folder in the desktop build, and a card that closed once while typing in Vim insert mode and hasn't done it again.