(s: ScrollBoxHandle, delta: number)
| 411 | // still clears — its async pendingScrollDelta drain means the actual |
| 412 | // delta isn't known synchronously (follow-up). |
| 413 | function translateSelectionForJump(s: ScrollBoxHandle, delta: number): void { |
| 414 | const sel = selection.getState(); |
| 415 | if (!sel?.anchor || !sel.focus) return; |
| 416 | const top = s.getViewportTop(); |
| 417 | const bottom = top + s.getViewportHeight() - 1; |
| 418 | // Only translate if the selection is ON scrollbox content. Selections |
| 419 | // in the footer/prompt/StickyPromptHeader are on static text — the |
| 420 | // scroll doesn't move what's under them. Same guard as ink.tsx's |
| 421 | // auto-follow translate (commit 36a8d154). |
| 422 | if (sel.anchor.row < top || sel.anchor.row > bottom) return; |
| 423 | // Cross-boundary: anchor in scrollbox, focus in footer/header. Mirror |
| 424 | // ink.tsx's Flag-3 guard — fall through without shifting OR capturing. |
| 425 | // The static endpoint pins the selection; shifting would teleport it |
| 426 | // into scrollbox content. |
| 427 | if (sel.focus.row < top || sel.focus.row > bottom) return; |
| 428 | const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); |
| 429 | const cur = s.getScrollTop() + s.getPendingDelta(); |
| 430 | // Actual scroll distance after boundary clamp. jumpBy may call |
| 431 | // scrollToBottom when target >= max but the view can't move past max, |
| 432 | // so the selection shift is bounded here. |
| 433 | const actual = Math.max(0, Math.min(max, cur + delta)) - cur; |
| 434 | if (actual === 0) return; |
| 435 | if (actual > 0) { |
| 436 | // Scrolling down: content moves up. Rows at the TOP leave viewport. |
| 437 | // Anchor+focus shift -actual so they track the content that moved up. |
| 438 | selection.captureScrolledRows(top, top + actual - 1, 'above'); |
| 439 | selection.shiftSelection(-actual, top, bottom); |
| 440 | } else { |
| 441 | // Scrolling up: content moves down. Rows at the BOTTOM leave viewport. |
| 442 | const a = -actual; |
| 443 | selection.captureScrolledRows(bottom - a + 1, bottom, 'below'); |
| 444 | selection.shiftSelection(a, top, bottom); |
| 445 | } |
| 446 | } |
| 447 | useKeybindings({ |
| 448 | 'scroll:pageUp': () => { |
| 449 | const s_0 = scrollRef.current; |
no test coverage detected