Skip to content

Commit 8454f39

Browse files
authored
Merge pull request #73 from fulll/feat/tui-navigation-result-management
feat: TUI — Navigation & result management (EPIC #66) — v1.6.0
2 parents 4248228 + 650ba25 commit 8454f39

10 files changed

Lines changed: 290 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ 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.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 |
7+
| Version | Blog post |
8+
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
9+
| [v1.6.0](https://fulll.github.io/github-code-search/blog/release-v1-6-0) | Power navigation: global fold/unfold, gg/G top/bottom, paged scroll, open-in-browser |
10+
| [v1.5.0](https://fulll.github.io/github-code-search/blog/release-v1-5-0) | Advanced filter targets, regex mode, word-jump, scroll fix |
11+
| [v1.4.0](https://fulll.github.io/github-code-search/blog/release-v1-4-0) | TUI visual overhaul, community files, demo animation |
12+
| [v1.3.0](https://fulll.github.io/github-code-search/blog/release-v1-3-0) | Team-prefix grouping, replay command, JSON output |
13+
| [v1.0.0](https://fulll.github.io/github-code-search/blog/release-v1-0-0) | Initial release |
1314

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

demo/demo.gif

-69.6 KB
Loading

demo/demo.tape

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,52 +36,41 @@ Enter
3636
# Wait for results to load (API call + TUI render)
3737
Sleep 4s
3838

39-
# ── Navigate down a few rows ──────────────────────────────────────────────────
40-
Down
41-
Sleep 300ms
42-
Down
43-
Sleep 300ms
39+
# ── Fold all repos — Z gives a compact bird's-eye view ────────────────────────
40+
Type "Z"
41+
Sleep 800ms
4442

45-
# ── Toggle selection off on current extract ───────────────────────────────────
46-
Space
47-
Sleep 400ms
43+
# ── Unfold all repos — Z again expands everything ─────────────────────────────
44+
Type "Z"
45+
Sleep 800ms
4846

49-
# ── Filter by file path (default target) ──────────────────────────────────────
47+
# ── Filter by file path to focus on YAML files ────────────────────────────────
5048
Type "f"
5149
Sleep 300ms
5250
Type ".yml"
5351
Sleep 600ms
5452
Enter
5553
Sleep 500ms
5654

57-
# ── Select all visible (filtered) rows ────────────────────────────────────────
55+
# ── Jump to top with gg ───────────────────────────────────────────────────────
56+
Type "g"
57+
Sleep 150ms
58+
Type "g"
59+
Sleep 500ms
60+
61+
# ── Select all visible (filtered) results ─────────────────────────────────────
5862
Type "a"
5963
Sleep 500ms
6064

61-
# ── Reset filter ──────────────────────────────────────────────────────────────
62-
Type "r"
65+
# ── Jump to bottom with G to inspect the last match ───────────────────────────
66+
Type "G"
6367
Sleep 600ms
6468

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
69+
# ── Open the focused file directly in the browser ─────────────────────────────
70+
Type "o"
71+
Sleep 600ms
8372

84-
# ── Reset and confirm ─────────────────────────────────────────────────────────
73+
# ── Reset filter and confirm selection ────────────────────────────────────────
8574
Type "r"
8675
Sleep 600ms
8776
Enter

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.6.0](./release-v1-6-0) | Power navigation: global fold/unfold (`Z`), Vim `gg`/`G` jumps, paged scroll, open-in-browser (`o`) |
1415
| [v1.5.0](./release-v1-5-0) | Advanced filter targets (content/path/repo), regex mode, word-jump shortcuts, scroll accuracy fix |
1516
| [v1.4.0](./release-v1-4-0) | TUI visual overhaul, violet branding, demo animation, SECURITY / Code of Conduct, README improvements |
1617
| [v1.3.0](./release-v1-3-0) | Richer upgrade output, update-available notice, colorized `--help`, deep doc links, What's New blog |

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: "What's new in v1.6.0"
3+
description: "Power navigation shortcuts: global fold/unfold, Vim-style gg/G top/bottom jumps, paged scrolling with Page Up/Down and Ctrl+U/D, and open-in-browser for any result."
4+
date: 2026-03-02
5+
---
6+
7+
# What's new in github-code-search v1.6.0
8+
9+
> Full release notes: <https://github.com/fulll/github-code-search/releases/tag/v1.6.0>
10+
11+
## Highlights
12+
13+
### Global fold / unfold — `Z`
14+
15+
Press **`Z`** to collapse every repository in one keystroke. Press `Z` again to expand them all at once.
16+
17+
The logic is straightforward:
18+
19+
- If at least one repo is currently expanded, `Z` folds everything.
20+
- If every repo is already folded, `Z` unfolds everything.
21+
22+
The cursor tracks the repository it was positioned on before the fold, so you never lose your place in the list.
23+
24+
### Vim-style top / bottom navigation — `gg` and `G`
25+
26+
| Shortcut | Action |
27+
| -------- | ---------------------------- |
28+
| `gg` | Jump to the **first** result |
29+
| `G` | Jump to the **last** result |
30+
31+
`gg` triggers on two consecutive `g` keypresses. Both shortcuts skip over section headers produced by `--group-by-team-prefix`, landing only on repo or extract rows.
32+
33+
### Paged scrolling — `Page Up`/`Page Down` and `Ctrl+U`/`Ctrl+D`
34+
35+
Scroll through large result sets without holding an arrow key:
36+
37+
| Shortcut | Action |
38+
| ---------------------- | -------------------- |
39+
| `Page Up` / `Ctrl+U` | Scroll up one page |
40+
| `Page Down` / `Ctrl+D` | Scroll down one page |
41+
42+
Page size is computed from the actual terminal height, so the jump covers exactly what is visible on screen.
43+
44+
### Open-in-browser — `o`
45+
46+
Press **`o`** on any focused row to open the corresponding page directly in your default browser — no copy-paste required.
47+
48+
| Row type | Opens |
49+
| ----------- | -------------------------------- |
50+
| Repo row | The GitHub repository page |
51+
| Extract row | The exact file in the repository |
52+
53+
Works on macOS (`open`), Linux (`xdg-open`), and Windows (`cmd /c start`).
54+
55+
---
56+
57+
## Upgrade
58+
59+
```bash
60+
github-code-search upgrade
61+
```
62+
63+
Or grab the latest binary directly from the
64+
[GitHub Releases page](https://github.com/fulll/github-code-search/releases/tag/v1.6.0).

docs/reference/keyboard-shortcuts.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# Keyboard shortcuts
22

3-
All shortcuts are active in the interactive TUI. Keys are **case-sensitive** and must be typed in lowercase.
3+
All shortcuts are active in the interactive TUI. Keys are **case-sensitive** — most use lowercase letters, but a few bindings (such as `Z` and `G`) require an uppercase letter.
44

55
## Navigation
66

7-
| Key | Action |
8-
| --------- | ------------------------------------- |
9-
| `` / `k` | Move cursor up (repos and extracts) |
10-
| `` / `j` | Move cursor down (repos and extracts) |
11-
| `` | Fold the repo under the cursor |
12-
| `` | Unfold the repo under the cursor |
7+
| Key | Action |
8+
| ---------------------- | ------------------------------------------------------------------------------------------ |
9+
| `` / `k` | Move cursor up (repos and extracts) |
10+
| `` / `j` | Move cursor down (repos and extracts) |
11+
| `` | Fold the repo under the cursor |
12+
| `` | Unfold the repo under the cursor |
13+
| `Z` | **Global fold / unfold** — fold all repos if any is unfolded; unfold all if all are folded |
14+
| `gg` | Jump to the **top** (first result) |
15+
| `G` | Jump to the **bottom** (last result) |
16+
| `Page Up` / `Ctrl+U` | Scroll up one full page |
17+
| `Page Down` / `Ctrl+D` | Scroll down one full page |
1318

1419
Section header rows (shown when `--group-by-team-prefix` is active) are skipped automatically during navigation.
1520

@@ -20,6 +25,7 @@ Section header rows (shown when `--group-by-team-prefix` is active) are skipped
2025
| `Space` | Toggle selection on the current repo or extract. On a repo row: cascades to all its extracts. |
2126
| `a` | Select **all**. On a repo row: selects all repos and their extracts. On an extract row: selects all extracts in the current repo. Respects active filters. |
2227
| `n` | Select **none**. Same context rules as `a`. Respects active filters. |
28+
| `o` | **Open in browser** — opens the focused item in the default browser. On a repo row: opens the repository page. On an extract row: opens the file directly. |
2329

2430
## Filtering
2531

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "github-code-search",
3-
"version": "1.5.0",
3+
"version": "1.6.0",
44
"description": "Interactive GitHub code search with per-repo aggregation",
55
"keywords": [
66
"bun",

src/render.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,37 @@ describe("renderHelpOverlay", () => {
902902
expect(stripped).toContain("Filter mode:");
903903
});
904904

905+
it("documents the Z global fold/unfold shortcut", () => {
906+
const out = renderHelpOverlay();
907+
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
908+
expect(stripped).toContain("Z");
909+
expect(stripped).toContain("fold / unfold all repos");
910+
});
911+
912+
it("documents the o open-in-browser shortcut", () => {
913+
const out = renderHelpOverlay();
914+
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
915+
expect(stripped).toContain("open in browser");
916+
});
917+
918+
it("documents gg/G fast navigation shortcuts", () => {
919+
const out = renderHelpOverlay();
920+
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
921+
expect(stripped).toContain("gg");
922+
expect(stripped).toContain("jump to top");
923+
expect(stripped).toContain("G");
924+
expect(stripped).toContain("jump to bottom");
925+
});
926+
927+
it("documents Page Up/Down fast navigation shortcuts", () => {
928+
const out = renderHelpOverlay();
929+
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
930+
expect(stripped).toContain("PgUp");
931+
expect(stripped).toContain("PgDn");
932+
expect(stripped).toContain("page up");
933+
expect(stripped).toContain("page down");
934+
});
935+
905936
it("is returned by renderGroups when showHelp=true", () => {
906937
const groups = [makeGroup("org/repo", ["a.ts"])];
907938
const rows = buildRows(groups);
@@ -968,6 +999,17 @@ describe("renderGroups filter opts", () => {
968999
expect(stripped).not.toContain("Filter:");
9691000
});
9701001

1002+
it("status bar hint line includes all navigation hint shortcuts", () => {
1003+
const groups = [makeGroup("org/repo", ["a.ts"])];
1004+
const rows = buildRows(groups);
1005+
const out = renderGroups(groups, 0, rows, 40, 0, "q", "org", {});
1006+
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
1007+
expect(stripped).toContain("Z fold-all");
1008+
expect(stripped).toContain("o open");
1009+
expect(stripped).toContain("gg/G top/bot");
1010+
expect(stripped).toContain("PgUp/Dn page");
1011+
});
1012+
9711013
it("shows mode badge [content] when filterTarget=content", () => {
9721014
const groups = [makeGroup("org/repo", ["a.ts"], false, true)];
9731015
const rows = buildRows(groups, "code", "content");

src/render.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ export function renderHelpOverlay(): string {
3333
bar,
3434
` ${pc.yellow("↑")} / ${pc.yellow("k")} navigate up ${pc.yellow("↓")} / ${pc.yellow("j")} navigate down`,
3535
` ${pc.yellow("←")} fold repo ${pc.yellow("→")} unfold repo`,
36+
` ${pc.yellow("Z")} fold / unfold all repos`,
37+
` ${pc.yellow("gg")} jump to top ${pc.yellow("G")} jump to bottom`,
38+
` ${pc.yellow("PgUp")} / ${pc.yellow("Ctrl+U")} page up ${pc.yellow("PgDn")} / ${pc.yellow("Ctrl+D")} page down`,
3639
` ${pc.yellow("Space")} toggle selection ${pc.yellow("Enter")} confirm & output`,
3740
` ${pc.yellow("a")} select all ${pc.yellow("n")} select none`,
3841
` ${pc.dim("(respects active filter)")}`,
42+
` ${pc.yellow("o")} open in browser ${pc.dim("(repo row → repo page, extract row → file)")}`,
3943
` ${pc.yellow("f")} enter filter mode ${pc.yellow("r")} reset filter`,
4044
` ${pc.yellow("t")} cycle filter target ${pc.dim("(path → content → repo)")}`,
4145
` ${pc.yellow("h")} / ${pc.yellow("?")} toggle this help ${pc.yellow("q")} / Ctrl+C quit`,
@@ -297,7 +301,7 @@ export function renderGroups(
297301

298302
lines.push(
299303
pc.dim(
300-
"← / → fold/unfold ↑ / ↓ navigate spc select a all n none f filter t target h help ↵ confirm q quit\n",
304+
"← / → fold/unfold Z fold-all ↑ / ↓ navigate gg/G top/bot PgUp/Dn page spc select a all n none o open f filter t target h help ↵ confirm q quit\n",
301305
),
302306
);
303307

0 commit comments

Comments
 (0)