Status: accepted · Date: 2026-10-27
The preview needs GNOME. Medical images are just as often on a cluster, a server reached over SSH or in a container, where the only display is a terminal. Two projects show images in a terminal: niicat (Python, prints the views with the iTerm2, Kitty or Sixel protocol, chosen by an option; nothing appears in a terminal without that protocol, such as GNOME Terminal) and headjack (Rust, an interactive viewer in half blocks, ratatui and crossterm). Neither reads more than NIfTI, nor gets the orientation of an oblique or permuted image right, nor colours segmentations, nor browses 4D images.
The library already does everything a viewer needs and draws each slice as RGB8. What a terminal viewer adds is putting those pixels on a terminal and handling keys.
nitty: NIfTI + TTY (niicat is taken). Without
-p in a terminal it is the interactive viewer; with -p, or when its
output is not a terminal, a quick look that prints and returns.backend/, not a feature of the
preview’s crate: cargo build in backend/ still builds only
nifti-preview (default member), with the same lock file, target folder and
release profile, so the preview’s build and binary do not change (its
installer and CI gain separate paths for nitty: --terminal, jobs of their
own); cargo install ... nitty and cargo build -p nitty build the
terminal viewer alone.server.rs computed in private for
the Sushi viewer (start cursor, readout, labels, series, cursor carried across
modes, loading on a thread) became view.rs, used by both; the panel’s text,
until then only in JavaScript, has a Rust port (panel.rs) that tests/gjs
compares with the JavaScript. The protocol is unchanged: a session of every
request on 488 files gives byte-identical output before and after.libc, not ratatui or crossterm:
libc is already in the lock file (memmap2): no new crate. ratatui 0.30
needs Rust 1.88 (the MSRV is 1.85) and brings about 35 crates./dev/tty when it needs it, which Landlock refuses
once applied. nitty opens the terminal once, before the sandbox, and takes
signals through a pipe.--protocol and --colors override the choice.install.sh --terminal and get.sh --terminal put
one file in ~/.local/bin (or /usr/local/bin as root, for containers); the
two installations never touch each other’s files. Releases carry a static
(musl) build per architecture in nitty.tar.gz, in the same release as the
preview: a separate tag would become releases/latest and hide one archive
from get.sh. The static build is as fast as the glibc one (quick look of a
1 mm T1: 97 against 92 ms; a 64 MB CT: 295 against 325 ms).view, panel, Grid::voxel_at,
ViewPlan::screen_point) that nifti-preview mostly does not use; the
linker drops it from that binary.pipe2, the sandbox), like the sandbox.panel.rs) and the protocol carries it, so that
the JavaScript only shows it and the duplicate goes away. Not done here, to
keep the protocol unchanged.