# TUI Reference [← Docs index](./README.md) · [Architecture](./architecture.md) The TUI is built with **React + [Ink](https://github.com/vadimdemedes/ink)**. It is presentation only: **no component talks to a forge or LLM directly — they call engine functions.** All color comes from `theme.tsx` (`ACCENT` / `DIM`); user-facing text is short, lowercase-ish, no exclamation marks; no emoji in the header (the logo is ASCII). The bare `codeturtle` command renders `` (see [`cli/index.ts`](../src/cli/index.ts)). ## Component tree ``` App setup-on-start router ├── Setup first-run wizard (model → GitHub → GitLab) │ └── ModelPicker provider → model → key └── Dashboard paste box + reviews list + events feed ├── ModelPicker (overlay: change model) └── RepoPicker (overlay: manage watched repos) ``` ## `App.tsx` The router. On mount it computes `needsSetup` = no forge configured **or** no reviewer key (and not a localhost endpoint). If so it shows ``, otherwise ``. > Setup runs **on start only** — first run or after a reset. There's no mid-session re-entry into > the wizard; the Dashboard handles in-session changes via overlays. ## `Setup.tsx` The first-run wizard. Steps: `model → connect → (githubDevice | githubKey | gitlabKey) → githubRepo → githubRepoManual → connected → done`. You **authenticate once**: the `connect` step is a single menu, and after one successful connection you land on the `connected` hub. There is **no forced GitLab gate** — the old `model → github → … → gitlab` two-gate flow was the source of the "can't skip GitLab" bug. - **Model** — delegates to ``, writes the `reviewer` config section. - **Connect** — one menu listing every auth method: _Sign in with GitHub (OAuth)_ (device flow; needs `GITHUB_CLIENT_ID`), _gh CLI session_ (`ghCliToken()`), _paste a GitHub PAT_, _connect GitLab (token)_. The last item is context-aware: `Skip — set up later` when nothing is connected yet, `← Back` once a forge is connected. GitHub tokens validate against `GET /user` (`backend: "mcp"`); GitLab against `{url}/api/v4/user` (`backend: "rest"`). - **GitHub repo** — fetches your repos (`/user/repos?sort=pushed`) and lets you pick one (or type it) to seed `watch.targets` as `github:owner/repo`, then → `connected`. - **Connected** — the finish hub. The default item is **Finish** (one Enter → dashboard); the second is **Connect another forge** (→ back to `connect`), so multi-forge stays possible without a reset. `isConfigured()` (exported) is the predicate `App` uses: true if any forge token exists in the store or env. ## `ModelPicker.tsx` opencode-style model selection. Steps: `provider → detecting → model → customModel → customUrl → key`. Reads the registry from [`providers.ts`](./engine-reference.md#providersts). - Picking a **local** provider (Ollama / LM Studio) runs `detectLocalModels()` and lists what's live; if none are detected you can still type a model id. Local providers need no API key. - "Custom endpoint" prompts for a base URL, then a model id, then a key. - Calls back with `{ provider, baseUrl, model, apiKey }`. ## `RepoPicker.tsx` Manage the auto-review target list. Steps: `forge → loading → pick → manual → remove`. - Lists your GitHub (`/user/repos`) or GitLab (`/projects?membership=true`) repos. - Adds targets as `forge:repo` into `watch.targets` (deduped), preserving the `watch.interval` (default 30s). Also supports removing a watched repo. ## `Dashboard.tsx` The main screen. Responsibilities: - **Paste box** — `startPastedReview()` parses the link ([`prLink`](./engine-reference.md#prlinkts)), fetches the head SHA, and calls [`runReview`](./engine-reference.md#pipelinets). Reviews show as rows (running → done / failed). - **Auto-review** — `startWatch(targets)` drives the [`watch`](./engine-reference.md#watchts) loop with an `AbortController`; `onJob` adds a "reviewing…" row. A shared `watchLog` callback feeds both the events feed and the per-PR status (it pattern-matches the pipeline's `pr=… found=… kept=…` / `review failed pr=…` log lines). - **Session selection** — on open, asks which configured repo to monitor for this session (or all, or a new one, or skip). - **Refresh** — both PR lists refetch in place (no loading wipe, selection kept): automatically on the watch cadence (`watch.interval`, default 30s) so raised, closed and merged PRs stay current without keypress, and instantly with `R` for the impatient path. Auto-refresh failures are silent (the list keeps its last good state); manual `R` failures land in the events feed. - **Overlays** (`Esc` opens settings): change model (``), manage repos (``), reset all config (with a red confirm), or quit. All abort the watcher on exit. ## `ReviewViewer.tsx` Browsing findings and code context of a posted review. Key elements: - **Findings List**: displays the list of findings on the PR. Navigate using `j`/`k` or `↓`/`↑`. - **Code Context**: shows the code around the finding. Press `Tab` to switch focus to the code pane, and use `j`/`k` or `↓`/`↑` to scroll the codebase viewport, marked with a green focus arrow `❯❯`. - **Summary**: toggles the overall PR review summary (`S`). - **File Filter**: cycles through files with findings (`F`). ## `theme.tsx` The shared look: - `ACCENT` (`#3498db`) and `DIM` (`gray`) — **the only colors components may use.** - `Header` — the ASCII "TURTLE" logo block + tagline + optional subtitle. **No emoji here.** - `KeyHint` — renders `[key, label]` pairs as a footer hint bar. --- [← Docs index](./README.md) · Next: [Configuration](./configuration.md)