mdo-cli 0.3.0

A simple Rust CLI to convert Markdown to HTML5 (styled with simple.css), with optional file watching
mdo-cli-0.3.0 is not a library.

📝 mdo — Markdown to HTML5 Converter (with optional live watch)

mdo is a small, fast command-line tool written in Rust that converts Markdown files into HTML.

By default it produces a complete, HTML5-compliant document styled with simple.css (vendored at build time — no network access at runtime). An optional watch mode keeps re-rendering the output whenever the Markdown source is edited.

Project site: https://maphew.github.io/mdo/


🚀 Features

  • ✅ Converts .md files to standalone HTML5 documents
  • 🎨 Pretty default styling via embedded simple.css
  • 🌓 Automatic light/dark mode (follows OS) plus a manual toggle button
  • 📄 --bare flag emits a raw HTML fragment (no <html>/<head>/<body>/CSS)
  • 👀 --watch flag enables auto-rerender on file change (with debouncing)
  • 🌐 --open flag renders to a temp dir and launches the system default browser
  • ⚡ Fast and self-contained — single binary, no runtime assets
  • 🧩 Built on pulldown-cmark, clap, and notify

📦 Installation

From crates.io

cargo install mdo-cli

Build from source

git clone https://github.com/maphew/mdo.git
cd mdo
cargo build --release
./target/release/mdo input.md

📦 Usage

Usage: mdo [OPTIONS] <INPUT>

Arguments:
  <INPUT>  Input Markdown file

Options:
  -o, --output <OUTPUT>  Output HTML file (defaults to <input>.html alongside the input,
                         or to a temp directory when --open is used). Existing files are overwritten
  -w, --watch            Watch the input file and re-render on every change
  -b, --bare             Emit only the raw HTML fragment (no <html>, <head>, <body>, no CSS)
      --open             Render to a temp directory and launch the system default browser.
                         The source folder is left untouched unless --output is given
  -h, --help             Print help
  -V, --version          Print version

If --output is omitted, the output is written next to the input with the extension changed to .html (e.g. foo.mdfoo.html). Existing files are overwritten without prompting.

When --open is used without --output, the rendered HTML goes to a stable location under your OS temp directory (e.g. %TEMP%\mdo\<hash>\<name>.html on Windows) so the source folder stays clean. Re-opening the same file overwrites the same temp output. A <base href="file:///…"> tag pointing at the source folder is automatically injected whenever the output lives elsewhere, so relative images and links in the Markdown still resolve correctly.

Examples

Convert once and exit (default — produces a styled, standalone HTML5 page next to the input). The README.html in this repo is generated like this:

mdo input.md                    # writes input.html
mdo input.md -o docs/out.html   # writes docs/out.html

Emit a bare HTML fragment (useful for embedding in another template):

mdo --bare input.md

Watch for changes and re-render on every save:

mdo --watch input.md

Render to a temp file and open it in your default browser (does not write next to the source):

mdo --open input.md

This is the recommended setup for a Windows Open as HTML file association — use the bundled mdo-open.exe wrapper and double-clicking a .md file in Explorer will render to the platform temp directory and open it without leaving any artifacts in the source folder.


Linux file manager integration

The repo also ships Linux helpers under scripts/ for per-user file-manager integration:

# Add "Open as HTML" as an "Open With" handler for Markdown files
./scripts/install-linux-file-manager.sh

# Same, but also make it the default Markdown handler
./scripts/install-linux-file-manager.sh --set-default

# Undo everything the install script did
./scripts/uninstall-linux-file-manager.sh

The installer writes ~/.local/share/applications/mdo.desktop, whose command is mdo --open %f, plus a small SVG icon under ~/.local/share/icons/hicolor/scalable/apps/mdo.svg. The desktop entry is named Open as HTML, so GNOME Files/Nautilus and other XDG file managers show an action-oriented entry instead of a tool-name-only entry. Rerunning the installer also removes older Nautilus Scripts entries named Preview with mdo or Render with mdo.

Pass --exe /path/to/mdo if the binary is not on PATH. The script looks for mdo on PATH first, then falls back to target/release/mdo next to this repo after cargo build --release.

Result examples after install:

  • Most XDG file managers: right-click a .md file → Open WithOpen as HTML.
  • With --set-default: double-clicking a Markdown file launches mdo --open.
  • The rendered page opens in your default browser from a temp path such as /tmp/mdo/<hash>/<name>.html, and no .html file is left beside the source.

🪟 Windows Explorer integration

The repo ships two PowerShell helpers under scripts/ that wire mdo into Explorer for the current user only (no admin, no HKLM changes):

