Add transcript search and per-activity detail controls (#46734)

## What changed

- Add incremental, case-insensitive literal search across transcript details, loading older history as needed. Use `F3` to start Find, or `/` in the transcript pager, and navigate matches in either direction. Closing Find restores the previous presentation and reading position.
- Add clickable activity detail controls and `F4` keyboard focus for inspecting individual activity groups in the owned transcript. Preserve expansion state as live activity becomes history.
- Expose configurable `global.find_transcript`, `global.focus_activity`, and `pager.find` bindings under `tui.keymap`. Let new defaults yield to existing custom bindings and show available completions for pending chords.

## Testing

Add regression coverage for Unicode matching, bounded scanning, paginated and changing history, selection during search, composer input isolation, activity controls, and custom shortcut conflicts.

GitOrigin-RevId: 5bcbcdfbca7c5c296c635ec9b079ccd7a908e336
This commit is contained in:
Eric Traut
2026-09-20 00:06:25 +00:00
committed by copyberry
parent f1feda2299
commit df7f717c85
77 changed files with 4774 additions and 481 deletions
+14 -3
View File
@@ -102,7 +102,10 @@ impl TranscriptView {
|| (modifiers == KeyModifiers::NONE
&& matches!(code, KeyCode::PageUp | KeyCode::PageDown));
}
false
self.is_search_active()
&& (matches!(code, KeyCode::Esc | KeyCode::Enter)
|| (modifiers == KeyModifiers::CONTROL
&& matches!(code, KeyCode::Char('c' | 'n' | 'p'))))
}
pub(crate) fn handle_key(
@@ -113,7 +116,9 @@ impl TranscriptView {
if key.kind == KeyEventKind::Release {
return None;
}
if let Some(action) = self.handle_disclosure_key(key, cells) {
return Some(action);
}
if self.selection.is_some() {
if let Some(action) = self.handle_selection_key(key, cells) {
return Some(action);
@@ -129,7 +134,9 @@ impl TranscriptView {
}
return Some(ViewAction::Changed);
}
if self.handle_search_key(key, cells) {
return Some(ViewAction::Changed);
}
self.handle_scroll_key(key, cells)
}
@@ -259,6 +266,10 @@ impl TranscriptView {
event: MouseEvent,
cells: &[Arc<dyn HistoryCell>],
) -> Option<ViewAction> {
if event.modifiers.is_empty() && self.toggle_disclosure_at(cells, event.column, event.row) {
return Some(ViewAction::Changed);
}
self.disclosure.focused = None;
let visible = self
.visible
.get(usize::from(event.row.checked_sub(self.area.y)?))?;