@embedpdf/plugin-stage 3.0.0-next.5 → 3.0.0-next.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +566 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +185 -44
- package/dist/index.d.ts +185 -44
- package/dist/index.js +562 -85
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ZoomMode","S","InteractionToken"],"sources":["../src/settings.ts","../src/capability.ts","../src/scroll-handler.ts","../src/effects.ts","../src/reducer.ts","../src/types.ts","../src/stage.plugin.ts","../src/destination.ts","../src/wheel.ts"],"sourcesContent":["import { ZoomMode } from '@embedpdf/core-stage';\nimport type { StageSettings } from './types';\n\n/**\n * Out-of-the-box defaults — a sensible document-reading feel. They are JUST\n * defaults: every field is overridable in `stagePlugin(config)` and at runtime via\n * the setters / `update()`. The plugin ships NO named presets (\"document\",\n * \"canvas\", …) — a preset is simply an object the app keeps and passes to\n * `update()`, so that taxonomy stays a customer concern.\n */\nexport const DEFAULT_SETTINGS: StageSettings = {\n flow: 'continuous',\n layout: 'vertical',\n spread: 'none',\n sizing: 'intrinsic',\n columns: 'square',\n bounded: true,\n padding: 24,\n gap: 16,\n pageFrame: { top: 0, right: 0, bottom: 0, left: 0 },\n direction: 'ltr',\n fitAlign: { x: 'center', y: 'center' },\n // The reading defaults: navigation lands top/reading-start at every zoom\n // (v2 parity), button-zoom inflates around the middle, resizes pin the top\n // (the browser scroll model). Construction/presentation apps set all four\n // to center/center.\n arrivalAlign: { x: 'start', y: 'start' },\n zoomAlign: { x: 'center', y: 'center' },\n anchorAlign: { x: 'start', y: 'start' },\n viewRotation: 0,\n zoom: { mode: ZoomMode.Automatic },\n scrollBehavior: 'smooth',\n // Web: 1 PDF point = 96/72 CSS px, so 100% is physically accurate. A native\n // adapter overrides this at registration with its own logical-unit factor.\n viewUnitsPerPoint: 96 / 72,\n};\n\n/**\n * How a CHANGE to each setting affects the view — THE single source of truth.\n * One row per setting, completeness enforced by the compiler (`Record<keyof\n * StageSettings, …>`: adding a setting without classifying it is a type error).\n * Everything else derives from this table: the scene-cache invalidation and key,\n * `update()`'s reaction, the settings snapshot/patch picks, and the React\n * selector equality.\n *\n * 'reflow' — crosses the flow boundary: camera coordinates are meaningless\n * there, so re-place canonically onto the cursor's page.\n * 'scene' — a LAYOUT INPUT: invalidates the scene, then re-applies the anchor.\n * 'refit' — re-resolves zoom against the (possibly re-keyed) scene: re-applies\n * the anchor without an explicit invalidation.\n * 'reclamp' — pure clamp policy: re-clamp the current camera in place.\n * 'none' — guides future verbs only (arrival/zoom/anchor alignment,\n * scroll behavior).\n *\n * The classes also carry each change's INVARIANT — what stays fixed while the\n * view reacts: 'refit' (a zoom-intent change) holds the zoomAlign focal point;\n * 'scene' reframes (and viewport resizes) hold the anchorAlign point.\n */\nexport type SettingEffect = 'reflow' | 'scene' | 'refit' | 'reclamp' | 'none';\nexport const SETTINGS_EFFECT: Record<keyof StageSettings, SettingEffect> = {\n flow: 'reflow',\n layout: 'scene',\n spread: 'scene',\n sizing: 'scene',\n columns: 'scene',\n bounded: 'reclamp',\n padding: 'reclamp',\n gap: 'scene',\n pageFrame: 'scene',\n direction: 'scene',\n fitAlign: 'reclamp',\n arrivalAlign: 'none',\n zoomAlign: 'none',\n anchorAlign: 'none',\n viewRotation: 'scene', // a layout input: every page's footprint swaps w↔h\n zoom: 'refit',\n scrollBehavior: 'none',\n viewUnitsPerPoint: 'scene', // a layout input: changing it resizes every page\n};\nexport const SETTING_KEYS = Object.keys(SETTINGS_EFFECT) as Array<keyof StageSettings>;\n\n// Settings values are primitives or one-level objects (align pairs, pageFrame,\n// gap { px }, zoom intents) — one level of structural equality covers them all.\nconst valueEq = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const ka = Object.keys(a);\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k])\n );\n};\n/** Field-by-field settings equality, derived from the registry — a new setting is\n * covered automatically. (The React `useStageSettings` selector equality.) */\nexport const settingsEqual = (a: StageSettings, b: StageSettings): boolean =>\n SETTING_KEYS.every((k) => valueEq(a[k], b[k]));\n","import * as S from '@embedpdf/core-stage';\nimport {\n addRotations,\n applyRect,\n applyPoint,\n displaySize,\n intersectRects,\n pageTransform,\n rotateScaleMatrix,\n snapToDevice,\n} from '@embedpdf/core-geometry';\nimport type { Rect } from '@embedpdf/core-geometry';\nimport type { PluginContext } from '@embedpdf/core';\nimport { SETTINGS_EFFECT, SETTING_KEYS } from './settings';\nimport type { SettingEffect } from './settings';\nimport type {\n GoToOptions,\n RevealAnchorValue,\n RevealOptions,\n Scheduler,\n StageAction,\n StageCapability,\n StageConfig,\n StageSettings,\n StageState,\n StageViewState,\n Viewpoint,\n VisiblePage,\n} from './types';\n\n/**\n * The Stage capability — selectors (pure reads) + intents (the only writers).\n *\n * Layering, stated honestly:\n * • stage-core — PURE spatial math (no DOM, no time). Ports to Rust later.\n * • this capability — the IMPURE platform shell. It dispatches, caches, and owns\n * the camera tween. It is NOT pure; the one host dependency it has (frame\n * timing) enters through an injected Scheduler, never a hidden global.\n *\n * The model: EVERY camera move is defined by what it holds fixed.\n * • gestures (pan/pinch/wheel) — the content under the pointer. Physics, no setting.\n * • focal zoom (zoomIn/zoomTo/fit-mode switches) — the zoomAlign viewport point.\n * • reframes (resize/rotate/re-layout) — the page-point at the anchorAlign\n * viewport point: captured there before the change, restored there after.\n * • arrivals (goToPage/next/prev/reset) — nothing prior: a fresh landing at\n * arrivalAlign, THE SAME at every zoom (landing is policy, never a zoom side effect).\n * • explicit (reveal/destination/viewpoint) — whatever the call specifies.\n * fitAlign stands apart: the clamp's standing rest constraint wherever an axis has no\n * freedom against the TRUE bounds — it shapes every move above, and is why a fitting\n * axis settles identically whatever arrivalAlign says.\n *\n * \"Does it fit the viewport?\" still decides SIZE and SUBJECT — never alignment:\n * • step size: next/prev step by ITEM (spread) when the item fits, by PAGE when\n * zoomed in past it.\n * • subject: you arrive AT the unit that fits (item, page — or the whole scene\n * under fit-all).\n *\n * The `cursor` is THE current page in both flows: navigation sets it; continuous\n * scrolling syncs it from the camera; paged panning never moves it.\n */\nexport function createStageCapability(\n ctx: PluginContext<StageState, StageAction>,\n config: StageConfig = {},\n): StageCapability {\n // ── host timing seam (the only host dependency) ──────────────────────────────\n const host = globalThis as {\n requestAnimationFrame?: (cb: (t: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n };\n const canAnimate = !!config.scheduler || typeof host.requestAnimationFrame === 'function';\n const scheduler: Scheduler =\n config.scheduler ??\n (typeof host.requestAnimationFrame === 'function'\n ? { raf: (cb) => host.requestAnimationFrame!(cb), caf: (h) => host.cancelAnimationFrame!(h) }\n : { raf: () => 0, caf: () => {} }); // no host frames → navigation jumps instantly\n\n const cam = () => ctx.getState().camera;\n const vp = () => ctx.getState().vp;\n const dpr = () => ctx.getState().dpr;\n const pad = () => ctx.getState().padding;\n const paged = () => ctx.getState().flow === 'paged';\n const isFitAll = () => {\n const z = ctx.getState().zoom;\n return 'mode' in z && z.mode === S.ZoomMode.FitAll;\n };\n\n // ── the document's item model (spread grouping) — independent of the rendered\n // scene, so navigation can reason about ALL items while a paged SCENE holds\n // only one. The cursor is a page; itemIndexOfPage maps it (survives regrouping).\n let groupingCache: { key: string; grouping: number[][]; firstPages: number[] } | null = null;\n const grouping = (): { grouping: number[][]; firstPages: number[] } => {\n const doc = ctx.document();\n const st = ctx.getState();\n const key = `${doc ? doc.pageCount : 0}|${st.spread}`;\n if (groupingCache && groupingCache.key === key) return groupingCache;\n const g = S.groupPages(doc ? doc.pageCount : 0, st.spread);\n groupingCache = { key, grouping: g, firstPages: g.map((item) => item[0]) };\n return groupingCache;\n };\n const itemCountFull = (): number => grouping().grouping.length;\n const itemIndexOfPage = (pageIndex: number): number => {\n const fp = grouping().firstPages;\n let lo = 0;\n let hi = fp.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (fp[mid] <= pageIndex) lo = mid + 1;\n else hi = mid;\n }\n return Math.max(0, lo - 1);\n };\n\n // The effective zoom for converting SCREEN px settings into world units — a\n // fixed zoom intent gives an exact, stable value (the thumbnail case); other\n // intents fall back to the camera's current zoom (`stabilized` converges it).\n // Screen-px settings (wrapped lineWidth, pageFrame) are the ONLY way a scene\n // depends on the viewport/zoom.\n const effectiveZoom = (): number => {\n const z = ctx.getState().zoom;\n return 'level' in z ? z.level : Math.max(ctx.getState().camera.zoom, 0.0001);\n };\n\n // Wrapped grid: the line width (world units) the columns must fit.\n const wrapLineWidth = (): number => Math.max(1, (vp().width - 2 * pad()) / effectiveZoom());\n\n // pageFrame (screen px) → world units at the effective zoom.\n const worldPageFrame = (): S.PageFrame => {\n const m = ctx.getState().pageFrame;\n if (!m.top && !m.right && !m.bottom && !m.left) return m;\n const ez = effectiveZoom();\n return { top: m.top / ez, right: m.right / ez, bottom: m.bottom / ez, left: m.left / ez };\n };\n const frameKey = (): string => {\n const m = ctx.getState().pageFrame;\n if (!m.top && !m.right && !m.bottom && !m.left) return '-';\n const w = worldPageFrame();\n return `${Math.round(w.top)},${Math.round(w.right)},${Math.round(w.bottom)},${Math.round(w.left)}`;\n };\n\n // gap → world units. A plain number IS world (the scene stays zoom-invariant —\n // the rigid-canvas default); { px } converts at the effective zoom, exactly\n // like pageFrame (UI-stable spacing for browser-style lenses).\n const worldGap = (): number => {\n const g = ctx.getState().gap;\n return typeof g === 'number' ? g : g.px ? g.px / effectiveZoom() : 0;\n };\n const gapKey = (): string => {\n const g = ctx.getState().gap;\n return typeof g === 'number' ? String(g) : `px:${Math.round(worldGap())}`;\n };\n\n const layoutFor = (groups: number[][]): S.Scene => {\n const st = ctx.getState();\n // Engine PageLayout (PDF document geometry) structurally satisfies stage-core's\n // viewer-local PageGeom (`size` + `rotation`): intrinsic page size needs no\n // transform, so it flows straight into the layout with no conversion.\n //\n // THE view-rotation injection point: each page's display rotation is its\n // /Rotate + this lens's viewRotation, composed HERE — the one spot where\n // the \"TOTAL = document /Rotate + view rotation\" of geometry's PageRotation\n // doc is resolved. Everything downstream (displaySize w↔h swap, the page\n // transform + CSS rotate, hit-testing, fit zoom, content overlays) reads the\n // composed `PageBox.rotation` and needs no other change. `size` stays the\n // page's own un-rotated points, so content space is view-rotation-invariant.\n const raw = ctx.document()?.pages ?? [];\n const vr = st.viewRotation;\n const pages =\n vr === 0 ? raw : raw.map((p) => ({ ...p, rotation: addRotations(p.rotation, vr) }));\n const pageFrame = worldPageFrame();\n const gap = worldGap();\n const vupp = st.viewUnitsPerPoint;\n if (st.layout === 'grid') {\n return S.gridLayout(pages, groups, {\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n columns: typeof st.columns === 'number' ? st.columns : undefined,\n lineWidth: st.columns === 'auto' ? wrapLineWidth() : undefined,\n });\n }\n return st.layout === 'horizontal'\n ? S.linearLayout(pages, groups, {\n axis: 'x',\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n })\n : S.linearLayout(pages, groups, {\n axis: 'y',\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n });\n };\n\n // Scene-cache key fragment for the column policy ('auto' quantizes the line width\n // so sub-pixel resizes don't churn the cache).\n const columnsKey = (): string => {\n const st = ctx.getState();\n if (st.layout !== 'grid') return '-';\n return st.columns === 'auto' ? `auto:${Math.round(wrapLineWidth())}` : String(st.columns);\n };\n\n // The scene's settings signature — DERIVED from the registry: every 'scene'\n // setting contributes automatically, so a new layout-affecting setting only\n // needs its SETTINGS_EFFECT row — it can't be forgotten here, which is what\n // makes stale-scene bugs unrepresentable. The default keys by VALUE (objects\n // via JSON); the custom fns aren't for correctness, they QUANTIZE px-derived\n // values so sub-pixel zoom/resize churn doesn't rebuild the scene.\n const SCENE_KEY_FNS: Partial<Record<keyof StageSettings, () => string>> = {\n columns: columnsKey,\n gap: gapKey,\n pageFrame: frameKey,\n };\n const SCENE_KEYS = SETTING_KEYS.filter((k) => SETTINGS_EFFECT[k] === 'scene');\n const settingsKey = (): string =>\n SCENE_KEYS.map((k) => {\n const fn = SCENE_KEY_FNS[k];\n if (fn) return fn();\n const v = ctx.getState()[k];\n return typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v);\n }).join('|');\n\n // Scene cache. Continuous = the whole document. Paged = a ONE-ITEM SLICE at the\n // origin containing only the cursor's item — so isolation is STRUCTURAL (no other\n // page exists to leak), unbounded pan is free, and coordinates stay local.\n let sceneCache: { key: string; scene: S.Scene } | null = null;\n // Registry signature: page count + the kernel's monotonic `revision`. The\n // revision bumps on every page-mutation event (rotate/move/delete), so a\n // change that leaves pageCount the same — a rotation — still re-keys the\n // scene. Without it a rotated page would render in its stale box.\n const docKey = (): string => {\n const doc = ctx.document();\n return doc ? `${doc.pageCount}.${doc.revision}` : '0.0';\n };\n const buildScene = (): S.Scene => {\n const st = ctx.getState();\n const { grouping: g } = grouping();\n if (st.flow === 'paged') {\n const idx = g.length ? Math.min(itemIndexOfPage(st.cursor), g.length - 1) : 0;\n const key = `paged|${docKey()}|${settingsKey()}|${idx}`;\n if (sceneCache && sceneCache.key === key) return sceneCache.scene;\n const scene = layoutFor(g.length ? [g[idx]] : []);\n sceneCache = { key, scene };\n return scene;\n }\n const key = `cont|${docKey()}|${settingsKey()}`;\n if (sceneCache && sceneCache.key === key) return sceneCache.scene;\n const scene = layoutFor(g);\n sceneCache = { key, scene };\n return scene;\n };\n\n // ── geometry helpers ──────────────────────────────────────────────────────────\n const sceneRect = (): S.Rect => {\n const { width, height } = buildScene().size;\n return { x: 0, y: 0, width, height };\n };\n const itemRect = (it: S.SceneItem): S.Rect => ({\n x: it.x,\n y: it.y,\n width: it.width,\n height: it.height,\n });\n const pageRectOf = (it: S.SceneItem, pageIndex: number): S.Rect => {\n const box = it.pages.find((p) => p.pageIndex === pageIndex) ?? it.pages[0];\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n };\n /** The item shown for the cursor: the slice's only item (paged) / the full-scene item. */\n const cursorItem = (): S.SceneItem => {\n const sc = buildScene();\n return paged() ? sc.items[0] : sc.items[itemIndexOfPage(ctx.getState().cursor)];\n };\n\n /**\n * CONTENT-space rect on a page → WORLD rect: the same quarter-turn matrix\n * `pageRectToScreen` uses, minus the camera — so a positioned reveal and\n * the rendered overlay can never disagree about where a rect is.\n */\n const worldRectForContent = (it: S.SceneItem, pageIndex: number, rect: Rect): S.Rect => {\n const box = it.pages.find((p) => p.pageIndex === pageIndex) ?? it.pages[0];\n const content = displaySize({ width: box.width, height: box.height }, box.rotation);\n const m = rotateScaleMatrix(box.contentScale, content.width, content.height, box.rotation);\n const wr = applyRect(m, rect);\n return { x: box.x + wr.x, y: box.y + wr.y, width: wr.width, height: wr.height };\n };\n\n /**\n * One axis of a positioned-reveal camera. `undefined` = 'nearest' (only\n * move if the target is outside the padded view) — unless the zoom just\n * changed, where \"don't move\" is meaningless and the spec's slack-axis\n * rule (center) applies. 'keep' never moves the axis (PDF /XYZ null).\n */\n const revealAxis = (\n a: RevealAnchorValue | undefined,\n camPos: number,\n rectPos: number,\n rectExtent: number,\n vpExtent: number,\n zoom: number,\n zoomChanged: boolean,\n ): number => {\n if (a === 'keep') return camPos;\n const p = pad();\n if (a === undefined) {\n if (!zoomChanged) {\n const lo = camPos + p / zoom;\n const hi = camPos + (vpExtent - p) / zoom;\n if (rectPos >= lo && rectPos + rectExtent <= hi) return camPos; // already visible\n if (rectExtent > hi - lo || rectPos < lo) return rectPos - p / zoom;\n return rectPos + rectExtent - (vpExtent - p) / zoom;\n }\n a = 'center';\n }\n if (a === 'start') return rectPos - p / zoom;\n if (a === 'end') return rectPos + rectExtent - (vpExtent - p) / zoom;\n const f = a === 'center' ? 0.5 : Math.min(1, Math.max(0, a));\n return rectPos + rectExtent / 2 - (vpExtent * f) / zoom;\n };\n\n // THE predicate. \"Does this rect fit the padded viewport at this zoom?\" decides\n // the navigation step size and the arrival subject — never alignment.\n const fits = (rect: S.Rect, zoom: number): boolean => {\n const v = vp();\n const p = pad();\n const eps = 0.5;\n return (\n rect.width * zoom <= v.width - 2 * p + eps && rect.height * zoom <= v.height - 2 * p + eps\n );\n };\n // An alignment policy → a concrete viewport point (a fraction per axis:\n // start=0, center=½, end=1; named x stops are LOGICAL under RTL). Both\n // zoomAlign (the focal point of pointer-less zooms) and anchorAlign (the\n // reframe reference point) resolve through this. The fraction interpolates\n // the PADDED range: 'start' is the first visible content line (just inside\n // the gutter), not the absolute corner — an arrival puts the page edge\n // exactly there, so the reference pins to the page, never to the gap above.\n const alignFraction = (a: S.AlignValue): number =>\n a === 'start' ? 0 : a === 'center' ? 0.5 : a === 'end' ? 1 : Math.min(1, Math.max(0, a));\n const alignPoint = (al: S.AlignmentValue, v: S.Size): S.Point => {\n const rtl = ctx.getState().direction === 'rtl';\n const ax = rtl && al.x === 'start' ? 'end' : rtl && al.x === 'end' ? 'start' : al.x;\n const p = pad();\n return {\n x: p + (v.width - 2 * p) * alignFraction(ax),\n y: p + (v.height - 2 * p) * alignFraction(al.y),\n };\n };\n const anchorPoint = (): S.Point => alignPoint(ctx.getState().anchorAlign, vp());\n\n /** Fit-box for resolving the zoom intent: whole scene (fit-all), the current item\n * (paged — per-page fit), or the document max (continuous — doc-stable zoom). */\n const fitBox = (item: S.SceneItem): S.Size => {\n if (isFitAll()) return buildScene().size;\n return paged() ? { width: item.width, height: item.height } : buildScene().maxItemSize;\n };\n /** Clamp rect for a camera write targeting this item (scene-wide in continuous). */\n const boundsFor = (it: S.SceneItem): S.Rect => (paged() ? itemRect(it) : sceneRect());\n /** Clamp rect for a \"stay\" write (pan/zoom): the slice item (paged) / the scene. */\n const stayBounds = (): S.Rect => (paged() ? itemRect(buildScene().items[0]) : sceneRect());\n\n const constraint = (): S.CameraConstraint => ({\n bounded: ctx.getState().bounded,\n padding: pad(),\n fitAlign: ctx.getState().fitAlign,\n direction: ctx.getState().direction,\n });\n\n // The ONE low-level camera write: clamp to `bounds`, dispatch. MECHANISM only —\n // it never touches the cursor (see syncCursorFromCamera for the policy).\n const setCam = (next: S.Camera, bounds: S.Rect = stayBounds()) => {\n ctx.dispatch({ type: 'CAMERA', camera: S.clampCamera(next, bounds, vp(), constraint()) });\n };\n\n /**\n * Cursor reconciliation, one direction per interaction:\n * navigation → cursor is INTENT, set explicitly; the camera honors it as far\n * as the clamp allows — and a clamped camera never revokes it.\n * manipulation → (pan / drag / pinch) the camera moves freely; the cursor is\n * DERIVED from it. Only those verbs call this. Paged never syncs.\n */\n const syncCursorFromCamera = () => {\n if (paged()) return;\n const sc = buildScene();\n if (!sc.itemCount) return;\n const page = S.anchorFromCamera(cam(), sc, vp()).pageIndex;\n if (page !== ctx.getState().cursor) ctx.dispatch({ type: 'CURSOR', cursor: page });\n };\n\n // ── camera tween (impure shell concern; uses the injected Scheduler) ─────────\n let raf = 0;\n const cancelAnim = () => {\n if (raf) {\n scheduler.caf(raf);\n raf = 0;\n }\n };\n const lerp = (a: number, b: number, k: number) => a + (b - a) * k;\n // The tween clamps to an EXPLICIT bounds rect every frame (never a re-derived\n // current item), so animating toward a different item isn't clamped back.\n // `then` runs on natural completion only — a cancelled tween belongs to the\n // verb that cancelled it.\n const animateTo = (target: S.Camera, bounds: S.Rect, ms = 240, then?: () => void) => {\n if (!canAnimate) {\n setCam(target, bounds);\n then?.();\n return;\n }\n cancelAnim();\n const from = cam();\n const ease = (t: number) => 1 - Math.pow(1 - t, 3);\n let started = false;\n let t0 = 0;\n const tick = (now: number) => {\n if (!started) {\n started = true;\n t0 = now; // anchor to the first real timestamp (works even when now === 0)\n }\n const k = ease(Math.min(1, (now - t0) / ms));\n setCam(\n {\n x: lerp(from.x, target.x, k),\n y: lerp(from.y, target.y, k),\n zoom: lerp(from.zoom, target.zoom, k),\n },\n bounds,\n );\n if (k < 1) {\n raf = scheduler.raf(tick);\n } else {\n raf = 0;\n then?.();\n }\n };\n raf = scheduler.raf(tick);\n };\n\n // ── anchor: the durable \"what am I looking at\". Capture before a structural\n // change, re-apply after — one mechanism for layout/spread/zoom/resize/restore.\n // WHERE in the viewport the anchor lives is the reframe's invariant policy:\n // anchorAlign for reframes (resize, scene changes), zoomAlign for pure\n // zoom-intent changes. Capture and restore always use the SAME point.\n const anchorAt = (at: S.Point): S.Anchor => {\n const sc = buildScene();\n return sc.itemCount\n ? S.anchorFromCamera(cam(), sc, vp(), at)\n : { pageIndex: ctx.getState().cursor, fx: 0.5, fy: 0 };\n };\n const currentAnchor = (): S.Anchor => anchorAt(anchorPoint());\n\n /**\n * Run a placement whose RESOLVED zoom may change the scene (wrapped mode: zoom is\n * a layout input). Re-run it until the scene it placed against is the scene that\n * results — every non-wrapped mode is stable after the first pass by construction,\n * and wrapped fit-modes converge on the second (the fit-box is wrap-independent).\n * Capped at 3 passes for the one genuinely circular case (fit-all + wrapped).\n */\n const stabilized = (place: () => void) => {\n for (let attempt = 0; attempt < 3; attempt++) {\n const scene = buildScene();\n place();\n if (buildScene() === scene) return;\n }\n };\n\n const applyAnchor = (anchor: S.Anchor, align?: S.AlignmentValue) => {\n stabilized(() => {\n const scene = buildScene();\n if (!scene.itemCount) return;\n const item = scene.items[scene.itemOfPage(anchor.pageIndex)];\n const zoom = S.resolveZoom(ctx.getState().zoom, fitBox(item), vp(), pad());\n // resolved against the CURRENT viewport — a resize restores the anchor to\n // the new viewport's policy point (start/start: the top stays pinned)\n const at = alignPoint(align ?? ctx.getState().anchorAlign, vp());\n setCam(S.cameraFromAnchor(anchor, scene, vp(), zoom, at), boundsFor(item));\n });\n };\n /**\n * Re-apply the view after a structural/zoom/viewport change. Normally anchor-\n * preserving (keep looking at the same spot, held at the invariant point).\n * Under fit-all the subject is the WHOLE scene, so \"keep my anchor\" is\n * meaningless — re-place instead (this is what centers the scene even when\n * unbounded).\n */\n const reapply = (anchor: S.Anchor, align?: S.AlignmentValue) => {\n if (isFitAll()) goToTarget(ctx.getState().cursor, { behavior: 'instant' });\n else applyAnchor(anchor, align);\n };\n\n // ── navigation: ONE arrival procedure for goToPage / next / prev / reset ─────\n // Navigation is CANONICAL: goToPage(N) always ends in the same camera state,\n // regardless of where you came from, what happens to be visible, or how zoomed\n // you are (no zoom-dependent landing — that's a discontinuity at the fit\n // threshold).\n // 1. move the cursor to the target page (paged: rebuilds the one-item slice),\n // 2. choose the SUBJECT by the fits-predicate (scene under fit-all; the item if\n // it fits; else the page),\n // 3. placeCamera(subject) lands it at arrivalAlign — the same rule at every\n // zoom — and setCam clamps against the TRUE bounds, which collapses any\n // axis with no freedom to the fitAlign rest point.\n // The legitimate \"nothing moves\" cases are STRUCTURAL, not conditional: under\n // fit-all the canonical placement is the centered scene, which doesn't change.\n const goToTarget = (pageIndex: number, opts?: GoToOptions) => {\n cancelAnim();\n const doc = ctx.document();\n if (!doc || doc.pageCount === 0) return;\n const target = Math.max(0, Math.min(pageIndex, doc.pageCount - 1));\n if (target !== ctx.getState().cursor) ctx.dispatch({ type: 'CURSOR', cursor: target });\n\n // Restore path (per-page view memory): exact viewpoint instead of fresh placement.\n if (opts?.viewpoint) {\n ctx.dispatch({ type: 'PATCH', patch: { zoom: opts.viewpoint.zoom } });\n applyAnchor(opts.viewpoint.anchor);\n return;\n }\n\n // Placement against the CURRENT scene; null when there is nothing to place.\n const placement = (): { camera: S.Camera; bounds: S.Rect } | null => {\n const sc = buildScene(); // paged: the (possibly new) slice; continuous: full scene\n if (!sc.itemCount) return null;\n const item = paged() ? sc.items[0] : sc.items[itemIndexOfPage(target)];\n const zoom = S.resolveZoom(ctx.getState().zoom, fitBox(item), vp(), pad());\n const subject = isFitAll()\n ? sceneRect()\n : fits(itemRect(item), zoom)\n ? itemRect(item)\n : pageRectOf(item, target);\n // The landing policy: a per-call override beats the setting (explicit\n // beats default); 'keep' pins that axis to the current camera.\n const want = { ...ctx.getState().arrivalAlign, ...opts?.arrivalAlign };\n const placed = S.placeCamera(\n subject,\n vp(),\n zoom,\n pad(),\n {\n x: want.x === 'keep' ? 'start' : want.x,\n y: want.y === 'keep' ? 'start' : want.y,\n },\n ctx.getState().direction,\n );\n // Arrivals are canonical even on an UNBOUNDED stage: the landing clamps\n // within the true bounds regardless (free roam is a manipulation\n // affordance, not an arrival one) — an axis with no freedom collapses to\n // its fitAlign rest here, and the fit-all scene centers even unbounded.\n const bounds = boundsFor(item);\n const settled = S.clampCamera(placed, bounds, vp(), { ...constraint(), bounded: true });\n return {\n camera: {\n zoom,\n x: want.x === 'keep' ? cam().x : settled.x,\n y: want.y === 'keep' ? cam().y : settled.y,\n },\n bounds,\n };\n };\n\n if ((opts?.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n const p = placement();\n if (p) animateTo(p.camera, p.bounds);\n } else {\n // instant placement converges with the scene (wrapped: zoom is a layout input)\n stabilized(() => {\n const p = placement();\n if (p) setCam(p.camera, p.bounds);\n });\n }\n };\n\n /** Step by the navigation unit: the ITEM when it fits the viewport, else the PAGE. */\n const step = (direction: 1 | -1, opts?: GoToOptions) => {\n const st = ctx.getState();\n const item = cursorItem();\n if (!item) return;\n const { grouping: g } = grouping();\n if (isFitAll() || fits(itemRect(item), cam().zoom)) {\n const idx = Math.max(0, Math.min(itemIndexOfPage(st.cursor) + direction, g.length - 1));\n goToTarget(g[idx][0], opts);\n } else {\n goToTarget(st.cursor + direction, opts);\n }\n };\n\n // pon (durable identity) for a page's display index, from the registry captured at open.\n const ponForIndex = (index: number): number =>\n ctx.document()?.pages[index]?.pageObjectNumber ?? index + 1;\n\n // Fallback un-rotated point size from a laid-out box, when the registry entry is\n // momentarily absent. `displaySize` is its own inverse (display box → content\n // box); ÷ contentScale recovers points.\n const unrotatedPoints = (box: S.PageBox): S.Size => {\n const content = displaySize({ width: box.width, height: box.height }, box.rotation);\n return { width: content.width / box.contentScale, height: content.height / box.contentScale };\n };\n\n // Attach durable identity + the per-page transform (PDF points → view px →\n // device px). `scale` is view px per point = contentScale (world per point) ×\n // zoom (view px per world); `pageSize` is the page's UN-rotated points from the\n // registry. Camera-invariant (only zoom/rotation/contentScale/dpr), so the same\n // box always yields the same transform regardless of pan.\n const withTransform = (box: S.PageBox): VisiblePage => {\n const reg = ctx.document()?.pages[box.pageIndex];\n const pageSize = reg\n ? { width: reg.size.width, height: reg.size.height }\n : unrotatedPoints(box);\n const c = cam();\n const ratio = dpr();\n // device-snapped footprint top-left (camera-resolved) — keeps a rotated page\n // on the device grid; the adapter just consumes it.\n const screenX = snapToDevice((box.x - c.x) * c.zoom, ratio);\n const screenY = snapToDevice((box.y - c.y) * c.zoom, ratio);\n const transform = pageTransform({\n pageSize,\n rotation: box.rotation,\n scale: box.contentScale * c.zoom,\n // The page's PHYSICAL 100%: platform unit factor × its /UserUnit — so\n // `transform.zoom` reads as \"percent of Acrobat's 100%\" per page,\n // independent of sizing mode or camera state.\n baseScale: ctx.getState().viewUnitsPerPoint * (reg?.userUnit ?? 1),\n dpr: ratio,\n });\n // The on-screen page region in points: viewport ∩ footprint in VIEW space\n // (both axis-aligned), inverted through the transform (exact for\n // quarter-turns). The same intersection the virtualizer's query already\n // decided coarsely — refined per page, once, HERE, so no adapter ever\n // re-derives camera math (tiling's demand reads this field).\n const v = vp();\n const onScreen = intersectRects(\n { x: -screenX, y: -screenY, width: v.width, height: v.height },\n { x: 0, y: 0, width: transform.viewWidth, height: transform.viewHeight },\n );\n const visibleRect =\n onScreen.width > 0 && onScreen.height > 0\n ? transform.viewToPageRect(onScreen)\n : { x: 0, y: 0, width: 0, height: 0 };\n return {\n ...box,\n pon: ponForIndex(box.pageIndex),\n screenX,\n screenY,\n transform,\n visibleRect,\n };\n };\n\n // The scroller projection — the camera in native DOM vocabulary, against the\n // SAME bounds the pan clamp uses (stayBounds: the slice item in paged flow,\n // the scene in continuous). Memoized like visiblePages: a stable reference\n // until a field actually moves, so adapter selectors can use plain equality.\n let scrollMemo: S.ScrollMetrics | null = null;\n const scrollMetricsNow = (): S.ScrollMetrics => {\n const sc = buildScene();\n const m = S.scrollMetrics(\n cam(),\n sc.itemCount ? stayBounds() : { x: 0, y: 0, width: 0, height: 0 },\n vp(),\n pad(),\n );\n const p = scrollMemo;\n if (\n p &&\n p.scrollLeft === m.scrollLeft &&\n p.scrollTop === m.scrollTop &&\n p.scrollWidth === m.scrollWidth &&\n p.scrollHeight === m.scrollHeight &&\n p.clientWidth === m.clientWidth &&\n p.clientHeight === m.clientHeight\n ) {\n return p; // scrollableX/Y derive from the numbers — covered by the six\n }\n scrollMemo = m;\n return m;\n };\n\n // Memoized visiblePages -> stable reference (no useSyncExternalStore tearing loop).\n // Paged renders ONLY the slice's item; continuous renders the camera's query window.\n let visSig = '';\n let vis: VisiblePage[] = [];\n const visiblePages = (): VisiblePage[] => {\n const c = cam();\n const v = vp();\n const sc = buildScene();\n const items = paged() ? sc.items.slice(0, 1) : sc.query(S.cameraWorldRect(c, v));\n const sig = `${sceneCache!.key}|${ctx.getState().flow}|${c.x},${c.y},${c.zoom}|${v.width}x${v.height}|${dpr()}`;\n if (sig === visSig) return vis;\n visSig = sig;\n vis = items.flatMap((it) => it.pages).map(withTransform);\n return vis;\n };\n\n // The settings slice of a larger object (state, or a saved view) — derived from\n // the registry, so the shape is never spelled out by hand again.\n const pickSettings = (src: StageSettings): StageSettings => {\n const out: Partial<Record<keyof StageSettings, unknown>> = {};\n for (const k of SETTING_KEYS) out[k] = src[k];\n return out as StageSettings;\n };\n const snapshotSettings = (): StageSettings => pickSettings(ctx.getState());\n\n // Initial-view providers (storage restore, deep-link, an explicit prop…). One owner\n // (placeInitial) resolves them by priority — no effect-ordering races.\n const initialViewProviders: Array<{ priority: number; fn: () => StageViewState | null }> = [];\n let hasPlaced = false;\n\n const api: StageCapability = {\n // ── selectors ──\n camera: cam,\n viewport: vp,\n scrollMetrics: scrollMetricsNow,\n pageCount: () => ctx.document()?.pageCount ?? 0,\n visiblePages,\n currentPage: () => ctx.getState().cursor,\n currentItemPages: () => {\n const item = cursorItem();\n return item ? [...item.pageIndexes] : [];\n },\n pages: () =>\n (ctx.document()?.pages ?? []).map((p) => ({\n index: p.index,\n pon: p.pageObjectNumber,\n label: p.label ?? null,\n })),\n pageRect: (pon) => {\n const meta = ctx.document();\n const index = meta ? meta.pages.findIndex((p) => p.pageObjectNumber === pon) : -1;\n if (index < 0) return null;\n const sc = buildScene();\n if (!sc.itemCount) return null;\n const box = sc.items[sc.itemOfPage(index)].pages.find((p) => p.pageIndex === index);\n return box ? withTransform(box) : null;\n },\n pageAt: (screen) => {\n // Find the visible page whose device-snapped display box contains the\n // point, then invert that page's transform — same `viewToPage` the\n // per-page PageContext.toPagePoint uses, so the two never drift.\n for (const p of visiblePages()) {\n const lx = screen.x - p.screenX;\n const ly = screen.y - p.screenY;\n if (lx >= 0 && ly >= 0 && lx <= p.transform.viewWidth && ly <= p.transform.viewHeight) {\n return {\n pon: p.pon,\n point: p.transform.viewToPage({ x: lx, y: ly }),\n scale: p.transform.viewScale,\n rotation: p.rotation,\n zoom: p.transform.zoom,\n };\n }\n }\n return null;\n },\n pointOnPage: (pon, screen) => {\n // `pageAt` minus the containment check: project onto ONE page's plane,\n // valid outside its bounds — the same inverse transform, so no drift.\n const p = visiblePages().find((v) => v.pon === pon);\n if (!p) return null;\n return p.transform.viewToPage({ x: screen.x - p.screenX, y: screen.y - p.screenY });\n },\n pageToWorld: (pon, pt) => {\n const pr = api.pageRect(pon);\n if (!pr) return null;\n // Place the content point into the page's display box via the SAME\n // quarter-turn matrix the layout/renderer use (`rotateScaleMatrix`) — so\n // this forward transform and the adapter's inverse hit-test (which inverts\n // the same matrix) can't drift. `displaySize` is its own inverse, so it\n // recovers the un-rotated content size from the display box.\n const content = displaySize({ width: pr.width, height: pr.height }, pr.rotation);\n const m = rotateScaleMatrix(pr.contentScale, content.width, content.height, pr.rotation);\n const offset = applyPoint(m, pt);\n return { x: pr.x + offset.x, y: pr.y + offset.y };\n },\n pageRectToScreen: (pon, rect) => {\n const pr = api.pageRect(pon);\n if (!pr) return null;\n const content = displaySize({ width: pr.width, height: pr.height }, pr.rotation);\n const m = rotateScaleMatrix(pr.contentScale, content.width, content.height, pr.rotation);\n const wr = applyRect(m, rect);\n const c = cam();\n const tl = S.toScreen(c, { x: pr.x + wr.x, y: pr.y + wr.y });\n return { x: tl.x, y: tl.y, width: wr.width * c.zoom, height: wr.height * c.zoom };\n },\n toScreen: (w) => S.toScreen(cam(), w),\n toWorld: (s) => S.toWorld(cam(), s),\n flow: () => ctx.getState().flow,\n layout: () => ctx.getState().layout,\n spread: () => ctx.getState().spread,\n sizing: () => ctx.getState().sizing,\n columns: () => ctx.getState().columns,\n bounded: () => ctx.getState().bounded,\n padding: () => ctx.getState().padding,\n gap: () => ctx.getState().gap,\n pageFrame: () => ctx.getState().pageFrame,\n fitAlign: () => ctx.getState().fitAlign,\n arrivalAlign: () => ctx.getState().arrivalAlign,\n zoomAlign: () => ctx.getState().zoomAlign,\n anchorAlign: () => ctx.getState().anchorAlign,\n direction: () => ctx.getState().direction,\n scrollBehavior: () => ctx.getState().scrollBehavior,\n viewRotation: () => ctx.getState().viewRotation,\n zoomLevel: () => cam().zoom,\n zoomMode: () => {\n const z = ctx.getState().zoom;\n return 'mode' in z ? z.mode : 'custom';\n },\n viewpoint: (): Viewpoint => ({ anchor: currentAnchor(), zoom: ctx.getState().zoom }),\n settings: snapshotSettings,\n viewState: (): StageViewState => ({\n ...snapshotSettings(),\n cursor: ctx.getState().cursor,\n anchor: currentAnchor(),\n }),\n\n // ── intents ──\n setViewport: (v) => {\n // Initial placement is LEVEL-triggered, owned here: the moment the stage\n // first learns a real size (both axes) and the document has pages, resolve\n // the initial view (storage/deep-link providers, else reset). Every report\n // re-checks the condition — no watch, no effect-registration race, no edge\n // to miss when the viewport was already sized before anyone listened.\n if (!hasPlaced) {\n ctx.dispatch({ type: 'VP', vp: v });\n if (v.width > 0 && v.height > 0 && (ctx.document()?.pageCount ?? 0) > 0) {\n api.placeInitial();\n }\n return;\n }\n // Afterwards every resize keeps the same page and re-resolves fit-modes.\n cancelAnim();\n const anchor = currentAnchor(); // measured against the OLD viewport\n ctx.dispatch({ type: 'VP', vp: v }); // new viewport\n reapply(anchor);\n },\n setDevicePixelRatio: (ratio) => {\n // Pure device-resolution change: it only affects each page transform's\n // device px (crispness) + sub-pixel box snapping, never the layout or\n // camera — so no re-place, just store it; visiblePages re-keys on dpr.\n if (ratio > 0 && ratio !== dpr()) ctx.dispatch({ type: 'DPR', dpr: ratio });\n },\n setCamera: (c) => {\n cancelAnim();\n setCam(c);\n syncCursorFromCamera();\n },\n panBy: (dx, dy) => {\n cancelAnim();\n setCam(S.panByScreen(cam(), dx, dy));\n syncCursorFromCamera();\n },\n scrollTo: (opts) => {\n cancelAnim();\n const sc = buildScene();\n if (!sc.itemCount) return;\n const next = S.cameraFromScroll(cam(), stayBounds(), vp(), pad(), opts);\n if (opts.behavior === 'smooth') {\n animateTo(next, stayBounds(), 240, syncCursorFromCamera);\n } else {\n setCam(next);\n syncCursorFromCamera();\n }\n },\n scrollBy: ({ left, top, behavior }) => {\n const m = scrollMetricsNow();\n api.scrollTo({\n left: left === undefined ? undefined : m.scrollLeft + left,\n top: top === undefined ? undefined : m.scrollTop + top,\n behavior,\n });\n },\n zoomAround: (pt, factor) => {\n cancelAnim();\n const before = buildScene();\n // page-relative focal point: the durable identity of \"what's under the cursor\"\n const focal = before.itemCount ? S.anchorAtPoint(before, S.toWorld(cam(), pt)) : null;\n setCam(S.zoomAround(cam(), pt, factor));\n // record the resulting fixed level as the zoom intent — focal, so no re-anchor…\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: cam().zoom } } });\n // …UNLESS zoom is a LAYOUT INPUT (wrapped grid) and the scene just re-wrapped\n // underneath the camera. The old world point is stale then — re-pin the SAME\n // page-point under the cursor and clamp against the new geometry. In every\n // non-wrapped mode the scene reference is unchanged and this never runs.\n const after = buildScene();\n if (after !== before && focal && after.itemCount) {\n setCam(S.cameraForAnchorAtScreen(focal, after, pt, cam().zoom));\n }\n syncCursorFromCamera();\n },\n // Pointer-less zooms magnify around the zoomAlign focal point (pinch and\n // wheel pass their own pointer to zoomAround — physics beats policy).\n zoomIn: () => api.zoomAround(alignPoint(ctx.getState().zoomAlign, vp()), 1.2),\n zoomOut: () => api.zoomAround(alignPoint(ctx.getState().zoomAlign, vp()), 1 / 1.2),\n zoomTo: (spec) => api.update({ zoom: spec }),\n fitWidth: () => api.update({ zoom: { mode: S.ZoomMode.FitWidth } }),\n fitPage: () => api.update({ zoom: { mode: S.ZoomMode.FitPage } }),\n fitAll: () => api.update({ zoom: { mode: S.ZoomMode.FitAll } }),\n automatic: () => api.update({ zoom: { mode: S.ZoomMode.Automatic } }),\n refit: () => {\n // The page geometry changed underneath us (rotate/move/delete). Treat it\n // exactly like a viewport resize: re-resolve the active zoom intent and\n // re-place against the now-current scene, keeping the anchored page-point.\n // No-op until the first placement; the scene is re-keyed on the registry\n // revision, so `reapply` reads the rotated footprint.\n if (!hasPlaced) return;\n cancelAnim();\n reapply(currentAnchor());\n },\n goToPage: (pageIndex, opts) => goToTarget(pageIndex, opts),\n reveal: (pageIndex, opts) => {\n const doc = ctx.document();\n if (!doc || doc.pageCount === 0) return;\n const target = Math.max(0, Math.min(pageIndex, doc.pageCount - 1));\n const positioned =\n !!opts &&\n (opts.rect !== undefined ||\n opts.anchor !== undefined ||\n (opts.zoom !== undefined && opts.zoom !== 'keep'));\n\n if (!positioned) {\n // Bare reveal — NOT navigation: minimal visibility, cursor untouched.\n if (paged()) {\n // the page isn't in the one-item slice — revealing it IS navigating to it\n goToTarget(target, opts);\n return;\n }\n const sc = buildScene();\n if (!sc.itemCount) return;\n const page = pageRectOf(sc.items[itemIndexOfPage(target)], target);\n // Reveal the OUTER box: pageFrame chrome (labels, buttons) belongs to the\n // page, so \"make the page visible\" includes its reserved bands.\n const m = worldPageFrame();\n const box = {\n x: page.x - m.left,\n y: page.y - m.top,\n width: page.width + m.left + m.right,\n height: page.height + m.top + m.bottom,\n };\n const camera = S.revealCamera(cam(), box, vp(), pad());\n const current = cam();\n if (camera.x === current.x && camera.y === current.y) return; // already visible\n cancelAnim();\n if ((opts?.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n animateTo(camera, sceneRect());\n } else {\n setCam(camera, sceneRect());\n }\n return;\n }\n\n // Positioned reveal: an ARRIVAL at a rect/point (search hit, PDF\n // destination). Like navigation, the cursor is INTENT — set up front\n // (paged: this also rebuilds the one-item slice), not derived from a\n // possibly mid-tween camera.\n cancelAnim();\n if (target !== ctx.getState().cursor) {\n ctx.dispatch({ type: 'CURSOR', cursor: target });\n }\n\n const place = (): { camera: S.Camera; bounds: S.Rect; zoomChanged: boolean } | null => {\n const sc = buildScene();\n if (!sc.itemCount) return null;\n const item = paged() ? sc.items[0] : sc.items[itemIndexOfPage(target)];\n const world = opts.rect\n ? worldRectForContent(item, target, opts.rect)\n : pageRectOf(item, target);\n const zd = opts.zoom ?? 'keep';\n const availW = Math.max(1, vp().width - 2 * pad());\n const availH = Math.max(1, vp().height - 2 * pad());\n let zoom =\n typeof zd === 'object'\n ? zd.level\n : zd === 'fit'\n ? Math.min(availW / world.width, availH / world.height)\n : zd === 'fit-width'\n ? availW / world.width\n : zd === 'fit-height'\n ? availH / world.height\n : cam().zoom;\n // Degenerate target (a point with a fit directive) → pan only.\n if (!Number.isFinite(zoom) || zoom <= 0) zoom = cam().zoom;\n const zoomChanged = zd !== 'keep';\n const a = opts.anchor ?? {};\n return {\n camera: {\n x: revealAxis(a.x, cam().x, world.x, world.width, vp().width, zoom, zoomChanged),\n y: revealAxis(a.y, cam().y, world.y, world.height, vp().height, zoom, zoomChanged),\n zoom,\n },\n bounds: boundsFor(item),\n zoomChanged,\n };\n };\n\n const first = place();\n if (!first) return;\n // A resolved zoom becomes the zoom intent (like zoomAround), so later\n // resizes/refits keep the destination's magnification.\n if (first.zoomChanged) {\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: first.camera.zoom } } });\n }\n if ((opts.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n // If the zoom patch re-wrapped the scene (zoom is a layout input in\n // wrapped mode), recompute once against the new geometry.\n const p = place() ?? first;\n animateTo(p.camera, p.bounds);\n } else {\n stabilized(() => {\n const p = place();\n if (p) setCam(p.camera, p.bounds);\n });\n }\n },\n next: (opts) => step(1, opts),\n prev: (opts) => step(-1, opts),\n update: (patch) => {\n cancelAnim();\n // React per the registry — the strongest effect among the touched settings\n // wins: 'reflow' ⊃ 'scene'/'refit' ⊃ 'reclamp' ⊃ 'none'.\n const touched = (effect: SettingEffect) =>\n SETTING_KEYS.some((k) => patch[k] !== undefined && SETTINGS_EFFECT[k] === effect);\n // The change's INVARIANT point: a pure zoom-intent change ('refit' alone)\n // holds the zoomAlign focal point — zoomTo/fit-mode switches magnify\n // around the same spot the zoom buttons do; every other reframe holds\n // the anchorAlign reference. Resolved once, used for capture AND restore.\n const align =\n touched('refit') && !touched('scene') && !touched('reflow')\n ? ctx.getState().zoomAlign\n : ctx.getState().anchorAlign;\n const anchor = anchorAt(alignPoint(align, vp())); // capture against the current scene\n ctx.dispatch({ type: 'PATCH', patch });\n if (touched('scene')) sceneCache = null;\n if (touched('reflow')) {\n // flow toggled: re-place onto the cursor's page under the new flow's scene\n // (the camera's coordinates are meaningless across the flow boundary).\n goToTarget(ctx.getState().cursor, { behavior: 'instant' });\n } else if (touched('scene') || touched('refit')) {\n reapply(anchor, align); // rebuild + keep page + re-fit (fit-all: re-place the scene)\n } else if (touched('reclamp')) {\n setCam(cam()); // clamp policy changed: just re-clamp the current camera\n }\n // 'none' (arrivalAlign, zoomAlign, anchorAlign, scrollBehavior): future verbs only\n },\n setFlow: (flow) => api.update({ flow }),\n setLayout: (layout) => api.update({ layout }),\n setSpread: (spread) => api.update({ spread }),\n setSizing: (sizing) => api.update({ sizing }),\n setColumns: (columns) => api.update({ columns }),\n setBounded: (bounded) => api.update({ bounded }),\n setPadding: (padding) => api.update({ padding }),\n setGap: (gap) => api.update({ gap }),\n setPageFrame: (pageFrame) => api.update({ pageFrame }),\n setFitAlign: (fitAlign) => api.update({ fitAlign }),\n setArrivalAlign: (arrivalAlign) => api.update({ arrivalAlign }),\n setZoomAlign: (zoomAlign) => api.update({ zoomAlign }),\n setAnchorAlign: (anchorAlign) => api.update({ anchorAlign }),\n setDirection: (direction) => api.update({ direction }),\n setViewRotation: (viewRotation) => api.update({ viewRotation }),\n rotateView: (delta) =>\n api.setViewRotation(addRotations(ctx.getState().viewRotation, delta === 90 ? 90 : 270)),\n setScrollBehavior: (behavior) => api.update({ scrollBehavior: behavior }),\n applyViewState: (view) => {\n cancelAnim();\n ctx.dispatch({ type: 'PATCH', patch: pickSettings(view) });\n ctx.dispatch({ type: 'CURSOR', cursor: view.cursor ?? 0 });\n sceneCache = null;\n applyAnchor(view.anchor);\n },\n provideInitialView: (priority, fn) => {\n initialViewProviders.push({ priority, fn });\n },\n placeInitial: () => {\n hasPlaced = true;\n const sorted = [...initialViewProviders].sort((a, b) => b.priority - a.priority);\n for (const p of sorted) {\n const view = p.fn();\n if (view) {\n api.applyViewState(view);\n return;\n }\n }\n api.resetView();\n },\n // Home = page 0, a fresh arrival at arrivalAlign (the clamp collapses any\n // fitting axis to its fitAlign rest point).\n resetView: () => goToTarget(0, { behavior: 'instant' }),\n };\n return api;\n}\n","import type { InteractionCapability, InteractionHandler } from '@embedpdf/plugin-interaction';\nimport type { StageCapability } from './types';\n\nexport interface ScrollHandlerOptions {\n /**\n * Let a drag over a page GAP pan regardless of the active tool — and show a grab\n * cursor there — so the gutter always pans (v2 parity, and the natural feel:\n * outside a page there's nothing to draw/select on). On-page behaviour is\n * untouched; the tool's own gesture still owns page space. Default true.\n */\n panFallback?: boolean;\n}\n\n/**\n * Pan-the-camera as an interaction handler. Live under the built-in `pan` tool\n * (the `scroll` tag) and — with `panFallback` — under every tool, where it then\n * captures ONLY page-gap drags (on-page downs belong to the active tool, which\n * claims them first at higher priority). It uses the sample's viewport delta, so\n * it pans over the whole viewport (pages and gaps).\n *\n * Cursor feel: a closed hand (`grabbing`) while dragging; an open hand (`grab`)\n * hovering a gap under a non-pan tool. The pan tool's own resting `grab` comes\n * from its `Tool.cursor`, so hovering it needs no claim here.\n */\nexport function createScrollHandler(\n stage: StageCapability,\n interaction: InteractionCapability,\n options: ScrollHandlerOptions = {},\n): InteractionHandler {\n const panFallback = options.panFallback ?? true;\n let last = { x: 0, y: 0 };\n const isPanTool = (): boolean => interaction.activeTool().enables.has('scroll');\n return {\n id: 'stage-scroll',\n priority: 10, // below page-aware handlers (selection/annotation) — they claim first\n enabledFor: (tool) => tool.enables.has('scroll') || panFallback,\n onDown: (s) => {\n // Fallback: a non-pan tool pans only over a gap. On a page, decline so the\n // tool's own (higher-priority) handler keeps the down it already captured.\n if (!isPanTool() && s.page) return false;\n last = s.viewport;\n interaction.setCursor('stage-grab', 'grabbing', 40); // closed hand while panning\n return true; // capture the drag\n },\n onMove: (s) => {\n stage.panBy(s.viewport.x - last.x, s.viewport.y - last.y);\n last = s.viewport;\n },\n onUp: () => interaction.setCursor('stage-grab', null),\n onHover: (s) => {\n // Open hand over a gap when a non-pan tool would fall back to pan there;\n // cleared over a page (the tool's cursor shows). The pan tool skips this —\n // its resting `grab` is the tool cursor.\n if (panFallback && !isPanTool()) {\n interaction.setCursor('stage-pan-fallback', s.page ? null : 'grab', 5);\n }\n },\n };\n}\n","import type { CapabilityToken, EffectContext } from '@embedpdf/core';\nimport type { StageAction, StageCapability, StageState } from './types';\n\n/**\n * The one effect a stage lens needs: when the document's page registry changes\n * (rotate/move/delete bump `DocumentMeta.revision`), the page geometry under\n * this lens just changed, so re-fit — re-resolve the active zoom intent and\n * re-place against the new footprint.\n *\n * Without it, fit/pixel zoom modes stay anchored to the pre-mutation dimensions:\n * `pageWidth: 110` renders wider after a 90° rotation (width↔height swap), and\n * `fitPage` over- or under-fills. The scene already re-keys on the revision, so\n * only the resolved `cam.zoom` was stale; `refit()` recomputes it.\n *\n * Steady-state only: this reacts to revision CHANGES, never the initial value,\n * so it can't race the capability's level-triggered initial placement (the\n * reason the rest of the plugin is effect-free).\n */\nexport function registerStageEffects(\n ctx: EffectContext<StageState, StageAction>,\n token: CapabilityToken<StageCapability>,\n): void {\n ctx.watch(\n () => ctx.document()?.revision ?? 0,\n () => ctx.get(token).refit(),\n );\n}\n","import { DEFAULT_SETTINGS } from './settings';\nimport type { StageAction, StageConfig, StageSettings, StageState } from './types';\n\nexport const initialStageState = (config: StageConfig): StageState => {\n const { scheduler: _scheduler, ...overrides } = config;\n return {\n camera: { x: 0, y: 0, zoom: 1 },\n vp: { width: 0, height: 0 },\n dpr: 1,\n cursor: 0,\n ...DEFAULT_SETTINGS,\n ...overrides, // config overrides any default; the rest fall back to DEFAULT_SETTINGS\n };\n};\n\n/** Merge a settings patch, ignoring undefined values (safe for partial restores). */\nconst applyPatch = (state: StageState, patch: Partial<StageSettings>): StageState => {\n const next: StageState = { ...state };\n let key: keyof StageSettings;\n for (key in patch) {\n const value = patch[key];\n if (value !== undefined) Object.assign(next, { [key]: value });\n }\n return next;\n};\n\n/**\n * Pure. Every transition is here; nothing else mutates Stage state. Settings are a\n * flat bag of primitives — one PATCH action sets any subset (the capability decides\n * what camera follow-up, if any, each change needs).\n */\nexport const stageReducer = (state: StageState, a: StageAction): StageState => {\n switch (a.type) {\n case 'CAMERA':\n return { ...state, camera: a.camera };\n case 'VP':\n return { ...state, vp: a.vp };\n case 'DPR':\n return { ...state, dpr: a.dpr };\n case 'CURSOR':\n return { ...state, cursor: a.cursor };\n case 'PATCH':\n return applyPatch(state, a.patch);\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { PageObjectNumber } from '@embedpdf/core';\nimport type { PageRotation, PageTransform, Rect } from '@embedpdf/core-geometry';\nimport type {\n Alignment,\n AlignmentValue,\n AlignValue,\n Anchor,\n Direction,\n Camera,\n PageBox,\n PageFrame,\n Point,\n ScrollMetrics,\n Size,\n SizingMode,\n SpreadMode,\n ZoomModeValue,\n ZoomSpec,\n} from '@embedpdf/core-stage';\n\nexport type LayoutKind = 'vertical' | 'horizontal' | 'grid';\n/** Navigation (goToPage) either tweens the camera or jumps instantly. */\nexport type ScrollBehaviorKind = 'smooth' | 'instant';\n/**\n * Presentation flow:\n * 'continuous' — the whole document is scrollable; the camera roams the full scene.\n * 'paged' — one item (page or spread) at a time: the scene is a one-item\n * slice at the cursor, and next/prev step between items.\n */\nexport type FlowMode = 'continuous' | 'paged';\n/**\n * Grid column policy:\n * 'square' — ≈√n columns (the classic canvas arrangement)\n * 'auto' — WRAPPED: as many columns as fit the viewport line at the current\n * zoom (the responsive thumbnail-sidebar behavior; re-wraps on resize)\n * number — a fixed column count\n */\nexport type GridColumns = 'square' | 'auto' | number;\n\n/**\n * Space between items — the value's shape carries the unit (like ZoomSpec):\n * number — WORLD units: the gap is part of the canvas and scales with zoom,\n * so the whole scene zooms as one rigid object (the document feel).\n * { px: n } — SCREEN px: UI-stable spacing, the same in every document at every\n * zoom (the browser-of-items feel: thumbnails, organizers).\n */\nexport type Gap = number | { px: number };\n\n/**\n * One axis of the ARRIVAL policy — stage-core's AlignValue ('start' |\n * 'center' | 'end' | viewport fraction 0–1) plus one navigation-only word:\n * 'keep' — this axis does not move on arrival: page forward, hold your\n * pan (the two-column-paper feel; the PDF /XYZ null semantic).\n */\nexport type ArrivalAlignValue = AlignValue | 'keep';\nexport interface ArrivalAlignment {\n x: ArrivalAlignValue;\n y: ArrivalAlignValue;\n}\n\n/**\n * The Stage's orthogonal, independently-settable primitives. Every field can be set\n * on its own (setLayout, setBounded, …) or several at once via `update()`. A\n * \"preset\" is just a `Partial<StageSettings>` the app keeps and applies — no preset\n * machinery lives here.\n */\nexport interface StageSettings {\n /** Continuous scroll, or one item at a time. */\n flow: FlowMode;\n layout: LayoutKind;\n spread: SpreadMode;\n /** Page sizing: true PDF sizes, or equalize the cross axis so pages sit flush. */\n sizing: SizingMode;\n /** Grid column policy (grid layout only): 'square', 'auto' (wrapped), or a count. */\n columns: GridColumns;\n /** Clamp the camera to the content? Off = free infinite pan (plans / CAD). */\n bounded: boolean;\n /**\n * Breathing room (screen px) around the content — the one spacing concept.\n * Fit-modes inset by it, arrivals leave it as a gutter, and the clamp lets the\n * camera reveal exactly this much beyond each content edge.\n */\n padding: number;\n /**\n * Space BETWEEN items — and between the halves of a spread. A number is world\n * units (scales with zoom — the canvas feel); `{ px }` is screen px (UI-stable\n * — the thumbnail feel). See {@link Gap}.\n */\n gap: Gap;\n /**\n * Reserved chrome real estate around EACH PAGE, in SCREEN px — one thickness\n * per side. The page content is inset by these; the bands hold box-space\n * chrome (a label below, a button row above, side rails) painted by the app\n * via the adapter's `pageChrome` slot. Per page, not per item: in a spread\n * every page keeps its own flanks. Constant screen px (unaffected by zoom).\n *\n * Naming rule for this settings bag: every setting describes the STAGE itself\n * (the container) — `padding`, `gap`, `layout`, … The rare setting owned by the\n * page carries the `page` prefix (`pageFrame`; `pageWidth` in zoom).\n */\n pageFrame: PageFrame;\n /**\n * Reading direction. RTL: horizontal items advance leftward, spreads bind on the\n * right, grid rows fill right→left, and alignment 'start' on x means the RIGHT\n * edge (logical, CSS-style). Navigation is index-based and never changes.\n */\n direction: Direction;\n /**\n * The alignment family — EVERY camera move is defined by what it holds\n * fixed. Gestures (pan/pinch/wheel) hold the pointer: physics, no setting.\n * Explicit arrivals (positioned reveal, destinations, viewpoints) hold\n * whatever the call specifies. These four settings govern the rest:\n *\n * fitAlign — the standing CONSTRAINT: where content rests on an axis\n * the camera cannot travel (it fits the TRUE bounds — the\n * scene in continuous flow, the item slice in paged). The\n * clamp enforces it on every camera write — which is why a\n * fitting axis settles identically whatever arrivalAlign\n * says. center/center = document feel; y:'start' = sidebar\n * thumbnails hugging the top.\n * arrivalAlign — the landing POLICY: where navigation (goToPage, next/\n * prev, reset) puts the target — THE SAME at every zoom.\n * start/start = reading (top-left, direction-aware);\n * center/center = presentation/drawings (Drawboard feel);\n * y: 0.35 = the find-bar line; 'keep' = don't move an axis.\n * zoomAlign — the FOCAL point of a pointer-less zoom (zoomIn/zoomOut,\n * zoomTo, fit-mode switches). Pinch/ctrl+wheel always hold\n * the pointer instead — that is physics, not policy.\n * center/center = the view inflates around its middle;\n * y:'start' = the first visible line holds still.\n * anchorAlign — the viewport point that SURVIVES a reframe (viewport\n * resize, page rotation, spread/gap change): the view\n * anchor is captured there and restored there. start/start\n * = the browser scroll model (growth reveals below — a\n * container that mounts small and expands never shoves the\n * document down); center/center = canvas-style symmetric\n * resizes (the Figma feel).\n *\n * Named x values are LOGICAL (CSS-style: 'start' = reading start — the\n * right edge in RTL); fractions are physical, like screen coordinates.\n */\n fitAlign: Alignment;\n /** See {@link StageSettings.fitAlign} — where navigation lands, per axis. */\n arrivalAlign: ArrivalAlignment;\n /** See {@link StageSettings.fitAlign} — the pointer-less zoom focal point. */\n zoomAlign: AlignmentValue;\n /** See {@link StageSettings.fitAlign} — the viewport point reframes hold. */\n anchorAlign: AlignmentValue;\n /**\n * NON-PERSISTENT view rotation: a quarter-turn (clockwise) applied to how\n * EVERY page is DISPLAYED in this lens, on top of each page's own /Rotate —\n * Adobe's \"Rotate View\". A display setting like `zoom` or `layout`: per lens\n * (the main viewer can rotate while a thumbnail lens stays upright), never\n * written to the document, gone when the lens resets. The PERMANENT\n * counterpart — writing /Rotate into the PDF — is plugin-page-edit's\n * `rotateBy`/`setRotation`.\n */\n viewRotation: PageRotation;\n /** Zoom intent: a fit-mode (automatic/fit-page/fit-width/fit-all) or a fixed level. */\n zoom: ZoomSpec;\n /** Default behaviour for goToPage/next/prev. */\n scrollBehavior: ScrollBehaviorKind;\n /**\n * View pixels per PDF point — the platform's physical unit factor, folded into\n * the layout so 100% (zoom 1) is physically accurate. Web = 96/72 (1 pt = 1/72\",\n * 1 CSS px = 1/96\"); a native platform injects its own (iOS pt, Android dp). It\n * scales every page's world size (and thus `contentScale`), so the camera math,\n * `gap`/`padding`/`pageFrame` (world units), and absolute-px zoom modes\n * (`pageWidth`) are all unaffected — only the pages themselves resize.\n */\n viewUnitsPerPoint: number;\n}\n\n/**\n * A laid-out page handed to the shell.\n * - PageBox + `pon`: LAYOUT truth (world coords + identity) — the shell uses\n * `x/y/width/height` only to POSITION the page container.\n * - `transform`: PRESENTATION truth — the single bridge between PDF points,\n * view px, and device px for this page. Plugins do ALL coordinate work\n * through it (`pageToView` / `viewToPage` / `deviceWidth` / `cssMatrix`),\n * never by re-deriving `x * scale` / `* dpr`. Page-local, so it's\n * camera/pan-invariant.\n */\nexport interface VisiblePage extends PageBox {\n pon: PageObjectNumber;\n /**\n * The page's DISPLAY-box (footprint) top-left in screen px, camera-resolved and\n * snapped to the device grid. The shell positions the page container at this —\n * snapping here (not in the adapter) keeps a CSS-rotated page on the pixel grid\n * for every framework, with no hand-rounding.\n */\n screenX: number;\n screenY: number;\n transform: PageTransform;\n /**\n * The page region actually ON SCREEN, in UN-rotated page points (y-down) —\n * viewport ∩ footprint inverted through the transform (exact for\n * quarter-turns). Zero-sized when the page sits outside the viewport.\n * Visibility is the STAGE's data (it already intersects viewport × pages to\n * virtualize); adapters and demand consumers (tiling's `PageViewDemand`)\n * read it instead of re-deriving camera math per framework.\n */\n visibleRect: Rect;\n}\n\nexport interface StageState extends StageSettings {\n camera: Camera;\n vp: Size;\n /**\n * Device pixels per view pixel (web: `window.devicePixelRatio`). Reported by\n * the shell like the viewport; feeds each page's transform so bitmaps render\n * crisp (exact device px) and boxes land on the device grid. Defaults to 1.\n */\n dpr: number;\n /**\n * The current page — transient like `camera` (NOT a setting), valid in BOTH flows.\n * Navigation sets it; in continuous flow scrolling syncs it from the camera; in\n * paged flow panning never moves it (the scene is a one-item slice at this page).\n * Stored as a page index so it survives spread/layout regrouping.\n */\n cursor: number;\n}\n\nexport type StageAction =\n | { type: 'CAMERA'; camera: Camera }\n | { type: 'VP'; vp: Size }\n | { type: 'DPR'; dpr: number }\n | { type: 'CURSOR'; cursor: number }\n | { type: 'PATCH'; patch: Partial<StageSettings> };\n\n/**\n * A page-relative view memento: \"what I'm looking at and how zoomed\". The durable\n * currency for per-page view memory (construction worksheets) — capture with\n * `viewpoint()`, restore with `goToPage(i, { viewpoint })`. Survives resizes\n * because the anchor is page-relative and fit-modes re-resolve.\n */\nexport interface Viewpoint {\n anchor: Anchor;\n zoom: ZoomSpec;\n}\n\n/** Durable, serializable view state — the unit of session persistence. */\nexport interface StageViewState extends StageSettings {\n cursor: number;\n anchor: Anchor;\n}\n\n/**\n * Host timing seam. The pure core (stage-core) never touches time; the camera tween\n * lives in this (impure) shell and asks for frames through a Scheduler. The default\n * is the browser's requestAnimationFrame; inject a fake in tests, or an instant one\n * in Node/SSR.\n */\nexport interface Scheduler {\n /** Run the callback on the next frame; returns a handle for cancellation. */\n raf(callback: (timestampMs: number) => void): number;\n /** Cancel a scheduled callback. */\n caf(handle: number): void;\n}\n\n/**\n * Options for the scroller writes — `Element.scrollTo` semantics: absolute\n * offsets (screen px) into the current scroll range (see\n * {@link StageCapability.scrollMetrics}); an omitted axis does not move.\n * `behavior` defaults to 'instant' (the DOM's 'auto'), NOT the stage's\n * `scrollBehavior` setting — that setting governs navigation verbs, and a\n * scrollbar thumb must track the pointer exactly.\n */\nexport interface StageScrollToOptions {\n left?: number;\n top?: number;\n behavior?: ScrollBehaviorKind;\n}\n\n/** Options for navigation intents. */\nexport interface GoToOptions {\n behavior?: ScrollBehaviorKind;\n /** Restore this exact viewpoint instead of fresh placement (per-page memory). */\n viewpoint?: Viewpoint;\n /** Override the landing for THIS navigation only (explicit beats default). */\n arrivalAlign?: Partial<ArrivalAlignment>;\n}\n\n/**\n * One axis of a reveal arrival — `scrollIntoView` vocabulary plus two\n * PDF-protocol necessities:\n * absent → minimal movement: scroll only if the target is off-screen\n * (CSS 'nearest'; today's reveal semantics)\n * 'keep' → this axis does not move AT ALL (PDF /XYZ null coordinate)\n * 'start' → target edge at the viewport start (plus padding)\n * 'center' → target centered\n * 'end' → target edge at the viewport end (minus padding)\n * number 0–1 → target CENTER at this viewport fraction (0.35 = \"top middle\",\n * the browser find-bar feel)\n */\nexport type RevealAnchorValue = 'keep' | 'start' | 'center' | 'end' | number;\n\nexport interface RevealAnchor {\n x?: RevealAnchorValue;\n y?: RevealAnchorValue;\n}\n\n/**\n * What happens to zoom on a reveal — always relative to the reveal's target\n * rect (the whole page when no `rect` is given):\n * 'keep' → pure pan, zoom untouched (search hits, /XYZ null zoom)\n * { level } → explicit factor (/XYZ zoom)\n * 'fit' → the rect fully visible (/FitR; /Fit, /FitB via rect=page/bbox)\n * 'fit-width' → the rect's width fills the viewport (/FitH, /FitBH)\n * 'fit-height' → the rect's height fills the viewport (/FitV, /FitBV)\n */\nexport type RevealZoom = 'keep' | 'fit' | 'fit-width' | 'fit-height' | { level: number };\n\n/**\n * Options for `reveal` — the follower-UI arrival verb (search hits, outline\n * clicks, PDF destinations, \"jump to comment\").\n *\n * With none of `rect`/`zoom`/`anchor` set, reveal keeps its original\n * semantics: minimal movement to make the page visible, cursor untouched.\n * A POSITIONED reveal (any of the three set) is \"you are now looking at\n * THIS spot\": the camera places the target per the anchor, a zoom\n * directive resolves to a concrete level (recorded as the zoom intent),\n * and the cursor follows the camera — while still clamping against the\n * normal camera bounds, so anchors are best-effort near document edges.\n */\nexport interface RevealOptions {\n behavior?: ScrollBehaviorKind;\n /**\n * CONTENT-space target rect on the page (y-down, crop-relative, unscaled\n * points — the same space selection/search rects live in). Absent → the\n * whole page. A zero-size rect is a point (/XYZ).\n */\n rect?: Rect;\n zoom?: RevealZoom;\n anchor?: RevealAnchor;\n}\n\n/** The Stage's public contract: selectors (reads) + intents (the only writers). */\nexport interface StageCapability {\n // ── selectors ──\n camera(): Camera;\n viewport(): Size;\n /**\n * The camera as a NATIVE SCROLLER — the DOM scroll vocabulary in screen px:\n * `scrollTop`/`scrollHeight`/`clientHeight` (and the x twins) mean exactly\n * what they mean on a DOM element; `scrollableX/Y` false ⇔ nothing to scroll\n * on that axis (native: no bar). Derived from the SAME travel range the pan\n * clamp uses — paged flow reads the one-item slice — so a scrollbar built on\n * it can never disagree with where panning stops. On an UNBOUNDED stage the\n * range is the union of the padded content and the current window (the Figma\n * bar): pan away and it grows, the thumb shrinking toward the edge but always\n * remaining a road back. Reference-stable until a field actually changes.\n */\n scrollMetrics(): ScrollMetrics;\n pageCount(): number;\n visiblePages(): VisiblePage[];\n /** The current page (the cursor) — valid in both flows. */\n currentPage(): number;\n /** The display indices of the current item's pages (1 page, or a spread's pages). */\n currentItemPages(): number[];\n /** The full page list with PDF labels — for page thumbnails / worksheet tabs. */\n pages(): Array<{ index: number; pon: PageObjectNumber; label: string | null }>;\n /** The laid-out box for a page by its durable pon. */\n pageRect(pon: PageObjectNumber): VisiblePage | null;\n /**\n * Screen point (this Stage's container px) → the page under it + its content\n * point, or null over a gap. The viewport-level hit-test the interaction hub\n * needs so a single pointer source can drive page-aware features (text\n * selection, annotations) AND cross-page drags.\n */\n pageAt(screen: Point): {\n pon: PageObjectNumber;\n point: Point;\n scale: number;\n /** The hit page's TOTAL display rotation (document /Rotate + view rotation). */\n rotation: PageRotation;\n /** The hit page's zoom relative to its 100% baseline (`transform.zoom`). */\n zoom: number;\n } | null;\n /**\n * Screen point → `pon`'s content space, UNCLAMPED — valid even when the point\n * is outside the page's bounds (coordinates then fall outside `[0, size]`).\n * The frame-stable projection a page-anchored gesture (annotation move/resize)\n * tracks with, where `pageAt` would re-resolve to whatever page is under the\n * cursor (what a cross-page drag like text selection wants). Null when the\n * page isn't currently laid out.\n */\n pointOnPage(pon: PageObjectNumber, screen: Point): Point | null;\n /**\n * Page space (intrinsic PDF points) → world space. Applies the page's placed\n * origin and contentScale — the transform sizing policies introduce. Compose\n * with toScreen for viewport-space overlays anchored to page content.\n */\n pageToWorld(pon: PageObjectNumber, pt: Point): Point | null;\n /**\n * Content rect on a page → this Stage viewport's screen-space AABB. Applies page\n * rotation/contentScale and the current camera. Use for upright viewport overlays\n * that need to frame a selected page region.\n */\n pageRectToScreen(pon: PageObjectNumber, rect: Rect): Rect | null;\n toScreen(world: Point): Point;\n toWorld(screen: Point): Point;\n flow(): FlowMode;\n layout(): LayoutKind;\n spread(): SpreadMode;\n sizing(): SizingMode;\n columns(): GridColumns;\n bounded(): boolean;\n padding(): number;\n gap(): Gap;\n pageFrame(): PageFrame;\n fitAlign(): Alignment;\n arrivalAlign(): ArrivalAlignment;\n zoomAlign(): AlignmentValue;\n anchorAlign(): AlignmentValue;\n direction(): Direction;\n scrollBehavior(): ScrollBehaviorKind;\n /** The lens's view rotation — see {@link StageSettings.viewRotation}. */\n viewRotation(): PageRotation;\n zoomLevel(): number;\n /** The active zoom intent: a fit-mode, or 'custom' for a fixed level. */\n zoomMode(): ZoomModeValue | 'custom';\n /** What I'm looking at + zoom intent — capture for per-page view memory. */\n viewpoint(): Viewpoint;\n /** A snapshot of all settings (handy for building/saving a customer preset). */\n settings(): StageSettings;\n viewState(): StageViewState;\n\n // ── intents ──\n setViewport(vp: Size): void;\n /** Report the device pixel ratio (web: `devicePixelRatio`). The shell calls\n * this once on mount and whenever it changes (e.g. dragging between monitors)\n * so page transforms render crisp. */\n setDevicePixelRatio(dpr: number): void;\n setCamera(c: Camera): void;\n panBy(dxScreen: number, dyScreen: number): void;\n /** `Element.scrollTo` for the camera: absolute offsets into the scroll range\n * (see {@link StageScrollToOptions}) — clamped into it, cursor-synced, zoom\n * untouched (scrolling is a pan in scroller clothing). */\n scrollTo(opts: StageScrollToOptions): void;\n /** `Element.scrollBy`: relative offsets — sugar over `scrollTo`. */\n scrollBy(opts: StageScrollToOptions): void;\n zoomAround(screenPt: Point, factor: number): void;\n zoomIn(): void;\n zoomOut(): void;\n zoomTo(spec: ZoomSpec): void;\n fitWidth(): void;\n fitPage(): void;\n /** Fit the whole scene (every page) in view — the construction overview. */\n fitAll(): void;\n /** Fit width but never upscale past 100% (Adobe's \"Automatic\"). */\n automatic(): void;\n /**\n * Re-resolve the active zoom intent and re-place against the CURRENT scene,\n * preserving the anchored page-point. Call after the page geometry changes\n * underneath the lens — rotate/move/delete — so fit/pixel zoom modes\n * (`fitPage`, `pageWidth`, …) recompute against the new footprint. A no-op\n * before the first placement; for a fixed `{ level }` zoom it just re-anchors.\n * Wired automatically to the document's page-registry revision; exposed for\n * any external geometry change a host wants to react to.\n */\n refit(): void;\n /** Go to a page. Fresh arrival places by the unit rule; pass `viewpoint` to restore. */\n goToPage(pageIndex: number, opts?: GoToOptions): void;\n /**\n * The follower-UI arrival verb (thumbnail sidebars, search hits, outline\n * clicks, PDF destinations). Bare: make the page visible with minimal\n * movement — zero if it already is, cursor untouched (scrollIntoView\n * semantics; paged flow delegates to navigation since the page isn't in\n * the slice). POSITIONED (rect/zoom/anchor set — see {@link RevealOptions}):\n * place the target at the anchor, optionally re-zooming; the cursor\n * follows the camera.\n */\n reveal(pageIndex: number, opts?: RevealOptions): void;\n /** Step forward by the navigation unit (the item if it fits the viewport, else the page). */\n next(opts?: GoToOptions): void;\n /** Step backward by the navigation unit. */\n prev(opts?: GoToOptions): void;\n /** Set any subset of settings at once — ONE anchor-preserving update. The way to\n * apply a customer preset: `update(myPreset)`. */\n update(patch: Partial<StageSettings>): void;\n setFlow(flow: FlowMode): void;\n setLayout(layout: LayoutKind): void;\n setSpread(spread: SpreadMode): void;\n setSizing(sizing: SizingMode): void;\n setColumns(columns: GridColumns): void;\n setBounded(bounded: boolean): void;\n setPadding(padding: number): void;\n setGap(gap: Gap): void;\n setPageFrame(pageFrame: PageFrame): void;\n setFitAlign(fitAlign: Alignment): void;\n setArrivalAlign(arrivalAlign: ArrivalAlignment): void;\n setZoomAlign(zoomAlign: AlignmentValue): void;\n setAnchorAlign(anchorAlign: AlignmentValue): void;\n setDirection(direction: Direction): void;\n /** Set the lens's view rotation to an absolute quarter-turn — see\n * {@link StageSettings.viewRotation}. An anchor-preserving reframe, like a\n * page rotation: the spot you were looking at stays put (`anchorAlign`). */\n setViewRotation(viewRotation: PageRotation): void;\n /** Rotate the view a quarter-turn from where it is (the toolbar verb) —\n * relative sugar over {@link setViewRotation}, mirroring page-edit's `rotateBy`. */\n rotateView(delta: 90 | -90): void;\n setScrollBehavior(behavior: ScrollBehaviorKind): void;\n applyViewState(view: StageViewState): void;\n /** Offer a candidate initial view; the highest-priority non-null wins at placement. */\n provideInitialView(priority: number, provider: () => StageViewState | null): void;\n /** Resolve the registered providers once (else reset). Called when the viewport is ready. */\n placeInitial(): void;\n /** Back to the start: page 0, placed by the unit rule at the current zoom intent. */\n resetView(): void;\n}\n\nexport interface StageConfig extends Partial<StageSettings> {\n /** Override the host timing seam (tests/SSR). Defaults to browser rAF. */\n scheduler?: Scheduler;\n}\n\nexport const StageToken = createCapabilityToken<StageCapability>('stage');\n","import { definePlugin } from '@embedpdf/core';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { createStageCapability } from './capability';\nimport { createScrollHandler } from './scroll-handler';\nimport { registerStageEffects } from './effects';\nimport { initialStageState, stageReducer } from './reducer';\nimport { StageToken } from './types';\nimport type { StageAction, StageCapability, StageConfig, StageState } from './types';\n\n/**\n * Options for registering a stage instance. The Stage is a LENS, not a singleton:\n * a document may be viewed through several stages at once (the main view, a wrapped\n * thumbnail sidebar, …), each with independent camera/settings. Register additional\n * lenses by giving them their own `id` + `token`:\n *\n * const ThumbsToken = createCapabilityToken<StageCapability>('stage-thumbs');\n * plugins = [\n * stagePlugin(), // main lens\n * stagePlugin({ id: 'stage-thumbs', token: ThumbsToken,\n * layout: 'grid', columns: 'auto', zoom: { level: 0.2 } }),\n * ];\n *\n * Everything multiplexes automatically: state slices, capabilities, and teardown\n * are already keyed by plugin-id × document in the kernel.\n */\nexport interface StagePluginOptions extends StageConfig {\n id?: string;\n token?: CapabilityToken<StageCapability>;\n /**\n * Opt this lens into the interaction hub: register a tool-gated `scroll`\n * handler (so dragging pans only in `pan` mode). Pair with `<Stage interaction>`\n * on the React side, which forwards pointer events to the hub. Default false —\n * secondary lenses (thumbnails) stay click-to-navigate.\n */\n interaction?: boolean;\n /**\n * When {@link interaction} is on, let drags over page GAPS pan regardless of the\n * active tool (and show a grab cursor there) — the gutter always pans, matching\n * v2 and the intuition that there's nothing to draw/select outside a page.\n * On-page behaviour is untouched. Default true; ignored without `interaction`.\n */\n panFallback?: boolean;\n}\n\n/**\n * Wires the parts into a kernel plugin. This file is the \"manifest\": it says what\n * the plugin IS (id, token, state, reducer, capability) — the how lives in the\n * sibling files.\n */\nexport const stagePlugin = (options: StagePluginOptions = {}) => {\n const {\n id = 'stage',\n token = StageToken,\n interaction = false,\n panFallback = true,\n ...config\n } = options;\n return definePlugin<StageState, StageAction, StageCapability>({\n id,\n token,\n scope: 'document', // one instance of THIS lens per open document\n // When this lens drives interaction, it contributes the pan-scroll handler to\n // the hub (optional dep — the hub may not be present in a headless setup).\n optional: interaction ? [InteractionToken] : undefined,\n initialState: () => initialStageState(config),\n reduce: stageReducer,\n capability: (ctx) => createStageCapability(ctx, config),\n init: interaction\n ? (ctx) => {\n const ix = ctx.tryGet(InteractionToken);\n if (ix) ix.registerHandler(createScrollHandler(ctx.get(token), ix, { panFallback }));\n }\n : undefined,\n // INITIAL placement is deliberately NOT an effect: it's LEVEL-triggered\n // inside the capability's setViewport (place when the stage first learns a\n // real size), so it cannot race effect registration. Other plugins only\n // *offer* initial views via provideInitialView; placeInitial resolves them\n // by priority. The one effect below is STEADY-STATE — it re-fits when the\n // page registry mutates (rotate/move/delete) and so has no such race.\n effects: (ctx) => registerStageEffects(ctx, token),\n });\n};\n","/**\n * PDF destination → reveal: the pure translator between the PDF protocol's\n * navigation vocabulary (ISO 32000-1 §12.3.2.2) and the Stage's arrival\n * primitive. Outline clicks, link annotations, and `/OpenAction` all\n * resolve (engine-side) to an explicit `PdfDestination`; this maps it onto\n * ONE `reveal(pageIndex, options)` call — no destination-specific camera\n * code exists anywhere else.\n *\n * The whole protocol collapses onto three knobs:\n * rect — what to look at (a point for /XYZ, a rect for /FitR, the\n * page for /Fit, the content bounding box for /FitB*)\n * zoom — 'keep' | {level} | 'fit' | 'fit-width' | 'fit-height'\n * anchor — where it lands; 'keep' encodes the spec's null-means-retain\n *\n * Coordinates convert PDF user space (y-up, absolute) → content space\n * (y-down, crop-relative) through the SAME `pdfToContent` matrix selection\n * and search use, so a destination and an overlay can never disagree.\n */\nimport { applyPoint, pageGeometry } from '@embedpdf/core-geometry';\nimport type { Rect } from '@embedpdf/core-geometry';\nimport type { PageLayout, PdfDestination } from '@embedpdf/core';\nimport type { RevealOptions } from './types';\n\nexport interface DestinationReveal {\n /** Display index for `reveal()` — from the layout row. */\n pageIndex: number;\n options: RevealOptions;\n}\n\n/**\n * Translate one explicit destination against its target page's layout.\n * `bbox` is the page's content BOUNDING box in CONTENT space, for the\n * `/FitB*` kinds — pass it when known; the crop box is the spec-tolerated\n * fallback until the engine exposes it.\n */\nexport function destinationToReveal(\n dest: PdfDestination,\n layout: PageLayout,\n bbox?: Rect,\n): DestinationReveal {\n const geo = pageGeometry(\n { crop: layout.boxes.crop, rotation: layout.rotation, userUnit: layout.userUnit },\n 1,\n );\n const crop = layout.boxes.crop;\n const toContent = (x: number, y: number) => applyPoint(geo.pdfToContent, { x, y });\n const page: Rect = { x: 0, y: 0, width: layout.size.width, height: layout.size.height };\n const box = bbox ?? page;\n\n const options = ((): RevealOptions => {\n switch (dest.kind) {\n case 'xyz': {\n const hasLeft = dest.left != null;\n const hasTop = dest.top != null;\n // Null axes keep the current camera value — the coordinate fed in\n // for them is inert (the 'keep' anchor never reads it).\n const p = toContent(dest.left ?? crop.left, dest.top ?? crop.top);\n return {\n rect: { x: p.x, y: p.y, width: 0, height: 0 },\n anchor: { x: hasLeft ? 'start' : 'keep', y: hasTop ? 'start' : 'keep' },\n // A /XYZ zoom of 0 means null means \"retain current\".\n zoom: dest.zoom != null && dest.zoom !== 0 ? { level: dest.zoom } : 'keep',\n };\n }\n case 'fit':\n return { zoom: 'fit' }; // whole page; slack axis centers\n case 'fitH': {\n const hasTop = dest.top != null;\n const y = hasTop ? toContent(crop.left, dest.top!).y : 0;\n return {\n rect: { x: 0, y, width: page.width, height: 0 },\n zoom: 'fit-width',\n anchor: { y: hasTop ? 'start' : 'keep' },\n };\n }\n case 'fitV': {\n const hasLeft = dest.left != null;\n const x = hasLeft ? toContent(dest.left!, crop.top).x : 0;\n return {\n rect: { x, y: 0, width: 0, height: page.height },\n zoom: 'fit-height',\n anchor: { x: hasLeft ? 'start' : 'keep' },\n };\n }\n case 'fitR': {\n const tl = toContent(dest.left, dest.top);\n const br = toContent(dest.right, dest.bottom);\n return {\n rect: {\n x: Math.min(tl.x, br.x),\n y: Math.min(tl.y, br.y),\n width: Math.abs(br.x - tl.x),\n height: Math.abs(br.y - tl.y),\n },\n zoom: 'fit',\n };\n }\n case 'fitB':\n return { rect: box, zoom: 'fit' };\n case 'fitBH': {\n const hasTop = dest.top != null;\n const y = hasTop ? toContent(crop.left, dest.top!).y : box.y;\n return {\n rect: { x: box.x, y, width: box.width, height: 0 },\n zoom: 'fit-width',\n anchor: { y: hasTop ? 'start' : 'keep' },\n };\n }\n case 'fitBV': {\n const hasLeft = dest.left != null;\n const x = hasLeft ? toContent(dest.left!, crop.top).x : box.x;\n return {\n rect: { x, y: box.y, width: 0, height: box.height },\n zoom: 'fit-height',\n anchor: { x: hasLeft ? 'start' : 'keep' },\n };\n }\n }\n })();\n\n return { pageIndex: layout.index, options };\n}\n","/**\n * Wheel-input classification for ambient zoom — pure, so every framework\n * adapter shares one feel and the mapping is unit-testable.\n *\n * A ctrl/cmd wheel event is one of three physically different inputs that\n * happen to share an event type, each with its own unit system:\n *\n * • trackpad PINCH — browsers (Chrome/Edge/Firefox) synthesize ctrl+wheel\n * px-deltas scaled to the de-facto convention that `exp(-Δ/100)` tracks\n * the physical finger scale 1:1. Anything weaker feels dead (the content\n * must track the fingers — the Figma/maps feel).\n * • mouse NOTCHES — coarse steps: ±100/±120 px in Chrome, lines/pages mode\n * in Firefox. The pinch mapping would triple per click; a notch instead\n * steps by 1.2, the same ratio as the zoom buttons.\n * • cmd + continuous SCROLL — real scroll px, big and streaming (with\n * momentum): a gentle scrub coefficient. A cmd+mouse notch (±120 px)\n * lands within 0.3% of the 1.2 button step, so it needs no special case.\n *\n * (Safari never synthesizes ctrl+wheel for pinch — it fires proprietary\n * gesture events with an absolute scale; that wiring lives in the adapters.)\n */\nexport interface WheelSample {\n deltaY: number;\n /** WheelEvent.deltaMode: 0 = pixels, 1 = lines, 2 = pages. */\n deltaMode: number;\n ctrlKey: boolean;\n metaKey: boolean;\n}\n\n/** The per-notch step for discrete wheels — the zoom buttons' ratio. */\nconst NOTCH = 1.2;\n\n/** Zoom factor for a ctrl/cmd wheel event (the caller decides zoom vs pan). */\nexport function wheelZoomFactor(e: WheelSample): number {\n // Lines/pages mode (Firefox mouse wheels): one button-step per notch.\n if (e.deltaMode !== 0) return Math.pow(NOTCH, -Math.sign(e.deltaY));\n if (e.ctrlKey) {\n // px deltas with ctrl: a real mouse notch arrives big (±100/±120) and\n // steps like the buttons; anything smaller is a synthesized pinch.\n if (Math.abs(e.deltaY) >= 100) return Math.pow(NOTCH, -Math.sign(e.deltaY));\n return Math.exp(-e.deltaY / 100);\n }\n // cmd + continuous scroll: the gentle scrub.\n return Math.exp(-e.deltaY * 0.0015);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ZoomMode","S"],"sources":["../src/motion.ts","../src/responsive.ts","../src/settings.ts","../src/capability.ts","../src/effects.ts","../src/reducer.ts","../src/types.ts","../src/stage.plugin.ts","../src/destination.ts","../src/scroll-handler.ts"],"sourcesContent":["/**\n * The camera's MOTION vocabulary — every formula, pure.\n *\n * The capability owns the impure drivers (scheduler frames, dispatch, gesture\n * state); the LAWS those drivers integrate live here, parameterized by dt/t\n * with no state, no scheduler, no dispatch — unit-testable in isolation, and\n * with zero plugin dependencies, liftable into stage-core if the native port\n * ever wants them.\n *\n * Two families:\n * • ballistic — one per-axis law for every free motion: `glideStep` (the\n * touch fling, decaying on UIScrollView's curve) and `springStep` (a\n * critically-damped return to the clamp). A glide that reaches a content\n * edge converts to a spring with its velocity carried in, so the bounce\n * grows out of physics rather than a scripted overshoot.\n * • resistance — the iOS rubber-band curve `rubberOut` and its exact\n * inverse `rubberIn`: dragging `d` px past an edge displays an\n * asymptotically-bounded stretch, and re-entering a gesture over a\n * stretched camera (the mid-bounce catch) reconstructs the\n * finger-integrated position the curve came from.\n * Plus the tween interpolators: `easeOutCubic` for coordinate tweens and\n * `zoomLerp` — GEOMETRIC zoom interpolation (zoom is multiplicative; equal\n * ratios per unit time, not equal deltas — the linear lerp's visible \"dip\").\n */\n\n// ── constants ─────────────────────────────────────────────────────────────────\n/** Resistance coefficient of the rubber-band curve (the iOS feel). */\nexport const RUBBER = 0.55;\n/** Glide decay per ms — UIScrollView's normal deceleration rate. */\nexport const FLING_DECAY = 0.998;\n/** px/ms — below ~20 px/s the eye reads \"stopped\". */\nexport const FLING_STOP = 0.02;\n/** rad/ms — critically damped, ~400ms to settle. */\nexport const SPRING_OMEGA = 0.015;\n\n// ── resistance (rubber-band) ──────────────────────────────────────────────────\n/**\n * Overshoot distance → displayed stretch: asymptotic to the viewport\n * dimension `dim`, so the content never leaves the screen however far the\n * finger travels. `rubberOut(0) === 0` — inside the bounds the curve is\n * invisible.\n */\nexport const rubberOut = (d: number, dim: number): number => (1 - 1 / ((d * RUBBER) / dim + 1)) * dim;\n/** Exact inverse of `rubberOut` (defined for `out < dim`). */\nexport const rubberIn = (out: number, dim: number): number => (out * dim) / (RUBBER * (dim - out));\n\n// ── ballistic (one axis, one frame) ───────────────────────────────────────────\n/** The state one integration step hands back; `done` means \"at rest\". */\nexport interface MotionStep {\n p: number;\n v: number;\n done: boolean;\n}\n/**\n * One frame of free glide: exponential velocity decay, position advanced by\n * the midpoint integral of the decay over `dt` (exact for the exponential —\n * frame-rate independent).\n */\nexport const glideStep = (p: number, v: number, dt: number): MotionStep => {\n const k = Math.pow(FLING_DECAY, dt);\n const np = p + v * ((dt * (1 + k)) / 2);\n const nv = v * k;\n return Math.abs(nv) < FLING_STOP ? { p: np, v: 0, done: true } : { p: np, v: nv, done: false };\n};\n/**\n * One frame of critically-damped spring toward `edge` — semi-implicit Euler\n * on x'' = −ω²(x−e) − 2ωx'. Lands exactly ON the edge (position snapped,\n * velocity zeroed) once inside half a pixel at negligible speed.\n */\nexport const springStep = (p: number, v: number, edge: number, dt: number): MotionStep => {\n const nv = v + (-SPRING_OMEGA * SPRING_OMEGA * (p - edge) - 2 * SPRING_OMEGA * v) * dt;\n const np = p + nv * dt;\n if (Math.abs(np - edge) < 0.5 && Math.abs(nv) < 0.01) return { p: edge, v: 0, done: true };\n return { p: np, v: nv, done: false };\n};\n\n// ── tween interpolators ───────────────────────────────────────────────────────\nexport const easeOutCubic = (t: number): number => 1 - Math.pow(1 - t, 3);\n/**\n * Geometric zoom interpolation: `z0·(z1/z0)^k`. Zoom is multiplicative, so a\n * constant-RATE zoom moves equal ratios per unit time; interpolating the\n * level linearly instead makes the start rush and the end crawl (and, under\n * a focal anchor, swings the anchored point along a curved path).\n */\nexport const zoomLerp = (z0: number, z1: number, k: number): number => z0 * Math.pow(z1 / z0, k);\n","import type { Size } from '@embedpdf/core-stage';\nimport type { BoxQuery, ResponsiveRule, StageBox, StageSettings } from './types';\n\n/**\n * Container queries for the settings bag — the pure half. The capability owns\n * the driver (when to re-resolve, how to react); the LAWS live here: what a\n * box is, when a query matches, and what the effective settings are. No state,\n * no dispatch — property-testable in isolation (the `wheel.ts` convention).\n *\n * The query vocabulary is deliberately just the box: a headless, DOM-free\n * stage knows exactly one environmental fact — the viewport it was told.\n * Anything else (pointer coarseness, platform, app state) is either per-event\n * (`PointerSample.pointerType`) or the app's own business via `update()`.\n */\n\n/** The reported viewport as a queryable box. A square box is 'portrait' (CSS). */\nexport const boxOf = (vp: Size): StageBox => ({\n width: vp.width,\n height: vp.height,\n orientation: vp.height >= vp.width ? 'portrait' : 'landscape',\n});\n\n/** All bounds inclusive, all fields optional, conditions AND-ed (CSS ranges). */\nexport const matchesQuery = (q: BoxQuery, box: StageBox): boolean =>\n (q.minWidth === undefined || box.width >= q.minWidth) &&\n (q.maxWidth === undefined || box.width <= q.maxWidth) &&\n (q.minHeight === undefined || box.height >= q.minHeight) &&\n (q.maxHeight === undefined || box.height <= q.maxHeight) &&\n (q.orientation === undefined || box.orientation === q.orientation);\n\nconst ruleMatches = (rule: ResponsiveRule, box: StageBox): boolean =>\n typeof rule.when === 'function' ? !!rule.when(box) : matchesQuery(rule.when, box);\n\n/** Merge a patch over settings, skipping undefined values (Partial semantics). */\nexport const mergeSettings = (into: StageSettings, patch: Partial<StageSettings>): StageSettings => {\n const out = { ...into };\n let key: keyof StageSettings;\n for (key in patch) {\n const value = patch[key];\n if (value !== undefined) Object.assign(out, { [key]: value });\n }\n return out;\n};\n\n/**\n * Setting-value equality: primitives by identity, the flat objects the\n * settings vocabulary uses (`{ px }`, `{ x, y }`, page frames, zoom specs) by\n * one level of own-key comparison. Settings values are never nested deeper.\n */\nexport const eqSetting = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const ka = Object.keys(a);\n const kb = Object.keys(b);\n return (\n ka.length === kb.length &&\n ka.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k])\n );\n};\n\nexport interface ResolvedResponsive {\n /** base ⊕ every matching rule's patch, in source order (later wins per key). */\n effective: StageSettings;\n /** Names of the matching rules, in source order. */\n active: string[];\n}\n\nexport const resolveResponsive = (\n base: StageSettings,\n rules: readonly ResponsiveRule[],\n box: StageBox,\n): ResolvedResponsive => {\n let effective = base;\n const active: string[] = [];\n for (const rule of rules) {\n if (!ruleMatches(rule, box)) continue;\n if (rule.name !== undefined) active.push(rule.name);\n if (rule.settings) effective = mergeSettings(effective, rule.settings);\n }\n return { effective, active };\n};\n","import { ZoomMode } from '@embedpdf/core-stage';\nimport type { ResponsiveRule, StageSettings } from './types';\n\n/**\n * Out-of-the-box defaults — a sensible document-reading feel. They are JUST\n * defaults: every field is overridable in `stagePlugin(config)` and at runtime via\n * the setters / `update()`. The plugin ships NO named presets (\"document\",\n * \"canvas\", …) — a preset is simply an object the app keeps and passes to\n * `update()`, so that taxonomy stays a customer concern.\n */\nexport const DEFAULT_SETTINGS: StageSettings = {\n flow: 'continuous',\n layout: 'vertical',\n spread: 'none',\n sizing: 'intrinsic',\n columns: 'square',\n bounded: true,\n padding: 24,\n gap: 16,\n pageFrame: { top: 0, right: 0, bottom: 0, left: 0 },\n direction: 'ltr',\n fitAlign: { x: 'center', y: 'center' },\n // The reading defaults: navigation lands top/reading-start at every zoom\n // (v2 parity), button-zoom inflates around the middle, resizes pin the top\n // (the browser scroll model). Construction/presentation apps set all four\n // to center/center.\n arrivalAlign: { x: 'start', y: 'start' },\n zoomAlign: { x: 'center', y: 'center' },\n anchorAlign: { x: 'start', y: 'start' },\n viewRotation: 0,\n zoom: { mode: ZoomMode.Automatic },\n scrollBehavior: 'smooth',\n // Web: 1 PDF point = 96/72 CSS px, so 100% is physically accurate. A native\n // adapter overrides this at registration with its own logical-unit factor.\n viewUnitsPerPoint: 96 / 72,\n};\n\n/**\n * Out-of-the-box responsive rules — the platform feel with zero configuration:\n * a compact container gets the thin gutter phones use (space, not device — an\n * embedded 500px pane on a desktop is compact too). Override the whole list\n * with `stagePlugin({ responsive: [...] })`; `responsive: []` opts out. The\n * 'compact' name is queryable app-side via `stage.matches('compact')`.\n */\nexport const DEFAULT_RESPONSIVE: readonly ResponsiveRule[] = [\n { name: 'compact', when: { maxWidth: 600 }, settings: { padding: 4 } },\n];\n\n/**\n * How a CHANGE to each setting affects the view — THE single source of truth.\n * One row per setting, completeness enforced by the compiler (`Record<keyof\n * StageSettings, …>`: adding a setting without classifying it is a type error).\n * Everything else derives from this table: the scene-cache invalidation and key,\n * `update()`'s reaction, the settings snapshot/patch picks, and the React\n * selector equality.\n *\n * 'reflow' — crosses the flow boundary: camera coordinates are meaningless\n * there, so re-place canonically onto the cursor's page.\n * 'scene' — a LAYOUT INPUT: invalidates the scene, then re-applies the anchor.\n * 'refit' — re-resolves zoom against the (possibly re-keyed) scene: re-applies\n * the anchor without an explicit invalidation.\n * 'reclamp' — pure clamp policy: re-clamp the current camera in place.\n * 'none' — guides future verbs only (arrival/zoom/anchor alignment,\n * scroll behavior).\n *\n * The classes also carry each change's INVARIANT — what stays fixed while the\n * view reacts: 'refit' (a zoom-intent change) holds the zoomAlign focal point;\n * 'scene' reframes (and viewport resizes) hold the anchorAlign point.\n */\nexport type SettingEffect = 'reflow' | 'scene' | 'refit' | 'reclamp' | 'none';\nexport const SETTINGS_EFFECT: Record<keyof StageSettings, SettingEffect> = {\n flow: 'reflow',\n layout: 'scene',\n spread: 'scene',\n sizing: 'scene',\n columns: 'scene',\n bounded: 'reclamp',\n padding: 'reclamp',\n gap: 'scene',\n pageFrame: 'scene',\n direction: 'scene',\n fitAlign: 'reclamp',\n arrivalAlign: 'none',\n zoomAlign: 'none',\n anchorAlign: 'none',\n viewRotation: 'scene', // a layout input: every page's footprint swaps w↔h\n zoom: 'refit',\n scrollBehavior: 'none',\n viewUnitsPerPoint: 'scene', // a layout input: changing it resizes every page\n};\nexport const SETTING_KEYS = Object.keys(SETTINGS_EFFECT) as Array<keyof StageSettings>;\n\n// Settings values are primitives or one-level objects (align pairs, pageFrame,\n// gap { px }, zoom intents) — one level of structural equality covers them all.\nconst valueEq = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const ka = Object.keys(a);\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k])\n );\n};\n/** Field-by-field settings equality, derived from the registry — a new setting is\n * covered automatically. (The React `useStageSettings` selector equality.) */\nexport const settingsEqual = (a: StageSettings, b: StageSettings): boolean =>\n SETTING_KEYS.every((k) => valueEq(a[k], b[k]));\n","import * as S from '@embedpdf/core-stage';\nimport {\n addRotations,\n applyRect,\n applyPoint,\n displaySize,\n intersectRects,\n pageTransform,\n rotateScaleMatrix,\n snapToDevice,\n} from '@embedpdf/core-geometry';\nimport type { Rect } from '@embedpdf/core-geometry';\nimport type { PluginContext } from '@embedpdf/core';\nimport { FLING_STOP, easeOutCubic, glideStep, rubberIn, rubberOut, springStep, zoomLerp } from './motion';\nimport { boxOf, eqSetting, mergeSettings, resolveResponsive } from './responsive';\nimport { DEFAULT_RESPONSIVE, SETTINGS_EFFECT, SETTING_KEYS } from './settings';\nimport type { SettingEffect } from './settings';\nimport type {\n GoToOptions,\n ResponsiveRule,\n RevealAnchorValue,\n RevealOptions,\n Scheduler,\n StageAction,\n StageCapability,\n StageConfig,\n StageSettings,\n StageState,\n StageViewState,\n Viewpoint,\n VisiblePage,\n} from './types';\n\n/**\n * The Stage capability — selectors (pure reads) + intents (the only writers).\n *\n * Layering, stated honestly:\n * • stage-core — PURE spatial math (no DOM, no time). Ports to Rust later.\n * • this capability — the IMPURE platform shell. It dispatches, caches, and owns\n * the camera tween. It is NOT pure; its one host dependency (frame timing)\n * enters through the Scheduler seam — injectable, defaulting to the host's\n * own frame clock when one exists (see the seam below).\n *\n * The model: EVERY camera move is defined by what it holds fixed.\n * • gestures (pan/pinch/wheel) — the content under the pointer. Physics, no setting.\n * • focal zoom (zoomIn/zoomTo/fit-mode switches) — the zoomAlign viewport point.\n * • reframes (resize/rotate/re-layout) — the page-point at the anchorAlign\n * viewport point: captured there before the change, restored there after.\n * • arrivals (goToPage/next/prev/reset) — nothing prior: a fresh landing at\n * arrivalAlign, THE SAME at every zoom (landing is policy, never a zoom side effect).\n * • explicit (reveal/destination/viewpoint) — whatever the call specifies.\n * fitAlign stands apart: the clamp's standing rest constraint wherever an axis has no\n * freedom against the TRUE bounds — it shapes every move above, and is why a fitting\n * axis settles identically whatever arrivalAlign says.\n *\n * \"Does it fit the viewport?\" still decides SIZE and SUBJECT — never alignment:\n * • step size: next/prev step by ITEM (spread) when the item fits, by PAGE when\n * zoomed in past it.\n * • subject: you arrive AT the unit that fits (item, page — or the whole scene\n * under fit-all).\n *\n * The `cursor` is THE current page in both flows: navigation sets it; continuous\n * scrolling syncs it from the camera; paged panning never moves it.\n */\nexport function createStageCapability(\n ctx: PluginContext<StageState, StageAction>,\n config: StageConfig = {},\n): StageCapability {\n // ── host timing seam ─────────────────────────────────────────────────────────\n // Frame timing enters through the Scheduler seam. By DEFAULT the seam binds\n // to the host's own frame clock (globalThis.requestAnimationFrame) when one\n // exists — inject to override (tests do); a frameless host degrades to\n // instant navigation, no animation.\n const host = globalThis as {\n requestAnimationFrame?: (cb: (t: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n };\n const canAnimate = !!config.scheduler || typeof host.requestAnimationFrame === 'function';\n const scheduler: Scheduler =\n config.scheduler ??\n (typeof host.requestAnimationFrame === 'function'\n ? { raf: (cb) => host.requestAnimationFrame!(cb), caf: (h) => host.cancelAnimationFrame!(h) }\n : { raf: () => 0, caf: () => {} }); // no host frames → navigation jumps instantly\n\n const cam = () => ctx.getState().camera;\n const vp = () => ctx.getState().vp;\n const dpr = () => ctx.getState().dpr;\n const pad = () => ctx.getState().padding;\n const paged = () => ctx.getState().flow === 'paged';\n const isFitAll = () => {\n const z = ctx.getState().zoom;\n return 'mode' in z && z.mode === S.ZoomMode.FitAll;\n };\n\n // ── the document's item model (spread grouping) — independent of the rendered\n // scene, so navigation can reason about ALL items while a paged SCENE holds\n // only one. The cursor is a page; itemIndexOfPage maps it (survives regrouping).\n let groupingCache: { key: string; grouping: number[][]; firstPages: number[] } | null = null;\n const grouping = (): { grouping: number[][]; firstPages: number[] } => {\n const doc = ctx.document();\n const st = ctx.getState();\n const key = `${doc ? doc.pageCount : 0}|${st.spread}`;\n if (groupingCache && groupingCache.key === key) return groupingCache;\n const g = S.groupPages(doc ? doc.pageCount : 0, st.spread);\n groupingCache = { key, grouping: g, firstPages: g.map((item) => item[0]) };\n return groupingCache;\n };\n const itemCountFull = (): number => grouping().grouping.length;\n const itemIndexOfPage = (pageIndex: number): number => {\n const fp = grouping().firstPages;\n let lo = 0;\n let hi = fp.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (fp[mid] <= pageIndex) lo = mid + 1;\n else hi = mid;\n }\n return Math.max(0, lo - 1);\n };\n\n // The effective zoom for converting SCREEN px settings into world units — a\n // fixed zoom intent gives an exact, stable value (the thumbnail case); other\n // intents fall back to the camera's current zoom (`stabilized` converges it).\n // Screen-px settings (wrapped lineWidth, pageFrame) are the ONLY way a scene\n // depends on the viewport/zoom.\n const effectiveZoom = (): number => {\n const z = ctx.getState().zoom;\n return 'level' in z ? z.level : Math.max(ctx.getState().camera.zoom, 0.0001);\n };\n\n // Wrapped grid: the line width (world units) the columns must fit.\n const wrapLineWidth = (): number => Math.max(1, (vp().width - 2 * pad()) / effectiveZoom());\n\n // pageFrame (screen px) → world units at the effective zoom.\n const worldPageFrame = (): S.PageFrame => {\n const m = ctx.getState().pageFrame;\n if (!m.top && !m.right && !m.bottom && !m.left) return m;\n const ez = effectiveZoom();\n return { top: m.top / ez, right: m.right / ez, bottom: m.bottom / ez, left: m.left / ez };\n };\n const frameKey = (): string => {\n const m = ctx.getState().pageFrame;\n if (!m.top && !m.right && !m.bottom && !m.left) return '-';\n const w = worldPageFrame();\n return `${Math.round(w.top)},${Math.round(w.right)},${Math.round(w.bottom)},${Math.round(w.left)}`;\n };\n\n // gap → world units. A plain number IS world (the scene stays zoom-invariant —\n // the rigid-canvas default); { px } converts at the effective zoom, exactly\n // like pageFrame (UI-stable spacing for browser-style lenses).\n const worldGap = (): number => {\n const g = ctx.getState().gap;\n return typeof g === 'number' ? g : g.px ? g.px / effectiveZoom() : 0;\n };\n const gapKey = (): string => {\n const g = ctx.getState().gap;\n return typeof g === 'number' ? String(g) : `px:${Math.round(worldGap())}`;\n };\n\n const layoutFor = (groups: number[][]): S.Scene => {\n const st = ctx.getState();\n // Engine PageLayout (PDF document geometry) structurally satisfies stage-core's\n // viewer-local PageGeom (`size` + `rotation`): intrinsic page size needs no\n // transform, so it flows straight into the layout with no conversion.\n //\n // THE view-rotation injection point: each page's display rotation is its\n // /Rotate + this lens's viewRotation, composed HERE — the one spot where\n // the \"TOTAL = document /Rotate + view rotation\" of geometry's PageRotation\n // doc is resolved. Everything downstream (displaySize w↔h swap, the page\n // transform + CSS rotate, hit-testing, fit zoom, content overlays) reads the\n // composed `PageBox.rotation` and needs no other change. `size` stays the\n // page's own un-rotated points, so content space is view-rotation-invariant.\n const raw = ctx.document()?.pages ?? [];\n const vr = st.viewRotation;\n const pages =\n vr === 0 ? raw : raw.map((p) => ({ ...p, rotation: addRotations(p.rotation, vr) }));\n const pageFrame = worldPageFrame();\n const gap = worldGap();\n const vupp = st.viewUnitsPerPoint;\n if (st.layout === 'grid') {\n return S.gridLayout(pages, groups, {\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n columns: typeof st.columns === 'number' ? st.columns : undefined,\n lineWidth: st.columns === 'auto' ? wrapLineWidth() : undefined,\n });\n }\n return st.layout === 'horizontal'\n ? S.linearLayout(pages, groups, {\n axis: 'x',\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n })\n : S.linearLayout(pages, groups, {\n axis: 'y',\n gap,\n sizing: st.sizing,\n direction: st.direction,\n pageFrame,\n viewUnitsPerPoint: vupp,\n });\n };\n\n // Scene-cache key fragment for the column policy ('auto' quantizes the line width\n // so sub-pixel resizes don't churn the cache).\n const columnsKey = (): string => {\n const st = ctx.getState();\n if (st.layout !== 'grid') return '-';\n return st.columns === 'auto' ? `auto:${Math.round(wrapLineWidth())}` : String(st.columns);\n };\n\n // The scene's settings signature — DERIVED from the registry: every 'scene'\n // setting contributes automatically, so a new layout-affecting setting only\n // needs its SETTINGS_EFFECT row — it can't be forgotten here, which is what\n // makes stale-scene bugs unrepresentable. The default keys by VALUE (objects\n // via JSON); the custom fns aren't for correctness, they QUANTIZE px-derived\n // values so sub-pixel zoom/resize churn doesn't rebuild the scene.\n const SCENE_KEY_FNS: Partial<Record<keyof StageSettings, () => string>> = {\n columns: columnsKey,\n gap: gapKey,\n pageFrame: frameKey,\n };\n const SCENE_KEYS = SETTING_KEYS.filter((k) => SETTINGS_EFFECT[k] === 'scene');\n const settingsKey = (): string =>\n SCENE_KEYS.map((k) => {\n const fn = SCENE_KEY_FNS[k];\n if (fn) return fn();\n const v = ctx.getState()[k];\n return typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v);\n }).join('|');\n\n // Scene cache. Continuous = the whole document. Paged = a ONE-ITEM SLICE at the\n // origin containing only the cursor's item — so isolation is STRUCTURAL (no other\n // page exists to leak), unbounded pan is free, and coordinates stay local.\n let sceneCache: { key: string; scene: S.Scene } | null = null;\n // Registry signature: page count + the kernel's monotonic `revision`. The\n // revision bumps on every page-mutation event (rotate/move/delete), so a\n // change that leaves pageCount the same — a rotation — still re-keys the\n // scene. Without it a rotated page would render in its stale box.\n const docKey = (): string => {\n const doc = ctx.document();\n return doc ? `${doc.pageCount}.${doc.revision}` : '0.0';\n };\n const buildScene = (): S.Scene => {\n const st = ctx.getState();\n const { grouping: g } = grouping();\n if (st.flow === 'paged') {\n const idx = g.length ? Math.min(itemIndexOfPage(st.cursor), g.length - 1) : 0;\n const key = `paged|${docKey()}|${settingsKey()}|${idx}`;\n if (sceneCache && sceneCache.key === key) return sceneCache.scene;\n const scene = layoutFor(g.length ? [g[idx]] : []);\n sceneCache = { key, scene };\n return scene;\n }\n const key = `cont|${docKey()}|${settingsKey()}`;\n if (sceneCache && sceneCache.key === key) return sceneCache.scene;\n const scene = layoutFor(g);\n sceneCache = { key, scene };\n return scene;\n };\n\n // ── geometry helpers ──────────────────────────────────────────────────────────\n const sceneRect = (): S.Rect => {\n const { width, height } = buildScene().size;\n return { x: 0, y: 0, width, height };\n };\n const itemRect = (it: S.SceneItem): S.Rect => ({\n x: it.x,\n y: it.y,\n width: it.width,\n height: it.height,\n });\n const pageRectOf = (it: S.SceneItem, pageIndex: number): S.Rect => {\n const box = it.pages.find((p) => p.pageIndex === pageIndex) ?? it.pages[0];\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n };\n /** The item shown for the cursor: the slice's only item (paged) / the full-scene item. */\n const cursorItem = (): S.SceneItem => {\n const sc = buildScene();\n return paged() ? sc.items[0] : sc.items[itemIndexOfPage(ctx.getState().cursor)];\n };\n\n /**\n * CONTENT-space rect on a page → WORLD rect: the same quarter-turn matrix\n * `pageRectToScreen` uses, minus the camera — so a positioned reveal and\n * the rendered overlay can never disagree about where a rect is.\n */\n const worldRectForContent = (it: S.SceneItem, pageIndex: number, rect: Rect): S.Rect => {\n const box = it.pages.find((p) => p.pageIndex === pageIndex) ?? it.pages[0];\n const content = displaySize({ width: box.width, height: box.height }, box.rotation);\n const m = rotateScaleMatrix(box.contentScale, content.width, content.height, box.rotation);\n const wr = applyRect(m, rect);\n return { x: box.x + wr.x, y: box.y + wr.y, width: wr.width, height: wr.height };\n };\n\n /**\n * One axis of a positioned-reveal camera. `undefined` = 'nearest' (only\n * move if the target is outside the padded view) — unless the zoom just\n * changed, where \"don't move\" is meaningless and the spec's slack-axis\n * rule (center) applies. 'keep' never moves the axis (PDF /XYZ null).\n */\n const revealAxis = (\n a: RevealAnchorValue | undefined,\n camPos: number,\n rectPos: number,\n rectExtent: number,\n vpExtent: number,\n zoom: number,\n zoomChanged: boolean,\n ): number => {\n if (a === 'keep') return camPos;\n const p = pad();\n if (a === undefined) {\n if (!zoomChanged) {\n const lo = camPos + p / zoom;\n const hi = camPos + (vpExtent - p) / zoom;\n if (rectPos >= lo && rectPos + rectExtent <= hi) return camPos; // already visible\n if (rectExtent > hi - lo || rectPos < lo) return rectPos - p / zoom;\n return rectPos + rectExtent - (vpExtent - p) / zoom;\n }\n a = 'center';\n }\n if (a === 'start') return rectPos - p / zoom;\n if (a === 'end') return rectPos + rectExtent - (vpExtent - p) / zoom;\n const f = a === 'center' ? 0.5 : Math.min(1, Math.max(0, a));\n return rectPos + rectExtent / 2 - (vpExtent * f) / zoom;\n };\n\n // THE predicate. \"Does this rect fit the padded viewport at this zoom?\" decides\n // the navigation step size and the arrival subject — never alignment.\n const fits = (rect: S.Rect, zoom: number): boolean => {\n const v = vp();\n const p = pad();\n const eps = 0.5;\n return (\n rect.width * zoom <= v.width - 2 * p + eps && rect.height * zoom <= v.height - 2 * p + eps\n );\n };\n // An alignment policy → a concrete viewport point (a fraction per axis:\n // start=0, center=½, end=1; named x stops are LOGICAL under RTL). Both\n // zoomAlign (the focal point of pointer-less zooms) and anchorAlign (the\n // reframe reference point) resolve through this. The fraction interpolates\n // the PADDED range: 'start' is the first visible content line (just inside\n // the gutter), not the absolute corner — an arrival puts the page edge\n // exactly there, so the reference pins to the page, never to the gap above.\n const alignFraction = (a: S.AlignValue): number =>\n a === 'start' ? 0 : a === 'center' ? 0.5 : a === 'end' ? 1 : Math.min(1, Math.max(0, a));\n const alignPoint = (al: S.AlignmentValue, v: S.Size): S.Point => {\n const rtl = ctx.getState().direction === 'rtl';\n const ax = rtl && al.x === 'start' ? 'end' : rtl && al.x === 'end' ? 'start' : al.x;\n const p = pad();\n return {\n x: p + (v.width - 2 * p) * alignFraction(ax),\n y: p + (v.height - 2 * p) * alignFraction(al.y),\n };\n };\n const anchorPoint = (): S.Point => alignPoint(ctx.getState().anchorAlign, vp());\n\n /** Fit-box for resolving the zoom intent: whole scene (fit-all), the current item\n * (paged — per-page fit), or the document max (continuous — doc-stable zoom). */\n const fitBox = (item: S.SceneItem): S.Size => {\n if (isFitAll()) return buildScene().size;\n return paged() ? { width: item.width, height: item.height } : buildScene().maxItemSize;\n };\n /** Clamp rect for a camera write targeting this item (scene-wide in continuous). */\n const boundsFor = (it: S.SceneItem): S.Rect => (paged() ? itemRect(it) : sceneRect());\n /** Clamp rect for a \"stay\" write (pan/zoom): the slice item (paged) / the scene. */\n const stayBounds = (): S.Rect => (paged() ? itemRect(buildScene().items[0]) : sceneRect());\n\n const constraint = (): S.CameraConstraint => ({\n bounded: ctx.getState().bounded,\n padding: pad(),\n fitAlign: ctx.getState().fitAlign,\n direction: ctx.getState().direction,\n });\n\n // Initial-view placement flag — declared up here so the rest detector can\n // read it; the placement logic further down owns it.\n let hasPlaced = false;\n\n // ── gesture transaction (touch pan/pinch) ───────────────────────────────────\n // While open: zoomAround defers its intent PATCH, and the rest countdown is\n // held — a hesitation inside a pinch is not \"at rest\". Depth-counted so\n // nested brackets compose. An ELASTIC gesture may additionally hold the\n // camera past the clamp (rubber-band); `gestureRaw` is its unclamped,\n // finger-integrated camera — the resistance curve maps it to what renders.\n let gestureDepth = 0;\n let gestureZoomed = false;\n let gestureElastic = false;\n let gestureRaw: S.Camera | null = null;\n\n // ── camera-rest detector ────────────────────────────────────────────────────\n // A continuous zoom and a device-snapped origin cannot coexist without the\n // anchor point jittering (each step rounds differently, ±0.5 device px per\n // axis). So origin snapping is gated on REST: fractional placement while\n // the zoom moves, one snap when it settles — when crispness matters. The\n // window is counted in scheduler frames (the same timing seam the tween\n // uses), so tests stay deterministic.\n const REST_MS = 150;\n let restRaf = 0;\n const armRest = () => {\n // Initial placement snaps immediately (first paint is crisp), and an\n // environment without real frames keeps snapping always-on — rest-gating\n // is a live-gesture refinement, not a contract.\n if (!canAnimate || !hasPlaced) return;\n if (ctx.getState().cameraResting) ctx.dispatch({ type: 'CAMERA_REST', resting: false });\n if (restRaf) scheduler.caf(restRaf);\n let t0 = 0;\n const tick = (ts: number) => {\n restRaf = 0;\n if (!t0) t0 = ts;\n if (ts - t0 >= REST_MS) {\n ctx.dispatch({ type: 'CAMERA_REST', resting: true });\n return;\n }\n restRaf = scheduler.raf(tick);\n };\n restRaf = scheduler.raf(tick);\n };\n\n // The ONE low-level camera write: clamp to `bounds`, dispatch. MECHANISM only —\n // it never touches the cursor (see syncCursorFromCamera for the policy).\n const setCam = (next: S.Camera, bounds: S.Rect = stayBounds()) => {\n const clamped = S.clampCamera(next, bounds, vp(), constraint());\n if (clamped.zoom !== cam().zoom) {\n if (gestureDepth > 0) {\n // Mid-gesture: un-rest immediately (fractional placement) but hold the\n // 150 ms countdown — rest is declared at endGesture, not at a pinch\n // hesitation.\n gestureZoomed = true;\n if (ctx.getState().cameraResting) ctx.dispatch({ type: 'CAMERA_REST', resting: false });\n if (restRaf) {\n scheduler.caf(restRaf);\n restRaf = 0;\n }\n } else {\n armRest();\n }\n }\n ctx.dispatch({ type: 'CAMERA', camera: clamped });\n };\n\n // ── rubber-band (elastic overscroll) ────────────────────────────────────────\n // The curve pair lives in motion.ts; these are its STATE adapters. One rule\n // governs where the rubber exists at all: it softens only the edges of a\n // scroll RANGE. An axis whose content FITS the viewport has no travel — the\n // clamp holds it at its fitAlign rest, and it stays rigid however hard the\n // finger tugs (the UIScrollView default: bouncing exists only where content\n // exceeds bounds).\n const axisTravels = (origin: number, content: number, view: number, zoom: number): boolean =>\n !S.travelRange(origin, content, view, zoom, constraint().padding).fits;\n /** Unclamped finger-integrated camera → the DISPLAYED camera: clamp, then\n * re-apply the overshoot through the resistance curve, per travelling axis.\n * Inside the bounds this is exactly the clamp (rubber of zero is zero);\n * on a fitting axis it is exactly the clamp ALWAYS. */\n const rubberize = (raw: S.Camera): S.Camera => {\n const clamped = S.clampCamera(raw, stayBounds(), vp(), constraint());\n const v = vp();\n const b = stayBounds();\n const axis = (rawA: number, clampedA: number, dim: number, travels: boolean): number => {\n if (!travels) return clampedA;\n const dWorld = rawA - clampedA;\n if (dWorld === 0) return clampedA;\n const out = rubberOut(Math.abs(dWorld) * raw.zoom, Math.max(1, dim));\n return clampedA + (Math.sign(dWorld) * out) / raw.zoom;\n };\n return {\n zoom: raw.zoom,\n x: axis(raw.x, clamped.x, v.width, axisTravels(b.x, b.width, v.width, raw.zoom)),\n y: axis(raw.y, clamped.y, v.height, axisTravels(b.y, b.height, v.height, raw.zoom)),\n };\n };\n /** Displayed camera → the raw position `rubberize` would have produced it\n * from. Overshoot is capped just under the asymptote so the inverse stays\n * finite whatever state a catch finds the camera in. */\n const unrubberize = (displayed: S.Camera): S.Camera => {\n const clamped = S.clampCamera(displayed, stayBounds(), vp(), constraint());\n const v = vp();\n const b = stayBounds();\n const axis = (dispA: number, clampedA: number, dim: number, travels: boolean): number => {\n if (!travels) return clampedA;\n const dWorld = dispA - clampedA;\n if (dWorld === 0) return clampedA;\n const d = Math.max(1, dim);\n const out = Math.min(Math.abs(dWorld) * displayed.zoom, d - 1);\n return clampedA + (Math.sign(dWorld) * rubberIn(out, d)) / displayed.zoom;\n };\n return {\n zoom: displayed.zoom,\n x: axis(displayed.x, clamped.x, v.width, axisTravels(b.x, b.width, v.width, displayed.zoom)),\n y: axis(displayed.y, clamped.y, v.height, axisTravels(b.y, b.height, v.height, displayed.zoom)),\n };\n };\n // The elastic write path: NO clamp — used only by the elastic pan and the\n // edge spring, whose math guarantees every trajectory terminates clamped.\n const setCamRaw = (c: S.Camera) => ctx.dispatch({ type: 'CAMERA', camera: c });\n\n /**\n * Cursor reconciliation, one direction per interaction:\n * navigation → cursor is INTENT, set explicitly; the camera honors it as far\n * as the clamp allows — and a clamped camera never revokes it.\n * manipulation → (pan / drag / pinch) the camera moves freely; the cursor is\n * DERIVED from it. Only those verbs call this. Paged never syncs.\n */\n const syncCursorFromCamera = () => {\n if (paged()) return;\n const sc = buildScene();\n if (!sc.itemCount) return;\n const page = S.anchorFromCamera(cam(), sc, vp()).pageIndex;\n if (page !== ctx.getState().cursor) ctx.dispatch({ type: 'CURSOR', cursor: page });\n };\n\n // ── camera tween (impure shell concern; uses the injected Scheduler) ─────────\n let raf = 0;\n const cancelAnim = () => {\n if (raf) {\n scheduler.caf(raf);\n raf = 0;\n }\n };\n const lerp = (a: number, b: number, k: number) => a + (b - a) * k;\n // The tween clamps to an EXPLICIT bounds rect every frame (never a re-derived\n // current item), so animating toward a different item isn't clamped back.\n // `then` runs on natural completion only — a cancelled tween belongs to the\n // verb that cancelled it.\n const animateTo = (target: S.Camera, bounds: S.Rect, ms = 240, then?: () => void) => {\n if (!canAnimate) {\n setCam(target, bounds);\n then?.();\n return;\n }\n cancelAnim();\n const from = cam();\n let started = false;\n let t0 = 0;\n const tick = (now: number) => {\n if (!started) {\n started = true;\n t0 = now; // anchor to the first real timestamp (works even when now === 0)\n }\n const k = easeOutCubic(Math.min(1, (now - t0) / ms));\n setCam(\n {\n x: lerp(from.x, target.x, k),\n y: lerp(from.y, target.y, k),\n zoom: lerp(from.zoom, target.zoom, k),\n },\n bounds,\n );\n if (k < 1) {\n raf = scheduler.raf(tick);\n } else {\n raf = 0;\n then?.();\n }\n };\n raf = scheduler.raf(tick);\n };\n\n /**\n * Zoom-anchored tween — animates THE INVARIANT, not the coordinates. A\n * camera move is defined by what it holds fixed; for an anchored zoom that\n * is \"the focal page-point stays at `pt` while scale changes\". Lerping\n * x/y/zoom independently (the plain tween) breaks that mid-flight: holding\n * a screen point requires `x(t) = focal.x − pt.x / zoom(t)` — hyperbolic in\n * the zoom — so linear coordinates swing the tapped point along a curved\n * path (the visible \"dip\"). Here the zoom interpolates GEOMETRICALLY\n * (constant rate — zoom is multiplicative) and each frame's camera derives\n * from the anchor at that zoom, then clamps: the focal point is stationary\n * by construction, and a zoom-out rails into its fit progressively as the\n * shrinking travel range lets the clamp take over.\n */\n const animateZoomAnchored = (\n focal: S.Anchor,\n pt: S.Point,\n targetZoom: number,\n ms = 240,\n then?: () => void,\n ) => {\n if (!canAnimate) {\n const sc = buildScene();\n if (sc.itemCount) setCam(S.cameraForAnchorAtScreen(focal, sc, pt, targetZoom));\n then?.();\n return;\n }\n cancelAnim();\n const z0 = cam().zoom;\n let started = false;\n let t0 = 0;\n const tick = (now: number) => {\n if (!started) {\n started = true;\n t0 = now;\n }\n const k = easeOutCubic(Math.min(1, (now - t0) / ms));\n const z = zoomLerp(z0, targetZoom, k);\n const sc = buildScene();\n if (sc.itemCount) setCam(S.cameraForAnchorAtScreen(focal, sc, pt, z));\n if (k < 1) {\n raf = scheduler.raf(tick);\n } else {\n raf = 0;\n then?.();\n }\n };\n raf = scheduler.raf(tick);\n };\n\n // Momentum + edge physics — the driver over motion.ts's per-axis laws:\n // glide — the touch fling, decaying on UIScrollView's curve across free\n // travel;\n // spring — a critically-damped return to the clamp, entered when a glide\n // reaches a content edge (remaining velocity becomes the bounce)\n // or when the motion STARTS displaced (release while stretched).\n // Axes are independent (a diagonal fling can bounce off the bottom while\n // still gliding horizontally). Every trajectory terminates ON the clamp.\n // Shares the `raf` handle with the tween, so cancelAnim() — every verb's\n // first act — is also \"catch\": catching mid-bounce holds the stretch.\n const startCameraPhysics = (vxFinger: number, vyFinger: number) => {\n if (!canAnimate) {\n setCam(cam()); // no host frames: snap straight into bounds\n return;\n }\n cancelAnim();\n const zoom = cam().zoom;\n // Per-axis state in camera-space SCREEN px (the camera moves OPPOSITE the\n // finger; positions scale by zoom once, here, so the laws read in px).\n interface AxisState {\n p: number;\n v: number;\n springing: boolean;\n done: boolean;\n }\n const c0 = cam();\n const cl0 = S.clampCamera(c0, stayBounds(), vp(), constraint());\n const b0 = stayBounds();\n const v0 = vp();\n const mk = (camA: number, clampedA: number, fingerV: number): AxisState => ({\n p: camA * zoom,\n v: -fingerV / 1000,\n springing: camA !== clampedA,\n done: false,\n });\n // A fitting axis takes no ballistic motion: no travel to glide across and\n // no bounce (rigid, like the platform) — release velocity is discarded.\n // The spring stays armed for a DISPLACED fitting axis (repair to rest).\n const ax = mk(c0.x, cl0.x, axisTravels(b0.x, b0.width, v0.width, zoom) ? vxFinger : 0);\n const ay = mk(c0.y, cl0.y, axisTravels(b0.y, b0.height, v0.height, zoom) ? vyFinger : 0);\n if (!ax.springing && !ay.springing && Math.hypot(ax.v, ay.v) < FLING_STOP) return;\n let started = false;\n let last = 0;\n const tick = (now: number) => {\n raf = 0;\n // First frame assumes one 60Hz step (anchoring like the tween — works\n // even when the first timestamp is 0).\n const dt = started ? Math.min(64, Math.max(1, now - last)) : 16;\n started = true;\n last = now;\n // The clamp of the CURRENT position is each axis's home this frame: for\n // a stretched axis it is the edge; for a fitting axis, its rest point.\n const cl = S.clampCamera(\n { x: ax.p / zoom, y: ay.p / zoom, zoom },\n stayBounds(),\n vp(),\n constraint(),\n );\n const step = (a: AxisState, edgeWorld: number) => {\n if (a.done) return;\n const r = a.springing\n ? springStep(a.p, a.v, edgeWorld * zoom, dt)\n : glideStep(a.p, a.v, dt);\n a.p = r.p;\n a.v = r.v;\n a.done = r.done;\n };\n step(ax, cl.x);\n step(ay, cl.y);\n // A glide that left the travel range converts to a spring AT the edge —\n // the incoming velocity carries in, so the bounce grows out of physics\n // rather than a scripted overshoot.\n const ncl = S.clampCamera(\n { x: ax.p / zoom, y: ay.p / zoom, zoom },\n stayBounds(),\n vp(),\n constraint(),\n );\n const convert = (a: AxisState, clampedA: number) => {\n if (!a.springing && Math.abs(clampedA * zoom - a.p) > 1e-6) {\n a.p = clampedA * zoom;\n a.springing = true;\n a.done = false;\n }\n };\n convert(ax, ncl.x);\n convert(ay, ncl.y);\n setCamRaw({ x: ax.p / zoom, y: ay.p / zoom, zoom });\n syncCursorFromCamera();\n if (ax.done && ay.done) {\n setCam(cam()); // exact landing: clamped, snapped, settled\n return;\n }\n raf = scheduler.raf(tick);\n };\n raf = scheduler.raf(tick);\n };\n\n // ── anchor: the durable \"what am I looking at\". Capture before a structural\n // change, re-apply after — one mechanism for layout/spread/zoom/resize/restore.\n // WHERE in the viewport the anchor lives is the reframe's invariant policy:\n // anchorAlign for reframes (resize, scene changes), zoomAlign for pure\n // zoom-intent changes. Capture and restore always use the SAME point.\n const anchorAt = (at: S.Point): S.Anchor => {\n const sc = buildScene();\n return sc.itemCount\n ? S.anchorFromCamera(cam(), sc, vp(), at)\n : { pageIndex: ctx.getState().cursor, fx: 0.5, fy: 0 };\n };\n const currentAnchor = (): S.Anchor => anchorAt(anchorPoint());\n\n /**\n * Run a placement whose RESOLVED zoom may change the scene (wrapped mode: zoom is\n * a layout input). Re-run it until the scene it placed against is the scene that\n * results — every non-wrapped mode is stable after the first pass by construction,\n * and wrapped fit-modes converge on the second (the fit-box is wrap-independent).\n * Capped at 3 passes for the one genuinely circular case (fit-all + wrapped).\n */\n const stabilized = (place: () => void) => {\n for (let attempt = 0; attempt < 3; attempt++) {\n const scene = buildScene();\n place();\n if (buildScene() === scene) return;\n }\n };\n\n const applyAnchor = (anchor: S.Anchor, align?: S.AlignmentValue) => {\n stabilized(() => {\n const scene = buildScene();\n if (!scene.itemCount) return;\n const item = scene.items[scene.itemOfPage(anchor.pageIndex)];\n const zoom = S.resolveZoom(ctx.getState().zoom, fitBox(item), vp(), pad());\n // resolved against the CURRENT viewport — a resize restores the anchor to\n // the new viewport's policy point (start/start: the top stays pinned)\n const at = alignPoint(align ?? ctx.getState().anchorAlign, vp());\n setCam(S.cameraFromAnchor(anchor, scene, vp(), zoom, at), boundsFor(item));\n });\n };\n /**\n * Re-apply the view after a structural/zoom/viewport change. Normally anchor-\n * preserving (keep looking at the same spot, held at the invariant point).\n * Under fit-all the subject is the WHOLE scene, so \"keep my anchor\" is\n * meaningless — re-place instead (this is what centers the scene even when\n * unbounded).\n */\n const reapply = (anchor: S.Anchor, align?: S.AlignmentValue) => {\n if (isFitAll()) goToTarget(ctx.getState().cursor, { behavior: 'instant' });\n else applyAnchor(anchor, align);\n };\n\n // ── navigation: ONE arrival procedure for goToPage / next / prev / reset ─────\n // Navigation is CANONICAL: goToPage(N) always ends in the same camera state,\n // regardless of where you came from, what happens to be visible, or how zoomed\n // you are (no zoom-dependent landing — that's a discontinuity at the fit\n // threshold).\n // 1. move the cursor to the target page (paged: rebuilds the one-item slice),\n // 2. choose the SUBJECT by the fits-predicate (scene under fit-all; the item if\n // it fits; else the page),\n // 3. placeCamera(subject) lands it at arrivalAlign — the same rule at every\n // zoom — and setCam clamps against the TRUE bounds, which collapses any\n // axis with no freedom to the fitAlign rest point.\n // The legitimate \"nothing moves\" cases are STRUCTURAL, not conditional: under\n // fit-all the canonical placement is the centered scene, which doesn't change.\n const goToTarget = (pageIndex: number, opts?: GoToOptions) => {\n cancelAnim();\n const doc = ctx.document();\n if (!doc || doc.pageCount === 0) return;\n const target = Math.max(0, Math.min(pageIndex, doc.pageCount - 1));\n if (target !== ctx.getState().cursor) ctx.dispatch({ type: 'CURSOR', cursor: target });\n\n // Restore path (per-page view memory): exact viewpoint instead of fresh placement.\n if (opts?.viewpoint) {\n ctx.dispatch({ type: 'PATCH', patch: { zoom: opts.viewpoint.zoom } });\n applyAnchor(opts.viewpoint.anchor);\n return;\n }\n\n // Placement against the CURRENT scene; null when there is nothing to place.\n const placement = (): { camera: S.Camera; bounds: S.Rect } | null => {\n const sc = buildScene(); // paged: the (possibly new) slice; continuous: full scene\n if (!sc.itemCount) return null;\n const item = paged() ? sc.items[0] : sc.items[itemIndexOfPage(target)];\n const zoom = S.resolveZoom(ctx.getState().zoom, fitBox(item), vp(), pad());\n const subject = isFitAll()\n ? sceneRect()\n : fits(itemRect(item), zoom)\n ? itemRect(item)\n : pageRectOf(item, target);\n // The landing policy: a per-call override beats the setting (explicit\n // beats default); 'keep' pins that axis to the current camera.\n const want = { ...ctx.getState().arrivalAlign, ...opts?.arrivalAlign };\n const placed = S.placeCamera(\n subject,\n vp(),\n zoom,\n pad(),\n {\n x: want.x === 'keep' ? 'start' : want.x,\n y: want.y === 'keep' ? 'start' : want.y,\n },\n ctx.getState().direction,\n );\n // Arrivals are canonical even on an UNBOUNDED stage: the landing clamps\n // within the true bounds regardless (free roam is a manipulation\n // affordance, not an arrival one) — an axis with no freedom collapses to\n // its fitAlign rest here, and the fit-all scene centers even unbounded.\n const bounds = boundsFor(item);\n const settled = S.clampCamera(placed, bounds, vp(), { ...constraint(), bounded: true });\n return {\n camera: {\n zoom,\n x: want.x === 'keep' ? cam().x : settled.x,\n y: want.y === 'keep' ? cam().y : settled.y,\n },\n bounds,\n };\n };\n\n if ((opts?.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n const p = placement();\n if (p) animateTo(p.camera, p.bounds);\n } else {\n // instant placement converges with the scene (wrapped: zoom is a layout input)\n stabilized(() => {\n const p = placement();\n if (p) setCam(p.camera, p.bounds);\n });\n }\n };\n\n /** Step by the navigation unit: the ITEM when it fits the viewport, else the PAGE. */\n const step = (direction: 1 | -1, opts?: GoToOptions) => {\n const st = ctx.getState();\n const item = cursorItem();\n if (!item) return;\n const { grouping: g } = grouping();\n if (isFitAll() || fits(itemRect(item), cam().zoom)) {\n const idx = Math.max(0, Math.min(itemIndexOfPage(st.cursor) + direction, g.length - 1));\n goToTarget(g[idx][0], opts);\n } else {\n goToTarget(st.cursor + direction, opts);\n }\n };\n\n // pon (durable identity) for a page's display index, from the registry captured at open.\n const ponForIndex = (index: number): number =>\n ctx.document()?.pages[index]?.pageObjectNumber ?? index + 1;\n\n // Fallback un-rotated point size from a laid-out box, when the registry entry is\n // momentarily absent. `displaySize` is its own inverse (display box → content\n // box); ÷ contentScale recovers points.\n const unrotatedPoints = (box: S.PageBox): S.Size => {\n const content = displaySize({ width: box.width, height: box.height }, box.rotation);\n return { width: content.width / box.contentScale, height: content.height / box.contentScale };\n };\n\n // Attach durable identity + the per-page transform (PDF points → view px →\n // device px). `scale` is view px per point = contentScale (world per point) ×\n // zoom (view px per world); `pageSize` is the page's UN-rotated points from the\n // registry. Camera-invariant (only zoom/rotation/contentScale/dpr), so the same\n // box always yields the same transform regardless of pan.\n const withTransform = (box: S.PageBox): VisiblePage => {\n const reg = ctx.document()?.pages[box.pageIndex];\n const pageSize = reg\n ? { width: reg.size.width, height: reg.size.height }\n : unrotatedPoints(box);\n const c = cam();\n const ratio = dpr();\n // Footprint top-left (camera-resolved). Device-snapped AT REST — keeps a\n // rotated page on the device grid; while the zoom is in motion it places\n // fractionally, because snapping mid-zoom makes the anchor point jitter\n // ±0.5 device px per step (see StageState.cameraResting).\n const rawX = (box.x - c.x) * c.zoom;\n const rawY = (box.y - c.y) * c.zoom;\n const resting = ctx.getState().cameraResting;\n const screenX = resting ? snapToDevice(rawX, ratio) : rawX;\n const screenY = resting ? snapToDevice(rawY, ratio) : rawY;\n const transform = pageTransform({\n pageSize,\n rotation: box.rotation,\n scale: box.contentScale * c.zoom,\n // The page's PHYSICAL 100%: platform unit factor × its /UserUnit — so\n // `transform.zoom` reads as \"percent of Acrobat's 100%\" per page,\n // independent of sizing mode or camera state.\n baseScale: ctx.getState().viewUnitsPerPoint * (reg?.userUnit ?? 1),\n dpr: ratio,\n });\n // The on-screen page region in points: viewport ∩ footprint in VIEW space\n // (both axis-aligned), inverted through the transform (exact for\n // quarter-turns). The same intersection the virtualizer's query already\n // decided coarsely — refined per page, once, HERE, so no adapter ever\n // re-derives camera math (tiling's demand reads this field).\n const v = vp();\n const onScreen = intersectRects(\n { x: -screenX, y: -screenY, width: v.width, height: v.height },\n { x: 0, y: 0, width: transform.viewWidth, height: transform.viewHeight },\n );\n const visibleRect =\n onScreen.width > 0 && onScreen.height > 0\n ? transform.viewToPageRect(onScreen)\n : { x: 0, y: 0, width: 0, height: 0 };\n return {\n ...box,\n pon: ponForIndex(box.pageIndex),\n screenX,\n screenY,\n transform,\n visibleRect,\n };\n };\n\n // The scroller projection — the camera in native DOM vocabulary, against the\n // SAME bounds the pan clamp uses (stayBounds: the slice item in paged flow,\n // the scene in continuous). Memoized like visiblePages: a stable reference\n // until a field actually moves, so adapter selectors can use plain equality.\n let scrollMemo: S.ScrollMetrics | null = null;\n const scrollMetricsNow = (): S.ScrollMetrics => {\n const sc = buildScene();\n const m = S.scrollMetrics(\n cam(),\n sc.itemCount ? stayBounds() : { x: 0, y: 0, width: 0, height: 0 },\n vp(),\n pad(),\n );\n const p = scrollMemo;\n if (\n p &&\n p.scrollLeft === m.scrollLeft &&\n p.scrollTop === m.scrollTop &&\n p.scrollWidth === m.scrollWidth &&\n p.scrollHeight === m.scrollHeight &&\n p.clientWidth === m.clientWidth &&\n p.clientHeight === m.clientHeight\n ) {\n return p; // scrollableX/Y derive from the numbers — covered by the six\n }\n scrollMemo = m;\n return m;\n };\n\n // Memoized visiblePages -> stable reference (no useSyncExternalStore tearing loop).\n // Paged renders ONLY the slice's item; continuous renders the camera's query window.\n let visSig = '';\n let vis: VisiblePage[] = [];\n const visiblePages = (): VisiblePage[] => {\n const c = cam();\n const v = vp();\n const sc = buildScene();\n const items = paged() ? sc.items.slice(0, 1) : sc.query(S.cameraWorldRect(c, v));\n const sig = `${sceneCache!.key}|${ctx.getState().flow}|${c.x},${c.y},${c.zoom}|${v.width}x${v.height}|${dpr()}|r${ctx.getState().cameraResting ? 1 : 0}`;\n if (sig === visSig) return vis;\n visSig = sig;\n vis = items.flatMap((it) => it.pages).map(withTransform);\n return vis;\n };\n\n // The settings slice of a larger object (state, or a saved view) — derived from\n // the registry, so the shape is never spelled out by hand again.\n const pickSettings = (src: StageSettings): StageSettings => {\n const out: Partial<Record<keyof StageSettings, unknown>> = {};\n for (const k of SETTING_KEYS) out[k] = src[k];\n return out as StageSettings;\n };\n const snapshotSettings = (): StageSettings => pickSettings(ctx.getState());\n\n /**\n * Settings change, reacted per the registry — the strongest effect among the\n * touched settings wins: 'reflow' ⊃ 'scene'/'refit' ⊃ 'reclamp' ⊃ 'none'.\n * The ONE reactive applier: the public `update()` and the responsive driver\n * both land here, so a breakpoint crossing gets exactly the invariant a\n * hand-written update would.\n */\n const applySettings = (patch: Partial<StageSettings>) => {\n cancelAnim();\n const touched = (effect: SettingEffect) =>\n SETTING_KEYS.some((k) => patch[k] !== undefined && SETTINGS_EFFECT[k] === effect);\n // The change's INVARIANT point: a pure zoom-intent change ('refit' alone)\n // holds the zoomAlign focal point — zoomTo/fit-mode switches magnify\n // around the same spot the zoom buttons do; every other reframe holds\n // the anchorAlign reference. Resolved once, used for capture AND restore.\n const align =\n touched('refit') && !touched('scene') && !touched('reflow')\n ? ctx.getState().zoomAlign\n : ctx.getState().anchorAlign;\n const anchor = anchorAt(alignPoint(align, vp())); // capture against the current scene\n ctx.dispatch({ type: 'PATCH', patch });\n if (touched('scene')) sceneCache = null;\n if (touched('reflow')) {\n // flow toggled: re-place onto the cursor's page under the new flow's scene\n // (the camera's coordinates are meaningless across the flow boundary).\n goToTarget(ctx.getState().cursor, { behavior: 'instant' });\n } else if (touched('scene') || touched('refit')) {\n reapply(anchor, align); // rebuild + keep page + re-fit (fit-all: re-place the scene)\n } else if (touched('reclamp')) {\n setCam(cam()); // clamp policy changed: just re-clamp the current camera\n }\n // 'none' (arrivalAlign, zoomAlign, anchorAlign, scrollBehavior): future verbs only\n };\n\n // ── responsive settings (container queries for the settings bag) ─────────────\n // BASE (config + runtime setters) ⊕ matching rules' patches = the EFFECTIVE\n // settings the reducer holds. Rules assert at TRANSITIONS — box, base, or\n // rules changes — which is why the sync diffs against the last EFFECTIVE\n // resolution, never against live state: between crossings interaction owns\n // the state (a pinch-zoomed level survives a resize unless a rule actually\n // flips), and keys the rules don't touch never get re-asserted.\n let base = snapshotSettings();\n let rules: readonly ResponsiveRule[] = config.responsive ?? DEFAULT_RESPONSIVE;\n let lastEffective = base; // rules first assert when a real box arrives\n const publishActive = (active: readonly string[]) => {\n const prev = ctx.getState().activeRules;\n if (prev.length !== active.length || active.some((n, i) => n !== prev[i])) {\n ctx.dispatch({ type: 'RESPONSIVE', active });\n }\n };\n /**\n * Re-resolve and apply what CHANGED since the last resolution. `react: true`\n * routes the patch through `applySettings` (full anchor-preserving update);\n * `react: false` (inside `setViewport`, whose caller runs its own reframe)\n * just lands the patch + cache invalidation and reports the strongest\n * effect so the caller can escalate a rule-driven flow flip.\n */\n const syncResponsive = (react: boolean): SettingEffect => {\n const { effective, active } = resolveResponsive(base, rules, boxOf(vp()));\n const patch: Partial<StageSettings> = {};\n for (const k of SETTING_KEYS) {\n if (!eqSetting(effective[k], lastEffective[k])) {\n Object.assign(patch, { [k]: effective[k] });\n }\n }\n lastEffective = effective;\n publishActive(active);\n const keys = Object.keys(patch) as Array<keyof StageSettings>;\n if (keys.length === 0) return 'none';\n if (react) {\n applySettings(patch);\n return 'none'; // reacted in full — nothing left for the caller\n }\n ctx.dispatch({ type: 'PATCH', patch });\n const rank: Record<SettingEffect, number> = { none: 0, reclamp: 1, refit: 2, scene: 3, reflow: 4 };\n let strongest: SettingEffect = 'none';\n for (const k of keys) if (rank[SETTINGS_EFFECT[k]] > rank[strongest]) strongest = SETTINGS_EFFECT[k];\n if (strongest === 'scene' || strongest === 'reflow') sceneCache = null;\n return strongest;\n };\n\n // Initial-view providers (storage restore, deep-link, an explicit prop…). One owner\n // (placeInitial) resolves them by priority — no effect-ordering races.\n // (`hasPlaced` is declared above the camera-rest detector, which reads it.)\n const initialViewProviders: Array<{ priority: number; fn: () => StageViewState | null }> = [];\n\n const api: StageCapability = {\n // ── selectors ──\n camera: cam,\n viewport: vp,\n scrollMetrics: scrollMetricsNow,\n pageCount: () => ctx.document()?.pageCount ?? 0,\n visiblePages,\n currentPage: () => ctx.getState().cursor,\n currentItemPages: () => {\n const item = cursorItem();\n return item ? [...item.pageIndexes] : [];\n },\n pages: () =>\n (ctx.document()?.pages ?? []).map((p) => ({\n index: p.index,\n pon: p.pageObjectNumber,\n label: p.label ?? null,\n })),\n pageRect: (pon) => {\n const meta = ctx.document();\n const index = meta ? meta.pages.findIndex((p) => p.pageObjectNumber === pon) : -1;\n if (index < 0) return null;\n const sc = buildScene();\n if (!sc.itemCount) return null;\n const box = sc.items[sc.itemOfPage(index)].pages.find((p) => p.pageIndex === index);\n return box ? withTransform(box) : null;\n },\n pageAt: (screen) => {\n // Find the visible page whose device-snapped display box contains the\n // point, then invert that page's transform — same `viewToPage` the\n // per-page PageContext.toPagePoint uses, so the two never drift.\n for (const p of visiblePages()) {\n const lx = screen.x - p.screenX;\n const ly = screen.y - p.screenY;\n if (lx >= 0 && ly >= 0 && lx <= p.transform.viewWidth && ly <= p.transform.viewHeight) {\n return {\n pon: p.pon,\n point: p.transform.viewToPage({ x: lx, y: ly }),\n scale: p.transform.viewScale,\n rotation: p.rotation,\n zoom: p.transform.zoom,\n };\n }\n }\n return null;\n },\n pointOnPage: (pon, screen) => {\n // `pageAt` minus the containment check: project onto ONE page's plane,\n // valid outside its bounds — the same inverse transform, so no drift.\n const p = visiblePages().find((v) => v.pon === pon);\n if (!p) return null;\n return p.transform.viewToPage({ x: screen.x - p.screenX, y: screen.y - p.screenY });\n },\n pageToWorld: (pon, pt) => {\n const pr = api.pageRect(pon);\n if (!pr) return null;\n // Place the content point into the page's display box via the SAME\n // quarter-turn matrix the layout/renderer use (`rotateScaleMatrix`) — so\n // this forward transform and the adapter's inverse hit-test (which inverts\n // the same matrix) can't drift. `displaySize` is its own inverse, so it\n // recovers the un-rotated content size from the display box.\n const content = displaySize({ width: pr.width, height: pr.height }, pr.rotation);\n const m = rotateScaleMatrix(pr.contentScale, content.width, content.height, pr.rotation);\n const offset = applyPoint(m, pt);\n return { x: pr.x + offset.x, y: pr.y + offset.y };\n },\n pageRectToScreen: (pon, rect) => {\n const pr = api.pageRect(pon);\n if (!pr) return null;\n const content = displaySize({ width: pr.width, height: pr.height }, pr.rotation);\n const m = rotateScaleMatrix(pr.contentScale, content.width, content.height, pr.rotation);\n const wr = applyRect(m, rect);\n const c = cam();\n const tl = S.toScreen(c, { x: pr.x + wr.x, y: pr.y + wr.y });\n return { x: tl.x, y: tl.y, width: wr.width * c.zoom, height: wr.height * c.zoom };\n },\n toScreen: (w) => S.toScreen(cam(), w),\n toWorld: (s) => S.toWorld(cam(), s),\n flow: () => ctx.getState().flow,\n layout: () => ctx.getState().layout,\n spread: () => ctx.getState().spread,\n sizing: () => ctx.getState().sizing,\n columns: () => ctx.getState().columns,\n bounded: () => ctx.getState().bounded,\n padding: () => ctx.getState().padding,\n gap: () => ctx.getState().gap,\n pageFrame: () => ctx.getState().pageFrame,\n fitAlign: () => ctx.getState().fitAlign,\n arrivalAlign: () => ctx.getState().arrivalAlign,\n zoomAlign: () => ctx.getState().zoomAlign,\n anchorAlign: () => ctx.getState().anchorAlign,\n direction: () => ctx.getState().direction,\n scrollBehavior: () => ctx.getState().scrollBehavior,\n viewRotation: () => ctx.getState().viewRotation,\n zoomLevel: () => cam().zoom,\n zoomMode: () => {\n const z = ctx.getState().zoom;\n return 'mode' in z ? z.mode : 'custom';\n },\n viewpoint: (): Viewpoint => ({ anchor: currentAnchor(), zoom: ctx.getState().zoom }),\n settings: snapshotSettings,\n viewState: (): StageViewState => ({\n ...snapshotSettings(),\n cursor: ctx.getState().cursor,\n anchor: currentAnchor(),\n }),\n\n // ── intents ──\n setViewport: (v) => {\n // Initial placement is LEVEL-triggered, owned here: the moment the stage\n // first learns a real size (both axes) and the document has pages, resolve\n // the initial view (storage/deep-link providers, else reset). Every report\n // re-checks the condition — no watch, no effect-registration race, no edge\n // to miss when the viewport was already sized before anyone listened.\n if (!hasPlaced) {\n ctx.dispatch({ type: 'VP', vp: v });\n syncResponsive(false); // rules see the box before placement resolves\n if (v.width > 0 && v.height > 0 && (ctx.document()?.pageCount ?? 0) > 0) {\n api.placeInitial();\n }\n return;\n }\n // Afterwards every resize keeps the same page and re-resolves fit-modes.\n cancelAnim();\n const anchor = currentAnchor(); // measured against the OLD viewport\n ctx.dispatch({ type: 'VP', vp: v }); // new viewport\n // A breakpoint crossing rides the SAME reframe: the rule patch lands\n // before the anchor is re-applied, so the restore resolves under the\n // new settings. Only a rule-driven FLOW flip escalates past reapply\n // (camera coordinates are meaningless across the flow boundary).\n const fx = syncResponsive(false);\n if (fx === 'reflow') {\n goToTarget(ctx.getState().cursor, { behavior: 'instant' });\n } else {\n reapply(anchor);\n }\n },\n setDevicePixelRatio: (ratio) => {\n // Pure device-resolution change: it only affects each page transform's\n // device px (crispness) + sub-pixel box snapping, never the layout or\n // camera — so no re-place, just store it; visiblePages re-keys on dpr.\n if (ratio > 0 && ratio !== dpr()) ctx.dispatch({ type: 'DPR', dpr: ratio });\n },\n setCamera: (c) => {\n cancelAnim();\n setCam(c);\n syncCursorFromCamera();\n },\n panBy: (dx, dy) => {\n cancelAnim();\n if (gestureDepth > 0 && gestureElastic) {\n // Elastic: integrate the finger on the UNCLAMPED camera and display it\n // through the resistance curve. Initialized from the displayed camera's\n // inverse, so catching a mid-bounce stretch continues seamlessly.\n gestureRaw = S.panByScreen(gestureRaw ?? unrubberize(cam()), dx, dy);\n setCamRaw(rubberize(gestureRaw));\n } else {\n setCam(S.panByScreen(cam(), dx, dy));\n }\n syncCursorFromCamera();\n },\n scrollTo: (opts) => {\n cancelAnim();\n const sc = buildScene();\n if (!sc.itemCount) return;\n const next = S.cameraFromScroll(cam(), stayBounds(), vp(), pad(), opts);\n if (opts.behavior === 'smooth') {\n animateTo(next, stayBounds(), 240, syncCursorFromCamera);\n } else {\n setCam(next);\n syncCursorFromCamera();\n }\n },\n scrollBy: ({ left, top, behavior }) => {\n const m = scrollMetricsNow();\n api.scrollTo({\n left: left === undefined ? undefined : m.scrollLeft + left,\n top: top === undefined ? undefined : m.scrollTop + top,\n behavior,\n });\n },\n zoomAround: (pt, factor) => {\n cancelAnim();\n const before = buildScene();\n // page-relative focal point: the durable identity of \"what's under the cursor\"\n const focal = before.itemCount ? S.anchorAtPoint(before, S.toWorld(cam(), pt)) : null;\n setCam(S.zoomAround(cam(), pt, factor));\n // record the resulting fixed level as the zoom intent — focal, so no\n // re-anchor… deferred to endGesture inside a gesture bracket (one PATCH\n // per pinch instead of one per event).\n if (gestureDepth === 0) {\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: cam().zoom } } });\n }\n // …UNLESS zoom is a LAYOUT INPUT (wrapped grid) and the scene just re-wrapped\n // underneath the camera. The old world point is stale then — re-pin the SAME\n // page-point under the cursor and clamp against the new geometry. In every\n // non-wrapped mode the scene reference is unchanged and this never runs.\n const after = buildScene();\n if (after !== before && focal && after.itemCount) {\n setCam(S.cameraForAnchorAtScreen(focal, after, pt, cam().zoom));\n }\n // A zoom write mid-elastic-gesture (pinch) re-bases the pan integrator:\n // bounds just changed shape under the stretch, so the displayed camera\n // becomes the new reference and resistance re-accumulates from here.\n if (gestureDepth > 0 && gestureElastic) gestureRaw = unrubberize(cam());\n syncCursorFromCamera();\n },\n beginGesture: (options) => {\n gestureDepth++;\n if (gestureDepth === 1) {\n cancelAnim(); // catch: the next touch-down stops any tween or fling\n gestureZoomed = false;\n gestureElastic = options?.elastic === true;\n gestureRaw = null;\n }\n },\n endGesture: () => {\n if (gestureDepth === 0) return;\n gestureDepth--;\n if (gestureDepth > 0) return;\n gestureRaw = null;\n const wasElastic = gestureElastic;\n gestureElastic = false;\n if (gestureZoomed) {\n gestureZoomed = false;\n // The deferred zoom intent: ONE patch for the whole gesture. (In a\n // wrapped grid this may re-wrap the scene; the next reframe\n // re-anchors through the normal update path.)\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: cam().zoom } } });\n armRest(); // the gesture is over — NOW the 150 ms rest countdown runs\n }\n syncCursorFromCamera();\n if (wasElastic) {\n // Released while stretched → spring home. A fling() arriving right\n // after simply re-enters the same physics with the release velocity.\n const c = cam();\n const cl = S.clampCamera(c, stayBounds(), vp(), constraint());\n if (cl.x !== c.x || cl.y !== c.y) startCameraPhysics(0, 0);\n }\n },\n fling: (vx, vy) => startCameraPhysics(vx, vy),\n cameraInMotion: () => raf !== 0,\n doubleTapZoom: (pt) => {\n cancelAnim();\n const sc0 = buildScene();\n if (!sc0.itemCount) return;\n const item = paged() ? sc0.items[0] : sc0.items[itemIndexOfPage(ctx.getState().cursor)];\n // The ladder (the platform convention): ascending READING POSTURES, each\n // derived from a zoom intent — see the page (automatic), read the text\n // (fit-width), inspect (2.5× the automatic fit). Stops within 10% of a\n // neighbor collapse — on phones automatic IS fit-width, so the ladder\n // degenerates to the familiar two-state toggle.\n //\n // The rule (iOS's): the ladder ascends only from ON a rung — a tap at a\n // posture moves to the next, wrapping past the top. A pinch to any\n // OTHER level is leaving the ladder, and double-tap there is a RESET to\n // the base fit (\"take me back to reading\"), never a further zoom-in.\n const fit = fitBox(item);\n const auto = S.resolveZoom({ mode: S.ZoomMode.Automatic }, fit, vp(), pad());\n const fitW = S.resolveZoom({ mode: S.ZoomMode.FitWidth }, fit, vp(), pad());\n const stops = [auto, fitW, Math.min(auto * 2.5, S.ZOOM_MAX)]\n .sort((a, b) => a - b)\n .filter((z, i, all) => i === 0 || z > all[i - 1] * 1.1);\n // the same ±10% band the dedupe uses: \"at a posture\" tolerates fit drift\n const near = (z: number, stop: number) => z > stop / 1.1 && z < stop * 1.1;\n const onRung = stops.findIndex((s) => near(cam().zoom, s));\n const target = onRung >= 0 ? stops[(onRung + 1) % stops.length] : stops[0];\n // The INTENT commits UP FRONT (the `reveal` precedent): a caught tween\n // must never leave the camera on some intermediate zoom while the stored\n // intent still says \"fit\" — the next refit would snap somewhere the user\n // didn't ask for. Interrupted or not, the record says where we were going.\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: target } } });\n // Wrapped grids re-layout on the intent change — anchor the focal point\n // against the scene that will actually be on screen.\n const sc = buildScene();\n if (!sc.itemCount) return;\n const focal = S.anchorAtPoint(sc, S.toWorld(cam(), pt));\n // The focal-anchored tween: the tapped point holds still by construction\n // (linear coordinate lerps would swing it mid-flight — the \"dip\").\n animateZoomAnchored(focal, pt, target, 240, syncCursorFromCamera);\n },\n // Pointer-less zooms magnify around the zoomAlign focal point (pinch and\n // wheel pass their own pointer to zoomAround — physics beats policy).\n zoomIn: () => api.zoomAround(alignPoint(ctx.getState().zoomAlign, vp()), 1.2),\n zoomOut: () => api.zoomAround(alignPoint(ctx.getState().zoomAlign, vp()), 1 / 1.2),\n zoomTo: (spec) => api.update({ zoom: spec }),\n fitWidth: () => api.update({ zoom: { mode: S.ZoomMode.FitWidth } }),\n fitPage: () => api.update({ zoom: { mode: S.ZoomMode.FitPage } }),\n fitAll: () => api.update({ zoom: { mode: S.ZoomMode.FitAll } }),\n automatic: () => api.update({ zoom: { mode: S.ZoomMode.Automatic } }),\n refit: () => {\n // The page geometry changed underneath us (rotate/move/delete). Treat it\n // exactly like a viewport resize: re-resolve the active zoom intent and\n // re-place against the now-current scene, keeping the anchored page-point.\n // No-op until the first placement; the scene is re-keyed on the registry\n // revision, so `reapply` reads the rotated footprint.\n if (!hasPlaced) return;\n cancelAnim();\n reapply(currentAnchor());\n },\n goToPage: (pageIndex, opts) => goToTarget(pageIndex, opts),\n reveal: (pageIndex, opts) => {\n const doc = ctx.document();\n if (!doc || doc.pageCount === 0) return;\n const target = Math.max(0, Math.min(pageIndex, doc.pageCount - 1));\n const positioned =\n !!opts &&\n (opts.rect !== undefined ||\n opts.anchor !== undefined ||\n (opts.zoom !== undefined && opts.zoom !== 'keep'));\n\n if (!positioned) {\n // Bare reveal — NOT navigation: minimal visibility, cursor untouched.\n if (paged()) {\n // the page isn't in the one-item slice — revealing it IS navigating to it\n goToTarget(target, opts);\n return;\n }\n const sc = buildScene();\n if (!sc.itemCount) return;\n const page = pageRectOf(sc.items[itemIndexOfPage(target)], target);\n // Reveal the OUTER box: pageFrame chrome (labels, buttons) belongs to the\n // page, so \"make the page visible\" includes its reserved bands.\n const m = worldPageFrame();\n const box = {\n x: page.x - m.left,\n y: page.y - m.top,\n width: page.width + m.left + m.right,\n height: page.height + m.top + m.bottom,\n };\n const camera = S.revealCamera(cam(), box, vp(), pad());\n const current = cam();\n if (camera.x === current.x && camera.y === current.y) return; // already visible\n cancelAnim();\n if ((opts?.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n animateTo(camera, sceneRect());\n } else {\n setCam(camera, sceneRect());\n }\n return;\n }\n\n // Positioned reveal: an ARRIVAL at a rect/point (search hit, PDF\n // destination). Like navigation, the cursor is INTENT — set up front\n // (paged: this also rebuilds the one-item slice), not derived from a\n // possibly mid-tween camera.\n cancelAnim();\n if (target !== ctx.getState().cursor) {\n ctx.dispatch({ type: 'CURSOR', cursor: target });\n }\n\n const place = (): { camera: S.Camera; bounds: S.Rect; zoomChanged: boolean } | null => {\n const sc = buildScene();\n if (!sc.itemCount) return null;\n const item = paged() ? sc.items[0] : sc.items[itemIndexOfPage(target)];\n const world = opts.rect\n ? worldRectForContent(item, target, opts.rect)\n : pageRectOf(item, target);\n const zd = opts.zoom ?? 'keep';\n const availW = Math.max(1, vp().width - 2 * pad());\n const availH = Math.max(1, vp().height - 2 * pad());\n let zoom =\n typeof zd === 'object'\n ? zd.level\n : zd === 'fit'\n ? Math.min(availW / world.width, availH / world.height)\n : zd === 'fit-width'\n ? availW / world.width\n : zd === 'fit-height'\n ? availH / world.height\n : cam().zoom;\n // Degenerate target (a point with a fit directive) → pan only.\n if (!Number.isFinite(zoom) || zoom <= 0) zoom = cam().zoom;\n const zoomChanged = zd !== 'keep';\n const a = opts.anchor ?? {};\n return {\n camera: {\n x: revealAxis(a.x, cam().x, world.x, world.width, vp().width, zoom, zoomChanged),\n y: revealAxis(a.y, cam().y, world.y, world.height, vp().height, zoom, zoomChanged),\n zoom,\n },\n bounds: boundsFor(item),\n zoomChanged,\n };\n };\n\n const first = place();\n if (!first) return;\n // A resolved zoom becomes the zoom intent (like zoomAround), so later\n // resizes/refits keep the destination's magnification.\n if (first.zoomChanged) {\n ctx.dispatch({ type: 'PATCH', patch: { zoom: { level: first.camera.zoom } } });\n }\n if ((opts.behavior ?? ctx.getState().scrollBehavior) === 'smooth') {\n // If the zoom patch re-wrapped the scene (zoom is a layout input in\n // wrapped mode), recompute once against the new geometry.\n const p = place() ?? first;\n animateTo(p.camera, p.bounds);\n } else {\n stabilized(() => {\n const p = place();\n if (p) setCam(p.camera, p.bounds);\n });\n }\n },\n next: (opts) => step(1, opts),\n prev: (opts) => step(-1, opts),\n lensId: () => ctx.id,\n update: (patch) => {\n // Writes the responsive BASE; the resolver decides what actually lands\n // (a matching rule's key wins until its rule stops matching). With no\n // rules in play this degenerates to exactly the old direct update.\n base = mergeSettings(base, patch);\n syncResponsive(true);\n },\n setResponsive: (next) => {\n rules = next;\n syncResponsive(true);\n },\n matches: (name) => ctx.getState().activeRules.includes(name),\n activeRules: () => ctx.getState().activeRules,\n setFlow: (flow) => api.update({ flow }),\n setLayout: (layout) => api.update({ layout }),\n setSpread: (spread) => api.update({ spread }),\n setSizing: (sizing) => api.update({ sizing }),\n setColumns: (columns) => api.update({ columns }),\n setBounded: (bounded) => api.update({ bounded }),\n setPadding: (padding) => api.update({ padding }),\n setGap: (gap) => api.update({ gap }),\n setPageFrame: (pageFrame) => api.update({ pageFrame }),\n setFitAlign: (fitAlign) => api.update({ fitAlign }),\n setArrivalAlign: (arrivalAlign) => api.update({ arrivalAlign }),\n setZoomAlign: (zoomAlign) => api.update({ zoomAlign }),\n setAnchorAlign: (anchorAlign) => api.update({ anchorAlign }),\n setDirection: (direction) => api.update({ direction }),\n setViewRotation: (viewRotation) => api.update({ viewRotation }),\n rotateView: (delta) =>\n api.setViewRotation(addRotations(ctx.getState().viewRotation, delta === 90 ? 90 : 270)),\n setScrollBehavior: (behavior) => api.update({ scrollBehavior: behavior }),\n applyViewState: (view) => {\n cancelAnim();\n // A restored view is app-level state: it writes the BASE, and the rules\n // re-assert on top — so a snapshot saved on a desktop restores compact\n // padding on a phone. One PATCH carries the full effective result.\n base = mergeSettings(base, pickSettings(view));\n const { effective, active } = resolveResponsive(base, rules, boxOf(vp()));\n lastEffective = effective;\n publishActive(active);\n ctx.dispatch({ type: 'PATCH', patch: effective });\n ctx.dispatch({ type: 'CURSOR', cursor: view.cursor ?? 0 });\n sceneCache = null;\n applyAnchor(view.anchor);\n },\n provideInitialView: (priority, fn) => {\n initialViewProviders.push({ priority, fn });\n },\n placeInitial: () => {\n hasPlaced = true;\n const sorted = [...initialViewProviders].sort((a, b) => b.priority - a.priority);\n for (const p of sorted) {\n const view = p.fn();\n if (view) {\n api.applyViewState(view);\n return;\n }\n }\n api.resetView();\n },\n // Home = page 0, a fresh arrival at arrivalAlign (the clamp collapses any\n // fitting axis to its fitAlign rest point).\n resetView: () => goToTarget(0, { behavior: 'instant' }),\n };\n return api;\n}\n","import type { CapabilityToken, EffectContext } from '@embedpdf/core';\nimport type { StageAction, StageCapability, StageState } from './types';\n\n/**\n * The one effect a stage lens needs: when the document's page registry changes\n * (rotate/move/delete bump `DocumentMeta.revision`), the page geometry under\n * this lens just changed, so re-fit — re-resolve the active zoom intent and\n * re-place against the new footprint.\n *\n * Without it, fit/pixel zoom modes stay anchored to the pre-mutation dimensions:\n * `pageWidth: 110` renders wider after a 90° rotation (width↔height swap), and\n * `fitPage` over- or under-fills. The scene already re-keys on the revision, so\n * only the resolved `cam.zoom` was stale; `refit()` recomputes it.\n *\n * Steady-state only: this reacts to revision CHANGES, never the initial value,\n * so it can't race the capability's level-triggered initial placement (the\n * reason the rest of the plugin is effect-free).\n */\nexport function registerStageEffects(\n ctx: EffectContext<StageState, StageAction>,\n token: CapabilityToken<StageCapability>,\n): void {\n ctx.watch(\n () => ctx.document()?.revision ?? 0,\n () => ctx.get(token).refit(),\n );\n}\n","import { DEFAULT_SETTINGS } from './settings';\nimport type { StageAction, StageConfig, StageSettings, StageState } from './types';\n\nexport const initialStageState = (config: StageConfig): StageState => {\n // `scheduler` and `responsive` are capability config, not settings — strip\n // them so the spread below stays a pure settings override.\n const { scheduler: _scheduler, responsive: _responsive, ...overrides } = config;\n return {\n camera: { x: 0, y: 0, zoom: 1 },\n cameraResting: true,\n vp: { width: 0, height: 0 },\n dpr: 1,\n cursor: 0,\n activeRules: [],\n ...DEFAULT_SETTINGS,\n ...overrides, // config overrides any default; the rest fall back to DEFAULT_SETTINGS\n };\n};\n\n/** Merge a settings patch, ignoring undefined values (safe for partial restores). */\nconst applyPatch = (state: StageState, patch: Partial<StageSettings>): StageState => {\n const next: StageState = { ...state };\n let key: keyof StageSettings;\n for (key in patch) {\n const value = patch[key];\n if (value !== undefined) Object.assign(next, { [key]: value });\n }\n return next;\n};\n\n/**\n * Pure. Every transition is here; nothing else mutates Stage state. Settings are a\n * flat bag of primitives — one PATCH action sets any subset (the capability decides\n * what camera follow-up, if any, each change needs).\n */\nexport const stageReducer = (state: StageState, a: StageAction): StageState => {\n switch (a.type) {\n case 'CAMERA':\n return { ...state, camera: a.camera };\n case 'CAMERA_REST':\n return state.cameraResting === a.resting ? state : { ...state, cameraResting: a.resting };\n case 'VP':\n return { ...state, vp: a.vp };\n case 'DPR':\n return { ...state, dpr: a.dpr };\n case 'CURSOR':\n return { ...state, cursor: a.cursor };\n case 'PATCH':\n return applyPatch(state, a.patch);\n case 'RESPONSIVE':\n return { ...state, activeRules: a.active };\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { PageObjectNumber } from '@embedpdf/core';\nimport type { PageRotation, PageTransform, Rect } from '@embedpdf/core-geometry';\nimport type {\n Alignment,\n AlignmentValue,\n AlignValue,\n Anchor,\n Direction,\n Camera,\n PageBox,\n PageFrame,\n Point,\n ScrollMetrics,\n Size,\n SizingMode,\n SpreadMode,\n ZoomModeValue,\n ZoomSpec,\n} from '@embedpdf/core-stage';\n\nexport type LayoutKind = 'vertical' | 'horizontal' | 'grid';\n/** Navigation (goToPage) either tweens the camera or jumps instantly. */\nexport type ScrollBehaviorKind = 'smooth' | 'instant';\n/**\n * Presentation flow:\n * 'continuous' — the whole document is scrollable; the camera roams the full scene.\n * 'paged' — one item (page or spread) at a time: the scene is a one-item\n * slice at the cursor, and next/prev step between items.\n */\nexport type FlowMode = 'continuous' | 'paged';\n/**\n * Grid column policy:\n * 'square' — ≈√n columns (the classic canvas arrangement)\n * 'auto' — WRAPPED: as many columns as fit the viewport line at the current\n * zoom (the responsive thumbnail-sidebar behavior; re-wraps on resize)\n * number — a fixed column count\n */\nexport type GridColumns = 'square' | 'auto' | number;\n\n/**\n * Space between items — the value's shape carries the unit (like ZoomSpec):\n * number — WORLD units: the gap is part of the canvas and scales with zoom,\n * so the whole scene zooms as one rigid object (the document feel).\n * { px: n } — SCREEN px: UI-stable spacing, the same in every document at every\n * zoom (the browser-of-items feel: thumbnails, organizers).\n */\nexport type Gap = number | { px: number };\n\n/**\n * The one environmental fact a headless stage has: the box it was told about\n * (`setViewport`). Responsive rules can query nothing else — no user agent, no\n * pointer type (modality is per-event, on `PointerSample`), no window. Space,\n * not device: a narrow pane on a desktop is compact too, and each stage\n * instance resolves against ITS OWN box.\n */\nexport interface StageBox {\n width: number;\n height: number;\n /** Of the CONTAINER, not the device. A square box is 'portrait' (the CSS rule). */\n orientation: 'portrait' | 'landscape';\n}\n\n/** Declarative box query — all bounds inclusive, all fields optional (AND-ed). */\nexport interface BoxQuery {\n minWidth?: number;\n maxWidth?: number;\n minHeight?: number;\n maxHeight?: number;\n orientation?: 'portrait' | 'landscape';\n}\n\n/**\n * One `@container` block for the settings bag: when the box matches, assert\n * this settings patch. Rules evaluate in source order and ALL matching rules\n * apply, later winning per key (each key replaced whole — no deep merges).\n * Effective settings = base (config + runtime setters) ⊕ matching patches.\n *\n * Semantics, stated honestly:\n * • Runtime setters write the BASE; a matching rule wins over it. Apps\n * needing situational absolute control edit the rules (`setResponsive`).\n * • Rules assert at TRANSITIONS (box/base/rules changes), not continuously —\n * between crossings, interaction owns the state. A rule containing `zoom`\n * re-fits when the box crosses it (the rotate-an-iPad behavior) and then\n * leaves the pinch alone.\n *\n * A rule with a `name` is a queryable fact (`matches(name)`), reactive in\n * every framework; a named rule with NO settings is a pure shared breakpoint\n * the app chrome can key its own presentation off — one definition serving\n * both the layout math and the UI.\n */\nexport interface ResponsiveRule {\n name?: string;\n /** Declarative box query, or a predicate for anything the box can answer. */\n when: BoxQuery | ((box: StageBox) => boolean);\n /** The settings this situation asserts. Omit for a pure named query. */\n settings?: Partial<StageSettings>;\n}\n\n/**\n * One axis of the ARRIVAL policy — stage-core's AlignValue ('start' |\n * 'center' | 'end' | viewport fraction 0–1) plus one navigation-only word:\n * 'keep' — this axis does not move on arrival: page forward, hold your\n * pan (the two-column-paper feel; the PDF /XYZ null semantic).\n */\nexport type ArrivalAlignValue = AlignValue | 'keep';\nexport interface ArrivalAlignment {\n x: ArrivalAlignValue;\n y: ArrivalAlignValue;\n}\n\n/**\n * The Stage's orthogonal, independently-settable primitives. Every field can be set\n * on its own (setLayout, setBounded, …) or several at once via `update()`. A\n * \"preset\" is just a `Partial<StageSettings>` the app keeps and applies — no preset\n * machinery lives here.\n */\nexport interface StageSettings {\n /** Continuous scroll, or one item at a time. */\n flow: FlowMode;\n layout: LayoutKind;\n spread: SpreadMode;\n /** Page sizing: true PDF sizes, or equalize the cross axis so pages sit flush. */\n sizing: SizingMode;\n /** Grid column policy (grid layout only): 'square', 'auto' (wrapped), or a count. */\n columns: GridColumns;\n /** Clamp the camera to the content? Off = free infinite pan (plans / CAD). */\n bounded: boolean;\n /**\n * Breathing room (screen px) around the content — the one spacing concept.\n * Fit-modes inset by it, arrivals leave it as a gutter, and the clamp lets the\n * camera reveal exactly this much beyond each content edge.\n */\n padding: number;\n /**\n * Space BETWEEN items — and between the halves of a spread. A number is world\n * units (scales with zoom — the canvas feel); `{ px }` is screen px (UI-stable\n * — the thumbnail feel). See {@link Gap}.\n */\n gap: Gap;\n /**\n * Reserved chrome real estate around EACH PAGE, in SCREEN px — one thickness\n * per side. The page content is inset by these; the bands hold box-space\n * chrome (a label below, a button row above, side rails) painted by the app\n * via the adapter's `pageChrome` slot. Per page, not per item: in a spread\n * every page keeps its own flanks. Constant screen px (unaffected by zoom).\n *\n * Naming rule for this settings bag: every setting describes the STAGE itself\n * (the container) — `padding`, `gap`, `layout`, … The rare setting owned by the\n * page carries the `page` prefix (`pageFrame`; `pageWidth` in zoom).\n */\n pageFrame: PageFrame;\n /**\n * Reading direction. RTL: horizontal items advance leftward, spreads bind on the\n * right, grid rows fill right→left, and alignment 'start' on x means the RIGHT\n * edge (logical, CSS-style). Navigation is index-based and never changes.\n */\n direction: Direction;\n /**\n * The alignment family — EVERY camera move is defined by what it holds\n * fixed. Gestures (pan/pinch/wheel) hold the pointer: physics, no setting.\n * Explicit arrivals (positioned reveal, destinations, viewpoints) hold\n * whatever the call specifies. These four settings govern the rest:\n *\n * fitAlign — the standing CONSTRAINT: where content rests on an axis\n * the camera cannot travel (it fits the TRUE bounds — the\n * scene in continuous flow, the item slice in paged). The\n * clamp enforces it on every camera write — which is why a\n * fitting axis settles identically whatever arrivalAlign\n * says. center/center = document feel; y:'start' = sidebar\n * thumbnails hugging the top.\n * arrivalAlign — the landing POLICY: where navigation (goToPage, next/\n * prev, reset) puts the target — THE SAME at every zoom.\n * start/start = reading (top-left, direction-aware);\n * center/center = presentation/drawings (Drawboard feel);\n * y: 0.35 = the find-bar line; 'keep' = don't move an axis.\n * zoomAlign — the FOCAL point of a pointer-less zoom (zoomIn/zoomOut,\n * zoomTo, fit-mode switches). Pinch/ctrl+wheel always hold\n * the pointer instead — that is physics, not policy.\n * center/center = the view inflates around its middle;\n * y:'start' = the first visible line holds still.\n * anchorAlign — the viewport point that SURVIVES a reframe (viewport\n * resize, page rotation, spread/gap change): the view\n * anchor is captured there and restored there. start/start\n * = the browser scroll model (growth reveals below — a\n * container that mounts small and expands never shoves the\n * document down); center/center = canvas-style symmetric\n * resizes (the Figma feel).\n *\n * Named x values are LOGICAL (CSS-style: 'start' = reading start — the\n * right edge in RTL); fractions are physical, like screen coordinates.\n */\n fitAlign: Alignment;\n /** See {@link StageSettings.fitAlign} — where navigation lands, per axis. */\n arrivalAlign: ArrivalAlignment;\n /** See {@link StageSettings.fitAlign} — the pointer-less zoom focal point. */\n zoomAlign: AlignmentValue;\n /** See {@link StageSettings.fitAlign} — the viewport point reframes hold. */\n anchorAlign: AlignmentValue;\n /**\n * NON-PERSISTENT view rotation: a quarter-turn (clockwise) applied to how\n * EVERY page is DISPLAYED in this lens, on top of each page's own /Rotate —\n * Adobe's \"Rotate View\". A display setting like `zoom` or `layout`: per lens\n * (the main viewer can rotate while a thumbnail lens stays upright), never\n * written to the document, gone when the lens resets. The PERMANENT\n * counterpart — writing /Rotate into the PDF — is plugin-page-edit's\n * `rotateBy`/`setRotation`.\n */\n viewRotation: PageRotation;\n /** Zoom intent: a fit-mode (automatic/fit-page/fit-width/fit-all) or a fixed level. */\n zoom: ZoomSpec;\n /** Default behaviour for goToPage/next/prev. */\n scrollBehavior: ScrollBehaviorKind;\n /**\n * View pixels per PDF point — the platform's physical unit factor, folded into\n * the layout so 100% (zoom 1) is physically accurate. Web = 96/72 (1 pt = 1/72\",\n * 1 CSS px = 1/96\"); a native platform injects its own (iOS pt, Android dp). It\n * scales every page's world size (and thus `contentScale`), so the camera math,\n * `gap`/`padding`/`pageFrame` (world units), and absolute-px zoom modes\n * (`pageWidth`) are all unaffected — only the pages themselves resize.\n */\n viewUnitsPerPoint: number;\n}\n\n/**\n * A laid-out page handed to the shell.\n * - PageBox + `pon`: LAYOUT truth (world coords + identity) — the shell uses\n * `x/y/width/height` only to POSITION the page container.\n * - `transform`: PRESENTATION truth — the single bridge between PDF points,\n * view px, and device px for this page. Plugins do ALL coordinate work\n * through it (`pageToView` / `viewToPage` / `deviceWidth` / `cssMatrix`),\n * never by re-deriving `x * scale` / `* dpr`. Page-local, so it's\n * camera/pan-invariant.\n */\nexport interface VisiblePage extends PageBox {\n pon: PageObjectNumber;\n /**\n * The page's DISPLAY-box (footprint) top-left in screen px, camera-resolved and\n * snapped to the device grid. The shell positions the page container at this —\n * snapping here (not in the adapter) keeps a CSS-rotated page on the pixel grid\n * for every framework, with no hand-rounding.\n */\n screenX: number;\n screenY: number;\n transform: PageTransform;\n /**\n * The page region actually ON SCREEN, in UN-rotated page points (y-down) —\n * viewport ∩ footprint inverted through the transform (exact for\n * quarter-turns). Zero-sized when the page sits outside the viewport.\n * Visibility is the STAGE's data (it already intersects viewport × pages to\n * virtualize); adapters and demand consumers (tiling's `PageViewDemand`)\n * read it instead of re-deriving camera math per framework.\n */\n visibleRect: Rect;\n}\n\nexport interface StageState extends StageSettings {\n camera: Camera;\n /**\n * Names of the responsive rules currently matching the box, in source order.\n * State (not derived) so `matches()` is reactive through the ordinary\n * selector machinery in every framework.\n */\n activeRules: readonly string[];\n /**\n * False while the ZOOM is in motion, true once it has rested (~150ms of\n * frames without a zoom write). Device-snapping of page origins is gated\n * on this: a continuous zoom and a snapped origin cannot coexist without\n * the anchor point jittering ±0.5 device px per step (the rounding lands\n * differently every frame), so pages place fractionally while zooming and\n * snap once at rest — when crispness actually matters. Pans always snap\n * (a pure translation snap has no anchor error). Transient like `camera`.\n */\n cameraResting: boolean;\n vp: Size;\n /**\n * Device pixels per view pixel (web: `window.devicePixelRatio`). Reported by\n * the shell like the viewport; feeds each page's transform so bitmaps render\n * crisp (exact device px) and boxes land on the device grid. Defaults to 1.\n */\n dpr: number;\n /**\n * The current page — transient like `camera` (NOT a setting), valid in BOTH flows.\n * Navigation sets it; in continuous flow scrolling syncs it from the camera; in\n * paged flow panning never moves it (the scene is a one-item slice at this page).\n * Stored as a page index so it survives spread/layout regrouping.\n */\n cursor: number;\n}\n\nexport type StageAction =\n | { type: 'CAMERA'; camera: Camera }\n | { type: 'CAMERA_REST'; resting: boolean }\n | { type: 'VP'; vp: Size }\n | { type: 'DPR'; dpr: number }\n | { type: 'CURSOR'; cursor: number }\n | { type: 'PATCH'; patch: Partial<StageSettings> }\n | { type: 'RESPONSIVE'; active: readonly string[] };\n\n/**\n * A page-relative view memento: \"what I'm looking at and how zoomed\". The durable\n * currency for per-page view memory (construction worksheets) — capture with\n * `viewpoint()`, restore with `goToPage(i, { viewpoint })`. Survives resizes\n * because the anchor is page-relative and fit-modes re-resolve.\n */\nexport interface Viewpoint {\n anchor: Anchor;\n zoom: ZoomSpec;\n}\n\n/** Durable, serializable view state — the unit of session persistence. */\nexport interface StageViewState extends StageSettings {\n cursor: number;\n anchor: Anchor;\n}\n\n/**\n * Host timing seam. The pure core (stage-core) never touches time; the camera tween\n * lives in this (impure) shell and asks for frames through a Scheduler. The default\n * is the browser's requestAnimationFrame; inject a fake in tests, or an instant one\n * in Node/SSR.\n */\nexport interface Scheduler {\n /** Run the callback on the next frame; returns a handle for cancellation. */\n raf(callback: (timestampMs: number) => void): number;\n /** Cancel a scheduled callback. */\n caf(handle: number): void;\n}\n\n/**\n * Options for the scroller writes — `Element.scrollTo` semantics: absolute\n * offsets (screen px) into the current scroll range (see\n * {@link StageCapability.scrollMetrics}); an omitted axis does not move.\n * `behavior` defaults to 'instant' (the DOM's 'auto'), NOT the stage's\n * `scrollBehavior` setting — that setting governs navigation verbs, and a\n * scrollbar thumb must track the pointer exactly.\n */\nexport interface StageScrollToOptions {\n left?: number;\n top?: number;\n behavior?: ScrollBehaviorKind;\n}\n\n/** Options for navigation intents. */\nexport interface GoToOptions {\n behavior?: ScrollBehaviorKind;\n /** Restore this exact viewpoint instead of fresh placement (per-page memory). */\n viewpoint?: Viewpoint;\n /** Override the landing for THIS navigation only (explicit beats default). */\n arrivalAlign?: Partial<ArrivalAlignment>;\n}\n\n/**\n * One axis of a reveal arrival — `scrollIntoView` vocabulary plus two\n * PDF-protocol necessities:\n * absent → minimal movement: scroll only if the target is off-screen\n * (CSS 'nearest'; today's reveal semantics)\n * 'keep' → this axis does not move AT ALL (PDF /XYZ null coordinate)\n * 'start' → target edge at the viewport start (plus padding)\n * 'center' → target centered\n * 'end' → target edge at the viewport end (minus padding)\n * number 0–1 → target CENTER at this viewport fraction (0.35 = \"top middle\",\n * the browser find-bar feel)\n */\nexport type RevealAnchorValue = 'keep' | 'start' | 'center' | 'end' | number;\n\nexport interface RevealAnchor {\n x?: RevealAnchorValue;\n y?: RevealAnchorValue;\n}\n\n/**\n * What happens to zoom on a reveal — always relative to the reveal's target\n * rect (the whole page when no `rect` is given):\n * 'keep' → pure pan, zoom untouched (search hits, /XYZ null zoom)\n * { level } → explicit factor (/XYZ zoom)\n * 'fit' → the rect fully visible (/FitR; /Fit, /FitB via rect=page/bbox)\n * 'fit-width' → the rect's width fills the viewport (/FitH, /FitBH)\n * 'fit-height' → the rect's height fills the viewport (/FitV, /FitBV)\n */\nexport type RevealZoom = 'keep' | 'fit' | 'fit-width' | 'fit-height' | { level: number };\n\n/**\n * Options for `reveal` — the follower-UI arrival verb (search hits, outline\n * clicks, PDF destinations, \"jump to comment\").\n *\n * With none of `rect`/`zoom`/`anchor` set, reveal keeps its original\n * semantics: minimal movement to make the page visible, cursor untouched.\n * A POSITIONED reveal (any of the three set) is \"you are now looking at\n * THIS spot\": the camera places the target per the anchor, a zoom\n * directive resolves to a concrete level (recorded as the zoom intent),\n * and the cursor follows the camera — while still clamping against the\n * normal camera bounds, so anchors are best-effort near document edges.\n */\nexport interface RevealOptions {\n behavior?: ScrollBehaviorKind;\n /**\n * CONTENT-space target rect on the page (y-down, crop-relative, unscaled\n * points — the same space selection/search rects live in). Absent → the\n * whole page. A zero-size rect is a point (/XYZ).\n */\n rect?: Rect;\n zoom?: RevealZoom;\n anchor?: RevealAnchor;\n}\n\n/** The Stage's public contract: selectors (reads) + intents (the only writers). */\nexport interface StageCapability {\n // ── selectors ──\n camera(): Camera;\n viewport(): Size;\n /**\n * The camera as a NATIVE SCROLLER — the DOM scroll vocabulary in screen px:\n * `scrollTop`/`scrollHeight`/`clientHeight` (and the x twins) mean exactly\n * what they mean on a DOM element; `scrollableX/Y` false ⇔ nothing to scroll\n * on that axis (native: no bar). Derived from the SAME travel range the pan\n * clamp uses — paged flow reads the one-item slice — so a scrollbar built on\n * it can never disagree with where panning stops. On an UNBOUNDED stage the\n * range is the union of the padded content and the current window (the Figma\n * bar): pan away and it grows, the thumb shrinking toward the edge but always\n * remaining a road back. Reference-stable until a field actually changes.\n */\n scrollMetrics(): ScrollMetrics;\n pageCount(): number;\n visiblePages(): VisiblePage[];\n /** The current page (the cursor) — valid in both flows. */\n currentPage(): number;\n /** The display indices of the current item's pages (1 page, or a spread's pages). */\n currentItemPages(): number[];\n /** The full page list with PDF labels — for page thumbnails / worksheet tabs. */\n pages(): Array<{ index: number; pon: PageObjectNumber; label: string | null }>;\n /** The laid-out box for a page by its durable pon. */\n pageRect(pon: PageObjectNumber): VisiblePage | null;\n /**\n * Screen point (this Stage's container px) → the page under it + its content\n * point, or null over a gap. The viewport-level hit-test the interaction hub\n * needs so a single pointer source can drive page-aware features (text\n * selection, annotations) AND cross-page drags.\n */\n pageAt(screen: Point): {\n pon: PageObjectNumber;\n point: Point;\n scale: number;\n /** The hit page's TOTAL display rotation (document /Rotate + view rotation). */\n rotation: PageRotation;\n /** The hit page's zoom relative to its 100% baseline (`transform.zoom`). */\n zoom: number;\n } | null;\n /**\n * Screen point → `pon`'s content space, UNCLAMPED — valid even when the point\n * is outside the page's bounds (coordinates then fall outside `[0, size]`).\n * The frame-stable projection a page-anchored gesture (annotation move/resize)\n * tracks with, where `pageAt` would re-resolve to whatever page is under the\n * cursor (what a cross-page drag like text selection wants). Null when the\n * page isn't currently laid out.\n */\n pointOnPage(pon: PageObjectNumber, screen: Point): Point | null;\n /**\n * Page space (intrinsic PDF points) → world space. Applies the page's placed\n * origin and contentScale — the transform sizing policies introduce. Compose\n * with toScreen for viewport-space overlays anchored to page content.\n */\n pageToWorld(pon: PageObjectNumber, pt: Point): Point | null;\n /**\n * Content rect on a page → this Stage viewport's screen-space AABB. Applies page\n * rotation/contentScale and the current camera. Use for upright viewport overlays\n * that need to frame a selected page region.\n */\n pageRectToScreen(pon: PageObjectNumber, rect: Rect): Rect | null;\n toScreen(world: Point): Point;\n toWorld(screen: Point): Point;\n flow(): FlowMode;\n layout(): LayoutKind;\n spread(): SpreadMode;\n sizing(): SizingMode;\n columns(): GridColumns;\n bounded(): boolean;\n padding(): number;\n gap(): Gap;\n pageFrame(): PageFrame;\n fitAlign(): Alignment;\n arrivalAlign(): ArrivalAlignment;\n zoomAlign(): AlignmentValue;\n anchorAlign(): AlignmentValue;\n direction(): Direction;\n scrollBehavior(): ScrollBehaviorKind;\n /** The lens's view rotation — see {@link StageSettings.viewRotation}. */\n viewRotation(): PageRotation;\n zoomLevel(): number;\n /** The active zoom intent: a fit-mode, or 'custom' for a fixed level. */\n zoomMode(): ZoomModeValue | 'custom';\n /** What I'm looking at + zoom intent — capture for per-page view memory. */\n viewpoint(): Viewpoint;\n /** A snapshot of all settings (handy for building/saving a customer preset). */\n settings(): StageSettings;\n viewState(): StageViewState;\n\n // ── intents ──\n setViewport(vp: Size): void;\n /** Report the device pixel ratio (web: `devicePixelRatio`). The shell calls\n * this once on mount and whenever it changes (e.g. dragging between monitors)\n * so page transforms render crisp. */\n setDevicePixelRatio(dpr: number): void;\n setCamera(c: Camera): void;\n panBy(dxScreen: number, dyScreen: number): void;\n /** `Element.scrollTo` for the camera: absolute offsets into the scroll range\n * (see {@link StageScrollToOptions}) — clamped into it, cursor-synced, zoom\n * untouched (scrolling is a pan in scroller clothing). */\n scrollTo(opts: StageScrollToOptions): void;\n /** `Element.scrollBy`: relative offsets — sugar over `scrollTo`. */\n scrollBy(opts: StageScrollToOptions): void;\n zoomAround(screenPt: Point, factor: number): void;\n /**\n * Bracket a continuous direct-manipulation gesture (touch pan / pinch).\n * While a gesture is open the camera writes stay cheap and visually calm:\n * `zoomAround` defers its zoom-intent PATCH (one per gesture instead of one\n * per event), and the camera-rest detector holds un-rested — device\n * snapping and settle-gated rendering wait for the END of the gesture, not\n * for a 150 ms hesitation inside it. Re-entrant (nesting counts). Opening a\n * gesture cancels any running tween or fling — that is how a finger\n * \"catches\" a moving page.\n *\n * `elastic` opts the gesture into RUBBER-BAND overscroll: pans past the\n * clamp stretch on the iOS resistance curve instead of stopping dead, and\n * ending the gesture (or a fling reaching an edge) springs the camera home\n * on a critically-damped curve. The clamp itself stays the untouched law of\n * REST — elasticity is a transient the gesture is allowed to hold, never a\n * state the camera can settle in. Touch contacts pass it; mouse drags and\n * wheel pans stay rigid (the desktop convention). Default false.\n */\n beginGesture(options?: { elastic?: boolean }): void;\n endGesture(): void;\n /**\n * Momentum scroll: keep panning from a release velocity (screen px/s, the\n * same sign convention as `panBy` deltas), decelerating on UIScrollView's\n * curve. Every other camera verb — including `beginGesture` from the next\n * touch — cancels it. No-op without host frames (SSR/tests without a\n * scheduler jump nowhere: momentum is presentation, not state).\n */\n fling(velocityX: number, velocityY: number): void;\n /** True while the camera is animating (navigation tween or fling) — the\n * input layer reads it to tell a \"catch\" from a tap. */\n cameraInMotion(): boolean;\n /**\n * The touch double-tap toggle: zoomed out → animate to ~2.5× the automatic\n * fit around `screenPt`; already zoomed past that → animate back to the fit\n * level, same focal point. Lands as a fixed zoom level either way.\n */\n doubleTapZoom(screenPt: Point): void;\n zoomIn(): void;\n zoomOut(): void;\n zoomTo(spec: ZoomSpec): void;\n fitWidth(): void;\n fitPage(): void;\n /** Fit the whole scene (every page) in view — the construction overview. */\n fitAll(): void;\n /** Fit width but never upscale past 100% (Adobe's \"Automatic\"). */\n automatic(): void;\n /**\n * Re-resolve the active zoom intent and re-place against the CURRENT scene,\n * preserving the anchored page-point. Call after the page geometry changes\n * underneath the lens — rotate/move/delete — so fit/pixel zoom modes\n * (`fitPage`, `pageWidth`, …) recompute against the new footprint. A no-op\n * before the first placement; for a fixed `{ level }` zoom it just re-anchors.\n * Wired automatically to the document's page-registry revision; exposed for\n * any external geometry change a host wants to react to.\n */\n refit(): void;\n /** Go to a page. Fresh arrival places by the unit rule; pass `viewpoint` to restore. */\n goToPage(pageIndex: number, opts?: GoToOptions): void;\n /**\n * The follower-UI arrival verb (thumbnail sidebars, search hits, outline\n * clicks, PDF destinations). Bare: make the page visible with minimal\n * movement — zero if it already is, cursor untouched (scrollIntoView\n * semantics; paged flow delegates to navigation since the page isn't in\n * the slice). POSITIONED (rect/zoom/anchor set — see {@link RevealOptions}):\n * place the target at the anchor, optionally re-zooming; the cursor\n * follows the camera.\n */\n reveal(pageIndex: number, opts?: RevealOptions): void;\n /** Step forward by the navigation unit (the item if it fits the viewport, else the page). */\n next(opts?: GoToOptions): void;\n /** Step backward by the navigation unit. */\n prev(opts?: GoToOptions): void;\n /**\n * This lens's identity — the stage plugin id it was registered under\n * ('stage' for the main lens; a custom id per additional lens). The surface\n * binding stamps it on every pointer sample (`PointerSample.source`) and\n * lens-scoped interaction handlers register under it, so two stages on one\n * document can never capture each other's input.\n */\n lensId(): string;\n /** Set any subset of settings at once — ONE anchor-preserving update. The way to\n * apply a customer preset: `update(myPreset)`. Writes the responsive BASE:\n * a matching rule's key wins until its rule stops matching. */\n update(patch: Partial<StageSettings>): void;\n /** Replace the responsive rules (see {@link ResponsiveRule}); re-resolves\n * immediately with the usual anchor-preserving reactions. */\n setResponsive(rules: readonly ResponsiveRule[]): void;\n /** Is the named responsive rule currently matching? Reactive: recomputed\n * whenever the box crosses a rule boundary. */\n matches(name: string): boolean;\n /** Names of all currently-matching rules, in source order. */\n activeRules(): readonly string[];\n setFlow(flow: FlowMode): void;\n setLayout(layout: LayoutKind): void;\n setSpread(spread: SpreadMode): void;\n setSizing(sizing: SizingMode): void;\n setColumns(columns: GridColumns): void;\n setBounded(bounded: boolean): void;\n setPadding(padding: number): void;\n setGap(gap: Gap): void;\n setPageFrame(pageFrame: PageFrame): void;\n setFitAlign(fitAlign: Alignment): void;\n setArrivalAlign(arrivalAlign: ArrivalAlignment): void;\n setZoomAlign(zoomAlign: AlignmentValue): void;\n setAnchorAlign(anchorAlign: AlignmentValue): void;\n setDirection(direction: Direction): void;\n /** Set the lens's view rotation to an absolute quarter-turn — see\n * {@link StageSettings.viewRotation}. An anchor-preserving reframe, like a\n * page rotation: the spot you were looking at stays put (`anchorAlign`). */\n setViewRotation(viewRotation: PageRotation): void;\n /** Rotate the view a quarter-turn from where it is (the toolbar verb) —\n * relative sugar over {@link setViewRotation}, mirroring page-edit's `rotateBy`. */\n rotateView(delta: 90 | -90): void;\n setScrollBehavior(behavior: ScrollBehaviorKind): void;\n applyViewState(view: StageViewState): void;\n /** Offer a candidate initial view; the highest-priority non-null wins at placement. */\n provideInitialView(priority: number, provider: () => StageViewState | null): void;\n /** Resolve the registered providers once (else reset). Called when the viewport is ready. */\n placeInitial(): void;\n /** Back to the start: page 0, placed by the unit rule at the current zoom intent. */\n resetView(): void;\n}\n\nexport interface StageConfig extends Partial<StageSettings> {\n /** Override the host timing seam (tests/SSR). Defaults to browser rAF. */\n scheduler?: Scheduler;\n /**\n * Container queries for the settings bag (see {@link ResponsiveRule}).\n * Defaults to `DEFAULT_RESPONSIVE` (compact containers get the thin phone\n * gutter); pass `[]` to opt out entirely.\n */\n responsive?: readonly ResponsiveRule[];\n}\n\nexport const StageToken = createCapabilityToken<StageCapability>('stage');\n","import { definePlugin } from '@embedpdf/core';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { createStageCapability } from './capability';\nimport { registerStageEffects } from './effects';\nimport { initialStageState, stageReducer } from './reducer';\nimport { StageToken } from './types';\nimport type { StageAction, StageCapability, StageConfig, StageState } from './types';\n\n/**\n * Options for registering a stage instance. The Stage is a LENS, not a singleton:\n * a document may be viewed through several stages at once (the main view, a wrapped\n * thumbnail sidebar, …), each with independent camera/settings. Register additional\n * lenses by giving them their own `id` + `token`:\n *\n * const ThumbsToken = createCapabilityToken<StageCapability>('stage-thumbs');\n * plugins = [\n * stagePlugin(), // main lens\n * stagePlugin({ id: 'stage-thumbs', token: ThumbsToken,\n * layout: 'grid', columns: 'auto', zoom: { level: 0.2 } }),\n * ];\n *\n * Everything multiplexes automatically: state slices, capabilities, and teardown\n * are already keyed by plugin-id × document in the kernel.\n */\nexport interface StagePluginOptions extends StageConfig {\n id?: string;\n token?: CapabilityToken<StageCapability>;\n}\n\n/**\n * Wires the parts into a kernel plugin. This file is the \"manifest\": it says what\n * the plugin IS (id, token, state, reducer, capability) — the how lives in the\n * sibling files.\n */\nexport const stagePlugin = (options: StagePluginOptions = {}) => {\n const { id = 'stage', token = StageToken, ...config } = options;\n return definePlugin<StageState, StageAction, StageCapability>({\n id,\n token,\n scope: 'document', // one instance of THIS lens per open document\n initialState: () => initialStageState(config),\n reduce: stageReducer,\n capability: (ctx) => createStageCapability(ctx, config),\n // Interaction opt-in lives with the SAMPLE SOURCE, not here: the surface\n // binding (`<Stage interaction>` / `createStageSurface`) both forwards\n // pointer samples AND registers this lens's scroll handler, lens-scoped —\n // one knob, and a handler can never exist without its input stream.\n // INITIAL placement is deliberately NOT an effect: it's LEVEL-triggered\n // inside the capability's setViewport (place when the stage first learns a\n // real size), so it cannot race effect registration. Other plugins only\n // *offer* initial views via provideInitialView; placeInitial resolves them\n // by priority. The one effect below is STEADY-STATE — it re-fits when the\n // page registry mutates (rotate/move/delete) and so has no such race.\n effects: (ctx) => registerStageEffects(ctx, token),\n });\n};\n","/**\n * PDF destination → reveal: the pure translator between the PDF protocol's\n * navigation vocabulary (ISO 32000-1 §12.3.2.2) and the Stage's arrival\n * primitive. Outline clicks, link annotations, and `/OpenAction` all\n * resolve (engine-side) to an explicit `PdfDestination`; this maps it onto\n * ONE `reveal(pageIndex, options)` call — no destination-specific camera\n * code exists anywhere else.\n *\n * The whole protocol collapses onto three knobs:\n * rect — what to look at (a point for /XYZ, a rect for /FitR, the\n * page for /Fit, the content bounding box for /FitB*)\n * zoom — 'keep' | {level} | 'fit' | 'fit-width' | 'fit-height'\n * anchor — where it lands; 'keep' encodes the spec's null-means-retain\n *\n * Coordinates convert PDF user space (y-up, absolute) → content space\n * (y-down, crop-relative) through the SAME `pdfToContent` matrix selection\n * and search use, so a destination and an overlay can never disagree.\n */\nimport { applyPoint, pageGeometry } from '@embedpdf/core-geometry';\nimport type { Rect } from '@embedpdf/core-geometry';\nimport type { PageLayout, PdfDestination } from '@embedpdf/core';\nimport type { RevealOptions } from './types';\n\nexport interface DestinationReveal {\n /** Display index for `reveal()` — from the layout row. */\n pageIndex: number;\n options: RevealOptions;\n}\n\n/**\n * Translate one explicit destination against its target page's layout.\n * `bbox` is the page's content BOUNDING box in CONTENT space, for the\n * `/FitB*` kinds — pass it when known; the crop box is the spec-tolerated\n * fallback until the engine exposes it.\n */\nexport function destinationToReveal(\n dest: PdfDestination,\n layout: PageLayout,\n bbox?: Rect,\n): DestinationReveal {\n const geo = pageGeometry(\n { crop: layout.boxes.crop, rotation: layout.rotation, userUnit: layout.userUnit },\n 1,\n );\n const crop = layout.boxes.crop;\n const toContent = (x: number, y: number) => applyPoint(geo.pdfToContent, { x, y });\n const page: Rect = { x: 0, y: 0, width: layout.size.width, height: layout.size.height };\n const box = bbox ?? page;\n\n const options = ((): RevealOptions => {\n switch (dest.kind) {\n case 'xyz': {\n const hasLeft = dest.left != null;\n const hasTop = dest.top != null;\n // Null axes keep the current camera value — the coordinate fed in\n // for them is inert (the 'keep' anchor never reads it).\n const p = toContent(dest.left ?? crop.left, dest.top ?? crop.top);\n return {\n rect: { x: p.x, y: p.y, width: 0, height: 0 },\n anchor: { x: hasLeft ? 'start' : 'keep', y: hasTop ? 'start' : 'keep' },\n // A /XYZ zoom of 0 means null means \"retain current\".\n zoom: dest.zoom != null && dest.zoom !== 0 ? { level: dest.zoom } : 'keep',\n };\n }\n case 'fit':\n return { zoom: 'fit' }; // whole page; slack axis centers\n case 'fitH': {\n const hasTop = dest.top != null;\n const y = hasTop ? toContent(crop.left, dest.top!).y : 0;\n return {\n rect: { x: 0, y, width: page.width, height: 0 },\n zoom: 'fit-width',\n anchor: { y: hasTop ? 'start' : 'keep' },\n };\n }\n case 'fitV': {\n const hasLeft = dest.left != null;\n const x = hasLeft ? toContent(dest.left!, crop.top).x : 0;\n return {\n rect: { x, y: 0, width: 0, height: page.height },\n zoom: 'fit-height',\n anchor: { x: hasLeft ? 'start' : 'keep' },\n };\n }\n case 'fitR': {\n const tl = toContent(dest.left, dest.top);\n const br = toContent(dest.right, dest.bottom);\n return {\n rect: {\n x: Math.min(tl.x, br.x),\n y: Math.min(tl.y, br.y),\n width: Math.abs(br.x - tl.x),\n height: Math.abs(br.y - tl.y),\n },\n zoom: 'fit',\n };\n }\n case 'fitB':\n return { rect: box, zoom: 'fit' };\n case 'fitBH': {\n const hasTop = dest.top != null;\n const y = hasTop ? toContent(crop.left, dest.top!).y : box.y;\n return {\n rect: { x: box.x, y, width: box.width, height: 0 },\n zoom: 'fit-width',\n anchor: { y: hasTop ? 'start' : 'keep' },\n };\n }\n case 'fitBV': {\n const hasLeft = dest.left != null;\n const x = hasLeft ? toContent(dest.left!, crop.top).x : box.x;\n return {\n rect: { x, y: box.y, width: 0, height: box.height },\n zoom: 'fit-height',\n anchor: { x: hasLeft ? 'start' : 'keep' },\n };\n }\n }\n })();\n\n return { pageIndex: layout.index, options };\n}\n","import type { InteractionCapability, InteractionHandler } from '@embedpdf/plugin-interaction';\nimport type { StageCapability } from './types';\n\nexport interface ScrollHandlerOptions {\n /**\n * Let a drag over a page GAP pan regardless of the active tool — and show a grab\n * cursor there — so the gutter always pans (v2 parity, and the natural feel:\n * outside a page there's nothing to draw/select on). On-page behaviour is\n * untouched; the tool's own gesture still owns page space. Default true.\n */\n panFallback?: boolean;\n}\n\n/**\n * Pan-the-camera as an interaction handler. Live under the built-in `pan` tool\n * (the `scroll` tag) and — with `panFallback` — under every tool, where it then\n * captures ONLY page-gap drags (on-page downs belong to the active tool, which\n * claims them first at higher priority). It uses the sample's viewport delta, so\n * it pans over the whole viewport (pages and gaps).\n *\n * Cursor feel: a closed hand (`grabbing`) while dragging; an open hand (`grab`)\n * hovering a gap under a non-pan tool. The pan tool's own resting `grab` comes\n * from its `Tool.cursor`, so hovering it needs no claim here.\n */\nexport function createScrollHandler(\n stage: StageCapability,\n interaction: InteractionCapability,\n options: ScrollHandlerOptions = {},\n): InteractionHandler {\n const panFallback = options.panFallback ?? true;\n let last = { x: 0, y: 0 };\n const isPanTool = (): boolean => interaction.activeTool().enables.has('scroll');\n return {\n id: 'stage-scroll',\n priority: 10, // below page-aware handlers (selection/annotation) — they claim first\n enabledFor: (tool) => tool.enables.has('scroll') || panFallback,\n onDown: (s) => {\n // Fallback: a non-pan tool pans only over a gap. On a page, decline so the\n // tool's own (higher-priority) handler keeps the down it already captured.\n if (!isPanTool() && s.page) return false;\n last = s.viewport;\n interaction.setCursor('stage-grab', 'grabbing', 40); // closed hand while panning\n return true; // capture the drag\n },\n onMove: (s) => {\n stage.panBy(s.viewport.x - last.x, s.viewport.y - last.y);\n last = s.viewport;\n },\n onUp: () => interaction.setCursor('stage-grab', null),\n onHover: (s) => {\n // Open hand over a gap when a non-pan tool would fall back to pan there;\n // cleared over a page (the tool's cursor shows). The pan tool skips this —\n // its resting `grab` is the tool cursor.\n if (panFallback && !isPanTool()) {\n interaction.setCursor('stage-pan-fallback', s.page ? null : 'grab', 5);\n }\n },\n };\n}\n"],"mappings":"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"}
|