Markdown reader

Read markdown like it's print.

A desktop reader for local folders, GitHub repos, and any markdown URL — rendered through a sanitized Rust pipeline. Offline-first. Data stays on your machine.

Vidi /ˈwiː.diː/ · Latin, first-person perfect of vidēre, “to see.” “I saw.” The viewing half of Caesar's veni, vidi, vici.

Vidi rendering The Colophon Tribune — a newspaper-style markdown sample with masthead, standfirst, byline, and pull-quote
Vidi rendering The Colophon Tribune, one of the bundled sample documents. Serif body, ragged-right, hyphenated, with a centered pull-quote.
Download latest release →

Install

Grab the build for your platform from GitHub Releases.

macOS .dmg — Apple Silicon and Intel
Windows .msi installer or .exe (NSIS)
Linux .deb or .AppImage
First-run on macOS: builds are unsigned. Right-click the app and choose Open the first time to bypass Gatekeeper; macOS remembers the decision afterwards.

Quick start

  1. Launch Vidi.
  2. Press ⌘O to open a folder of markdown files, or paste a URL like github.com/owner/repo/blob/main/README.md into the address bar and press Open.
  3. Use the left sidebar to switch files or jump to a section. Use ⌘F to find within the current doc and ⌘⇧F to search across the whole folder.
  4. Press ⌘T for a new tab — each tab has its own folder, document, and reading position.

Bundled demo content

The Vidi repository ships a samples/ directory of fictional documentation that exercises every rendering feature — GFM tables, task lists, alerts, footnotes, math, Mermaid diagrams, syntax-highlighted code across several languages, nested folders, relative image paths, raw-HTML images, a deliberately broken image to show the fallback, and an external image to exercise the load prompt. Clone the repo and point Vidi at samples/ to tour the whole feature set in a few minutes.

Reading view

The reader is the centerpiece. Text is set in Source Serif 4 with hyphenation, hanging punctuation, and old-style numerals enabled by default — the goal is that running prose feels like a book, not a web page.

  • Adjustable column width. Hover either edge of the column and drag — both edges highlight as you move, and the column rebalances around the center line. Double-click an edge to reset to the default measure (92ch). The numeric value is also in Settings.
  • Dark mode. Follows prefers-color-scheme automatically. Toggle manually with the moon/sun control in the bottom-right corner.
  • Reading position is remembered per source. Come back to a long doc and you'll land where you left off.

Folders & the sidebar

Open a folder (⌘O) and the left sidebar fills with two collapsible sections:

  • Files — a recursive tree of every .md / .markdown file. Respects .gitignore, skips .git and node_modules. Filter input for quick lookups.
  • Contents — a live table of contents for the active document, built from heading text with stable slug anchors. Click any entry to smooth-scroll to that heading.

Each section takes half the remaining height by default. Collapse one and the other expands to fill. A file-system watcher keeps the tree in sync with disk changes and re-renders the active doc when it's saved externally.

Sidebar with Files and Contents sections visible
Files (top) and Contents (bottom). Either section can be collapsed.

Remote URLs & GitHub

Paste any of these into the address bar:

  • https://github.com/owner/repo — loads README.md from HEAD.
  • https://github.com/owner/repo/blob/main/docs/foo.md
  • https://raw.githubusercontent.com/owner/repo/main/path.md
  • https://gist.github.com/user/id or a gist raw URL
  • Any other URL whose body is markdown

GitHub URLs are normalized to their raw-content equivalents so relative links (./other.md, ../api.md) resolve correctly. External links and external images both prompt before they touch the network — decisions can be remembered per host in Settings.

Tabs

Press ⌘T to open a new tab. Each tab carries its own folder tree, active document, loading state, and reading position. Tabs appear above the address bar only when there are two or more open — a single tab keeps the chrome quiet.

  • Overflow picker. When tabs don't fit, a ⌄ N chip appears at the right end; clicking it opens a searchable list of every open tab.
  • Middle-click a tab to close it. ⌘W closes the current. ⌘⇥ cycles forward, ⌘⇧⇥ backward. ⌘1⌘9 jumps by index.
Multiple tabs open with an overflow chip
Tabs share the trusted-hosts list; everything else is per-tab.

