@hardikg/mdview
v0.8.1
Published
Local CLI markdown viewer with live reload, outline navigation, and folder browsing
Downloads
33
Maintainers
Readme
mdview
A local markdown viewer for reading long, hierarchical docs without losing focus. Run a command, browser opens with a beautifully rendered, navigable view of your file or folder. Edits in your editor show up live. Close terminal to stop.
mdview ./docs # browse a folder
mdview README.md # view a single file
mdview # current directoryWhy
Reading long markdown docs is hard:
- You scroll, get distracted, come back, can't find your spot.
- Deep inside a subsection, you forget how it relates to the parent topic.
- AI-generated brainstorming docs grow as you iterate — paste-and-render tools can't keep up.
- Raw markdown without color or rhythm is monotonous over hundreds of lines.
mdview is built for that. It's a single-machine, single-user, read-only viewer with editorial typography, persistent navigation, and live reload.
Install
npm install -g @hardikg/mdviewRequires Node >= 20. The mdview binary lands on your PATH.
Install from source
git clone https://github.com/hardikg2907/mdview.git
cd mdview
npm install
npm run build
npm install -g . # puts `mdview` on your PATHOr run from source without global install:
npm install && npm run build
node bin/mdview.mjs ./docsShare with friends without publishing
npm run build && npm pack # produces hardikg-mdview-<version>.tgzSend them the .tgz (Slack/Drive/AirDrop). They install with:
npm install -g ./hardikg-mdview-<version>.tgzUsage
mdview <path> # open a file or folder; the shell comes straight back
mdview # current directory
mdview ls # what's open, and where the server is
mdview rm <name> # stop serving a folder
mdview stop # stop the background server
mdview --foreground # run in this terminal instead (Ctrl-C to stop)
mdview --no-open # don't auto-launch the browser
mdview --port 9000 # one-off server on a specific port (implies --foreground)
mdview --palette nord # palette for this run (implies --foreground)
mdview --help # show usageOne server, many folders
mdview does not hold your terminal. It starts a single background server the
first time you need one, and every later mdview <path> — in any repo — adds
that folder to the same server and opens it. So you can read
~/work/api/docs and ~/notes side by side in one window, with one stable
URL, whether or not the folders are anywhere near each other.
$ mdview ~/work/api
mdview → http://mdview.localhost:7331/?root=api
added: /Users/you/work/api (api)
$ cd ~/notes && mdview .
mdview → http://mdview.localhost:7331/?root=notes
added: /Users/you/notes (notes)
$ mdview ls
● api ~/work/api (primary)
● notes ~/notes
server: http://mdview.localhost:7331/ (pid 40113)
logs: ~/.config/mdview/daemon.logFolders stay open until you remove them with mdview rm <name>, across restarts
and reboots. Up to 8 at a time — each one holds a live file watcher, so the
limit is there to keep mdview from quietly watching your whole disk. Opening a
file or subfolder inside a folder that's already open reuses it rather than
adding a duplicate.
The server keeps running until you mdview stop it, or you reboot. If you'd
rather it stood down on its own, set MDVIEW_IDLE_TIMEOUT to a number of
minutes and it will exit that long after the last browser tab disconnects; the
next mdview starts it again.
Its state lives in ~/.config/mdview/ (daemon.json, workspace.json,
daemon.log) and nowhere else. workspace.json is also how the CLI talks to
the server: mdview <path> writes the file and the server picks the change up,
which is why there is no HTTP endpoint that can change what's being served.
The URL is http://mdview.localhost:7331/ — *.localhost always resolves to
loopback (RFC 6761) with no setup, and the server still listens on 127.0.0.1
only. If your resolver doesn't answer the name, mdview falls back to printing
http://127.0.0.1:<port>/. Setting PORT changes the default port, so mdview
works as-is behind a local proxy like
portless if you want the port gone
entirely.
--palette takes classic, paper, nord, solarized, or high-contrast.
It wins over both .mdview.json and the global config, and never writes to
either. There is also a --vscode flag, used by the VS Code extension to run
mdview as a sidecar; it implies --no-open and prints a single JSON ready line
instead of the human-readable output.
--port and --palette both override a setting that belongs to a whole server,
and the background one is shared with every other folder you have open — so each
implies --foreground, giving you a server of your own. That also means every
invocation that worked before still behaves exactly as it did; only the bare
mdview [path] form returns the shell now.
--foreground closes when you Ctrl-C or kill it. The background server is
stopped with mdview stop. Set MDVIEW_DEBUG=1 for full stack traces on
unexpected errors.
Keyboard shortcuts
| Shortcut | Action |
|----------|--------|
| ⌘P / Ctrl+P | Quick file switcher (fuzzy search) |
| ⌘F / Ctrl+F or / | Open in-doc search |
| ⇧⌘F / Ctrl+Shift+F | Open folder-wide search |
| ⌘B / Ctrl+B | Toggle file tree |
| ⌘. / Ctrl+. | Toggle outline |
| ⌘\ / Ctrl+\ | Toggle theme |
| j / k | Next / previous heading |
| gg / ⇧G | Top / bottom of document |
| [ / ] | Previous / next heading at the same level |
| ⇧H / ⇧L | Previous / next file in folder |
| Ctrl+D / Ctrl+U | Half-page down / up |
| Alt / ⌥ + scroll | Fast scroll (~4×) in the main pane |
| f / m | Toggle focus mode / minimap |
| e / ⇧E | Expand all / collapse all sections |
| Tab (in search) | Cycle Doc ↔ Folder scope |
| Enter / Shift+Enter | Next / previous match in search |
| Esc | Close search / lightbox / panel |
| ? | Open shortcuts panel |
Click the keyboard icon in the header anytime to see the full list.
Features
Reading
- 3-pane layout: folder tree, content, outline. Both sidebars collapse to a thin label rail; both have drag handles to resize (widths persist).
- Editorial typography (serif body, italic accent H1, paper-grain background, JetBrains Mono in code blocks).
- Light & dark theme — follows your OS preference, with a manual override that persists.
- Five built-in palettes: classic / paper / nord / solarized / high-contrast. Pick one from the header palette swatch. Code blocks follow the palette (Nord syntax in Nord, Solarized in Solarized, etc.).
- Reading-progress bar pinned to the bottom of the header.
- Doc stats strip below the H1 (reading time, word count, heading count, "Updated N ago").
- Focus mode dims everything except the section at the viewport center.
- Optional minimap rail at the right edge: bars per heading, draggable to scroll, viewport indicator follows.
Navigation
- Folder tree sidebar; click any file to load it. Cross-file
[link](other.md)navigates inside the viewer. - Outline sidebar with scroll-spy and collapsible nesting. Six toggle pills (
H1–H6) filter which levels show. - Breadcrumbs in the header reflecting your current viewport heading; click any segment to jump.
- Stable per-heading anchors. Hover any heading to reveal a
#link that copies a deep-link URL. - Collapsible sections — every heading has a hover-revealed chevron that folds the section's content; anchor links auto-expand their target.
e/⇧Eexpand or collapse the whole doc. ⌘Pquick file switcher with fuzzy matching across the whole tree.
Rendering
- CommonMark + GFM (tables, task lists, strikethrough, autolinks).
- Server-side syntax highlighting via Shiki, palette-aware (10 variants per token — one for every palette/theme combination) — zero client highlighter bundle, no re-render on theme/palette swap.
- Mermaid diagrams, lazy-loaded only when a doc contains a
mermaidfence. - Math via KaTeX (
$inline$and$$block$$), lazy-loaded only when a doc contains math. - Custom-styled task list checkboxes (filled accent when checked, hollow when unchecked).
- Front matter detected and shown as a small collapsible metadata block.
- Inline HTML pass-through (your content, your trust).
- Images render via a safe
/__asset/*route with relative-path resolution against the file's folder. Click any image to open in a lightbox. - External links get an
↗icon and open in a new tab withnoopener noreferrer.
Live reload
- File edits show up instantly. Scroll position and outline collapse state are preserved.
Search
- In-doc search highlights all matches; counter shows current position;
Enter/Shift+Enterstep through them. - Folder-wide search greps every markdown file (
⇧⌘For theFoldertoggle); results grouped by file with snippets, click any hit to open that file. - Three options on the search bar:
Aa(case-sensitive),ab(whole word),.*(regex).
Polish
- Loading skeleton while files first load.
- Smooth transitions on theme swap, sidebar collapse, outline highlight.
- Copy button on every code block.
- Subtle hover tooltips on every header control.
Config
Two layered files, both optional, same schema:
- Global:
~/.config/mdview/config.json(honours$XDG_CONFIG_HOME) — your machine-wide defaults. - Per-project:
.mdview.jsonat the folder root — overrides global for this folder.
{
"palette": "nord",
"fontFamily": "serif",
"lineWidth": "70ch",
"defaultCollapsed": { "tree": false, "outline": false },
"ignore": ["deps", "_site"]
}All fields optional. Validated on load — invalid values fall back silently with a warning. Edit the per-project file while the server is running and the SPA picks up palette/font/lineWidth changes live; ignore is read once at startup (the file watcher's skip list is frozen — restart mdview after editing).
ignore extends the built-in skip list (which already covers node_modules, dist, build, out, target, coverage, vendor, __pycache__, venv, Pods, DerivedData, _build, plus every dotfile/dotdir). Add your own basenames here — handy when running mdview at a repo root with non-standard build dirs to avoid EMFILE: too many open files from the OS file-watch limit.
You can edit the ignore list from the CLI without opening the JSON:
mdview config path # print the global config path
mdview config ignore list # show built-in + user-added entries
mdview config ignore add deps _site # add one or more basenames
mdview config ignore rm deps # remove one or more basenamesStack
Node 20+, TypeScript, Fastify 5, markdown-it 14, Shiki 1, gray-matter, chokidar 4, Preact 10 + @preact/signals, KaTeX 0.16, mermaid 11 (lazy), JetBrains Mono via @fontsource, Vite 6, tsup 8, vitest 2.
Documentation
docs/ARCHITECTURE.md— how the system is put togetherdocs/CONTRIBUTING.md— dev workflow + how to add featuresdocs/FEATURES.md— comprehensive feature catalogTODO.md— roadmap (phase 3, phase 4)VERIFICATION.md— manual verification scenarios
Tests
npm test # vitest, server + client
npm run typecheck # both tsconfigs
npm run build # vite (client) + tsup (server CLI)Reporting issues
Found a bug or have a feature request? File it at github.com/hardikg2907/mdview/issues.
License
MIT.
