A Markdown previewer that re-renders the moment the file changes on disk. Native macOS app (Swift, WKWebView) plus a Linux port (GTK3, WebKit2GTK) sharing the same renderer.

Swift 1 star main
  • macOS
  • Swift
  • Linux
  • GTK
View on GitHub
README.md

MDLive

Install on macOS  ·  Install on Linux

A small Markdown previewer with simple editing that re-renders the moment the file changes on disk.

It was built for working next to AI coding agents. When Claude Code or Codex edits a .md file, you see the new version straight away. There is no vault and no server. It opens a file and shows it. If something needs fixing, click anywhere in the page and type, like a Google Doc, and the change is autosaved straight back to the Markdown file. Editing is Mac only for now. The Linux port is still viewer only.

Dark theme reading view Light theme with the outline sidebar and find
LaTeX math rendered with KaTeX Settings window

What it does

  • Re-renders on every save, including atomic saves where the editor writes a temp file and renames it over the original
  • Click into the rendered page and type to edit it in place. Edits autosave about a second after you stop typing, and only the lines of the blocks you touched change in the file, so the rest of your Markdown stays byte for byte what it was. Math and Mermaid diagrams are kept as they are
  • A Format menu for simple edits: bold, italic, strikethrough, inline code, Heading 1 to 3, body text, bulleted and numbered lists, and links. Each one toggles, so Bold on bold text takes it off and Body Text turns a heading back into a paragraph, and the result is written back as plain Markdown. With nothing selected, formatting acts on the word under the cursor, or on what you type next when the cursor is at a word boundary
  • The standard Edit menu: undo, redo, cut, copy, paste (always as plain text) and select all
  • Keeps your scroll position across refreshes, so the page does not jump
  • Syntax highlighting for fenced code blocks
  • GitHub style tables, task lists, footnotes, definition lists, strikethrough
  • LaTeX math with KaTeX, off by default, works offline
  • Mermaid diagrams from fenced mermaid blocks, themed to match, works offline
  • Dark and light themes, font zoom, and an adjustable content width
  • Find, with a match count
  • An outline sidebar built from the document headings
  • Print, plus export to PDF or self contained HTML
  • Keep the window on top, so it can sit beside your editor
  • One window per file, and re-opening a file focuses the window it is in already
  • Fully offline. Nothing is fetched at runtime and nothing is reported anywhere.

Linux

Needs Python 3, GTK 3 and WebKit2GTK, which most desktops already have:

sudo apt install python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1

Then install into ~/.local, no root required:

git clone https://github.com/partypancake8/MDLive.git
cd MDLive/linux
./install.sh

That puts mdlive on your PATH, adds a desktop entry, and makes MDLive the default opener for .md files. Use ./install.sh --no-default to leave your current default alone, and ./install.sh --uninstall to remove it.

mdlive README.md

Shortcuts

Action Key
Open Ctrl+O
Refresh Ctrl+R
Find Ctrl+F
Find next, find previous Ctrl+G, Ctrl+Shift+G
Zoom in, out, reset Ctrl++, Ctrl+-, Ctrl+0
Outline sidebar Ctrl+Alt+1
Keep on top Ctrl+Shift+T
Copy file path Ctrl+L
Print Ctrl+P
Settings Ctrl+,
Undo, redo (Mac) Ctrl+Z, Ctrl+Shift+Z
Cut, copy, paste (Mac) Ctrl+X, Ctrl+C, Ctrl+V
Bold, italic (Mac) Ctrl+B, Ctrl+I
Inline code (Mac) Ctrl+Shift+C
Heading 1, 2, 3, body text (Mac) Ctrl+Cmd+1, Ctrl+Cmd+2, Ctrl+Cmd+3, Ctrl+Cmd+0
Bulleted list, numbered list (Mac) Ctrl+Shift+8, Ctrl+Shift+7
Link (Mac) Ctrl+K
Version history (Mac) Ctrl+Alt+Y (⌥⌘Y on the Mac)

macOS

The Mac version is a native Swift app built on AppKit and WKWebView. Build it with XcodeGen:

