Skip to content

feat: advanced filter — regex mode, content/repo targets, cursor navigation + scroll fixes #64

@shouze

Description

@shouze

Summary

This issue tracks the implementation of advanced TUI filter capabilities and two scroll bug fixes.


✅ Filter enhancements (roadmap #63)

Regex support in filter bar

Toggle with Tab while the filter bar is open. A [regex] badge appears in the bar. Invalid patterns are handled gracefully (no crash — zero results shown).

Closes roadmap item: 🔴 Regex support in path filter

Filter by extract content

Cycle the filter target with t to reach content mode. The filter is then matched against the code fragment text (TextMatch.fragment) instead of file paths.

Closes roadmap item: 🔴 Filter by extract content

Filter by repository name

Cycle the filter target with t to reach repo mode. The filter is then matched against the repository full name (org/repo). The entire repo row and all its extracts are hidden when they don't match.

Closes roadmap item: 🟡 Filter by repository name

Text cursor navigation in filter bar

The filter input now supports a full inline text cursor:

  • / — move one character
  • ⌥← / ⌥→ (macOS) · Alt+← / Alt+→ — jump one word
  • Backspace — delete character before cursor
  • ⌥⌫ / Ctrl+W — delete word before cursor

🐛 Scroll bug fixes

isCursorVisible did not account for row height

The old implementation returned true as soon as it found the cursor row in the loop, without checking whether the row's height (h) fit in the remaining viewport space. For extract rows with a code fragment (h = 2), this caused the scroll adjuster to stop one iteration too early — making the last extracts of an unfolded repo unreachable.

Fix: mirror the renderGroups break condition: usedLines === 0 || usedLines + h <= viewportHeight.

viewportHeight off-by-one in tui.ts

viewportHeight was computed as termHeight - 5 instead of termHeight - 6. renderGroups consumes HEADER_LINES (4) + position indicator (2) = 6 fixed lines, so the scroll adjuster was comparing against a viewport 1 line larger than what renderGroups actually renders into.

Fix: viewportHeight = termHeight - 6.


Files changed

File Change
src/types.ts New FilterTarget = "path" | "content" | "repo"
src/render/filter-match.ts New — makePatternTest, makeExtractMatcher, makeRepoMatcher
src/render/filter-match.test.ts New — unit tests for filter matchers
src/render/rows.ts Fixed isCursorVisible break condition
src/tui.ts Regexmode, filter target cycling, cursor navigation, scroll fix
src/render.ts Filter target rendering (badge, bar display)
src/render/filter.ts Updated FilterStats + buildFilterStats for new targets
src/render/selection.ts applySelectAll / applySelectNone respect new targets
src/render.test.ts Extended tests + regression test for isCursorVisible
docs/reference/keyboard-shortcuts.md New filter targets table + new filter-mode bindings
docs/usage/filtering.md Updated for all three targets + regex
docs/usage/interactive-mode.md Updated

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions