rcmd-egui 4.30.8

rcmd in a window: the orthodox dual-pane file manager, its ratatui drawing painted by egui instead of a terminal.
rcmd-egui-4.30.8 is not a library.

rcmd

A Midnight Commander replacement in Rust: orthodox dual-pane file manager with MC keybindings, built on ratatui. All four roadmaps are complete - 1.0 (docs/PLAN.md), 2.0 (docs/PLAN2.md), 3.0 (docs/PLAN3.md) and now 4.0, the parity release (docs/PLAN4.md), whose scope came from a decision-by-decision comparison against mc (docs/MC-DIFF.md). Every row that comparison marked Adopt is closed, and the places where rcmd still differs on purpose are written down there rather than left to be discovered.

4.11 onwards is a second comparison, against the rest of the orthodox family - Far Manager, DOS Navigator, Volkov Commander, Total Commander, and the modern TUI managers - written down the same way in docs/ORTHODOX-DIFF.md. Those rows are not parity work and nothing is owed to anybody, which is why Skip appears there as often as Adopt.

rcmd demo: browsing, the syntax-highlighted viewer, marking and copying, the persistent subshell

(recorded by tests/e2e/record_demo.py - the same pty harness that runs the test suite - and rendered with agg)

4.28 adds a second front end: rcmd-egui is the same file manager drawn by egui in a window instead of by a terminal - see In a window.

rcmd-egui: the same two panels, the same keys, in a window

Status

2.0 - complete MC-workflow parity and beyond: marking and F5–F8 operations with MC-style dialogs (mtimes preserved, F8 goes to trash), command line + shell integration with real job control, F3 chunked viewer with wrap and hex modes, F9 menu, F1 help, config file with keymap presets/custom bindings, quick search, filter, hotlist, themes, archive browsing (zip, tar, tar.{gz,xz,bz2}) with extraction and copy-into-zip; find file / panelize / directory compare, non-blocking listings with filesystem watching, SFTP remote panels, a built-in editor with syntax highlighting, mouse support, per-panel directory history, quick view, info panel, listing formats, git status in the panels, openers and an F2 user menu, and MC's ESC-prefix - see below.

3.0 - the live commander: the persistent subshell (Ctrl+O), SFTP auth depth (passphrase keys, keyboard-interactive), bulk rename via the editor, viewer follow mode (tail -f) with syntax highlighting and precise search-match highlighting, [[view]] filters (F3 through pdftotext & co.), Tab path completion, gitignore-aware find, recent directories in the hotlist, a job queue with background transfers, chmod/chown/symlink dialogs, editor soft-wrap + $1 capture groups + block ops, copy into tar, rar and 7z browsing (via 7z/unrar), click-to-sort headers, and an MC alias batch (S-F4/S-F5/S-F6, C-x t/p, M-c quick cd…).

3.8 - toward parity with mc: config.toml is now yours alone, with everything rcmd changes itself moved to a state file; F9 > Options > Panel options is one grouped dialog covering mc's whole setting surface; [keys.viewer] and [keys.editor] rebind inside the viewer and the editor; rcmd --import-mc converts an existing mc configuration; the command line gained mc's keys, macros and a persistent history; and the panels gained the Layout settings (horizontal split, adjustable ratio, optional bars), a per-panel mini status and the multi-column brief listing.

3.10 - the panels themselves: mc's directory tree, both as the Command-menu dialog (Enter moves this panel) and as a panel listing mode (Enter moves the other one and the tree stays), scanned on demand so there is no tree cache to go stale; and the user-defined listing format, where listing = "user" draws whatever listing_format names in mc's own format language - half type name | size | mtime and the other fifteen fields, with widths that grow.

3.11 - [[highlight]] colour rules: entries are painted by glob or by kind, which is mc's filehighlight without the second file.

3.12 - mc's menu bar: Left, File, Command, Options, Right, where the two panel menus act on their own panel whichever one has the focus.

3.51 - the wider world (4.0 S7): -e and -v and the rcedit / rcview / rcdiff aliases, mc's startup flags (-b -c -C -S -d -u -U -l), the shipped shell wrappers, skins - rcmd's own theme files and mc's skin files read where they lie - and macOS builds back in CI and in the releases.

4.28 - a second front end: rcmd-egui draws the same screen with egui, in a window, sharing every crate with the terminal build and calling the same ui::draw. rcmd-tui became a library as well as a binary to allow it, and App::run's body became App::tick so a front end that does not own its event loop can drive the same state machine. Ctrl+O opens a real terminal pane in it: vt100 interprets what the existing subshell's pty produces, so less and vim work there. See In a window.

4.11-4.27 - the orthodox pass, from docs/ORTHODOX-DIFF.md: directory synchronize (compare, then a plan that says which way each difference goes), Alt+F5 packs into a new archive, Ctrl+X u undoes a move or a bulk rename, mask lists (*.c,*.h|*_test.*) everywhere a glob is typed, named filter sets, select and filter by size and age, five more sort orders including unsorted, the hotlist ranked by frecency and searchable, rcmd --remote driving a running instance (and with it a plugin story that needs no runtime), rclone panels, what is mounted in the C-x a list, checksum files and a Verify box on the copy form, wipe, apply a command per file, the numbered places, recent files, and the small keys the others always had - restore marks, size every directory, names to the clipboard, hide a panel.

4.0 - the parity release, and the leftovers that finished it: mc's quick search with an input field of its own, Learn keys, [keys.dialog], a hotlist with groups and a label prompt and editing, user-menu conditions and submenus plus the per-directory .mc.menu, mc's full macro set, dialog input history, mouse and underlined hotkeys, the mc clipboard file, and user syntax files. What is left in docs/MC-DIFF.md is the divergences - Tab completing rather than switching panels, F8 to the trash, one grouped options dialog instead of five - each of them a decision with a reason next to it.

Install & run

cargo install rcmd-tui                 # from crates.io; installs the `rcmd` binary
# or straight from git:
cargo install --git https://github.com/jaroslavpachola/rcmd rcmd-tui
# from a checkout:
cargo install --path crates/rcmd-tui   # installs the `rcmd` binary
# or during development:
cargo run -p rcmd-tui                  # or: just run

cargo install rcmd-egui                # the window build
cargo install --path crates/rcmd-egui  # the same, from a checkout
cargo run -p rcmd-egui
just install-desktop                   # and a menu entry for the window

just install-desktop writes ~/.local/share/applications/rcmd-egui.desktop from crates/rcmd-egui/dist, pinning Exec to the installed binary because a desktop session rarely carries ~/.cargo/bin on its PATH; a packager installs the file as it stands. The entry claims no inode/directory handler on purpose - it starts the window where a launcher starts anything, and which program a folder opens in is the desktop's own setting, not this file's to take.

Release binaries are attached to GitHub releases (built by .github/workflows/release.yml on v* tags): the glibc Linux build, a static musl build (x86_64-unknown-linux-musl, C dependencies vendored) that runs on any distro with no shared-library requirements, and macOS on both architectures (x86_64-apple-darwin, aarch64-apple-darwin, OpenSSL vendored so nothing has to be installed alongside).

