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

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.

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 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.

// 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 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:

// 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, which establishes the prefix+[ binding to activate copy mode. Visual feedback and help text are generated by src/ui/keybind_help.rs, while 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 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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →