# How to Configure Scroll Offset and Cursor Centering Behavior in tuicr

> Configure tuicr scroll offset and cursor centering with zz zt zb commands in config toml for optimal viewing. Learn to control scroll position and center lines easily.

- Repository: [Almog Gavra/tuicr](https://github.com/agavra/tuicr)
- Tags: how-to-guide
- Published: 2026-08-02

---

**Set `scroll_offset` in `~/.config/tuicr/config.toml` to control the initial vertical scroll position, and use Vim-style `zz`, `zt`, and `zb` commands to dynamically center or align the cursor line in the viewport.**

The `tuicr` terminal diff viewer provides fine-grained control over scrolling behavior through both configuration file options and runtime keyboard commands. Whether you need to skip boilerplate headers on startup or dynamically reposition the cursor during code review, the scroll offset and cursor centering behavior in tuicr can be adjusted to match your workflow preferences.

## Setting the Initial Scroll Offset in Config

The `scroll_offset` configuration value determines how many lines are skipped from the top when `tuicr` first renders the diff pane.

In `src/config/mod.rs:133-134`, the `AppConfig` struct defines this field:

```rust
#[serde(default = "default_scroll_offset")]
pub scroll_offset: usize,

```

At startup, `src/main.rs:310-312` applies this value to the `App` instance:

```rust
app.diff_state.scroll_offset = config.scroll_offset;

```

To configure a starting offset of 4 lines, add this to your `~/.config/tuicr/config.toml`:

```toml
scroll_offset = 4

```

This is particularly useful when reviewing diffs that contain consistent header blocks or when you want to jump directly to relevant hunks.

## Centering the Cursor with zz

The `zz` command centers the cursor line vertically in the viewport, following standard Vim conventions.

### How the Key Sequence Works

When you press `z`, `tuicr` enters a pending state. In `src/main.rs:60-78`, the event loop captures this:

```rust
KeyCode::Char('z') => {
    pending_z = true;
}

```

On the second keypress, if it's another `z`, the application calls `App::center_cursor()`.

### The Centering Calculation

The actual scroll adjustment happens in `src/ui/diff_view.rs:365-374`. The helper method recalculates `app.diff_state.scroll_offset` based on:

- The cursor's current line position
- The viewport height
- The desired center alignment

The result places the active line in the middle of the visible area.

## Alternative Alignments: zt and zb

After the initial `z` keypress, pressing `t` or `b` provides top and bottom alignment respectively.

| Command | Function Call | Result |
|---------|-------------|--------|
| `zz` | `App::center_cursor()` | Cursor line centered in viewport |
| `zt` | `App::cursor_to_top()` | Cursor line at top of viewport |
| `zb` | `App::cursor_to_bottom()` | Cursor line at bottom of viewport |

The event handling logic in [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) implements this as follows:

```rust
if pending_z {
    pending_z = false;
    match key.code {
        KeyCode::Char('z') => app.center_cursor(),
        KeyCode::Char('t') => app.cursor_to_top(),
        KeyCode::Char('b') => app.cursor_to_bottom(),
        _ => {}
    }
}

```

## Controlling Cursor Line Highlight

Related to cursor visibility, the `cursor_line` boolean setting toggles the visual highlight on the current line.

Defined in `src/config/mod.rs:123`:

```rust
#[serde(default = "default_cursor_line")]
pub cursor_line: bool,

```

Applied in `src/main.rs:307-309`:

```rust
app.cursor_line = config.cursor_line;

```

To disable the highlight:

```toml
cursor_line = false

```

## Modifying Scroll Offset at Runtime

`tuicr` does not expose a direct command to modify `scroll_offset` while the application is running. However, you can achieve equivalent behavior through:

1. **Cursor movement + recentering**: Navigate with `j`/`k`, then press `zz` to reposition the view
2. **Configuration change**: Edit `scroll_offset` in [`config.toml`](https://github.com/agavra/tuicr/blob/main/config.toml) and restart `tuicr`

## Key Source Files Reference

| File | Purpose |
|------|---------|
| [`src/config/mod.rs`](https://github.com/agavra/tuicr/blob/main/src/config/mod.rs) | Defines `scroll_offset` and `cursor_line` in `AppConfig` |
| [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) | Applies configuration values and handles `zz`/`zt`/`zb` key sequences |
| [`src/ui/diff_view.rs`](https://github.com/agavra/tuicr/blob/main/src/ui/diff_view.rs) | Implements scroll offset calculations for cursor positioning |
| [`src/ui/diff_unified.rs`](https://github.com/agavra/tuicr/blob/main/src/ui/diff_unified.rs) / [`src/ui/diff_side_by_side.rs`](https://github.com/agavra/tuicr/blob/main/src/ui/diff_side_by_side.rs) | Render views using the calculated `scroll_offset` |

## Summary

- **Configure startup position**: Set `scroll_offset` in `~/.config/tuicr/config.toml` to skip initial lines
- **Center dynamically**: Press `zz` to center the cursor line (implemented in `App::center_cursor()`)
- **Align to edges**: Use `zt` for top alignment or `zb` for bottom alignment
- **Toggle highlight**: Set `cursor_line = false` to disable the cursor line indicator
- **Workaround runtime changes**: Combine cursor movement with `zz` since direct scroll offset commands aren't available

## Frequently Asked Questions

### How do I make tuicr start scrolled down past a license header?

Add `scroll_offset = N` to your `~/.config/tuicr/config.toml`, where N is the number of lines to skip. This value is read at startup from `src/config/mod.rs:133-134` and applied in `src/main.rs:310-312`.

### Why doesn't zz work immediately after I press z once?

`tuicr` uses a pending-z state machine as implemented in `src/main.rs:60-78`. The first `z` sets `pending_z = true`; you must press a second key (`z`, `t`, or `b`) within the timeout to trigger the corresponding action.

### Can I change scroll offset without restarting tuicr?

No direct command exists. According to the `agavra/tuicr` source code, `scroll_offset` is only read from configuration at startup. Navigate with `j`/`k` and use `zz`, `zt`, or `zb` to reposition the viewport dynamically instead.

### What's the difference between cursor_line and scroll_offset?

`cursor_line` is a boolean that controls visual highlighting of the active line (`src/config/mod.rs:123`), while `scroll_offset` is a numeric value controlling how many lines are hidden above the initial viewport (`src/config/mod.rs:133-134`). They operate independently.