usage: rcmd [OPTIONS] [DIR1 [DIR2]]
       rcedit FILE...    rcview FILE    rcdiff FILE1 FILE2
       rcmd --import-mc [MC_CONFIG_DIR]

  -e, --edit FILE     start in the editor on FILE (repeatable)
  -v, --view FILE     start in the viewer on FILE
  -P, --printwd FILE  write the last active directory to FILE on exit
  -S, --skin NAME     theme: mc, dark, bw
  -b, --nocolor       black and white
  -c, --color         colour (the default)
  -C, --colors SPEC   mc colour spec: keyword=fg,bg:keyword=fg,bg
  -d, --nomouse       no mouse
  -u / -U             subshell off / on for this run
  -l, --ftplog FILE   log the FTP/fish dialogue to FILE
      --remote LINE   hand LINE to a running rcmd and exit
      --to PID        which one, when several are running

-e and -v bring rcmd up on one screen instead of the panels, and closing it ends the session - that is mc's mcedit / mcview, and the same thing happens when the binary is reached through a link named rcedit, rcview or rcdiff (mc's names work too, if that is what your fingers type):

ln -s "$(command -v rcmd)" ~/.local/bin/rcedit    # and rcview, rcdiff
rcedit notes.txt draft.txt   # two editor screens; Alt+` lists them
rcdiff old.rs new.rs         # the two files side by side

-b is the one to reach for when the colours are not arriving - it drops to the terminal's own foreground and background, with reverse video where something has to stand out, and it overrides -S. -C takes mc's colour spec (normal=brightgreen,black:directory=white) and lays it over whatever theme is loaded; keywords rcmd has nowhere to put are named on the status line rather than dropped in silence. -l writes every line of FTP and fish:// dialogue to a file - the transcript is what a server that will not list looks like from outside - with the password redacted.

Coming from mc? rcmd --import-mc reads your menu, mc.ext and mc.keymap and prints the equivalent rcmd config on stdout - user menu entries, openers, view filters and panel key bindings. It never touches your config.toml; review what it prints and paste what you want. Anything with no rcmd equivalent (type/ matchers, %cd commands, unsupported macros) is reported on stderr rather than guessed at.

To make your shell follow rcmd's last directory on exit (the mc-wrapper trick), source one of the shipped wrappers - contrib/rc.sh for bash/zsh, contrib/rc.fish for fish. They come with the release tarballs, and are one function each if you would rather copy it into your shell config than source a file:

. /path/to/rc.sh                              # bash/zsh: in ~/.bashrc
cp rc.fish ~/.config/fish/functions/rc.fish   # fish
rc                                            # rcmd, and cd where it ended

Both are the same idea: rcmd writes its last active directory to a file on exit (-P), the function reads it and cds there. A run that ends in a crash, or in a directory that has since gone away, leaves the shell exactly where it was.

In a window

rcmd-egui is rcmd drawn by egui instead of by a terminal. Same panels, same keys, same config.toml, same themes, same viewer and editor - it is a front end and nothing more. The whole of rcmd-core, rcmd-edit and rcmd-tui is shared with the terminal build rather than reimplemented, and the drawing code (ui.rs) is called unchanged: what the window contributes is a ratatui Backend that paints a monospace cell grid, a translation from egui input into the crossterm events the state machine already dispatches on, and an answer to "run this command" that does not assume a tty.

usage: rcmd-egui [OPTIONS] [DIR1 [DIR2]]

  -e, --edit FILE     start in the editor on FILE (repeatable)
  -v, --view FILE     start in the viewer on FILE
  -D, --diff A B      start in the diff viewer on A and B
  -S, --skin NAME     theme: mc, dark, bw
  -b, --nocolor       black and white
  -C, --colors SPEC   mc colour spec: keyword=fg,bg:keyword=fg,bg
      --font-size N   point size of the grid font, for this session

$TERMINAL names the emulator to fall back on when there is no subshell; $RCMD_EGUI_FONT a .ttf/.otf to draw the grid in for the one session; $RCMD_EGUI_KEYS keys to play in at startup, for driving the window from a script.

The font is yours to choose - the one setting a terminal cannot have. Options > Font... in the window's menu bar lists the monospaced families the system has, with a size slider beside them, and the grid behind the dialog changes as you pick; OK keeps the choice, Cancel puts back what you had. Ctrl+= / Ctrl+- / Ctrl+0 step the size and put it back, as in any windowed terminal. Both are written to window.toml beside rcmd's state.toml. By hand, the same two go under a [window] table in config.toml, which the terminal build ignores:

[window]
font = "DejaVu Sans Mono"   # a family name, or a path to a .ttf/.otf
font_size = 14

Without any of that the grid is drawn in a system monospace face - DejaVu, Liberation, Menlo or Consolas, whichever is there - because egui's bundled font is missing most of the box-drawing block and rcmd frames every panel and every dialog in it. egui's own font is the fallback beneath whatever is chosen, so a glyph the chosen face lacks is still drawn.

Ctrl+O opens a terminal pane, the same as it opens the output screen in a terminal. subshell.rs already owned the pty, spawned the shell, tracked its working directory through the prompt hooks and buffered every byte it wrote; the window only had to add the interpreting half, which is vt100 - bytes in, a grid of cells out - and painting a grid of cells is what this front end does anyway.

rcmd-egui: git status and ls in the embedded terminal pane

Colour, bold, the alternate screen and application-cursor mode all work, so less, vim and anything built on ncurses behave as they do anywhere else. A second Ctrl+O brings the panels back, and the active panel has followed the shell's cd - and the shell follows the panel's, the same sync the terminal build does. A command typed on rcmd's own command line opens the pane, runs there, and closes it again when it finishes.

The protocol behind that - wait for a prompt, sync the directory in, feed the command, know when it finished, sync back out - is not written twice. It is App::begin_subshell / step_subshell / end_subshell, and the terminal build loops over the same three calls while passing keys through raw.

The menu bar is the window's own, above the grid, rather than mc's row of titles drawn inside it: real dropdowns, the pointer's, with the key shown beside each entry and the hotkey letter underlined and working - F9 opens the leftmost menu as it does in a terminal, the arrow keys and Enter walk it, f then q is still File > Quit. The entries are the same tables the terminal build draws (MENUS and, in the editor, EDIT_MENUS), and choosing one makes the same call its dropdown makes on Enter, so Left and Right act on their own panel here too. The editor on top swaps in its own bar; a dialog, a job, the help or the viewer greys it, since what is on top has the keys. The show_menubar row is the terminal build's and is not drawn here.

What is still different. Openers and [[open]] rules are spawned detached, which is what an opener always wanted - a GUI program no longer needs its trailing &. And with no subshell at all (subshell = false, or a shell that would not spawn) commands fall back to a terminal emulator: $TERMINAL if it is set, else the usual ones, with a "press Enter" pause so a command that only prints an error can be read.

Everything else is shared, including the state file - the directory a window was left in is where the next terminal session starts.

Keys