# Add: an "Open as HTML" right-click verb and Open With app entry
powershell -ExecutionPolicy Bypass -File .\scripts\install-explorer.ps1

# Undo everything the install script did
powershell -ExecutionPolicy Bypass -File .\scripts\uninstall-explorer.ps1

The install script registers mdo-open.exe, a tiny windows-subsystem wrapper built alongside mdo.exe. The wrapper exists for one reason: when Explorer launches a normal console binary it briefly flashes a black console window. mdo-open.exe runs as a GUI subsystem app and spawns mdo.exe --open with CREATE_NO_WINDOW, so double-clicking a .md file renders from the platform temp directory and opens straight in the browser with no flash. The regular CLI is unchanged — mdo.exe from a terminal still prints to stdout normally.

The install script auto-locates mdo-open.exe via PATH, falling back to target\release\mdo-open.exe next to the repo. Pass -ExePath C:\path\to\mdo-open.exe to override. mdo.exe must sit next to mdo-open.exe; both are produced by cargo build --release and cargo install mdo-cli.

It also generates a small .ico at %LOCALAPPDATA%\mdo\md.ico by rendering a single Unicode character — by default Ⓜ (circled M) in a mid-tone blue chosen so it stays legible in both light and dark Explorer themes. Override either via parameters:

.\scripts\install-explorer.ps1 -IconChar "📄" -IconColor "#E64A19"

uninstall-explorer.ps1 removes the .ico (and its folder if empty) along with all the registry keys. It also removes the old Preview with mdo and Render with mdo verbs from earlier installs.

To make Open as HTML the default .md handler after running the install script, right-click a .md file → Open with → Choose another app → pick Open as HTML → tick Always use this app. Windows requires that last step to be done interactively.

Result examples after install:

  • Right-click any .md file → Open as HTML. On Windows 11, this may appear under Show more options.
  • Open with → Open as HTML appears as an available app for Markdown files.
  • If you make Open as HTML the default handler, double-clicking a .md file opens the rendered page in your browser with no console-window flash.
  • The rendered page opens from %TEMP%\mdo\<hash>\<name>.html, and the source folder stays unchanged.

macOS Finder quick action

mdo does not ship a macOS installer script yet, but Finder can run mdo --open through a per-user Automator Quick Action. Apple documents Quick Action workflows and shell-script actions in the Automator User Guide:

Create a Quick Action in Automator, set it to receive files in Finder, add Run Shell Script, set Pass input to as arguments, and use the absolute path to mdo:

for file in "$@"; do
  /path/to/mdo --open "$file"
done

Save the workflow as Open as HTML. The result is:

  • Finder shows Quick Actions → Open as HTML for selected Markdown files.
  • Running the Quick Action opens the rendered page in your browser.
  • The rendered page opens from a temp path under $TMPDIR/mdo/<hash>/<name>.html, and no .html file is left beside the source.

🎨 Default output

The default (non---bare) output is a complete HTML5 document:

  • <!DOCTYPE html> + <html lang="en">
  • UTF-8 charset and responsive viewport meta
  • <title> derived from the first # Heading in the source (falls back to the input filename)
  • An inlined copy of simple.css inside <style>, giving you sensible typography and automatic light/dark mode out of the box
  • A small floating ☀/☾ button (top-right) for manually overriding the theme; the choice is remembered in localStorage
  • Body content wrapped in <main>

Markdown extensions enabled: tables, footnotes, task lists, strikethrough.


🙏 Credits

This project is a grateful fork of Hafiz Ali Raza's original Markdown-to-HTML CLI. Hafiz remains credited as an author, and this fork keeps that lineage explicit so future improvements can be offered back upstream.

This fork adds:

  • Convert-and-exit as the default; watch mode is now opt-in (--watch)
  • Optional --output (defaults to <input>.html next to the source)
  • Standalone HTML5 output with embedded simple.css
  • A --bare flag that preserves the original fragment-only behavior
  • An --open flag that renders to a temp directory and launches the system default browser (with auto-injected <base href> so relative refs resolve)
  • Light/dark theme toggle button overlaid on the rendered page
  • Title auto-derived from the first heading
  • Debounced file-change events (no more duplicate renders per save)
  • Surfaced watcher errors instead of swallowing them
  • Markdown extensions: tables, footnotes, task lists (in addition to strikethrough)

The bundled simple.css is © 2020 Kev Quirk and distributed under the MIT License — see assets/simple.css.LICENSE.


📄 License

This project is dual-licensed under either of:

at your option, matching the licensing of the upstream project.