# Herdr Copy Mode Navigation Bindings: Complete Guide to Vim-Style Scrollback Control

> Master herdr copy mode navigation bindings for efficient scrollback control. Learn Vim-style navigation and text selection to streamline your workflow.

- Repository: [Can Celik/herdr](https://github.com/ogulcancelik/herdr)
- Tags: how-to-guide
- Published: 2026-05-31

---

**Herdr's copy mode uses vim-style key bindings (h/j/k/l) combined with traditional terminal shortcuts to navigate scrollback history, select text, and copy to the system clipboard via `handle_copy_mode_key` in [`src/app/input/copy_mode.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/app/input/copy_mode.rs).**

Herdr is a Rust-based terminal multiplexer that provides efficient scrollback navigation through its copy mode feature. The application implements a comprehensive set of **herdr copy mode navigation bindings** that combine familiar vim motions with standard terminal controls. When activated, Herdr switches to `Mode::Copy` and routes all keyboard input through the centralized `AppState::handle_copy_mode_key` handler to manage cursor movement and text selection.

## Entering Copy Mode

To access the scrollback buffer, press the default **prefix** (`Ctrl+b`) followed by **[**. This binding is defined in [`src/config/model.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/config/model.rs) and triggers the state transition to `Mode::Copy`. Once active, the application captures all subsequent keystrokes to navigate the buffer rather than sending them to the underlying process.

```rust
// Enter copy mode (default prefix = Ctrl-b)
app.handle_key(TerminalKey::new(KeyCode::Char('['), KeyModifiers::empty()));

```

## Basic Cursor Movement

Herdr supports dual input schemes for cursor navigation, accepting both arrow keys and their vim-style equivalents. All movement commands in this category invoke `move_copy_cursor` with delta parameters to update the virtual cursor position.

### Arrow Keys and Vim Motions

The following bindings control basic directional movement within the scrollback buffer:

- **Move cursor left**: `←` or `h` — Decrements column via `move_copy_cursor(..., 0, -1)`
- **Move cursor down**: `↓` or `j` — Increments row via `move_copy_cursor(..., 1, 0)`
- **Move cursor up**: `↑` or `k` — Decrements row via `move_copy_cursor(..., -1, 0)`
- **Move cursor right**: `→` or `l` — Increments column via `move_copy_cursor(..., 0, 1)`

## Scrolling Commands

For navigating large scrollback histories, Herdr provides page-based and absolute positioning commands handled by `scroll_copy_mode_page` and the history jump functions.

### Page and Half-Page Navigation

These bindings utilize `scroll_copy_mode_page` with directional integers and a boolean flag indicating half-screen movements:

- **Page up**: `PgUp` — Scrolls up one full screen (`scroll_copy_mode_page(..., -1, false)`)
- **Page down**: `PgDn` — Scrolls down one full screen (`scroll_copy_mode_page(..., 1, false)`)
- **Half-page up**: `Ctrl+u` — Scrolls up half a screen (`scroll_copy_mode_page(..., -1, true)`)
- **Half-page down**: `Ctrl+d` — Scrolls down half a screen (`scroll_copy_mode_page(..., 1, true)`)

### Jump to Buffer Edges

Absolute positioning commands move the cursor to the beginning or end of the scrollback history:

- **Scroll to top of history**: `g` — Jumps to the oldest line via `copy_mode_history_top`
- **Scroll to bottom (live view)**: `G` — Jumps to the newest line via `copy_mode_history_bottom`

## Line Navigation

Horizontal movement within a single line supports both standard terminal keys and vim-style line-position commands, implemented through `copy_mode_line_edge` and `copy_mode_first_non_blank`.

- **Go to beginning of line**: `Home` or `0` — Sets column to `0` (`copy_mode_line_edge(..., false)`)
- **Go to end of line**: `End` or `$` — Sets column to last visible column (`copy_mode_line_edge(..., true)`)
- **First non-blank on line**: `^` — Moves to the first non-space character (`copy_mode_first_non_blank`)

## Word and Paragraph Navigation

Advanced text navigation follows vim conventions for word-based movement and paragraph jumping. These commands utilize the `WordMotion` enum variants (`NextStart`, `PreviousStart`, `NextEnd`) via `copy_mode_word_motion`, and `copy_mode_paragraph` for block-based navigation.

- **Next word start**: `w` — Moves to start of next word (`WordMotion::NextStart`)
- **Previous word start**: `b` — Moves to start of previous word (`WordMotion::PreviousStart`)
- **Next word end**: `e` — Moves to end of next word (`WordMotion::NextEnd`)
- **Paragraph up**: `{` — Moves upward until blank line (`copy_mode_paragraph(..., -1)`)
- **Paragraph down**: `}` — Moves downward until blank line (`copy_mode_paragraph(..., 1)`)

## Implementation Details

The complete navigation system resides in [`src/app/input/copy_mode.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/app/input/copy_mode.rs) within the `handle_copy_mode_key` function (lines 86-155). This match block processes input in priority order: first checking for literal arrow keys and special keys (`Esc`, `Enter`, `Home`, `End`), then control-page combinations, and finally falling back to character-based commands through `copy_mode_command_char`.

The function signature and routing logic demonstrate how Herdr distinguishes between immediate scrolling actions and command-style movements:

```rust
// Example cursor movement down one line using both input methods
app.handle_key(TerminalKey::new(KeyCode::Down, KeyModifiers::empty()));
app.handle_key(TerminalKey::new(KeyCode::Char('j'), KeyModifiers::empty()));

// Jump to top of buffer history
app.handle_key(TerminalKey::new(KeyCode::Char('g'), KeyModifiers::empty()));

```

## Configuration and Default Bindings

The default keymap configuration is defined in [`src/config/model.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/config/model.rs), which establishes the `prefix+[` binding to activate copy mode. Visual feedback and help text are generated by [`src/ui/keybind_help.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/ui/keybind_help.rs), while [`src/ui/navigator.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/ui/navigator.rs) provides the cursor position updates during selection changes.

## Summary

- **Herdr copy mode navigation bindings** combine vim motions (h/j/k/l, w/b/e) with terminal standards (arrows, PgUp/PgDn) for intuitive scrollback control.
- All key handling routes through `handle_copy_mode_key` in [`src/app/input/copy_mode.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/app/input/copy_mode.rs) when the application state is `Mode::Copy`.
- Navigation functions include `move_copy_cursor` for basic movement, `scroll_copy_mode_page` for paging, and specialized functions for word/paragraph jumps.
- Default activation requires pressing `prefix+[` (typically `Ctrl+b` followed by `[`).
- The implementation supports both line-based and history-based absolute positioning via `g`/`G` and `0`/`$`/`^` commands.

## Frequently Asked Questions

### How do I enter copy mode in Herdr?

Press the default prefix key (`Ctrl+b`) followed by the left square bracket `[`. This binding is configured in [`src/config/model.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/config/model.rs) and transitions the application from normal mode to `Mode::Copy`, enabling all navigation bindings.

### What is the difference between `g` and `G` in Herdr copy mode?

The lowercase `g` command invokes `copy_mode_history_top` and jumps to the oldest line in the scrollback buffer, while uppercase `G` calls `copy_mode_history_bottom` to return to the newest line and live view. These correspond to "go to top" and "go to bottom" respectively.

### Can I use arrow keys instead of vim bindings?

Yes. Herdr accepts both arrow keys (`↑`, `↓`, `←`, `→`) and their vim equivalents (`h`, `j`, `k`, `l`) interchangeably. The `handle_copy_mode_key` function processes both input types through the same underlying `move_copy_cursor` logic, so you can mix navigation styles based on preference.

### Where is the copy mode logic implemented?

The core implementation resides in [`src/app/input/copy_mode.rs`](https://github.com/ogulcancelik/herdr/blob/main/src/app/input/copy_mode.rs), specifically within the `handle_copy_mode_key` function (lines 72-155). This file contains the state machine for `Mode::Copy` and defines all navigation functions including `move_copy_cursor`, `scroll_copy_mode_page`, and `copy_mode_word_motion`.