# bmrk — Bookmark Manager and Directory Navigator
**A fast, compact TUI for directory navigation and bookmark management.**
[](LICENSE)
[](https://crates.io/crates/bmrk)
[](https://github.com/holgertkey/bmrk/actions/workflows/ci.yml)
---
## What is bmrk?
**bmrk** is a compact inline TUI that fits in 8 rows and never takes over your terminal screen.
Navigate directories, manage bookmarks, search for folders and files — all from the keyboard.
The binary is `bmrk`. The shell wrapper `bm` wraps it and handles `cd` automatically — because a
process cannot change its parent shell's directory, a thin wrapper is required (the same approach
used by `fzf`, `zoxide`, and `autojump`).
---
## Features
- **Compact inline mode** — 8 rows, no fullscreen takeover, terminal fully restored on exit
- **Bookmarks** — save and jump to favorite directories; `bm myproject` navigates instantly
- **Interactive tree view** — directory navigation with expand/collapse
- **Fuzzy search** — fast asynchronous search with intelligent matching
- **Disk selection** — browse and switch between all drives/mount points (`d` key)
- **Mouse support** — click, double-click, scroll
- **Customizable** — TOML configuration with theme presets and custom colors
---
## Installation
### Step 1 — Install the `bmrk` binary
**From crates.io:**
```bash
cargo install bmrk
```
**From source:**
```bash
git clone https://github.com/holgertkey/bmrk.git
cd bmrk
cargo build --release
```
The binary is at `target/release/bmrk` (Linux/macOS) or `target\release\bmrk.exe` (Windows).
---
### Step 2 — Set up the `bm` wrapper
The repository includes three ready-made wrapper files. Each one integrates `bmrk` with a specific
shell so that `cd` works correctly. Pick the file that matches your environment.
---
#### `bm.bat` — Windows CMD
**What it does.** `bm.bat` is a batch script that runs `bmrk.exe` and redirects its stdout to a
temporary file. If the captured output is a valid directory path, it calls `cd /d` to change the
current session's directory. Non-directory output (help text, version, bookmark lists) is printed
as-is. The previous directory is saved in `%BMRK_PREV_DIR%` so that `bm -` can return to it.
Flags that do not trigger navigation (`-h`, `--help`, `-v`, `--version`, `-l`, `--list`, `-a`,
`--add`, `-c`, `-d`, `--del`) are passed straight through to `bmrk.exe`.
**Installation:**
1. Copy `bmrk.exe` and `bm.bat` to the same directory that is on your `PATH`
(e.g. `C:\Users\<YourName>\bin\`).
2. If that directory is not on your PATH yet, add it and open a new CMD window:
```
setx PATH "%PATH%;C:\Users\<YourName>\bin"
```
3. Test:
```
bm --version
bm
```
---
#### `bm.ps1` — Windows PowerShell
**What it does.** `bm.ps1` defines a `bm` function for PowerShell. It runs `bmrk.exe`, captures
stdout to a temporary file, and calls `Set-Location` if the result is a valid directory path.
The previous directory is stored in `$env:BMRK_PREV_DIR` for `bm -` support. Pass-through flags
(`-h`, `--help`, `-v`, `--version`, `-l`, `--list`, `-a`, `--add`, `-c`, `-d`, `--del`) are forwarded
directly to `bmrk.exe` without any cd logic. Compatible with Windows PowerShell 5.1 and
PowerShell 7+.
**Installation:**
1. Copy `bmrk.exe` to a directory on your PATH (e.g. `C:\Users\<YourName>\bin\`).
2. Open your PowerShell profile for editing:
```powershell
notepad $PROFILE
```
If the file does not exist yet, create it first:
```powershell
New-Item -ItemType File -Force $PROFILE
```
3. Add the following line to your profile:
```powershell
. "C:\path\to\bmrk\bm.ps1"
```
4. Reload the profile:
```powershell
. $PROFILE
```
5. Test:
```powershell
bm --version
bm
```
---
#### `bm.sh` — bash, zsh
**What it does.** `bm.sh` defines a `bm` function for POSIX-compatible shells (bash and zsh). It
runs `bmrk` via command substitution, captures stdout, and calls `cd` when the result is a valid
directory. The previous directory is stored in `$BMRK_PREV_DIR` for `bm -` support. Any output
that is not a directory path (help text, version, bookmark list) is echoed to the terminal
unchanged. For fish, use `bm.fish` instead (see below).
**Installation — bash:**
1. Copy the `bmrk` binary to a directory on your PATH:
```bash
cp target/release/bmrk ~/.local/bin/
```
2. Source the wrapper from your shell config:
```bash
echo 'source /path/to/bmrk/bm.sh' >> ~/.bashrc
```
3. Reload and test:
```bash
source ~/.bashrc
bm --version
bm
```
**Installation — zsh:**
Same steps as bash, but add to `~/.zshrc` instead of `~/.bashrc`.
---
#### `bm.fish` — fish
**What it does.** `bm.fish` defines a `bm` function for fish. Fish uses its own function-file
format and cannot source `bm.sh` directly, so this is a separate file. It runs `bmrk`, captures
stdout, and calls `cd` when the result is a valid directory. The previous directory is stored in
`$BMRK_PREV_DIR` for `bm -` support. Any output that is not a directory path (help text, version,
bookmark list) is echoed to the terminal unchanged.
**Installation:**
1. Copy the `bmrk` binary to a directory on your PATH:
```bash
cp target/release/bmrk ~/.local/bin/
```
2. Copy or symlink `bm.fish` into fish's functions directory:
```bash
cp /path/to/bmrk/bm.fish ~/.config/fish/functions/bm.fish
ln -s /path/to/bmrk/bm.fish ~/.config/fish/functions/bm.fish
```
3. Fish picks up functions in that directory automatically — no reload step needed. Test with:
```bash
bm --version
bm
```
---
## Usage
```bash
bm # Open interactive TUI (compact, 8 rows)
bm /path/to/dir # Open TUI at specific directory
bm myproject # Jump to bookmark (cd directly, no TUI)
bm myproject/src/app # Jump into a sub-directory of a bookmark
bm - # Return to previous directory
# Bookmark management
bm -l # List all bookmarks
bm -a work # Save current directory as 'work'
bm -a work /some/path # Save specific path as 'work'
bm -d work # Remove bookmark 'work'
bm -v / --version # Print version
bm -h / --help # Print help
```
### Keyboard shortcuts (inside TUI)
| `j` / `↓` | Move down |
| `k` / `↑` | Move up |
| `l` / `→` | Expand directory |
| `h` / `←` | Collapse directory; go to parent once collapsed (from the root row, always goes to parent) |
| `u` | Go to parent directory |
| `Backspace` | Go back (undo last navigation) |
| `Enter` | Go into directory (change root)|
| `q` | Exit and cd to selected dir |
| `Esc` | Exit without cd |
| `/` | Search |
| `Tab` | Quick jump (type a folder name to jump to it) |
| `m` | Create bookmark |
| `'` | Select bookmark |
| `d` | Disk selection |
| `c` | Copy selected item's path to clipboard |
---
## Configuration
Config file is created automatically on first run:
- **Linux/macOS**: `~/.config/bmrk/config.toml`
- **Windows**: `%APPDATA%\bmrk\config.toml`
Bookmarks are stored as `bookmarks.json` in the same directory. A background-built directory
index cache lives alongside them as `dir_index.txt` — it's a performance optimization, not user
data, so it's safe to delete to force a rebuild.
```toml
[appearance]
theme = "default" # default, gruvbox, nord, tokyonight, dracula, obsidian
max_name_length = 80 # Truncate long names in the middle (0 = disabled)
icons = "unicode" # "unicode" (▼▶) or "ascii" (v>)
show_cursor_path = true # Show the selected item's full path in the header (Tab mode too)
[appearance.colors]
# header_path_color = "cyan" # Path/label in the header row
# header_hints_color = "darkgray" # Key hints in the header row
# directory_color = "gray" # Directory names; also the header icon (▼)
# See docs/configuration.md for all available color fields
[behavior]
show_hidden = true
follow_symlinks = true
mouse_scroll_lines = 1
[keybindings]
search = ["/"]
create_bookmark = ["m"]
select_bookmark = ["'"]
select_disk = ["d"]
go_to_parent = ["u"]
copy_path = ["c"]
go_back = ["Backspace"]
quit = ["q"]
exit = ["Esc"]
[index]
enabled = true # Background directory index accelerating Tab/`/` under `roots`
refresh_hours = 24 # Rebuild once the cached index is older than this
# roots = ["/home/username"] # Defaults to the home directory
# ignore_dirs = ["node_modules", "target", ".git"] # Replaces the default skip-list, not merged
```
---
## How the wrapper works
`bmrk` writes its TUI to **stderr** (visible in the terminal) and the selected directory path to
**stdout**. The wrapper captures stdout via a temp file (CMD: `set /p`; PowerShell/bash: `$t`):
- If captured output is a **valid directory path** → `cd` to it (saves previous dir for `bm -`)
- If output is **empty** (Esc pressed) → do nothing
- Otherwise → print the output as-is (help, version, bookmark list)
| `bm` (TUI → `q`) | `/selected/path` | `cd` there |
| `bm myproject` | `/bookmarked/path` | `cd` there |
| `bm myproject/sub` | `/bookmarked/path/sub` | `cd` there |
| `bm -` | _(no bmrk call)_ | `cd` to previous dir |
| `bm -l` | `Bookmarks: …` (text) | Print it (passthrough)|
| `bm -a work` | `Bookmark 'work' added: …` | Print it (passthrough)|
| `bm -d work` | `Bookmark 'work' removed` | Print it (passthrough)|
| `bm --help` | Help text | Print it (passthrough)|
| `bm -v` | Version + build info | Print it (passthrough)|
| `bm` (TUI → Esc) | _(empty)_ | Do nothing |
---
## License
MIT — see [LICENSE](LICENSE).
See [CHANGELOG.md](CHANGELOG.md) for version history.