Releases: Eoin-McMahon/comhad
Release list
Comhad v0.1.0: a ranger-style terminal browser for S3
Comhad v0.1.0: a ranger-style terminal browser for S3
Comhad (pronounced "KOH-ad", the Irish word for "file") is a dual-pane terminal file
browser for S3, built with ratatui. Think
Cyberduck, but in the terminal: ranger-style navigation, local ⇄ remote panes side by
side, live previews, background transfers, and non-destructive sync.
What it does
- Ranger-style panes: local ⇄ S3 side by side, with a third pane for preview or object info.
- Real previews: syntax-highlighted text, and images inline via Kitty, iTerm2 or Sixel.
- Background transfers: every download, upload and zip runs as a cancellable job with live progress.
- Copy, cut and paste: across directories, across panes, across backends, with ghost rows showing where things will land.
- Non-destructive sync: a git-diff-style view of what will be added and updated, in either direction. Sync never deletes.
- Fuzzy deep filter:
/searches the listing and quietly recurses, surfacing matches from nested prefixes.
A few things I learned building it in Rust
Backends behind one async trait. Everything talks to storage through a single
StorageProvider
trait (via async_trait), so app code never names a concrete backend. S3 is the only
implementation right now, but the goal is for comhad to grow into a Cyberduck-style
multi-backend tool, so adding SFTP or GCS later should just mean implementing this trait,
not touching the UI or transfer/sync logic at all.
Terminal graphics detection has real edge cases. Image previews use
ratatui-image, which queries the terminal
via escape sequences at startup to pick a graphics protocol (Kitty, iTerm2, Sixel, or a
halfblock fallback). One quirk I had to work around: inside tmux, the Sixel capability
query can come back positive even when the outer terminal (iTerm2, in my case) can't
actually render Sixel and just shows garbled raw data. TERM_PROGRAM doesn't survive
through tmux, but ITERM_SESSION_ID does (the same signal ratatui-image's own tmux
detection relies on), so comhad checks that env var and forces the iTerm2 protocol over
Sixel when it's set:
// tmux passthrough can answer the Sixel capability query even when the outer terminal
// (e.g. iTerm2) renders it as garbled raw data, trust ITERM_SESSION_ID over Sixel when set.
if picker.protocol_type() == ratatui_image::picker::ProtocolType::Sixel && is_iterm2_via_env() {
picker.set_protocol_type(ratatui_image::picker::ProtocolType::Iterm2);
}Testable without a real terminal. src/main.rs is a two-line shell around
comhad::run_app; the terminal setup/teardown and event loop live in src/lib.rs
precisely so the rest of the app doesn't need a real terminal to be testable. Anything
that touches disk (config, bookmarks) takes its path as an explicit argument rather than
resolving $HOME internally, so integration tests point it at a tempdir instead of
mocking the filesystem.
Install
cargo install comhadRequires Rust 1.85+. Full README has the
keybindings, configuration reference, and more detail on the sync/preview/transfer model.
