MCPcopy Create free account
hub / github.com/Noumena-Network/code / useVirtualScroll

Function useVirtualScroll

src/hooks/useVirtualScroll.ts:513–1136  ·  view source on GitHub ↗
(
  scrollRef: RefObject<ScrollBoxHandle | null>,
  itemKeys: readonly string[],
  /**
   * Terminal column count. On change, cached heights are stale (text
   * rewraps) — SCALED by oldCols/newCols rather than cleared. Clearing
   * made the pessimistic coverage back-walk mount ~190 items (every
   * uncached item → PESSIMISTIC_HEIGHT=1 → walk 190 to reach
   * viewport+2×overscan). Each fresh mount runs marked.lexer + syntax
   * highlighting ≈ 3ms; ~600ms React reconcile on first resize with a
   * long conversation. Scaling keeps heightCache populated → back-walk
   * uses real-ish heights → mount range stays tight. Scaled estimates
   * are overwritten by real Yoga heights on next useLayoutEffect.
   *
   * Scaled heights are close enough that the black-screen-on-widen bug
   * (inflated pre-resize offsets overshoot post-resize scrollTop → end
   * loop stops short of tail) doesn't trigger: ratio<1 on widen scales
   * heights DOWN, keeping offsets roughly aligned with post-resize Yoga.
  */
  columns: number,
  estimateItemHeight?: (index: number) => number | undefined,
)

Source from the content-addressed store, hash-verified

511 * the last N items regardless of what scrollTop claims.
512 */
513export function useVirtualScroll(
514 scrollRef: RefObject<ScrollBoxHandle | null>,
515 itemKeys: readonly string[],
516 /**
517 * Terminal column count. On change, cached heights are stale (text
518 * rewraps) — SCALED by oldCols/newCols rather than cleared. Clearing
519 * made the pessimistic coverage back-walk mount ~190 items (every
520 * uncached item → PESSIMISTIC_HEIGHT=1 → walk 190 to reach
521 * viewport+2×overscan). Each fresh mount runs marked.lexer + syntax
522 * highlighting ≈ 3ms; ~600ms React reconcile on first resize with a
523 * long conversation. Scaling keeps heightCache populated → back-walk
524 * uses real-ish heights → mount range stays tight. Scaled estimates
525 * are overwritten by real Yoga heights on next useLayoutEffect.
526 *
527 * Scaled heights are close enough that the black-screen-on-widen bug
528 * (inflated pre-resize offsets overshoot post-resize scrollTop → end
529 * loop stops short of tail) doesn't trigger: ratio<1 on widen scales
530 * heights DOWN, keeping offsets roughly aligned with post-resize Yoga.
531 */
532 columns: number,
533 estimateItemHeight?: (index: number) => number | undefined,
534): VirtualScrollResult {
535 const terminalSize = useContext(TerminalSizeContext)
536 const heightCache = useRef(new Map<string, number>())
537 // Bump whenever heightCache mutates so offsets rebuild on next read. Ref
538 // (not state) — checked during render phase, zero extra commits.
539 const offsetVersionRef = useRef(0)
540 // scrollTop at last commit, for detecting fast-scroll mode (slide cap gate).
541 const lastScrollTopRef = useRef(0)
542 const offsetsRef = useRef<VirtualScrollOffsetsState>({
543 arr: new Float64Array(0),
544 version: -1,
545 n: -1,
546 })
547 const itemRefs = useRef(new Map<string, DOMElement>())
548 const refCache = useRef(new Map<string, (el: DOMElement | null) => void>())
549 // Inline ref-compare: must run before offsets is computed below. The
550 // skip-flag guards useLayoutEffect from re-populating heightCache with
551 // PRE-resize Yoga heights (useLayoutEffect reads Yoga from the frame
552 // BEFORE this render's calculateLayout — the one that had the old width).
553 // Next render's useLayoutEffect reads post-resize Yoga → correct.
554 const prevColumns = useRef(columns)
555 const skipMeasurementRef = useRef(false)
556 // Freeze the mount range for the resize-settling cycle. Already-mounted
557 // items have warm useMemo (marked.lexer, highlighting); recomputing range
558 // from scaled/pessimistic estimates causes mount/unmount churn (~3ms per
559 // fresh mount = ~150ms visible as a second flash). The pre-resize range is
560 // as good as any — items visible at old width are what the user wants at
561 // new width. Frozen for 2 renders: render #1 has skipMeasurement (Yoga
562 // still pre-resize), render #2's useLayoutEffect reads post-resize Yoga
563 // into heightCache. Render #3 has accurate heights → normal recompute.
564 const prevRangeRef = useRef<readonly [number, number] | null>(null)
565 const freezeRendersRef = useRef(0)
566 const stableClampBoundsRef = useRef<VirtualScrollClampBounds>({
567 clampMin: undefined,
568 clampMax: undefined,
569 })
570 const reusedStableClampRef = useRef(false)

Callers 1

VirtualMessageListFunction · 0.85

Tested by

no test coverage detected