✨ Become an AI-era PM — words → product → doAIPM.com
#MD SoloMD

The bug we couldn't fix in JavaScript — why SoloMD's Windows editor left contentEditable

In June a user reported that typing Chinese in SoloMD on Windows was "sometimes fine, sometimes broken" — characters silently dropped, punctuation needing two keypresses, worst with Sogou Pinyin (#108). A month later the fix shipped, and it wasn't a patch: SoloMD v4.7 replaced CodeMirror on Windows with a block editor built on native <textarea> elements. This post is the debugging trail that justified something that drastic.

The first suspect: our own autocomplete

IME composition is a fragile dance: between compositionstart and compositionend, the browser owns a small piece of the DOM, and the editor must not disturb it. CodeMirror 6 knows this and freezes what it can. But tracing every composition event from a real Windows WebView2 into a log receiver showed something dispatching transactions mid-composition — and the stack pointed at autocompletion.

With activateOnTyping: true, CodeMirror's autocomplete plugin classifies every IME keystroke as an activation: it churns the completion machinery on each pinyin letter, and its compositionend handler schedules a startCompletion transaction ~20 ms after each commit. That post-commit dispatch aborts the next composition — WebView2 gives up and commits raw pinyin. Guarding our completion sources wasn't enough (the plugin activates regardless); turning activateOnTyping off and re-triggering popups from an explicit character whitelist was. That fix was real, and it removed the intermittent failures.

The defect underneath

Two symptoms survived every JavaScript mitigation we tried: the first character of a composition was dropped (吃字), and full-width punctuation (,。?!) required two keypresses. We could reproduce both in a page with no editor at all — a bare contentEditable div with event logging. Suppressing all DOM mutations on the composing node during composition changed nothing, which told us the bug lives below the DOM/JS layer, in WebView2's TSF (Text Services Framework) integration.

The decisive contrast: the same keystrokes, same IME, same session, typed into a plain <textarea> on the same page, worked perfectly. The native text-input path is fine; only contentEditable is broken. That means every contentEditable-based editor — CodeMirror 6, ProseMirror, Slate — inherits the bug inside WebView2, and none of them can fix it from JavaScript.

We reduced it to a self-contained repro.html (a contentEditable box and a textarea side by side, with composition / beforeinput / input logging) and filed it upstream: MicrosoftEdge/WebView2Feedback#5625 — "WebView2 drops first CJK IME character & doubles Chinese punctuation in contentEditable" (reproduced on Evergreen runtime 149.0.4022.69, Windows 11).

The rewrite: native textareas, one block at a time

Once the defect is below JavaScript, an app has two honest options: wait for Microsoft, or stop standing on the broken floor. Chinese input is not an edge case for a Markdown editor with a large CJK user base, so v4.7 moved SoloMD's Windows live editor onto the input surface we had just proven correct: the document is split into Markdown blocks, inactive blocks render as HTML, and the active block is a real <textarea> — the native IME path, no contentEditable anywhere. macOS, Linux, iOS and Android keep CodeMirror; their webviews don't have this bug.

The cost of that decision is feature parity, re-implemented by hand: live preview with markers fading per block, autocomplete and slash commands, focus and typewriter modes, smart list continuation, AI rewrite, in-document find & replace, clipboard image paste, task-checkbox toggling, undo/redo coalescing, Tab indent, spellcheck, word wrap. A block editor also has seams a single buffer doesn't: Backspace at a block boundary used to no-op (fixed in 4.7.4 by folding boundary deletes onto the full document), and Ctrl+A could only select inside one block (fixed in 4.8.10 by collapsing the document into one merged block under a native full selection).

What we'd tell other editor authors

SoloMD is a free, MIT-licensed Markdown editor for macOS, Windows, Linux, iOS and Android — solomd.app. The Windows block editor ships since v4.7; the full change log is on the what's new page.

Comments & feedback

Comments live on GitHub Discussions — sign in once with your GitHub account.

Comments load from GitHub Discussions. If you see this message instead, the maintainer has not finished the one-time giscus.app setup yet — see web/COMMUNITY-SETUP.md.

Open on GitHub Discussions →