{"id":51278071,"url":"https://github.com/thgossler/mdv","last_synced_at":"2026-06-29T23:01:44.671Z","repository":{"id":366928690,"uuid":"1271345360","full_name":"thgossler/mdv","owner":"thgossler","description":"A minimal, self-contained cross-platform markdown viewer with TUI and GUI.","archived":false,"fork":false,"pushed_at":"2026-06-23T23:22:50.000Z","size":16268,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T00:14:55.798Z","etag":null,"topics":["cli","console","cross-platform","golang","gui","linux","macos","markdown","pdf","pipe","single-file-exe","tui","viewer","wails3","windows"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thgossler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-16T15:13:06.000Z","updated_at":"2026-06-23T23:22:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/thgossler/mdv","commit_stats":null,"previous_names":["thgossler/mdv"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/thgossler/mdv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgossler%2Fmdv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgossler%2Fmdv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgossler%2Fmdv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgossler%2Fmdv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thgossler","download_url":"https://codeload.github.com/thgossler/mdv/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thgossler%2Fmdv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34945707,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","console","cross-platform","golang","gui","linux","macos","markdown","pdf","pipe","single-file-exe","tui","viewer","wails3","windows"],"created_at":"2026-06-29T23:01:41.064Z","updated_at":"2026-06-29T23:01:44.042Z","avatar_url":"https://github.com/thgossler.png","language":"Go","funding_links":["https://github.com/sponsors/thgossler","https://www.paypal.com/donate/?hosted_button_id=JVG7PFJ8DMW7J"],"categories":[],"sub_categories":[],"readme":"\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/thgossler/mdv\"\u003e\n    \u003cimg src=\"images/image.png\" alt=\"Icon\" width=\"800\" /\u003e\n  \u003c/a\u003e\n\n  \u003ch1 align=\"center\"\u003emdv\u003c/h1\u003e\n\n  \u003cp align=\"center\"\u003e\n    A minimal, self-contained cross-platform markdown viewer with TUI and GUI. One executable, no installation, no dependencies, runs everywhere.\n    \u003cbr /\u003e\n\n[![Platforms](https://img.shields.io/badge/platforms-macOS%20%7C%20Windows%20%7C%20Linux-informational?label=Platforms)](https://github.com/thgossler/mdv/releases/latest)\n[![CI](https://img.shields.io/github/actions/workflow/status/thgossler/mdv/ci.yml?branch=main\u0026logo=github\u0026label=CI)](https://github.com/thgossler/mdv/actions/workflows/ci.yml)\n[![Latest release](https://img.shields.io/github/v/release/thgossler/mdv?logo=github\u0026label=Latest%20release\u0026sort=semver)](https://github.com/thgossler/mdv/releases/latest)\n[![Downloads](https://img.shields.io/github/downloads/thgossler/mdv/total?logo=github\u0026label=Downloads)](https://github.com/thgossler/mdv/releases)\n[![Open issues](https://img.shields.io/github/issues/thgossler/mdv?logo=github\u0026label=Issues)](https://github.com/thgossler/mdv/issues)\n[![Contributors](https://img.shields.io/github/contributors/thgossler/mdv?label=Contributors)](https://github.com/thgossler/mdv/graphs/contributors)\n[![Stars](https://img.shields.io/github/stars/thgossler/mdv?style=flat\u0026logo=github\u0026label=Stars)](https://github.com/thgossler/mdv/stargazers)\n[![License](https://img.shields.io/github/license/thgossler/mdv?color=blue\u0026label=License)](LICENSE.md)\n[![Sponsor](https://img.shields.io/github/sponsors/thgossler?logo=githubsponsors\u0026color=ea4aaa\u0026label=Sponsors)](https://github.com/sponsors/thgossler)\n\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003e **For most users**\n\u003e\n\u003e Open Markdown like any other document - cleanly, safely, and without editing\n\u003e tools getting in the way.\n\n\u003e **For experts and developers**\n\u003e\n\u003e Browse an entire local Markdown documentation set like a website, with linked\n\u003e navigation, rich rendering, and cross-document search - without building or\n\u003e serving anything. \n\n`mdv` adapts to wherever it runs:\n\n- **GUI** - a native-webview window with full rendering (default on desktops).\n- **TUI** - a rich terminal UI when no graphical environment is available.\n- **Console** - plain rendered output to stdout when piped or non-interactive.\n\n\u003e Honestly? This might be the missing tool that *should* ship pre-installed on\n\u003e every OS, with `.md` files associated to it out of the box. Or - even better -\n\u003e every OS should just bake this kind of instant markdown preview straight into\n\u003e its native file manager (File Explorer, Finder, Nautilus, you name it). Until\n\u003e that glorious day arrives, there's `mdv`. 😉\n\nIt is built so it **always starts**, including inside headless Docker containers\nover SSH: the distributed binary is a pure-Go launcher with **zero webview\nlinkage**, so missing `WebKitGTK`/GUI libraries never cause a failure. The GUI\nis a separate helper embedded in the binary and only spawned when a graphical\nenvironment is actually present.\n\n\u003e **Linux GUI requirement:** the embedded GUI helper needs **GTK \u003e= 4.14** and\n\u003e **WebKitGTK 6.0** (e.g. Debian 13+, Ubuntu 24.04+, Fedora 39+). On older\n\u003e systems mdv detects this and runs in the TUI/console instead; the installer\n\u003e prints a note when the GUI cannot run.\n\n## Install\n\nNo package managers needed - the install scripts download a single executable\nfrom GitHub Releases.\n\n**macOS / Linux:**\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/thgossler/mdv/main/scripts/install.sh | sh\n```\n\n**Windows (PowerShell):**\n\n```powershell\nirm https://raw.githubusercontent.com/thgossler/mdv/main/scripts/install.ps1 | iex\n```\n\nThe PowerShell installer is cross-platform - with PowerShell 7+ it also works on\nmacOS and Linux:\n\n```sh\npwsh -c \"irm https://raw.githubusercontent.com/thgossler/mdv/main/scripts/install.ps1 | iex\"\n```\n\nSilent install with automatic `.md` file association (for unattended setups\nor chaining from another installer):\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/thgossler/mdv/main/scripts/install.sh | sh -s -- --silent --associate-md-file-extension\n```\n\n```powershell\n$s = irm https://raw.githubusercontent.com/thgossler/mdv/main/scripts/install.ps1\n\u0026 ([scriptblock]::Create($s)) -Silent -AssociateMdFileExtension\n```\n\nOr download a binary directly from the [Releases](https://github.com/thgossler/mdv/releases)\npage:\n\n| Platform          | Asset                               |\n| ----------------- | ----------------------------------- |\n| macOS (universal) | `mdv-macos-darwin-universal.tar.gz` |\n| Windows (x64)     | `mdv-windows-x64.zip`               |\n| Windows (arm64)   | `mdv-windows-arm64.zip`             |\n| Linux (x64)       | `mdv-linux-x64.tar.gz`              |\n| Linux (arm64)     | `mdv-linux-arm64.tar.gz`            |\n\n### Where it gets installed and how PATH is handled\n\n| Platform      | Default location                | PATH handling             |\n| ---- | ------------------------ | ----------------------------------------- |\n| Windows       | `%LOCALAPPDATA%\\Programs\\mdv\\mdv.exe` | Added to your **user** `Path` (persisted for new terminals) and prepended to the current session. |\n| macOS / Linux | `/usr/local/bin/mdv` if it is on your `PATH` and writable, otherwise `~/.local/bin/mdv` | If the chosen directory isn't already on `PATH`, the installer appends it to your shell profile (`.zshrc`, `.bashrc`, `.bash_profile`, or `.profile`). |\n\nSet the `MDV_INSTALL` environment variable to install somewhere else (e.g.\n`MDV_INSTALL=$HOME/bin`), and `MDV_VERSION` to pin a specific release tag.\n\nOn Windows and macOS the installer also **asks whether to associate `.md` files\nwith mdv** so you can open Markdown by double-clicking. Set\n`MDV_ASSOCIATE_MD=1` to associate without being prompted (or `=no` to skip it\nsilently in non-interactive installs). See\n[Open Markdown files from File Manager](#open-markdown-files-from-file-manager)\nfor how association works and how to change the default by hand.\n\nFor unattended installs (e.g. chaining the script from another installer or\ntool), pass `--silent` (`-Silent` in PowerShell) to suppress all prompts; this\nleaves the file association untouched. Add\n`--associate-md-file-extension` (`-AssociateMdFileExtension` in PowerShell) to\nrequest the `.md` association even in silent mode (see the\n[silent install example](#install) above).\n\nBoth installers make `mdv` usable in the **same shell**, with no restart or\nmanual `source` needed:\n\n- The PowerShell installer, when run with the `irm … | iex` one-liner, executes\n  in your current session and prepends the install directory to `$env:PATH`\n  immediately.\n- The POSIX installer prefers a directory that is already on your `PATH`, so the\n  binary is found right away. If it has to fall back to `~/.local/bin`, it\n  updates your profile for future shells and prints the one-line `export` to\n  enable it in the current one (or run it sourced - `. install.sh` - to have the\n  `PATH` update applied directly to your shell).\n\n## Usage Examples\n\n```sh\nmdv README.md            # open a single document\nmdv ./docs               # open a folder (sidebar lists all markdown files)\nmdv README.md --remote --sidepanel --ignore \"*,\\!/README.md,\\!docs/\" # open \n  # document, show sidebar with related markdown files, but only within docs/\nmdv --tui README.md      # force the terminal UI\nmdv --console README.md  # render to stdout and exit\ncat README.md | mdv --console # render Markdown piped on stdin (see note below)\nmdv --pdf out.pdf README.md   # render to a PDF and exit (headless-friendly)\nmdv --pdf out.pdf --force README.md  # overwrite an existing PDF without asking\nmdv --pdf out.pdf --remote README.md # allow downloading remote images/assets\nmdv --version            # show current SemVer version number\nmdv --init-config        # write a default settings.jsonc\n```\n\n| Flag              | Description                                              |\n| ------------ | ------------------------------------------------------------- |\n| `--tui`           | Force the interactive terminal UI                        |\n| `--gui`           | Force the graphical UI                                   |\n| `--console`, `-c` | Render to stdout and exit                                |\n| `--pdf PATH`      | Render the input to a PDF at PATH (file or folder) and exit; see [PDF export](#pdf-export) |\n| `--force`         | With `--pdf`, overwrite an existing output file without prompting |\n| `--remote`        | With `--pdf`, allow downloading remote (http/https) images/assets (blocked by default) |\n| `--no-color`      | Disable ANSI colors in console output                    |\n| `--max-width N`   | Cap the render width to N columns                        |\n| `--images MODE`   | Image rendering: `auto`, `graphics`, `blocks`, `off`     |\n| `--sidepanel`     | Force the document navigator panel to start visible (GUI and TUI) |\n| `--version`       | Print version and exit                                   |\n| `--init-config`   | Write a default settings file and exit                   |\n\n\u003e [!NOTE]\n\u003e **On Windows the prompt returns before mdv prints.** The shipped `mdv.exe`\n\u003e is a GUI-subsystem binary, which is exactly what lets you double-click a\n\u003e `.md` file in Explorer without a console window ever flashing up. A small,\n\u003e harmless side effect of that feature: Windows command-line shells\n\u003e (PowerShell and `cmd.exe`) do not wait for GUI-subsystem programs, so after a\n\u003e run such as `mdv --console README.md` you get a fresh prompt immediately and\n\u003e mdv's output is drawn just after it. The command finished successfully -\n\u003e press \u003ckbd\u003eEnter\u003c/kbd\u003e for a clean prompt if the redraw looks untidy. This\n\u003e never affects double-clicking, the GUI, or the TUI.\n\n### Piping Markdown on stdin\n\nmdv reads Markdown piped on stdin, so you can render the output of another\ncommand without a temporary file:\n\n```sh\ncat README.md | mdv --console            # macOS / Linux\ntype README.md | mdv --console           # Windows (cmd.exe)\n```\n\n\u003e [!NOTE]\n\u003e **Windows PowerShell only:** the shipped `mdv.exe` is a GUI-subsystem binary\n\u003e (so double-clicking a `.md` file in Explorer never flashes a console window).\n\u003e As a side effect, PowerShell does not wait for or capture its stdout unless a\n\u003e downstream command consumes the pipeline, so a bare\n\u003e `type README.md | mdv --console` prints nothing. Pipe the result through any\n\u003e consumer to force PowerShell to drain the output - `Out-String` (then\n\u003e `Write-Host`) preserves the rendering:\n\u003e\n\u003e ```powershell\n\u003e Get-Content README.md | mdv --console | Out-String | Write-Host\n\u003e ```\n\u003e\n\u003e This affects only piped stdin in PowerShell. `cmd.exe`, PowerShell on\n\u003e macOS/Linux, passing a file path (`mdv --console README.md`), and redirecting\n\u003e to a file in `cmd.exe` all work without the extra step.\n\n### Open Markdown files from File Manager\n\nmdv can register itself as a handler for `.md` files so you can open Markdown by\ndouble-clicking it in your file manager - the document opens in the mdv GUI. The\ninstaller script sets this up for you when you opt in (it asks during an interactive\ninstall, or pass `--associate-md-file-extension` / `-AssociateMdFileExtension`,\nor set `MDV_ASSOCIATE_MD=1`). Either way, the plain `mdv` command stays the way\nto use mdv from the terminal.\n\nTwo platform specifics are worth knowing:\n\n- **macOS** - Finder's **Open With** only lists application bundles, so the\n  release archive also ships a small **`mdv.app`** wrapper that forwards opened\n  files to `mdv --gui`. The installer copies it into `/Applications`, registers\n  it, and sets it as the default handler using\n  [`duti`](https://github.com/moretension/duti) (installing `duti` via Homebrew\n  first if needed). To set the default by hand, either pick `mdv` via a `.md`\n  file's **Get Info → Open with → Change All…**, or run\n  `duti -s de.thomas-gossler.apps.mdv net.daringfireball.markdown all`. The\n  wrapper runs as a background agent, so only the mdv GUI icon appears in the\n  Dock.\n- **Linux** - automatic `.md` association is **not** performed; set up your\n  desktop environment's file association manually if you want it.\n\nOn **Windows** the installer registers a per-user handler (no admin rights\nneeded), so association works out of the box once you opt in.\n\n### Document content search\n\nThe document navigator can search inside your documents, not just filter by\nfilename:\n\n- **GUI** - click the **⌕** toggle next to the navigator filter box. When\n  enabled, each matching document is shown with its matching lines nested\n  beneath it; click a match to open the document and jump straight to that line,\n  highlighted like in-document search. Toggle it off again to return to plain\n  filename/title filtering.\n- **TUI** - press `Tab` (or `Ctrl+B`) to open the document navigator. It lists\n  every markdown file in the folder even when you opened a single file. In the\n  list, press `/` to filter by name, or type `//` to switch to content search.\n  Matches appear indented under each document; press Enter on a match to open the\n  document and jump to it. Press `Esc` (or `Ctrl+B`) to hide the navigator again.\n  You can also start content search straight from the content view: press `/` to\n  search the current page, or type `/` again (`//`) to search across all\n  documents.\n\nSearch is case-insensitive and treats your query as a smart **fuzzy phrase**:\nthe words must appear in order and close together - but minor differences are\ntolerated, so \"client approvals\" also matches \"Client-side Approvals\". It also\nforgives small typos (edit-distance matching) and matches a query word inside a\nlonger word (\"approval\" finds \"approvals\"). Content search even spans a single\nline break, so a multi-word term that is hard-wrapped across two source lines\n(e.g. at ~80 columns) is still found, while words separated by a blank line are\ntreated as different paragraphs and not matched together.\n\nThe same smart matching powers the navigator's **filename/title filter** (when\ncontent search is off), so filtering documents by name behaves just like\nsearching their content. Only documents with a filename or content match remain\nin the list. The search runs entirely in-memory with no external dependencies.\n\n## PDF export\n\nmdv can export a document to PDF from both the GUI and the command line. To keep\nbehaviour predictable, both surfaces prefer the highest-fidelity engine that is\navailable and silently fall back to a self-contained engine otherwise. **A4\nportrait** is always used.\n\n- **GUI** - click the **PDF** button in the toolbar. mdv asks where to save the\n  file and opens the result in your OS default PDF viewer.\n- **CLI** - `mdv --pdf \u003cpath\u003e \u003cfile\u003e` renders and exits without opening a window,\n  so it works over SSH, in CI, and inside containers. `\u003cpath\u003e` may be a file\n  (`out.pdf`, or any name - `.pdf` is appended if missing) or an existing\n  directory / trailing-slash path (the PDF is named after the source document).\n  The input may be a Markdown file or piped on stdin (`cat doc.md | mdv --pdf out.pdf`);\n  a folder as input is not supported for `--pdf`. The output path is printed on\n  success, along with the engine that was used.\n\nIf the output file already exists, mdv asks for confirmation before overwriting\nit (when run interactively); pass `--force` to overwrite without prompting. With\nnon-interactive input (e.g. piped stdin) mdv refuses to overwrite unless\n`--force` is given. When the offline goldmark-pdf engine has to drop content it\ncannot render (HTML tags outside code blocks, or remote/SVG/WebP images), it\nprints a `warning:` line to stderr describing what was skipped.\n\n### Remote images and assets\n\nFor safety, PDF export **never loads anything from a remote location by\ndefault** - in every engine and on every surface. A document that references\nremote (`http`/`https`) images or assets is rendered with those resources\nblocked, so generating a PDF cannot trigger network requests or leak that a file\nwas opened. To opt in:\n\n- **CLI** - pass `--remote` alongside `--pdf` to allow remote images/assets to be\n  downloaded and embedded.\n- **GUI** - turn on the remote-images toggle in the toolbar (the same one that\n  controls remote images in the live view). PDF export then mirrors what you see\n  on screen. The toggle is off after every restart.\n\n### Engines and what to expect\n\nWhich engine renders the PDF depends on the surface and on whether a browser is\ninstalled, so the result can differ. This is by design - the fallbacks let PDF\nexport work everywhere, including fully offline and headless - but it is worth\nknowing the trade-offs:\n\n| Surface | Tier 1 (preferred)            | Tier 2 (fallback)               | Fallback used when… |\n| ----- | ------------------ | ------------------- | ------------------------- |\n| GUI     | Installed browser, printToPDF | html2pdf.js (in the webview)    | no browser found / release bundle absent |\n| CLI     | Installed browser, printToPDF | goldmark-pdf (pure Go, offline) | no browser found / release bundle absent |\n\n| Engine             | Fidelity | Selectable text  | Needs a browser  | Notes |\n| --- | ---------------- | --- | ----- | ------------------------------------ |\n| Browser printToPDF | Highest - matches the on-screen render (Mermaid, KaTeX math, syntax highlighting, fonts) | Yes | Yes (Chrome/Chromium/Edge) | Uses a browser already installed on the machine; mdv never downloads one. Set `MDV_CHROME` (or `CHROME_BIN`) to point at a specific binary. |\n| html2pdf.js        | High - rasterised snapshot of the rendered page | No (image) | No | GUI-only fallback; diagrams and math look right but the text is an image, and very long pages may break across pages imperfectly. |\n| goldmark-pdf       | Basic - clean text layout with inbuilt PDF fonts | Yes | No | CLI-only fallback; works fully offline with a 1 cm page margin. HTML tags outside code blocks are stripped (HTML inside code blocks is kept verbatim); Mermaid and KaTeX are **not** rendered as graphics; SVG and WebP are always skipped, and remote images are skipped unless `--remote` is given (even then this offline engine embeds them only on a best-effort basis - it reliably embeds only local PNG/JPEG/GIF). Dropped content is reported as a stderr warning. Limited Unicode coverage. |\n\nNotes:\n\n- The **browser** path is only compiled into official release builds (it embeds\n  a small print harness). Plain `go build` / `go run` builds therefore use the\n  Tier 2 fallback even when a browser is installed; build with\n  `-tags pdf_bundled` after staging the frontend to enable it locally.\n- Browser detection looks for Chrome, Chromium, Microsoft Edge and Brave in the\n  usual per-OS locations. The browser runs headless with `--no-sandbox` so it\n  also works as root inside containers.\n- By default no PDF engine reaches out to the network: the browser path loads\n  only local content from a temporary loopback server and blocks every remote\n  request, while the goldmark fallback embeds only local images. Pass `--remote`\n  (CLI) or enable the GUI remote-images toggle to allow remote downloads.\n\n## Features\n\n- GitHub Flavored Markdown (tables, task lists, strikethrough, autolinks)\n- GitHub alerts (`\u003e [!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, `[!CAUTION]`)\n- Extended inline syntax (opt-in): math via KaTeX (`$inline$`, `$$block$$`,\n  ` ```math ` blocks), subscript `~x~`, superscript `^x^`, highlight `==x==`,\n  inserted `++x++` - off by default; see [Extended syntax](#extended-syntax)\n- Mermaid diagrams (theme-aware)\n- Syntax highlighting with 6 themes (Glyph, GitHub, Monokai, Nord, Solarized Light/Dark)\n- Inline images in the console and terminal UI\n- Wikilinks `[[doc]]`, `[[doc|alias]]`, `[[doc#heading]]` with a backlinks panel\n- Smart link resolution: case-insensitive, directory-index fallback, symlink-aware\n- Table-of-contents sidebar with scroll-spy, heading anchors\n- CSV/TSV fenced blocks rendered as tables\n- YAML frontmatter metadata block, emoji shortcodes\n- Azure DevOps constructs (`[[_TOC_]]`, `:::video:::`, `#123` work items, image sizing `![alt](img =800x600)`)\n- Sanitized inline HTML (DOMPurify)\n- In-document search (Cmd/Ctrl+F), toggleable live reload, drag-and-drop\n- Document content search in the navigator (smart fuzzy-phrase matching)\n- Entry-point highlighting in the document navigator (e.g. README)\n- Export to PDF from the GUI and CLI, headless-friendly; see [PDF export](#pdf-export)\n- Read-only \"View raw Markdown\" toggle in the GUI\n- Recently opened files and folders list in the GUI\n- Open the current document in your associated external app\n- OS file-manager integration: \"Open with mdv\" context-menu entry and `.md` association\n- Zoom (Cmd/Ctrl + wheel / +/-), light/dark/system themes, configurable fonts\n- History navigation, link target preview in the status bar\n- \"Open in new window\"\n- Automatic update checks\n\n## Configuration\n\n`mdv` works with zero configuration. To customize, create\n`~/.config/mdv/settings.jsonc` (or run `mdv --init-config`). The file is JSONC\n(JSON with comments and trailing commas) and is merged over the built-in\ndefaults. On Windows/macOS the location follows `XDG_CONFIG_HOME` if set.\n\n```jsonc\n{\n  // \"system\" | \"light\" | \"dark\"\n  \"theme\": \"system\",\n  \"codeTheme\": \"github\",\n  \"fontFamily\": \"\",\n  \"fontSizePx\": 16,\n  \"lineHeight\": 1.6,\n  \"contentWidthPx\": 860,\n  \"navLabelMode\": \"filename\", // or \"title\"\n  \"liveReload\": false, // initial state of the active-document auto-reload toggle\n  \"enableExtendedSyntax\": false, // math, sub/sup, highlight, inserted (GUI only)\n  \"checkForUpdates\": true,\n  \"images\": \"auto\", // \"auto\" | \"graphics\" | \"blocks\" | \"off\"\n  \"imagesRemote\": true, // fetch http(s) images in console/TUI (falls back to alt text)\n}\n```\n\n### Extended syntax\n\nA handful of inline extensions reuse characters that appear in ordinary prose,\nso enabling them globally can silently misrender plain text - for example `$5\nto $10` becoming math, or `~note~` becoming a subscript. These are therefore\n**off by default** and grouped behind a single \"extended syntax\" toggle:\n\n- Math via KaTeX: `$inline$`, `$$block$$`, and ` ```math ` fenced blocks\n- Subscript `~x~`, superscript `^x^`\n- Highlight `==x==`, inserted `++x++`\n\nAll other constructs (tables, alerts, wikilinks, footnotes, emoji, CSV blocks,\nAzure DevOps syntax, etc.) use distinctive delimiters and stay on at all times.\n\nEnable extended syntax in one of three ways:\n\n- Set `\"enableExtendedSyntax\": true` in `settings.jsonc` (default for new windows)\n- In the GUI, click the **∑** toolbar button to toggle it live\n- In the terminal UI, press **`x`** to toggle it\n\nThe runtime choice is remembered in `~/.config/mdv/state.jsonc` and shared\nbetween the GUI and TUI. Note that the terminal renderer cannot display these\nconstructs, so the TUI toggle only updates the shared preference for the GUI;\nthe terminal output is unchanged.\n\n## Building from source\n\nRequires Go 1.26+, Node.js 18+, and the [Wails v3](https://v3alpha.wails.io/)\nCLI (`go install github.com/wailsapp/wails/v3/cmd/wails3@latest`).\n\n```sh\nscripts/build.sh          # macOS/Linux -\u003e build/mdv\npwsh scripts/build.ps1    # Windows     -\u003e build/mdv.exe\n```\n\nThe script builds the frontend, compiles the GUI helper, compresses and embeds\nit into the launcher, and produces a single self-contained executable. On macOS\nthe result is a universal (arm64 + amd64) binary, and the script additionally\nproduces `build/mdv.app` (the Finder wrapper described under\n[Open Markdown files from File Manager](#open-markdown-files-from-file-manager)).\n\n### Architecture\n\n```\ncmd/mdv             pure-Go launcher (no webview linkage) - picks GUI/TUI/console\ninternal/core       shared logic: config, links, slugs, backlinks, updates\ninternal/console    glamour-based stdout rendering\ninternal/tui        Bubble Tea terminal UI\ninternal/launcher   environment detection + embedded GUI extraction/spawn\ngui/                Wails v3 GUI helper (Go bridge + TypeScript frontend)\n```\n\nThe launcher embeds the GUI helper (gzip-compressed) and extracts it to a\nper-version cache directory on first GUI launch, then runs it detached. Because\nthe launcher itself links no native UI libraries, it starts cleanly in any\nenvironment and degrades gracefully to TUI or console.\n\n### Running the tests\n\nThe Go test suite covers both unit logic (config parsing, link/wikilink\nresolution, slugging, backlinks, folder listing, version comparison) and\nend-to-end CLI behavior (the built binary's `--version`, `--console`,\n`--init-config`, and no-arg usage paths). It is the automated quality gate for\nevery pull request.\n\n```sh\ngo test ./...                 # unit + end-to-end tests\ngo test -short ./...          # skip the slower e2e build test\ngo test -race -coverprofile=coverage.out ./...   # what CI runs\ngo tool cover -html=coverage.out                 # browse coverage\n```\n\nIn VS Code, press \u003ckbd\u003eCmd/Ctrl\u003c/kbd\u003e+\u003ckbd\u003eShift\u003c/kbd\u003e+\u003ckbd\u003eP\u003c/kbd\u003e →\n**Tasks: Run Test Task**, or pick any of the `test*` / `coverage report` tasks\nfrom the Command Palette.\n\n## Contributing\n\nPull requests are warmly welcome - whether it's a one-character typo fix or a\nwhole new rendering feature. `mdv` is a small codebase on purpose, so it's an\napproachable place to make your first open-source contribution. 🌱\n\n**The quick path:**\n\n1. **Fork** the repo and create a branch: `git checkout -b feature/amazing-thing`.\n2. **Hack** away. Keep the launcher webview-free - that headless-safety guarantee\n   is the whole point of the project, so anything touching native UI belongs in\n   `gui/`, never in `cmd/mdv` or `internal/launcher`.\n3. **Test** your change: `go test ./...` must stay green, and please add a test\n   for anything you fix or add. The CI quality gate runs `go vet`, `gofmt`, the\n   race detector, and the full suite - running them locally first saves a round\n   trip.\n4. **Format**: `gofmt -w .` for Go and `npx tsc --noEmit` in `gui/frontend` for\n   the TypeScript side.\n5. **Open a PR** with a clear description of the _why_, not just the _what_.\n   Screenshots or a short clip for UI changes earn you bonus goodwill. ✨\n\n**Good first issues:** new syntax-highlight themes, additional markdown\nextensions, emoji shortcode coverage, TUI keybindings, and documentation polish\nare all great starting points. Look for the\n[`good first issue`](https://github.com/thgossler/mdv/labels/good%20first%20issue)\nlabel.\n\n**House rules:** be kind, assume good intent, and remember there's a human on\nthe other side of every review. By participating you agree to uphold a\nwelcoming, harassment-free environment for everyone.\n\n## Sponsor\n\n`mdv` is free, MIT-licensed, and built in spare evenings fueled by curiosity\n(and a non-trivial amount of coffee ☕). If it saves you a few clicks every day,\nconsider giving a little back:\n\n- ⭐ **Star the repo** - it's free, it takes two seconds, and it genuinely helps\n  others discover the project.\n- 💬 **Spread the word** - blog about it, tell a colleague, or drop it in your\n  team's tooling channel.\n- 🐛 **Report bugs and ideas** - high-signal issues are worth their weight in gold.\n- 💖 **Back it financially** via \n  - [GitHub Sponsors](https://github.com/sponsors/thgossler) or \n  - [PayPal](https://www.paypal.com/donate/?hosted_button_id=JVG7PFJ8DMW7J).\n  \n  - every tier, down to \"buy the maintainer a coffee,\" keeps the lights on and\n  the commits coming.\n\nSponsorships directly fund maintenance time, code-signing certificates, and the\noccasional release-day pizza. Thank you for keeping independent open source\nalive. 🙏\n\n## License\n\nReleased under the [MIT License](LICENSE.md) - do what you like, just keep the\ncopyright notice. \n\nCopyright © 2026 Thomas Gossler\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthgossler%2Fmdv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthgossler%2Fmdv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthgossler%2Fmdv/lists"}