Skip to content

Latest commit

 

History

60 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fm

A keyboard-driven file manager for the terminal that tells you what every folder actually weighs.

Three Miller columns — parent, current folder, preview — in the spirit of ranger, nnn and lf. You navigate, search, select, create, rename, move and delete without reaching for the mouse and without leaving the terminal.

╭ /tmp/fm-demo ───╮╭ /tmp/fm-demo/projects ──────────────────╮╭ photos-2025 ───────────────────╮
│ Documents       ││  ▸ web-dashboard                1.3 GB  ││ beach.jpg  1.7 MB              │
│ Downloads       ││  ▸ fm                           780 MB  ││ sunset.jpg  879 KB             │
│ projects        ││  ▸ photos-2025                  2.8 MB  ││ profile.png  244 KB            │
│                 ││  ▸ dotfiles                     3.0 KB  ││                                │
│                 ││    setup.sh                     1.1 KB  ││                                │
│                 ││    README.md                      62 B  ││                                │
│                 ││                                         ││                                │
│                 ││                                         ││                                │
╰─────────────────╯╰─────────────────────────────────────────╯╰────────────────────────────────╯
 6 items · 2.0 GB · size                       j/k move · / search · r rename · d trash · ? help

That is a real dump of the interface, rendered onto a 96×12 test backend and written out exactly as it sits in the buffer — which is why it has no color. In a real terminal, folders are bold blue, sizes shift hue with their magnitude, and selected entries are highlighted.

Why fm

Every folder shows its true recursive size on disk, not just the size of the loose files inside it. That number is computed by walking the whole subtree on a background thread pool, so it never blocks navigation: while it is being computed the column shows a spinner and the running total, and the final result is cached until something inside that folder changes.

This turns sorting by size (s) into something you can actually use. The heavy folders — the node_modules, the target, the photo folder nobody has opened in three years — surface at a glance, instead of having to du your way down one directory at a time.

One search key that scales. / filters the folder you are looking at. Put a * at the front of what you type and the same key searches the entire subtree below you, in the background. One key, two ranges — not two modes to remember.

Features

  • Real folder sizes, computed in the background and cached, with color that tracks magnitude — gray under 100 MB, yellow past 1 GB, red past 10 GB.
  • Fuzzy search, local or recursive, with results ranked as they arrive. crg finds Cargo.toml.
  • Nothing touches the disk without a dialog. Create, rename, paste, Trash and permanent deletion all go through a confirmation you can back out of. Permanent deletion is not a keypress: you type the name of what you are destroying, and the dialog lists every item with its size first.
  • Image previews — PNG, JPEG, GIF, WebP, BMP and SVG — drawn with the terminal's graphics protocol where one exists, and unicode halfblocks everywhere else.
  • Create files and folders from one key. a asks for a name, and the name decides: notes.md makes a file, docs/ makes a folder, docs/notes.md makes both. A goes straight to a folder. Tab cycles the common extensions.
  • Run a command without leaving. t asks for one, runs it in the folder you are looking at and brings the output back into a scrollable box. Esc kills it while it runs, and the listing is reread afterwards, so what the command changed is on screen the moment it finishes.
  • A terminal wherever you are, in a tab or beside you. T opens one in the folder you are looking at and C opens one with Claude Code already running. Both ask where first: a new tab, a split to the right, or a split below.
  • Copies to the system clipboard. y puts the selection on the pasteboard as real files, so ⌘V pastes them in Finder or anywhere else.
  • Sends deletions to the system Trash by default. Bypassing it is a separate, louder key.
  • A preview column that reads what it finds: the first lines of a text file, the contents of a folder, metadata for a binary.

Install

You need a Rust toolchain with edition 2024 support — Rust 1.85 or newer.

git clone https://github.com/Jsuaaa/FileManager.git
cd FileManager
cargo install --path .

That installs the fm binary. (The package is named FileManager; the binary and the library are both fm.)

Developed and tested on macOS. It should build on Linux — nothing here depends on more than Unix permissions and symlinks — but it is not tested there, and the terminal detection behind T and C knows only about macOS terminals; elsewhere, set FM_TERMINAL (see below). Copying to the system clipboard with y is macOS-only for the same reason. Windows is not supported.

Usage

fm                # open the current folder
fm ~/Downloads    # open a specific one
fm --icons        # use Nerd Font icons instead of unicode glyphs
fm --help         # print the help and exit, without entering the interface

