Page Mode Scrolling in the tui-logger Smart Widget: Timestamp-Referenced Navigation Explained

Page mode scrolling in the tui-logger Smart Widget anchors the view to the timestamp of the bottom log line when you press Page Up, allowing stable navigation through filtered log history using LinePointer references instead of fragile line numbers.

The TuiLoggerSmartWidget in the gin66/tui-logger crate provides sophisticated log navigation through page mode scrolling, a mechanism that uses timestamp-referenced anchoring to maintain view stability. Unlike standard terminal scrolling that relies on line offsets, this feature preserves your position when filtering criteria change. This article examines the implementation details in the source code and demonstrates how to leverage this feature in your terminal applications.

What Is Page Mode Scrolling?

Page mode is a special navigation state activated when the user presses Page Up. Upon activation, the widget captures the timestamp of the event currently displayed on the bottom line of the visible log area and uses it as a fixed anchor point for subsequent navigation.

While operating in page mode, the following key bindings control movement through the log history:

  • Page Up: Moves the view approximately half a page upward (toward older events)
  • Page Down: Scrolls exactly 10 events downward (toward newer events) — functional only while in page mode
  • Escape: Exits page mode and returns the widget to normal "follow-tail" scrolling behavior

How Timestamp-Referenced Scrolling Works

The core innovation of page mode is its reliance on timestamp-referenced scrolling rather than simple line-number offsets. The system tracks navigation through LinePointer structures that identify specific events and sub-lines within multi-line log records.

The LinePointer Structure

As implemented in the widget state management code, a LinePointer contains:

LinePointer { event_index, subline }

This pointer identifies the exact event in the history buffer and the specific sub-line within that event if the log entry spans multiple terminal rows.

State Persistence Across Filters

The widget stores three critical pointers in TuiWidgetInnerState (defined in src/widget/inner.rs):

pub struct TuiWidgetInnerState {
    // ...
    pub(crate) opt_line_pointer_center: Option<LinePointer>,
    pub(crate) opt_line_pointer_next_page: Option<LinePointer>,
    pub(crate) opt_line_pointer_prev_page: Option<LinePointer>,
    // ...
}

When opt_line_pointer_center is Some, the widget operates in page mode. The timestamp of the anchored event remains fixed at the bottom of the view, ensuring that changing filter settings or target-focus toggles does not cause the display to jump. As documented in src/lib.rs (lines 93-96):

"The timestamp of the event at event history's bottom line is used as reference. This means, changing the filters … should work as expected without jumps in the history."

Implementation Details

State Management in inner.rs

State transitions occur in TuiWidgetState::transition within src/widget/inner.rs (lines 135-138). When key events are received, the state updates the line pointers accordingly:

PrevPageKey => self.opt_line_pointer_center = self.opt_line_pointer_prev_page,
NextPageKey => self.opt_line_pointer_center = self.opt_line_pointer_next_page,
EscapeKey   => self.opt_line_pointer_center = None,

Pointer Calculation During Rendering

After each render cycle in src/widget/standard.rs (lines 125-139), the widget examines the lines actually drawn (rev_lines) to calculate navigation boundaries for the next operation:

state.opt_line_pointer_next_page = if can_scroll_down {
    rev_lines.first().map(|l| l.0)
} else { None };
state.opt_line_pointer_prev_page = if can_scroll_up {
    rev_lines.last().map(|l| l.0)
} else { None };

The first visible line becomes the "next page" marker, while the last visible line becomes the "previous page" marker, but only if scrolling in that direction is possible given the current buffer constraints.

Entering Page Mode

When the first Page Up event occurs, opt_line_pointer_center is None, triggering the widget to capture the bottom line's pointer as the reference anchor. Subsequent page navigation moves the view relative to this stored timestamp rather than recalculating from the current display offset.

Practical Usage Examples

Activating Page Mode

The following example demonstrates handling keyboard input to activate page mode scrolling:

