@embedpdf/plugin-render 3.0.0-next.4 → 3.0.0-next.6

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/paint-plan.ts","../src/raster-store.ts","../src/tiles.ts","../src/tile-manager.ts","../src/capability.ts","../src/effects.ts","../src/reducer.ts","../src/types.ts","../src/render.plugin.ts"],"sourcesContent":["import type { PageImageHandle } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n/**\n * The demand a page HOST supplies through dependency inversion:\n * plugin-render defines the shape, producers fill it. The Stage's page\n * host knows the camera and supplies `visibleRect`/`velocity`; a\n * stage-less `<PageView>` supplies neither — absent `visibleRect` means\n * \"assume the whole page is visible\", which a thumbnail-sized demand\n * turns into \"no tiles engage\" by arithmetic, not configuration.\n */\nexport interface PageViewDemand {\n /** Desired device pixels across the page's unrotated content width. */\n desiredDeviceWidth: number;\n /** Visible page region — y-down page points. ABSENT = whole page. */\n visibleRect?: Rect;\n /** Scroll velocity in page points/s — prefetch direction bias only. */\n velocity?: { dx: number; dy: number };\n}\n\n/**\n * One tile the layer should have in the DOM. `key` is the reconciliation\n * identity (stable across plan recomputes — keyed lists preserve the DOM\n * node, which IS the retention mechanism); `rect` is y-down page points\n * inside the unrotated content box; `z` stacks by resolution so arriving\n * sharper tiles occlude retained coarser ones per region.\n */\nexport interface TilePaintSource {\n key: string;\n scale: number;\n rect: Rect;\n z: number;\n handle: PageImageHandle;\n}\n\n/**\n * What a TileLayer paints right now. `paint` draws ONLY from resolved\n * rasters — retained generations live here until the release rules fire;\n * \"loading\" never reaches the DOM. `fetching` is diagnostic (badge/tests).\n */\nexport interface TilePaintPlan {\n /** Tiles engaged at all? False = the base rung fully covers demand. */\n engaged: boolean;\n paint: TilePaintSource[];\n /** Keys currently in flight (want-set members not yet resolved). */\n fetching: string[];\n /** Memoization stamp — a new object appears only when this changes. */\n stamp: string;\n}\n\nexport const EMPTY_TILE_PLAN: TilePaintPlan = {\n engaged: false,\n paint: [],\n fetching: [],\n stamp: 'empty',\n};\n\n/** Resolved tiling configuration (renderPlugin options + defaults). */\nexport interface TilingConfig {\n /** Tile edge in device px. Default 512. */\n tileSize?: number;\n /**\n * Client pyramid used while the policy doesn't advertise `tiles`.\n * Default `[1, 2, 4, 8, 16, 32]` — ×2 aligned.\n */\n scales?: readonly number[];\n /**\n * Sharpness deficit (desired ÷ supplied-by-base) above which tiles\n * engage. Default 1.25 — small CSS upscales read fine; past that the\n * ladder cap is visible and the pyramid takes over.\n */\n engageAt?: number;\n /** Prefetch ring around the visible rect. */\n prefetch?: {\n /** Extra coverage per side, in fractions of the visible rect. Default 0.5. */\n margin?: number;\n /** Stretch the ring toward scroll direction. Default true. */\n velocityBias?: boolean;\n };\n /**\n * Hysteresis for LEVEL changes (a zoom settling between pyramid rungs):\n * tile fetches for a NEW level wait this long; pan-driven fetches at the\n * current level fire immediately. Default 150ms; 0 disables.\n */\n settleMs?: number;\n}\n\nexport interface ResolvedTiling {\n tileSize: number;\n scales: readonly number[];\n engageAt: number;\n prefetchMargin: number;\n velocityBias: boolean;\n settleMs: number;\n}\n\nexport function resolveTiling(config: TilingConfig | undefined): ResolvedTiling {\n return {\n tileSize: config?.tileSize ?? 512,\n scales: config?.scales ?? [1, 2, 4, 8, 16, 32],\n engageAt: config?.engageAt ?? 1.25,\n prefetchMargin: config?.prefetch?.margin ?? 0.5,\n velocityBias: config?.prefetch?.velocityBias ?? true,\n settleMs: config?.settleMs ?? 150,\n };\n}\n","import type { PageImageHandle } from '@embedpdf/core';\n\n/**\n * Client-side mirror of the server's one-door read-through: per-key\n * SINGLEFLIGHT + LRU over encoded page images.\n *\n * The key is the raster's canonical identity (conformed viewport +\n * annotations flag + epoch — see `renderSourceKey`), so the lattice does the\n * caching work: every ask inside one rung is the same key, and rung\n * re-crossings (zoom 1280 → 2560 → back) resolve instantly from the LRU\n * instead of re-rendering. Epoch bumps mint new keys; stale-epoch entries\n * age out through the LRU (they are never re-requested).\n *\n * Consumer aborts are REFCOUNTED: a caller abandoning a fetch (camera moved,\n * layer unmounted) detaches without killing it for other consumers; the\n * underlying engine call aborts only when the LAST in-flight consumer leaves.\n * Resolved entries are kept (evictable by LRU) — an abort after resolution\n * is a no-op by design.\n */\nexport class RasterStore {\n private readonly entries = new Map<string, Entry>();\n private tick = 0;\n\n constructor(private readonly maxEntries = 48) {}\n\n acquire(\n key: string,\n fetch: (signal: AbortSignal) => Promise<PageImageHandle>,\n signal?: AbortSignal,\n ): Promise<PageImageHandle> {\n const existing = this.entries.get(key);\n if (existing) {\n existing.used = ++this.tick;\n return this.attach(key, existing, signal);\n }\n\n const abort = new AbortController();\n const entry: Entry = {\n promise: undefined as unknown as Promise<PageImageHandle>,\n refs: 0,\n abort,\n used: ++this.tick,\n };\n entry.promise = fetch(abort.signal).then(\n (handle) => {\n entry.handle = handle;\n return handle;\n },\n (err) => {\n // Failed fetches must not be sticky — drop so the next ask retries.\n if (this.entries.get(key) === entry) this.entries.delete(key);\n throw err;\n },\n );\n // Swallow the shared promise's rejection when no consumer is attached\n // (everyone aborted) — each consumer observes it via its own attachment.\n entry.promise.catch(() => {});\n this.entries.set(key, entry);\n this.evict();\n return this.attach(key, entry, signal);\n }\n\n /** Resolved handle if present — synchronous peek for paint-time reuse. */\n peek(key: string): PageImageHandle | undefined {\n const entry = this.entries.get(key);\n if (entry?.handle) entry.used = ++this.tick;\n return entry?.handle;\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n private attach(key: string, entry: Entry, signal?: AbortSignal): Promise<PageImageHandle> {\n if (signal?.aborted) return Promise.reject(abortReason(signal));\n // EVERY consumer holds a ref while the fetch is in flight — including\n // signal-less ones (\"I never abandon\"), otherwise a signal-bearing\n // sibling's abort could kill a fetch someone else is still awaiting.\n entry.refs += 1;\n if (!signal) {\n const release = () => {\n entry.refs -= 1;\n };\n entry.promise.then(release, release);\n return entry.promise;\n }\n let settled = false;\n return new Promise<PageImageHandle>((resolve, reject) => {\n const onAbort = () => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n // Last live consumer walking away from an UNRESOLVED fetch kills it;\n // resolved entries stay for the LRU (that's the cache).\n if (entry.refs <= 0 && entry.handle === undefined) {\n entry.abort.abort(abortReason(signal));\n if (this.entries.get(key) === entry) this.entries.delete(key);\n }\n reject(abortReason(signal));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n entry.promise.then(\n (handle) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n resolve(handle);\n },\n (err) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n }\n\n private evict(): void {\n if (this.entries.size <= this.maxEntries) return;\n // Oldest-first over evictable entries: resolved and consumer-free.\n const candidates = [...this.entries.entries()]\n .filter(([, e]) => e.refs <= 0 && e.handle !== undefined)\n .sort((a, b) => a[1].used - b[1].used);\n for (const [key] of candidates) {\n if (this.entries.size <= this.maxEntries) return;\n this.entries.delete(key);\n }\n }\n}\n\ninterface Entry {\n promise: Promise<PageImageHandle>;\n handle?: PageImageHandle;\n refs: number;\n abort: AbortController;\n used: number;\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('aborted', 'AbortError');\n}\n","import type { PdfRect } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n// One owner for space math: rect intersection lives in core-geometry\n// (the stage's visibility computation uses the same helper).\nexport { intersectRects } from '@embedpdf/core-geometry';\n\n/**\n * Pure tile-grid math.\n *\n * The pyramid is ALIGNED: one origin (the page's top-left), a fixed tile\n * size in device pixels, and ×2 scale steps. Alignment is what makes the\n * retention bookkeeping index arithmetic instead of rectangle geometry —\n * a level-s tile is covered by exactly the level-2s tiles whose indices\n * fall in its doubled range, so occlusion/release checks are O(1).\n *\n * Spaces: tile COORDS live on the device grid at their level's scale\n * (tileSize² device px per tile, constant per-job cost by construction);\n * PAINT rects are y-down page points (the viewer convention — the layer\n * multiplies by one container transform); ENGINE rects are y-up PDF user\n * space (the `target: {kind:'rect'}` contract).\n */\n\nexport interface PageSizePt {\n width: number;\n height: number;\n}\n\nexport interface TileCoord {\n ix: number;\n iy: number;\n}\n\nexport interface TileGrid {\n /** Device px per PDF point at this pyramid level. */\n scale: number;\n /** Tile edge in device px. */\n tileSize: number;\n cols: number;\n rows: number;\n}\n\nexport function tileGrid(page: PageSizePt, scale: number, tileSize: number): TileGrid {\n return {\n scale,\n tileSize,\n cols: Math.max(1, Math.ceil((page.width * scale) / tileSize)),\n rows: Math.max(1, Math.ceil((page.height * scale) / tileSize)),\n };\n}\n\n/** One tile's edge in page points at its level. */\nconst tileSpanPt = (grid: TileGrid): number => grid.tileSize / grid.scale;\n\n/**\n * The tiles of `grid` intersecting a y-down page-point rect, clamped to\n * the page. An empty intersection yields an empty list.\n */\nexport function tilesInRect(grid: TileGrid, page: PageSizePt, rect: Rect): TileCoord[] {\n const span = tileSpanPt(grid);\n const x0 = Math.max(0, Math.floor(rect.x / span));\n const y0 = Math.max(0, Math.floor(rect.y / span));\n const x1 = Math.min(grid.cols - 1, Math.ceil((rect.x + rect.width) / span) - 1);\n const y1 = Math.min(grid.rows - 1, Math.ceil((rect.y + rect.height) / span) - 1);\n const out: TileCoord[] = [];\n for (let iy = y0; iy <= y1; iy++) {\n for (let ix = x0; ix <= x1; ix++) out.push({ ix, iy });\n }\n return out;\n}\n\n/** Paint rect: y-down page points, clamped to the page edge. */\nexport function tilePaintRect(grid: TileGrid, page: PageSizePt, c: TileCoord): Rect {\n const span = tileSpanPt(grid);\n const x = c.ix * span;\n const y = c.iy * span;\n return {\n x,\n y,\n width: Math.min(span, page.width - x),\n height: Math.min(span, page.height - y),\n };\n}\n\n/** Engine target rect: y-UP PDF user space (top > bottom). */\nexport function tileEngineRect(grid: TileGrid, page: PageSizePt, c: TileCoord): PdfRect {\n const paint = tilePaintRect(grid, page, c);\n return {\n left: paint.x,\n right: paint.x + paint.width,\n top: page.height - paint.y,\n bottom: page.height - (paint.y + paint.height),\n };\n}\n\n/**\n * Does a set of PAINTED want-level tiles cover `region` (y-down points)?\n * The retention release check: a retained source may leave the paint list\n * once its visible footprint answers true here. Pure index arithmetic on\n * the aligned grid — the whole reason the pyramid is aligned.\n */\nexport function regionCovered(\n grid: TileGrid,\n page: PageSizePt,\n region: Rect,\n isPainted: (c: TileCoord) => boolean,\n): boolean {\n if (region.width <= 0 || region.height <= 0) return true;\n for (const c of tilesInRect(grid, page, region)) {\n if (!isPainted(c)) return false;\n }\n return true;\n}\n\n/**\n * Inflate a visible rect by a prefetch margin (fractions of the rect's own\n * size per side), optionally velocity-biased: the margin stretches toward\n * the direction of travel and shrinks behind it. Pure — the producer's\n * velocity is just data.\n */\nexport function inflateRect(\n rect: Rect,\n margin: number,\n velocity?: { dx: number; dy: number },\n): Rect {\n const mx = rect.width * margin;\n const my = rect.height * margin;\n let left = mx;\n let right = mx;\n let up = my;\n let down = my;\n if (velocity) {\n // Direction buckets only — raw magnitudes would make the want set\n // churn with every pointer sample.\n if (velocity.dx > 0) {\n right = mx * 2;\n left = mx / 2;\n } else if (velocity.dx < 0) {\n left = mx * 2;\n right = mx / 2;\n }\n if (velocity.dy > 0) {\n down = my * 2;\n up = my / 2;\n } else if (velocity.dy < 0) {\n up = my * 2;\n down = my / 2;\n }\n }\n return {\n x: rect.x - left,\n y: rect.y - up,\n width: rect.width + left + right,\n height: rect.height + up + down,\n };\n}\n\n/** Snap UP through a pyramid's sorted scales; cap at the top. */\nexport function snapToPyramid(scales: readonly number[], needed: number): number {\n const sorted = [...scales].sort((a, b) => a - b);\n return sorted.find((s) => s >= needed) ?? sorted[sorted.length - 1]!;\n}\n","import {\n snapFullPageViewport,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PdfRect,\n} from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\nimport {\n EMPTY_TILE_PLAN,\n type PageViewDemand,\n type ResolvedTiling,\n type TilePaintPlan,\n type TilePaintSource,\n} from './paint-plan';\nimport type { RasterStore } from './raster-store';\nimport {\n inflateRect,\n intersectRects,\n regionCovered,\n snapToPyramid,\n tileGrid,\n tilesInRect,\n tileEngineRect,\n tilePaintRect,\n type PageSizePt,\n type TileCoord,\n type TileGrid,\n} from './tiles';\n\n/**\n * The tile retention state machine. One instance per\n * plugin instance (per document), all levels and pages in one place, over\n * the SAME RasterStore the base renders use.\n *\n * Invariant it enforces: every screen region paints the sharpest PAINTED\n * pixels available; quality per region only goes up until the want set\n * resolves. Mechanics:\n * - want vs paint: `plan()` schedules fetches for the want set (P0\n * visible first, P1 prefetch when P0 is fully resolved) and returns a\n * paint list drawn ONLY from resolved entries — current level AND\n * retained older generations.\n * - release-on-occlusion: when a want-level tile reports PAINTED (the\n * layer's img onload — the decode boundary), retained sources whose\n * visible footprint is covered by painted want tiles leave the paint\n * list. Their bytes stay in the RasterStore (demotion, not eviction) —\n * zoom-back re-promotes from cache.\n * - epoch exception: an invalidation bump means retained pixels are\n * WRONG — everything of the old epoch drops immediately.\n *\n * `plan()` is called from selectors: it MUST NOT dispatch. Fetch kickoff\n * is idempotent (the store singleflights); resolution handlers dispatch\n * the wake-up (`onAdvance`) that makes subscribed layers recompute.\n */\nexport class TileManager {\n private readonly pages = new Map<number, PageTileState>();\n\n constructor(\n private readonly deps: {\n store: RasterStore;\n config: ResolvedTiling;\n /** The document fact off the kernel registry — never null: the kernel\n * materializes it (continuous fallback) before the doc publishes. */\n getPolicy(): EngineRenderPolicy;\n getPageSize(pon: number): PageSizePt | undefined;\n getEpoch(pon: number, includeAnnotations: boolean): number;\n fetchTile(\n pon: number,\n rect: PdfRect,\n scale: number,\n includeAnnotations: boolean,\n signal: AbortSignal,\n ): Promise<PageImageHandle>;\n /** Wake subscribed layers (dispatches PAINT_ADVANCED outside plan()). */\n onAdvance(pon: number): void;\n },\n ) {}\n\n plan(pon: number, demand: PageViewDemand, includeAnnotations: boolean): TilePaintPlan {\n const { config } = this.deps;\n const policy = this.deps.getPolicy();\n const page = this.deps.getPageSize(pon);\n if (!page) return EMPTY_TILE_PLAN;\n\n const epoch = this.deps.getEpoch(pon, includeAnnotations);\n const state = this.pageState(pon);\n\n // Epoch exception: old-epoch pixels are WRONG, not blurry — drop all.\n if (state.epoch !== epoch) {\n this.abortAll(state);\n state.entries.clear();\n state.epoch = epoch;\n state.wantScale = null;\n state.planCache = null;\n }\n\n // Engagement: deficit of the base rung vs demand. Under `continuous`\n // the base supplies exactly what was asked — deficit 1, never engaged\n // (v2 exactness); memory protection there is the POLICY's job\n // (localEngine({ renderPolicy })), not this manager's.\n const baseSupplied =\n policy.kind === 'lattice'\n ? widthOf(snapFullPageViewport(policy, { kind: 'width', width: demand.desiredDeviceWidth }))\n : demand.desiredDeviceWidth;\n const deficit = demand.desiredDeviceWidth / baseSupplied;\n if (deficit <= config.engageAt) {\n if (state.entries.size || state.wantScale !== null) {\n this.abortAll(state);\n state.entries.clear();\n state.wantScale = null;\n state.planCache = null;\n }\n return EMPTY_TILE_PLAN;\n }\n\n // Level selection: use the advertised policy pyramid when available,\n // otherwise use client defaults — same snapped requests either way.\n const pyramid = policy.kind === 'lattice' && policy.tiles ? policy.tiles.scales : config.scales;\n const wantScale = snapToPyramid(pyramid, demand.desiredDeviceWidth / page.width);\n const grid = tileGrid(page, wantScale, config.tileSize);\n\n const visible = demand.visibleRect ?? { x: 0, y: 0, width: page.width, height: page.height };\n state.lastVisible = visible;\n const p0 = tilesInRect(grid, page, visible);\n const ring = inflateRect(\n visible,\n config.prefetchMargin,\n config.velocityBias ? demand.velocity : undefined,\n );\n const p0Keys = new Set(p0.map((c) => coordKey(c)));\n const p1 = tilesInRect(grid, page, ring).filter((c) => !p0Keys.has(coordKey(c)));\n\n this.schedule(pon, state, page, grid, wantScale, includeAnnotations, epoch, p0, p1);\n\n // Paint list: resolved entries intersecting the visible rect, coarser\n // levels first (painter's algorithm — sharper occludes per region).\n // The memo key holds SNAPPED values only (identity law): raw demand\n // width matters solely through engagement and the level, both already\n // folded in — zoom inside a level is plan-stable by construction.\n const demandKey = `${wantScale}|${rectKey(visible)}|e${epoch}`;\n if (\n state.planCache &&\n state.planCache.demandKey === demandKey &&\n state.planCache.version === state.version\n ) {\n return state.planCache.plan;\n }\n const paint: TilePaintSource[] = [];\n const fetching: string[] = [];\n for (const entry of state.entries.values()) {\n const visIntersect = intersectRects(entry.rect, visible);\n if (entry.handle) {\n if (visIntersect.width > 0 && visIntersect.height > 0) {\n paint.push({\n key: entry.key,\n scale: entry.scale,\n rect: entry.rect,\n z: zOf(pyramid, entry.scale),\n handle: entry.handle,\n });\n }\n } else if (entry.scale === wantScale) {\n fetching.push(entry.key);\n }\n }\n paint.sort((a, b) => a.z - b.z || a.key.localeCompare(b.key));\n const plan: TilePaintPlan = {\n engaged: true,\n paint,\n fetching: fetching.sort(),\n stamp: `${demandKey}|v${state.version}`,\n };\n state.planCache = { demandKey, version: state.version, plan };\n return plan;\n }\n\n /** The layer's decode-boundary report: this key's <img> actually painted. */\n sourcePainted(pon: number, key: string): void {\n const state = this.pages.get(pon);\n const entry = state?.entries.get(key);\n if (!state || !entry || entry.painted) return;\n entry.painted = true;\n this.releaseOccluded(state, entry);\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n }\n\n /** A lens unmounted its TileLayer: stop fetching, drop bookkeeping.\n * Resolved bytes stay in the RasterStore for a re-mount. */\n releasePage(pon: number): void {\n const state = this.pages.get(pon);\n if (!state) return;\n this.abortAll(state);\n this.pages.delete(pon);\n }\n\n private pageState(pon: number): PageTileState {\n let state = this.pages.get(pon);\n if (!state) {\n state = {\n epoch: -1,\n wantScale: null,\n entries: new Map(),\n version: 0,\n planCache: null,\n settleTimer: null,\n pendingLevel: null,\n lastVisible: null,\n };\n this.pages.set(pon, state);\n }\n return state;\n }\n\n private schedule(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n wantScale: number,\n includeAnnotations: boolean,\n epoch: number,\n p0: TileCoord[],\n p1: TileCoord[],\n ): void {\n const { config } = this.deps;\n const firstEngage = state.wantScale === null;\n const levelChanged = state.wantScale !== null && state.wantScale !== wantScale;\n state.wantScale = wantScale;\n\n // Abort in-flight fetches that left the want set (pan away, level\n // change) — resolved entries are retention's business, not ours.\n const wanted = new Set(\n [...p0, ...p1].map((c) => this.tileKey(pon, wantScale, c, includeAnnotations, epoch)),\n );\n for (const [key, entry] of state.entries) {\n if (entry.handle === undefined && !wanted.has(key)) {\n entry.abort?.abort();\n state.entries.delete(key);\n }\n }\n\n const kickoff = () => {\n const allP0Ready = this.ensureFetches(pon, state, page, grid, includeAnnotations, epoch, p0);\n // P1 strictly after P0: prefetch never competes with on-screen tiles.\n if (allP0Ready) this.ensureFetches(pon, state, page, grid, includeAnnotations, epoch, p1);\n };\n\n // Level-change hysteresis: a zoom passing THROUGH levels shouldn't\n // fetch each intermediate one. First engagement fires immediately —\n // there's nothing on screen above the base yet.\n if (levelChanged && config.settleMs > 0) {\n state.pendingLevel = wantScale;\n if (state.settleTimer !== null) clearTimeout(state.settleTimer);\n state.settleTimer = setTimeout(() => {\n state.settleTimer = null;\n if (state.pendingLevel === wantScale) kickoff();\n }, config.settleMs);\n return;\n }\n if (state.settleTimer !== null && !levelChanged && !firstEngage) {\n // Same level again before the timer fired — the zoom came back;\n // cancel the pending level fetch.\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n state.pendingLevel = null;\n }\n kickoff();\n }\n\n /** Start missing fetches; true when every coord is already resolved. */\n private ensureFetches(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n includeAnnotations: boolean,\n epoch: number,\n coords: TileCoord[],\n ): boolean {\n let allReady = true;\n for (const coord of coords) {\n const key = this.tileKey(pon, grid.scale, coord, includeAnnotations, epoch);\n const existing = state.entries.get(key);\n if (existing) {\n if (existing.handle === undefined) allReady = false;\n continue;\n }\n allReady = false;\n const abort = new AbortController();\n const entry: TileEntry = {\n key,\n scale: grid.scale,\n coord,\n rect: tilePaintRect(grid, page, coord),\n painted: false,\n abort,\n };\n state.entries.set(key, entry);\n this.deps.store\n .acquire(\n key,\n (signal) =>\n this.deps.fetchTile(\n pon,\n tileEngineRect(grid, page, coord),\n grid.scale,\n includeAnnotations,\n signal,\n ),\n abort.signal,\n )\n .then(\n (handle) => {\n if (state.entries.get(key) !== entry) return; // aborted/superseded\n entry.handle = handle;\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n },\n () => {\n if (state.entries.get(key) === entry) state.entries.delete(key);\n },\n );\n }\n return allReady;\n }\n\n /**\n * The release rule: after `painted` lands on a want-level tile, retained\n * sources (any other level) whose footprint ∩ painted-tile region is\n * covered by PAINTED want-level tiles leave the paint list. Index\n * arithmetic via the aligned grid — no rectangle geometry.\n */\n private releaseOccluded(state: PageTileState, painted: TileEntry): void {\n const wantScale = state.wantScale;\n if (wantScale === null || painted.scale !== wantScale) return;\n const page = this.deps.getPageSize(pagePonOf(painted.key));\n if (!page) return;\n const grid = tileGrid(page, wantScale, this.deps.config.tileSize);\n const paintedAt = (c: TileCoord) => {\n const key = this.tileKey(\n pagePonOf(painted.key),\n wantScale,\n c,\n annotationsOf(painted.key),\n state.epoch,\n );\n return state.entries.get(key)?.painted === true;\n };\n for (const [key, entry] of state.entries) {\n if (entry.scale === wantScale || entry.handle === undefined) continue;\n const overlap = intersectRects(entry.rect, painted.rect);\n if (overlap.width <= 0 || overlap.height <= 0) continue;\n // VISIBLE footprint only: an edge parent whose\n // offscreen children were never fetched must still release once its\n // on-screen region is covered — and its bytes stay in the store, so\n // a pan that re-exposes the rest re-promotes from cache.\n const region = state.lastVisible ? intersectRects(entry.rect, state.lastVisible) : entry.rect;\n if (regionCovered(grid, page, region, paintedAt)) {\n state.entries.delete(key);\n }\n }\n }\n\n private abortAll(state: PageTileState): void {\n if (state.settleTimer !== null) {\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n }\n for (const entry of state.entries.values()) {\n if (entry.handle === undefined) entry.abort?.abort();\n }\n }\n\n private tileKey(\n pon: number,\n scale: number,\n c: TileCoord,\n includeAnnotations: boolean,\n epoch: number,\n ): string {\n return `t:${pon}|s${scale}|${c.ix},${c.iy}|a${includeAnnotations ? 1 : 0}|e${epoch}`;\n }\n}\n\ninterface TileEntry {\n key: string;\n scale: number;\n coord: TileCoord;\n /** y-down page points. */\n rect: Rect;\n handle?: PageImageHandle;\n painted: boolean;\n abort?: AbortController;\n}\n\ninterface PageTileState {\n epoch: number;\n wantScale: number | null;\n entries: Map<string, TileEntry>;\n /** Bumped on ready/painted/drop — the plan memo key. */\n version: number;\n planCache: { demandKey: string; version: number; plan: TilePaintPlan } | null;\n settleTimer: ReturnType<typeof setTimeout> | null;\n pendingLevel: number | null;\n /** Last visible rect from plan() — the release rule's \"on screen\". */\n lastVisible: Rect | null;\n}\n\nconst widthOf = (v: { kind: string; width?: number }): number =>\n v.kind === 'width' && v.width !== undefined ? v.width : Number.POSITIVE_INFINITY;\n\nconst coordKey = (c: TileCoord): string => `${c.ix},${c.iy}`;\nconst rectKey = (r: Rect): string =>\n `${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}`;\n\n/** Stacking: position in the ascending pyramid (coarse under fine). */\nconst zOf = (pyramid: readonly number[], scale: number): number => {\n const sorted = [...pyramid].sort((a, b) => a - b);\n const idx = sorted.indexOf(scale);\n return idx === -1 ? sorted.findIndex((s) => s > scale) : idx;\n};\n\nconst pagePonOf = (key: string): number => Number(key.slice(2, key.indexOf('|')));\nconst annotationsOf = (key: string): boolean => key.includes('|a1|');\n","import {\n CONTINUOUS_RENDER_POLICY,\n snapFullPageViewport,\n type EngineRenderPolicy,\n type PageObjectNumber,\n type PageRenderViewport,\n type PluginContext,\n} from '@embedpdf/core';\n\nimport { resolveTiling } from './paint-plan';\nimport { RasterStore } from './raster-store';\nimport { TileManager } from './tile-manager';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * The render capability: the ONE policy consumer in the client stack\n * (the three-layer rule). The engine never snaps; framework\n * layers never see the policy; everything between — conforming a desired\n * scale to the deployment lattice, collapsing same-rung asks in the raster\n * store, exposing stable source keys — happens here.\n */\nexport function createRenderCapability(\n ctx: PluginContext<RenderState, RenderAction>,\n options: RenderPluginOptions = {},\n): RenderCapability {\n // Document-lifetime cache; keys embed the canonical viewport + epoch, so\n // the lattice is the cache axis and staleness is a new key, never a flush.\n const store = new RasterStore();\n\n // The policy is a DOCUMENT FACT on the kernel's registry — materialized\n // before publish (one lifecycle, in the kernel), interpreted by the pure\n // engine-core snap helpers (one interpretation). This plugin just reads it.\n const policy = (): EngineRenderPolicy => ctx.document()?.renderPolicy ?? CONTINUOUS_RENDER_POLICY;\n\n const pageOf = (pon: PageObjectNumber) =>\n (ctx.document()?.pages ?? []).find((p) => p.pageObjectNumber === pon);\n\n const pageWidthOf = (pon: PageObjectNumber): number => {\n const page = pageOf(pon);\n if (!page) throw new Error(`render: unknown page object number ${pon}`);\n return page.size.width;\n };\n\n const conformViewport = (pon: PageObjectNumber, scale: number): PageRenderViewport => {\n const p = policy();\n if (p.kind !== 'lattice') return { kind: 'scale', scale };\n // scale × pageWidth = desired device width; the ONE snap implementation\n // converts and snaps UP to the canonical ladder width.\n return snapFullPageViewport(p, { kind: 'scale', scale }, { pageWidth: pageWidthOf(pon) });\n };\n\n const viewportKey = (viewport: PageRenderViewport): string =>\n viewport.kind === 'width' ? `w${viewport.width}` : `s${viewport.scale ?? 1}`;\n\n const epochOf = (pon: PageObjectNumber, includeAnnotations: boolean): number => {\n const s = ctx.getState();\n const content = s.contentEpochs[pon] ?? 0;\n if (!includeAnnotations) return content;\n return content + (s.annotatedEpochs[pon] ?? 0);\n };\n\n // Tiling shares THIS store, THIS policy, THIS ledger — one scheduler,\n // one budget, one invalidation truth.\n const tiles = new TileManager({\n store,\n config: resolveTiling(options.tiling),\n getPolicy: policy,\n getPageSize: (pon) => pageOf(pon)?.size,\n getEpoch: epochOf,\n fetchTile: (pon, rect, scale, includeAnnotations, signal) => {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const task = doc.page(pon).render.image({\n target: { kind: 'rect', rect },\n viewport: { kind: 'scale', scale },\n includeAnnotations,\n });\n if (signal.aborted) task.abort(signal.reason);\n else signal.addEventListener('abort', () => task.abort(signal.reason), { once: true });\n return task;\n },\n onAdvance: (pon) => ctx.dispatch({ type: 'PAINT_ADVANCED', pon }),\n });\n\n return {\n renderPage(pon, { scale, includeAnnotations, signal }) {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const annotations = includeAnnotations ?? true;\n const viewport = conformViewport(pon, scale);\n const key = `${pon}|${viewportKey(viewport)}|a${annotations ? 1 : 0}|e${epochOf(pon, annotations)}`;\n return store.acquire(\n key,\n (storeSignal) => {\n const task = doc.page(pon).render.image({ viewport, includeAnnotations: annotations });\n if (storeSignal.aborted) task.abort(storeSignal.reason);\n else\n storeSignal.addEventListener('abort', () => task.abort(storeSignal.reason), {\n once: true,\n });\n return task; // AbortablePromise<PageImageHandle> is a Promise<PageImageHandle>\n },\n signal,\n );\n },\n renderSourceKey(pon, { scale, includeAnnotations }) {\n const annotations = includeAnnotations ?? true;\n const viewport = conformViewport(pon, scale);\n return `${pon}|${viewportKey(viewport)}|a${annotations ? 1 : 0}|e${epochOf(pon, annotations)}`;\n },\n conformViewport,\n renderPolicy: policy,\n tilePlan(pon, demand, opts) {\n return tiles.plan(pon, demand, opts?.includeAnnotations ?? true);\n },\n tilePainted(pon, key) {\n tiles.sourcePainted(pon, key);\n },\n releaseTiles(pon) {\n tiles.releasePage(pon);\n },\n renderEpoch(pon, includeAnnotations = true) {\n // The sum of two monotonic counters is itself a valid monotonic version:\n // a content bump reaches BOTH products; an annotation bump only this one.\n return epochOf(pon, includeAnnotations);\n },\n invalidate({ pons, scope = 'content' } = {}) {\n const target = pons ?? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n if (target.length) ctx.dispatch({ type: 'INVALIDATE', scope, pons: target });\n },\n };\n}\n","/**\n * Makes page rasters REACTIVE — the same event-stream pattern the metadata\n * and annotation plugins use.\n *\n * A render bakes whatever the engine knows into the bitmap, so it goes stale\n * the moment a confirmed mutation changes those pixels. This effect is the\n * BUILT-IN door for such facts: it subscribes to the document event stream\n * and bumps the touched pages' ledger (see reducer.ts); layers key their\n * fetch on `renderEpoch(pon)` and refetch on the bump. The OTHER door is the\n * capability's `invalidate` verb — for plugins whose mutation vocabulary\n * this map doesn't know (redaction, text edit, third-party).\n *\n * Deliberately ORIGIN-AGNOSTIC (unlike the annotation plugin's remote-only\n * fold): a baked raster is stale whether YOU moved the highlight or a\n * collaborator did — the event model's exactly-once guarantee means one bump\n * either way. And because events fire only on CONFIRMED mutations, a drag\n * invalidates once at commit — optimistic previews live in the overlay, never\n * here.\n */\nimport type { DocumentEvent, EffectContext, PageObjectNumber } from '@embedpdf/core';\nimport type { RenderAction, RenderState } from './types';\n\n/**\n * Which pages' ANNOTATED raster a confirmed mutation repaints — the whole\n * event→scope map. Everything the engine emits today is appearance-scoped;\n * when it grows content-mutation events (redaction.applied, content.edited),\n * they get a case here dispatching scope 'content'. Over-invalidation is\n * acceptable (a z-order move that changes nothing repaints one thumb);\n * under-invalidation is the bug.\n */\nexport function annotatedPons(\n event: DocumentEvent,\n allPons: () => PageObjectNumber[],\n): PageObjectNumber[] {\n switch (event.type) {\n case 'annotation.created':\n case 'annotation.updated':\n case 'annotation.deleted':\n case 'annotation.moved': // z-order move — baked stacking can change\n return [event.pageObjectNumber];\n // A field's widgets can live on several pages; the results name exactly\n // the widgets whose appearance changed, each with its page.\n case 'form.valueChanged':\n case 'form.effectsApplied':\n return event.changedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldDeleted':\n return event.removedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldCreated':\n case 'form.fieldUpdated':\n case 'form.widgetAttached':\n case 'form.widgetDetached':\n return event.field.widgets.map((w) => w.pageObjectNumber);\n // Coarse results (counts only, no per-widget detail) — repaint every page.\n case 'form.imported':\n case 'form.repaired':\n return allPons();\n // pages.* replace the page registry: the kernel's `revision` bump already\n // re-keys every layout, and metadata never touches pixels.\n default:\n return [];\n }\n}\n\nexport function registerRenderEffects(ctx: EffectContext<RenderState, RenderAction>): void {\n const doc = ctx.doc;\n if (!doc) return;\n const allPons = () => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Content-scope facts: the page ITSELF changed. A redaction apply\n // destroys page content, so every applied page's base raster is stale —\n // and content strictly contains annotations, so annotated readers bump\n // too (see reducer.ts). Origin-agnostic like the rest of this map.\n if (event.type === 'redaction.applied') {\n const pons = event.results\n .filter((r) => r.status === 'applied')\n .map((r) => r.pageObjectNumber);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'content', pons });\n return;\n }\n const pons = annotatedPons(event, allPons);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'annotations', pons });\n });\n ctx.cleanup(unsubscribe);\n}\n","import type { RenderAction, RenderState } from './types';\n\nexport const initialRenderState = (): RenderState => ({\n contentEpochs: {},\n annotatedEpochs: {},\n paintVersions: {},\n});\n\nconst bump = (\n epochs: Readonly<Record<number, number>>,\n pons: readonly number[],\n): Record<number, number> => {\n const next: Record<number, number> = { ...epochs };\n for (const pon of pons) next[pon] = (next[pon] ?? 0) + 1;\n return next;\n};\n\n/** Pure. Two transitions: bump the touched pages' ledger for the fact's\n * scope (one action per fact, so a batch result bumps each pon once — a\n * 'content' fact bumps ONLY the content ledger; annotated readers sum both,\n * which is how content invalidation reaches them too), and the tile\n * wake-up counter. The render POLICY is deliberately NOT here — it's a\n * document fact on `DocumentMeta`, materialized by the kernel at open. */\nexport const renderReducer = (state: RenderState, a: RenderAction): RenderState => {\n if (a.type === 'PAINT_ADVANCED') {\n // A tile resolution/paint advanced the (store-external) tile state —\n // this bump only wakes subscribed layers to recompute `tilePlan`.\n return { ...state, paintVersions: bump(state.paintVersions, [a.pon]) };\n }\n if (a.type !== 'INVALIDATE' || a.pons.length === 0) return state;\n return a.scope === 'content'\n ? { ...state, contentEpochs: bump(state.contentEpochs, a.pons) }\n : { ...state, annotatedEpochs: bump(state.annotatedEpochs, a.pons) };\n};\n","import {\n createCapabilityToken,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PageObjectNumber,\n type PageRenderViewport,\n} from '@embedpdf/core';\n\nimport type { PageViewDemand, TilePaintPlan, TilingConfig } from './paint-plan';\n\n/** Options for `renderPlugin()`. Tiling is capability + economics here;\n * WHICH view tiles is composition (mount a TileLayer per lens), and\n * WHETHER it engages is demand arithmetic — three levels, three owners. */\nexport interface RenderPluginOptions {\n tiling?: TilingConfig;\n}\n\nexport interface RenderPageOptions {\n /** Device px per PDF point (use the page transform's `renderScale`). */\n scale: number;\n /**\n * Bake annotations into the page bitmap. Default true. Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't painted twice\n * (once baked, once by the overlay).\n */\n includeAnnotations?: boolean;\n /** Abort the render (camera moved / layer unmounted). */\n signal?: AbortSignal;\n}\n\n/**\n * The two invalidation scopes — every pixel-changing fact is one of them:\n *\n * 'annotations' — only baked APPEARANCES changed (an annotation mutated, a\n * form widget re-baked). Base renders keep their pixels.\n * 'content' — the PAGE ITSELF changed (redaction applied, text edited).\n * Invalidates everything: no mutation can change base pixels\n * yet leave an annotated raster valid, so content strictly\n * contains annotations.\n */\nexport type InvalidateScope = 'content' | 'annotations';\n\n/**\n * Per-page versions of the two raster products a page has — base\n * (`includeAnnotations: false`) and annotated. Fed by the document event\n * stream (see effects.ts) and by the `invalidate` verb: a confirmed\n * pixel-changing fact — own or remote — bumps the touched pages, and anything\n * holding a rendered bitmap (a thumbnail rail) refetches.\n */\nexport interface RenderState {\n /** Base-raster versions — bumped by CONTENT facts (redaction, text edit). */\n readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;\n /** Appearance versions — bumped by ANNOTATION facts (annotations, form widgets). */\n readonly annotatedEpochs: Readonly<Record<PageObjectNumber, number>>;\n /**\n * Tile paint-plan wake-ups. The tile manager's state (fetch/painted/\n * retention) lives OUTSIDE the store — it holds live handles and abort\n * controllers — so resolutions bump this counter to make subscribed\n * layers recompute `tilePlan`. The value itself carries no meaning.\n */\n readonly paintVersions: Readonly<Record<PageObjectNumber, number>>;\n}\n\nexport type RenderAction =\n | {\n type: 'INVALIDATE';\n scope: InvalidateScope;\n pons: readonly PageObjectNumber[];\n }\n | { type: 'PAINT_ADVANCED'; pon: PageObjectNumber };\n\nexport interface RenderCapability {\n /**\n * Render a page (by its durable pon) to an ENCODED image. Abortable. Encoded\n * output is identical for local & cloud and cheap over the wire (vs. raw RGBA).\n *\n * CONFORMS to the deployment policy's three-layer rule:\n * under a lattice the desired `scale` converts to the canonical ladder width\n * via `snapFullPageViewport`; under `continuous` it passes through exactly.\n * Same-key requests collapse in the plugin's raster store (singleflight +\n * LRU), so repeated asks inside one rung cost one engine call.\n */\n renderPage(pon: PageObjectNumber, options: RenderPageOptions): Promise<PageImageHandle>;\n /**\n * The identity of the raster `renderPage` would produce for these options —\n * canonical viewport + annotations flag + epoch, as one stable string.\n * Layers key their fetch effect on THIS instead of the raw scale: inside a\n * rung the key never changes (zoom 1.2→1.5 under a [1,2]-ish lattice is a\n * no-op — no refetch, no DOM churn); it changes exactly at rung crossings\n * and epoch bumps. Always available: the kernel materializes the policy on\n * `DocumentMeta` before the document publishes.\n */\n renderSourceKey(\n pon: PageObjectNumber,\n options: { scale: number; includeAnnotations?: boolean },\n ): string;\n /**\n * The canonical viewport a desired scale conforms to for this page —\n * width-kind on a lattice, the exact scale under `continuous`.\n */\n conformViewport(pon: PageObjectNumber, scale: number): PageRenderViewport;\n /** The document's advertised policy (sugar over `DocumentMeta.renderPolicy`). */\n renderPolicy(): EngineRenderPolicy;\n /**\n * The tile paint plan for a page under a host-supplied demand. Tiling is a\n * STRATEGY inside this plugin, not a sibling.\n * Memoized: the same object returns until the demand, an epoch, or a\n * tile resolution actually changes it, so layers can subscribe with\n * plain `Object.is`. Calling it schedules the want-set fetches (visible\n * first, prefetch ring after) — idempotent, store-deduped. Engagement\n * is pure arithmetic: it fires only when the policy's ladder caps the\n * base below the demand; a thumbnail-sized demand never engages.\n */\n tilePlan(\n pon: PageObjectNumber,\n demand: PageViewDemand,\n opts?: { includeAnnotations?: boolean },\n ): TilePaintPlan;\n /**\n * The painter's decode-boundary report: the `<img>` for this plan key\n * actually painted (onload). Retained coarser generations covered by\n * painted want-set tiles release on this signal — never on fetch\n * completion, which would flash during decode.\n */\n tilePainted(pon: PageObjectNumber, key: string): void;\n /** A lens unmounted its TileLayer: abort in-flight tile fetches and drop\n * the page's tile bookkeeping (resolved bytes stay cached). */\n releaseTiles(pon: PageObjectNumber): void;\n /**\n * Version of the raster the given options would produce. Key a long-lived\n * render on it: when it bumps, refetch. Base renders version on content\n * facts; annotated renders on content AND annotation facts. Bumps only on\n * CONFIRMED mutations — never optimistically — so a drag invalidates once,\n * at commit.\n */\n renderEpoch(pon: PageObjectNumber, includeAnnotations?: boolean): number;\n /**\n * Declare that page pixels changed — the open door for facts the built-in\n * event map doesn't know (a plugin's own mutation vocabulary: redaction,\n * text edit, anything third-party). Call at CONFIRMATION (after the engine\n * write resolves), never for optimistic previews — those belong in overlay\n * layers. `pons` omitted = every page; `scope` defaults to 'content'\n * (repaint everything) because a caller who doesn't say is safest repainted\n * fully. Redundant with a mapped engine event? Harmless — one extra refetch.\n */\n invalidate(opts?: { pons?: readonly PageObjectNumber[]; scope?: InvalidateScope }): void;\n}\n\nexport const RenderToken = createCapabilityToken<RenderCapability>('render');\n","import { definePlugin } from '@embedpdf/core';\nimport { createRenderCapability } from './capability';\nimport { registerRenderEffects } from './effects';\nimport { initialRenderState, renderReducer } from './reducer';\nimport { RenderToken } from './types';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * Document-scoped. The ONE policy consumer in the client stack: the doc-bind\n * effect resolves the engine's advertised render\n * policy; `renderPage` conforms desired scales to it and collapses\n * same-rung asks in the raster store; the tile manager turns host-supplied\n * demand into a retention-safe paint plan over the SAME store. State is the\n * per-page ledger — raster versions (two doors: the document event stream's\n * built-in map and the `invalidate` verb), the policy latch, and the tile\n * wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch\n * exactly when those change.\n */\nexport const renderPlugin = (options: RenderPluginOptions = {}) =>\n definePlugin<RenderState, RenderAction, RenderCapability>({\n id: 'render',\n scope: 'document',\n token: RenderToken,\n initialState: initialRenderState,\n reduce: renderReducer,\n capability: (ctx) => createRenderCapability(ctx, options),\n effects: registerRenderEffects,\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/paint-plan.ts","../src/raster-store.ts","../src/strategy.ts","../src/tiles.ts","../src/tile-manager.ts","../src/capability.ts","../src/effects.ts","../src/reducer.ts","../src/types.ts","../src/render.plugin.ts"],"sourcesContent":["import type { PageImageHandle } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n/**\n * The demand a page HOST supplies through dependency inversion:\n * plugin-render defines the shape, producers fill it. The Stage's page\n * host knows the camera and supplies `visibleRect`/`velocity`; a\n * stage-less `<PageView>` supplies neither — absent `visibleRect` means\n * \"assume the whole page is visible\", which a thumbnail-sized demand\n * turns into \"no tiles engage\" by arithmetic, not configuration.\n */\nexport interface PageViewDemand {\n /** Desired device pixels across the page's unrotated content width. */\n desiredDeviceWidth: number;\n /** Visible page region — y-down page points. ABSENT = whole page. */\n visibleRect?: Rect;\n /** Scroll velocity in page points/s — prefetch direction bias only. */\n velocity?: { dx: number; dy: number };\n}\n\n/**\n * One tile the layer should have in the DOM. `key` is the reconciliation\n * identity (stable across plan recomputes — keyed lists preserve the DOM\n * node, which IS the retention mechanism); `rect` is y-down page points\n * inside the unrotated content box; `z` stacks by resolution so arriving\n * sharper tiles occlude retained coarser ones per region.\n */\nexport interface TilePaintSource {\n key: string;\n scale: number;\n rect: Rect;\n z: number;\n handle: PageImageHandle;\n}\n\n/**\n * What a tile plane paints right now. `paint` draws ONLY from resolved\n * rasters — retained generations live here until the release rules fire;\n * \"loading\" never reaches the DOM. `fetching` is diagnostic (badge/tests).\n */\nexport interface TilePaintPlan {\n /** Tiles engaged at all? False = the base rung fully covers demand. */\n engaged: boolean;\n paint: TilePaintSource[];\n /** Keys currently in flight (want-set members not yet resolved). */\n fetching: string[];\n /** Memoization stamp — a new object appears only when this changes. */\n stamp: string;\n}\n\nexport const EMPTY_TILE_PLAN: TilePaintPlan = {\n engaged: false,\n paint: [],\n fetching: [],\n stamp: 'empty',\n};\n\n/**\n * The base-plane strategy: the pixel BUDGET and the render points below it.\n * Device px throughout (CSS px × devicePixelRatio).\n */\nexport interface FullPageOptions {\n /**\n * The budget: no full-page raster ever renders wider. Small by design —\n * the base is the instant backdrop; sharpness past it is the tile plane's\n * job. Default 640.\n */\n maxWidth?: number;\n /**\n * Render points under a `continuous` policy. `'exact'` (default) renders\n * the settled demand precisely — resting pixels are never resampled, the\n * only way ~1px text stems stay crisp on dpr-1 screens. A width ladder\n * opts into rung caching instead (defensible on dpr≥2 embeds). An\n * advertised deployment lattice always wins over either.\n */\n quantize?: 'exact' | readonly number[];\n}\n\n/** The tile-plane strategy. Pass `tiles: false` to disable the plane. */\nexport interface TilesOptions {\n /** Tile edge in device px. Default 512 (policy `tileSizes` win when advertised). */\n size?: number;\n /**\n * Tile levels under a `continuous` policy: `'exact'` (default) renders the\n * settled scale precisely; a scale ladder opts into pyramid reuse. An\n * advertised `policy.tiles` block always wins.\n */\n quantize?: 'exact' | readonly number[];\n /**\n * Exact-mode safety clamp on the tile level (device px per point) — the\n * sharpness ceiling. Past `maxScale × pageWidth` device px of demand,\n * tiles CSS-stretch instead of re-rendering. Per-screen tile cost is\n * constant at any level, so this bounds nothing but numeric range.\n * Default 128 (≈ 6,400% zoom on a letter page at dpr 2).\n */\n maxScale?: number;\n /**\n * Overlap neighboring tiles by this many device pixels per shared edge\n * (each tile renders a slightly larger region and is placed to match).\n * Kills hairline seams whenever tiles display at anything other than 1:1 —\n * separate `<img>`s at fractional boundaries each get partial-coverage\n * edge anti-aliasing, and bilinear scaling smears their last row — by\n * making every edge land over the neighbor's identical content instead of\n * the backdrop. Default 1; 0 disables.\n */\n bleed?: number;\n /**\n * Sharpness deficit (desired ÷ supplied-by-base) above which tiles engage.\n * Resolved default: 1.0 when the base is exact (nothing may rest\n * stretched), 1.25 under a lattice.\n */\n engageAt?: number;\n /** Prefetch ring around the visible rect. */\n prefetch?: {\n /** Extra coverage per side, in fractions of the visible rect. Default 0.5. */\n margin?: number;\n /** Stretch the ring toward scroll direction. Default true. */\n velocityBias?: boolean;\n };\n /**\n * Settle gate for LEVEL changes (a zoom in motion): tile fetches for a new\n * level wait this long; pan-driven fetches at the current level fire\n * immediately. Default 150ms; 0 disables.\n */\n settleMs?: number;\n /** Optional arrival cross-fade for tiles, in ms. Default 0 (hard pop —\n * clean once painting is decode-gated). */\n fadeMs?: number;\n}\n\n/** The ×2 client pyramid used for lattice deployments without a tiles block. */\nexport const DEFAULT_TILE_PYRAMID: readonly number[] = [1, 2, 4, 8, 16, 32];\n\nexport interface ResolvedRenderOptions {\n fullPage: {\n maxWidth: number;\n /** True when the embedder set maxWidth themselves — only then does it\n * also filter an ADVERTISED deployment ladder (the mobile-memory\n * knob); the default budget governs the client's own strategy only. */\n maxWidthExplicit: boolean;\n quantize: 'exact' | readonly number[];\n };\n tiles: {\n enabled: boolean;\n size: number;\n quantize: 'exact' | readonly number[];\n maxScale: number;\n bleedPx: number;\n engageAt: number | undefined;\n prefetchMargin: number;\n velocityBias: boolean;\n settleMs: number;\n fadeMs: number;\n /** Pyramid for lattice deployments that don't advertise tiles yet. */\n fallbackPyramid: readonly number[];\n };\n format?: 'png' | 'webp' | 'bmp';\n quality?: number;\n /** Diagnostic logging (tile scheduling, fetch outcomes). */\n debug: boolean;\n}\n\nexport function resolveRenderOptions(options: {\n fullPage?: FullPageOptions;\n tiles?: TilesOptions | false;\n format?: 'png' | 'webp' | 'bmp';\n quality?: number;\n debug?: boolean;\n}): ResolvedRenderOptions {\n const tiles = options.tiles === false ? undefined : options.tiles;\n return {\n fullPage: {\n maxWidth: options.fullPage?.maxWidth ?? 640,\n maxWidthExplicit: options.fullPage?.maxWidth !== undefined,\n quantize: options.fullPage?.quantize ?? 'exact',\n },\n tiles: {\n enabled: options.tiles !== false,\n size: tiles?.size ?? 512,\n quantize: tiles?.quantize ?? 'exact',\n maxScale: tiles?.maxScale ?? 128,\n bleedPx: tiles?.bleed ?? 1,\n engageAt: tiles?.engageAt,\n prefetchMargin: tiles?.prefetch?.margin ?? 0.5,\n velocityBias: tiles?.prefetch?.velocityBias ?? true,\n settleMs: tiles?.settleMs ?? 150,\n fadeMs: tiles?.fadeMs ?? 0,\n fallbackPyramid: DEFAULT_TILE_PYRAMID,\n },\n format: options.format,\n quality: options.quality,\n debug: options.debug ?? false,\n };\n}\n","import type { PageImageHandle } from '@embedpdf/core';\n\n/**\n * Client-side mirror of the server's one-door read-through: per-key\n * SINGLEFLIGHT + cost-budgeted LRU over encoded page images.\n *\n * The key is the raster's canonical identity (conformed viewport +\n * annotations flag + epoch — see `renderSourceKey`), so the render points do\n * the caching work: every ask at one point is the same key, and\n * re-crossings (zoom 1280 → 2560 → back) resolve instantly from the LRU\n * instead of re-rendering. Epoch bumps mint new keys; stale-epoch entries\n * age out through the LRU (they are never re-requested).\n *\n * The budget is COST, approximately bytes: an entry costs its encoded byte\n * length when the handle carries bytes (local), its pixel count as a\n * decoded-order estimate otherwise (URL-backed cloud handles), and 1 when\n * neither is known. Entry counts lie — a 512² webp tile and a BMP full page\n * differ by 100× — and long deep-zoom sessions cycle hundreds of tile keys,\n * so the budget is what actually bounds memory.\n *\n * Consumer aborts are REFCOUNTED: a caller abandoning a fetch (camera moved,\n * layer unmounted) detaches without killing it for other consumers; the\n * underlying engine call aborts only when the LAST in-flight consumer leaves.\n * Resolved entries are kept (evictable by LRU) — an abort after resolution\n * is a no-op by design.\n */\nexport class RasterStore {\n private readonly entries = new Map<string, Entry>();\n private tick = 0;\n private total = 0;\n\n constructor(private readonly maxCost = 128 * 1024 * 1024) {}\n\n acquire(\n key: string,\n fetch: (signal: AbortSignal) => Promise<PageImageHandle>,\n signal?: AbortSignal,\n ): Promise<PageImageHandle> {\n const existing = this.entries.get(key);\n if (existing) {\n existing.used = ++this.tick;\n return this.attach(key, existing, signal);\n }\n\n const abort = new AbortController();\n const entry: Entry = {\n promise: undefined as unknown as Promise<PageImageHandle>,\n refs: 0,\n abort,\n used: ++this.tick,\n };\n entry.promise = fetch(abort.signal).then(\n (handle) => {\n entry.handle = handle;\n entry.cost = costOf(handle);\n this.total += entry.cost;\n this.evict(); // cost is known only now — budget check at resolution\n return handle;\n },\n (err) => {\n // Failed fetches must not be sticky — drop so the next ask retries.\n this.drop(key, entry);\n throw err;\n },\n );\n // Swallow the shared promise's rejection when no consumer is attached\n // (everyone aborted) — each consumer observes it via its own attachment.\n entry.promise.catch(() => {});\n this.entries.set(key, entry);\n this.evict();\n return this.attach(key, entry, signal);\n }\n\n /** Resolved handle if present — synchronous peek for paint-time reuse. */\n peek(key: string): PageImageHandle | undefined {\n const entry = this.entries.get(key);\n if (entry?.handle) entry.used = ++this.tick;\n return entry?.handle;\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** Total cost (~bytes) of resolved entries — the budgeted quantity. */\n get costUsed(): number {\n return this.total;\n }\n\n private attach(key: string, entry: Entry, signal?: AbortSignal): Promise<PageImageHandle> {\n if (signal?.aborted) return Promise.reject(abortReason(signal));\n // EVERY consumer holds a ref while the fetch is in flight — including\n // signal-less ones (\"I never abandon\"), otherwise a signal-bearing\n // sibling's abort could kill a fetch someone else is still awaiting.\n entry.refs += 1;\n if (!signal) {\n const release = () => {\n entry.refs -= 1;\n };\n entry.promise.then(release, release);\n return entry.promise;\n }\n let settled = false;\n return new Promise<PageImageHandle>((resolve, reject) => {\n const onAbort = () => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n // Last live consumer walking away from an UNRESOLVED fetch kills it;\n // resolved entries stay for the LRU (that's the cache).\n if (entry.refs <= 0 && entry.handle === undefined) {\n entry.abort.abort(abortReason(signal));\n this.drop(key, entry);\n }\n reject(abortReason(signal));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n entry.promise.then(\n (handle) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n resolve(handle);\n },\n (err) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n }\n\n /** Remove an entry, keeping the cost ledger honest. */\n private drop(key: string, entry: Entry): void {\n if (this.entries.get(key) !== entry) return;\n this.entries.delete(key);\n if (entry.cost !== undefined) this.total -= entry.cost;\n }\n\n private evict(): void {\n if (this.total <= this.maxCost) return;\n // Oldest-first over evictable entries: resolved and consumer-free.\n const candidates = [...this.entries.entries()]\n .filter(([, e]) => e.refs <= 0 && e.handle !== undefined)\n .sort((a, b) => a[1].used - b[1].used);\n for (const [key, entry] of candidates) {\n if (this.total <= this.maxCost) return;\n this.drop(key, entry);\n }\n }\n}\n\ninterface Entry {\n promise: Promise<PageImageHandle>;\n handle?: PageImageHandle;\n /** Approximate bytes; set at resolution (see class doc). */\n cost?: number;\n refs: number;\n abort: AbortController;\n used: number;\n}\n\n/** Approximate memory cost: encoded bytes when known, pixel count as a\n * decoded-order estimate for URL handles, 1 when nothing is known. */\nfunction costOf(handle: PageImageHandle): number {\n if (handle.source?.kind === 'bytes') return handle.source.bytes.byteLength;\n if (handle.width !== undefined && handle.height !== undefined) {\n return handle.width * handle.height;\n }\n return 1;\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('aborted', 'AbortError');\n}\n","import type { EngineRenderPolicy, PageImageOptions } from '@embedpdf/core';\n\nimport type { ResolvedRenderOptions } from './paint-plan';\n\n/** Encoded formats the strategy may request (the engine's own union). */\nexport type RenderFormat = NonNullable<PageImageOptions['format']>;\n\n/**\n * The render points a document actually uses — POLICY ∧ STRATEGY, resolved\n * once per (policy, options) pair.\n *\n * Policy is an engine fact (what the deployment serves and caches); strategy\n * is a plugin decision (what the viewer spends, and which points it renders\n * when the engine permits anything). The one rule, applied three times:\n * a strategy value applies as written under `continuous`, and conforms to\n * the advertised lattice when there is one — so `quantize: 'exact'` or\n * `format: 'bmp'` run unchanged against cloud with no branches in client\n * code.\n *\n * - `widths: null` → base renders the EXACT settled demand, capped at\n * `maxWidth` (the local default — resting pixels are never resampled,\n * which is the only way ~1px text stems stay crisp on dpr-1 screens).\n * - `widths: [...]` → base snaps UP the rung list (an advertised lattice,\n * or the opt-in client ladder), capped by `maxWidth`.\n * - `pyramid` mirrors the same split for tile levels.\n */\nexport interface ResolvedStrategy {\n /** Base render points; null = exact-up-to-budget. */\n readonly widths: readonly number[] | null;\n /** The budget: no full-page raster is ever wider (device px). */\n readonly maxWidth: number;\n /** Tile levels (device px per point); null = exact scale at rest. */\n readonly pyramid: readonly number[] | null;\n /** Tile edge in device px (policy tileSizes win when advertised). */\n readonly tileSize: number;\n /** Sharpness deficit above which tiles engage. Resolved default: 1.0 when\n * the base is exact (nothing may rest stretched), 1.25 under a lattice\n * (rungs make the band cheap and CSS ≤1.25× upscale reads fine). */\n readonly engageAt: number;\n /** Exact-mode tile-level safety clamp (device px per point). */\n readonly tileMaxScale: number;\n /** Encode format for BOTH planes, policy-conformed. Undefined = engine default. */\n readonly format?: RenderFormat;\n readonly quality?: number;\n}\n\nconst ascending = (values: readonly number[]): number[] => [...values].sort((a, b) => a - b);\n\n/** Rung list capped by the budget — always keeps at least the smallest rung. */\nconst capWidths = (widths: readonly number[], maxWidth: number): readonly number[] => {\n const sorted = ascending(widths);\n const kept = sorted.filter((w) => w <= maxWidth);\n return kept.length ? kept : sorted.slice(0, 1);\n};\n\nexport function resolveStrategy(\n policy: EngineRenderPolicy,\n options: ResolvedRenderOptions,\n): ResolvedStrategy {\n const lattice = policy.kind === 'lattice';\n\n // An advertised ladder is the deployment's intent — the DEFAULT budget\n // never second-guesses it; an EXPLICIT maxWidth filters it (every rung is\n // CDN-valid, so choosing lower ones is always safe — the mobile knob).\n const widths = lattice\n ? options.fullPage.maxWidthExplicit\n ? capWidths(policy.fullPage.widths, options.fullPage.maxWidth)\n : ascending(policy.fullPage.widths)\n : options.fullPage.quantize === 'exact'\n ? null\n : capWidths(options.fullPage.quantize, options.fullPage.maxWidth);\n\n // Tile levels: an advertised tiles block always wins; otherwise the client\n // quantizer — exact on continuous by default, and the ×2 client pyramid as\n // the fallback for lattice deployments that don't advertise tiles yet.\n const clientPyramid =\n options.tiles.quantize === 'exact' ? null : ascending(options.tiles.quantize);\n const pyramid = lattice\n ? (policy.tiles?.scales ? ascending(policy.tiles.scales) : (clientPyramid ?? options.tiles.fallbackPyramid))\n : clientPyramid;\n\n const tileSize =\n lattice && policy.tiles?.tileSizes.length\n ? policy.tiles.tileSizes.includes(options.tiles.size)\n ? options.tiles.size\n : policy.tiles.tileSizes[0]!\n : options.tiles.size;\n\n const engageAt = options.tiles.engageAt ?? (widths === null ? 1.0 : 1.25);\n\n // Format conforms like everything else: valid under this policy or the\n // deployment's preferred one. (BMP is local-only by the wire contract.)\n const requested = options.format;\n const format =\n requested === undefined\n ? undefined\n : !lattice || (policy.formats as readonly string[]).includes(requested)\n ? requested\n : policy.formats[0];\n\n return {\n widths,\n maxWidth: options.fullPage.maxWidth,\n pyramid,\n tileSize,\n engageAt,\n tileMaxScale: options.tiles.maxScale,\n format,\n quality: options.quality,\n };\n}\n\n/**\n * The ONE base-sizing function — four callers: `renderPage`,\n * `renderSourceKey`, `conformViewport`, and tile engagement. Exact mode\n * returns the (settled) demand itself up to the budget; ladder mode snaps\n * UP and caps at the top rung.\n */\nexport function baseAskWidth(strategy: ResolvedStrategy, demandWidth: number): number {\n const wanted = Math.max(1, Math.round(demandWidth));\n if (strategy.widths === null) return Math.min(wanted, strategy.maxWidth);\n return strategy.widths.find((w) => w >= wanted) ?? strategy.widths[strategy.widths.length - 1]!;\n}\n","import type { PdfRect } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n// One owner for space math: rect intersection lives in core-geometry\n// (the stage's visibility computation uses the same helper).\nexport { intersectRects } from '@embedpdf/core-geometry';\n\n/**\n * Pure tile-grid math.\n *\n * The pyramid is ALIGNED: one origin (the page's top-left), a fixed tile\n * size in device pixels, and ×2 scale steps. Alignment is what makes the\n * retention bookkeeping index arithmetic instead of rectangle geometry —\n * a level-s tile is covered by exactly the level-2s tiles whose indices\n * fall in its doubled range, so occlusion/release checks are O(1).\n *\n * Spaces: tile COORDS live on the device grid at their level's scale\n * (tileSize² device px per tile, constant per-job cost by construction);\n * PAINT rects are y-down page points (the viewer convention — the layer\n * multiplies by one container transform); ENGINE rects are y-up PDF user\n * space (the `target: {kind:'rect'}` contract).\n */\n\nexport interface PageSizePt {\n width: number;\n height: number;\n}\n\nexport interface TileCoord {\n ix: number;\n iy: number;\n}\n\nexport interface TileGrid {\n /** Device px per PDF point at this pyramid level. */\n scale: number;\n /** Tile edge in device px. */\n tileSize: number;\n cols: number;\n rows: number;\n}\n\nexport function tileGrid(page: PageSizePt, scale: number, tileSize: number): TileGrid {\n return {\n scale,\n tileSize,\n cols: Math.max(1, Math.ceil((page.width * scale) / tileSize)),\n rows: Math.max(1, Math.ceil((page.height * scale) / tileSize)),\n };\n}\n\n/** One tile's edge in page points at its level. */\nconst tileSpanPt = (grid: TileGrid): number => grid.tileSize / grid.scale;\n\n/**\n * The tiles of `grid` intersecting a y-down page-point rect, clamped to\n * the page. An empty intersection yields an empty list.\n */\nexport function tilesInRect(grid: TileGrid, page: PageSizePt, rect: Rect): TileCoord[] {\n const span = tileSpanPt(grid);\n const x0 = Math.max(0, Math.floor(rect.x / span));\n const y0 = Math.max(0, Math.floor(rect.y / span));\n const x1 = Math.min(grid.cols - 1, Math.ceil((rect.x + rect.width) / span) - 1);\n const y1 = Math.min(grid.rows - 1, Math.ceil((rect.y + rect.height) / span) - 1);\n const out: TileCoord[] = [];\n for (let iy = y0; iy <= y1; iy++) {\n for (let ix = x0; ix <= x1; ix++) out.push({ ix, iy });\n }\n return out;\n}\n\n/** Paint rect: y-down page points, clamped to the page edge. */\nexport function tilePaintRect(grid: TileGrid, page: PageSizePt, c: TileCoord): Rect {\n const span = tileSpanPt(grid);\n const x = c.ix * span;\n const y = c.iy * span;\n return {\n x,\n y,\n width: Math.min(span, page.width - x),\n height: Math.min(span, page.height - y),\n };\n}\n\n/** Engine target rect (y-UP PDF user space, top > bottom) for an arbitrary\n * y-down page-point rect. */\nexport function toEngineRect(page: PageSizePt, rect: Rect): PdfRect {\n return {\n left: rect.x,\n right: rect.x + rect.width,\n top: page.height - rect.y,\n bottom: page.height - (rect.y + rect.height),\n };\n}\n\n/** Engine target rect: y-UP PDF user space (top > bottom). */\nexport function tileEngineRect(grid: TileGrid, page: PageSizePt, c: TileCoord): PdfRect {\n return toEngineRect(page, tilePaintRect(grid, page, c));\n}\n\n/**\n * Expand a paint rect by `pt` page points per side, clamped to the page —\n * the tile BLEED. Neighboring tiles rendered with bleed overlap by twice\n * this amount, and the overlapping strips contain identical content (same\n * page region, same scale), so every img edge composites over its\n * neighbor's duplicated pixels instead of the backdrop: no AA hairlines,\n * no bilinear edge smear, at any CSS stretch.\n */\nexport function bleedRect(rect: Rect, pt: number, page: PageSizePt): Rect {\n const x = Math.max(0, rect.x - pt);\n const y = Math.max(0, rect.y - pt);\n return {\n x,\n y,\n width: Math.min(page.width, rect.x + rect.width + pt) - x,\n height: Math.min(page.height, rect.y + rect.height + pt) - y,\n };\n}\n\n/**\n * Does a set of PAINTED want-level tiles cover `region` (y-down points)?\n * The retention release check: a retained source may leave the paint list\n * once its visible footprint answers true here. Pure index arithmetic on\n * the aligned grid — the whole reason the pyramid is aligned.\n */\nexport function regionCovered(\n grid: TileGrid,\n page: PageSizePt,\n region: Rect,\n isPainted: (c: TileCoord) => boolean,\n): boolean {\n if (region.width <= 0 || region.height <= 0) return true;\n for (const c of tilesInRect(grid, page, region)) {\n if (!isPainted(c)) return false;\n }\n return true;\n}\n\n/**\n * Inflate a visible rect by a prefetch margin (fractions of the rect's own\n * size per side), optionally velocity-biased: the margin stretches toward\n * the direction of travel and shrinks behind it. Pure — the producer's\n * velocity is just data.\n */\nexport function inflateRect(\n rect: Rect,\n margin: number,\n velocity?: { dx: number; dy: number },\n): Rect {\n const mx = rect.width * margin;\n const my = rect.height * margin;\n let left = mx;\n let right = mx;\n let up = my;\n let down = my;\n if (velocity) {\n // Direction buckets only — raw magnitudes would make the want set\n // churn with every pointer sample.\n if (velocity.dx > 0) {\n right = mx * 2;\n left = mx / 2;\n } else if (velocity.dx < 0) {\n left = mx * 2;\n right = mx / 2;\n }\n if (velocity.dy > 0) {\n down = my * 2;\n up = my / 2;\n } else if (velocity.dy < 0) {\n up = my * 2;\n down = my / 2;\n }\n }\n return {\n x: rect.x - left,\n y: rect.y - up,\n width: rect.width + left + right,\n height: rect.height + up + down,\n };\n}\n\n/** Snap UP through a pyramid's sorted scales; cap at the top. */\nexport function snapToPyramid(scales: readonly number[], needed: number): number {\n const sorted = [...scales].sort((a, b) => a - b);\n return sorted.find((s) => s >= needed) ?? sorted[sorted.length - 1]!;\n}\n","import type { EngineRenderPolicy, PageImageHandle, PdfRect } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\nimport {\n EMPTY_TILE_PLAN,\n type PageViewDemand,\n type ResolvedRenderOptions,\n type TilePaintPlan,\n type TilePaintSource,\n} from './paint-plan';\nimport type { RasterStore } from './raster-store';\nimport { baseAskWidth, resolveStrategy, type ResolvedStrategy } from './strategy';\nimport {\n bleedRect,\n inflateRect,\n intersectRects,\n regionCovered,\n snapToPyramid,\n tileGrid,\n tilesInRect,\n tilePaintRect,\n toEngineRect,\n type PageSizePt,\n type TileCoord,\n type TileGrid,\n} from './tiles';\n\n/**\n * The tile retention state machine. One instance per\n * plugin instance (per document), all levels and pages in one place, over\n * the SAME RasterStore the base renders use.\n *\n * Invariant it enforces: every screen region paints the sharpest PAINTED\n * pixels available; quality per region only goes up until the want set\n * resolves. Mechanics:\n * - want vs paint: `plan()` schedules fetches for the want set (P0\n * visible first — center-out — then P1 prefetch when P0 is fully\n * resolved) and returns a paint list drawn ONLY from resolved entries —\n * current level AND retained older generations.\n * - release-on-occlusion: when a want-level tile reports PAINTED (after the\n * layer's first presentation opportunity), retained sources whose\n * visible footprint is\n * covered by painted want tiles leave the paint list. Their bytes stay\n * in the RasterStore (demotion, not eviction) — zoom-back re-promotes\n * from cache.\n * - epoch exception: an invalidation bump means retained pixels are\n * WRONG — everything of the old epoch drops immediately.\n *\n * Levels come from the resolved STRATEGY (policy ∧ options): a pyramid\n * under a lattice (or the opt-in client ladder), the EXACT settled scale\n * under exact mode — where the level identity is the demand's device width\n * across the page, so keys stay integer and stable. The retention/coverage\n * math is generic over any mix of retained scales.\n *\n * `plan()` is called from selectors: it MUST NOT dispatch. Fetch kickoff\n * is idempotent (the store singleflights); resolution handlers dispatch\n * the wake-up (`onAdvance`) that makes subscribed layers recompute.\n */\n/** Backpressure: raw rasters in flight at once (render + encode transit). */\nconst MAX_IN_FLIGHT = 8;\n/** Stage-less (no visibleRect) demand is capped to this many whole-page tiles. */\nconst STAGELESS_TILE_CAP = 64;\n\nexport class TileManager {\n private readonly pages = new Map<number, PageTileState>();\n private strategyMemo: { policy: EngineRenderPolicy; strategy: ResolvedStrategy } | null = null;\n /** Live fetches across all pages — the backpressure counter. */\n private inFlight = 0;\n private warnedStageless = false;\n\n constructor(\n private readonly deps: {\n store: RasterStore;\n options: ResolvedRenderOptions;\n /** The document fact off the kernel registry — never null: the kernel\n * materializes it (continuous fallback) before the doc publishes. */\n getPolicy(): EngineRenderPolicy;\n getPageSize(pon: number): PageSizePt | undefined;\n getEpoch(pon: number, includeAnnotations: boolean): number;\n fetchTile(\n pon: number,\n rect: PdfRect,\n scale: number,\n includeAnnotations: boolean,\n signal: AbortSignal,\n ): Promise<PageImageHandle>;\n /** Wake subscribed layers (dispatches PAINT_ADVANCED outside plan()). */\n onAdvance(pon: number): void;\n /** Diagnostic sink (options.debug) — scheduling and fetch outcomes. */\n debug?(msg: string): void;\n },\n ) {}\n\n private strategy(): ResolvedStrategy {\n const policy = this.deps.getPolicy();\n if (this.strategyMemo?.policy !== policy) {\n this.strategyMemo = { policy, strategy: resolveStrategy(policy, this.deps.options) };\n }\n return this.strategyMemo.strategy;\n }\n\n plan(pon: number, demand: PageViewDemand, includeAnnotations: boolean): TilePaintPlan {\n const { options } = this.deps;\n if (!options.tiles.enabled) return EMPTY_TILE_PLAN;\n const strategy = this.strategy();\n const page = this.deps.getPageSize(pon);\n if (!page) return EMPTY_TILE_PLAN;\n\n const epoch = this.deps.getEpoch(pon, includeAnnotations);\n const state = this.pageState(pon);\n\n // Epoch exception: old-epoch pixels are WRONG, not blurry — drop all.\n if (state.epoch !== epoch) {\n this.abortAll(state);\n state.entries.clear();\n state.failedKeys.clear();\n state.epoch = epoch;\n state.wantScale = null;\n state.wantWidth = null;\n state.planCache = null;\n }\n\n // Engagement: deficit of what the base ACTUALLY supplies vs demand —\n // the same `baseAsk` the base layer sizes with, so local and cloud run\n // the identical arithmetic. Exact mode engages at 1.0 (nothing may rest\n // stretched past the budget); a lattice tolerates its band.\n const supplied = baseAskWidth(strategy, demand.desiredDeviceWidth);\n const deficit = demand.desiredDeviceWidth / supplied;\n if (deficit <= strategy.engageAt) {\n // Disengage drops bookkeeping immediately; resolved bytes stay in the\n // RasterStore, so a re-engage promotes from cache — and below the\n // threshold the base itself is crisp, so nothing visible is lost.\n if (state.entries.size || state.wantScale !== null) {\n this.deps.debug?.(`disengage pon=${pon} (deficit ${deficit.toFixed(2)})`);\n this.abortAll(state);\n state.entries.clear();\n state.failedKeys.clear();\n state.wantScale = null;\n state.wantWidth = null;\n state.planCache = null;\n }\n return EMPTY_TILE_PLAN;\n }\n\n // Level selection. Pyramid mode snaps UP the ladder; exact mode renders\n // the demand itself (clamped by the safety cap), with the level identity\n // being the integer device width across the page.\n let wantWidth: number;\n let wantScale: number;\n if (strategy.pyramid) {\n wantScale = snapToPyramid(strategy.pyramid, demand.desiredDeviceWidth / page.width);\n wantWidth = Math.round(wantScale * page.width);\n } else {\n wantWidth = Math.min(\n Math.max(1, Math.round(demand.desiredDeviceWidth)),\n Math.round(strategy.tileMaxScale * page.width),\n );\n wantScale = wantWidth / page.width;\n }\n\n // STAGE-LESS demand (no visibleRect) means the WHOLE page tiles at the\n // want level — unbounded at deep zoom (a 4,650% page is ~28,000 tiles).\n // Clamp the level so the whole-page tile count stays bounded: the lens\n // degrades to bounded sharpness instead of unbounded memory, consistent\n // with the budget philosophy everywhere else. Hosts that want true deep\n // zoom supply a visibleRect (the Stage does).\n if (!demand.visibleRect) {\n const maxStagelessWidth = Math.max(\n strategy.tileSize,\n Math.floor(\n strategy.tileSize * Math.sqrt((STAGELESS_TILE_CAP * page.width) / page.height),\n ),\n );\n if (wantWidth > maxStagelessWidth) {\n if (!this.warnedStageless) {\n this.warnedStageless = true;\n console.warn(\n `[render] stage-less tile demand of ${Math.round(demand.desiredDeviceWidth)} device px ` +\n `would tile the whole page — clamped to ${maxStagelessWidth}. Supply a visibleRect ` +\n `(host the page in a Stage) for sharp deep zoom.`,\n );\n }\n if (strategy.pyramid) {\n const fitting = strategy.pyramid.filter(\n (s) => Math.round(s * page.width) <= maxStagelessWidth,\n );\n wantScale = fitting.length ? fitting[fitting.length - 1]! : strategy.pyramid[0]!;\n wantWidth = Math.round(wantScale * page.width);\n } else {\n wantWidth = maxStagelessWidth;\n wantScale = wantWidth / page.width;\n }\n }\n }\n const grid = tileGrid(page, wantScale, strategy.tileSize);\n\n const visible = demand.visibleRect ?? { x: 0, y: 0, width: page.width, height: page.height };\n state.lastVisible = visible;\n const p0 = tilesInRect(grid, page, visible);\n const ring = inflateRect(\n visible,\n options.tiles.prefetchMargin,\n options.tiles.velocityBias ? demand.velocity : undefined,\n );\n const p0Keys = new Set(p0.map((c) => coordKey(c)));\n const p1 = tilesInRect(grid, page, ring).filter((c) => !p0Keys.has(coordKey(c)));\n\n this.schedule(\n pon,\n state,\n page,\n grid,\n wantScale,\n wantWidth,\n includeAnnotations,\n epoch,\n p0,\n p1,\n visible,\n );\n\n // Release retained generations covered by the CURRENT painted set —\n // evaluated here, not only on painted reports, so release can never be\n // stranded by report ordering.\n if (this.releaseCovered(pon, state, page, includeAnnotations)) {\n state.version += 1;\n state.planCache = null;\n }\n\n // Paint list: resolved entries intersecting the visible rect, coarser\n // levels first (painter's algorithm — sharper occludes per region).\n // The memo key holds the LEVEL identity (integer width) — zoom inside a\n // pyramid rung is plan-stable by construction; exact mode re-plans per\n // settled level, which the schedule gate keeps rare.\n const demandKey = `w${wantWidth}|${rectKey(visible)}|e${epoch}`;\n if (\n state.planCache &&\n state.planCache.demandKey === demandKey &&\n state.planCache.version === state.version\n ) {\n return state.planCache.plan;\n }\n const paint: TilePaintSource[] = [];\n const fetching: string[] = [];\n const stale: string[] = [];\n const bleedPx = this.deps.options.tiles.bleedPx;\n for (const entry of state.entries.values()) {\n const visIntersect = intersectRects(entry.rect, visible);\n if (entry.resolved) {\n if (visIntersect.width > 0 && visIntersect.height > 0) {\n // OWNERSHIP: bytes live in the RasterStore alone; the manager\n // holds keys. Peek resolves the handle at paint time — an entry\n // whose bytes were evicted is simply no longer resolved (dropped\n // here; re-fetched on the next pass if still wanted).\n const handle = this.deps.store.peek(entry.key);\n if (!handle) {\n stale.push(entry.key);\n continue;\n }\n paint.push({\n key: entry.key,\n scale: entry.scale,\n // The PLACEMENT rect is the bled one — it matches the bitmap the\n // fetch rendered. Retention/coverage math stays on the logical\n // (unbled) `entry.rect`; the overlap strips duplicate the\n // neighbor's content, so painting them is what kills the seams.\n rect: bleedPx > 0 ? bleedRect(entry.rect, bleedPx / entry.scale, page) : entry.rect,\n z: 0, // ranked below — stacking is scale order among PRESENT entries\n handle,\n });\n }\n } else if (entry.scale === wantScale) {\n fetching.push(entry.key);\n }\n }\n for (const key of stale) state.entries.delete(key);\n // Stacking: rank the scales actually present (generic over exact levels\n // and pyramid rungs alike) — coarse under fine.\n const rank = new Map<number, number>();\n for (const s of [...new Set(paint.map((p) => p.scale))].sort((a, b) => a - b)) {\n rank.set(s, rank.size);\n }\n for (const p of paint) p.z = rank.get(p.scale)!;\n paint.sort((a, b) => a.z - b.z || a.key.localeCompare(b.key));\n const plan: TilePaintPlan = {\n engaged: true,\n paint,\n fetching: fetching.sort(),\n stamp: `${demandKey}|v${state.version}`,\n };\n state.planCache = { demandKey, version: state.version, plan };\n return plan;\n }\n\n /** The layer's painted report: this key's pixels had a presentation opportunity. */\n sourcePainted(pon: number, key: string): void {\n const state = this.pages.get(pon);\n const entry = state?.entries.get(key);\n if (!state || !entry || entry.painted) return;\n entry.painted = true;\n const page = this.deps.getPageSize(pon);\n if (page) this.releaseCovered(pon, state, page, annotationsOf(key));\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n }\n\n /**\n * The inverse report: this key's <img> left the DOM (pan-away, plan drop),\n * so its pixels are NOT currently compositable. Without this, a tile that\n * unmounts and later remounts is still counted as painted while its new\n * <img> re-decodes — and an adjacent fresh `sourcePainted` could release\n * retained coarse coverage over a region that momentarily has no sharp\n * pixels. Painted is a statement about the SCREEN, so it follows the DOM.\n */\n sourceUnpainted(pon: number, key: string): void {\n const entry = this.pages.get(pon)?.entries.get(key);\n if (entry) entry.painted = false;\n }\n\n /** A lens unmounted its tile plane: stop fetching, drop bookkeeping.\n * Resolved bytes stay in the RasterStore for a re-mount. */\n releasePage(pon: number): void {\n const state = this.pages.get(pon);\n if (!state) return;\n this.abortAll(state);\n this.pages.delete(pon);\n }\n\n private pageState(pon: number): PageTileState {\n let state = this.pages.get(pon);\n if (!state) {\n state = {\n epoch: -1,\n wantScale: null,\n wantWidth: null,\n entries: new Map(),\n failedKeys: new Set(),\n version: 0,\n planCache: null,\n settleTimer: null,\n pendingLevel: null,\n lastVisible: null,\n };\n this.pages.set(pon, state);\n }\n return state;\n }\n\n private schedule(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n wantScale: number,\n wantWidth: number,\n includeAnnotations: boolean,\n epoch: number,\n p0: TileCoord[],\n p1: TileCoord[],\n visible: Rect,\n ): void {\n const { options } = this.deps;\n const firstEngage = state.wantWidth === null;\n const levelChanged = state.wantWidth !== null && state.wantWidth !== wantWidth;\n if (levelChanged) state.failedKeys.clear(); // a new level gets fresh chances\n state.wantScale = wantScale;\n state.wantWidth = wantWidth;\n\n // Entries FOLLOW the want set. In-flight fetches that left it abort;\n // resolved SAME-LEVEL tiles that left it are dropped — their bytes stay\n // in the RasterStore's LRU, so a pan-back re-promotes from cache\n // instead of re-rendering. Only cross-level RETAINED entries stay, and\n // those are the release rules' business. Without this, panning at deep\n // zoom accumulates every tile ever visited.\n const wanted = new Set(\n [...p0, ...p1].map((c) => this.tileKey(pon, wantWidth, c, includeAnnotations, epoch)),\n );\n for (const [key, entry] of state.entries) {\n if (wanted.has(key)) continue;\n if (!entry.resolved) {\n entry.abort?.abort();\n // The transit slot frees NOW, synchronously — the rejection handler\n // runs a microtask later, and fetches started in THIS plan must see\n // the freed capacity.\n this.releaseSlot(entry);\n state.entries.delete(key);\n } else if (entry.scale === wantScale) {\n state.entries.delete(key);\n }\n }\n\n // Center-out: the region under the user's gesture sharpens first.\n const cx = visible.x + visible.width / 2;\n const cy = visible.y + visible.height / 2;\n const span = grid.tileSize / grid.scale;\n const orderedP0 = [...p0].sort((a, b) => {\n const da = (a.ix + 0.5) * span - cx;\n const db = (b.ix + 0.5) * span - cx;\n const ea = (a.iy + 0.5) * span - cy;\n const eb = (b.iy + 0.5) * span - cy;\n return da * da + ea * ea - (db * db + eb * eb);\n });\n\n const kickoff = () => {\n const allP0Ready = this.ensureFetches(\n pon,\n state,\n page,\n grid,\n wantWidth,\n includeAnnotations,\n epoch,\n orderedP0,\n );\n // P1 strictly after P0: prefetch never competes with on-screen tiles.\n if (allP0Ready) {\n this.ensureFetches(pon, state, page, grid, wantWidth, includeAnnotations, epoch, p1);\n }\n };\n\n // Level-change settle: a zoom IN MOTION shouldn't fetch each\n // intermediate level (under exact mode every gesture frame is a new\n // level — this gate is what makes exact affordable). First engagement\n // fires immediately — there's nothing on screen above the base yet.\n if (levelChanged && options.tiles.settleMs > 0) {\n this.deps.debug?.(`arm settle pon=${pon} level=${wantWidth}`);\n state.pendingLevel = wantWidth;\n if (state.settleTimer !== null) clearTimeout(state.settleTimer);\n state.settleTimer = setTimeout(() => {\n state.settleTimer = null;\n this.deps.debug?.(\n `settle fired pon=${pon} level=${wantWidth} current=${state.pendingLevel === wantWidth}`,\n );\n if (state.pendingLevel === wantWidth) kickoff();\n }, options.tiles.settleMs);\n return;\n }\n if (state.settleTimer !== null && !levelChanged && !firstEngage) {\n // Same level again before the timer fired — the zoom came back;\n // cancel the pending level fetch.\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n state.pendingLevel = null;\n }\n kickoff();\n }\n\n /** Start missing fetches; true when every coord is already resolved. */\n private ensureFetches(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n wantWidth: number,\n includeAnnotations: boolean,\n epoch: number,\n coords: TileCoord[],\n ): boolean {\n let allReady = true;\n let started = 0;\n const bleedPt = this.deps.options.tiles.bleedPx / grid.scale;\n for (const coord of coords) {\n const key = this.tileKey(pon, wantWidth, coord, includeAnnotations, epoch);\n // A key that FAILED (non-abort) at this level is not retried until the\n // level or epoch changes — retrying every plan would loop on a\n // permanent error. The base shows through the hole; degraded, honest.\n if (state.failedKeys.has(key)) continue;\n const existing = state.entries.get(key);\n if (existing) {\n if (!existing.resolved) allReady = false;\n continue;\n }\n allReady = false;\n // BACKPRESSURE: bound raw rasters in transit (render + encode). The\n // wake → plan → ensureFetches loop is the pump — each resolution\n // replans and starts the next batch; no queue machinery needed.\n if (this.inFlight >= MAX_IN_FLIGHT) continue;\n this.inFlight += 1;\n started += 1;\n const abort = new AbortController();\n const logical = tilePaintRect(grid, page, coord);\n const entry: TileEntry = {\n key,\n scale: grid.scale,\n coord,\n rect: logical,\n resolved: false,\n painted: false,\n charged: true,\n abort,\n };\n state.entries.set(key, entry);\n this.deps.store\n .acquire(\n key,\n (signal) =>\n this.deps.fetchTile(\n pon,\n // The RENDERED region is the bled rect — it matches the bled\n // placement rect the paint list emits for this entry.\n toEngineRect(page, bleedPt > 0 ? bleedRect(logical, bleedPt, page) : logical),\n grid.scale,\n includeAnnotations,\n signal,\n ),\n abort.signal,\n )\n .then(\n () => {\n this.releaseSlot(entry);\n if (state.entries.get(key) !== entry) return; // aborted/superseded\n // The handle stays in the STORE (single ownership) — the paint\n // list peeks it back out; this entry just records success.\n entry.resolved = true;\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n },\n (err) => {\n this.releaseSlot(entry);\n if (state.entries.get(key) !== entry) return;\n state.entries.delete(key);\n // Our own abort (pan-away, level change) is expected silence. A\n // real failure marks the key and WAKES the layers — the plan\n // recomputes so the rest of the want set keeps making progress\n // instead of waiting on a resolution that will never come.\n if (!abort.signal.aborted) {\n state.failedKeys.add(key);\n this.deps.debug?.(`tile failed ${key}: ${String(err)}`);\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n }\n },\n );\n }\n if (started > 0) this.deps.debug?.(`fetch pon=${pon} level=${wantWidth} +${started} tiles`);\n return allReady;\n }\n\n /**\n * The release rule, evaluated as a PURE FUNCTION of the current painted\n * set: retained sources (any non-want level) whose VISIBLE footprint is\n * covered by PAINTED want-level tiles leave the paint list. Runs on every\n * `plan()` and on every painted report — never only \"against the tile\n * that just painted\": release must not depend on paint ORDER (center-out\n * scheduling means the report that completes a retained tile's coverage\n * routinely lands far away from it), and re-evaluating from current state\n * also self-heals any painted-flag transition the reports raced past.\n * Coverage is index arithmetic over the WANT grid — generic over whatever\n * mix of retained scales exists (exact levels included).\n */\n private releaseCovered(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n includeAnnotations: boolean,\n ): boolean {\n const { wantScale, wantWidth } = state;\n if (wantScale === null || wantWidth === null) return false;\n const grid = tileGrid(page, wantScale, this.strategy().tileSize);\n const paintedAt = (c: TileCoord) => {\n const key = this.tileKey(pon, wantWidth, c, includeAnnotations, state.epoch);\n return state.entries.get(key)?.painted === true;\n };\n let released = false;\n for (const [key, entry] of state.entries) {\n if (entry.scale === wantScale || !entry.resolved) continue;\n // VISIBLE footprint only: an edge parent whose\n // offscreen children were never fetched must still release once its\n // on-screen region is covered — and its bytes stay in the store, so\n // a pan that re-exposes the rest re-promotes from cache.\n const region = state.lastVisible ? intersectRects(entry.rect, state.lastVisible) : entry.rect;\n if (regionCovered(grid, page, region, paintedAt)) {\n state.entries.delete(key);\n released = true;\n } else if (this.deps.debug) {\n const missing = tilesInRect(grid, page, region).filter((c) => !paintedAt(c));\n const detail = missing.slice(0, 3).map((c) => {\n const k = this.tileKey(pon, wantWidth, c, includeAnnotations, state.epoch);\n const e = state.entries.get(k);\n const st = !e ? 'NO-ENTRY' : !e.resolved ? 'PENDING' : 'RESOLVED-unpainted';\n return `${c.ix},${c.iy}=${st}`;\n });\n this.deps.debug(`retained ${key} blocked by: ${detail.join(' ')} (${missing.length} missing)`);\n }\n }\n return released;\n }\n\n /** Test/diagnostic introspection: bookkeeping size for one page. */\n stats(pon: number): { entries: number; inFlight: number } {\n return { entries: this.pages.get(pon)?.entries.size ?? 0, inFlight: this.inFlight };\n }\n\n /** Idempotent transit-slot release — the ONE place inFlight decrements. */\n private releaseSlot(entry: TileEntry): void {\n if (!entry.charged) return;\n entry.charged = false;\n this.inFlight -= 1;\n }\n\n private abortAll(state: PageTileState): void {\n if (state.settleTimer !== null) {\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n }\n for (const entry of state.entries.values()) {\n if (!entry.resolved) {\n entry.abort?.abort();\n this.releaseSlot(entry);\n }\n }\n }\n\n /** Level identity is the integer device width across the page — integer\n * and stable for pyramid rungs and exact levels alike. The key also\n * carries everything that determines the fetched BITMAP for a coord\n * (tile size, bleed, encode format): a raster's identity must include\n * its geometry, or a cached handle from one configuration could be\n * stretched into another's rect. Format genuinely varies at runtime —\n * the cloud policy arrives async after open and can change the resolved\n * format under a live store. */\n private tileKey(\n pon: number,\n levelWidth: number,\n c: TileCoord,\n includeAnnotations: boolean,\n epoch: number,\n ): string {\n const { tiles } = this.deps.options;\n const format = this.strategy().format;\n const geometry = `g${tiles.size}.${tiles.bleedPx}${format ? `.${format}` : ''}`;\n return `t:${pon}|w${levelWidth}|${c.ix},${c.iy}|a${includeAnnotations ? 1 : 0}|e${epoch}|${geometry}`;\n }\n}\n\ninterface TileEntry {\n key: string;\n scale: number;\n coord: TileCoord;\n /** y-down page points. */\n rect: Rect;\n /** Fetch completed — the BYTES live in the RasterStore (peeked at paint\n * time), never here: single ownership is what makes the store's budget\n * the actual bound on residency. */\n resolved: boolean;\n painted: boolean;\n /** Holds an in-flight transit slot (see releaseSlot — idempotent). */\n charged?: boolean;\n abort?: AbortController;\n}\n\ninterface PageTileState {\n epoch: number;\n wantScale: number | null;\n /** The want level's identity: integer device px across the page. */\n wantWidth: number | null;\n entries: Map<string, TileEntry>;\n /** Keys that FAILED (non-abort) at the current level — not retried until\n * the level or epoch changes. */\n failedKeys: Set<string>;\n /** Bumped on ready/painted/drop — the plan memo key. */\n version: number;\n planCache: { demandKey: string; version: number; plan: TilePaintPlan } | null;\n settleTimer: ReturnType<typeof setTimeout> | null;\n /** Pending level identity (wantWidth) while the settle gate runs. */\n pendingLevel: number | null;\n /** Last visible rect from plan() — the release rule's \"on screen\". */\n lastVisible: Rect | null;\n}\n\nconst coordKey = (c: TileCoord): string => `${c.ix},${c.iy}`;\nconst rectKey = (r: Rect): string =>\n `${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}`;\n\nconst pagePonOf = (key: string): number => Number(key.slice(2, key.indexOf('|')));\nconst annotationsOf = (key: string): boolean => key.includes('|a1|');\n","import {\n CONTINUOUS_RENDER_POLICY,\n type EngineRenderPolicy,\n type PageObjectNumber,\n type PageRenderViewport,\n type PluginContext,\n} from '@embedpdf/core';\n\nimport { resolveRenderOptions, type ResolvedRenderOptions } from './paint-plan';\nimport { RasterStore } from './raster-store';\nimport { baseAskWidth, resolveStrategy, type ResolvedStrategy } from './strategy';\nimport { TileManager } from './tile-manager';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * The render capability: the ONE place STRATEGY (plugin options — what the\n * viewer spends) composes with POLICY (the engine fact — what the deployment\n * serves). The engine never snaps; framework layers never see either;\n * everything between — conforming demand to the resolved render points,\n * collapsing same-key asks in the raster store, exposing stable source\n * keys — happens here.\n */\nexport function createRenderCapability(\n ctx: PluginContext<RenderState, RenderAction>,\n options: RenderPluginOptions = {},\n): RenderCapability {\n const resolved: ResolvedRenderOptions = resolveRenderOptions(options);\n\n // Document-lifetime cache; keys embed the conformed width + epoch, so\n // the render points are the cache axis and staleness is a new key, never\n // a flush.\n const store = new RasterStore();\n\n // The policy is a DOCUMENT FACT on the kernel's registry — materialized\n // before publish (one lifecycle, in the kernel). This plugin composes it\n // with the strategy once per policy reference.\n const policy = (): EngineRenderPolicy => ctx.document()?.renderPolicy ?? CONTINUOUS_RENDER_POLICY;\n let strategyMemo: { policy: EngineRenderPolicy; strategy: ResolvedStrategy } | null = null;\n const strategy = (): ResolvedStrategy => {\n const p = policy();\n if (strategyMemo?.policy !== p) {\n strategyMemo = { policy: p, strategy: resolveStrategy(p, resolved) };\n warnDroppedFormat(strategyMemo.strategy);\n }\n return strategyMemo.strategy;\n };\n\n // One-shot developer hints — misconfigurations, not errors.\n let warnedFormat = false;\n const warnDroppedFormat = (s: ResolvedStrategy) => {\n if (warnedFormat || resolved.format === undefined || s.format === resolved.format) return;\n warnedFormat = true;\n console.warn(\n `[render] format '${resolved.format}' is not in the deployment's formats — using '${s.format}'.`,\n );\n };\n let warnedBudget = false;\n const warnPastBudgetNoTiles = (demandWidth: number, supplied: number) => {\n if (warnedBudget || resolved.tiles.enabled || demandWidth <= supplied * 2) return;\n warnedBudget = true;\n console.warn(\n `[render] demand is ${(demandWidth / supplied).toFixed(1)}× over the base budget and the ` +\n `tile plane is disabled (tiles: false) — the page rests blurry. Raise fullPage.maxWidth ` +\n `or re-enable tiles.`,\n );\n };\n\n const pageOf = (pon: PageObjectNumber) =>\n (ctx.document()?.pages ?? []).find((p) => p.pageObjectNumber === pon);\n\n const pageWidthOf = (pon: PageObjectNumber): number => {\n const page = pageOf(pon);\n if (!page) throw new Error(`render: unknown page object number ${pon}`);\n return page.size.width;\n };\n\n // The ONE base-sizing path — renderPage, renderSourceKey, conformViewport,\n // and (via the tile manager) engagement all go through baseAskWidth.\n const conformViewport = (pon: PageObjectNumber, scale: number): PageRenderViewport => {\n const s = strategy();\n const demandWidth = scale * pageWidthOf(pon);\n const width = baseAskWidth(s, demandWidth);\n warnPastBudgetNoTiles(demandWidth, width);\n return { kind: 'width', width };\n };\n\n const epochOf = (pon: PageObjectNumber, includeAnnotations: boolean): number => {\n const s = ctx.getState();\n const content = s.contentEpochs[pon] ?? 0;\n if (!includeAnnotations) return content;\n return content + (s.annotatedEpochs[pon] ?? 0);\n };\n\n // A raster's identity includes its encode format: the cloud policy arrives\n // async after open, so the resolved format can change under a live store —\n // the key must change with it. Absent format (engine default) adds nothing,\n // keeping default-local keys stable.\n const baseKey = (\n pon: PageObjectNumber,\n viewport: PageRenderViewport,\n annotations: boolean,\n ): string => {\n const format = strategy().format;\n return (\n `${pon}|w${viewport.kind === 'width' ? viewport.width : 0}` +\n `|a${annotations ? 1 : 0}|e${epochOf(pon, annotations)}${format ? `|f${format}` : ''}`\n );\n };\n\n // Tile resolutions arrive in bursts (a want set landing) — coalesce the\n // wake-ups per frame so N arrivals become one plan bump, one commit.\n const pendingAdvance = new Set<PageObjectNumber>();\n let advanceScheduled = false;\n const raf: (cb: () => void) => void =\n typeof requestAnimationFrame === 'function'\n ? (cb) => requestAnimationFrame(() => cb())\n : (cb) => void setTimeout(cb, 16);\n const wake = (pon: PageObjectNumber) => {\n pendingAdvance.add(pon);\n if (advanceScheduled) return;\n advanceScheduled = true;\n raf(() => {\n advanceScheduled = false;\n const pons = [...pendingAdvance];\n pendingAdvance.clear();\n for (const p of pons) ctx.dispatch({ type: 'PAINT_ADVANCED', pon: p });\n });\n };\n\n // Tiling shares THIS store, THIS strategy, THIS ledger — one scheduler,\n // one budget, one invalidation truth.\n const tiles = new TileManager({\n store,\n options: resolved,\n getPolicy: policy,\n getPageSize: (pon) => pageOf(pon)?.size,\n getEpoch: epochOf,\n fetchTile: (pon, rect, scale, includeAnnotations, signal) => {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const s = strategy();\n const task = doc.page(pon).render.image({\n target: { kind: 'rect', rect },\n viewport: { kind: 'scale', scale },\n includeAnnotations,\n ...(s.format !== undefined ? { format: s.format } : {}),\n ...(s.quality !== undefined ? { quality: s.quality } : {}),\n });\n if (signal.aborted) task.abort(signal.reason);\n else signal.addEventListener('abort', () => task.abort(signal.reason), { once: true });\n return task;\n },\n onAdvance: wake,\n ...(resolved.debug\n ? { debug: (msg: string) => console.debug(`[render] ${msg}`) }\n : {}),\n });\n\n return {\n renderPage(pon, { scale, includeAnnotations, signal }) {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const annotations = includeAnnotations ?? true;\n const viewport = conformViewport(pon, scale);\n const key = baseKey(pon, viewport, annotations);\n return store.acquire(\n key,\n (storeSignal) => {\n const s = strategy();\n const task = doc.page(pon).render.image({\n viewport,\n includeAnnotations: annotations,\n ...(s.format !== undefined ? { format: s.format } : {}),\n ...(s.quality !== undefined ? { quality: s.quality } : {}),\n });\n if (storeSignal.aborted) task.abort(storeSignal.reason);\n else\n storeSignal.addEventListener('abort', () => task.abort(storeSignal.reason), {\n once: true,\n });\n return task; // AbortablePromise<PageImageHandle> is a Promise<PageImageHandle>\n },\n signal,\n );\n },\n renderSourceKey(pon, { scale, includeAnnotations }) {\n const annotations = includeAnnotations ?? true;\n return baseKey(pon, conformViewport(pon, scale), annotations);\n },\n conformViewport,\n paintSettings() {\n return {\n fadeMs: resolved.tiles.fadeMs,\n tiles: resolved.tiles.enabled,\n };\n },\n renderPolicy: policy,\n tilePlan(pon, demand, opts) {\n return tiles.plan(pon, demand, opts?.includeAnnotations ?? true);\n },\n tilePainted(pon, key) {\n tiles.sourcePainted(pon, key);\n },\n tileUnpainted(pon, key) {\n tiles.sourceUnpainted(pon, key);\n },\n releaseTiles(pon) {\n tiles.releasePage(pon);\n },\n renderEpoch(pon, includeAnnotations = true) {\n // The sum of two monotonic counters is itself a valid monotonic version:\n // a content bump reaches BOTH products; an annotation bump only this one.\n return epochOf(pon, includeAnnotations);\n },\n invalidate({ pons, scope = 'content' } = {}) {\n const target = pons ?? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n if (target.length) ctx.dispatch({ type: 'INVALIDATE', scope, pons: target });\n },\n };\n}\n","/**\n * Makes page rasters REACTIVE — the same event-stream pattern the metadata\n * and annotation plugins use.\n *\n * A render bakes whatever the engine knows into the bitmap, so it goes stale\n * the moment a confirmed mutation changes those pixels. This effect is the\n * BUILT-IN door for such facts: it subscribes to the document event stream\n * and bumps the touched pages' ledger (see reducer.ts); layers key their\n * fetch on `renderEpoch(pon)` and refetch on the bump. The OTHER door is the\n * capability's `invalidate` verb — for plugins whose mutation vocabulary\n * this map doesn't know (redaction, text edit, third-party).\n *\n * Deliberately ORIGIN-AGNOSTIC (unlike the annotation plugin's remote-only\n * fold): a baked raster is stale whether YOU moved the highlight or a\n * collaborator did — the event model's exactly-once guarantee means one bump\n * either way. And because events fire only on CONFIRMED mutations, a drag\n * invalidates once at commit — optimistic previews live in the overlay, never\n * here.\n */\nimport type { DocumentEvent, EffectContext, PageObjectNumber } from '@embedpdf/core';\nimport type { RenderAction, RenderState } from './types';\n\n/**\n * Which pages' ANNOTATED raster a confirmed mutation repaints — the whole\n * event→scope map. Everything the engine emits today is appearance-scoped;\n * when it grows content-mutation events (redaction.applied, content.edited),\n * they get a case here dispatching scope 'content'. Over-invalidation is\n * acceptable (a z-order move that changes nothing repaints one thumb);\n * under-invalidation is the bug.\n */\nexport function annotatedPons(\n event: DocumentEvent,\n allPons: () => PageObjectNumber[],\n): PageObjectNumber[] {\n switch (event.type) {\n case 'annotation.created':\n case 'annotation.updated':\n case 'annotation.deleted':\n case 'annotation.moved': // z-order move — baked stacking can change\n return [event.pageObjectNumber];\n // A field's widgets can live on several pages; the results name exactly\n // the widgets whose appearance changed, each with its page.\n case 'form.valueChanged':\n case 'form.effectsApplied':\n return event.changedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldDeleted':\n return event.removedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldCreated':\n case 'form.fieldUpdated':\n case 'form.widgetAttached':\n case 'form.widgetDetached':\n return event.field.widgets.map((w) => w.pageObjectNumber);\n // Coarse results (counts only, no per-widget detail) — repaint every page.\n case 'form.imported':\n case 'form.repaired':\n return allPons();\n // pages.* replace the page registry: the kernel's `revision` bump already\n // re-keys every layout, and metadata never touches pixels.\n default:\n return [];\n }\n}\n\nexport function registerRenderEffects(ctx: EffectContext<RenderState, RenderAction>): void {\n const doc = ctx.doc;\n if (!doc) return;\n const allPons = () => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Content-scope facts: the page ITSELF changed. A redaction apply\n // destroys page content, so every applied page's base raster is stale —\n // and content strictly contains annotations, so annotated readers bump\n // too (see reducer.ts). Origin-agnostic like the rest of this map.\n if (event.type === 'redaction.applied') {\n const pons = event.results\n .filter((r) => r.status === 'applied')\n .map((r) => r.pageObjectNumber);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'content', pons });\n return;\n }\n const pons = annotatedPons(event, allPons);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'annotations', pons });\n });\n ctx.cleanup(unsubscribe);\n}\n","import type { RenderAction, RenderState } from './types';\n\nexport const initialRenderState = (): RenderState => ({\n contentEpochs: {},\n annotatedEpochs: {},\n paintVersions: {},\n});\n\nconst bump = (\n epochs: Readonly<Record<number, number>>,\n pons: readonly number[],\n): Record<number, number> => {\n const next: Record<number, number> = { ...epochs };\n for (const pon of pons) next[pon] = (next[pon] ?? 0) + 1;\n return next;\n};\n\n/** Pure. Two transitions: bump the touched pages' ledger for the fact's\n * scope (one action per fact, so a batch result bumps each pon once — a\n * 'content' fact bumps ONLY the content ledger; annotated readers sum both,\n * which is how content invalidation reaches them too), and the tile\n * wake-up counter. The render POLICY is deliberately NOT here — it's a\n * document fact on `DocumentMeta`, materialized by the kernel at open. */\nexport const renderReducer = (state: RenderState, a: RenderAction): RenderState => {\n if (a.type === 'PAINT_ADVANCED') {\n // A tile resolution/paint advanced the (store-external) tile state —\n // this bump only wakes subscribed layers to recompute `tilePlan`.\n return { ...state, paintVersions: bump(state.paintVersions, [a.pon]) };\n }\n if (a.type !== 'INVALIDATE' || a.pons.length === 0) return state;\n return a.scope === 'content'\n ? { ...state, contentEpochs: bump(state.contentEpochs, a.pons) }\n : { ...state, annotatedEpochs: bump(state.annotatedEpochs, a.pons) };\n};\n","import {\n createCapabilityToken,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PageImageOptions,\n type PageObjectNumber,\n type PageRenderViewport,\n} from '@embedpdf/core';\n\nimport type { FullPageOptions, PageViewDemand, TilePaintPlan, TilesOptions } from './paint-plan';\n\n/**\n * Options for `renderPlugin()` — the render STRATEGY: what the viewer\n * chooses to spend, and which render points it uses when the engine permits\n * anything. The shape mirrors the deployment policy (`policy.fullPage` /\n * `policy.tiles`), and the one composition rule is: a strategy value\n * applies as written under a `continuous` policy, and the advertised\n * lattice wins when there is one — so the same options run unchanged\n * against local and cloud.\n */\nexport interface RenderPluginOptions {\n /** Base plane: the pixel budget + render points. */\n fullPage?: FullPageOptions;\n /** Tile plane strategy; `false` disables the plane entirely. */\n tiles?: TilesOptions | false;\n /**\n * Encode format for BOTH planes. Unset → engine default (png local, the\n * deployment's format on cloud). `'bmp'` is the local fast path — no\n * compression, no encoder-worker round trip; under a lattice it conforms\n * to `policy.formats`.\n */\n format?: NonNullable<PageImageOptions['format']>;\n /** Encoder quality (webp/png); ignored for bmp. */\n quality?: number;\n /**\n * Diagnostic logging: tile scheduling, settle timing, fetch outcomes —\n * the counters that catch convergence issues in long sessions. Console\n * `debug` level; off by default.\n */\n debug?: boolean;\n}\n\n/** Layer-facing paint settings (resolved options the view layers need). */\nexport interface PaintSettings {\n /** Tile arrival cross-fade (ms); 0 = hard pop. */\n fadeMs: number;\n /** Whether the tile plane is enabled at all (`tiles: false` opts out). */\n tiles: boolean;\n}\n\nexport interface RenderPageOptions {\n /** Device px per PDF point (use the page transform's `renderScale`). */\n scale: number;\n /**\n * Bake annotations into the page bitmap. Default true. Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't painted twice\n * (once baked, once by the overlay).\n */\n includeAnnotations?: boolean;\n /** Abort the render (camera moved / layer unmounted). */\n signal?: AbortSignal;\n}\n\n/**\n * The two invalidation scopes — every pixel-changing fact is one of them:\n *\n * 'annotations' — only baked APPEARANCES changed (an annotation mutated, a\n * form widget re-baked). Base renders keep their pixels.\n * 'content' — the PAGE ITSELF changed (redaction applied, text edited).\n * Invalidates everything: no mutation can change base pixels\n * yet leave an annotated raster valid, so content strictly\n * contains annotations.\n */\nexport type InvalidateScope = 'content' | 'annotations';\n\n/**\n * Per-page versions of the two raster products a page has — base\n * (`includeAnnotations: false`) and annotated. Fed by the document event\n * stream (see effects.ts) and by the `invalidate` verb: a confirmed\n * pixel-changing fact — own or remote — bumps the touched pages, and anything\n * holding a rendered bitmap (a thumbnail rail) refetches.\n */\nexport interface RenderState {\n /** Base-raster versions — bumped by CONTENT facts (redaction, text edit). */\n readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;\n /** Appearance versions — bumped by ANNOTATION facts (annotations, form widgets). */\n readonly annotatedEpochs: Readonly<Record<PageObjectNumber, number>>;\n /**\n * Tile paint-plan wake-ups. The tile manager's state (fetch/painted/\n * retention) lives OUTSIDE the store — it holds live handles and abort\n * controllers — so resolutions bump this counter to make subscribed\n * layers recompute `tilePlan`. The value itself carries no meaning.\n */\n readonly paintVersions: Readonly<Record<PageObjectNumber, number>>;\n}\n\nexport type RenderAction =\n | {\n type: 'INVALIDATE';\n scope: InvalidateScope;\n pons: readonly PageObjectNumber[];\n }\n | { type: 'PAINT_ADVANCED'; pon: PageObjectNumber };\n\nexport interface RenderCapability {\n /**\n * Render a page (by its durable pon) to an ENCODED image. Abortable. Encoded\n * output is identical for local & cloud and cheap over the wire (vs. raw RGBA).\n *\n * This is the VIEWER door: the desired `scale` conforms through the\n * resolved render points (STRATEGY ∧ POLICY) — the exact demand capped at\n * the pixel budget under `continuous`, the advertised ladder under a\n * lattice — and same-key requests collapse in the plugin's raster store\n * (singleflight + LRU). Scale-precise offline output (export, print)\n * belongs on the engine door: `doc.page(pon).render.image(...)`.\n */\n renderPage(pon: PageObjectNumber, options: RenderPageOptions): Promise<PageImageHandle>;\n /**\n * The identity of the raster `renderPage` would produce for these options —\n * conformed width + annotations flag + epoch, as one stable string. Layers\n * key their fetch effect on THIS instead of the raw scale. Under a lattice\n * it moves only at rung crossings; under exact-mode `continuous` it tracks\n * the demand, is constant above the budget (always the budget width), and\n * the LAYER settle-gates adoption so it never refetches mid-gesture.\n * Always available: the kernel materializes the policy on `DocumentMeta`\n * before the document publishes.\n */\n renderSourceKey(\n pon: PageObjectNumber,\n options: { scale: number; includeAnnotations?: boolean },\n ): string;\n /**\n * The canonical viewport a desired scale conforms to for this page —\n * always width-kind: the exact demand capped at the budget (exact mode)\n * or the snapped rung (ladder/lattice mode).\n */\n conformViewport(pon: PageObjectNumber, scale: number): PageRenderViewport;\n /** Resolved layer-facing paint settings (settle, fade, tiles on/off). */\n paintSettings(): PaintSettings;\n /** The document's advertised policy (sugar over `DocumentMeta.renderPolicy`). */\n renderPolicy(): EngineRenderPolicy;\n /**\n * The tile paint plan for a page under a host-supplied demand. Tiling is a\n * STRATEGY inside this plugin, not a sibling.\n * Memoized: the same object returns until the demand, an epoch, or a\n * tile resolution actually changes it, so layers can subscribe with\n * plain `Object.is`. Calling it schedules the want-set fetches (visible\n * first, center-out, prefetch ring after) — idempotent, store-deduped.\n * Engagement is pure arithmetic against what the base will ACTUALLY\n * supply (the same `baseAsk` the base layer sizes with): tiles fire when\n * demand exceeds the budget/ladder cap × engageAt — on every engine.\n * A thumbnail-sized demand never engages.\n */\n tilePlan(\n pon: PageObjectNumber,\n demand: PageViewDemand,\n opts?: { includeAnnotations?: boolean },\n ): TilePaintPlan;\n /**\n * The painter's report: the image for this plan key had a presentation\n * opportunity. Retained coarser generations covered by painted want-set\n * tiles release on this signal — never on fetch completion or image load,\n * which could drop the backdrop before replacement pixels are presented.\n */\n tilePainted(pon: PageObjectNumber, key: string): void;\n /**\n * The inverse report: this plan key's <img> left the DOM, so its pixels\n * are no longer compositable. Painted is a statement about the SCREEN and\n * must follow the DOM — a remounting tile re-decodes, and until it\n * reports painted again, retention may not count it as coverage.\n */\n tileUnpainted(pon: PageObjectNumber, key: string): void;\n /** A lens unmounted its tile plane: abort in-flight tile fetches and drop\n * the page's tile bookkeeping (resolved bytes stay cached). */\n releaseTiles(pon: PageObjectNumber): void;\n /**\n * Version of the raster the given options would produce. Key a long-lived\n * render on it: when it bumps, refetch. Base renders version on content\n * facts; annotated renders on content AND annotation facts. Bumps only on\n * CONFIRMED mutations — never optimistically — so a drag invalidates once,\n * at commit.\n */\n renderEpoch(pon: PageObjectNumber, includeAnnotations?: boolean): number;\n /**\n * Declare that page pixels changed — the open door for facts the built-in\n * event map doesn't know (a plugin's own mutation vocabulary: redaction,\n * text edit, anything third-party). Call at CONFIRMATION (after the engine\n * write resolves), never for optimistic previews — those belong in overlay\n * layers. `pons` omitted = every page; `scope` defaults to 'content'\n * (repaint everything) because a caller who doesn't say is safest repainted\n * fully. Redundant with a mapped engine event? Harmless — one extra refetch.\n */\n invalidate(opts?: { pons?: readonly PageObjectNumber[]; scope?: InvalidateScope }): void;\n}\n\nexport const RenderToken = createCapabilityToken<RenderCapability>('render');\n","import { definePlugin } from '@embedpdf/core';\nimport { createRenderCapability } from './capability';\nimport { registerRenderEffects } from './effects';\nimport { initialRenderState, renderReducer } from './reducer';\nimport { RenderToken } from './types';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * Document-scoped. The ONE policy consumer in the client stack: the doc-bind\n * effect resolves the engine's advertised render\n * policy; `renderPage` conforms desired scales to it and collapses\n * same-rung asks in the raster store; the tile manager turns host-supplied\n * demand into a retention-safe paint plan over the SAME store. State is the\n * per-page ledger — raster versions (two doors: the document event stream's\n * built-in map and the `invalidate` verb), the policy latch, and the tile\n * wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch\n * exactly when those change.\n */\nexport const renderPlugin = (options: RenderPluginOptions = {}) =>\n definePlugin<RenderState, RenderAction, RenderCapability>({\n id: 'render',\n scope: 'document',\n token: RenderToken,\n initialState: initialRenderState,\n reduce: renderReducer,\n capability: (ctx) => createRenderCapability(ctx, options),\n effects: registerRenderEffects,\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embedpdf/plugin-render",
3
- "version": "3.0.0-next.4",
3
+ "version": "3.0.0-next.6",
4
4
  "private": false,
5
5
  "description": "Document-scoped render capability: turns the engine DocumentHandle into an abortable renderPage(pageIndex, scale) → RGBA. The home for tiling.",
6
6
  "type": "module",
@@ -18,8 +18,8 @@
18
18
  "dist"
19
19
  ],
20
20
  "dependencies": {
21
- "@embedpdf/core": "3.0.0-next.4",
22
- "@embedpdf/core-geometry": "3.0.0-next.4"
21
+ "@embedpdf/core": "3.0.0-next.6",
22
+ "@embedpdf/core-geometry": "3.0.0-next.6"
23
23
  },
24
24
  "devDependencies": {
25
25
  "typescript": "^5.0.0",