Fix VoiceOver QuickNav, stale content, and review-cursor jumps in the terminal - #697
Fix VoiceOver QuickNav, stale content, and review-cursor jumps in the terminal#697Deon588 wants to merge 1 commit into
Conversation
|
Thanks for this, and for the unusually precise writeup. I investigated the same three symptoms independently on master and want to share one finding before it lands, since it may affect the QuickNav (step 1) fix. I reproduced your attribute-surface approach (drop AXDocument, make AXValue non-settable to VoiceOver via But I hit a residual leak the initial-interaction test does not cover: if you run a second command while still interacting and then press Up/Down, the arrows leak to the shell again (history navigation), even with AXValue hidden. It is essentially step 1 recurring after output. The debug log shows the trigger is independent of AXValue settability. Right before the leaked arrow: VoiceOver reads us as an editable AXTextArea whose caret just moved, switches into text-editing mode, and defers the arrow as caret movement. The thing that flips it into that mode is the value-changed + selected-text-changed pair that Why I am flagging it here: your steps 4/5 fix posts a value-changed + selected-text-changed pair at settle, which is the same class of notification I saw pulling VoiceOver into caret mode. So it is worth verifying this specific repro on your branch:
I tested an approximation on master, not your exact branch, so your full combination (especially the ordering of the announcement vs the notification pair) may behave differently, but we are on the same OS (macOS 26.5.2) so I suspect it reproduces. If it does, the deeper issue is that a focused editable AXTextArea with a caret inherently makes VoiceOver treat arrows as caret keys, and the fix may need to suppress the cursor-follow notifications while VoiceOver is browsing (or move away from one editable AXTextArea toward per-line elements, which is possibly how Terminal.app avoids it). Happy to pair or dig in the debugger if useful. |
… terminal VoiceOver treats the terminal as more than a plain text area because it advertises an AXDocument attribute and a settable AXValue (settable only because of the no-op setter kept for Full Keyboard Access, Issue 10023); Terminal.app advertises neither. As a result, QuickNav navigation does not engage after the user interacts with the terminal until QuickNav is toggled off and on. Hide both from the accessibility surface while VoiceOver is running via isAccessibilitySelectorAllowed:. The gate preserves Full Keyboard Access when VoiceOver is off; when both run at once, hiding AXValue wins because QuickNav breaks otherwise. The announce-new-output feature suppresses value-changed notifications during an output burst and posts layout-changed with NSAccessibilityUIElementsKey once output settles. That notification directs VoiceOver to move its cursor to the listed element, relocating the user's review cursor to the top of the terminal on every command; and because nothing re-posts a content notification at settle, VoiceOver keeps reading pre-burst text during an active interaction and cannot reach new output without re-interacting. Replace the layout-changed with a value-changed + selected-text-changed pair posted after the announcement: the pair makes VoiceOver refresh its cached content and extend its navigable model (value-changed alone does not) without moving the review cursor. Terminal.app posts the same pair for output and never posts layout-changed. Issue 12920
750590f to
446548a
Compare
|
Thanks for the careful analysis. I ran your exact reproduction steps on this branch (rebased onto 4416dd3 today), macOS 26.5.2, VoiceOver with QuickNav on: An AXObserver capture over the whole reproduction steps/flow shows why I think your master test behaved differently. On this branch the only notifications at settle are a single pair of AXValueChanged and AXSelectedTextChanged, and zero AXLayoutChanged. Master still posts AXLayoutChanged with NSAccessibilityUIElementsKey at settle ( Two data points from the diagnosis that may save you time. First, Terminal.app does not expose the terminal as separate elements per line: its accessibility surface is a single AXTextArea with zero children and the same parameterized attributes as iTerm's (no text markers). Second, what Terminal does differently, per an AXObserver capture, is preserve the caret VoiceOver sets in AXSelectedTextRange across output (it held the review position while content grew), whereas iTerm snaps the reported caret to the shell cursor on every move. If an edit mode flip does show up in some configuration, holding the caret VoiceOver set rather than suppressing notifications looks like the principled direction. If it still reproduces for you on the branch, I would take you up on pairing. One thing to note: I think we might be in different time zones, I am based in South Africa, so UTC+2. Could differences in VoiceOver settings (for example “Synchronize keyboard focus and VoiceOver cursor”) explain the divergence? On a side note: it would be amazing to get this landed. With these fixes iTerm2 becomes, for the first time a genuinely usable terminal for blind users, a real alternative to macOS Terminal with all of iTerm2's power on top. That would mean a lot to me and to other VoiceOver users... |
Fixes three VoiceOver problems when interacting with terminal content, none of which occur in Terminal.app. Reported in https://gitlab.com/gnachman/iterm2/-/issues/12920.
Causes and fixes:
Testing: verified with VoiceOver on macOS 26.5.2 — QuickNav engages immediately on interaction; output announcements still speak; the review cursor holds its position when new output arrives and the new output is reachable by navigating down; shell command history at the prompt and deleted-character announcements are unaffected. Release notes updated.