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
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ 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.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.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).
15 changes: 11 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,24 @@ bun install
github-code-search.ts # CLI entry point (Commander subcommands: query, upgrade)
build.ts # Build script (compiles the standalone binary)
src/
types.ts # Shared TypeScript types
api.ts # GitHub REST API client
types.ts # Shared TypeScript types (TextMatchSegment, CodeMatch, RepoGroup, Row, FilterTarget, …)
api.ts # GitHub REST API client (search, team listing)
api-utils.ts # Shared retry (fetchWithRetry) and pagination (paginatedFetch) helpers
api-utils.test.ts # Unit tests for api-utils.ts
api.test.ts # Unit tests for api.ts
cache.ts # Disk cache for the team list (24 h TTL)
cache.test.ts # Unit tests for cache.ts
aggregate.ts # Result grouping and filtering logic
aggregate.test.ts # Unit tests for aggregate.ts
render.ts # FaΓ§ade: re-exports sub-modules + TUI renderGroups/renderHelpOverlay
render.test.ts # Unit tests for render.ts (rows, filter, selection, rendering)
render/
highlight.ts # Syntax highlighting (language detection, token rules, highlightFragment)
highlight.test.ts # Unit tests for highlight.ts (per-language tokenizer coverage)
filter.ts # Filter helpers (FilterStats, buildFilterStats)
rows.ts # Row navigation (buildRows, rowTerminalLines, isCursorVisible)
filter.ts # Filter stats (FilterStats, buildFilterStats)
filter-match.ts # Pure pattern matchers (makeExtractMatcher, makeRepoMatcher)
filter-match.test.ts # Unit tests for filter-match.ts
rows.ts # Row builder (buildRows, rowTerminalLines, isCursorVisible)
summary.ts # Stats labels (buildSummary, buildSummaryFull, buildSelectionSummary)
selection.ts # Selection mutations (applySelectAll, applySelectNone)
output.ts # Text (markdown) and JSON output formatters
Expand Down
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.
27 changes: 23 additions & 4 deletions demo/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,12 @@ Down
Sleep 300ms
Down
Sleep 300ms
Down
Sleep 300ms

# ── Toggle selection off on current extract ───────────────────────────────────
Space
Sleep 400ms

# ── Enter filter mode to exclude .ts files ────────────────────────────────────
# ── Filter by file path (default target) ──────────────────────────────────────
Type "f"
Sleep 300ms
Type ".yml"
Expand All @@ -64,7 +62,28 @@ Sleep 500ms
Type "r"
Sleep 600ms

# ── Confirm and print output ──────────────────────────────────────────────────
# ── 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

# ── Reset and confirm ─────────────────────────────────────────────────────────
Type "r"
Sleep 600ms
Enter

Sleep 2s
42 changes: 28 additions & 14 deletions docs/architecture/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ C4Component

## 3b β€” TUI render layer

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

