Skip to main content

Crate diffler

Crate diffler 

Source
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::handle is a pure-ish state transition (no terminal IO) so the whole shell is unit-testable; rendering reads the state in ui::draw. Per-screen state and handlers live in the status, log, diff, ci (runs + graph), ci_log, and pr submodules.
ci
Provider-agnostic CI run/job/log acquisition, plus the host glue that picks a provider for the repo and maps a normalized RunDetail onto the graph model. Adapters (providers/) implement ForgeProvider over each forge (via gh/glab/curl through the CommandRunner seam) 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: App queues an EditorRequest, the main loop suspends the terminal, runs the subprocess, restores the screen, and hands the outcome back to App::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 a GraphView, renders it, and reacts to GraphActions.
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 McpRequest and answered by App::handle_mcp on 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 feeds Search its rows as (row index, text); the search holds the query, matches, and active match, and drives incremental highlight + n/N navigation. Matching is plain substring with smartcase: case-insensitive unless the query has an uppercase letter.
theme
Color themes. github-dark is the default; ui.theme selects 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_rows turns 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. draw never computes review state; it reads App (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.