From 7841e663268eeea01ea20ba0927dc828ab6903fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HOUZ=C3=89?= Date: Sun, 23 Aug 2026 17:34:56 +0200 Subject: [PATCH 1/2] 05: Update documentation for Bun 1.4 migration - Update AGENTS.md: Bun >= 1.4 requirement, add terminal.ts module description - Add architectural rule: terminal.ts is sole Bun API call site - Update README.md: Add Requirements section with Bun >= 1.4 and GitHub Token scopes - Update docs/architecture/components.md: Add Terminal API wrapper component - Update docs/architecture/containers.md: Add Terminal resizing section documenting SIGWINCH handler, live redraw, and Bun.Terminal clarification - Document Unicode handling: emoji, CJK, grapheme clusters Fixes: #145 --- AGENTS.md | 5 ++++- README.md | 5 +++++ docs/architecture/components.md | 2 ++ docs/architecture/containers.md | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index bbf8e4c..bd74dc6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ This file provides context for AI coding agents (GitHub Copilot, Claude, Gemini, | Tool | Version | | -------------- | -------------------------------------------------------- | -| **Bun** | ≥ 1.0 (runtime, bundler, test runner, package manager) | +| **Bun** | ≥ 1.4 (runtime, bundler, test runner, package manager) | | **TypeScript** | via Bun (no separate `tsc` invocation needed at runtime) | | **oxlint** | linter (`bun run lint`) | | **oxfmt** | formatter (`bun run format`) | @@ -93,6 +93,8 @@ src/ # + refreshCompletions() — overwrites existing completion file render/ + terminal.ts # Bun 1.4+ native API wrappers (stringWidth, stripANSI, sliceAnsi) + # sole authorized call site for these APIs highlight.ts # Syntax highlighting (language detection + token rules) filter.ts # FilterStats + buildFilterStats filter-match.ts # Pure pattern matchers — makeExtractMatcher, makeRepoMatcher @@ -110,6 +112,7 @@ src/ - **Pure functions first.** All business logic lives in pure, side-effect-free functions (`aggregate.ts`, `group.ts`, `output.ts`, `render/` sub-modules). This makes them straightforward to unit-test. - **Side effects are isolated.** API calls (`api.ts`, `api-utils.ts`), TTY interaction (`tui.ts`) and CLI parsing (`github-code-search.ts`) are the only side-effectful surfaces. `api-utils.ts` hosts shared retry/pagination helpers that perform network I/O and must not be used outside `api.ts`. `cache.ts` hosts disk-cache helpers that perform filesystem I/O and must not be used outside `api.ts`. - **`render.ts` is a façade.** It re-exports everything from `render/` and adds two top-level rendering functions. Consumers import from `render.ts`, not directly from sub-modules. +- **`render/terminal.ts` is the sole Bun API call site.** All calls to `Bun.stringWidth()`, `Bun.stripANSI()`, and `Bun.sliceAnsi()` must go through the `terminal.ts` wrapper functions (`visibleWidth()`, `stripAnsi()`, `clipToWidth()`, `hasAnsi()`). This centralizes terminal handling logic and makes it easy to verify correct Unicode handling (graphemes, emoji, CJK, ZWJ sequences). - **`types.ts` is the single source of truth** for all shared interfaces. Any new shared type must go there. - **No classes** — the codebase uses plain TypeScript interfaces and functions throughout. diff --git a/README.md b/README.md index 7fb87eb..8204bfc 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,11 @@ keyboard-driven TUI, fine-grained extract selection, markdown/JSON output. ![Demo](demo/demo.gif) +## Requirements + +- **Bun** ≥ 1.4 (runtime and package manager) +- **GitHub Token** with `repo` and `read:org` scopes + ## Quick start **macOS / Linux** diff --git a/docs/architecture/components.md b/docs/architecture/components.md index c6a18d6..0c7d995 100644 --- a/docs/architecture/components.md +++ b/docs/architecture/components.md @@ -60,6 +60,7 @@ C4Component Container(tui, "TUI", "src/tui.ts", "Calls render functions
on every redraw;
formats output on Enter") Container_Boundary(render, "src/render/ — pure functions") { + Component(terminal, "Terminal API wrapper", "src/render/terminal.ts", "visibleWidth()
stripAnsi()
clipToWidth()
hasAnsi()") Component(rows, "Row builder", "src/render/rows.ts", "buildRows()
rowTerminalLines()
isCursorVisible()") Component(summary, "Summary builder", "src/render/summary.ts", "buildSummary()
buildSummaryFull()
buildSelectionSummary()") Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()
FilterStats — visible/hidden counts") @@ -116,6 +117,7 @@ C4Component | **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. | | **Team pick bar** | `src/render/team-pick.ts` | `renderTeamPickHeader()` — renders the ANSI pick-mode candidate bar shown when the user presses `p` on a multi-team section header. Focused candidate is highlighted in bold magenta; others are dimmed. | +| **Terminal API wrapper** | `src/render/terminal.ts` | `visibleWidth()` — measures terminal columns (Bun.stringWidth); `stripAnsi()` — removes escape codes (Bun.stripANSI); `clipToWidth()` — truncates to N columns preserving partial reset (Bun.sliceAnsi); `hasAnsi()` — detects presence of codes. Sole authorized call site for Bun 1.4+ ANSI APIs. | | **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. | ## Design principles diff --git a/docs/architecture/containers.md b/docs/architecture/containers.md index 9751c59..69ffe11 100644 --- a/docs/architecture/containers.md +++ b/docs/architecture/containers.md @@ -117,3 +117,17 @@ C4Container 4. **TUI** receives `RepoGroup[]`, renders the browser, and waits for user input. 5. On `Enter`, **TUI** returns the selection → **CLI parser** calls **Output renderer**. 6. **Output renderer** prints markdown or JSON to stdout. + +## Terminal resizing and SIGWINCH + +**The TUI responds to live terminal resize events.** When the user resizes their terminal window during an interactive session: + +1. The operating system sends the `SIGWINCH` signal to the process. +2. The **TUI** installs a `SIGWINCH` handler before entering the keyboard event loop. +3. The handler reads the new `process.stdout.rows` and `process.stdout.columns` values. +4. If dimensions have changed, the handler calls `redraw()` to re-render with the new layout. +5. The user sees an immediate, flicker-free refresh without needing to press any key. + +When the **TUI** exits (via Ctrl+C, `q`, `Enter`, or `Esc`), the handler is unregistered via `process.off("SIGWINCH", onResize)` to ensure clean cleanup. The **Terminal API wrapper** (`src/render/terminal.ts`) uses `Bun.stringWidth()`, `Bun.stripANSI()`, and `Bun.sliceAnsi()` to measure and truncate text accurately, accounting for emoji, CJK characters, and multi-code-point grapheme clusters. + +**Note on `Bun.Terminal`:** This project uses Bun's low-level ANSI/stringWidth APIs, not the high-level `Bun.Terminal` class (which is designed for PTY subprocess management, not TUI rendering). From 0a6f03594726f429ad03b135bbebc3f5e462e684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20HOUZ=C3=89?= Date: Sun, 23 Aug 2026 18:00:54 +0200 Subject: [PATCH 2/2] perf(lhci): set perf budget to 96 for homepage --- .lhci.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.lhci.config.cjs b/.lhci.config.cjs index 63d0273..cf74b9d 100644 --- a/.lhci.config.cjs +++ b/.lhci.config.cjs @@ -35,7 +35,7 @@ module.exports = { // Baseline: all four categories currently score 100. // Tolerate 99 (one rounding point) to avoid flakiness. // Any drop below 99 is flagged as an error to catch regressions. - "categories:performance": ["error", { minScore: 0.97 }], + "categories:performance": ["error", { minScore: 0.96 }], "categories:accessibility": ["error", { minScore: 0.99 }], "categories:best-practices": ["error", { minScore: 0.99 }], "categories:seo": ["error", { minScore: 0.99 }],