Mark up your markdown.
Annotations, highlights, and rich overlays on plain .md files —
without breaking the format underneath.
Markdown was designed to strip markup away. markdown-md adds it back — annotations, highlights, comments, structure — as a rich layer on top of files that stay plain text on disk. Open them anywhere, mark them up here.
your-file.md (plain text, never modified by annotations)
|
Tiptap / ProseMirror
(rich editor, markdown round-trip)
|
Markup Layer [coming]
(annotations, highlights, comments — stored separately)
|
Electron + React
(native file I/O, dark UI, autosave)
Open a .md file. Edit it with a rich editor. Mark it up with annotations that live in a separate overlay — the markdown underneath stays portable and unchanged.
git clone https://github.com/jj-valentine/markdown-md.git && cd markdown-md
npm install
npm run devAn Electron window opens with a dark-themed editor. Start typing, or open an existing .md file with Cmd+O.
Markdown is everywhere — docs, notes, READMEs, knowledge bases. But it's deliberately minimal. No annotations. No highlights. No way to layer meaning on top of a document without changing the document itself.
markdown-md is a structured overlay editor. Open any .md file, mark it up with annotations, highlights, comments, and structural cues — and the markdown underneath stays untouched. Your .md files remain portable, diffable, and readable in any tool. The markup layer lives separately.
Think of it as the difference between writing in a notebook and writing on a notebook — sticky notes, margin scribbles, highlighted passages — without altering a single page.
The editor foundation is in place. Markup features are coming next.
Editor core
- WYSIWYG editing backed by Tiptap 3 (ProseMirror) with full markdown round-tripping
- Headings, bold, italic, links, images, code blocks, tables, task lists, blockquotes
- Syntax highlighting in fenced code blocks (lowlight)
- Heading promote/demote with Cmd+= / Cmd+- and animated level badge
File I/O
- Native file dialogs for open/save/save-as (
.md,.markdown,.mdx,.txt) - IPC path allowlist — only files from native dialogs can be written
- Autosave to localStorage (1s debounce), restored on relaunch
- Close guard with save/discard/cancel dialog, error recovery on save failure
Dual backend
- Desktop: Electron IPC + Node
fs - Web: browser download/upload fallback
- Editor component has zero Electron imports — embeddable anywhere
| Shortcut | Action |
|---|---|
| Cmd+O | Open file |
| Cmd+S | Save |
| Cmd+Shift+S | Save As |
| Cmd+= | Promote heading (H3 → H2) |
| Cmd+- | Demote heading (H2 → H3) |
| Cmd+Z / Cmd+Shift+Z | Undo / Redo |
| Cmd+B / Cmd+I | Bold / Italic |
Zoom is disabled (Cmd+=/- are reserved for heading shortcuts).
markdown-md/
├── electron/
│ ├── main.ts # Window, menus, close guard, heading shortcuts
│ ├── preload.ts # contextBridge API (sandboxed)
│ └── ipc-handlers.ts # file:open, file:save, file:save-as
│
├── src/
│ ├── components/
│ │ ├── Editor/
│ │ │ ├── TiptapEditor.tsx # Tiptap instance, markdown round-trip
│ │ │ └── MarkdownEditor.tsx # Orchestrator: file I/O, autosave, store
│ │ ├── Layout/AppShell.tsx # Grid shell (topbar, sidebar, editor, status)
│ │ └── StatusBar/StatusBar.tsx # File name, word count, encoding
│ ├── hooks/useFileIO.ts # Open/save/saveAs + menu event wiring
│ ├── stores/editor-store.ts # Zustand: fileName, isDirty, wordCount
│ ├── lib/
│ │ ├── file-io.ts # Dual backend adapter
│ │ └── autosave.ts # localStorage autosave (1s debounce)
│ ├── types/electron.d.ts # Window.api type declarations
│ └── styles/
│ ├── tokens.css # Design tokens (dark palette)
│ ├── reset.css # Box model reset, body defaults
│ ├── layout.css # AppShell grid, topbar, sidebar
│ └── editor-content.css # ProseMirror content styling
│
├── electron.vite.config.ts # Build config (main, preload, renderer)
└── package.json
Renderer Main Process Disk
| | |
|--- invoke('file:save') --->| |
| |--- writeFile() ------->|
| |<-- ok ----------------|
|<-- { filePath } ----------| |
| | |
|--- send('save-complete') ->| |
| |-- win.destroy() --> |
Save operations go through IPC with a path allowlist. The renderer never touches the filesystem directly.
User closes window
|
Main: e.preventDefault()
Main: send('query:is-dirty')
|
Renderer: reply:is-dirty(true)
|
Main: showMessageBoxSync
|
+-- Save --> menu:save --> save-complete --> win.destroy()
+-- Don't --> win.destroy()
+-- Cancel --> (nothing)
+-- Failure --> save-failed --> error dialog, window stays
+-- No response (crash) --> second close forces quit
Dark indigo/violet palette. No CSS framework — vanilla CSS with custom properties.
| Token | Value | Usage |
|---|---|---|
--surface |
#0e0e0e |
Main background |
--surface-container |
#1a1919 |
Elevated surfaces |
--primary |
#a3a6ff |
Links, active states |
--primary-dim |
#6063ee |
Blockquote borders |
--tertiary |
#ffa5d9 |
Inline code |
--on-surface |
#ffffff |
Primary text |
--on-surface-variant |
#adaaaa |
Secondary text |
--outline-variant |
#494847 |
Borders |
Typography: Inter for body, JetBrains Mono for code. Both loaded locally.
| Layer | Tech |
|---|---|
| Runtime | Electron 41 |
| Editor | Tiptap 3 (ProseMirror) |
| Markdown | @tiptap/markdown |
| UI | React 19 |
| State | Zustand 5 |
| Icons | Lucide |
| Syntax | lowlight (highlight.js) |
| Build | electron-vite 5 + Vite 7 |
| Language | TypeScript 5.9, vanilla CSS |
npm run dev # Start dev server (hot-reload renderer)
npm run build # Production build → out/
npm run preview # Preview production build
npm run lint # ESLintThe MarkdownEditor component is designed for embedding into other React apps. It has no Electron imports — all platform integration flows through props and the window.api bridge, which gracefully degrades when absent.
import MarkdownEditor from './components/Editor/MarkdownEditor'
// Works standalone — no Electron required
<MarkdownEditor />This is the integration point for CEREBRO's Global Files & Editor section.
Markup layer (the point of all this)
- Inline annotations — attach notes to any span of text
- Highlights with categories (question, important, revisit)
- Margin comments — visible alongside content, stored separately
- Annotation persistence — overlay data stored alongside
.mdfiles without modifying them
Editor
- Toolbar with full formatting controls
- Find and replace
- File tree sidebar
- Tabs for multiple open files
Export & theming
- Export to HTML / PDF (with or without annotations)
- Custom themes
- Plugin system
MIT