Overitall is a TUI with three main components:
- App (
ui/app.rs) - UI state: selection, filters, overlays, view mode - ProcessManager (
process.rs) - spawns processes, collects logs into a shared buffer - Config (
config.rs) - persistent settings, loaded at startup, saved on changes
The main loop in main.rs uses event-driven refresh: it wakes immediately on terminal events or new logs, with frame-rate limiting (~60fps) to batch rapid updates.
Terminal Events → EventHandler → Operations → App/ProcessManager/Config
↓
Log Files → ProcessManager → LogBuffer → App (for display)
↓
UI Draw ← App state
All business logic lives in operations/ modules. Event handlers and commands just route to operations.
To add a new feature:
- Create an operation in
operations/that modifies App/ProcessManager/Config - Wire it to a command in
command.rsor key handler inevent_handler.rs - Update help overlay if user-facing
To add a new command:
- Add variant to
Commandenum incommand.rs - Add parsing in
parse_command() - Handle in
CommandExecutor::execute()by calling an operation
To add a new key binding:
- Add handler method in
event_handler.rs - Call an operation from the handler
- LogBuffer (
log/buffer.rs) - circular buffer with memory limit, FIFO eviction - Dual timestamps - each LogLine has parsed timestamp (from content) + arrival timestamp (when received)
- Batch grouping - lines arriving within
batch_window_msare grouped for navigation
- Overlays (
ui/overlays/) - modal views (help, expanded line, trace selection) - Widgets (
ui/widgets/) - stateless rendering (log viewer, process list, status bar) - App state drives what's rendered; widgets read from App
The main content area renders one of several views, selected by DisplayState::content_view (ContentView enum). draw() switches on it: Logs renders the log viewer, ProcessTree renders the process tree viewer. P toggles the tree; Esc returns to logs. The tree view owns its own scroll state (process_tree_scroll/process_tree_viewport); the widget clamps the offset against rendered content each frame. When the tree view is active, navigation keys scroll the tree and log navigation/search are suppressed.
process_tree.rs snapshots the OS process table via the cross-platform sysinfo crate (cached with a short TTL in ProcessTreeCache) and builds a descendant tree from each managed process's root pid (ProcessHandle::root_pid(), gated by status so stopped/failed processes never expose a stale pgid). Snapshot conversion and tree-line generation are pure functions; the ui/widgets/process_tree.rs widget styles and scrolls the result.
External tools (AI agents, scripts) control oit via Unix socket IPC.
CLI (oit ping) → IpcClient → Unix Socket → IpcServer → IpcCommandHandler → Response
- IpcServer (
ipc/server.rs) - Unix socket listener, non-blocking poll for commands - IpcClient (
ipc/client.rs) - connects to socket, sends requests, receives responses - IpcCommandHandler (
ipc/handler.rs) - processes requests, returns JSON responses - Protocol (
ipc/protocol.rs) -IpcRequestandIpcResponsetypes, newline-delimited JSON
Socket location: .oit.sock in the current working directory.
To add a new IPC command:
- Add handler method in
IpcCommandHandler(e.g.,handle_mycommand) - Add match arm in
handle()method - Add CLI subcommand in
cli.rsif needed - Add tests
Use TestBackend for TUI tests, insta for snapshots. Run cargo test, review with cargo insta review.
Test helpers: Some ProcessManager methods like set_process_status_for_testing and reset_process_status are marked #[doc(hidden)] pub because integration tests in tests/ need access but #[cfg(test)] items aren't visible to them. A cleaner future approach would be a test-helpers feature flag with #[cfg(any(test, feature = "test-helpers"))].