Skip ghost text in VS Code / Cursor integrated terminals#656
Open
FuJacob wants to merge 1 commit into
Open
Conversation
The integrated terminal, code editor, and Copilot chat all share one bundle id (com.microsoft.VSCode), so the app-level TerminalAppDetector blocklist can only block or allow all three together. Detect the terminal at the surface level instead: xterm.js focuses an AXTextField whose AXDOMClassList contains `xterm-helper-textarea`, which the Monaco editor and chat (native-edit-context) never carry. Verified live against VS Code's accessibility tree. FocusSnapshotResolver reads AXDOMClassList on the resolved focused element and records FocusedInputSnapshot.isIntegratedTerminal; SuggestionAvailabilityEvaluator suppresses suggestions and visual-context capture there unless the new suggestInIntegratedTerminals setting is on (default off; toggle in the Apps pane). Editor and Copilot chat keep working; standalone terminal apps are unchanged. Generalizes to Cursor/Windsurf and browser-hosted web terminals (all xterm.js).
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.
Summary
Cotabby showed ghost text in the VS Code integrated terminal (#647), overlapping shell prompts and interactive command output. The terminal, code editor, and Copilot chat all share one bundle id (
com.microsoft.VSCode), so the existing app-level terminal blocklist (TerminalAppDetector.isTerminal) could only block or allow all three together. This recognizes the integrated terminal at the surface level — the focused element'sAXDOMClassList(xterm.js focuses anAXTextFieldwhose class isxterm-helper-textarea) — and suppresses suggestions there by default, while the editor and Copilot chat keep working. A new Suggest in Integrated Terminals toggle (Apps pane, off by default) lets power users opt back in. Generalizes for free to Cursor/Windsurf and browser-hosted web terminals (all xterm.js).Validation
The detection signal was verified live against VS Code 1.123's real accessibility tree: the focused terminal input is
AXTextFieldwithAXDOMClassList = ["xterm-helper-textarea"](flaggedAXFocused=true), while the Monaco editor and Copilot chat inputs arenative-edit-contextinsidemonaco-editor— noxtermclass. Not yet driven end-to-end in the running app.Linked issues
Fixes #647
Risk / rollout notes
suggestInIntegratedTerminals(UserDefaults keycotabbySuggestInIntegratedTerminals, defaultfalse). Existing installs get the default, i.e. the terminal is suppressed — consistent with how standalone terminal apps are already skipped. Threaded throughSuggestionSettingsData/SuggestionSettingsStore/ the snapshot publisher.FocusedInputSnapshot.isIntegratedTerminal, resolved from one extraAXDOMClassListread on the focused element. The read is placed after a real editable field has resolved, so idle / non-editable focus polls pay nothing; native apps don't vend the attribute.Greptile Summary
Introduces surface-level detection of xterm.js integrated terminals (VS Code, Cursor, Windsurf) by reading
AXDOMClassListfrom the focused AX element, solving the problem that the terminal, editor, and Copilot chat all share one bundle ID and cannot be separated with the existing app-level blocklist. A newsuggestInIntegratedTerminalsUserDefaults flag (defaultfalse) threads through the entire settings stack and availability evaluator, suppressing ghost text in the terminal while leaving the code editor and Copilot chat unaffected.FocusedInputSnapshot.isIntegratedTerminal(resolved once per real-editable-field poll viaAXHelper.stringArrayValue) andTerminalAppDetector.isIntegratedTerminal(domClassList:)which matches any class with anxtermprefix.suggestInIntegratedTerminalssetting throughSuggestionSettingsData→SuggestionSettingsModel→SuggestionSettingsSnapshot→ all fiveSuggestionAvailabilityEvaluatorcall sites, with aCombineLatestpair nested inside the existingCombineLatest4to stay within Combine's publisher-count limit.Confidence Score: 4/5
Safe to merge; the change adds a new off-by-default suppression path that is well-isolated and does not alter existing terminal or editor behavior.
The detection heuristic (hasPrefix("xterm")) is intentionally broad for forward-compatibility, but it would also silently suppress ghost text in any VS Code extension panel that happens to use xterm.js for non-terminal output display. This is a known tradeoff documented in comments, not an oversight, but it creates a hidden interaction for future extension authors or power users running xterm.js-based log viewers inside VS Code.
Cotabby/Support/TerminalAppDetector.swift — the prefix-match strategy is the only area worth revisiting if users report suggestions missing in non-terminal xterm.js panels.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Focus poll fires] --> B{Real editable\nfield resolved?} B -- No --> C[Return nil snapshot] B -- Yes --> D[Read AXDOMClassList\nfrom focusedElement] D --> E[TerminalAppDetector\n.isIntegratedTerminal] E --> F[FocusedInputSnapshot\nisIntegratedTerminal set] F --> G[SuggestionAvailabilityEvaluator\n.disabledReason] G --> H{isTerminal\nbundle ID?} H -- Yes --> I[Block: terminal app] H -- No --> J{isIntegratedTerminal\n&& !suggestInTerminals?} J -- Yes --> K[Block: integrated terminal] J -- No --> L{inputMonitoring\ngranted?} L -- No --> M[Block: permission] L -- Yes --> N[Suggest ✓]Comments Outside Diff (1)
Cotabby/Support/TerminalAppDetector.swift, line 383-384 (link)xtermprefix may suppress suggestions in non-terminal xterm.js panelshasPrefix("xterm")will also match any VS Code extension or Electron app that renders output/log viewer panels using xterm.js (e.g.,xterm-viewer,xterm-output). If a user has focus inside such a panel's editable field, suggestions will be suppressed even though it isn't a shell terminal. The current design is intentional (per the doc-comment: "keeps detection working if xterm renames its input node"), but the tradeoff means any future xterm.js-powered non-terminal component inside VS Code silently loses ghost text.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Reviews (1): Last reviewed commit: "Skip ghost text in VS Code / Cursor inte..." | Re-trigger Greptile