A simple, high performance text editor.
Gboard sends Enter as a key event, not a commit, so the IME's model lags the buffer by exactly one edit and its next commit is anchored at the pre-edit caret. The drift guard snapped such commits to the new caret, duplicating the just-typed word (2026-09-19 phone incident: 'a<enter>a' became 'A' + 'AaAa'). - state.go: track one pending non-IME content edit (imeStaleEditByte, armed by noteNonIMEEdit on Enter/backspace/delete/cut/paste, consumed by the next IME commit or key edit, cleared by a tap). The guard accepts a small (<= 2 rune) TEXT commit ending at or before the edit position as-is (IME STALE-OK): everything before the edit is byte-identical in both models. Empty-text commits and commits ending past the edit still snap to the caret. - render.go + state.go + main.go: arm the pre-commit flush hold with the drained frame's own EditSeq. The hold was armed with the last drawn frame's seq while the frame's TextField.EditSeq was never set (always 0), so every frame matched and the hold released only via its 8-frame timeout - silently swallowing the resync pushes (and all IME sync) that heal the desync. EditorLayout now ships EditSeq with the frame's TextField; FlushIME releases on the next higher seq. - e2e (ime_key_edit_test.go): reproduce the incident (same-batch and next-batch variants) and pin the guard edges (deletions and past-the-edit commits still snap). - ime.md: document the key-event desync invariant and the hold. |
||
|---|---|---|
| .qwen | ||
| cmd/pad | ||
| doc | ||
| internal | ||
| scripts | ||
| tools/touchinject | ||
| .DS_Store | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||