This is a repository which contains a terminal file browser written in Rust.
I made the project with the aim for practicing Rust and this tool was almost created accroding to my own situations and demands.
./install.sh --target ~/.local/bin/hire
# Build with colemak keyboard layout
./install.sh --keymap colemak.toml --target ~/.local/bin/hireThere're three config files for hire: auto_config.toml, user_config.toml and keymap.toml.
By default they live in ~/.config/springhan/hire/. Set the HIRE_CONFIG_DIR
environment variable to use another directory, e.g.
HIRE_CONFIG_DIR=/tmp/hire-config hire; this is handy to run the app (or tests)
with a configuration that is independent from your own, so commands such as
gui_file_open_cmd are never triggered by accident.
When developing hire itself, src/config/mod.rs has a deliberate toggle which
switches the file names to auto_config_dev.toml, user_config_dev.toml and
keymap_dev.toml, so a new feature can be tested without touching the real
configuration. That is a developer-only local state; normal builds read the
production names above. See docs/ARCHITECTURE.md §6 for details.
Notice: The auto_config.toml is generated & edited by app.
File colors come from the LS_COLORS environment variable, like ls --color.
When it is not set (or is empty), hire uses a built-in dircolors palette
shipped in src/app/default_ls_colors.txt, so colours stay readable in CI, a
bare shell or a minimal container.
This file contains user-specific configuration options. Here are the available settings:
# Default shell to use for commands
default_shell = "bash"
# GUI commands to show in the interface to avoid a required key press.
gui_commands = ["lazygit", "vim"]
# Program to use for reading files, such as vim, cat, bat, etc.
file_read_program = "vim"
# Command of a GUI program to open the output file/directory, such as xdg-open/emacs.
# When it's empty, the path will be written into the output file instead.
gui_file_open_cmd = "emacsclient -n"This file defines key bindings for the application.
Defaultly, the keymap.toml will be copied to ~/.config/springhan/hire folder after running install.sh.
The format is:
keymap = [
# ... other keybindings ...
{ key = "k", run = "command" }
]
# Define custom shell commands that can be executed with a key binding
# Format: "shell_command *Whether refresh displaying files after command* *command*"
# Use "$." to substitute the currently selected file/directory
# E.g.
keymap = [
# ... other keybindings ...
{ key = "v", run = "shell_command true vim $." }
]Two extra arrays bind the keys of the attached shell view (see "Shell Sessions").
Unlike keymap, their key values are full key specs: a single character, an
optional modifier (C-, M-, S-) and a named key (Up, PageUp, Esc,
Enter, F5, …), e.g. "M-u", "C-b", "PageUp".
# Keys which scroll the shell history instead of being sent to the shell.
shell_scroll = [
{ key = "M-u", run = "scroll up" },
{ key = "M-e", run = "scroll down" },
]
# Keys of the scroll mode (`C-s [`).
shell_scroll_mode = [
{ key = "k", run = "scroll up" },
{ key = "j", run = "scroll down" },
{ key = "PageUp", run = "scroll page up" },
{ key = "PageDown", run = "scroll page down" },
{ key = "g", run = "scroll top" },
{ key = "G", run = "scroll bottom" },
{ key = "q", run = "scroll quit" },
]The commands are scroll up, scroll down, scroll page up,
scroll page down, scroll top, scroll bottom and scroll quit. The
defaults listed above (plus the arrow keys, C-p/C-n/C-b/C-f,
Home/End, Esc and Enter) stay in place when a section is missing; an
entry for a key which already has a default replaces that binding, so a single
key can be remapped without repeating the whole list.
You can get the arguments by:
hire --helpEdit Mode provides an enhanced interface for batch file operations. When activated (with edit_mode command), it allows you to:
-
Navigation:
- Move between files with
u/e(Colemak) ork/j(QWERTY) - Jump to top/bottom with
g/G - Scroll list with
v/V
- Move between files with
-
File Operations:
- Create new files/directories (
k/Kin Colemak,n/Nin QWERTY) - Mark files with
deletesign (d) - Mark files for batch operations:
m- Mark/unmark all filesSpace- Mark/unmark single file
- Create new files/directories (
-
Text Editing:
- Insert text at beginning/end of filenames (
H/Ain Colemak,I/Ain QWERTY) - Move to the next/previous word:
Alt-f/Alt-b; delete a whole word before/after the cursor:C-Backspace/Alt-d; delete the character after the cursor:C-d
- Insert text at beginning/end of filenames (
-
Exiting:
Enter- Apply edited content to current pathQ/Esc- Quit edit mode
The Output File feature allows you to write file/directory paths to a specified output file. This is useful for integration with other tools such as your editor. It's a really convenient way to get target file path with hire.
Key behaviors:
- When enabled, writes either:
- The full path of the currently selected file (if one is selected and you pressed key of
move rightcommand) - The current directory path (if you pressed
Enterin a path)
- The full path of the currently selected file (if one is selected and you pressed key of
- Automatically quits the application after writing
This feature should be used with --output-file argument:
hire --output-file /tmp/hire_output.txtHire remembers a working directory, which is shared between runs:
W(work_directory set) - set the current directory as the working directoryw(work_directory goto) - jump to the working directory
Whenever a working directory has been set, its path is shown on the right side of the state line (long paths are abbreviated).
tab_operation (default t) opens the tab menu:
| Key | Action |
|---|---|
n |
Create a new tab |
f / b |
Switch to the next / previous tab |
c |
Close the current tab |
d then a number |
Delete the tab with that number |
o |
Close every other tab |
s then a number |
Open a stored tab group |
S |
Store the currently open tabs as a group |
Removing a tab kills the shell sessions mounted on it, so whenever a mounted shell would be lost — closing the current tab (c), deleting a tab by number (d), closing the other tabs (o) or restoring a stored group (s) — hire asks first and only acts on y; any other key returns to the tab menu.
spawn_shell (default S) opens a shell that hire owns and shows it in the current tab. The shell is a real PTY session: it keeps running after it is detached, and one tab can hold several of them (like tmux windows).
S— create a new shell on the current tab and attach it.T(shell_menu) — list the shells which can be attached: the current tab's shells first (their numbers are unchanged, starting at0), then the shells of the other tabs grouped underTab N. Digits attach one,ncreates a new shell on the current tab,xfollowed by a number kills one, andDkills every shell of the current tab (the tab stays open; pressyto confirm).xandDonly ever touch the current tab.- Attaching a shell of another tab borrows it: hire switches to that tab internally (the title bar shows it while the shell is in front) and, on detach, refreshes the shell's own tab and then returns you to the tab you were on. So
C-s dputs you back where you started. The prefix commands keep working on the shell's own tab (C-s copens there,C-s n/pcycle its shells), because that is the tab the view belongs to. B(attach_shell) — go straight back to the shell this tab used last, without opening the shell menu. The tab remembers the shell which was attached last (Tab::active_shell), however it was chosen (S, the menu,C-s n/C-s porC-s <number>), so it follows you across detach/attach. On a tab without a shell,Bborrows the shell of another tab which was attached most recently, with the same "refresh and return" behaviour; an error is shown only when no other tab has a shell either.- The shell menu and the attached shell's status line name the program currently running in the shell (
vim,htop,nvim, …), not just the shell program it was started with. A shell sitting at its prompt shows its own name (bash,zsh), so several shells started with the samedefault_shellcan be told apart at a glance. - While a shell is attached every key is forwarded to it; the prefix chord
C-s(shell_prefix) gives hire a command. The status line only lists the prefix commands afterC-shas been pressed, so the bottom of the screen stays clean while the shell is used normally:
| Key | Action |
|---|---|
C-s d |
Detach: the file browser comes back, the shell keeps running on the tab |
C-s n / C-s p |
Attach the next / previous shell of the tab |
C-s <number> |
Attach the shell with that number (0-based) |
C-s c |
Create a new shell on this tab and attach it |
C-s x |
Kill the attached shell (press y to confirm) |
C-s D |
Kill every shell mounted on the current tab (press y to confirm) |
C-s o |
Kill every other shell of the tab and keep the attached one (press y to confirm) |
C-s [ |
Enter the scroll mode: browse the shell's output history |
C-s C-s |
Send a literal C-s to the shell |
-
Scrollback. Commands which print more than one screen (
ls -la,tree, build logs, …) stay readable:C-s [enters the scroll mode. The status line then showsSCROLL <n>and the configured keys;PageUp/PageDown(orC-b/C-f) page,k/j(or↑/↓,C-p/C-n) move one line,g/Homejumps to the oldest line andG/Endreturns to the bottom.q,EscorEnteralso return to the live screen. Scrolling back to the bottom leaves the mode automatically.- The prefix chord still works while scrolled:
C-sleaves the mode and arms the prefix, soC-s d,C-s x, … behave as usual. - Without entering the mode,
Alt+uscrolls up andAlt+escrolls down. Scrolling up enters the mode (so the keys no longer reach the shell); scrolling down to the bottom leaves it. - Both key sets are configurable in
keymap.toml(shell_scrollandshell_scroll_mode, see below); the defaults areM-u/M-eand the keys listed above. The status-line hint is built from the configured bindings.
-
Mouse and pasted text:
- The wheel belongs to the program in the shell by default. When it enabled mouse tracking (
vim,less,htop, …) hire forwards wheel, click and drag events to it, so its own mouse scrolling keeps working; when it did not, hire does not capture the mouse at all, so the terminal's native wheel and drag-selection work as usual (noShiftneeded). Shift+ wheel is hire's: it enters the scroll mode (or scrolls it when it is already open). This needs hire to receive the wheel, i.e. either the program enabled mouse tracking or the scroll mode is open; with no mouse reporting the terminal handles the wheel itself, so useC-s [orAlt+uinstead.- In the scroll mode hire captures the mouse, so the wheel scrolls the history freely regardless of the program's mouse mode; leaving the mode (or reaching the bottom) releases the mouse again. Drag-selection needs
Shiftonly while a program inside the shell has asked for the mouse (the terminal's usual override). - When the program enables bracketed paste (
ESC[?2004h), a paste arrives as a single bracketed paste, preserving newlines and CJK text. A paste while the scroll mode is open is ignored (pressqfirst). - Focus events are not forwarded.
- The wheel belongs to the program in the shell by default. When it enabled mouse tracking (
-
Numbers are 0-based and may have more than one digit: with 11 shells,
C-s 1 0attaches shell 10. A partial number is committed onEnter, when no further digit could still be valid, or after a short timeout; the status line shows the digits while they are being typed. -
The title bar shows how many shells the current tab has (
|1:2|means tab 1 has two shells) and the attached shell's position (SH 2/3). -
Leaving the shell view —
C-s dor the shell exiting — returns to the browser and re-reads the current directory, so files the shell created, renamed or deleted show up immediately (the selected file is kept). The same happens when a shell is killed and there is no shell left to attach. -
Images painted by a TUI inside a mounted shell (for example
yazipreviews) are shown when the terminal speaks the kitty graphics protocol: hire answers the program's terminal queries and forwards the image data, and the program's own unicode placeholders place it on screen. Programmes that fall back to sixel/iTerm2 images, and hire running inside tmux, keep the old behaviour (the image is not shown inside the shell).default_shellselects the program, andshell_prefixchanges the prefix chord:
default_shell = "bash"
shell_prefix = "C-s"Notes:
C-sis normally XOFF. Hire reads the terminal in raw mode and intercepts the chord before forwarding anything, so the shell never sees it. If you run hire inside tmux, or your terminal's ownC-shandling interferes, setshell_prefixto another chord (C-a,C-o,F12, …).- Shell sessions live inside the hire process: quitting hire kills them, closing a tab kills the shells mounted on it (the tab menu asks first when shells are mounted), and they are not handed over to a sudo session nor stored in
storage_tabs. hire --working-directoryopens a mounted shell as well, so it can be detached and the browser can be used without losing it.
Navigation Index allows quick jumping to specific items by entering their index number.
Usage:
- Activated by pressing the navigation index key (default
., which is keybinding forshow_navi_index) - Press keybindings for
navi_inputto insert index number (you can bind the middle line keys on your keyboard tonavi_input) - Press
Enterto jump to target item orEscto cancel jumping - Works in both normal mode and edit mode
An example of keymap configuration for navigation index:
keymap = [
# Other bindings.....
# An example for colemak keyboard layout.
{ key = "a", run = "navi_input 1" },
{ key = "r", run = "navi_input 2" },
{ key = "s", run = "navi_input 3" },
{ key = "t", run = "navi_input 4" },
{ key = "d", run = "navi_input 5" },
{ key = "h", run = "navi_input 6" },
{ key = "n", run = "navi_input 7" },
{ key = "e", run = "navi_input 8" },
{ key = "i", run = "navi_input 9" },
{ key = "o", run = "navi_input 0" }
]Every jump made with goto_operation, fzf_jump (and rg_search), or goto_file records the path you left. At most 30 paths are kept, the newest one is stored at the last place.
prev_path- jump back to the last recorded pathpath_history- open the path history page, which shows every recorded path numbered from01to30; press the number keys of a path to jump to it
An example of keymap configuration for path history:
keymap = [
# Other bindings.....
{ key = "b", run = "prev_path" }, # Jump back to the previous path
{ key = "H", run = "path_history" }, # Open the path history page
]Sometimes root privileges are needed in the middle of a browsing session. sudo_session starts a root instance of hire in the same terminal and hands the current tabs over to it, so the root instance opens exactly the tabs you had open:
keymap = [
# Other bindings.....
{ key = "Z", run = "sudo_session" },
]- Pressing the key suspends the current (unprivileged) instance, asks
sudofor the password on the normal screen and startssudo -E <hire> --sudo-session <state> --config-dir <dir>. - The new instance restores every tab (path, hidden-file flag and the file selected in the tab) and starts in the tab which was focused.
- Do the root work there, then quit it (
q, and confirm withywhen tabs are open). The root process exits and the original unprivileged instance comes back exactly where it was: this is how the root privileges are dropped manually. - The root instance runs with a throw-away copy of
auto_config.toml,user_config.tomlandkeymap.toml. Its configuration changes (new bookmarks, stored tabs) are discarded when it exits, so your real config files are never rewritten as root. - Changes of tabs made in the root instance are not synced back to the original instance.
- Files created or modified as root keep the root ownership; nothing is chowned automatically.
- Cursor move left:
C-b - Cursor move right:
C-f - Cursor move to beginning:
C-a - Cursor move to end:
C-e - Delete word:
Alt-Backspace(Also in edit mode) - Delete the whole word before the cursor:
C-Backspace(Also in edit mode; alsoC-h/C-won terminals without the kitty keyboard protocol) - Delete the whole word after the cursor:
Alt-d(Also in edit mode) - Cursor move to the end of the next word:
Alt-f(Also in edit mode) - Cursor move to the start of the previous word:
Alt-b(Also in edit mode) - Delete the character after the cursor:
C-d(Also in edit mode) - Expand command line:
Alt-Tab - Previous/next completion candidate:
C-p/C-n(also page the expanded command line view and switch prev/next history command) - Previous/next command history from the browser:
Up/Down(enters the command line and fills the newest / oldest history command)
- Image preview support
- Storage of specific tabs
- Keymap config support
- Optimize the editing ability for command line
- Add Edit mode for more convenient file editing
- Mountable shell sessions per tab (tmux-like attach/detach)
- Refactor the project
- Highlight for file preview
- Migrate to Rust 2024 Edition
MIT