DEV Community

Cover image for I Built a Markdown Editor in a Weekend Because Every Other One Annoyed Me
Alexander van Rossum
Alexander van Rossum

Posted on • Originally published at mipyip.com

I Built a Markdown Editor in a Weekend Because Every Other One Annoyed Me

I didn't plan to build a markdown editor this weekend. I was working on something else, and somewhere in the middle of it I opened my markdown editor to take notes and my annoyance with every markdown editor I've tried finally reached a head.

Not annoyed in the "this is broken" sense. Annoyed in the "why does this app need a cloud account and fourteen features I'll never use" sense. Every alternative I'd tried had the same problem in different packaging — too expensive, too bloated, or too clever.

So I opened Claude Code and started building one.

What I built

Three panes. File browser on the left, editor in the middle, live preview on the right. Tabs for multiple open files. Session restore — close the app, reopen it, everything's still there. Dark mode. Search and replace. A formatting toolbar for the things I always forget the syntax for.

That's it. No cloud sync, no collaboration, no plugin architecture; just markdown files on my computer, edited in a clean interface.

Simple Markdown Editor — three-pane layout with file browser, editor, and live preview

The stack: Electron 33, React 18, CodeMirror 6, marked for GFM rendering, Vite 6 for the build, electron-builder for packaging.

Why the first version was usable in under an hour

Not because AI is magic. Because the AI had context.

I use a governance-first development workflow: before any code gets written, the AI agent has access to persistent architecture documents, wireframes, a personal coding style guide, personal design guidelines, and detailed specifications. These files survive across sessions and context window compaction. The prompt describes what to build. The governance documents describe how to build it, and to what standard.

That's the difference between "a thing that kind of works" and "a thing I'm actually using that same day."

Not perfect on the first pass. But functional enough that I was taking notes in it within the first hour. Then I started tweaking.

(And I wrote this post using it)

The interesting technical bits

Bidirectional scroll sync — The naive approach (percentage-based) breaks immediately when the editor and preview have different content heights. I built section-based anchor mapping instead: the editor and preview each maintain a map of heading positions, and scrolling either pane updates the corresponding pane by interpolating between anchor points. Both directions stay aligned regardless of content length differences.

Smart formatting toolbar — Each button doesn't just apply formatting — it first checks whether the cursor is already inside that formatting and toggles it off. Heading buttons cycle through H1→H2→H3→paragraph. List buttons handle multi-line selections and continue numbering from preceding items. Small details that make the toolbar feel considered rather than tacked on.

External change detection — This was a requirement of the original spec — and when it surfaced later during code review, it surprised me. Edit a file in another app while it's open in the editor, and you get a full diff view showing exactly what changed. Options: keep your version, accept the external changes, or save as a new file. No silent overwrites. I'd completely forgotten I'd even added it until I triggered it accidentally and thought oh, that's actually amazing.

External change detection — diff view showing changes made in another editor

Session restore — Open tabs, active tab, folder path, scroll positions, and window size/position all persist across app restarts. Multi-window support (Cmd+Shift+N), each window preserves its own state. Close the app, open it tomorrow — everything's exactly where you left it.

The security wake-up call

After the features were working, I ran an adversarial code review on the codebase — a separate Claude Code instance with its own repo, its own governance documents, read-only access to the target code, and zero shared context with the building agent. Its only job is to find everything wrong.

What it found was embarrassing in the best way.

The worst finding wasn't missing security — it was the ceremony of security without the substance. contextBridge, contextIsolation: true, proper cleanup functions — all present, all technically correct, and all masking a straight pipeline from a malicious .md file to arbitrary filesystem access. The sandbox: false with a wrong justification comment was the cherry on top.

It's exactly the kind of thing that survives review after review because it sounds right, and nobody actually traces the dependency to verify it.

Specific fixes:

  • XSS prevention — DOMPurify sanitizes all markdown before rendering in the preview pane
  • Sandbox enabled — Chromium sandbox and context isolation enforced on all windows
  • Filesystem access control — path validation limits access to home directory and /Volumes; sensitive directories (.ssh, .gnupg, .aws) blocked
  • Path traversal protectionlocal-resource:// protocol restricted to image file extensions
  • Content Security Policy — tightened CSP on settings and update dialogs
  • URL scheme allowlistingshell.openExternal limited to https://, http://, mailto:

Twenty-plus security fixes across eleven versions. This is the part that concerns me about the current wave of AI-generated code shipping without independent review — technically functional apps with exploitable security models, because the same agent that writes the code is also the only one evaluating the code.

Thirty-one versions in two days

v0.1.0 to v0.1.31 in a weekend, and not because I was rushing — because the governance-first pattern means each feature lands cleanly, gets tested, gets committed, and the next one starts from solid ground.

The app is signed and notarized with Apple, auto-updates from GitHub Releases, handles file associations (shows up in Finder's "Open With" menu for .md, .markdown, .mdx, .txt files), and restores all windows with their tabs and folder paths on relaunch.

What it deliberately doesn't do

No cloud sync. No collaboration. No Vim mode. No WYSIWYG. No plugin system. No account creation. No subscription. No telemetry.

Every markdown editor eventually tries to become a knowledge management platform. This one won't. The filesystem is the organizational layer. Git is the version control. Markdown is the format — portable, readable, owned by you. The editor just makes working with those files fast and pleasant.

Your files are plain markdown on disk. Open them with anything, anywhere, forever.

Try it

It's still beta - v0.1.x - but it's a functional beta. Are there problems? Probably, and I'll find them while dogfooding. But it's satisfying my use case, and that's good enough for now.

The code is on GitHub — MIT licensed, macOS only. Grab the .dmg from Releases. Signed and notarized, no Gatekeeper warnings. macOS 12+ required, Apple Silicon supported.

If you live in markdown and every editor you've tried wants to be something it shouldn't be — this one doesn't.

Top comments (0)