···11+// Generated with claude code, sonnet 4.5
22+/**
33+ * Scrolls an element into view within a scrolling container using Intersection Observer
44+ * and the scrollTo API, instead of the native scrollIntoView.
55+ *
66+ * @param elementId - The ID of the element to scroll into view
77+ * @param scrollContainerId - The ID of the scrolling container (defaults to "pages")
88+ * @param threshold - Intersection observer threshold (0-1, defaults to 0.2 for 20%)
99+ */
1010+export function scrollIntoView(
1111+ elementId: string,
1212+ scrollContainerId: string = "pages",
1313+ threshold: number = 0.2,
1414+) {
1515+ const element = document.getElementById(elementId);
1616+ const scrollContainer = document.getElementById(scrollContainerId);
1717+1818+ if (!element || !scrollContainer) {
1919+ console.warn(`scrollIntoView: element or container not found`, {
2020+ elementId,
2121+ scrollContainerId,
2222+ element,
2323+ scrollContainer,
2424+ });
2525+ return;
2626+ }
2727+2828+ // Create an intersection observer to check if element is visible
2929+ const observer = new IntersectionObserver(
3030+ (entries) => {
3131+ const entry = entries[0];
3232+3333+ // If element is not sufficiently visible, scroll to it
3434+ if (!entry.isIntersecting || entry.intersectionRatio < threshold) {
3535+ const elementRect = element.getBoundingClientRect();
3636+ const containerRect = scrollContainer.getBoundingClientRect();
3737+3838+ // Calculate the target scroll position
3939+ // We want to center the element horizontally in the container
4040+ const targetScrollLeft =
4141+ scrollContainer.scrollLeft +
4242+ elementRect.left -
4343+ containerRect.left -
4444+ (containerRect.width - elementRect.width) / 2;
4545+4646+ scrollContainer.scrollTo({
4747+ left: targetScrollLeft,
4848+ behavior: "smooth",
4949+ });
5050+ }
5151+5252+ // Disconnect after checking once
5353+ observer.disconnect();
5454+ },
5555+ {
5656+ root: scrollContainer,
5757+ threshold: threshold,
5858+ },
5959+ );
6060+6161+ observer.observe(element);
6262+}