|
| 1 | +# Components (C4 L3) |
| 2 | + |
| 3 | +This diagram zooms into the **pure-function core** — the modules that contain all |
| 4 | +business logic with no side effects. Every component here is fully unit-tested and |
| 5 | +takes in data structures defined in `src/types.ts`; none of them perform I/O. |
| 6 | + |
| 7 | +The CLI parser (`github-code-search.ts`) and the TUI (`src/tui.ts`) call these |
| 8 | +components after fetching raw data from the API. |
| 9 | + |
| 10 | +```mermaid |
| 11 | +C4Component |
| 12 | + title Components — pure-function core |
| 13 | +
|
| 14 | + Container_Boundary(core, "Pure-function core") { |
| 15 | + Component(aggregate, "Filter & aggregation", "src/aggregate.ts", "applyFiltersAndExclusions() — applies --exclude-repositories and --exclude-extracts, normalises org-prefixed names") |
| 16 | + Component(group, "Team grouping", "src/group.ts", "groupByTeamPrefix() / flattenTeamSections() — groups RepoGroups by team prefix, falls back to flat list") |
| 17 | + Component(rows, "Row builder", "src/render/rows.ts", "buildRows() — converts RepoGroups into terminal Row[], computes visibility and cursor position") |
| 18 | + Component(summary, "Summary builder", "src/render/summary.ts", "buildSummary() / buildSummaryFull() / buildSelectionSummary() — header and footer lines rendered in the TUI") |
| 19 | + Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats() — counts visible vs total rows for the status bar") |
| 20 | + Component(selection, "Selection helpers", "src/render/selection.ts", "applySelectAll() / applySelectNone() — bulk selection mutations on Row[]") |
| 21 | + Component(highlight, "Syntax highlighter", "src/render/highlight.ts", "highlight() — detects language from filename, applies token-level ANSI colouring") |
| 22 | + Component(outputFn, "Output formatter", "src/output.ts", "buildOutput() — serialises selected RepoGroup[] to markdown or JSON") |
| 23 | + } |
| 24 | +
|
| 25 | + Container(tui, "TUI", "src/tui.ts", "Calls render components on every redraw") |
| 26 | + Container(cli, "CLI parser", "github-code-search.ts", "Calls aggregate + group, then TUI or output directly") |
| 27 | +
|
| 28 | + Rel(cli, aggregate, "Filters raw CodeMatch[]") |
| 29 | + Rel(cli, group, "Groups into TeamSection[]") |
| 30 | + Rel(cli, outputFn, "Formats selection (non-interactive mode)") |
| 31 | + Rel(tui, rows, "Builds terminal rows") |
| 32 | + Rel(tui, summary, "Builds header / footer lines") |
| 33 | + Rel(tui, filter, "Builds filter status bar") |
| 34 | + Rel(tui, selection, "Applies select-all / none") |
| 35 | + Rel(tui, highlight, "Highlights code extracts") |
| 36 | + Rel(tui, outputFn, "Formats selection on Enter") |
| 37 | +``` |
| 38 | + |
| 39 | +## Component descriptions |
| 40 | + |
| 41 | +| Component | Source file | Key exports | |
| 42 | +| ------------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 43 | +| **Filter & aggregation** | `src/aggregate.ts` | `applyFiltersAndExclusions()` — filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. | |
| 44 | +| **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. | |
| 45 | +| **Row builder** | `src/render/rows.ts` | `buildRows()` — converts `RepoGroup[]` into `Row[]` with expanded/collapsed state; `rowTerminalLines()` — measures wrapped height; `isCursorVisible()` — viewport clipping. | |
| 46 | +| **Summary builder** | `src/render/summary.ts` | `buildSummary()` — compact header line; `buildSummaryFull()` — detailed counts; `buildSelectionSummary()` — "N files selected" footer. | |
| 47 | +| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` — produces the `FilterStats` object (visible count, total count, active filter string) used by the TUI status bar. | |
| 48 | +| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` — marks all visible rows as selected; `applySelectNone()` — deselects all. | |
| 49 | +| **Syntax highlighter** | `src/render/highlight.ts` | `highlight()` — maps file extension to a language token ruleset and applies ANSI escape sequences. Falls back to plain text for unknown extensions. | |
| 50 | +| **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--output-type json` serialisation of the confirmed selection. | |
| 51 | + |
| 52 | +## Design principles |
| 53 | + |
| 54 | +- **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. |
| 55 | +- **Single responsibility.** Each component owns exactly one concern (rows, summary, selection, …). The TUI composes them at render time rather than duplicating logic. |
| 56 | +- **`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. |
| 57 | +- **`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. |
0 commit comments