FileCtrl is a light, opinionated, responsive, theme-able, and simple Text User Interface (TUI) file manager for Linux and macOS
You can download and install a pre-built binary for Linux or macOS:
curl -sL https://github.com/andornaut/filectrl/releases/latest/download/filectrl-linux -o filectrl
chmod +x filectrl
sudo mv filectrl /usr/local/bin/On macOS, allow the unsigned filectrl binary to be executed:
xattr -d com.apple.quarantine filectrlgit cloneandcdinto this repository- Run
cargo build --release && sudo cp target/release/filectrl /usr/local/bin/
Run filectrl --help to view the available command line arguments and options:
Usage: filectrl [-c <config>] [--write-default-config] [--write-default-themes] [--colors-256] [--] [<directory>]
FileCTRL is a light, opinionated, responsive, theme-able, and simple Text User Interface (TUI) file manager for Linux and macOS
Positional Arguments:
directory path to a directory to navigate to
Options:
-c, --config path to a configuration file
--write-default-config
write the default config to ~/.config/filectrl/config.toml,
then exit
--write-default-themes
write default theme files to ~/.config/filectrl/, then exit
--colors-256 force 256-color theme (disables truecolor detection)
--help, help display usage information
When you copy/cut a file or directory, FileCtrl puts ${operation} ${path} into your clipboard buffer
(where operation is "cp" or "mv").
If you then paste into a second FileCtrl window, this second instance of FileCtrl will perform the equivalent of:
${operation} ${path} ${current_directory}, e.g. cp filectrl.desktop ~/.local/share/applications/.
Under the hood, FileCtrl doesn't actually invoke cp or mv, but implements similar operations using the Rust standard library.
Normal mode
| Actions | Shortcuts |
|---|---|
| Quit | q |
| Navigate | ←/h, ↓/j, ↑/k, →/l |
| Go to home directory | ~ |
| Go to parent directory | ←/b/Backspace |
| Open | →/f/l/Enter/Space |
| Open custom | o |
| Select first row | Home/g/^ |
| Select last row | End/G/$ |
| Jump to middle row | z |
| Page down | Ctrl+f/Ctrl+d/PgDn |
| Page up | Ctrl+b/Ctrl+u/PgUp |
| Delete | Delete |
| Filter | / |
| Clear filter/alerts/clipboard/progress | Esc, a, c, p |
| Refresh | Ctrl+r/F5 |
| Rename | r/F2 |
| New window | w |
| Open terminal | t |
| Copy/Cut/Paste selected | Ctrl+c, Ctrl+x, Ctrl+v |
| Sort by name, modified, size | n, m, s |
| Toggle help | ? |
Prompt mode
| Actions | Shortcuts |
|---|---|
| Submit | Enter |
| Cancel | Esc |
| Reset to initial value | Ctrl+z |
| Move cursor | ←/→ |
| Move cursor by word | Ctrl+←/Ctrl+→ |
| Jump to line start/end | Ctrl+a/Ctrl+e, Home/End |
| Select text | Shift+←/Shift+→ |
| Select to beginning/end of line | Shift+Home/Shift+End |
| Select by word | Ctrl+Shift+←/Ctrl+Shift+→ |
| Select all | Ctrl+Shift+A |
| Copy/Cut/Paste text | Ctrl+c, Ctrl+x, Ctrl+v |
| Delete before/after cursor | Backspace/Delete |
Note
Ctrl+Shift keybindings require a terminal that supports the kitty keyboard protocol (e.g. Alacritty). tmux users must also add the following to ~/.tmux.conf:
set -g extended-keys on
set -ga terminal-features ",*:extkeys"
The configuration is drawn from the first of the following:
- The path specified by the command line option:
--config - The default path, if it exists:
~/.config/filectrl/config.toml - The built-in default configuration
Run filectrl --write-default-config to write the default configuration to ~/.config/filectrl/config.toml.
| Keyboard key | Description |
|---|---|
| f | Open the selected file using the default application configured in your environment |
| o | Open the selected file using the program configured by: openers.open_selected_file |
| t | Open the current directory in the program configured by: openers.open_current_directory |
| w | Open a new filectrl window in the terminal configured by: openers.open_new_window |
# Use [openers.linux] on Linux, or [openers.macos] on macOS.
# %s is replaced by the relevant path at runtime.
[openers.linux]
# %s will be replaced by the path to the current working directory:
open_current_directory = "alacritty --working-directory %s"
open_new_window = "alacritty --command filectrl %s"
# %s will be replaced by the path to the selected file or directory:
open_selected_file = "pcmanfm %s"
[openers.macos]
open_current_directory = "open %s"
open_new_window = "open -a Terminal %s"
open_selected_file = "open %s"All colors can be changed by editing the [theme] (truecolor) and [theme256] (256-color) sections inline in the configuration file:
filectrl --write-default-config
vim ~/.config/filectrl/config.tomlYou can see all of the available theme variables in the default configuration.
You can store themes in separate files and switch between them by setting theme_file and/or theme256_file in config.toml:
# Replaces the inline [theme] section with the contents of the specified file
theme_file = "my-theme.toml"
# Replaces the inline [theme256] section
theme256_file = "my-theme-256.toml"- Relative paths are resolved from the directory containing the config file (e.g.
~/.config/filectrl/) - Absolute paths are used as-is
- When set, the external file completely replaces the corresponding inline
[theme]/[theme256]section - If the file doesn't exist or can't be parsed, FileCtrl exits with an error
To get started with custom themes, export the built-in defaults as standalone files:
filectrl --write-default-themes
# Creates:
# ~/.config/filectrl/default-theme.toml
# ~/.config/filectrl/default-theme-256.tomlThen copy, rename, and edit them:
cp ~/.config/filectrl/default-theme.toml ~/.config/filectrl/solarized.toml
vim ~/.config/filectrl/solarized.tomlAnd point your config at the new file:
theme_file = "solarized.toml"You can make filectrl the default application for opening directories. Start by copying the filectrl.desktop file to ~/.local/share/applications/:
cp filectrl.desktop ~/.local/share/applications/
xdg-mime default filectrl.desktop inode/directory
update-desktop-database ~/.local/share/applications/- andornaut@github /til/rust
- See Cargo.toml for dependencies.
- Download files and folders of various types to test colors
- The
fixtures/directory contains a committed file tree for manual UI testing. Navigate into it withcargo runto exercise rendering edge cases:file_dates/— files with mtimes in each date-colour bucket (< 1 min, < 1 hour, < 1 day, < 1 month, < 1 year, > 1 year)file_sizes/— sparse files covering every size-colour bucket (bytes → GiB)file_types/— named pipe, symlinks, executable, and directory permission variants (other-writable, sticky)no_delete/— read-only parent directory (chmod 555); navigate here to trigger delete/rename permission errorsscrolling/— 53 entries with long filenames interspersed to exercise scrolling and multi-row truncation- Plus: executables, symlinks, hidden files, Unicode names, special characters, and long filenames
cargo clippy
cargo fix --allow-dirty --allow-staged
cargo test
cargo run
cargo build --release
./target/debug/filectrl
sudo cp ./target/debug/filectrl /usr/local/bin/
# Log to ./err
RUST_LOG=debug cargo run 2>errChanging cargo-husky configuration:
- Edit the
[dev-dependencies.cargo-husky]section of Cargo.toml rm .git/hooks/pre-commit(or other hook file)cargo cleancargo test- Verify that the changes have been applied to
.git/hooks/pre-commit
The project uses GitHub Actions to automate the release process. To release a new version:
-
Ensure you are on the
mainbranch and have pulled the latest changes. -
Create and push a new semantic version tag:
git tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0 -
The GitHub Actions release workflow will automatically trigger, build the binaries for Linux and macOS, and create a new GitHub Release with the artifacts.