Without --icons, folders are marked with and files carry no glyph, so it renders correctly in any font with no setup. With --icons, folders and the common file types (Rust, TOML/YAML/JSON, Markdown, images, archives) use Nerd Font glyphs — which means you need one installed and selected in your terminal.

Three environment variables cover what depends on which terminal you are running:

Variable What it does
FM_TERMINAL The command T runs to open a terminal, with {} standing for the folder and {cmd} — optional — for what runs in it: e.g. FM_TERMINAL='wezterm start --cwd {} -- {cmd}'. Overrides the terminal detected from TERM_PROGRAM.
FM_CLAUDE What C runs in the terminal it opens. Defaults to claude; set it to claude --continue, a wrapper, or a different tool entirely.
FM_IMAGE_PROTOCOL Set to halfblocks to draw image previews with unicode blocks instead of the terminal's graphics protocol.

Inside the app, ? opens the full list of shortcuts at any time.

Keys

^x means Ctrl+x.

MOVE

Key Action
j k move the cursor
h l out of the folder / into it
enter open (enters a folder, opens a file with the system app)
g G first / last entry
^d ^u half page down / up
~ go to the home folder

FILES

Key Action
a new file or folder (Tab cycles extensions)
A new folder (docs/2026/january makes the whole tree)
r rename
y copy to the system clipboard (⌘V pastes the files elsewhere)
x cut (marks the source; nothing moves yet)
p paste here
d move to Trash
D delete forever

FIND

Key Action
/ filter the folder you are looking at
/* search the whole subtree (the * goes first)
n N next / previous match of the last local search

SELECT

Key Action
space select or deselect the entry under the cursor
^a select everything visible
esc clear the selection

VIEW

Key Action
. show or hide hidden files
s change the sort key: namesizedatekind
S reverse the current order
R reload the folder from disk

APP

Key Action
t run a command here and show its output
T open a terminal here — asks for a tab or a split
C open one with Claude Code running — asks the same
? this help
q ^c quit; q answers even while an operation is running

Running a command with t

t opens a one-line dialog, and what you type runs in the folder you are looking at. Enter starts it, the box turns into a spinner while it works, and what it wrote comes back in a scrollable report — long output is cut, and the box says how much it left out. A command that succeeds without writing anything does not open a box at all: it just says so and rereads the folder.

Worth knowing before you reach for it:

  • Esc kills the command, but it kills the shell it started — a command that had already handed off to other processes (an npm install, say) can leave those running.
  • There is no interactive terminal, so vim, top and anything else that wants to take over the screen will not work. That is what T and C are for.
  • It runs under $SHELL -lc, which gets you the PATH from /etc/paths and your .zprofile — but not what your .zshrc adds. Aliases, shell functions and anything a mise activate or an nvm puts on the PATH are not there.

Where T and C open

Both put up the same one-key dialog: t for a new tab, l for a split to the right, j for a split below — the same directions the rest of the app navigates with. esc backs out.

Splits are Ghostty only, because that is where the recipe has been verified against a running terminal. Ask iTerm2, Terminal.app or an FM_TERMINAL of your own for one and you get a tab, and a warning saying so rather than silence.

Development

cargo test                                   # 262 passing, 3 ignored on purpose
cargo clippy --all-targets -- -D warnings
cargo build --release
tests/e2e/run.sh                             # the whole binary, keystroke by keystroke

Three layers, no cycles. fs/ is the core and does not know a terminal exists; ui/ only reads state and draws; app/ is the only place an event becomes a mutation. The core is tested without opening a terminal at all, and the interface is tested by rendering onto ratatui's TestBackend and checking the resulting buffer.

tests/e2e/run.sh is the one that is not a cargo test: it needs a release binary and expect, and it drives the real interface in a pty of its own, reading back the screen it painted. It covers the ten things the suite cannot look at because the suite never draws anything.

Two documents go deeper, both written in Spanish:

  • docs/internals.md — why each feature behaves the way it does: the caps on image decoding, why the terminal tab goes through AppleScript, what a long operation leaves behind if you quit during it, and the known limitations.
  • docs/context/README.md — architecture, the file map, the non-obvious design decisions, the environment traps and what is and is not covered by tests.

The code itself — identifiers, comments, tests and every string that reaches the screen — is written in English.

License

MIT. See LICENSE.

About

A keyboard-driven terminal file manager in Rust that shows the real recursive size of every folder.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages