export function buildCumulativeOffsets(heights: number[]): number[] { const offsets = new Array(heights.length + 1); offsets[0] = 0; for (let i = 0; i < heights.length; i++) { offsets[i + 1] = offsets[i] + heights[i]; } return offsets; } export function findVisibleRange( cumulativeOffsets: number[], itemCount: number, scrollTop: number, viewportHeight: number, buffer = 4, ) { if (!cumulativeOffsets.length || itemCount === 0 || viewportHeight <= 0) { return { start: 0, end: 0 }; } let lo = 0; let hi = itemCount - 1; while (lo < hi) { const mid = (lo + hi) >>> 1; if (cumulativeOffsets[mid + 1] < scrollTop) { lo = mid + 1; } else { hi = mid; } } const start = Math.max(0, lo - buffer); const endScroll = scrollTop + viewportHeight; lo = start; hi = itemCount - 1; while (lo < hi) { const mid = (lo + hi) >>> 1; if (cumulativeOffsets[mid] < endScroll) { lo = mid + 1; } else { hi = mid; } } return { start, end: Math.min(itemCount, lo + buffer), }; }