UNPKG

@assistant-ui/react

Version:

Open-source TypeScript/React library for building production-grade AI chat experiences

51 lines (50 loc) • 1.91 kB
"use client"; // src/primitives/thread/topAnchor/computeTopAnchorSlack.ts const getDocumentOffsetTop = (element) => { let top = 0; let current = element; while (current) { top += current.offsetTop; current = current.offsetParent; } return top; }; const getLayoutOffsetTop = (element, ancestor) => { let top = 0; let current = element; while (current && current !== ancestor) { top += current.offsetTop; current = current.offsetParent; } if (current === ancestor) return top; return getDocumentOffsetTop(element) - getDocumentOffsetTop(ancestor); }; /** * Compute the scroll position that pins the anchor (last user message) to the * top of the viewport. For tall user messages the anchor is intentionally * over-scrolled so only `visibleHeight` of it remains visible, leaving room * for the assistant message below. * * Depends only on the anchor's offset within the scroll content; never reads * `viewport.scrollHeight` (which is volatile while the assistant message * streams in). */ const computeTopAnchorTargetScrollTop = ({ viewport, anchor, tallerThan, visibleHeight }) => { const anchorTop = getLayoutOffsetTop(anchor, viewport); const anchorHeight = anchor.offsetHeight; return anchorTop + Math.max(0, anchorHeight - (anchorHeight <= tallerThan ? anchorHeight : visibleHeight)); }; const computeTopAnchorSlack = ({ scrollHeight, ...targetOptions }) => { const { viewport } = targetOptions; const targetScrollHeight = computeTopAnchorTargetScrollTop(targetOptions) + viewport.clientHeight; return Math.max(0, targetScrollHeight - scrollHeight); }; const computeTopAnchorReserve = ({ viewport, reserve, ...targetOptions }) => { return computeTopAnchorSlack({ viewport, ...targetOptions, scrollHeight: viewport.scrollHeight - reserve.offsetHeight }); }; export { computeTopAnchorReserve, computeTopAnchorTargetScrollTop }; //# sourceMappingURL=computeTopAnchorSlack.js.map