"An IME assembles keystrokes into a character before it commits. An editor that reacts to every keystroke corrupts it mid-air."
What an IME Actually Does
Type Korean and you're not sending finished characters — you're sending jamo that an Input Method Editor assembles into a syllable block live, in front of you. ㅎ then ㅏ then ㄴ becomes 한, but only after the composition commits. The same is true of Japanese kana-to-kanji and Chinese pinyin: multiple keystrokes, one committed character, an in-flight state in between.
Why Naive Editors Corrupt It
An editor that rewrites its document on every raw input event walks straight into the composition and stomps it — dropping jamo, duplicating characters, or committing half a syllable. The correct handling respects the composition lifecycle: know when composition starts, leave the in-progress text alone while it updates, and only apply the edit when it ends.
el.addEventListener("compositionstart", () => { composing = true; });
el.addEventListener("compositionupdate", () => { /* jamo assembling: do NOT commit */ });
el.addEventListener("compositionend", (e) => { composing = false; apply(e.data); });
CodeMirror 6 handles this entire lifecycle internally. Standing on it means you never write those three lines — and never spend the week it takes to debug them across browsers. This is the single biggest reason Rekindle borrows the core: IME is where hand-rolled editors go to die, and Dad writes in Korean every day.
The Bytes Bite Even in Your Source
Non-ASCII pain doesn't stop at the editing surface — it reaches your own source files. A real Rekindle war story: Korean sitting inside a source module (a voice-rule regex, for instance) worked in a production build and in Chromium but silently broke in tauri dev. The cause was byte decoding, not logic — and a charset=utf-8 header didn't fix it, because a module script is always UTF-8 by spec regardless of Content-Type.
Symptom: Korean in a source module works in prod + Chromium,
silently breaks in `tauri dev`.
Cause: Vite serves ES modules over HTTP; WKWebView mis-decodes
raw non-ASCII bytes on that transport.
Fix: A Vite plugin escapes non-ASCII to \uXXXX in served output.
Decode-invariant: no webview can corrupt \u-escaped ASCII.
The fix keeps source files readable Korean and makes only the served transport ASCII-only. The lesson generalizes: the moment non-ASCII text is real (and for Dad it always is), you inherit an entire class of encoding bugs — one more reason to borrow every layer you can and keep your own surface area small.