Expand description
diffler binary internals, exposed as a library so the integration tests can drive the full app (event pump + MCP server) headlessly. The public surface is the binary’s: nothing here is a stable API.
Modules§
- app
- Application state and event handling.
App::handleis a pure-ish state transition (no terminal IO) so the whole shell is unit-testable; rendering reads the state inui::draw. Per-screen state and handlers live in thestatus,log,diff,ci(runs + graph),ci_log, andprsubmodules. - ci
- Provider-agnostic CI run/job/log acquisition, plus the host glue that picks a
provider for the repo and maps a normalized
RunDetailonto the graph model. Adapters (providers/) implementForgeProviderover each forge (viagh/glab/curlthrough theCommandRunnerseam) and never touch the terminal. - clipboard
- System clipboard. Two mechanisms, used together for reach without native build deps (so the static musl binaries stay clean): an OSC52 escape sequence (written to the terminal by the main loop after a draw, never from rendering) which the terminal forwards even over ssh/tmux; and a best-effort pipe to the platform clipboard CLI, covering terminals that don’t honor OSC52.
- config
- Layered TOML configuration: defaults → global file → project file → CLI.
- editor
- $EDITOR integration. The TUI loop owns the terminal, so opening an
editor is a request:
Appqueues anEditorRequest, the main loop suspends the terminal, runs the subprocess, restores the screen, and hands the outcome back toApp::editor_finished. - event
- Terminal event pump: crossterm’s async stream plus a periodic tick,
multiplexed onto one channel. Kept thin: all decisions live in
App::handle, which is what the tests drive. - graph
- A navigable orthogonal node-graph component for ratatui, plus the host glue
mapping the app theme onto its palette. The component is IO-free (no terminal
setup, event loop, or network); the host builds a
Model(from CI, …), pushes it into aGraphView, renders it, and reacts toGraphActions. - keymap
- Key bindings: neogit-style defaults per screen, overridable from
[keys.*]config sections, with multi-key sequence resolution. - mcp
- Embedded MCP server: agents read review comments, answer them in place,
and long-poll for the human’s feedback. Tool handlers never touch the
review directly: every call is sent through the app event channel as an
McpRequestand answered byApp::handle_mcpon the main loop, so the app stays the single owner of all state (no locks). - proc
- Child processes diffler shells out to, kept away from the terminal.
- search
- Vim-style
/search. A pane feedsSearchits rows as(row index, text); the search holds the query, matches, and active match, and drives incremental highlight +n/Nnavigation. Matching is plain substring with smartcase: case-insensitive unless the query has an uppercase letter. - theme
- Color themes.
github-darkis the default;ui.themeselects a built-in, each pairing its palette with a matching syntax-highlight theme. - transient
- Magit/neogit-style transient menus: a top-level prefix key opens a transient whose own keys are pure leaves (immediate actions). The model is data, not closures, so resolution and the which-key panel layout stay pure and unit-testable; the app owns the live transient state and timer.
- tree
- The shared sidebar row vocabulary (
TreeRow/TreeNode) and pure directory-trie flattening, used by the diff sidebar and the status sections.visible_rowsturns a file list (repo-relative paths, in input order) into visible rows honoring fold state: directories before files at each level, input order preserved within a kind, folded directories hiding their subtree. It only ever emits Dir and File rows; Section rows are composed by the diff sidebar’s review layout on top of the same vocabulary. No rendering and no app state, so the navigation math is fully unit-testable. - ui
- Rendering.
drawnever computes review state; it readsApp(the diff view additionally fills its lazy highlight cache and follows the cursor with its scroll offset, which is why it takes&mut App). - watch
- Filesystem watcher: debounced notify events on the repo become
AppEvent::RepoChanged. Noise sources (the session store, lockfiles, git’s object database) are filtered out so staging or saving a comment does not echo back as a repo change.