Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ Release notes and changelogs are published on the **[project blog](https://fulll

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

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

> For the full list of commits between releases, see the
> [GitHub Releases page](https://github.com/fulll/github-code-search/releases).
Binary file modified demo/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 20 additions & 31 deletions demo/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -36,52 +36,41 @@ Enter
# Wait for results to load (API call + TUI render)
Sleep 4s

# ── Navigate down a few rows ──────────────────────────────────────────────────
Down
Sleep 300ms
Down
Sleep 300ms
# ── Fold all repos — Z gives a compact bird's-eye view ────────────────────────
Type "Z"
Sleep 800ms

# ── Toggle selection off on current extract ───────────────────────────────────
Space
Sleep 400ms
# ── Unfold all repos — Z again expands everything ─────────────────────────────
Type "Z"
Sleep 800ms

# ── Filter by file path (default target) ──────────────────────────────────────
# ── Filter by file path to focus on YAML files ────────────────────────────────
Type "f"
Sleep 300ms
Type ".yml"
Sleep 600ms
Enter
Sleep 500ms

# ── Select all visible (filtered) rows ────────────────────────────────────────
# ── Jump to top with gg ───────────────────────────────────────────────────────
Type "g"
Sleep 150ms
Type "g"
Sleep 500ms

# ── Select all visible (filtered) results ─────────────────────────────────────
Type "a"
Sleep 500ms

# ── Reset filter ──────────────────────────────────────────────────────────────
Type "r"
# ── Jump to bottom with G to inspect the last match ───────────────────────────
Type "G"
Sleep 600ms

# ── Switch to repo filter mode and filter by repo name ────────────────────────
# Press t twice: path → content → repo
Type "t"
Sleep 300ms
Type "t"
Sleep 400ms

# Enter filter mode and type a repo name fragment
Type "f"
Sleep 300ms
Type "toolkit"
Sleep 700ms
Enter
Sleep 500ms

# Select all repos matching the filter
Type "a"
Sleep 500ms
# ── Open the focused file directly in the browser ─────────────────────────────
Type "o"
Sleep 600ms

# ── Reset and confirm ─────────────────────────────────────────────────────────
# ── Reset filter and confirm selection ────────────────────────────────────────
Type "r"
Sleep 600ms
Enter
Expand Down
1 change: 1 addition & 0 deletions docs/blog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Full release notes and changelogs are always available on

| Release | Highlights |
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
| [v1.6.0](./release-v1-6-0) | Power navigation: global fold/unfold (`Z`), Vim `gg`/`G` jumps, paged scroll, open-in-browser (`o`) |
| [v1.5.0](./release-v1-5-0) | Advanced filter targets (content/path/repo), regex mode, word-jump shortcuts, scroll accuracy fix |
| [v1.4.0](./release-v1-4-0) | TUI visual overhaul, violet branding, demo animation, SECURITY / Code of Conduct, README improvements |
| [v1.3.0](./release-v1-3-0) | Richer upgrade output, update-available notice, colorized `--help`, deep doc links, What's New blog |
Expand Down
64 changes: 64 additions & 0 deletions docs/blog/release-v1-6-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: "What's new in v1.6.0"
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."
date: 2026-03-02
---

# What's new in github-code-search v1.6.0

> Full release notes: <https://github.com/fulll/github-code-search/releases/tag/v1.6.0>

## Highlights

### Global fold / unfold — `Z`

Press **`Z`** to collapse every repository in one keystroke. Press `Z` again to expand them all at once.

The logic is straightforward:

- If at least one repo is currently expanded, `Z` folds everything.
- If every repo is already folded, `Z` unfolds everything.

The cursor tracks the repository it was positioned on before the fold, so you never lose your place in the list.

### Vim-style top / bottom navigation — `gg` and `G`

| Shortcut | Action |
| -------- | ---------------------------- |
| `gg` | Jump to the **first** result |
| `G` | Jump to the **last** result |

`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.

### Paged scrolling — `Page Up`/`Page Down` and `Ctrl+U`/`Ctrl+D`

Scroll through large result sets without holding an arrow key:

| Shortcut | Action |
| ---------------------- | -------------------- |
| `Page Up` / `Ctrl+U` | Scroll up one page |
| `Page Down` / `Ctrl+D` | Scroll down one page |

Page size is computed from the actual terminal height, so the jump covers exactly what is visible on screen.

### Open-in-browser — `o`

Press **`o`** on any focused row to open the corresponding page directly in your default browser — no copy-paste required.

| Row type | Opens |
| ----------- | -------------------------------- |
| Repo row | The GitHub repository page |
| Extract row | The exact file in the repository |

Works on macOS (`open`), Linux (`xdg-open`), and Windows (`cmd /c start`).

---

## Upgrade

```bash
github-code-search upgrade
```

Or grab the latest binary directly from the
[GitHub Releases page](https://github.com/fulll/github-code-search/releases/tag/v1.6.0).
20 changes: 13 additions & 7 deletions docs/reference/keyboard-shortcuts.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
# Keyboard shortcuts

All shortcuts are active in the interactive TUI. Keys are **case-sensitive** and must be typed in lowercase.
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.

## Navigation

| Key | Action |
| --------- | ------------------------------------- |
| `↑` / `k` | Move cursor up (repos and extracts) |
| `↓` / `j` | Move cursor down (repos and extracts) |
| `←` | Fold the repo under the cursor |
| `→` | Unfold the repo under the cursor |
| Key | Action |
| ---------------------- | ------------------------------------------------------------------------------------------ |
| `↑` / `k` | Move cursor up (repos and extracts) |
| `↓` / `j` | Move cursor down (repos and extracts) |
| `←` | Fold the repo under the cursor |
| `→` | Unfold the repo under the cursor |
| `Z` | **Global fold / unfold** — fold all repos if any is unfolded; unfold all if all are folded |
| `gg` | Jump to the **top** (first result) |
| `G` | Jump to the **bottom** (last result) |
| `Page Up` / `Ctrl+U` | Scroll up one full page |
| `Page Down` / `Ctrl+D` | Scroll down one full page |

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

Expand All @@ -20,6 +25,7 @@ Section header rows (shown when `--group-by-team-prefix` is active) are skipped
| `Space` | Toggle selection on the current repo or extract. On a repo row: cascades to all its extracts. |
| `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. |
| `n` | Select **none**. Same context rules as `a`. Respects active filters. |
| `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. |

## Filtering

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "github-code-search",
"version": "1.5.0",
"version": "1.6.0",
"description": "Interactive GitHub code search with per-repo aggregation",
"keywords": [
"bun",
Expand Down
42 changes: 42 additions & 0 deletions src/render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,37 @@ describe("renderHelpOverlay", () => {
expect(stripped).toContain("Filter mode:");
});

it("documents the Z global fold/unfold shortcut", () => {
const out = renderHelpOverlay();
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
expect(stripped).toContain("Z");
expect(stripped).toContain("fold / unfold all repos");
});

it("documents the o open-in-browser shortcut", () => {
const out = renderHelpOverlay();
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
expect(stripped).toContain("open in browser");
});

it("documents gg/G fast navigation shortcuts", () => {
const out = renderHelpOverlay();
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
expect(stripped).toContain("gg");
expect(stripped).toContain("jump to top");
expect(stripped).toContain("G");
expect(stripped).toContain("jump to bottom");
});

it("documents Page Up/Down fast navigation shortcuts", () => {
const out = renderHelpOverlay();
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
expect(stripped).toContain("PgUp");
expect(stripped).toContain("PgDn");
expect(stripped).toContain("page up");
expect(stripped).toContain("page down");
});

it("is returned by renderGroups when showHelp=true", () => {
const groups = [makeGroup("org/repo", ["a.ts"])];
const rows = buildRows(groups);
Expand Down Expand Up @@ -968,6 +999,17 @@ describe("renderGroups filter opts", () => {
expect(stripped).not.toContain("Filter:");
});

it("status bar hint line includes all navigation hint shortcuts", () => {
const groups = [makeGroup("org/repo", ["a.ts"])];
const rows = buildRows(groups);
const out = renderGroups(groups, 0, rows, 40, 0, "q", "org", {});
const stripped = out.replace(/\x1b\[[0-9;]*m/g, "");
expect(stripped).toContain("Z fold-all");
expect(stripped).toContain("o open");
expect(stripped).toContain("gg/G top/bot");
expect(stripped).toContain("PgUp/Dn page");
});

it("shows mode badge [content] when filterTarget=content", () => {
const groups = [makeGroup("org/repo", ["a.ts"], false, true)];
const rows = buildRows(groups, "code", "content");
Expand Down
6 changes: 5 additions & 1 deletion src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export function renderHelpOverlay(): string {
bar,
` ${pc.yellow("↑")} / ${pc.yellow("k")} navigate up ${pc.yellow("↓")} / ${pc.yellow("j")} navigate down`,
` ${pc.yellow("←")} fold repo ${pc.yellow("→")} unfold repo`,
` ${pc.yellow("Z")} fold / unfold all repos`,
` ${pc.yellow("gg")} jump to top ${pc.yellow("G")} jump to bottom`,
` ${pc.yellow("PgUp")} / ${pc.yellow("Ctrl+U")} page up ${pc.yellow("PgDn")} / ${pc.yellow("Ctrl+D")} page down`,
` ${pc.yellow("Space")} toggle selection ${pc.yellow("Enter")} confirm & output`,
` ${pc.yellow("a")} select all ${pc.yellow("n")} select none`,
` ${pc.dim("(respects active filter)")}`,
` ${pc.yellow("o")} open in browser ${pc.dim("(repo row → repo page, extract row → file)")}`,
` ${pc.yellow("f")} enter filter mode ${pc.yellow("r")} reset filter`,
` ${pc.yellow("t")} cycle filter target ${pc.dim("(path → content → repo)")}`,
` ${pc.yellow("h")} / ${pc.yellow("?")} toggle this help ${pc.yellow("q")} / Ctrl+C quit`,
Expand Down Expand Up @@ -297,7 +301,7 @@ export function renderGroups(

lines.push(
pc.dim(
"← / → fold/unfold ↑ / ↓ navigate spc select a all n none f filter t target h help ↵ confirm q quit\n",
"← / → 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",
),
);

Expand Down
Loading