···11+# WYSIWYG Editor Design
22+33+## Overview
44+55+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`.
66+77+## Architecture
88+99+Single edit page at `/docs/{rkey}/edit`. Both editor containers exist in the DOM simultaneously — only one is visible at a time:
1010+1111+- `#editor-rich` — Milkdown, centered at max-width 720px, default mode
1212+- `#editor-source` — existing CodeMirror + preview split pane, "Source" mode
1313+1414+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"`.
1515+1616+Both editors initialize from the same hidden textarea on page load. The correct editor is shown immediately from localStorage — no flash.
1717+1818+Autosave is shared: both modes call the same `scheduleAutoSave(markdown, title)` that POSTs to `/api/docs/{rkey}/autosave`.
1919+2020+When in rich text mode, the Preview and Wrap toolbar buttons are hidden (they only apply to source view).
2121+2222+## Milkdown Setup
2323+2424+Use `@milkdown/kit` with:
2525+- `@milkdown/kit/core` — editor core
2626+- `@milkdown/kit/preset/commonmark` — headings, bold, italic, lists, blockquotes, code blocks, links; includes bubble menu on text selection
2727+- `@milkdown/kit/plugin/listener` — content change events for autosave
2828+2929+No toolbar, no slash commands. The bubble menu (appears on text selection) is the sole formatting UI — minimal, keyboard-shortcut-first.
3030+3131+## Bundle
3232+3333+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.
3434+3535+Milkdown injects its own styles via JS — no separate stylesheet needed.
3636+3737+## Mode Switching
3838+3939+**Rich → Source:**
4040+1. Extract markdown via Milkdown's `getMarkdown()`
4141+2. Load into CodeMirror: `view.dispatch({ changes: { from: 0, to: doc.length, insert: markdown } })`
4242+3. Hide `#editor-rich`, show `#editor-source`
4343+4444+**Source → Rich:**
4545+1. Get markdown from `view.state.doc.toString()`
4646+2. Destroy and recreate Milkdown with new content (Milkdown does not support live content replacement)
4747+3. Hide `#editor-source`, show `#editor-rich`
4848+4949+## Layout
5050+5151+```css
5252+.editor-rich {
5353+ flex: 1;
5454+ overflow: auto;
5555+ padding: 2rem 1.5rem;
5656+ display: flex;
5757+ flex-direction: column;
5858+ align-items: center;
5959+ background: var(--bg-card);
6060+}
6161+6262+.editor-rich .milkdown {
6363+ width: 100%;
6464+ max-width: 720px;
6565+}
6666+```
6767+6868+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`.
6969+7070+`#editor-source` is unchanged.
7171+7272+## Files Changed
7373+7474+- `static/vendor/milkdown.js` — new Milkdown bundle (esbuild)
7575+- `static/css/editor.css` — add `.editor-rich` and `.editor-rich .milkdown` styles
7676+- `templates/document_edit.html` — add `#editor-rich` container, Source toggle button, mode switching JS
7777+- `Dockerfile` — add esbuild step for milkdown.js