Herdr Pane Scrollback Buffer Management: Architecture and Implementation

Herdr treats pane scrollback as a pure, bounded byte buffer managed entirely within the application's state layer, enforcing fixed memory limits through the TerminalRuntime struct while supporting full persistence across sessions.

In the ogulcancelik/herdr terminal workspace application, scrollback buffer management follows a strict separation between state and runtime concerns. This design ensures predictable memory usage and seamless integration with the UI rendering pipeline, regardless of session duration or output volume.

Where Scrollback Lives in the Codebase

The scrollback implementation spans six core components that handle everything from memory allocation to UI visualization:

Component Role Key Source
TerminalRuntime Owns the runtime representation of a pane, including the bounded scrollback buffer and its size limit. [src/terminal/runtime.rs](https://github.com/ogulcancelik/herdr/blob/master/src/terminal/runtime.rs)
PaneRuntime Wraps TerminalRuntime to provide the high-level API used by the UI and server layers. [src/pane/terminal.rs](https://github.com/ogulcancelik/herdr/blob/master/src/pane/terminal.rs)
Workspace / Tab Propagate the user-configurable scrollback_limit_bytes down to each pane during creation. [src/workspace.rs](https://github.com/ogulcancelik/herdr/blob/master/src/workspace.rs) & [src/workspace/tab.rs](https://github.com/ogulcancelik/herdr/blob/master/src/workspace/tab.rs)
UI Scrollbars Render visual scrollbars only when the pane's scrollback buffer contains data. [src/ui/panes.rs](https://github.com/ogulcancelik/herdr/blob/master/src/ui/panes.rs)
Persistence Layer Serialises and restores scrollback bytes with workspace state. [src/persist/snapshot.rs](https://github.com/ogulcancelik/herdr/blob/master/src/persist/snapshot.rs) & [src/persist/restore.rs](https://github.com/ogulcancelik/herdr/blob/master/src/persist/restore.rs)

Buffer Allocation and Memory Bounds

When a pane is instantiated, Herdr stores the scrollback_limit_bytes value (defaulting to 1024 KB) inside the TerminalRuntime struct:

pub struct TerminalRuntime {
    // …
    scrollback_limit_bytes: usize,
    // …
}

The runtime creates an internal ScrollbackBuffer that grows dynamically until it reaches the byte limit. Once the limit is exceeded, the oldest data is automatically discarded, guaranteeing a fixed memory footprint regardless of how long a session runs or how much output is generated.

Writing to the Scrollback Buffer

All terminal output flows through the runtime API, ensuring the scrollback remains synchronized with the visible screen. Data passes from PaneRuntime::write to TerminalRuntime::write, which appends bytes to both the primary screen buffer and the scrollback buffer simultaneously.

The system also handles clear-scrollback escape sequences (such as ESC [3J). In [src/pane/terminal.rs](https://github.com/ogulcancelik/herdr/blob/master/src/pane/terminal.rs), the runtime detects these sequences and discards existing scrollback contents:

if (!alternate_screen && contains_scrollback_clear_sequence(bytes)) {
    maybe_filter_primary_screen_scrollback_clear(...)
}

This keeps the buffer in sync with host terminal expectations, maintaining compatibility with agents like droid that rely on explicit scrollback clearing.

Reading and Rendering Scrollback

The UI layer never mutates the buffer directly. Instead, it queries the runtime for rendered views. The PaneRuntime::render method returns a RenderResult containing a scrollback_view field, which provides the exact byte slice needed for the current viewport.

In [src/ui/panes.rs](https://github.com/ogulcancelik/herdr/blob/master/src/ui/panes.rs), the UI determines scrollbar visibility based on buffer state:

let show_scrollbar = terminal.has_scrollback();

The has_scrollback() method inspects the underlying buffer size, ensuring scrollbars appear only when historical data exists.

User Interaction and Scroll Limits

Users interact with scrollback through multiple input methods, all handled in the UI layer:

  • Mouse wheel scrolling
  • Arrow key navigation
  • Scrollbar gutter dragging

When these events occur, the code in src/ui/panes.rs updates pane.scroll_offset and triggers a re-render. The runtime enforces bounds checking, preventing scroll offsets from exceeding available scrollback data.

Persistence Across Sessions

Herdr's scrollback buffer survives application restarts through the persistence layer. When a workspace is saved via persist/snapshot.rs, the scrollback bytes are serialised alongside pane state. During restoration via persist/restore.rs, the TerminalRuntime receives the original scrollback_limit_bytes value, ensuring consistent buffer constraints across sessions.

Summary

  • State-only architecture: Scrollback lives as pure data inside TerminalRuntime, separate from UI rendering code.
  • Bounded memory: The scrollback_limit_bytes parameter (default 1024 KB) enforces fixed memory usage through automatic eviction of old data.
  • Runtime API enforcement: All mutations pass through PaneRuntime::write and TerminalRuntime::write, with special handling for clear-scrollback escape sequences.
  • Conditional UI rendering: Scrollbars appear only when has_scrollback() returns true, based on actual buffer contents.
  • Full session persistence: Scrollback data serialises with workspace snapshots and restores with identical memory constraints.

Frequently Asked Questions

How does Herdr prevent scrollback from consuming unlimited memory?

Herdr enforces a strict byte limit through the scrollback_limit_bytes field in TerminalRuntime. When the buffer exceeds this limit (default 1024 KB), the runtime automatically drops the oldest bytes to maintain constant memory usage, regardless of session duration or output volume.

Can scrollback data survive application restarts?

Yes. Herdr serialises the scrollback buffer during workspace snapshots in src/persist/snapshot.rs and restores it via src/persist/restore.rs. When restored, the pane receives the same scrollback_limit_bytes configuration, maintaining consistent behavior across sessions.

How does Herdr handle the "clear scrollback" command from terminal applications?

The runtime detects clear-scrollback escape sequences (such as ESC [3J) in src/pane/terminal.rs using contains_scrollback_clear_sequence(). When detected, it immediately discards existing scrollback contents through maybe_filter_primary_screen_scrollback_clear(), ensuring compatibility with applications that expect scrollback clearing to work as in traditional terminals.

What determines when the scrollbar appears in the UI?

The UI layer in src/ui/panes.rs calls terminal.has_scrollback() to check for non-empty buffer contents. The scrollbar renders only when this method returns true, ensuring visual clutter is minimized for panes without historical output.

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 →