Skip to content

Commit 4248228

Browse files
authored
Merge pull request #65 from fulll/feat/advanced-filter-targets-and-scroll-fixes
feat: advanced filter — regex mode, content/repo targets, cursor navigation + scroll fixes (v1.5.0)
2 parents a19cd89 + 424bab4 commit 4248228

File tree

20 files changed

+1362
-162
lines changed

20 files changed

+1362
-162
lines changed

CHANGELOG.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ Release notes and changelogs are published on the **[project blog](https://fulll
44

55
Each release entry covers the motivation, new features, breaking changes (if any), and upgrade notes.
66

7-
| Version | Blog post |
8-
| ------------------------------------------------------------------------ | ---------------------------------------------------- |
9-
| [v1.4.0](https://fulll.github.io/github-code-search/blog/release-v1-4-0) | TUI visual overhaul, community files, demo animation |
10-
| [v1.3.0](https://fulll.github.io/github-code-search/blog/release-v1-3-0) | Team-prefix grouping, replay command, JSON output |
11-
| [v1.0.0](https://fulll.github.io/github-code-search/blog/release-v1-0-0) | Initial release |
7+
| Version | Blog post |
8+
| ------------------------------------------------------------------------ | ---------------------------------------------------------- |
9+
| [v1.5.0](https://fulll.github.io/github-code-search/blog/release-v1-5-0) | Advanced filter targets, regex mode, word-jump, scroll fix |
10+
| [v1.4.0](https://fulll.github.io/github-code-search/blog/release-v1-4-0) | TUI visual overhaul, community files, demo animation |
11+
| [v1.3.0](https://fulll.github.io/github-code-search/blog/release-v1-3-0) | Team-prefix grouping, replay command, JSON output |
12+
| [v1.0.0](https://fulll.github.io/github-code-search/blog/release-v1-0-0) | Initial release |
1213

1314
> For the full list of commits between releases, see the
1415
> [GitHub Releases page](https://github.com/fulll/github-code-search/releases).

CONTRIBUTING.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,24 @@ bun install
2121
github-code-search.ts # CLI entry point (Commander subcommands: query, upgrade)
2222
build.ts # Build script (compiles the standalone binary)
2323
src/
24-
types.ts # Shared TypeScript types
25-
api.ts # GitHub REST API client
24+
types.ts # Shared TypeScript types (TextMatchSegment, CodeMatch, RepoGroup, Row, FilterTarget, …)
25+
api.ts # GitHub REST API client (search, team listing)
26+
api-utils.ts # Shared retry (fetchWithRetry) and pagination (paginatedFetch) helpers
27+
api-utils.test.ts # Unit tests for api-utils.ts
28+
api.test.ts # Unit tests for api.ts
29+
cache.ts # Disk cache for the team list (24 h TTL)
30+
cache.test.ts # Unit tests for cache.ts
2631
aggregate.ts # Result grouping and filtering logic
2732
aggregate.test.ts # Unit tests for aggregate.ts
2833
render.ts # Façade: re-exports sub-modules + TUI renderGroups/renderHelpOverlay
2934
render.test.ts # Unit tests for render.ts (rows, filter, selection, rendering)
3035
render/
3136
highlight.ts # Syntax highlighting (language detection, token rules, highlightFragment)
3237
highlight.test.ts # Unit tests for highlight.ts (per-language tokenizer coverage)
33-
filter.ts # Filter helpers (FilterStats, buildFilterStats)
34-
rows.ts # Row navigation (buildRows, rowTerminalLines, isCursorVisible)
38+
filter.ts # Filter stats (FilterStats, buildFilterStats)
39+
filter-match.ts # Pure pattern matchers (makeExtractMatcher, makeRepoMatcher)
40+
filter-match.test.ts # Unit tests for filter-match.ts
41+
rows.ts # Row builder (buildRows, rowTerminalLines, isCursorVisible)
3542
summary.ts # Stats labels (buildSummary, buildSummaryFull, buildSelectionSummary)
3643
selection.ts # Selection mutations (applySelectAll, applySelectNone)
3744
output.ts # Text (markdown) and JSON output formatters

demo/demo.gif

145 KB
Loading

demo/demo.tape

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,12 @@ Down
4141
Sleep 300ms
4242
Down
4343
Sleep 300ms
44-
Down
45-
Sleep 300ms
4644

4745
# ── Toggle selection off on current extract ───────────────────────────────────
4846
Space
4947
Sleep 400ms
5048

51-
# ── Enter filter mode to exclude .ts files ────────────────────────────────────
49+
# ── Filter by file path (default target) ──────────────────────────────────────
5250
Type "f"
5351
Sleep 300ms
5452
Type ".yml"
@@ -64,7 +62,28 @@ Sleep 500ms
6462
Type "r"
6563
Sleep 600ms
6664

67-
# ── Confirm and print output ──────────────────────────────────────────────────
65+
# ── Switch to repo filter mode and filter by repo name ────────────────────────
66+
# Press t twice: path → content → repo
67+
Type "t"
68+
Sleep 300ms
69+
Type "t"
70+
Sleep 400ms
71+
72+
# Enter filter mode and type a repo name fragment
73+
Type "f"
74+
Sleep 300ms
75+
Type "toolkit"
76+
Sleep 700ms
77+
Enter
78+
Sleep 500ms
79+
80+
# Select all repos matching the filter
81+
Type "a"
82+
Sleep 500ms
83+
84+
# ── Reset and confirm ─────────────────────────────────────────────────────────
85+
Type "r"
86+
Sleep 600ms
6887
Enter
6988

7089
Sleep 2s

docs/architecture/components.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ C4Component
4141

4242
## 3b — TUI render layer
4343

44-
The six pure render functions called by the TUI on every redraw. All six live in
45-
`src/render/` and are re-exported through the `src/render.ts` façade.
44+
The render-layer modules called by the TUI on every redraw. Most live in
45+
`src/render/` and are re-exported through the `src/render.ts` façade;
46+
`src/output.ts` is the output formatter invoked on confirmation and `src/render/filter-match.ts`
47+
provides shared pattern-matching helpers used by several render modules.
4648

4749
```mermaid
4850
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Poppins, Aestetico, Arial, sans-serif", "primaryColor": "#66CCFF", "primaryTextColor": "#000000", "lineColor": "#0000CC", "tertiaryColor": "#FFCC33"}, "themeCSS": ".label,.nodeLabel,.cluster-label > span{font-family:Poppins,Arial,sans-serif;letter-spacing:.2px} .cluster-label > span{font-weight:600;font-size:13px} .edgePath .path{stroke-width:2px}"}}%%
@@ -56,10 +58,11 @@ C4Component
5658
Container_Boundary(render, "src/render/ — pure functions") {
5759
Component(rows, "Row builder", "src/render/rows.ts", "buildRows()<br/>rowTerminalLines()<br/>isCursorVisible()")
5860
Component(summary, "Summary builder", "src/render/summary.ts", "buildSummary()<br/>buildSummaryFull()<br/>buildSelectionSummary()")
59-
Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()<br/>visible vs total rows")
61+
Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()<br/>FilterStats — visible/hidden counts")
6062
Component(selection, "Selection helpers", "src/render/selection.ts", "applySelectAll()<br/>applySelectNone()")
6163
Component(highlight, "Syntax highlighter", "src/render/highlight.ts", "highlightFragment()<br/>ANSI token colouring")
6264
Component(outputFn, "Output formatter", "src/output.ts", "buildOutput()<br/>markdown or JSON")
65+
Component(filterMatch, "Pattern matchers", "src/render/filter-match.ts", "makeExtractMatcher()<br/>makeRepoMatcher()")
6366
}
6467
6568
Rel(tui, rows, "Build terminal<br/>rows")
@@ -80,8 +83,18 @@ C4Component
8083
Rel(tui, outputFn, "Format<br/>on Enter")
8184
UpdateRelStyle(tui, outputFn, $offsetX="17", $offsetY="160")
8285
86+
Rel(rows, filterMatch, "Uses pattern<br/>matchers")
87+
UpdateRelStyle(rows, filterMatch, $offsetX="-5", $offsetY="-5")
88+
89+
Rel(filter, filterMatch, "Uses pattern<br/>matchers")
90+
UpdateRelStyle(filter, filterMatch, $offsetX="45", $offsetY="-5")
91+
92+
Rel(selection, filterMatch, "Uses pattern<br/>matchers")
93+
UpdateRelStyle(selection, filterMatch, $offsetX="165", $offsetY="-25")
94+
8395
UpdateElementStyle(tui, $bgColor="#FFCC33", $borderColor="#0000CC", $fontColor="#000000")
8496
UpdateElementStyle(rows, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
97+
UpdateElementStyle(filterMatch, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
8598
UpdateElementStyle(summary, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
8699
UpdateElementStyle(filter, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
87100
UpdateElementStyle(selection, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
@@ -91,20 +104,21 @@ C4Component
91104

92105
## Component descriptions
93106

94-
| Component | Source file | Key exports |
95-
| ------------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96-
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` — filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
97-
| **Team grouping** | `src/group.ts` | `groupByTeamPrefix()` — groups `RepoGroup[]` into `TeamSection[]` keyed by team slug; `flattenTeamSections()` — converts back to a flat list for the TUI row builder. |
98-
| **Row builder** | `src/render/rows.ts` | `buildRows()` — converts `RepoGroup[]` into `Row[]` with expanded/collapsed state; `rowTerminalLines()` — measures wrapped height; `isCursorVisible()` — viewport clipping. |
99-
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` — compact header line; `buildSummaryFull()` — detailed counts; `buildSelectionSummary()` — "N files selected" footer. |
100-
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` — produces the `FilterStats` object (visible count, total count, active filter string) used by the TUI status bar. |
101-
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` — marks all visible rows as selected; `applySelectNone()` — deselects all. |
102-
| **Syntax highlighter** | `src/render/highlight.ts` | `highlightFragment()` — maps file extension to a language token ruleset and applies ANSI escape sequences. Falls back to plain text for unknown extensions. |
103-
| **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |
107+
| Component | Source file | Key exports |
108+
| ------------------------ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
109+
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` — filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
110+
| **Team grouping** | `src/group.ts` | `groupByTeamPrefix()` — groups `RepoGroup[]` into `TeamSection[]` keyed by team slug; `flattenTeamSections()` — converts back to a flat list for the TUI row builder. |
111+
| **Row builder** | `src/render/rows.ts` | `buildRows()` — converts `RepoGroup[]` into `Row[]` filtered by the active target (path / content / repo); `rowTerminalLines()` — measures wrapped height; `isCursorVisible()` — viewport clipping. |
112+
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` — compact header line; `buildSummaryFull()` — detailed counts; `buildSelectionSummary()` — "N files selected" footer. |
113+
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` — produces the `FilterStats` object (visible repos, files, matches) used by the TUI filter bar live counter. |
114+
| **Pattern matchers** | `src/render/filter-match.ts` | `makeExtractMatcher()` — builds a case-insensitive substring or RegExp test function for path or content targets; `makeRepoMatcher()` — wraps the same logic for repo-name matching. |
115+
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` — marks all visible rows as selected (respects filter target); `applySelectNone()` — deselects all visible rows. |
116+
| **Syntax highlighter** | `src/render/highlight.ts` | `highlightFragment()` — maps file extension to a language token ruleset and applies ANSI escape sequences. Falls back to plain text for unknown extensions. |
117+
| **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |
104118

105119
## Design principles
106120

107121
- **No I/O.** Every component in this layer is a pure function: given the same inputs it always returns the same outputs. This makes them straightforward to test with Bun's built-in test runner.
108122
- **Single responsibility.** Each component owns exactly one concern (rows, summary, selection, …). The TUI composes them at render time rather than duplicating logic.
109-
- **`types.ts` as the contract.** All components share the interfaces defined in `src/types.ts` (`TextMatchSegment`, `TextMatch`, `CodeMatch`, `RepoGroup`, `Row`, `TeamSection`, `OutputFormat`, `OutputType`). Changes to these types require updating all components.
123+
- **`types.ts` as the contract.** All components share the interfaces defined in `src/types.ts` (`TextMatchSegment`, `TextMatch`, `CodeMatch`, `RepoGroup`, `Row`, `TeamSection`, `OutputFormat`, `OutputType`, `FilterTarget`). Changes to these types require updating all components.
110124
- **`render.ts` as façade.** External consumers import from `src/render.ts`, which re-exports all symbols from the `src/render/` sub-modules plus the top-level `renderGroups()` and `renderHelpOverlay()` functions.

docs/blog/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Full release notes and changelogs are always available on
1111

1212
| Release | Highlights |
1313
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
14+
| [v1.5.0](./release-v1-5-0) | Advanced filter targets (content/path/repo), regex mode, word-jump shortcuts, scroll accuracy fix |
1415
| [v1.4.0](./release-v1-4-0) | TUI visual overhaul, violet branding, demo animation, SECURITY / Code of Conduct, README improvements |
1516
| [v1.3.0](./release-v1-3-0) | Richer upgrade output, update-available notice, colorized `--help`, deep doc links, What's New blog |
1617
| [v1.0.0](./release-v1-0-0) | Initial public release — interactive TUI, per-repo aggregation, markdown / JSON output |

docs/blog/release-v1-5-0.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
title: "What's new in v1.5.0"
3+
description: "Advanced filter targets (content, path, repo), regex mode in the filter bar, word-jump shortcuts, and several TUI accuracy fixes."
4+
date: 2026-03-01
5+
---
6+
7+
# What's new in github-code-search v1.5.0
8+
9+
> Full release notes: <https://github.com/fulll/github-code-search/releases/tag/v1.5.0>
10+
11+
## Highlights
12+
13+
### Advanced filter targets
14+
15+
The filter bar (press `f`) can now search across three different targets. Outside filter mode, press **`t`** to cycle targets; inside the filter bar, use **Shift+Tab**:
16+
17+
| Badge | What is filtered |
18+
| ----------- | ------------------------------------------ |
19+
| `[path]` | File path of each result (default) |
20+
| `[content]` | Code snippet text inside each extract |
21+
| `[repo]` | Repository name (short or `org/repo` form) |
22+
23+
The active target badge is always visible in the filter bar, including in the default `[path]` mode, so you always know what you are filtering against.
24+
25+
### Regex mode
26+
27+
Press **Tab** while the filter bar is open to toggle regex mode. When active, the filter input is interpreted as a regular expression — the badge shows `[…·regex]` to make it obvious. Invalid patterns match nothing gracefully (zero visible rows, no crash).
28+
29+
### Word-jump shortcuts in the filter bar
30+
31+
Three equivalent key combinations now move the cursor one word at a time:
32+
33+
- **⌥←/→** (macOS Option+Arrow — the most natural shortcut on macOS terminals)
34+
- **Ctrl+←/→** (common on Linux/Windows terminals)
35+
- **Alt+b / Alt+f** (readline-style mnemonics)
36+
37+
**⌥⌫** (Option+Backspace) / **Ctrl+W** delete the word before the cursor, as before.
38+
39+
### Accurate scroll position
40+
41+
The viewport used by the scroll engine now accounts for the filter bar height (0–2 lines depending on the active filter mode and badge visibility). Previously, the static `termHeight − 6` estimate could cause the cursor to stop scrolling before it was actually visible. The fix ensures `isCursorVisible` uses exactly the same window as `renderGroups`.
42+
43+
### Search-match highlighting on the cursor extract row
44+
45+
When the cursor is positioned on an extract row, the file path now shows the search-match highlight (yellow on magenta background) — consistent with how the highlighted repo row already behaved.
46+
47+
---
48+
49+
## Upgrade
50+
51+
```bash
52+
github-code-search upgrade
53+
```
54+
55+
Or grab the latest binary directly from the
56+
[GitHub Releases page](https://github.com/fulll/github-code-search/releases/tag/v1.5.0).

0 commit comments

Comments
 (0)