Bookmarks & recents

⌘D adds the current document to Bookmarks. ⌘B opens the bookmarks panel. ⌘Y opens the recent-docs panel — the last 50 opened documents, with relative times.

Both lists live in a JSON file under your platform's app-data directory. Nothing leaves the machine.

Print / save as PDF

Press ⌘P to open the print dialog. The print stylesheet hides every piece of app chrome (address bar, sidebars, panels, find bar) and opens up the prose column to fill the page. On macOS, the print dialog's PDF → Save as PDF menu gives you a portable archive version for free.

External-image placeholders are omitted from print (we don't have the pixels), and external links are expanded inline with their URLs in angle brackets so the printout stays self-contained.

Supported markdown

All of GitHub-flavored markdown plus a few extras:

  • GFM coreTables, task lists, strikethrough, autolinks, footnotes.
  • Alerts> [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] rendered as semantic <aside>s.
  • MathInline $x^2$ and display $$…$$ rendered with KaTeX (lazy-loaded).
  • MermaidFenced ```mermaid blocks render as SVG (lazy-loaded, sandboxed).
  • Syntax highlightingEvery common language via syntect — inline styles, no JS.
  • Emoji shortcodes:smile: → 😄 (only outside code blocks).
  • Smart punctuationTypographic quotes, en/em dashes, ellipses where the source has them.
  • Raw HTMLAllowed through a strict sanitizer. Relative srcs on <img> tags are rewritten to asset://.

Settings

Open with ⌘,. Every setting persists to a JSON file in the app-data directory and takes effect live.

SettingWhat it does
ThemeSystem / light / dark.
MeasureColumn width cap in ch. Also adjustable by dragging the column edge.
Font scaleMultiplies the body size from 0.85× to 1.25×.
Drop capsEnables a newsprint-style drop cap on the first paragraph after h1.
Render mathToggle KaTeX globally.
Render mermaidToggle Mermaid globally.
Trusted hostsList of hosts approved for external images / links. Remove any entry to re-prompt.

Keyboard shortcuts

KeyAction
⌘OOpen folder…
⌘TNew tab
⌘WClose current tab
⌘⇥ / ⌘⇧⇥Next / previous tab
⌘1⌘9Jump to tab N
⌘\Toggle sidebar
⌘FFind in document
⌘⇧FSearch across folder
⌘BBookmarks panel
⌘DBookmark current document
⌘YRecent documents panel
⌘,Settings
⌘PPrint / save as PDF

Privacy & safety

Vidi is local-first by design. There is no account, no cloud, no analytics, no telemetry. The only outbound network requests are:

  • Fetching a markdown URL when you paste one in the address bar.
  • Loading an external image after you've confirmed it (once, or once per host).
  • Opening an external link in your OS browser after you've confirmed it.

Rendering pipeline

Every markdown document goes through the same Rust-side pipeline before any HTML reaches the webview:

  1. Parse with pulldown-cmark (GFM options enabled).
  2. Heading IDs slugify from text so anchor links work.
  3. Alerts rewriter turns > [!NOTE] into semantic <aside>.
  4. Emoji shortcodes are expanded (outside code).
  5. Math placeholders emitted for client-side KaTeX.
  6. Mermaid placeholders emitted for client-side rendering; output SVG is passed through DOMPurify.
  7. Syntect highlights fenced code blocks with inline styles.
  8. Links / images are classified (internal / external / asset / anchor) and annotated so the UI can route or prompt.
  9. Raw HTML <img src> in the source is normalized the same way.
  10. Ammonia sanitizes the final HTML with a strict allowlist: no scripts, no event handlers, no javascript:, no http: image sources (only https:, asset:, and data:image/).

Every <style> attribute is filtered down to the exact five properties syntect emits. Every data- attribute is on a named allowlist. Every URL scheme is explicit. Nothing about the sanitizer is derived from string matching in the webview.

License

Vidi is MIT-licensed — see LICENSE.

Bundled fonts: Source Serif 4 (OFL-1.1, Adobe) and JetBrains Mono (OFL-1.1, JetBrains). Full license texts ship with the app under public/fonts/*-LICENSE.txt.