romm-tui 1.5.0

Terminal UI for the RomM API
Documentation

romm-tui

Terminal UI for browsing and managing a RomM library. Built on ratatui and crossterm. Depends on romm-api.

Crate path: romm-tui/ in the workspace.


Run the TUI

cargo install romm-tui
romm-tui

Prebuilt binaries are on the Releases page under romm-tui-v* tags.

First launch runs a setup wizard if config.json is missing. You can also configure the server from Settings or via romm-cli init — see romm-api configuration.

On startup, the TUI checks for newer releases (disable with ROMM_CHECK_UPDATES=false).


Features

  • Library browsing — platforms, collections, and ROM lists; f filters the focused pane (consoles, collections, or games); / opens server-wide ROM search
  • Game detail — cover-first layout with inline image rendering when the terminal supports it (Kitty, iTerm2, Sixel); halfblocks fallback on Windows Terminal; o opens the cover in a browser; m matches metadata; Shift+U unmatches; t toggles technical details; RetroAchievements list when RomM has matched the ROM and your RA profile is synced (via RomM web UI)
  • Background downloads — start downloads and keep browsing
  • Settings — auth, paths, appearance, save-sync options
  • Save downloads — per-console save paths (see api.md — custom save paths)

Screenshots

Game details view

Game details view

Keyboard (game detail): Enter download · e extras · u upload save · D download save · m match metadata (edit search title, Enter) · Shift+U unmatch · t technical details · o open cover · Esc back

Search view

Search view


Theming

Built-in theme IDs include terminal, catppuccin, dracula, nord, tokyo-night, and others from Settings → Appearance.

  • Set in Settings → Appearance menu, config.json (theme field) or ROMM_THEME env var
  • Non-terminal presets paint full-frame backgrounds when the theme defines a palette background and NO_COLOR is unset

Library startup

Choosing Library from the main menu loads a compact on-disk snapshot of platforms and merged collections (if present) so the list renders without waiting for the network. A background task refetches endpoints, updates the UI, and writes a fresh snapshot.

The last selected console or collection is restored across launches when that row still exists; otherwise the TUI prefers a row with a complete ROM cache hit, then falls back to the first list entry.

Full ROM lists use the on-disk ROM cache with stale-while-revalidate: if a complete cached list exists for the selected console or collection, the Games pane paints it immediately (even when rom_count has changed). A background fetch refreshes the list when counts diverge; the pane is not cleared to empty while cache data exists.

OpenAPI compatibility and heartbeat version are loaded from local cache/embedded first so the TUI can paint before contacting the server; a background refresh updates feature gates and server version after first paint.

Override snapshot path with ROMM_LIBRARY_METADATA_SNAPSHOT_PATH (default: next to ROMM_CACHE_PATH). See api.md.


Internals

This section describes the TUI implementation for contributors. Read alongside cargo doc -p romm-tui --open and architecture.md.

Library selection interpretation lives in the library-browse module: current rows and collection prefetch reuse position-based cache identity and count lookup. ROM request targets and persisted selection derive from that identity; App callers ask the screen for the selected ROM request rather than dispatching on console versus collection.

Event loop

The heart of the TUI lives in romm_tui::tui::app::App::run:

  • Enable raw mode and enter the alternate screen
  • Loop: drain background tasks → render → poll keys → dispatch → deferred work

The TUI follows an Event → Action → update → render pipeline:

poll_frame_events()     # drain_background_events + crossterm input
  → map_event()         # AppEvent → Action
  → App::update()       # single state-mutation entry point
  → render()            # ratatui draw (screens are render-only)

Key modules under romm-tui/src/tui/:

Path Role
app/event.rs AppEvent, Action, global key mapping
app/update.rs Applies actions (navigation, spawns, completions)
app/run.rs Thin loop using runtime.rs (TuiSession)
app/handlers/screen_keys.rs Per-screen key → action dispatch
screens/setup_wizard/event.rs First-run wizard events

Public exports: romm_tui::tui::app::{App, AppScreen}.

Screens

Each screen is a struct under romm-tui/src/tui/screens/:

  • MainMenuScreen — entry menu
  • LibraryBrowseScreen — consoles/collections + ROM list
  • SearchScreen — text input + results table
  • GameDetailScreen — detail view for a single ROM
  • MetadataMatchScreen — provider search picker after m on game detail
  • DownloadScreen — overlay showing downloads
  • SettingsScreen — config summary and editors
  • SetupWizard — first-run / reconnect flow

AppScreen in tui::app wraps these so App holds one active screen. StartupSplash (screens/connected_splash) may render before the main menu.

App module layout

  • app/mod.rs — App, AppScreen, App::new, key dispatch
  • app/run.rs — terminal event loop
  • app/render.rs — frame drawing and global overlays
  • app/background/ — async task completion types and spawn/poll helpers
  • app/handlers/ — one module per screen group
  • app/rom_load.rs — ROM list fetch and collection prefetch

Theming implementation

ratatui-themekit presets via tui::theme::RommStyles. Semantic roles (selection, label, success, error, warning, muted) map to theme slots in romm-tui/src/tui/theme.rs. Screen renderers take &RommStyles instead of hardcoded colors.

Layout and scrolling

ratatui::layout::Layout divides the terminal into Rects (main area + footer; library browser splits horizontally). Scrolling uses a scroll_offset index and dynamic visible row counts so the selection stays in the viewport.


Related documentation