DEV Community

Arthur
Arthur

Posted on

I got tired of reading Claude Code's .md files in VS Code, so I built a native Mac viewer

Disclosure: I'm the developer of MacMD Viewer. Honest maker story — not a sponsored post.


Something changed when I started using Claude Code daily.

It drops markdown files everywhere. CLAUDE.md, plan.md, architecture notes after every session, progress summaries I didn't ask for. By the end of a sprint I'd have five .md files I hadn't actually read properly.

My workflow was Cmd+Shift+V into a VS Code preview pane. Which works. But I was always reading inside a code editor. The preview pane is half the screen. The file tree is still there. The terminal's still there. There's this constant friction between "reading mode" and "dev mode" that I couldn't shake.

I looked for a native Mac app that was specifically a viewer — read-only, one click to open, renders Mermaid, handles QuickLook properly. Couldn't find one I was happy with.

So I built one.


What I tried first (and why it didn't stick)

VS Code preview — already using it, already frustrated with it for long documents. Also: you can't just drag a .md onto it. You open VS Code, open the file, Cmd+Shift+V. Five steps to read one file. Feels wrong.

Typora — popular, but it's an editor. It opens in edit mode. I don't want to accidentally overwrite the plan Claude Code just wrote for me.

Obsidian — genuinely great app if you want a second brain. For opening a single file it's like using Photoshop to crop a photo. It also creates an .obsidian folder wherever you point it, which is fine in your notes directory and annoying everywhere else.

Quick Look (stock) — renders .md as plain text. Useless.

None of these were wrong tools. They were tools built for different jobs.


Building it — the two things that actually surprised me

I'm a Mac dev, I know Swift, I had two months of evenings. The architecture is straightforward: SwiftUI + WKWebView, markdown renders to HTML via highlight.js and mermaid.min.js, DocumentGroup for the native open-file model.

Two things caught me off-guard.

Swift 6 strict concurrency. I thought I was mostly there on actor isolation. I was not. Every function that touches WKWebView needs @MainActor. Not "most of them." All of them. I spent an embarrassing amount of time chasing "Sending 'coordinator' risks causing data races" warnings before I stopped fighting it and just marked the entire Coordinator class @MainActor. Then it worked. Swift 6 is right about this, it's just not gentle about teaching it.

The scroll spy. I wanted the sidebar to highlight the active heading as you scroll — one of those features that sounds simple until you actually try it. My solution: an IntersectionObserver injected into the WKWebView watches all headings; when one enters the viewport it fires window.webkit.messageHandlers.headingObserver.postMessage(id) back to Swift, and a WKScriptMessageHandler picks it up to update the UI. Works great.

The gotcha: I was calling MarkdownRenderer.render() in the SwiftUI view body to regenerate the HTML on each update. That function generates a fresh UUID nonce for the CSP on every call, which makes WKWebView treat it as a new document and reload from scroll position zero. Every. Time. The fix was caching the rendered HTML in @State and only regenerating it when the content actually changes. A weekend well spent, I guess.


The QuickLook part — this is the actual feature

MacMD ships a QuickLook extension alongside the main app.

Hit Space on any .md file in Finder. Rendered markdown. Syntax highlighting. Themes. Mermaid diagrams if they're in there. Not raw text. Not a wall of hashtags.

For my workflow this is the thing. Claude Code drops plan.md in my project. I'm in Finder. I hit Space. I read the plan. I close the preview. No app switch, no editor opening, no Cmd+Shift+V.


Honest specs (because I've seen some wild claims in this space)

  • highlight.js covers around 34 languages out of the box — I verified this in the source. Not 180+. Some tools claim ridiculous numbers.
  • No LaTeX / KaTeX support yet. It's on the list.
  • ~10 MB install size. Not 2 MB either.
  • File watching works: when Claude Code updates the file you're reading, MacMD auto-reloads.

Where it is now

macmdviewer.com — $19.99, one-time, no subscription. Also on Homebrew (brew install --cask macmd-viewer) and Setapp.

I built it for my own workflow. Turns out the Claude Code / Codex / Cursor crowd has the same problem — AI agents write .md to disk, and macOS has no good native way to read them. That's the use case I'm focused on.

If you're on Mac and this sounds familiar, give it a try. If something doesn't work the way you'd expect, I respond to everything — I'm the only developer.

Top comments (1)

Collapse
 
fromzerotoship profile image
FromZeroToShip

The "reading mode vs dev mode" friction is so real that I ended up doing the same thing you did — I built my own markdown reader. I'm not even a developer (physical therapist who builds internal tools for a hospital with AI), but my entire workflow lives in Claude-generated .md files: plans, audit reports, handoff notes. I read far more markdown than I write, and every tool I opened assumed the opposite — cursor blinking, edit mode armed, one stray keystroke away from corrupting a report.

So I had AI help me build a small read-only web viewer: drop in a file, get a clean rendered page, nothing editable. Crude next to what you've made, but the day it existed, reading stopped feeling like defusing a bomb.

That's why I love the shape of your solution: you didn't build an editor with a preview, you built a reader. Same reason people read books on a Kindle and not in Word. I'm on Windows so I can't use MacMD, but respect for shipping it native with the Space-bar QuickLook moment — that's the whole pitch — and for publishing the limitations right in the launch post. "Honest about what it doesn't do" is rarer than it should be.