Repository navigation
Search within the session log viewer - #47
Merged
Merged
Conversation
jongracecox
force-pushed
the
worktree/20260826-refine-transcript-search
branch
from
August 27, 2026 22:33
ef81aaa to
00c6e07
Compare
Reading a transcript back gave you no way to find anything in it, and the footer advertised the home screen's keys regardless of the view. - `/` searches the open transcript: matches highlighted in place, `n`/`N` stepping through them with wraparound, `Ctrl+R`/`Ctrl+I` toggling regex and case as they do in the transcript search. A match inside a folded tool call opens that fold, since a highlight you cannot scroll to is worse than none. - Matching runs in a worker thread and returns spans, not widgets; only the bodies whose matches changed are repainted. A full rebuild costs ~540ms on an 838-message log and takes the query box down with it, against ~1.7ms of matching plus ~6ms of Content building now. - The messages moved into their own scroll pane, so the header, query box and hint stay put while the transcript scrolls. - Backing out of a search result's actions menu now lands on the result you opened rather than the top of the list, so a set of results can be walked one at a time. - `check_action` drops bindings that do nothing in the current view. `/` is two bindings on one key so it can read "Filter" on the home screen and "Search" in the viewer. Two crashes fixed on the way: - A `Collapsible` title built from transcript bytes was parsed as console markup, so a Bash command containing `[` raised MarkupError and took the viewer down. Titles, the viewer header and the session picker rows are now Content. Same reasoning for the query box's `select_on_focus`, which was eating the query whenever focus returned to it. - A transcript the parser cannot structure now shows a warning banner and the raw log — searchable like any other — instead of an error screen. A file that cannot be read is still an error. `iter_hits` swallows the same class of failure per log, so one bad transcript cannot end a scan. Both swallowing paths are instrumented, since neither is visible in the UI: `claude.transcript_unparsed` carries the traceback, `search.scan_failed` names the log it dropped. The viewer logs `tui.log_view` and `log_search.start`/`.done`/`.dropped`, whose `repainted` count is what shows the in-place update still working. Also documents that a global install needs `--no-cache`: uv's build cache is keyed on the hatch-vcs version, which is identical across untagged commits, so `--force --reinstall` reports success while leaving the old wheel in place.
jongracecox
force-pushed
the
worktree/20260826-refine-transcript-search
branch
from
August 28, 2026 04:53
00c6e07 to
9c5705b
Compare
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.
Reading a transcript back gave you no way to find anything in it, and the footer
advertised the home screen's keys regardless of the view.
What's new
/searches the open transcript. Matches are highlighted in place,n/Nstep through them with wraparound, and
Ctrl+R/Ctrl+Itoggle regex and caseas they do in the
stranscript search. A match inside a folded tool call opensthat fold — a highlight you can't scroll to is worse than none.
Searching never rebuilds the view.
_render_transcriptnow returns thewidgets plus a
LogBodyper body (widget, source text, the folds hiding it);a query only calls
Static.update()on the bodies whose spans actually changed.Matching itself runs in a worker thread and returns plain span data — no widgets
are built off-thread — guarded by a generation token, since a scan can land
after the query has moved on.
Measured on a real 838-message log:
The remount was visible as the query box disappearing mid-keystroke.
The messages moved into their own scroll pane, so the header and query box
stay put while the transcript scrolls. The viewer's hint row is gone:
/andEscape are in the footer already, and a second strip of grey text under a
screenful of transcript reads as a second footer.
Backing out of a search result now lands on the result you opened rather
than the top of the list, so a set of results can be walked one at a time.
The footer only shows keys that do something here.
check_actiongatesthem;
/is two bindings on one key so it can read Filter on the home screenand Search in the viewer, and the mode chords are no longer hidden now that
they only appear where they apply.
Two crashes fixed on the way
Collapsibletitle built from transcript bytes was parsed as consolemarkup, so a
Bashcommand containing[raisedMarkupErrorand took theviewer down. Titles, the viewer header and the session picker rows are now
Content. Same reasoning for the query box'sselect_on_focus, which waseating the query whenever focus returned to it.
log — searchable like any other — instead of an error screen. A file that
can't be read is still an error.
iter_hitsswallows the same class offailure per log, so one bad transcript can't end a scan over hundreds.
Instrumentation
Both swallowing paths are the ones
--debugmost needs to see, since neither isvisible in the UI:
claude.transcript_unparsedcarries the traceback andsearch.scan_failednames the log it dropped. The viewer logstui.log_viewand
log_search.start/.done/.dropped, whoserepaintedcount is what showsthe in-place update still working. Verified under
--debug-redactedagainst areal transcript: no username leak, query redacted.
Also
Documents that a global install needs
--no-cache. uv's build cache is keyed onthe derived version, which
hatch-vcsmakes identical across untagged commits,so
--force --reinstallreports success while leaving the old wheel in place —which looks exactly like a feature that doesn't work.
Testing
977 tests pass, coverage on
cli.pyholds at 95%, pre-commit clean.