Editor
A markdown editor that is a textarea, a toolbar and a preview — and nothing else.
The value is markdown, in and out. Not HTML: markdown is what belongs in a
database. It is readable in a SQL console, it diffs, and it cannot carry a
script.
**It is not WYSIWYG, and that is the design.** Flux does not build its own
either — it sits on ProseMirror and loads it outside the main bundle. With a
`<textarea>`, selection, paste, IME, mobile keyboards and undo all stay the
browser's. A click on *Bold* goes onto the browser's own undo stack, so
`⌘Z` steps back through formatting and typing together. That is why the code
uses `execCommand('insertText')` rather than assigning `.value`: assigning
throws the undo history away, and there is no replacement API for this one
case. Verified in a real Chrome, because jsdom has no `execCommand` to verify
it with.
The toolbar is a string, as in Flux: `"heading | bold italic ~ preview"`. An
unknown name is skipped — a typo should cost a button, not the page. It is one
tab stop with arrow keys between the buttons, not twelve stops between the
previous field and the text.
**Raw HTML never reaches the preview.** That is where an editor becomes a
stored XSS: the text comes from whoever is typing, and the preview runs in the
reader's browser on your domain. Everything is escaped first, and link schemes
are limited to `http`, `https`, `mailto`, `tel` and relative addresses.
The test that holds this closed reads `a.protocol` back out of the DOM rather
than matching the output with a regex — a regex is an idea of HTML, the parser
is HTML.
`renderMarkdown` is exported for showing stored markdown elsewhere. It covers
what the toolbar can produce and nothing more; Lauf does not take a markdown
dependency for a preview.
Put it inside a [`Field`](/components/field) for the label, the description
and the error. The textarea is labelable, so `<label for>` works as it should.