use tui_logger::*;
use ratatui::crossterm::event::{KeyCode, KeyEvent};

fn main() -> Result<()> {
    init_logger(LevelFilter::Trace)?;
    let state = TuiWidgetState::new();

    // Generate sample log records
    for i in 0..30 {
        info!("record {}", i);
    }

    loop {
        terminal.draw(|f| {
            let smart = TuiLoggerSmartWidget::default()
                .state(&state);
            f.render_widget(smart, f.size());
        })?;

        if let Ok(event) = crossterm::event::read() {
            if let crossterm::event::Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::PageUp => state.transition(TuiWidgetEvent::PrevPageKey),
                    KeyCode::PageDown => state.transition(TuiWidgetEvent::NextPageKey),
                    KeyCode::Esc => state.transition(TuiWidgetEvent::EscapeKey),
                    _ => {}
                }
            }
        }
    }
}

Maintaining View Stability During Filter Changes

Page mode preserves your position when modifying log filters:

// Navigate to an older log slice
state.transition(TuiWidgetEvent::PrevPageKey);

// Adjust filter settings - the view remains anchored
state.set_level_for_target("my_target", LevelFilter::Warn);
// Display updates to show only Warn+ logs without jumping to the tail

Integration from the Demo Application

The official demo in examples/demo.rs shows the standard integration pattern:

match key.code {
    KeyCode::PageUp => state.transition(TuiWidgetEvent::PrevPageKey),
    KeyCode::PageDown => state.transition(TuiWidgetEvent::NextPageKey),
    KeyCode::Esc => state.transition(TuiWidgetEvent::EscapeKey),
    // ... other key handlers
}

Summary

  • Page mode scrolling activates when you press Page Up in a TuiLoggerSmartWidget, anchoring the view to the timestamp of the bottom visible line.
  • The system uses LinePointer structures containing event_index and subline to reference specific log events rather than relying on volatile line numbers.
  • Three pointers manage navigation state: opt_line_pointer_center (current anchor), opt_line_pointer_next_page, and opt_line_pointer_prev_page, all stored in TuiWidgetInnerState.
  • Timestamp-referenced scrolling ensures the view remains stable when changing filters or focus targets, as the anchor point persists regardless of which events are currently visible.
  • Page Down scrolls 10 events toward newer logs, while Escape exits page mode and resumes tail-following behavior.

Frequently Asked Questions

How do I enter page mode scrolling in the tui-logger Smart Widget?

Press Page Up while the widget has focus. The first press captures the current bottom line's timestamp as an anchor and moves the view up by approximately half a page. Subsequent Page Up presses continue navigating backward through the log history, while Page Down moves 10 events forward toward newer entries.

Why does my log view jump when I change filters in normal mode but not in page mode?

In normal "follow-tail" mode, the view always displays the most recent logs, so applying filters immediately redraws from the tail. In page mode, the opt_line_pointer_center anchor fixes the view to a specific timestamp. When filters change, the widget recalculates which lines to show around that anchored timestamp without changing the visible time range, preventing jumps.

What is the difference between scrolling with Page Down in page mode versus normal arrow key scrolling?

In page mode, Page Down moves exactly 10 events toward newer logs as defined in the state transition logic. Normal arrow key scrolling typically moves line-by-line or follows the tail continuously. Additionally, Page Down only functions after page mode has been activated by Page Up; attempting to use it in follow-tail mode has no effect on the anchored view.

Where is the page mode state stored in the tui-logger codebase?

The navigation state resides in TuiWidgetInnerState within src/widget/inner.rs, specifically in the opt_line_pointer_center, opt_line_pointer_next_page, and opt_line_pointer_prev_page fields. The rendering logic in src/widget/standard.rs updates these pointers after each draw cycle, while TuiWidgetState::transition in src/widget/inner.rs handles the key event mapping that modifies the center pointer.

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 →