brew install xcodegen
cd MDLive
xcodegen generate
xcodebuild -project MDLive.xcodeproj -scheme MDLive -configuration Debug \
  CODE_SIGNING_ALLOWED=NO -derivedDataPath build/dd build
cp -R build/dd/Build/Products/Debug/MDLive.app /Applications/MDLive.app
open -a MDLive sample/hello.md

Install to /Applications and launch it by name. Running open -a against the raw DerivedData path fails with Launch Services error -600.

The shortcuts match the table above with Command in place of Control, so Open is ⌘O and the outline is ⌥⌘1. The rows marked Mac are the Edit and Format menus, which only the Mac app has, since the Linux port is a read-only preview. The heading keys keep both modifiers, so Heading 1 is ⌃⌘1 and body text is ⌃⌘0. Strikethrough has no default key. Every Format shortcut can be remapped in the Shortcuts settings tab.

Version History

MDLive keeps a log of every change to every file it has open, a bit like Google Docs. File > Version History… (⌥⌘Y) opens a sidebar on the right that lists the versions of the current file, newest first and grouped by day. Each row shows the time and where the change came from:

  • Opened: the file as it was when MDLive opened it (recorded the first time, and again whenever the file on disk differs from the newest version).
  • You: an autosave of your edits. Saves less than two minutes apart are grouped into one version.
  • Outside change: another program changed the file while it was open.
  • Restored: a Restore from the sidebar.

Click a row to see that version, read only, with a bar on top that says when it is from. Restore writes it back to the file right away (the version it replaced stays in the list, so a restore can be undone the same way), and Back to current returns to the live file. Closing the sidebar also returns to it.

The log lives in ~/Library/Application Support/MDLive/history/, one folder per file (named by the SHA-256 of its path) with an index.json and one .md snapshot per version. Each file keeps its newest 200 versions and the whole log stays under 500 MB, dropping the oldest versions first. Set MDLIVE_HISTORY_DIR to keep it somewhere else (the tests and headless gates do).

The Mac build is ad hoc signed for local use. Developer ID signing and notarization are not set up, so the Sparkle update plumbing is wired in but inert.

How it works

Both versions share the same renderer. MDLive/Resources/web/ holds markdown-it 14.1.0, highlight.js, KaTeX 0.16.11 and Mermaid 11.17.2, all vendored and pinned, and index.html defines the entire contract: a render() function, a ready handshake, and a few message handlers for links, the outline and scroll position.

The native side of each app is only a shell around that:

macOS Linux
Windows and menus AppKit GTK 3
Web view WKWebView WebKit2GTK
File watching FSEvents GIO file monitor
Language Swift Python

Both web views are WebKit, so the Linux port uses the web assets byte for byte with no changes, including the window.webkit.messageHandlers bridge and the custom mdlive-img:// scheme that serves local images. Images are only served from the directory holding the open file, so a document cannot pull in files from elsewhere on disk.

The watcher monitors the parent directory rather than the file itself, because an atomic save replaces the inode and would break a watch on the file. Changes are debounced by 150 ms and only applied once the size and modification time stop moving, which avoids rendering a half written file. A slow poll runs alongside as a fallback.

Development

linux/mdlive.py --selftest

The selftest renders fixtures in a real offscreen WebKit view and reads the resulting DOM back, checking headings, bold text, syntax highlighting, blockquotes, tables, the outline, find and HTML export. It also covers the image path validation and the error strings. It exits non zero on failure.

The Mac side has an equivalent harness plus XCTest suites:

MDLIVE_SELFTEST=/tmp/out.json MDLIVE_OPEN="$PWD/sample/hello.md" \
  /Applications/MDLive.app/Contents/MacOS/MDLive

web-tests/run.sh drives the same harness against sample/mermaid.md and asserts that each valid diagram produced an svg and the broken one shows its error line. It also runs web-tests/edit-test.sh, which types into a paragraph, a list item, a code block, a table cell, a heading and a footnote of sample/kitchen-sink.md through MDLIVE_EDIT_SELFTEST and checks that the autosaved file changed only in those lines, and that just clicking in never rewrites the file.

sample/ holds the fixtures, including kitchen-sink.md, which exercises every supported element.

Project docs