Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com

docs: add WYSIWYG editor design plan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+77
+77
docs/plans/2026-03-05-wysiwyg-editor-design.md
··· 1 + # WYSIWYG Editor Design 2 + 3 + ## Overview 4 + 5 + Add a rich text (WYSIWYG) editing mode to the document editor using Milkdown. Rich text is the default. The existing CodeMirror split-pane view becomes "Source" mode. Users toggle between them with a toolbar button; the chosen mode persists in `localStorage`. 6 + 7 + ## Architecture 8 + 9 + Single edit page at `/docs/{rkey}/edit`. Both editor containers exist in the DOM simultaneously — only one is visible at a time: 10 + 11 + - `#editor-rich` — Milkdown, centered at max-width 720px, default mode 12 + - `#editor-source` — existing CodeMirror + preview split pane, "Source" mode 13 + 14 + A **"Source" toggle button** in the toolbar (using existing `btn-outline` + `active` pattern) switches modes. Choice persisted under `localStorage` key `editor-mode` (`"rich"` | `"source"`), defaulting to `"rich"`. 15 + 16 + Both editors initialize from the same hidden textarea on page load. The correct editor is shown immediately from localStorage — no flash. 17 + 18 + Autosave is shared: both modes call the same `scheduleAutoSave(markdown, title)` that POSTs to `/api/docs/{rkey}/autosave`. 19 + 20 + When in rich text mode, the Preview and Wrap toolbar buttons are hidden (they only apply to source view). 21 + 22 + ## Milkdown Setup 23 + 24 + Use `@milkdown/kit` with: 25 + - `@milkdown/kit/core` — editor core 26 + - `@milkdown/kit/preset/commonmark` — headings, bold, italic, lists, blockquotes, code blocks, links; includes bubble menu on text selection 27 + - `@milkdown/kit/plugin/listener` — content change events for autosave 28 + 29 + No toolbar, no slash commands. The bubble menu (appears on text selection) is the sole formatting UI — minimal, keyboard-shortcut-first. 30 + 31 + ## Bundle 32 + 33 + A separate `static/vendor/milkdown.js` built via esbuild, loaded only on the edit page alongside the existing `editor.js`. Keeping them separate preserves independent cache invalidation. The Dockerfile gets a second esbuild step for the Milkdown bundle. 34 + 35 + Milkdown injects its own styles via JS — no separate stylesheet needed. 36 + 37 + ## Mode Switching 38 + 39 + **Rich → Source:** 40 + 1. Extract markdown via Milkdown's `getMarkdown()` 41 + 2. Load into CodeMirror: `view.dispatch({ changes: { from: 0, to: doc.length, insert: markdown } })` 42 + 3. Hide `#editor-rich`, show `#editor-source` 43 + 44 + **Source → Rich:** 45 + 1. Get markdown from `view.state.doc.toString()` 46 + 2. Destroy and recreate Milkdown with new content (Milkdown does not support live content replacement) 47 + 3. Hide `#editor-source`, show `#editor-rich` 48 + 49 + ## Layout 50 + 51 + ```css 52 + .editor-rich { 53 + flex: 1; 54 + overflow: auto; 55 + padding: 2rem 1.5rem; 56 + display: flex; 57 + flex-direction: column; 58 + align-items: center; 59 + background: var(--bg-card); 60 + } 61 + 62 + .editor-rich .milkdown { 63 + width: 100%; 64 + max-width: 720px; 65 + } 66 + ``` 67 + 68 + No visible border or chrome on the editor surface — looks like a rendered document that is editable. Existing `markdown.css` prose styles apply via the `markdown-body` class or equivalent selector on `.milkdown`. 69 + 70 + `#editor-source` is unchanged. 71 + 72 + ## Files Changed 73 + 74 + - `static/vendor/milkdown.js` — new Milkdown bundle (esbuild) 75 + - `static/css/editor.css` — add `.editor-rich` and `.editor-rich .milkdown` styles 76 + - `templates/document_edit.html` — add `#editor-rich` container, Source toggle button, mode switching JS 77 + - `Dockerfile` — add esbuild step for milkdown.js