daemon: a scrolled-back view goes stale as output arrives and jumps on the next scroll #158

Closed
opened 2026-10-18 12:41:50 +09:00 by natsukium · 0 comments
Owner

Raised on the GitHub mirror by an embedder that holds the viewport itself (https://github.com/felis-terminal/felis/issues/4); felis's own window has the same defect.

Problem

While a window is scrolled back and the program keeps printing, the shown rows stay put but the offset they were composed at goes stale, and the next scroll jumps.

  • The daemon keeps a browsing subscriber's lines_from_bottom unchanged as rows enter scrollback: crates/felis-daemon/src/serve/streaming.rs only re-clamps it (grid.clamp_viewport) and recomposes the browsed rows only when the offset changed (viewport_changed), so the window keeps showing the rows composed at the old position.
  • The next scroll step computes its request from that stale offset (crates/felis-client/src/app_methods.rs, the ScrollStep arms and scroll_by_rows), and the daemon composes it against the newer bottom, so the view lands pushed lines away from where the user was reading.

Found by reading the code; not yet reproduced in a window.

Expected

A scrolled-back view stays on the same lines as output arrives: the offset grows by the lines pushed into scrollback since the last compose, clamped by clamp_viewport (trimming and screen switches included), and ViewportState reports the grown offset so the client's next step starts from it. At the live bottom (0) nothing changes; the view keeps following output.

A test pins it: output arrives during browse, then one more scroll moves exactly one step from the lines on screen.

If the anchoring lands in felis-grid (from scrollback_total_pushed()) rather than in the daemon's subscriber state, an embedder holding its own viewport can call the same logic.

Raised on the GitHub mirror by an embedder that holds the viewport itself (https://github.com/felis-terminal/felis/issues/4); felis's own window has the same defect. ## Problem While a window is scrolled back and the program keeps printing, the shown rows stay put but the offset they were composed at goes stale, and the next scroll jumps. - The daemon keeps a browsing subscriber's `lines_from_bottom` unchanged as rows enter scrollback: `crates/felis-daemon/src/serve/streaming.rs` only re-clamps it (`grid.clamp_viewport`) and recomposes the browsed rows only when the offset changed (`viewport_changed`), so the window keeps showing the rows composed at the old position. - The next scroll step computes its request from that stale offset (`crates/felis-client/src/app_methods.rs`, the `ScrollStep` arms and `scroll_by_rows`), and the daemon composes it against the newer bottom, so the view lands `pushed` lines away from where the user was reading. Found by reading the code; not yet reproduced in a window. ## Expected A scrolled-back view stays on the same lines as output arrives: the offset grows by the lines pushed into scrollback since the last compose, clamped by `clamp_viewport` (trimming and screen switches included), and `ViewportState` reports the grown offset so the client's next step starts from it. At the live bottom (`0`) nothing changes; the view keeps following output. A test pins it: output arrives during browse, then one more scroll moves exactly one step from the lines on screen. If the anchoring lands in felis-grid (from `scrollback_total_pushed()`) rather than in the daemon's subscriber state, an embedder holding its own viewport can call the same logic.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
felis-terminal/felis#158
No description provided.