scroll-to-cursor only when the cursor moved — fixes minimap snap-back

The daemon attaches a CursorByte to every frame it produces,
including the frames the GPU's own Viewport sends trigger. The
CursorByte handler followed the cursor unconditionally, so a
minimap jump away from a stationary cursor snapped straight back:
jump → Viewport → frame + re-announced CursorByte →
scroll_to_cursor → snap, looping on every scrub move (the reported
jitter). Wheel-scrolling past the cursor's screen had the same
latent loop. The handler now compares the arriving position to
own_cursor and follows only genuine movement — scrolling away from
a cursor that isn't moving is the user's prerogative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-06-12 13:10:48 -04:00
parent f8027a0161
commit c9ec6ef02b
1 changed files with 16 additions and 4 deletions

View File

@ -465,7 +465,7 @@ struct State {
}
/// pmacs-gpu's own cursor position, mirrored from `CursorByte`.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct OwnCursor {
buffer_id: BufferId,
byte: u64,
@ -1895,17 +1895,29 @@ impl State {
return None;
}
}
self.own_cursor = Some(OwnCursor {
let arrived = OwnCursor {
buffer_id,
byte: byte_pos,
});
};
let moved = self.own_cursor != Some(arrived);
self.own_cursor = Some(arrived);
self.cursor_fresh = self.current_buffer_id == Some(buffer_id);
// Session S1 — keep the caret on screen (Q#S2). When the
// cursor leaves the visible slice (arrows past an edge,
// PageUp/Down), scroll to follow it, re-shape the new
// slice, and re-declare the scoped Viewport so the
// producer ships spans for what's now visible.
if self.scroll_to_cursor() {
//
// Only when the cursor MOVED. The daemon attaches a
// CursorByte to every frame it produces — including
// the frames our own Viewport sends trigger — so an
// unconditional follow snapped the viewport back to
// a stationary cursor on every minimap jump / scrub
// (and on any wheel scroll past the cursor's screen):
// jump → Viewport → frame + re-announced CursorByte →
// snap, in a loop. Scrolling away from a cursor that
// isn't moving is the user's prerogative.
if moved && self.scroll_to_cursor() {
// Pure scroll: retained lines keep their shape
// caches; only newly exposed lines shape.
self.rebuild_lines_reusing_scroll();