Skip to content

fix(tickets): smooth cursor nav#113

Merged
StuBehan merged 1 commit into
mainfrom
fix/tickets-scroll-perf
Jul 1, 2026
Merged

fix(tickets): smooth cursor nav#113
StuBehan merged 1 commit into
mainfrom
fix/tickets-scroll-perf

Conversation

@StuBehan

@StuBehan StuBehan commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Holding ↑/↓ on the Tickets tab was jumpy, and crossing a group boundary made
the highlight appear to "bounce back up." This makes cursor navigation track
smoothly through a group's branches and straight on to the next group. The
selection logic was never wrong — this is all rendering/scroll.

Changes

  • Snap the scroll instead of animating it (the actual bounce)
    scrollTo(anchor: .center) was wrapped in a 0.15s animation and fired on
    every key-repeat. On a long list (100+ rows) the animations piled up, the
    scroll lagged the selection, then settled with an upward snap — which read as
    the highlight bouncing up. Dropping the animation pins the selection and
    tracks it smoothly.
  • Memoize the group derivationvisibleOutcomeGroups() re-ran the full
    grouping (a regex per ledger record + branch breakdown + sorts) on every
    body render, i.e. every selection change. Now cached and invalidated only
    when the ledger changes (handoffsRevision); the hideShipped filter still
    runs per call.
  • Anchor the header scroll to the header row, not the whole group container
    (which spans all its branches) — so scrollTo centers a small, consistent
    target whether you land on a header or a branch.
  • Highlight only the selected header row, not the entire group block —
    keeps the selection a consistent small marker at every row.

Testing

  • ./build.shBuild complete, zero warnings.
  • Diagnosed with live telemetry (temporary, since removed): the selection index
    moved perfectly monotonically (clean ±1 per keystroke, every index mapping to
    the right row) — which is how we confirmed the "bounce" was scroll/animation,
    not the selection.
  • Needs a manual eyeball (the panel can't run in CI): hold ↑/↓ through a
    multi-branch group and across boundaries — tracks smoothly, no bounce.

Related issues

Follow-up to the Tickets-tab repo-grouping work. No tracked issue.

@hiskudin hiskudin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff. Right diagnosis, right fix — no changes needed from me.

  • withAnimation(.easeOut(duration: 0.15)) around proxy.scrollTo(anchor: .center) firing on every key-repeat is exactly the bounce cause. Dropping the animation is the only fix that reads right at key-repeat cadence.
  • Memoization is correct: handoffsRevision didSet clears cachedOutcomeGroups, so ledger changes bust the cache; the hideShippedTickets filter is still applied per call on top, so toggling "hide shipped" or a PR flipping to shipped both still take effect without a ledger tick.
  • visibleOutcomeGroups() is only ever called from SwiftUI body renders + keyboard indexing (both main-thread), so the cache read/write stay serialized without an explicit lock. Consistent with the rest of PanelNav.
  • The .background moving from the whole groupRow container to just the header HStack fixes the "entire group lights up when a header is selected" perception cleanly, and .id(Self.headerID(group)) moving inside the header HStack is fine because that HStack is unconditionally rendered — proxy.scrollTo(id:) still finds it.

Ship it.

@StuBehan StuBehan merged commit ab68d8c into main Jul 1, 2026
6 checks passed
@StuBehan StuBehan deleted the fix/tickets-scroll-perf branch July 1, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants