"use client"; import { useCallback, useMemo, useSyncExternalStore } from "react"; import { parseReaderPlace, readerPlaceUrl, READER_VIEWS, type ReaderPlace, type ReaderView } from "../../../lib/report/slides"; // THE READER'S PLACE, FROM THE URL — which view (`?rv=`) and where in it (the // hash), as lib/report/slides.ts parses it; the export site's reader and // umtool's both read and write it here. A switch pushes an entry (Back undoes // it); a step within a view replaces it. Back and a hash change re-read it. const listeners = new Set<() => void>(); function subscribe(cb: () => void) { listeners.add(cb); window.addEventListener("popstate", cb); window.addEventListener("hashchange", cb); return () => { listeners.delete(cb); window.removeEventListener("popstate", cb); window.removeEventListener("hashchange", cb); }; } const snapshot = () => `${window.location.search}${window.location.hash}`; const serverSnapshot = () => ""; export type PlaceLike = { view: ReaderView; slide?: number | null; anchor?: string | null }; export function readerUrl(place: PlaceLike): string { return readerPlaceUrl(window.location.pathname, window.location.search, place); } export function goToPlace(place: PlaceLike, push: boolean): void { const url = readerUrl(place); const here = `${window.location.pathname}${window.location.search}${window.location.hash}`; if (url === here) return; window.history[push ? "pushState" : "replaceState"](window.history.state, "", url); for (const l of listeners) l(); } // The place in force; a view not offered (`views`) reads as the article. export function useReaderPlace(views: readonly ReaderView[] = READER_VIEWS): { place: ReaderPlace; go: (place: PlaceLike, push: boolean) => void; } { const url = useSyncExternalStore(subscribe, snapshot, serverSnapshot); const place = useMemo(() => { const at = url.indexOf("#"); const p = parseReaderPlace(at < 0 ? url : url.slice(0, at), at < 0 ? "" : url.slice(at)); return views.includes(p.view) ? p : { view: "article", slide: null, anchor: p.anchor }; }, [url, views]); const go = useCallback((p: PlaceLike, push: boolean) => goToPlace(p, push), []); return { place, go }; }