Key Action
Tab Switch panel
↑ ↓ PgUp PgDn Home End Move cursor
Enter Enter directory or archive (zip, tar, tar.{gz,xz,bz2})
Backspace Parent directory / leave archive
F1 Help
F2 User menu ([[commands]] from the config)
F3 View file (internal viewer)
F4 Edit file (built-in editor; editor = "external" for $EDITOR)
F9 Pulldown menu (highlighted letters are hotkeys: F9 o p = Panel options)
Insert, Ctrl+T Mark entry and advance
+ / - (or \) Select / unselect by glob
* Invert marks
Ctrl+X l / s / v Hard link / absolute symlink / relative symlink to the cursor entry
Ctrl+X Ctrl+S Change where an existing symlink points
F5 Copy marked (or cursor) entry - opens MC's form: source mask, destination, preserve attributes / follow links / dive into subdirs / stable symlinks, and OK / Background / Cancel
F6 Move / rename
F7 Make directory
Alt+F5 Pack the marked entries into a new archive (the name says the format)
F8 Delete to trash
Shift+F8 Delete permanently
Alt+Del Wipe: overwrite every byte, then delete
Ctrl+G Apply a command to each marked file, one at a time
Ctrl+X u Undo the last move (asks first; a second time is the redo)
Alt+N Sort by name (again = reverse; extension, size, the three times, owner, group and unsorted live in the panel's own F9 → Left/Right menu)
Alt+T Cycle listing format: brief (names in columns) / full / long (active long panel = full-width one-panel view)
Ctrl+U Swap panels
Alt+. Toggle hidden files
Ctrl+S, Alt+S Quick search: matches anywhere in the name, */? glob, smartcase; Ctrl+S or ↓/↑ walks the matches
Ctrl+F Filter shown files by glob (* or empty clears)
Alt+letter In a dialog: press the button whose underlined letter it is
Ctrl+\ Directory hotlist: Enter goes (or walks into a group), Ctrl+S narrows the list by what you type, a adds this directory, g makes a group, e renames, m moves an entry into another group, d drops, Alt+↑/↓ reorders
Alt+F7 Find file (glob + optional content); results panelized
Alt+← / Alt+→ Directory history back / forward (per panel)
Alt+Shift+H Directory history as a list; Enter goes there
Alt+↑ Directory hotlist
Ctrl+X d Compare directories (marks differences in both panels)
Ctrl+X m Put back the marks the last operation spent
Ctrl+X Space Size every directory in the panel, one after the other
Ctrl+X 0…9 The ten numbered places: go there, or set an empty one here
Ctrl+Ins / Ctrl+Alt+Ins Copy the marked names / full paths to the clipboard
Ctrl+F1 / Ctrl+F2 Hide the left / right panel, and give the other the screen
Ctrl+X ! Panelize a command's output
Alt+H Command history (kept across sessions); Alt+P / Alt+N walk it
Alt+A Insert the panel's path on the command line
Ctrl+X q Quick view: other panel previews the cursor file
Ctrl+X i Info panel: other panel shows the cursor file's full stat
Alt+i / Alt+o Other panel: same directory / directory under cursor
Ctrl+Space Directory size (background scan into the Size column)
Ctrl+R Reload panel (also restores listing after find/panelize)
Esc Cancel dialog / running operation / clear command line
Esc key MC meta prefix: Esc 1…0 = F1…F10, Esc x = Alt+X, Esc Esc = Esc
F10 Quit

Typing goes to the command line at the bottom; Enter runs it in the active panel's directory (cd changes the panel instead, and cd - goes back to where the panel came from; a relative cd that misses locally also tries $CDPATH). Dialog fields remember what was typed into them before - Alt+P and Alt+N walk a field's own history, kept per kind of question (destinations, mkdir, cd, chown…) and saved between sessions. MC's macros expand there too (the same set the user menu gets, below - so %%s is how you type a literal %s). Alt+Enter inserts the selected filename, Ctrl+P/Ctrl+N walk history, Ctrl+A/E are readline-style and Esc clears the line (Ctrl+U swaps panels, like MC). The +/-/*/\ selection keys apply only while the command line is empty.

The subshell (Ctrl+O): a persistent $SHELL runs on its own pty for the whole session, exactly like MC's. Ctrl+O flips between the panels and its screen - the last command's output is still there - and typed commands run inside it, so aliases, functions, history and $? survive between commands. cd sync goes both ways: the panels follow a cd typed in the subshell, and the subshell is moved to the active panel's directory before running anything. exit respawns it. bash, zsh and fish get a prompt hook for precise tracking; plain POSIX sh works with a /proc-based fallback. subshell = false in the config restores the old one-shot execution (also the automatic fallback if the shell cannot be spawned).

In dialogs: arrows/Tab move between buttons, Enter confirms, Esc cancels; overwrite and error prompts also take hotkeys (o/a/s/S, r/s/S). The The copy/move form takes MC's source mask: *.tar.gz with a destination of dir/*.tgz copies foo.tar.gz to dir/foo.tgz, and files the mask does not match stay where they are. The mask's wildcards are numbered left to right - * in the destination is the first, \1..\9 any of them, \0 the whole name - and \u \l \U \L \E change case. (Regex renaming with capture groups lives in F9 > File > Bulk rename, which is a better place for it than a one-line field.)

The overwrite prompt is MC's: both files' size and date on screen, then Overwrite / Append / Reget for this file and All / Update / Size differs / None for every remaining one (Up/Down switch rows). Append and Reget - MC's resume - need a local file on both sides.

Checksums: F9 → File → Checksum file (sha256) writes a sha256sum-format file for what is marked - hash name lines, the names relative to the file's own directory, so sha256sum -c reads it and so does Check the checksum file, which hashes each named file again and says how many matched and how many did not. That is the checksum you hand to someone else; the Verify box on the copy form is the one you keep to yourself - it reads every copy back and compares it with the source, which is the only thing that turns "the write returned no error" into "the bytes are there".

Alt+Del wipes: every byte of each file is overwritten once and flushed to the device before it is unlinked. What that is worth is on the confirm dialog, because the difference matters: the bytes that were in those blocks are written over, but a copy-on-write filesystem writes the zeroes somewhere else, an SSD's controller may do the same, and a snapshot or a backup was never this file's to overwrite. It is a better delete, not an erasure.

Ctrl+G applies a command to each marked file, one at a time: [[commands]] hands every marked file to a single invocation, which is the right shape for tar and the wrong one for convert. The command is a template like any other (%f is the file, %d the directory) and what runs is one line per file, in the terminal, where the output and Ctrl+C are as they always are. A %{question} is refused here rather than asked two hundred times.

Ctrl+X u undoes the last move. Every move F6 makes onto a name that was free is recorded as it happens - and so is every rename a bulk rename made, which used to be final the moment it finished - and Ctrl+X u asks before putting the items back where they came from. It is an ordinary job - the same progress, the same error prompts, the same Esc - and because the undo is itself a move, a second Ctrl+X u is the redo. A move that landed on a name that was already taken is not recorded: putting the source back would not bring back what it overwrote, and an undo that quietly destroyed something would be a second accident rather than the end of the first. A pair whose file has since moved on, or whose old name is occupied again, is left alone and counted as skipped. A copy leaves no record either - undoing one means deleting what it wrote, which is a deletion and should be asked for as one - and F8 has always gone to the trash, which is where a deletion is undone.

Esc doubles as MC's meta prefix everywhere: after a lone Esc, a digit is an F-key (Esc 1 = F1 … Esc 0 = F10) and any other key gets Alt added - handy on terminals without working F-keys or Alt. Esc Esc is a real Escape; an unanswered Esc acts as one after a second.

Viewer (F3): arrows/PgUp/PgDn/Home/End scroll, ←→ horizontal scroll, F2 toggles soft-wrap, F4 toggles hex mode, F3/F10/Esc/q quit. Lines are indexed lazily, so huge files open instantly; very long lines are broken at 4096 columns. A file that never declares its length - anything under /proc or /sys, or a block device - is read for it rather than shown as empty. The bottom bar names what each key does now, as mc's does: F2 says Unwrap once wrapping is on.

F5, Alt+L or : opens goto, which takes all three of mc's destinations in one field, told apart by how the number is written: a bare 201 is a line, 0x3e8 or 1000b a byte offset, and 50% a share of the file. m followed by a digit sets one of ten marks at the current position and r followed by the same digit returns to it. Alt+R toggles a column ruler under the title, which counts from the leftmost column on screen rather than from the start of the line, so it keeps telling the truth when the view is scrolled sideways.

F7 or / opens mc's search dialog: the pattern, and the four answers that change what it means. The pattern is read as Normal (a literal), a Regular expression, or Hexadecimal bytes - 7f454c46, 7f 45 4c 46 and 0x7f 0x45 0x4c 0x46 are the same four, and it is the only way to look for something that is not text. Alongside it: Case sensitive, Whole words and Backwards. Tab and the arrows move between rows, Space ticks, Enter searches; n repeats the search with its options intact. Matches are highlighted in the line and the found line is marked.

F8 turns nroff formatting on: _^Ht and t^Ht - a character, a backspace and another character, which is how a formatted man page has said "underline" and "bold" since printers could only move forward - are read as the attributes they stand for instead of showing up as control bytes. The search follows the mode, so with formatting on the word you can see is the word you can look for.

F6 swaps the [[view]] filter in and out under the same file: the parsed text from pdftotext, tr, unzip -l or whatever the rule runs, or the file as it actually is. It is the in-viewer form of the choice Shift+F3 makes when opening.

Alt+! (F9 > File > Filtered view) is the same thing for a command typed on the spot: the field starts as the file name, the command goes in front of it (head -50 , strings , xxd | less-style pipes are fine, %f works too), and the output opens in the viewer as a filter would, so F6 swaps the file itself back in.

Ctrl+F and Ctrl+B move to the next and previous file of the panel without leaving the viewer, keeping wrap, hex, the ruler, the formatting mode and the search - so reading through a directory is one key per file. The panel cursor follows, which is where you land when you quit.

In hex mode (F4), F2 puts a cursor on the bytes. Hex digits type over the byte it is on, two halves to a byte; Tab switches to the text column, where a character stands for itself; arrows, PgUp/PgDn and Home/End move it; F6 writes the changed bytes into the file and Esc stops editing. Nothing reaches the file until F6, changed bytes are marked until then, and leaving with any still unwritten asks first. Bytes are replaced, never inserted or deleted, so the file's length never moves - which is what makes writing a handful of bytes into a multi-GB file instant. Editing needs the file itself: on an archive member, a remote file or a [[view]] filter's output the viewer is on a copy, and it says so rather than writing to something about to be deleted.

Responsiveness: directory listings that take longer than ~100 ms (huge directories, cold network mounts) load in the background - the old listing stays up with a spinner, typing never blocks, Esc cancels. Panels also auto-reload when their directory changes on disk (debounced; watch = false in the config disables it).

Selecting and filtering: + and - select and unselect by pattern, and Ctrl+F filters what the panel shows at all. All three are mc's one dialog: the pattern, then Files only (directories are left alone, so a filter can never strand you), Case sensitive, and Shell patterns - unticked, the pattern is a regular expression instead of a glob. A regex that will not compile is quoted back at you rather than silently matching nothing. The panel names the filter it is under along its bottom edge, options included.

Named filter sets (Ctrl+X f) are Far's filter menu: [[filter]] entries in the config, each a name and a mask list, and a list of switches saying which of them this panel is under. Several can be on at once, and what the panel then shows is what any of them shows, minus what any of them hides - so a "sources" set of *.c,*.h|*_test.* and a "notes" set of *.md switched on together show the sources and the notes and still no tests. Space ticks a row, a switches them all off (or all on again), Enter applies, and with nothing ticked the panel is unfiltered. Ctrl+F stays what it always was: one glob, typed on the spot.

[[filter]]
name = "sources"
mask = "*.c,*.h|*_test.*"

[[filter]]
name = "no build output"
mask = "|*.o,*.d,target/*"

Beside the pattern the same dialog asks DN's other two questions: Size (>1M, <=100k, 1M-2G - k/M/G/T, 1024-based as the panel counts) and Newer than (30m, 24h, 7d, 2w). Both are empty by default and ask nothing; filled in, an entry has to satisfy all three. Directories are never held to a size or an age - the number beside a directory is not its own - so + with a size still leaves the tree navigable. "Everything over a hundred megabytes" and "everything I touched this week" were a trip through find and panelize before.

A shell pattern is a list, which is Far Manager's mask language and what mc's single glob grows into: *.c,*.h is either of them, and a | takes a second list back out again, so *.c,*.h|*_test.* is the sources without the tests and |*.o is everything except objects. Whitespace around a mask is not part of it, one mask is still one mask, and the same list works in the find dialog's filename field. It is the shell-pattern switch's language alone: with the switch unticked the pattern is a regular expression, where | is the alternation it has always been.

Power tools: Alt+F7 opens find file - where to start, a filename pattern, and the text to look for inside the files, with mc's answers beside them: whole words, case sensitive, regular expression (matched line by line), all charsets (the same word as another machine spelled it - KOI8-R, CP1251, Shift_JIS and the rest), skip hidden, follow symlinks, and rcmd's own skip gitignored. Matches arrive in a results window of their own as they are found, with mc's six buttons: Chdir (Enter on a row) takes the panel to the file and puts the cursor on it, Again reopens the dialog on the same question, Panelize turns the list into the panel listing, View and Edit open the match, and Quit closes. find_window = false restores the pre-4.0 shape, where matches stream straight into the panel as a panelized listing (paths relative to the search root), where marking and F5/F6/F8 work as usual. Panelize command… (F9 → Left/Right) turns any command's stdout lines into such a listing (git ls-files -m, rg -l TODO, …). Its output streams in as it arrives, so a slow command fills the panel while it runs and Esc stops it. Commands worth keeping sit above the field as a saved list: Tab moves between the list and the field, Ctrl+S saves what you typed under a name, F8 drops one. They live in [[panelize]] entries in the config, and the ones you save while running go to the state file. Compare directories (Ctrl+X d) asks mc's question first - Quick (size and date), Size only, or Thorough - and marks what differs on both sides, so a plain F5 copies the differences across. Thorough reads the files, which is the only way to tell two files with the same size and date apart; it runs in the background, marks each pair as it finds it, and Esc stops it. Size only is for a tree whose timestamps were never going to survive the trip.

Synchronize directories (F9 → Command) is what a comparison is for. It asks the same Quick / Size only / Thorough question, runs the same comparison, and then shows the plan instead of stopping at marks: one row per difference, an arrow saying which way it would be copied - the newer side wins, and a file only one side has crosses over - and a note saying why the row is there. Space skips a row, ←/→ turn one round, a switches them all off and on, Enter runs it as ordinary copy jobs, one per direction. What it copies replaces what it lands on without asking, because that is the question the plan answered; a row left switched off is not copied at all. Marking the differences and leaving F5 to guess the direction is where mc stops, and that guess is wrong exactly when the differences run both ways. Both sides have to be local, and directories are left alone - the comparison has always been about the files in one directory, not the tree under it.

Compare files (F9 → Command) puts the cursor file of each panel side by side, lined up by a Myers diff: changed lines are highlighted on both sides, a line only one file has shows opposite a ~~~ gap, and n and p walk from one difference to the next (it opens on the first one). q closes it. It is a screen like the viewer and the editor, so Alt+` lists it and you can leave it open while you do something else.

Mouse: click focuses a panel and moves the cursor, double-click enters, the wheel scrolls whatever it hovers (panels, viewer, editor, quick view), the bottom keybar and the F9 menu are clickable, and a click in the editor places the cursor. All additive - every feature stays keyboard-reachable. Hold Shift to select terminal text as usual; mouse = false in the config turns capture off entirely.

Panel history: each panel remembers where it has been - Alt+←/Alt+→ walk back and forward browser-style (sftp:// locations reconnect through the connection cache), Alt+Shift+H lists the whole history with * on where the panel is now and Enter moving the cursor there, Alt+↑ opens the hotlist.

Ctrl+X 0…9 are Far's folder shortcuts, and they are hotlist entries whose label is that digit: Ctrl+X 3 goes to the one labelled 3, and if there is no such entry it makes one here and says so. There is nothing new to store or forget - the hotlist already persists, reorders and renames, and a shortcut is a hotlist entry with a very short name.

Recent files (F9 → File) is what the viewer and the editor have opened, newest first, kept between sessions - the one history you want right after closing a screen. Enter puts the panel on the file, cursor and all.

The hotlist remembers where you go. Under your own entries it lists everywhere rcmd has been, ranked the way zoxide ranks directories - how often you went there, weighted by how recently, so three visits this morning beat ten last year and neither is forgotten - and the log is kept in the state file, so it is still there next session. Ctrl+S narrows the whole list by what you type (Backspace edits it, Esc drops it), which on a list already sorted by where you actually work is one key and three letters to anywhere.

Quick view (Ctrl+X q): the other panel becomes a live preview of the file under the cursor, updating as you move. It uses the viewer's chunked reader, so previewing a multi-GB log is instant. Tab focuses the preview for scrolling (arrows/PgUp/PgDn); Ctrl+X q turns it off.

Openers & user commands: [[open]] rules in the config make Enter open files by type - the first matching rule wins:

[[open]]
match = "*.pdf"              # a mask list, case-insensitive
run = "zathura %f >/dev/null 2>&1 &"

[[open]]
type = "^ELF"                # a regex over what `file -b` says of it
run = "objdump -d %f | less"

[[open]]
regex = "^[a-z]+[0-9]+\\.log$"  # a regex on the name ((?i) folds case)
directory = "^/var/log/"       # ...and one on the panel's path
run = "less %f"

Those are mc.ext's four matchers (match, regex, type, directory); every one a rule gives must hold. match takes the same mask list the select and filter dialogs do, so match = "*.jpg,*.png|thumb_*" is one rule rather than two and an exception; so does [[highlight]]'s. file is only asked when a rule has type =, and [[view]] rules take the same keys.

A file no rule claims goes to the desktop - xdg-open, or open on macOS - as long as there is a display ($DISPLAY or $WAYLAND_DISPLAY) to open it on; over a bare ssh Enter stays quiet, since the browser would open on the wrong machine. desktop_open = false switches the fallback off; a [[open]] rule always wins.

Openers run without a "press Enter" pause, so terminal programs (mpv, less) feel native and GUI programs just need a trailing &. With lynx-like motion on, Right still only enters directories - Enter opens. [[commands]] are named shell templates listed in the F2 user menu (first nine get digit hotkeys) and optionally bound directly:

[[commands]]
name = "git status"
run = "git status | less"
key = "ctrl+g"

[[commands]]                     # only offered where it makes sense
name = "extract here"
run = "tar xf %f"
when = "f *.tar.gz | f *.tgz"

[[commands]]                     # a submenu: Enter walks in, ← walks out
name = "Tools"
entries = [
  { name = "line count", run = "wc -l %s" },
]

when is mc's user-menu condition language: f/F the cursor file here or in the other panel, d/D the directory, t/T the file's type (r regular, d directory, l link, x executable, n not a directory, t something is marked), x a program that must exist, ! to negate, | and & to join - evaluated left to right, as mc evaluates them. Patterns are globs.

A .mc.menu in the panel's directory is read too, in mc's own format (shell_patterns=0 files have their regexes converted). Its entries come first and the configured ones stay after them: a project's menu is an addition to yours, where mc's would have replaced it.

Both expand mc's macros before running in the active panel's directory, everything shell-quoted:

Macro This panel Other panel
cursor file %f %F
directory %d %D
marked files %t %T
marked files, and drop the marks %u %U
marked files, or the cursor file if none %s %S

%q is the clipboard file (~/.cache/mc/mcedit/mcedit.clip, shared with mcedit - rcmd's editor writes it too), %% a literal percent, and %{Some question} asks before the command runs, putting the answer in unquoted, which is how options get passed. Anything else is left alone, so printf '%%s' needs its percent doubled like everywhere else.

File properties: the info panel (Ctrl+X i) turns the other panel into a live stat display of the file under the cursor - type, size, permissions, owner and group (resolved locally, numeric on SFTP), hard links, inode, and all three timestamps - plus the filesystem's free space, which also shows in every local panel's footer. Listing formats are switchable per panel from F9 → Left/Right: brief (names only, full width), full (the classic name/size/mtime), and long (ls-style perms/owner/group/size/name). A long listing needs room, so while the active panel is long it takes the whole width and the other panel is hidden - MC's one-panel view; Tab to the other panel (or cycle the format back) and the split returns. The choice persists in the config (listing).

Git awareness: inside a git work tree the panel title shows the branch ([main]) and each entry gets a one-cell status column - M modified, A added, ? untracked, ! ignored (ignored entries are dimmed); changes deep inside a subdirectory mark the subdirectory. Statuses are computed on a background thread so huge repositories never block the UI. Built behind the default-on git cargo feature; git = false in the config disables it at runtime.

Archives: Enter on a .zip, .tar or .cpio - plain or wrapped in .gz, .xz, .bz2 or .zst, with the usual short spellings (.tgz, .txz, .tbz2, .tzst) - browses it like a directory (the panel title shows archive://path). cpio is read in all three of its header shapes - newc/crc, the portable octal odc, and the old binary one in either byte order - and a hard link inside one lists and opens as the file it shares its bytes with.

ar archives open too, which is how a .a static library lists its members, and a Debian package (.deb, .udeb) opens as one tree rather than three: debian-binary at the root, the metadata and maintainer scripts under CONTROL/, and everything the package installs under CONTENTS/.

FISH: cd fish://[user@]host[:port][/path] puts a panel on a server that has a shell but no SFTP subsystem. It is the same SSH connection, the same authentication and the same host-key dialog; what differs is what happens after login. Every operation is one small command, and the listing comes back as NUL-separated records rather than ls -l output, so a filename containing a space, a newline or a -> survives - which ls -l cannot promise. stat(1) is used where the server has it and an ls-based fallback where it does not.

FTP: cd ftp://[user[:password]@]host[:port][/path] connects a panel to an FTP server - no user means the anonymous login. Listings prefer MLSD, which says what everything is, and fall back to LIST where the server is too old for it. Browsing, F3, F5 in both directions, F6, F7 and F8 all work; FTP has no symlinks and no way to change ownership, so those report that rather than pretending. Every transfer needs a connection of its own, so a small pool of logged-in ones is kept and reused: one login covers a whole session of listing and copying.

rclone: cd rclone://<remote>[/path] puts a panel on anything rclone reaches - S3, Google Drive, Dropbox, B2, WebDAV, Swift and the forty-odd others - using the config rclone already has on that machine. Listing, F3 and F5 out work; the panel is read-only, because writing back is rclone's copyto and belongs with the progress reporting rather than with the listing. One integration instead of forty protocols is the whole argument: rclone lsf and rclone cat are a smaller thing to depend on, and a much smaller thing to get wrong. Without rclone installed, opening one says so.

Ctrl+X A lists everywhere a panel can go: the open archives and live SFTP connections, with the panel each one belongs to, and under them what the machine has mounted - every mount point df knows, with what it is and how much room is left. Enter goes there (a connection is reused, so no second login), f frees a connection or an archive - the panel returns to a local directory and an idle connection is forgotten - and a mount point says it is not rcmd's to free. Alt+F1 and Alt+F2 open the same list for the left and right panel by name rather than for the active one, which is where Far keeps its drive menu.

rar, 7z, lha/lzh, arj and cab browse through an installed 7z (p7zip - rar needs its nonfree codec) or unrar, read-only and streamed one member at a time. Without one of those tools installed, opening one says which tool it wants rather than failing silently.

An mbox (.mbox, .mbx, plain or compressed) browses as the messages in it, each numbered so name order is arrival order and named by its subject - decoded, since real mail writes subjects as =?UTF-8?B?...?=. Opening one gives an ordinary RFC 822 message, without the From separator line the mbox format puts between them.

A patch (.patch, .diff, plain or compressed) browses as the tree it would apply to: one entry per file it touches, holding that file's hunks and nothing else, filed under the directories its paths name. Unified, git, context and Subversion headers all start a section. Nothing is applied or reversed - this is a way of reading a patch, not of using one.

An ISO 9660 image (.iso) browses in place. Rock Ridge names, modes and symlinks are used where the disc carries them, Joliet's UTF-16 names where it does not, and the base format's shouted 8.3 names (minus the ;1 version suffix) where it has neither.

An RPM package (.rpm, source packages included) takes the same shape. CONTROL/header is the package's tags rendered as text - name, version, license, summary, description, what the payload is wrapped in - and any install scriptlets sit beside it as prein, postin, preun, postun. CONTENTS/ is the payload, which is a cpio stream under gzip, xz, lzma, bzip2 or zstd. Signatures are stepped over, not checked: a listing is not a claim that a package is authentic. F5 copies members out with the usual progress/overwrite dialogs, F3 views them; move, delete, and mkdir are disabled inside. Copying into an archive works for zip and tar: F5 with the destination panel inside one, or any destination written as path/to/archive.zip://dir. A member with the same name is replaced, not shadowed by a second copy of the name. The archive index loads once at open; each member read decodes only that member.

Alt+F5 packs: the marked entries (or the one under the cursor) go into an archive of their own. The name decides the container - .zip, .tar, .tar.gz/.tgz, .tar.xz/.txz, .tar.bz2/.tbz2 - and it is offered in the other panel's directory, named after the single entry or after the directory holding several, the way either would have been named by hand. One entry or a whole tree goes in; a name that is already an archive is added to rather than replaced, which is the same thing F5 into an open one does. Anything rcmd cannot write itself says so rather than producing an empty file.

Inside a .zip or .tar, F8 deletes, F6 renames (type a bare name - an absolute one would mean leaving the archive, which is a copy) and F7 makes a directory. Each batch rewrites the container once, so deleting five members costs one rewrite rather than five, and the original is only replaced when the new one is complete. The other formats - deb, rpm, iso, cpio and the 7z-backed ones - stay read-only.

Editor (F4): a built-in mcedit-style editor. F2 saves (atomically, preserving permissions and CRLF line endings), F3 starts marking (Shift+arrows also select), Ctrl+C/X/V copy/cut/paste, Ctrl+Z/Ctrl+Y undo/redo (unlimited, with typing bursts grouped), F7 searches with a smartcase regex and Shift+F7 repeats, F4 replaces interactively (Replace / Skip / All / Quit), F8 deletes the selection or line, Enter auto-indents, Ctrl+arrows hop words, and F10/Esc quits (asking Save/Discard/Cancel when modified). Known file types get syntect syntax colors (skipped for files over 2 MB - a 50 MB log still opens in about 0.2 s). On an SFTP panel F4 edits a local scratch copy and uploads it back when you close the editor. Set editor = "external" in the config to keep using $VISUAL/$EDITOR.

F9 in the editor opens its own menu bar - File, Edit, Search, Options - over the title row, with every entry naming the key that already does it: the menu is how you find the key, not a second way of working. Options > General is mc's editor options dialog: tab size, fill tabs with spaces (Tab inserts spaces up to the next stop, so the file has no tabs in it), return does autoindent, backspace through tabs (inside an indent one Backspace takes the whole stop rather than one space of it) and the wrap column the soft wrap folds at - window means the window's width, which is mc's dynamic wrapping. Left/Right nudge the numbers, Space ticks the switches, and OK applies them to the open editor and remembers them for the next session. They are edit_tab_size, edit_fill_tabs, edit_auto_indent, edit_backspace_tabs, edit_wrap_column, edit_line_numbers, edit_backups and edit_clipboard in the config file. Options > Syntax picks the highlighting by hand - every syntax syntect knows, or plain text - for a file whose name does not say what it is.

Getting around, and bookmarks: Alt+L goes to a line by number, Alt+K bookmarks the line the cursor is on, Alt+J and Alt+I walk to the next and previous bookmark, and Alt+O drops them all. A bookmark follows its text: inserting or deleting lines above one moves it with what it marked, rather than leaving it pointing at whatever slid into that line number. Alt+N draws mc's line-number gutter, with a * beside a bookmarked line so the bookmarks can be seen and not only jumped to. Ctrl+U undoes, as it does in mc, beside rcmd's Ctrl+Z.

The desktop clipboard: Ctrl+C and Ctrl+X also put the text on the system clipboard and Ctrl+V reads it, through wl-copy, xclip, xsel or pbcopy - whichever is installed. With none of them there, or with nothing in the clipboard, the editor's own clipboard stands, so copy and paste inside rcmd work either way. edit_clipboard = false keeps it to the editor.

Backups: with edit_backups on, every save first copies what is on disk to file~ - mc's "Do backups", one step back rather than a history.

Codepages (Alt+E): a file is bytes, and nothing in it says what they mean - so Alt+E in the viewer, the editor or a panel picks the codepage to read it in: UTF-8, the Latin and Cyrillic and Greek and Baltic single-byte sets, KOI8-R/U, CP866, and Shift_JIS, EUC-JP, GBK, Big5 and EUC-KR. The viewer re-reads at once (and the search follows, because it reads what you can see); the editor re-reads and writes back in the same codepage, so editing a KOI8-R file leaves a KOI8-R file. Since changing it means re-reading, the editor asks you to save first rather than dropping an edit. The title bar names the codepage whenever it is not UTF-8.

On a panel (Alt+E, or Left/Right → Character set) the codepage is what the filenames are read in. Unix names are bytes, so a directory written on a CP1251 or KOI8-R machine shows as replacement characters until the panel is told - and then it reads, sorts, filters and quick- searches as text. Names typed into a dialog on that panel are written back in the same codepage, so the file you make is the file the panel then shows. The panel title names the codepage while one is set.

Screens (Alt+` ): several editors and viewers can be open at once. Alt+` lists them - the panels are the first row, then every open screen with what it is on and whether it has unsaved changes - and Enter switches. Closing a screen (F10 in the editor, q in the viewer) lands back on the panels, which is where mc puts you too. Quitting rcmd with an editor still holding unsaved changes says how many and asks, whether or not the ordinary exit question is switched on.

Remote filesystems (SFTP): cd sftp://[user@]host[:port][/path] (or F9 → Left/Right → SFTP link) connects a panel to a server - user defaults to your login, path to the remote home. Authentication tries your ssh-agent, then the default ~/.ssh/id_* keys, then asks for a password; host keys are checked against ~/.ssh/known_hosts, and unknown hosts show a fingerprint dialog before being saved. The panel title shows the URL. Everything works panel-normally: F5/F6 transfer between local and remote (or between two remote directories) with the usual progress/overwrite dialogs, F7 creates server directories, F8 deletes on the server (permanently - there is no remote trash), F3 views, and F4 edits a local scratch copy that is uploaded back when the editor saved it. cd path stays on the server; plain cd (or any ~ path) returns the panel to the local filesystem, and closing the last remote panel closes the connection. Both panels can share one connection - put the same host on both sides, or compare a local tree against a remote one with Ctrl+X d and F5 the differences across. The hotlist stores sftp:// entries, so Ctrl+\ + Enter reconnects.

Driving rcmd from outside

Every instance listens on a unix socket of its own, and rcmd --remote hands it one line:

rcmd --remote 'cd /var/log'        # move the active panel
rcmd --remote 'select *.log'       # mark by mask (prints how many)
rcmd --remote 'action sort-size'   # any action, by the name a key would bind
rcmd --remote pwd                  # ...and the questions a script asks
rcmd --remote cursor               # the file under the cursor
rcmd --remote marked               # what is marked, space separated

The vocabulary is small on purpose, because action is the whole keymap: anything rcmd can be told to do by a key can be asked for by name (rcmd --remote 'action listing-brief'). status TEXT puts a line on the status row, which is how a script says it finished.

With several instances running, name one with --to PID; a command rcmd itself started - a [[commands]] entry, anything typed in the subshell - needs no such thing, because it is handed RCMD_SOCKET in its environment and --remote follows it. That is the whole plugin story: a shell script that can cd the panel, mark files and run any action is a plugin, and it needs no ABI, no embedded runtime and no versioned interface to be one.

[[commands]]
name = "jump to the newest log"
run = 'rcmd --remote "cd /var/log" && rcmd --remote "select *.log"'

The socket lives in $XDG_RUNTIME_DIR/rcmd/<pid>.sock (or /tmp/rcmd-<uid>/ where there is no runtime directory), the directory 0700 and the socket 0600: anyone who can reach it can already run commands as you. It goes when the instance does, and a socket left behind by a crash is cleaned up the next time something looks for one.

Configuration

~/.config/rcmd/config.toml is yours - rcmd only ever reads it, so comments and hand formatting survive. Everything rcmd changes itself (panel sort mode, hidden files, listing format, the hotlist, and every options-form toggle) goes to $XDG_STATE_HOME/rcmd/state.toml (~/.local/state/rcmd/state.toml by default) and takes precedence over the config file. State is sparse - only keys you actually changed in the UI are stored, so a config edit keeps working for everything else - and writes merge into the on-disk file, so several rcmd instances never clobber each other. The state keys (show_hidden, sort_key, sort_reverse, listing, [[hotlist]]) are still read from config.toml as your defaults; what the UI changes goes to the state file on top of them.

The settings live in one sectioned checkbox form under F9 → Options → Panel options, applied live: Layout (split direction and size, the per-panel mini status, and which of the menu bar / status line / command line / key bar are drawn), Panel (hidden files, lynx-like motion, mouse, auto-reload, git), Confirmation (ask before deleting / overwriting / quitting) and Shell and editor (persistent subshell, internal or external editor). The theme has a list of its own under F9 → Options → Appearance, because a skin is one of however many files are installed rather than a two-way switch:

theme = "mc"        # "dark", "bw", or the name of a theme file
keymap = "mc"       # or "modern" (= lynx-like motion on by default)
lynx = false        # Left/Right = parent/enter; in the options form
watch = true        # auto-reload panels on external changes
restore_other_dir = true  # the other panel starts where it was left
mouse = true        # click/double-click/wheel support
git = false         # git status column + branch in panel titles (off by
                    # default since 4.10: it is a column on every row)
editor = "internal" # or "external" ($VISUAL/$EDITOR for F4)
subshell = true     # persistent $SHELL behind Ctrl+O (false = one-shot exec)
brief_columns = 2          # name columns in the brief listing (1..6)
split = "vertical"         # or "horizontal" (panels stacked)
split_ratio = 50           # percent for the left/top panel, 20..80
show_menubar = false       # MC's permanent menu bar (F9 works either way; the window has its own)
show_mini_status = false   # a status row inside each panel (MC's)
show_free_space = true     # free space in each local panel's footer
show_status = true         # the cursor-entry row inside the active panel
                           # (messages and job progress show there too)
show_cmdline = true        # the command line
show_keybar = true         # the F1..F10 bar along the bottom
confirm_delete = true      # ask before F8 / Shift+F8
confirm_overwrite = true   # ask before overwriting during copy/move
confirm_exit = false       # ask before F10 quits (MC asks; rcmd does not)
confirm_hotlist_delete = true   # ask before dropping a hotlist entry
confirm_execute = false    # ask before Enter runs an [[open]] command
esc_timeout_ms = 250 # how long a lone Esc waits for its meta follow-up
                     # (1000 = MC's roomier window for typing Esc 1..0)
edit_tab_size = 8          # the built-in editor, F9 > Options > General
edit_fill_tabs = false     # Tab inserts spaces up to the next stop
edit_auto_indent = true    # Enter copies the line's leading whitespace
edit_backspace_tabs = false  # in an indent, Backspace takes a whole stop
edit_wrap_column = 0       # column the soft wrap folds at; 0 = the window
find_window = true         # find file: matches in a window of their own
desktop_open = true        # Enter on a file no [[open]] rule claims: xdg-open
                           # (open on macOS) when there is a display
                           # (false = straight into the panel listing)
edit_line_numbers = false  # the line-number gutter (Alt+N toggles it)
edit_backups = false       # keep the previous contents as file~ on save
edit_clipboard = true      # share the desktop clipboard (wl-copy/xclip/...)
show_hidden = true
sort_key = "name"   # name | ext | size | mtime | atime | ctime
                    # | owner | group | unsorted
sort_reverse = false
listing = "full"    # brief | full | long | tree | user
# "user" draws listing_format: a panel size (half/full), an optional
# repeat count 1-9, then fields with optional :width (:width+ grows) -
# name size bsize type mark mtime atime ctime perm mode nlink ngid nuid
# owner group inode, plus "space" and "|". MC's Full listing written out:
listing_format = "half type name | size | mtime"

[window]            # the window build only; see "In a window"
font = "DejaVu Sans Mono"   # family name or a .ttf/.otf path
font_size = 14

[keys]              # custom bindings on top of the preset
"ctrl+y" = "swap-panels"     # bare entries bind in the panel
[keys.viewer]                # ...and these inside the F3 viewer
"ctrl+w" = "wrap"            # quit wrap hex search search-next follow
                             # goto set-mark go-mark ruler nroff raw
                             # next-file prev-file hex-edit hex-save
[keys.dialog]                # ...and these wherever a dialog is open
"ctrl+j" = "ok"              # ok cancel next prev - a bound key stands
                             # in for Enter / Esc / Tab / Shift+Tab
[keys.editor]                # ...and these inside the F4 editor
"ctrl+q" = "quit"            # save quit mark replace search search-next
                             # block-copy block-move delete-line undo
                             # redo copy cut paste select-all wrap menu
                             # goto bookmark bookmark-next bookmark-prev
                             # bookmark-clear line-numbers
# key syntax:  [ctrl+][alt+][shift+]<key>  (f1..f20, letters, +, -, etc.)
# actions: help view edit copy move mkdir delete delete-perm select-group
#   unselect-group invert-selection quit shell reload swap-panels
#   toggle-hidden sort-name sort-ext sort-size sort-mtime sort-reverse
#   menu mark quick-search hotlist filter up-dir enter history-back
#   history-forward quick-view info-view user-menu listing-brief
#   listing-full listing-long listing-tree listing-user listing-cycle
#   other-same-dir other-open-dir sftp-link find-file panelize
#   compare-dirs dir-size dir-tree appearance learn-keys edit-config

[[highlight]]          # MC's filehighlight, as rules: first match wins
match = "*.tar.gz"     # a mask list on the name (*.c,*.h|*_test.*)...
color = "brightred"    # ...mc's colour names, #rrggbb or "default"

[[highlight]]
type = "exe"           # ...or what the entry is: dir linkdir exe link
color = "magenta"      #    broken file
bold = true            # optional; left out, the kind's own weight stands

[[hotlist]]                 # Ctrl+\ - a tree, as in mc
label = "projects"
path = "/home/you/git"

[[hotlist]]                 # an entry with `entries` is a group to
label = "Work"              # walk into rather than a place to go
entries = [
  { label = "api", path = "/srv/api" },
]

[[open]]                    # Enter on a matching file runs this
match = "*.pdf"             # match (glob) / regex (name) / type (file -b)
run = "zathura %f >/dev/null 2>&1 &"   # / directory (path): all given must hold

[[panelize]]                # saved panelize commands (Ctrl+S adds one)
name = "modified"
run = "git ls-files -m"

[[commands]]                # F2 user menu; key = "..." binds directly
name = "git status"
run = "git status | less"

Syntax files

The editor highlights with syntect, which speaks .sublime-syntax. Drop your own into ~/.config/rcmd/syntax/ and they join the built-in list - by extension, and in F9 > Options > Syntax inside the editor. A file that will not parse costs itself and a note on the editor's status line, never the highlighting of everything else. (mc's own syntax format is a different language and is not read.)

Skins

A theme that is not one of the three built in (mc, dark, bw) is a file, looked up by name in ~/.config/rcmd/themes/ and then in mc's skin directories - ~/.local/share/mc/skins, /usr/local/share/mc/skins, /usr/share/mc/skins. rcmd's own format is TOML naming the fields it sets, over an optional base, so a skin can be three lines:

base = "dark"            # mc | dark | bw - the palette to patch (default mc)
dir_fg = "brightblue"
panel_bg = "#1e222a"
header_fg = "color214"

The fields are panel_fg panel_bg dir_fg exec_fg broken_fg header_fg mark_fg select_fg select_bg dialog_fg dialog_bg error_fg error_bg help_fg help_bg help_header_fg prompt_fg key_fg key_bg label_fg label_bg menu_fg menu_bg menu_hot_fg menu_sel_fg menu_sel_bg dialog_hot_fg. A colour is one of mc's names (black, brightgreen, brown, lightgray, ...), #rrggbb, colorN (0-255), rgbRGB (three digits 0-5), grayN (0-23), or default for the terminal's own.

mc's skins work as they are: -S julia256 reads /usr/share/mc/skins/julia256.ini and maps its [core], [dialog], [error], [help], [filehighlight] and [buttonbar] sections onto those fields. What rcmd draws for itself - the frames, the menus - is not taken from the skin, so a skin is read for its colours and nothing else. F9 → Options → Appearance lists everything found and switches on Enter, and the choice outlives the session.

Development

cargo test --workspace                              # unit tests (rcmd-core is TUI-free)
cargo clippy --workspace --all-targets -- -D warnings
python3 tests/e2e/run.py                            # drives the real binary in a pty
just check                                          # all of the above
just gui                                            # the window build

The e2e suite drives rcmd through a pty and so covers only the terminal front end; what the two share is covered by the unit tests either way. rcmd-egui can render a frame and exit without a pair of eyes on it, which is how docs/rcmd-egui.png is made:

cargo run -p rcmd-egui --features screenshot   # writes $EFRAME_SCREENSHOT_TO
RCMD_EGUI_KEYS='ctrl+o,l,s,enter' cargo run -p rcmd-egui  # keys, as if typed

$RCMD_EGUI_KEYS plays keys in one per frame, spelled the way config.toml spells them, which is how the pictures above of screens that need a keystroke were taken. (A literal comma cannot be spelled: it separates.)

The e2e suite includes an SFTP scenario that spins up a local paramiko server (pip install paramiko; skipped when unavailable).

Workspace layout: crates/rcmd-core (panel/fs logic, no TUI deps), crates/rcmd-edit (editor buffer/undo/search, TUI-free; syntect behind the syntax feature), crates/rcmd-tui (the file manager itself, as a library, plus the terminal binary rcmd), and crates/rcmd-egui (the window front end, the binary of the same name, ~3,100 lines on top of that library). CI runs fmt, clippy and the unit tests on Linux and macOS, and the pty e2e suite on Linux - it drives the binary through /dev/pts and installs shells to test them, which is a Linux job. Licensed MIT.