Windows · v0.1.2 · MIT

Notepad for .txt. kunang for .md.

A fast, simple Markdown viewer and editor for Windows. Double-click a .md file and it opens rendered — in about 47 milliseconds, not the one to three seconds a full editor takes. One keystroke gets you a real two-pane live-preview editor. Another gets you back.

Prefer an installer? kunang-setup-0.1.2.exe  ·  All releases

Single file · ~144 MB No admin rights No install step Open source

The whole product

One key between
reading and writing.

Most of the time you want to glance at a rendered document, occasionally fix a line, and close it. kunang is built around exactly that, and nothing else.

Double-click .md

Rendered view mode, immediately. Headings, tables, code, links — a real document, not a wall of asterisks. No splash screen, no workspace to pick.

Ctrl + E

Source left, live preview right. A CodeMirror 6 editor with synced scrolling, so the line you are typing is the line you are watching.

Esc

Back to reading. Esc backs out one level at a time — the find bar, then edit mode, then the tab, then the window. It never surprises you.

Double-click → painted

A race that
ends early.

The first 250 ms, magnified 12×
Read the numbers as a table
PathTime to a visible documentCondition
kunang47 ms (p50) · 57 ms (p95)Warm host, measured by npm run bench
kunang~960 msCold — no host running yet
Notepad80–200 msCold, plain text only
VS Code / Typora / Obsidian1,000–3,000 msCold Electron-class boot

Linear scale. kunang's figure is the warm path and is stated as such — the cold path is in the table too.

The trick

The double-click path
never boots Electron.

A cold Electron window costs 800 ms to 2 s. So the .md file association does not point at Electron. It points at a 2.4 MB Go stub whose only job is to reach an already-running host over a named pipe. The host stays resident after you close a window, so every open after the first is warm.

Explorer runs kunangstub.exeGo, ~2.4 MB, GUI subsystem — no console flash~5 ms
Connects to \\.\pipe\kunang.<secret>Grants foreground rights, writes argv + cwd + t013.6 ms
Resident host takes the payloadHands it to the window you were last looking at, or to a pre-warmed hidden one+0.0 ms
Renderer opens a tab and paintsmarkdown-it + highlight.js; KaTeX and Mermaid only if the document needs them+15.4 ms
Window shows — and rebuilds its spareA fresh hidden window is built immediately, ready for the next double-click+17.5 ms

Measured, not estimated

Hopp50p95
stub entry → pipe read13.6 ms16.7 ms
→ dispatched to renderer+0.0 ms
→ renderer painted+15.4 ms
→ window shown+17.5 ms
Total47.0 ms57.3 ms

npm run bench launches the real stub 50 times — the same binary Explorer runs on a double-click — so the numbers include process creation and the pipe handshake. Figures move a few milliseconds with machine load; the gate is the contract, not the exact number.

Cold, with no host running, bringing the host up takes about 960 ms; the very next open is 59.6 ms. The installer adds a logon task that warms the host 30 seconds after you sign in, so even the first open of the day is fast.

Then it gets out of the way.

Everything it does

Small app.
Not a simple one.

Fast is the headline. But a document editor earns trust in the details — what happens to your encoding, your line endings, your unsaved buffer when the file changes underneath you.

Tabs that behave

Every open lands as a tab in the window you were last looking at. Drag to reorder; drag one off the strip and it gets a window of its own — a move, never a copy. The strip only appears once there is more than one document.

Two-pane live preview

Source on the left in CodeMirror 6, rendered output on the right, updated through morphdom so the preview never flickers or loses your place. Scroll one pane, the other follows.

Outline sidebar

Ctrl+\ opens a heading outline for long documents. Click to jump; the position you were at is remembered per file, so reopening lands you where you left off.

Encoding survives the round trip

UTF-8, UTF-16 LE/BE with BOM sniffing, CP1252 detected by validating the bytes — each written back as it was found, along with the file's dominant line ending. Your git diffs stay clean.

Atomic saves

Writes go through Windows' own ReplaceFileW, preserving ACLs and alternate data streams. A CP1252 file that gains a character it cannot represent is promoted to UTF-8 rather than losing it to a ?.

It watches the file

Changed on disk? A clean buffer reloads silently; a dirty one tells you and waits. Delete the file and the buffer stays — at that point it is the only copy left. Rename it and kunang follows.

GFM, math and diagrams

