#7630·superset

Terminal: search leaves grey blocks behind over unrelated text

Author: hubertmineCreated Sep 17, 2026Updated Sep 17, 2026

Summary

  • After using Cmd+F in a terminal/agent pane, grey blocks stay painted where matches were, and they sit over unrelated text once the agent redraws
  • They never go away on their own: closing the find bar, switching tabs, scrolling and resizing do not clear them, and they come back when you scroll that part of the scrollback into view again
  • One block accumulates per keystroke typed into the find bar, so a six-letter query can leave several
  • Reproduced outside Superset against the pinned xterm build, on both the WebGL and the DOM renderer

Steps to reproduce

  1. Open an agent session with a few screens of output
  2. Press Cmd+F and type a word that occurs several times (type it, do not paste)
  3. Press Escape to close the find bar
  4. Let the agent write more output, or scroll away and back

Expected Closing the find bar removes every highlight.

Actual Grey blocks (#515c6a, the find bar's matchBackground) stay at the cell positions of earlier matches, including positions where the text has since changed, for as long as the terminal lives.

Investigation Reproduced in an isolated page with @xterm/xterm 6.1.0-beta.302 and @xterm/addon-search 0.17.0-beta.289 (the pinned versions), unpatched, both renderers. Findings:

  • After searchAddon.clearDecorations() the addon reports 0 results and the DOM overlay has 0 .xterm-decoration elements, but xterm's DecorationService still holds 4 entries, each with _isDisposed true and marker.line -1, still in the per-line index. Their widths (1, 1, 2, 3) are the prefixes typed into the find bar ("p", "p", "po", "por").
  • Cause is in DecorationService.registerDecoration (src/common/services/DecorationService.ts): the decoration's onDispose handler only cleans up the line index when the SortedList delete succeeds: if (this._decorations.delete(decoration)) { this._lineCache.remove(decoration); this._onDecorationRemoved.fire(decoration); } The SortedList is keyed by e => e.marker.line, and Marker.dispose() sets this.line = -1 before firing onDispose, which is what triggers the decoration's disposal. So the keyed delete misses, returns false, and the decoration is never removed from DecorationLineCache. getDecorationsAtCell/forEachDecorationAtCell iterate that index with no isDisposed check, so the renderer keeps painting the background.
  • Nothing available to the app repaints them away: terminal.refresh(0, rows-1), clearSelection(), reassigning options.theme and clearTextureAtlas() were each measured as no-ops. Not a renderer bug; the DOM renderer keeps the colour in the cell span's background style.
  • Still present in the newest published build, 6.1.0-beta.304, so no upgrade fixes it.
  • Superset makes it worse by calling findNext on every keystroke (TerminalSearch.tsx handleInputChange), which creates a fresh decoration generation per character.
  • Separately, one more block is the search selection: TerminalSearch never calls terminal.clearSelection(), so the last match stays selected after the find bar closes.

Fix verified in the harness: removing the decoration from the line cache unconditionally on dispose, and skipping disposed decorations in the two cell lookups, leaves the index empty and the viewport clean on both renderers. Adding clearSelection() on close removes the last block. PR to follow.

Environment Superset 1.29.0, macOS 26.0 arm64 (Darwin arm64)