// README "Alignment rule": every note's top sits exactly at its paragraph's top. // spacer[i] = max(0, noteTop[i] + noteHeight[i] + gap - nextParagraphTop). // Applied spacers shift all later paragraphs; callers pass naturalTop (measured top // minus spacers already applied) so repeated runs are idempotent. export function computeLayout(items, gap = 26) { const tops = {}, spacers = {}; let shift = 0; for (let i = 0; i < items.length; i++) { const { id, naturalTop, noteHeight } = items[i]; const top = naturalTop + shift; tops[id] = top; if (i < items.length - 1) { const nextTop = items[i + 1].naturalTop + shift; const overflow = Math.max(0, top + noteHeight + gap - nextTop); spacers[id] = overflow; shift += overflow; } else { spacers[id] = 0; } } return { tops, spacers }; }