Transcript search: n/N cannot reach most highlighted matches (highlighting scans the screen, navigation uses a narrower index)

Status Open
Reported on v2.1.247
Maintainer reply None cached
Activity 0 comments · opened Aug 27, 2026

Summary

In transcript view (Ctrl+O), / highlights every occurrence rendered on screen, but n/N can only step through matches found by a separate, much narrower message-level index. Any highlighted hit the index missed is unreachable: pressing n does nothing at all.

Repro

  1. A reasonably long session, fullscreen renderer (default).
  2. Ctrl+O to open the transcript.
  3. / and search for a string that occurs in assistant thinking blocks or non-whitelisted tool inputs (e.g. an Edit old_string) as well as in ordinary message text.
  4. Scroll up with PageUp — several occurrences are visibly highlighted.
  5. Press n / N — it will not visit them.

Debug evidence

claude --debug --debug-file=/tmp/cc.log, then grep -E 'seek\(i=|phantoms' /tmp/cc.log:

[DEBUG] seek(i=61 t=0): 31 positions
[DEBUG] seek(i=61 t=0): 5 positions
[DEBUG] seek(i=27 t=0): 2 positions
[DEBUG] seek(i=27 t=0): 1 positions
[DEBUG] step: wraparound at ptr=0, all 1 msgs phantoms
[DEBUG] step: wraparound at ptr=0, all 1 msgs phantoms
... (one line per `n` press, nothing moves)

all 1 msgs phantoms means the match index contained exactly one message, while many more were highlighted on screen. Note there are no no mount after scrollToIndex, skip lines — this is not a virtualization/mount-timing problem.

Cause

Highlighting and navigation are driven by two independent mechanisms.

Highlighting is a whole-screen scan, independent of the index — every rendered occurrence is painted:

v = tx(p.screen, this.searchHighlightQuery, this.stylePool)

Navigation is built from a message-level index via extractSearchText, whose base extractor only pulls:

  • user: text blocks, plus tool_result via toolUseResult
  • assistant: text blocks; for tool_use blocks only a whitelist of input fields —

["command","pattern","file_path","path","prompt","description","query","url","skill"] (plus args, files)

  • attachment: only relevant_memories, queued_command, tool_host_result_lines
  • everything else: "" (default: break)

So assistant thinking blocks, non-whitelisted tool inputs (Edit old_string/new_string, Write content, …), and unhandled message types are highlighted but never indexed, and therefore never reachable with n/N.

Secondary issue

When the index legitimately contains a single message, n is a dead key even for that message's own hits. The step function computes:

let se = (M.ptr + C + E.length) % E.length   // E.length === 1  =>  se === 0 === anchor

and the wraparound guard immediately bails and clears the highlight instead of cycling back to the first position within that message.

Expected

n/N should visit every occurrence that gets highlighted.

Environment

  • claude 2.1.247 (also reproduced on 2.1.246)
  • Arch Linux, kitty 0.48.2, fullscreen renderer

View original on GitHub ↗