mw 0.1.0 — markdown-source writing tool Ctrl-S save · F6 preview · F7 build · Ctrl-Q quit

mw

a Markdown-source writing tool

Small, fast, and terminal-first, in Rust. Plain UTF-8 text is canonical — the parse tree is a derived view, and layout is delegated to pandoc → XeLaTeX rather than reimplemented. Live browser preview, citations, cross-references, an LSP, minisign-signed builds, and undo that survives reopening.

Download · v0.1.0

Linuxx86-64 · static musl2.7 MBmw-0.1.0-x86_64-linux-musl.tar.gz5a93690de599…
Linuxaarch64 · static musl2.6 MBmw-0.1.0-aarch64-linux-musl.tar.gze3a4f7230acb…
macOSIntel · x86-642.5 MBmw-0.1.0-x86_64-macos.tar.gz8f6b0155b90e…
macOSApple Silicon · arm642.5 MBmw-0.1.0-aarch64-macos.tar.gz1e0b940bcd0c…
Windowsx86-642.8 MBmw-0.1.0-x86_64-windows.zip0448c4573dbe…

Each archive contains mw + mw-lsp. Checksums SHA256SUMS, minisign-signed .minisig (pubkey RWS4Ru+xtN2UWVLcfvEG8dobVwp2iWfAGmiWTliTO3Z3DUfvB/A3+Atb):
minisign -Vm SHA256SUMS -P RWS4Ru+xtN2UWVLcfvEG8dobVwp2iWfAGmiWTliTO3Z3DUfvB/A3+Atb && sha256sum -c SHA256SUMS
Extract & run: tar xzf mw-….tar.gz && ./mw-…/mw file.md. PDF export needs pandoc + XeLaTeX.

A small, fast, terminal-first Markdown writing tool in Rust. Plain UTF-8 text is canonical; the parse tree is a derived view, and layout is delegated to pandoc → XeLaTeX rather than reimplemented. See markdown-writing-tool-plan.md for the full design (and wp-rust-editor-plan.md for the deliberately opposite WordPerfect-parity design, which is not what this builds).

Website: https://mw.itys.net

Status

Phases 0–8 — done. The plan is complete.

Possible extras (beyond the plan): incremental tree-sitter re-parse, source-mapped build-error jumpback (synctex), a GUI front-end.

Build & run

Prebuilt, signed v0.1.0 binaries for Linux (x86_64 / aarch64, static musl), macOS (Apple silicon / Intel), and Windows (x86_64) are at https://mw.itys.net — each archive holds mw + mw-lsp, verifiable against a minisign-signed SHA256SUMS (public key RWS4Ru+xtN2UWVLcfvEG8dobVwp2iWfAGmiWTliTO3Z3DUfvB/A3+Atb). Or build from source:

cargo build --release
./target/release/mw path/to/file.md                 # or: cargo run -p mw-tui -- file.md
./target/release/mw --theme matrix path/to/file.md  # amber (default) | matrix | wp

Keys

