Typing with an IME no longer makes the line sink and snap back - #746
Merged
Conversation
…p back (#724) Monaco's hidden textarea becomes a one-row overlay on the composed line during IME composition, but it holds a page of text, so Monaco scrolls it to the caret's row. WebKit scrolls it back to "caret just visible" — 3px short of the row — on every composition update, and the next render scrolls it forward again. That is the jitter: the composed text sinks and snaps back on every keystroke, on every line but the first of the page. Monaco already treats `accessibilitySupport: 'auto'` in a browser as "no screen reader" when deciding whether to write into the textarea (vscode#192278), but not when deciding what to write; it hands that state a page. A build-time patch makes the two decisions agree, so the overlay holds one short line, has no overflow, and gives the browser nothing to scroll. Proposed upstream as microsoft/vscode#333909; the patch and its test go when a Monaco release carries it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Closes #724. Typing Chinese with the macOS IME made the line being composed sink a few pixels and snap back on every keystroke — on every line except the first. Reported by @PathGao, who also ran every experiment below on the builds that led here.
Mechanism
Not the block patcher, not fonts, not the editor's scroll. Two things had to be established first, both against the real app: no layout number the editor exposes changes during composition (content height, the caret line's top, the editor's scroll offset all hold still), and the jitter is gone with
accessibilitySupport: 'off'but unchanged with a one-line accessibility page.The moving part is Monaco's hidden textarea. In a browser without
EditContext— WKWebView, so every macOS build — the editor takes input through it, and during IME composition it becomes a one-row overlay on the composed line. It holds a page of document text, so Monaco scrolls it to the caret's row (scrollTop = newlinesBefore × lineHeight). Logged from inside the app, one keystroke:102 self-scrolls over three composed lines, every one exactly 3px short; on line 3 it is 42 → 39. The first line of the page never jitters because its target offset is 0. Chromium never runs this code path (
editContextdefaults on there), which is why the upstream report says "only in Safari".Monaco already treats
accessibilitySupport: 'auto'in a browser as "no screen reader" when deciding whether to write into the textarea — it skips render-time writes unless a screen reader is confirmed (vscode#192278) — but not when deciding what to write; that path hands the same state a page. This makes the two agree. The overlay then holds one short line, has no overflow, and there is nothing for the browser to scroll.Proposed upstream as microsoft/vscode#333909, against microsoft/monaco-editor#4796. Until a Monaco release carries it,
scripts/monacoImePatch.mjsapplies the same two-line change to the bundled Monaco at build time.Scope
A build-time source patch rather than an editor option:
accessibilitySupport: 'off'also fixes it but would take VoiceOver support away from every user; the patch keeps'on'behaving exactly as before. Monaco is still 0.55.1 — 0.56 does not carry the fix either (checked), and #599 keeps it off anyway.Things tried and rejected on the way, each measured: reordering Monaco's two writes (no effect — the write is never clamped, the offset is moved afterwards), re-asserting the offset every frame (works, but keeps the alignment depending on a value the browser owns), moving the overlay to follow the browser's scroll (worse: moving a focused editable makes WebKit reveal its caret through the ancestors, and the whole editor jumps), and giving the overlay its full height with
clip-path(broke rendering;_doRendercouples height and line-height).Tests
scripts/monacoImePatch.test.tspins the two anchors to the installed Monaco: each matches exactly once, the patch replaces exactly those two lines and nothing else, and a Monaco without the anchors throws instead of building unpatched. These are text assertions againstnode_modules, and that is the point — the contract is "these two lines still exist in this file". The Monaco bump that finally carries the upstream change turns them red, which is the signal to delete the patch. They read the file throughreadSource, per the convention test.Verification
Built the app from this branch and confirmed both replaced conditions are in the bundle. On the reporter's machine (macOS 26, system Chinese IME): composition on any line no longer moves, ASCII typing, select-all, copy, paste, undo and cross-line cursor movement unchanged.
Not verified: Windows and Linux builds (WebView2 is Chromium, so the textarea path is not used there; Linux WebKitGTK is, and should benefit the same way).
🤖 Generated with Claude Code