```mermaid
%%{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}"}}%%
Expand All @@ -56,10 +58,11 @@ C4Component
Container_Boundary(render, "src/render/ β€” pure functions") {
Component(rows, "Row builder", "src/render/rows.ts", "buildRows()<br/>rowTerminalLines()<br/>isCursorVisible()")
Component(summary, "Summary builder", "src/render/summary.ts", "buildSummary()<br/>buildSummaryFull()<br/>buildSelectionSummary()")
Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()<br/>visible vs total rows")
Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()<br/>FilterStats β€” visible/hidden counts")
Component(selection, "Selection helpers", "src/render/selection.ts", "applySelectAll()<br/>applySelectNone()")
Component(highlight, "Syntax highlighter", "src/render/highlight.ts", "highlightFragment()<br/>ANSI token colouring")
Component(outputFn, "Output formatter", "src/output.ts", "buildOutput()<br/>markdown or JSON")
Component(filterMatch, "Pattern matchers", "src/render/filter-match.ts", "makeExtractMatcher()<br/>makeRepoMatcher()")
}

Rel(tui, rows, "Build terminal<br/>rows")
Expand All @@ -80,8 +83,18 @@ C4Component
Rel(tui, outputFn, "Format<br/>on Enter")
UpdateRelStyle(tui, outputFn, $offsetX="17", $offsetY="160")

Rel(rows, filterMatch, "Uses pattern<br/>matchers")
UpdateRelStyle(rows, filterMatch, $offsetX="-5", $offsetY="-5")

Rel(filter, filterMatch, "Uses pattern<br/>matchers")
UpdateRelStyle(filter, filterMatch, $offsetX="45", $offsetY="-5")

Rel(selection, filterMatch, "Uses pattern<br/>matchers")
UpdateRelStyle(selection, filterMatch, $offsetX="165", $offsetY="-25")

UpdateElementStyle(tui, $bgColor="#FFCC33", $borderColor="#0000CC", $fontColor="#000000")
UpdateElementStyle(rows, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(filterMatch, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(summary, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(filter, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(selection, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
Expand All @@ -91,20 +104,21 @@ C4Component

## Component descriptions

| Component | Source file | Key exports |
| ------------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` β€” filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
| **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. |
| **Row builder** | `src/render/rows.ts` | `buildRows()` β€” converts `RepoGroup[]` into `Row[]` with expanded/collapsed state; `rowTerminalLines()` β€” measures wrapped height; `isCursorVisible()` β€” viewport clipping. |
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` β€” compact header line; `buildSummaryFull()` β€” detailed counts; `buildSelectionSummary()` β€” "N files selected" footer. |
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` β€” produces the `FilterStats` object (visible count, total count, active filter string) used by the TUI status bar. |
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` β€” marks all visible rows as selected; `applySelectNone()` β€” deselects all. |
| **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. |
| **Output formatter** | `src/output.ts` | `buildOutput()` β€” entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |
| Component | Source file | Key exports |
| ------------------------ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` β€” filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
| **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. |
| **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. |
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` β€” compact header line; `buildSummaryFull()` β€” detailed counts; `buildSelectionSummary()` β€” "N files selected" footer. |
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` β€” produces the `FilterStats` object (visible repos, files, matches) used by the TUI filter bar live counter. |
| **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. |
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` β€” marks all visible rows as selected (respects filter target); `applySelectNone()` β€” deselects all visible rows. |
| **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. |
| **Output formatter** | `src/output.ts` | `buildOutput()` β€” entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |

## Design principles

- **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.
- **Single responsibility.** Each component owns exactly one concern (rows, summary, selection, …). The TUI composes them at render time rather than duplicating logic.
- **`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.
- **`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.
- **`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.
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.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 |
| [v1.0.0](./release-v1-0-0) | Initial public release β€” interactive TUI, per-repo aggregation, markdown / JSON output |
Expand Down
56 changes: 56 additions & 0 deletions docs/blog/release-v1-5-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: "What's new in v1.5.0"
description: "Advanced filter targets (content, path, repo), regex mode in the filter bar, word-jump shortcuts, and several TUI accuracy fixes."
date: 2026-03-01
---

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

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

## Highlights

### Advanced filter targets

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**:

| Badge | What is filtered |
| ----------- | ------------------------------------------ |
| `[path]` | File path of each result (default) |
| `[content]` | Code snippet text inside each extract |
| `[repo]` | Repository name (short or `org/repo` form) |

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.

### Regex mode

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

### Word-jump shortcuts in the filter bar

Three equivalent key combinations now move the cursor one word at a time:

- **βŒ₯←/β†’** (macOS Option+Arrow β€” the most natural shortcut on macOS terminals)
- **Ctrl+←/β†’** (common on Linux/Windows terminals)
- **Alt+b / Alt+f** (readline-style mnemonics)

**βŒ₯⌫** (Option+Backspace) / **Ctrl+W** delete the word before the cursor, as before.

### Accurate scroll position

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

### Search-match highlighting on the cursor extract row

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.

---

## 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.5.0).
Loading