KeyAction
arrows / Home / End / PgUp / PgDnmove (hold Shift to select)
Ctrl-↑ / Ctrl-↓jump to previous / next heading
Alt-↑ / Alt-↓ (or Ctrl-Shift-↑ / ↓)move the current line up / down
Ctrl-Aselect all
Ctrl-Ssave (prompts for a path if the buffer is unnamed) · Ctrl-Z undo · Ctrl-Y/Ctrl-R redo
Ctrl-K / Ctrl-Ucut the current line or selection (consecutive cuts accumulate) / paste it back (nano-style)
Ctrl-Ddelete the character under the cursor
F2cycle color theme (amber → matrix → wp)
F3toggle outline sidebar
F4 / F5fold at cursor / fold-all toggle
F6open / focus live browser preview
F7build PDF (background; jumps to errors)
F8focus / typewriter mode
typing @citation / cross-ref completion (↑/↓ · Tab/Enter · Esc)
Ctrl-]go to cross-reference definition (@sec:x{#sec:x})
Ctrl-Fincremental search (type · Enter/↓ next · ↑ prev · Esc done)
Ctrl-Eexpand snippet (fm/today/tbl) · Ctrl-L ASCII-fix smart punctuation
Ctrl-Qquit (prompts once if unsaved)

Headless commands

mw build <file|dir>        # PDF; whole book if an mw.toml project is found
mw pdf|html|docx|epub <p>  # export to a specific format
mw wordcount <file|dir>    # per-file + project total (or single-file counts)
mw search <pattern> [dir] [-i]   # project-wide substring search
mw check <file|dir>        # report undefined citation keys
mw lint  <file|dir>        # ASCII + semantic-line-break warnings
mw fmt   <file> [--sembr]  # convert smart punctuation -> ASCII (+ one sentence/line)

mw template [file]         # print a full-feature scaffold (or write it to <file>)

mw keygen                  # create a signing key pair (prints the public key, base64)
mw sign   <file>           # write a detached <file>.minisig
mw verify <file> [-P <b64>]# verify <file> against its .minisig (key file or inline key)

Signing (minisign-compatible)

mw ships a small signing sidecar for documents and build artifacts. Signatures are Ed25519 in the minisign format — the same scheme as Frank Denis's minisign and OpenBSD's signify — so .minisig files made by mw verify with the stock minisign CLI and vice-versa. This is deliberately not ML-DSA / Dilithium (FIPS 204): the "breakable keys" weakness is a property of ML-DSA's lattice keys and does not apply to Ed25519 (nor to hash-based SLH-DSA / SPHINCS+). Ed25519 is classical, not post-quantum; if you also want PQ coverage later, a hash-based or hybrid backend is the conservative add-on.

mw keygen                          # -> ~/.config/mw/minisign.{key,pub}; prints pubkey
mw sign report.pdf                 # -> report.pdf.minisig
mw verify report.pdf               # uses ~/.config/mw/minisign.pub
mw verify report.pdf -P RWQ…       # or an inline base64 public key
minisign -Vm report.pdf -P RWQ…    # stock minisign verifies it too

mw build book/ --sign              # build, then sign the artifact (book.pdf.minisig)
MW_SIGN_AUTO=1 mw pdf doc.md       # auto-sign every headless build

--sign (or MW_SIGN_AUTO=1) signs the build output after a successful mw build|pdf|html|docx|epub; a signing failure is fatal. Auto-sign applies to headless builds only (the in-editor F7 build is unaffected).

Keys live in $MW_SIGN_DIR (default ~/.config/mw); the secret key is written 0600 and encrypted with a password. The password is read from $MW_SIGN_PASSWORD when set (non-interactive / CI), otherwise prompted. Flags: -s/--secret, -p/--public, -P/--pubkey <base64>, --sig <path>, -c/--comment <text>, -f/--force.

New-document template

Opening a new file (mw notes.md where notes.md doesn't exist) seeds the buffer with a small starter scaffold covering the most commonly used constructs (a YAML header, headings, styled text, a link, bullet/numbered lists, a block quote, a fenced code block, and a table). Three discoverable attributes are auto-filled:

mw template prints the same scaffold to stdout; mw template <file> writes it (it won't clobber an existing file without -f/--force). The scaffold builds to a PDF as-is.

Persisted undo history

mw keeps your document plain UTF-8 and canonical — but it also persists the undo stack beside the file so Ctrl-Z survives closing and reopening. Saving report.md writes a sidecar report.md.mwh: a line-oriented, human-readable journal — a one-line header anchoring the base text by content hash, then one line per undo unit (E <pos> <cursor_before> <cursor_after> + the removed / inserted text):

mwh1 cbf29ce484222325 0
E 0 0 19		The quick brown fox
E 19 19 20		\n
E 20 20 30		jumps over

The status bar shows your position in the timeline as ↑{undo} ↓{redo} — steps you can Ctrl-Z back through and Ctrl-Y/Ctrl-R forward through — and it's populated straight away on a reopened file, since the history was restored from the sidecar.

Set MW_HISTORY=0 to turn the sidecar off. The .mwh files are derived artifacts — add *.mwh to your document repo's .gitignore if you don't want them tracked.

Projects

A project is a directory with an mw.toml, or just a folder of *.md (sorted):

[project]
title = "A Small Book"
author = "MJ"
files = ["intro.md", "ch1.md", "ch2.md"]   # compile order; omit to use sorted *.md
template = "template.tex"                   # optional (pandoc --template)
bibliography = "refs.bib"                   # optional (pandoc --citeproc)
output = "book.pdf"                          # optional default output

mw walks up from the file you open to find mw.toml; build/wordcount/search then operate on the whole book.

Language server

mw-lsp speaks LSP over stdio — point any client at the mw-lsp binary for Markdown diagnostics (ASCII / semantic line breaks / undefined citations + cross-refs), an outline (document symbols), @key completion, and go-to-definition.

Themes

--theme amber|matrix|wp at launch, or F2 to cycle in-session: amber (amber phosphor on near-black, default), matrix (bright green on black), wp (white on classic WordPerfect-DOS blue). Defined in mw-tui/src/theme.rs.

Workspace