markdown-it with GitHub-flavoured Markdown and highlight.js. KaTeX for $…$, Mermaid for ```mermaid — both imported only when a document actually contains them, so the fast path never pays for features it does not use.

Export self-contained HTML

Ctrl+Shift+E writes one HTML file with the styles and images inlined — something you can email, or open on a machine that has never heard of Markdown.

Themes, zoom and your own CSS

Ctrl+Shift+T cycles auto / light / dark. Ctrl with +, or 0 handles zoom. Drop a stylesheet at %LOCALAPPDATA%\kunang\custom.css and restyle the document entirely — F5 re-reads it.

Drag and drop

Drop .md files anywhere to open them, one tab each. Drop an image while editing and it is inserted as Markdown, linked relative to the open document. Relative links between documents open in a tab.

Sessions come back

Open documents are remembered, in the order you arranged them. Restored tabs are created empty and only read when you return to one — so a fifty-file session costs nothing on the open path. Files that vanished are dropped quietly.

Find, print, and a 5 MB guard

Ctrl+F finds in the rendered page; edit mode hands over to CodeMirror's own search. Ctrl+P prints. Documents over 5 MB skip syntax highlighting and say so, rather than wedging the renderer.

Every shortcut

Learn it in
one screen.

Hover or tab through a key to see what it does. That is the entire keymap — there is no second page.

Ctrl + E Toggle edit mode — source left, live preview right

The Tools menu also carries Register / Unregister as default .md viewer.

Try the idea

This is what
Ctrl+E looks like.

A rough sketch of edit mode, running right here. Type on the left; the right side keeps up. In the real app the editor is CodeMirror 6 with syntax highlighting, and the preview is full GitHub-flavoured Markdown.

notes.md roadmap.md UTF-8 · CRLF

And it stays quiet.

Safe by default

A Markdown file
is untrusted input.

A single remote <img> in a document is enough to report that you opened it, and from which IP address. So kunang blocks every remote request by default and asks first.

That consent follows the active tab, and switching documents withdraws it. It is deliberately never persisted — the safe state is the one you get by doing nothing.

The renderer is a sandbox

contextIsolation, sandbox: true, no Node integration. It talks to the main process through one narrow contextBridge API and nothing else. Rendered HTML goes through DOMPurify on the way in.

Local files, local protocol

Images and links inside a document are served over a custom mdfile:// protocol rather than raw file access. External links open in your real browser, not inside the app.

Hostile content, handled

Mermaid runs with securityLevel: 'strict' and KaTeX with throwOnError: false — both are handed text straight from a document nobody vouched for. The test suite includes a deliberately hostile corpus.

The pipe is unguessable

The named pipe carries a random 32-hex secret stored in %LOCALAPPDATA%\kunang\pipe. Node cannot set pipe ACLs, so an unguessable name is what stops another user's process from driving your host.

Install

Pick one.
Not both.

Recommended

Portable — a single file

  • Download kunang-portable-0.1.2.exe (~144 MB) and run it.
  • It unpacks itself to %LOCALAPPDATA%\kunang\app\<version>\ in about 4 seconds.
  • Starts the resident host, then registers the .md association once the host is warm.
  • After that the exe has done its job. Double-clicking a .md runs only the 2.4 MB stub.
  • Run it again carrying a newer build and it upgrades in place — keyed on a hash of the payload, not the version number.
Download portable · 0.1.2
Alternative

NSIS installer

  • kunang-setup-0.1.2.exe — a per-user install, no admin rights.
  • Same result, plus a logon task that re-warms the host 30 seconds after you sign in.
  • That makes even the first open after a reboot fast. The portable build deliberately does not create that task.
  • Uninstalls through Windows the way you would expect.
Download installer · 0.1.2
Install one or the other. They register the same .md association and each wants a resident host at its own path, so the two would compete for the same named pipe.
If Windows ignores the association. First run registers .md for you. But on a machine where you have ever picked a default app for .md, Windows stores a salted hash under …\FileExts\.md\UserChoice that Microsoft made unforgeable specifically to stop apps hijacking associations — so no program can set it, not kunang, not an installer running as admin. Set it yourself: right-click any .mdOpen withChoose another app → kunang → Always.
Or build it from source. Node 20.19+ (or 22.12+) and Go 1.21+, then npm install and npm run package:portable. The full build notes are in the README.

Before you download

What it is not.

kunang is pre-1.0 and feature-complete against its original plan. Here is the honest list, the same one that sits in the README.

  • Windows only. The stub is Win32-specific — named pipes, ReplaceFileW, AllowSetForegroundWindow. There is no macOS or Linux build and the architecture does not port cheaply.
  • Expect a SmartScreen prompt. The portable exe is unsigned, self-extracting and writes to HKCU. That will keep tripping SmartScreen until it is code-signed. The source is public if you would rather build it yourself.
  • It is not small on disk. ~370 MB unpacked, ~200 MB resident at idle plus 60–80 MB per window — which is exactly why opens go to tabs in an existing window rather than a window each. That is the price of TypeScript instead of C++.
  • Rename detection is a heuristic. Windows reports a rename as an unrelated delete followed by an add, so they are paired by proximity in time within a directory. Deleting one file and creating another in that window looks identical. It errs toward following the file.
  • Not a zero-footprint portable app. A resident host and a file association both need a stable path on disk, so it unpacks once and stays. --uninstall plus deleting %LOCALAPPDATA%\kunang removes it completely.
  • Still on the list: multiple-window stress testing, taskbar grouping, network-drive and WSL paths, and idleTimeoutMinutes — the setting is read but nothing acts on it yet.