@embedpdf/plugin-render 3.0.0-next.14 → 3.0.0-next.16
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/contract/host.cjs +18 -0
- package/dist/contract/host.cjs.map +1 -0
- package/dist/contract/host.d.cts +3 -0
- package/dist/contract/host.d.ts +3 -0
- package/dist/contract/host.js +16 -0
- package/dist/contract/host.js.map +1 -0
- package/dist/contract-CGcu-84H.cjs +124 -0
- package/dist/contract-CGcu-84H.cjs.map +1 -0
- package/dist/contract-Dy5TiQDy.js +95 -0
- package/dist/contract-Dy5TiQDy.js.map +1 -0
- package/dist/contract-uc5nOice.d.cts +279 -0
- package/dist/contract-uc5nOice.d.ts +279 -0
- package/dist/contract.cjs +3 -1
- package/dist/contract.d.cts +2 -2
- package/dist/contract.d.ts +2 -2
- package/dist/contract.js +2 -2
- package/dist/host-contract-CTOQn0Xz.d.cts +127 -0
- package/dist/host-contract-CsjZjWjb.d.ts +127 -0
- package/dist/index.cjs +869 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -12
- package/dist/index.d.ts +29 -12
- package/dist/index.js +866 -492
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
- package/dist/contract-Bvlo2Ukp.cjs +0 -11
- package/dist/contract-Bvlo2Ukp.cjs.map +0 -1
- package/dist/contract-BvwW7rrm.js +0 -7
- package/dist/contract-BvwW7rrm.js.map +0 -1
- package/dist/contract-CWPMd1OW.d.cts +0 -332
- package/dist/contract-CWPMd1OW.d.ts +0 -332
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["CONTINUOUS_RENDER_POLICY","PermissionDenied","RenderToken"],"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/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/** One state entry per VIEW-of-page (see the class doc). */\nconst stateKey = (view: string, pon: number): string => `${view}\\u0000${pon}`;\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 /**\n * Tile state is PER VIEW-OF-PAGE, never per page: a document may be shown\n * through several lenses at once (the main view, a thumbnail rail), each\n * calling `plan` with its own demand. One shared entry would let the\n * rail's below-engage demand hit the disengage branch and destroy the\n * main view's tiles on every selector pass (the \"sidebar opens, main view\n * goes blurry\" bug). Keyed by `view\\0pon`; the RasterStore underneath\n * stays shared — bytes dedupe across views by conformed width.\n */\n private readonly pages = new Map<string, 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(view: string, 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, view);\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(view: string, pon: number, key: string): void {\n const state = this.pages.get(stateKey(view, 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(view: string, pon: number, key: string): void {\n const entry = this.pages.get(stateKey(view, 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(view: string, pon: number): void {\n const state = this.pages.get(stateKey(view, pon));\n if (!state) return;\n this.abortAll(state);\n this.pages.delete(stateKey(view, pon));\n }\n\n private pageState(pon: number, view: string): PageTileState {\n let state = this.pages.get(stateKey(view, 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(stateKey(view, 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(view: string, pon: number): { entries: number; inFlight: number } {\n return {\n entries: this.pages.get(stateKey(view, pon))?.entries.size ?? 0,\n inFlight: this.inFlight,\n };\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 PermissionDenied,\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 {\n RenderAction,\n RenderCapability,\n RenderPluginOptions,\n RenderState,\n ViewTiles,\n} 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 // The twin (permissions.md). Both fetch seams below consult it, so a\n // denied session never spends engine round-trips on doomed rasters.\n const canRender = (): boolean => ctx.doc?.security.allows('doc.render') ?? false;\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 // View-scoped tile handles (see RenderCapability.tilesFor).\n const viewTiles = new Map<string, ViewTiles>();\n\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 // Same refusal the engine would send, without the round trip — a denied\n // session's viewport would otherwise 403 once per tile, forever.\n if (!canRender()) return Promise.reject(new PermissionDenied('doc.render', 'render.tile'));\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 canRender,\n renderPage(pon, { scale, includeAnnotations, signal }) {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n if (!canRender())\n return Promise.reject(new PermissionDenied('doc.render', 'render.renderPage'));\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 tilesFor(view) {\n // One handle per view, reference-stable (a clean hook dependency); the\n // map lives for the document, like the state it scopes.\n let handle = viewTiles.get(view);\n if (!handle) {\n handle = {\n plan: (pon, demand, opts) => tiles.plan(view, pon, demand, opts?.includeAnnotations ?? true),\n painted: (pon, key) => tiles.sourcePainted(view, pon, key),\n unpainted: (pon, key) => tiles.sourceUnpainted(view, pon, key),\n release: (pon) => tiles.releasePage(view, pon),\n };\n viewTiles.set(view, handle);\n }\n return handle;\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 { 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.cjs","names":["abortReason","EMPTY_TILE_PLAN","resolveRenderOptions","CONTINUOUS_RENDER_POLICY","EMPTY_TILE_PLAN","RenderToken","RENDER_DEFAULTS"],"sources":["../src/appearance-store.ts","../src/invalidation.ts","../src/model.ts","../src/raster-store.ts","../src/strategy.ts","../src/tiles.ts","../src/tile-manager.ts","../src/controller.ts","../src/render.plugin.ts"],"sourcesContent":["/**\n * The engine's pictures of a page's annotations or form widgets, shared\n * between the views and plugins that draw them. One ask is one key (the\n * page, the family, the scale and the owner's items at their versions):\n * asks with the same key share one fetch.\n *\n * Consumer aborts are refcounted, as in the raster store: a view that leaves\n * detaches without stopping the fetch for the others; the fetch stops only\n * when the last one leaves before it resolved.\n *\n * Kept for undo:\n * - the last {@link SETS_KEPT} sets of each page and family: an undo that\n * brings a page back to an earlier set asks the engine for nothing;\n * - each item's last pictures ({@link RecentPictures}), a while after it left\n * its page: an undo that brings it back shows them at once.\n */\nexport class AppearanceStore<Picture> {\n private readonly entries = new Map<string, Entry<Picture>>();\n /** The keys of the resolved sets kept for each page and family, newest last. */\n private readonly kept = new Map<string, string[]>();\n\n /** `group` names the page and family; `key` the whole ask. */\n acquire(\n group: string,\n key: string,\n fetch: (signal: AbortSignal) => Promise<readonly Picture[]>,\n signal?: AbortSignal,\n ): Promise<readonly Picture[]> {\n const existing = this.entries.get(key);\n if (existing) {\n if (existing.resolved) this.keep(group, key);\n return this.attach(key, existing, signal);\n }\n\n const entry: Entry<Picture> = {\n group,\n promise: undefined as unknown as Promise<readonly Picture[]>,\n refs: 0,\n abort: new AbortController(),\n resolved: false,\n };\n entry.promise = fetch(entry.abort.signal).then(\n (pictures) => {\n entry.resolved = true;\n this.keep(group, key);\n return pictures;\n },\n (error) => {\n // A failed fetch isn't kept: the next ask tries again.\n this.drop(key, entry);\n throw error;\n },\n );\n // Each consumer sees the rejection through its own attachment.\n entry.promise.catch(() => {});\n this.entries.set(key, entry);\n return this.attach(key, entry, signal);\n }\n\n /** Keep `key` as the group's newest set, letting go of the oldest beyond the few kept. */\n private keep(group: string, key: string): void {\n const kept = (this.kept.get(group) ?? []).filter((each) => each !== key);\n kept.push(key);\n const gone = kept.splice(0, Math.max(0, kept.length - SETS_KEPT));\n this.kept.set(group, kept);\n for (const old of gone) {\n const entry = this.entries.get(old);\n if (entry && entry.refs <= 0) this.entries.delete(old);\n }\n }\n\n private isKept(group: string, key: string): boolean {\n return this.kept.get(group)?.includes(key) ?? false;\n }\n\n private attach(\n key: string,\n entry: Entry<Picture>,\n signal?: AbortSignal,\n ): Promise<readonly Picture[]> {\n if (signal?.aborted) return Promise.reject(abortReason(signal));\n entry.refs += 1;\n if (!signal) {\n const release = () => this.release(key, entry);\n entry.promise.then(release, release);\n return entry.promise;\n }\n let settled = false;\n return new Promise<readonly Picture[]>((resolve, reject) => {\n const onAbort = () => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n if (entry.refs <= 0 && !entry.resolved) {\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 (pictures) => {\n if (settled) return;\n settled = true;\n this.release(key, entry);\n signal.removeEventListener('abort', onAbort);\n resolve(pictures);\n },\n (error) => {\n if (settled) return;\n settled = true;\n this.release(key, entry);\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n }\n\n /** A consumer let go: a set no longer kept goes with the last one. */\n private release(key: string, entry: Entry<Picture>): void {\n entry.refs -= 1;\n if (entry.refs <= 0 && entry.resolved && !this.isKept(entry.group, key)) {\n this.drop(key, entry);\n }\n }\n\n private drop(key: string, entry: Entry<Picture>): void {\n if (this.entries.get(key) === entry) this.entries.delete(key);\n }\n}\n\n/** How many of each page's latest sets are kept, per family: an undo goes back to one. */\nexport const SETS_KEPT = 4;\n\n/** How many items' last pictures are kept, per family, for an undo that brings one back. */\nexport const ITEMS_KEPT = 200;\n\n/**\n * The last pictures of each item (all its states), the newest {@link ITEMS_KEPT}\n * items per family, kept after the item left its page.\n */\nexport class RecentPictures<Picture> {\n private readonly families = new Map<string, Map<string, readonly Picture[]>>();\n\n /** A fetch's pictures, grouped by item: each replaces what its item had. */\n remember(family: string, pictures: readonly Picture[], keyOf: (picture: Picture) => string) {\n const items = this.families.get(family) ?? new Map<string, readonly Picture[]>();\n const byItem = new Map<string, Picture[]>();\n for (const picture of pictures) {\n const key = keyOf(picture);\n byItem.set(key, [...(byItem.get(key) ?? []), picture]);\n }\n for (const [key, itemPictures] of byItem) {\n items.delete(key);\n items.set(key, itemPictures);\n }\n while (items.size > ITEMS_KEPT) items.delete(items.keys().next().value!);\n this.families.set(family, items);\n }\n\n of(family: string, key: string): readonly Picture[] {\n return this.families.get(family)?.get(key) ?? [];\n }\n}\n\ninterface Entry<Picture> {\n group: string;\n promise: Promise<readonly Picture[]>;\n refs: number;\n abort: AbortController;\n resolved: boolean;\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('aborted', 'AbortError');\n}\n","/**\n * The built-in event → pixels map: which pages' pictures a confirmed fact\n * repaints, and at which scope. `annotations` scope repaints the pictures\n * that draw annotations, `fields` the ones that draw form fields, and\n * `content` every picture (a redaction or a flatten rewrites the content\n * stream). Over-invalidation is acceptable (a z-order move that changes\n * nothing repaints one thumbnail); under-invalidation is the bug.\n *\n * The map ignores origin: a baked raster is stale whether this session moved\n * the highlight or a collaborator did. Events fire only for confirmed\n * mutations, so a drag invalidates once at commit; optimistic previews live\n * in the overlay, never here.\n */\nimport type { DocumentEvent, PageObjectNumber, PageRef } from '@embedpdf/core';\n\nimport type { InvalidateScope } from './contract';\n\nexport interface PixelChange {\n readonly pages: readonly PageObjectNumber[];\n readonly scope: InvalidateScope;\n}\n\nconst placedPages = (widgets: ReadonlyArray<{ page: PageRef | null }>): PageObjectNumber[] =>\n widgets.flatMap((widget) => (widget.page ? [widget.page.objectNumber] : []));\n\nconst changeOf = (\n pages: readonly PageObjectNumber[],\n scope: InvalidateScope,\n): PixelChange | null => (pages.length ? { pages: [...new Set(pages)], scope } : null);\n\n/**\n * The pages a form fact repaints. Every form fact names the widgets whose\n * look changed or that went (`meta.changedWidgets`), each with its page; the\n * widget rows it carries, and a deleted widget's page, are counted too. An\n * unplaced widget (`page: null`) has no pixels to repaint.\n */\nfunction formPagesOf(\n event: Extract<DocumentEvent, { meta: { changedWidgets: unknown } }>,\n): PageObjectNumber[] {\n const pages = placedPages(event.meta.changedWidgets);\n if ('widgets' in event) pages.push(...placedPages(event.widgets));\n if ('page' in event && event.page) pages.push(event.page.objectNumber);\n return pages;\n}\n\nexport function pixelChangeOf(\n event: DocumentEvent,\n allPageObjectNumbers: () => PageObjectNumber[],\n): PixelChange | null {\n switch (event.type) {\n case 'annotations.created':\n case 'annotations.updated':\n case 'annotations.deleted':\n case 'annotations.restored':\n case 'annotations.reordered': // z-order: baked stacking can change\n return changeOf([event.page.objectNumber], 'annotations');\n case 'forms.valueSet':\n case 'forms.created':\n case 'forms.updated':\n case 'forms.deleted':\n case 'forms.restored':\n case 'forms.widgetAdded':\n case 'forms.widgetRemoved':\n case 'forms.widgetDeleted':\n case 'forms.widgetRestored':\n case 'forms.widgetUpdated':\n case 'forms.widgetsReordered':\n return changeOf(formPagesOf(event), 'fields');\n // A coarse result (counts only, no per-widget detail): repaint every page.\n case 'forms.repaired':\n return changeOf(allPageObjectNumbers(), 'fields');\n // Sealing bakes the signature into its widget's appearance.\n case 'signatures.completed':\n return changeOf(\n event.signature.widget ? placedPages([event.signature.widget]) : [],\n 'fields',\n );\n // These rewrite the page content itself.\n case 'redaction.applied':\n case 'pages.flattened':\n return changeOf(\n event.results\n .filter((result) => result.status === 'applied')\n .map((result) => result.page.objectNumber),\n 'content',\n );\n case 'annotations.flattened':\n return changeOf(\n event.results.some((result) => result.status === 'applied')\n ? [event.page.objectNumber]\n : [],\n 'content',\n );\n // The stream lost events that will never arrive: any page may be stale.\n case 'stream.desynced':\n return changeOf(allPageObjectNumbers(), 'content');\n // pages.* replace the page registry: the kernel's `revision` bump already\n // re-keys every layout, and metadata never touches pixels. A new saved\n // version changes no pixels beyond the signature it seals. The\n // calculation order changes no pixels.\n default:\n return null;\n }\n}\n","/**\n * The render ledger: per-page raster versions. Every function below is a\n * pure transition or projection; the controller applies the transitions with\n * `ctx.state.update`.\n */\nimport type { PageObjectNumber, PageRenderLayers } from '@embedpdf/core';\nimport type { InvalidateScope } from './contract';\n\n/**\n * Per-page versions of what a page picture can draw: the page content, its\n * annotations and its form fields. Fed by the document event stream and by\n * the `invalidate` verb: a confirmed pixel-changing fact, own or remote,\n * bumps the touched pages, and anything holding a rendered bitmap refetches.\n * A picture's version is the sum of the parts it draws, and is part of every\n * raster and tile key.\n */\nexport interface RenderState {\n /** Content versions, bumped by content facts (redaction, flatten). */\n readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;\n /** Annotation versions, bumped by annotation facts (widgets excepted). */\n readonly annotationEpochs: Readonly<Record<PageObjectNumber, number>>;\n /** Form field versions, bumped by form facts: a fill, a widget's move or look, a signing. */\n readonly fieldEpochs: Readonly<Record<PageObjectNumber, number>>;\n}\n\nexport const initialRenderState = (): RenderState => ({\n contentEpochs: {},\n annotationEpochs: {},\n fieldEpochs: {},\n});\n\nconst bump = (\n epochs: Readonly<Record<PageObjectNumber, number>>,\n pageObjectNumbers: readonly PageObjectNumber[],\n): Record<PageObjectNumber, number> => {\n const next: Record<PageObjectNumber, number> = { ...epochs };\n for (const pageObjectNumber of pageObjectNumbers) {\n next[pageObjectNumber] = (next[pageObjectNumber] ?? 0) + 1;\n }\n return next;\n};\n\n/**\n * Bump the pages' versions in the ledger the scope names. A content fact\n * bumps only the content ledger; every picture counts content (see\n * {@link renderEpochOf}), which is how content invalidation reaches them all.\n */\nexport function invalidatePages(\n state: RenderState,\n pageObjectNumbers: readonly PageObjectNumber[],\n scope: InvalidateScope,\n): RenderState {\n if (pageObjectNumbers.length === 0) return state;\n switch (scope) {\n case 'content':\n return { ...state, contentEpochs: bump(state.contentEpochs, pageObjectNumbers) };\n case 'annotations':\n return { ...state, annotationEpochs: bump(state.annotationEpochs, pageObjectNumbers) };\n case 'fields':\n return { ...state, fieldEpochs: bump(state.fieldEpochs, pageObjectNumbers) };\n }\n}\n\n/** The version of one picture of a page: its content, plus the annotations and form fields it draws. */\nexport function renderEpochOf(\n state: RenderState,\n pageObjectNumber: PageObjectNumber,\n layers: PageRenderLayers,\n): number {\n return (\n (state.contentEpochs[pageObjectNumber] ?? 0) +\n (layers.includeAnnotations ? (state.annotationEpochs[pageObjectNumber] ?? 0) : 0) +\n (layers.includeFormFields ? (state.fieldEpochs[pageObjectNumber] ?? 0) : 0)\n );\n}\n\n/** What a picture draws, as one key part: `a1f0` draws annotations and no form fields. */\nexport const layersKeyOf = (layers: PageRenderLayers): string =>\n `a${layers.includeAnnotations ? 1 : 0}f${layers.includeFormFields ? 1 : 0}`;\n","import type { PageImageHandle } from '@embedpdf/core';\n\n/**\n * The client-side counterpart of the server's read-through cache: per-key\n * singleflight + cost-budgeted LRU over encoded page images.\n *\n * The key is the raster's canonical identity (for page rasters: width,\n * annotations flag, epoch and format, built by `rasterKey` in the\n * controller; for tiles: `TileManager`'s tile key), 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 (error) => {\n // Failed fetches must not be sticky — drop so the next ask retries.\n this.drop(key, entry);\n throw error;\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 (error) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n reject(error);\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(([, entry]) => entry.refs <= 0 && entry.handle !== undefined)\n .sort((left, right) => left[1].used - right[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[] =>\n [...values].sort((left, right) => left - right);\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((width) => width <= 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\n ? ascending(policy.tiles.scales)\n : (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. Its callers: the controller's\n * `conformViewport` (which `renderSource` and `getSourceKey` go through)\n * and tile engagement in the tile manager. 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 (\n strategy.widths.find((width) => width >= wanted) ?? strategy.widths[strategy.widths.length - 1]!\n );\n}\n","import 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 and engine render targets (`target: {kind:'rect'}`) are\n * page space, y-down page points (the layer multiplies by one container\n * transform).\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 firstColumn = Math.max(0, Math.floor(rect.x / span));\n const firstRow = Math.max(0, Math.floor(rect.y / span));\n const lastColumn = Math.min(grid.cols - 1, Math.ceil((rect.x + rect.width) / span) - 1);\n const lastRow = Math.min(grid.rows - 1, Math.ceil((rect.y + rect.height) / span) - 1);\n const coords: TileCoord[] = [];\n for (let iy = firstRow; iy <= lastRow; iy++) {\n for (let ix = firstColumn; ix <= lastColumn; ix++) coords.push({ ix, iy });\n }\n return coords;\n}\n\n/** Paint rect: y-down page points, clamped to the page edge. */\nexport function tilePaintRect(grid: TileGrid, page: PageSizePt, coord: TileCoord): Rect {\n const span = tileSpanPt(grid);\n const x = coord.ix * span;\n const y = coord.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/**\n * Expand a paint rect by `amountPt` 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, amountPt: number, page: PageSizePt): Rect {\n const x = Math.max(0, rect.x - amountPt);\n const y = Math.max(0, rect.y - amountPt);\n return {\n x,\n y,\n width: Math.min(page.width, rect.x + rect.width + amountPt) - x,\n height: Math.min(page.height, rect.y + rect.height + amountPt) - 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: (coord: TileCoord) => boolean,\n): boolean {\n if (region.width <= 0 || region.height <= 0) return true;\n for (const coord of tilesInRect(grid, page, region)) {\n if (!isPainted(coord)) 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 marginX = rect.width * margin;\n const marginY = rect.height * margin;\n let left = marginX;\n let right = marginX;\n let up = marginY;\n let down = marginY;\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 = marginX * 2;\n left = marginX / 2;\n } else if (velocity.dx < 0) {\n left = marginX * 2;\n right = marginX / 2;\n }\n if (velocity.dy > 0) {\n down = marginY * 2;\n up = marginY / 2;\n } else if (velocity.dy < 0) {\n up = marginY * 2;\n down = marginY / 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((left, right) => left - right);\n return sorted.find((scale) => scale >= needed) ?? sorted[sorted.length - 1]!;\n}\n","import {\n memo,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PageRenderLayers,\n} 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 { layersKeyOf } from './model';\nimport type { RasterStore } from './raster-store';\nimport { baseAskWidth, resolveStrategy } from './strategy';\nimport {\n bleedRect,\n inflateRect,\n intersectRects,\n regionCovered,\n snapToPyramid,\n tileGrid,\n tilesInRect,\n tilePaintRect,\n type PageSizePt,\n type TileCoord,\n type TileGrid,\n} from './tiles';\n\n/** One state entry per view of a page (see {@link TileManager}'s `pages`). */\nconst stateKey = (view: string, pageObjectNumber: number): string =>\n `${view}\\u0000${pageObjectNumber}`;\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\n/**\n * The tile retention state machine. One instance per plugin instance (per\n * document), all levels and pages in one place, over the same RasterStore\n * 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()` queues the want set (visible tiles first,\n * center-out, then the prefetch ring once every visible tile resolved)\n * and returns a paint list drawn only from resolved entries: the\n * current level and retained older generations.\n * - one line for every view and page: a transit slot goes to an on-screen\n * tile of any page before a tile of any prefetch ring, so a ring never\n * takes the slots an on-screen tile needs. The engine ranks the tiles it\n * is given by where each one is on screen.\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 covered by painted want tiles leave the paint\n * list. Their bytes stay in the RasterStore (demotion, not eviction), so\n * zooming back re-promotes from cache.\n * - epoch exception: an invalidation bump means retained pixels are\n * wrong, so 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()` never wakes readers itself: its callers do, once, when the\n * returned plan changed. Fetch kickoff is idempotent (the store\n * singleflights); resolution handlers call `onAdvance`, which re-plans the\n * page and wakes subscribed layers, and hand the freed slot on.\n */\nexport class TileManager {\n /**\n * Tile state is per view-of-page, never per page: a document may be shown\n * through several lenses at once (the main view, a thumbnail rail), each\n * calling `plan` with its own demand. One shared entry would let the\n * rail's below-engage demand hit the disengage branch and destroy the\n * main view's tiles on every re-plan (the sidebar opens and the main view\n * goes blurry). Keyed by {@link stateKey}; the RasterStore underneath\n * stays shared — bytes dedupe across views by conformed width.\n */\n private readonly pages = new Map<string, PageTileState>();\n /** The resolved strategy, recomputed when the policy or the settings change. */\n private readonly strategy = memo(\n () => [this.deps.getPolicy(), this.deps.getOptions()] as const,\n (policy, options) => resolveStrategy(policy, options),\n );\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 /** The render settings as they are now: read at every use, so a change applies at once. */\n getOptions(): 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(pageObjectNumber: number): PageSizePt | undefined;\n getEpoch(pageObjectNumber: number, layers: PageRenderLayers): number;\n /** Run `run` once, `ms` from now; returns its cancel (the plugin's `ctx.clock.after`). */\n after(ms: number, run: () => void): () => void;\n /**\n * Render a page-space (y-down page points) region for `view`, whose\n * pixels it is: the engine ranks it by where the region is in that view.\n */\n fetchTile(\n view: string,\n pageObjectNumber: number,\n rect: Rect,\n scale: number,\n layers: PageRenderLayers,\n signal: AbortSignal,\n ): Promise<PageImageHandle>;\n /** A page's plans changed outside `plan()`: re-plan it and wake subscribed layers. */\n onAdvance(pageObjectNumber: number): void;\n /** Diagnostic sink — scheduling and fetch outcomes; it logs while `debug` is set. */\n debug?(message: string): void;\n },\n ) {}\n\n plan(\n view: string,\n pageObjectNumber: number,\n demand: PageViewDemand,\n layers: PageRenderLayers,\n ): TilePaintPlan {\n const options = this.deps.getOptions();\n if (!options.tiles.enabled) return EMPTY_TILE_PLAN;\n const strategy = this.strategy();\n const page = this.deps.getPageSize(pageObjectNumber);\n if (!page) return EMPTY_TILE_PLAN;\n\n const epoch = this.deps.getEpoch(pageObjectNumber, layers);\n const state = this.pageState(pageObjectNumber, view);\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 `baseAskWidth` 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 page=${pageObjectNumber} (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 this.pump();\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(strategy.tileSize * Math.sqrt((STAGELESS_TILE_CAP * page.width) / page.height)),\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 (scale) => Math.round(scale * 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 visibleCoords = tilesInRect(grid, page, visible);\n const ring = inflateRect(\n visible,\n options.tiles.prefetchMargin,\n options.tiles.velocityBias ? demand.velocity : undefined,\n );\n const visibleKeys = new Set(visibleCoords.map((coord) => coordKey(coord)));\n const prefetchCoords = tilesInRect(grid, page, ring).filter(\n (coord) => !visibleKeys.has(coordKey(coord)),\n );\n\n this.schedule(\n pageObjectNumber,\n state,\n page,\n grid,\n wantScale,\n wantWidth,\n layers,\n epoch,\n visibleCoords,\n prefetchCoords,\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(pageObjectNumber, state, page, layers)) {\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.getOptions().tiles.bleedPx;\n for (const entry of state.entries.values()) {\n const visiblePart = intersectRects(entry.rect, visible);\n if (entry.resolved) {\n if (visiblePart.width > 0 && visiblePart.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 const scales = [...new Set(paint.map((source) => source.scale))];\n for (const scale of scales.sort((left, right) => left - right)) rank.set(scale, rank.size);\n for (const source of paint) source.z = rank.get(source.scale)!;\n paint.sort((left, right) => left.z - right.z || left.key.localeCompare(right.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(view: string, pageObjectNumber: number, key: string): void {\n const state = this.pages.get(stateKey(view, pageObjectNumber));\n const entry = state?.entries.get(key);\n if (!state || !entry || entry.painted) return;\n entry.painted = true;\n const page = this.deps.getPageSize(pageObjectNumber);\n if (page) this.releaseCovered(pageObjectNumber, state, page, layersOfKey(key));\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pageObjectNumber);\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(view: string, pageObjectNumber: number, key: string): void {\n const entry = this.pages.get(stateKey(view, pageObjectNumber))?.entries.get(key);\n if (entry) entry.painted = false;\n }\n\n /** A lens stopped painting tiles for the page (it left the view, or its\n * layer paints none): stop fetching, drop bookkeeping. Resolved bytes stay\n * in the RasterStore for a re-mount. */\n releasePage(view: string, pageObjectNumber: number): void {\n const state = this.pages.get(stateKey(view, pageObjectNumber));\n if (!state) return;\n this.abortAll(state);\n this.pages.delete(stateKey(view, pageObjectNumber));\n this.pump();\n }\n\n private pageState(pageObjectNumber: number, view: string): PageTileState {\n let state = this.pages.get(stateKey(view, pageObjectNumber));\n if (!state) {\n state = {\n view,\n pageObjectNumber,\n queue: null,\n epoch: -1,\n wantScale: null,\n wantWidth: null,\n entries: new Map(),\n failedKeys: new Set(),\n version: 0,\n planCache: null,\n cancelSettle: null,\n pendingLevel: null,\n lastVisible: null,\n };\n this.pages.set(stateKey(view, pageObjectNumber), state);\n }\n return state;\n }\n\n private schedule(\n pageObjectNumber: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n wantScale: number,\n wantWidth: number,\n layers: PageRenderLayers,\n epoch: number,\n visibleCoords: TileCoord[],\n prefetchCoords: TileCoord[],\n visible: Rect,\n ): void {\n const options = this.deps.getOptions();\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 keyOf = (coord: TileCoord) =>\n this.tileKey(pageObjectNumber, wantWidth, coord, layers, epoch);\n const wanted = new Set([...visibleCoords, ...prefetchCoords].map(keyOf));\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 centerX = visible.x + visible.width / 2;\n const centerY = visible.y + visible.height / 2;\n const span = grid.tileSize / grid.scale;\n const distanceToCenter = (coord: TileCoord): number => {\n const dx = (coord.ix + 0.5) * span - centerX;\n const dy = (coord.iy + 0.5) * span - centerY;\n return dx * dx + dy * dy;\n };\n const orderedVisible = [...visibleCoords].sort(\n (left, right) => distanceToCenter(left) - distanceToCenter(right),\n );\n\n const kickoff = () => {\n // The prefetch ring strictly after the visible tiles: prefetch never\n // competes with on-screen tiles. A tile that failed at this level counts\n // as done; the base shows through its hole.\n const allVisibleReady = orderedVisible.every((coord) => {\n const key = keyOf(coord);\n return state.failedKeys.has(key) || state.entries.get(key)?.resolved === true;\n });\n state.queue = {\n coords: allVisibleReady ? [...orderedVisible, ...prefetchCoords] : orderedVisible,\n ringFrom: orderedVisible.length,\n next: 0,\n page,\n grid,\n wantWidth,\n layers,\n epoch,\n };\n this.pump();\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 page=${pageObjectNumber} level=${wantWidth}`);\n // Nothing of the old level starts meanwhile.\n state.queue = null;\n state.pendingLevel = wantWidth;\n state.cancelSettle?.();\n state.cancelSettle = this.deps.after(options.tiles.settleMs, () => {\n state.cancelSettle = null;\n this.deps.debug?.(\n `settle fired page=${pageObjectNumber} level=${wantWidth} ` +\n `current=${state.pendingLevel === wantWidth}`,\n );\n if (state.pendingLevel === wantWidth) kickoff();\n });\n return;\n }\n if (state.cancelSettle !== null && !levelChanged && !firstEngage) {\n // Same level again before the timer fired — the zoom came back;\n // cancel the pending level fetch.\n state.cancelSettle();\n state.cancelSettle = null;\n state.pendingLevel = null;\n }\n kickoff();\n }\n\n /**\n * Starts queued tiles while transit slots are free: the first on-screen\n * tile of any view and page, in the order their queues were set, before\n * any tile of a prefetch ring. Within one view of a page, its queue's order\n * holds. Backpressure bounds raw rasters in transit (render + encode); each\n * resolution hands its slot on through here, so no other pump is needed.\n */\n private pump(): void {\n const started = new Map<PageTileState, number>();\n while (this.inFlight < MAX_IN_FLIGHT) {\n let next: { state: PageTileState; coord: TileCoord } | null = null;\n for (const state of this.pages.values()) {\n const queued = this.nextToStart(state);\n if (!queued || (next && queued.prefetch)) continue;\n next = { state, coord: queued.coord };\n if (!queued.prefetch) break;\n }\n if (!next) break;\n this.start(next.state, next.coord);\n started.set(next.state, (started.get(next.state) ?? 0) + 1);\n }\n for (const [state, count] of started) {\n this.deps.debug?.(\n `fetch page=${state.pageObjectNumber} level=${state.queue?.wantWidth} +${count} tiles`,\n );\n }\n }\n\n /** The first tile of the state's queue not started yet, nor failed at this level. */\n private nextToStart(state: PageTileState): { coord: TileCoord; prefetch: boolean } | null {\n const queue = state.queue;\n if (!queue) return null;\n for (; queue.next < queue.coords.length; queue.next += 1) {\n const coord = queue.coords[queue.next]!;\n const key = this.tileKey(\n state.pageObjectNumber,\n queue.wantWidth,\n coord,\n queue.layers,\n queue.epoch,\n );\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) || state.entries.has(key)) continue;\n return { coord, prefetch: queue.next >= queue.ringFrom };\n }\n return null;\n }\n\n /** Starts one queued tile's fetch, in a transit slot. */\n private start(state: PageTileState, coord: TileCoord): void {\n const { page, grid, wantWidth, layers, epoch } = state.queue!;\n const pageObjectNumber = state.pageObjectNumber;\n const key = this.tileKey(pageObjectNumber, wantWidth, coord, layers, epoch);\n const bleedPt = this.deps.getOptions().tiles.bleedPx / grid.scale;\n this.inFlight += 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 // The cached plan's `fetching` misses this tile now.\n state.planCache = null;\n this.deps.store\n .acquire(\n key,\n (signal) =>\n this.deps.fetchTile(\n state.view,\n pageObjectNumber,\n // The rendered region is the bled rect — it matches the bled\n // placement rect the paint list emits for this entry.\n bleedPt > 0 ? bleedRect(logical, bleedPt, page) : logical,\n grid.scale,\n layers,\n signal,\n ),\n abort.signal,\n )\n .then(\n () => {\n this.releaseSlot(entry);\n // Unless aborted or superseded meanwhile. The handle stays in the\n // store (single ownership) — the paint list peeks it back out;\n // this entry just records success.\n if (state.entries.get(key) === entry) {\n entry.resolved = true;\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pageObjectNumber);\n }\n this.pump();\n },\n (error) => {\n this.releaseSlot(entry);\n // Our own abort (pan-away, level change) is expected silence. A\n // real failure marks the key and wakes the layers: the paint plan\n // recomputes so the rest of the want set keeps making progress\n // instead of waiting on a resolution that never comes.\n if (state.entries.get(key) === entry) {\n state.entries.delete(key);\n if (!abort.signal.aborted) {\n state.failedKeys.add(key);\n this.deps.debug?.(`tile failed ${key}: ${String(error)}`);\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pageObjectNumber);\n }\n }\n this.pump();\n },\n );\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 pageObjectNumber: number,\n state: PageTileState,\n page: PageSizePt,\n layers: PageRenderLayers,\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 wantKeyOf = (coord: TileCoord) =>\n this.tileKey(pageObjectNumber, wantWidth, coord, layers, state.epoch);\n const paintedAt = (coord: TileCoord) => state.entries.get(wantKeyOf(coord))?.painted === true;\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 offscreen children\n // were never fetched must still release once its on-screen region is\n // covered, and its bytes stay in the store, so a pan that re-exposes\n // 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 && this.deps.getOptions().debug) {\n const missing = tilesInRect(grid, page, region).filter((coord) => !paintedAt(coord));\n const detail = missing.slice(0, 3).map((coord) => {\n const missingEntry = state.entries.get(wantKeyOf(coord));\n const status = !missingEntry\n ? 'NO-ENTRY'\n : !missingEntry.resolved\n ? 'PENDING'\n : 'RESOLVED-unpainted';\n return `${coord.ix},${coord.iy}=${status}`;\n });\n this.deps.debug(\n `retained ${key} blocked by: ${detail.join(' ')} (${missing.length} missing)`,\n );\n }\n }\n return released;\n }\n\n /** Test/diagnostic introspection: bookkeeping size for one page. */\n stats(view: string, pageObjectNumber: number): { entries: number; inFlight: number } {\n return {\n entries: this.pages.get(stateKey(view, pageObjectNumber))?.entries.size ?? 0,\n inFlight: this.inFlight,\n };\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 state.queue = null;\n state.cancelSettle?.();\n state.cancelSettle = null;\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 pageObjectNumber: number,\n levelWidth: number,\n coord: TileCoord,\n layers: PageRenderLayers,\n epoch: number,\n ): string {\n const { tiles } = this.deps.getOptions();\n const format = this.strategy().format;\n const geometry = `g${tiles.size}.${tiles.bleedPx}${format ? `.${format}` : ''}`;\n return (\n `t:${pageObjectNumber}|w${levelWidth}|${coord.ix},${coord.iy}` +\n `|${layersKeyOf(layers)}|e${epoch}|${geometry}`\n );\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\n/** The tiles one view of a page wants started, in order, with what starting one needs. */\ninterface TileQueue {\n /** Visible tiles center-out, then the prefetch ring once every visible tile resolved. */\n coords: TileCoord[];\n /** Where the ring starts in `coords`. */\n ringFrom: number;\n /** Where the pump looks next: every coord before it is started or failed. */\n next: number;\n page: PageSizePt;\n grid: TileGrid;\n wantWidth: number;\n layers: PageRenderLayers;\n epoch: number;\n}\n\ninterface PageTileState {\n view: string;\n pageObjectNumber: number;\n /** What the pump starts for this view of the page; null while nothing should start. */\n queue: TileQueue | null;\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; part of the paint-plan memo key. */\n version: number;\n planCache: { demandKey: string; version: number; plan: TilePaintPlan } | null;\n /** Cancels the level-change settle wait while it runs. */\n cancelSettle: (() => void) | 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 = (coord: TileCoord): string => `${coord.ix},${coord.iy}`;\nconst rectKey = (rect: Rect): string =>\n `${Math.round(rect.x)},${Math.round(rect.y)},${Math.round(rect.width)},${Math.round(rect.height)}`;\n\n/** What a tile key's picture draws, as {@link layersKeyOf} wrote it into the key. */\nconst layersOfKey = (key: string): PageRenderLayers => {\n const drawn = /\\|a([01])f([01])\\|/.exec(key);\n return { includeAnnotations: drawn?.[1] === '1', includeFormFields: drawn?.[2] === '1' };\n};\n","import {\n annotationKey,\n CONTINUOUS_RENDER_POLICY,\n isPluginError,\n memo,\n resolvePageLayers,\n snapAppearanceScale,\n toPageRef,\n toPluginError,\n toPluginErrorInfo,\n type BatchResult,\n type EventOrigin,\n type PluginContext,\n type DocCapability,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PageLayerRights,\n type PageObjectNumber,\n type PageRef,\n type PageRenderLayers,\n type PageRenderViewport,\n type PluginErrorInfo,\n} from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\nimport { AppearanceStore, RecentPictures } from './appearance-store';\nimport type {\n InvalidateOptions,\n InvalidateScope,\n PageLayerOptions,\n PageRender,\n RenderFormat,\n RenderInvalidatedEvent,\n RenderPageOptions,\n RenderPagesOptions,\n RenderSettings,\n} from './contract';\nimport type {\n AppearancePicture,\n AppearancesOptions,\n PaintSettings,\n RenderCompletedEvent,\n RenderFailedEvent,\n RenderHostCapability,\n RenderSourceOptions,\n ViewDemand,\n} from './host-contract';\nimport { pixelChangeOf } from './invalidation';\nimport { invalidatePages, layersKeyOf, renderEpochOf, type RenderState } from './model';\nimport {\n EMPTY_TILE_PLAN,\n resolveRenderOptions,\n type PageViewDemand,\n type ResolvedRenderOptions,\n type TilePaintPlan,\n} from './paint-plan';\nimport { RasterStore } from './raster-store';\nimport { baseAskWidth, resolveStrategy, type ResolvedStrategy } from './strategy';\nimport { TileManager } from './tile-manager';\n\nconst RENDER_SCOPE: DocCapability = 'doc.render';\nconst FORMS_SCOPE: DocCapability = 'doc.forms.read';\nconst ANNOTATIONS_SCOPE: DocCapability = 'doc.annotate.read';\n\n/** Every answer `getLayerRights()` gives, one object each, so the same rights are the same object. */\nconst LAYER_RIGHTS: readonly (readonly PageLayerRights[])[] = [false, true].map((annotations) =>\n [false, true].map((formFields) => Object.freeze({ annotations, formFields })),\n);\n/** An abort reason the engine task accepts (signals carry `unknown`). */\nconst reasonOf = (signal: AbortSignal): string | undefined =>\n typeof signal.reason === 'string' ? signal.reason : undefined;\nconst DEFAULT_BATCH_CONCURRENCY = 4;\n\n/** A view's live demand for one page, as last set through its handle. */\ninterface PageDemand {\n demand: PageViewDemand;\n layers: PageRenderLayers;\n plan: TilePaintPlan;\n}\n\n/**\n * The render controller: the one place strategy (config: what the viewer\n * spends) composes with policy (the engine fact: what the deployment\n * serves). The engine never snaps and layers never see either; everything\n * between happens here: conforming demand to the resolved render points,\n * collapsing same-key asks in the raster store, exposing stable source keys,\n * and turning a view's demand into a retention-safe tile plan.\n *\n * Two doors for rasters: `renderPage` (public, exact size) and\n * `renderSource` (host, conformed). Both share the raster store, so a\n * developer's thumbnail and the rail's base plane never render twice.\n *\n * Every render it asks for is the picture of a page itself, so it asks the\n * engine with `priority: 'high'`, naming the view it's for when it knows it:\n * the engine runs it before anything else about the same place (annotation\n * appearances, form fields, text geometry, links).\n *\n * Two doors for staleness: confirmed document events (see `connect`) and\n * the `invalidate` verb. Both go through `publishInvalidation`, the one\n * place the ledger bumps and `onInvalidated` fires.\n *\n * The tile manager and the per-view plans live outside state (they hold\n * live handles and abort controllers); a re-plan wakes readers with\n * `ctx.notify()`.\n */\nexport function createRenderController(ctx: PluginContext<RenderState, RenderSettings>) {\n const settings = ctx.settings();\n // The settings as the strategy reads them, resolved again when they change.\n const resolvedOptions: () => ResolvedRenderOptions = memo(\n () => [settings.get()] as const,\n (current) => resolveRenderOptions(current),\n );\n const store = new RasterStore();\n const appearances = new AppearanceStore<AppearancePicture>();\n const recentAppearances = new RecentPictures<AppearancePicture>();\n const invalidated = ctx.events.source<RenderInvalidatedEvent>();\n const renderCompleted = ctx.events.source<RenderCompletedEvent>();\n const renderFailed = ctx.events.source<RenderFailedEvent>();\n\n const canRender = (): boolean => ctx.allows(RENDER_SCOPE);\n\n // ── what a picture draws ──\n // The engine's rule (`resolvePageLayers`): each option as given, and one\n // left out draws what the session may read. The doors refuse an explicit\n // `true` the session may not read, as the engine would; reads (keys,\n // epochs, plans) never throw, and count such an option as left out.\n const rights = (): PageLayerRights =>\n LAYER_RIGHTS[ctx.allows('doc.annotate.read') ? 1 : 0]![ctx.allows('doc.forms.read') ? 1 : 0]!;\n const layersOf = (options: PageLayerOptions): PageRenderLayers => {\n const may = rights();\n return resolvePageLayers(\n {\n ...(options.includeAnnotations !== undefined &&\n (may.annotations || !options.includeAnnotations)\n ? { includeAnnotations: options.includeAnnotations }\n : {}),\n ...(options.includeFormFields !== undefined &&\n (may.formFields || !options.includeFormFields)\n ? { includeFormFields: options.includeFormFields }\n : {}),\n },\n may,\n );\n };\n const pictureLayers = (options: PageLayerOptions, operation: string): PageRenderLayers => {\n if (options.includeAnnotations === true) ctx.assertAllowed('doc.annotate.read', operation);\n if (options.includeFormFields === true) ctx.assertAllowed(FORMS_SCOPE, operation);\n return layersOf(options);\n };\n\n // One-shot developer hints: misconfigurations, not errors.\n let warnedFormat = false;\n const warnDroppedFormat = (strategy: ResolvedStrategy, requested: RenderFormat | undefined) => {\n if (warnedFormat || requested === undefined || strategy.format === requested) return;\n warnedFormat = true;\n console.warn(\n `[render] format '${requested}' is not in the deployment's formats — using '${strategy.format}'.`,\n );\n };\n let warnedBudget = false;\n const warnPastBudgetNoTiles = (demandWidth: number, supplied: number) => {\n if (warnedBudget || resolvedOptions().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 // ── strategy ∧ policy ──\n // The policy is a document fact on the kernel's registry, materialized\n // before publish. Composed with the strategy once per policy reference.\n const renderPolicy = (): EngineRenderPolicy =>\n ctx.document()?.renderPolicy ?? CONTINUOUS_RENDER_POLICY;\n const currentStrategy = memo(\n () => [renderPolicy(), resolvedOptions()] as const,\n (policy, options) => {\n const strategy = resolveStrategy(policy, options);\n warnDroppedFormat(strategy, options.format);\n return strategy;\n },\n );\n\n // ── pages ──\n const pageWidthOf = (page: PageRef): number => {\n const info = ctx.getPage(page);\n if (!info) ctx.assertPageRef(page); // throws not-found\n return info!.size.width;\n };\n const allPageObjectNumbers = (): PageObjectNumber[] =>\n (ctx.document()?.pages ?? []).map((info) => info.ref.objectNumber);\n\n // The one base-sizing path: renderSource, getSourceKey, conformViewport,\n // and (through the tile manager) engagement all go through baseAskWidth.\n const conformViewport = (page: PageRef, scale: number): PageRenderViewport => {\n const demandWidth = scale * pageWidthOf(page);\n const width = baseAskWidth(currentStrategy(), demandWidth);\n warnPastBudgetNoTiles(demandWidth, width);\n return { kind: 'width', width };\n };\n\n const epochOf = (pageObjectNumber: PageObjectNumber, layers: PageRenderLayers): number =>\n renderEpochOf(ctx.state.get(), pageObjectNumber, layers);\n\n // A raster's identity includes its encode format: the cloud policy arrives\n // asynchronously after open, so the resolved format can change under a\n // live store, and the key must change with it. An absent format (the\n // engine default) adds nothing.\n const rasterKey = (\n pageObjectNumber: PageObjectNumber,\n width: number,\n layers: PageRenderLayers,\n format: RenderFormat | undefined,\n quality: number | undefined,\n ): string =>\n `${pageObjectNumber}|w${width}|${layersKeyOf(layers)}|e${epochOf(pageObjectNumber, layers)}` +\n `${format ? `|f${format}` : ''}${quality !== undefined ? `|q${quality}` : ''}`;\n\n // ── the raster store door (shared by both render doors and the tiles) ──\n function acquireRaster(\n page: PageRef,\n key: string,\n request: {\n viewport: PageRenderViewport;\n layers: PageRenderLayers;\n format: RenderFormat | undefined;\n quality: number | undefined;\n /** The view whose picture it is, so the engine ranks it by where the page is there. */\n view: string | undefined;\n },\n signal: AbortSignal | undefined,\n ): Promise<PageImageHandle> {\n const result = store.acquire(\n key,\n (storeSignal) => {\n const doc = ctx.doc.with({\n priority: 'high',\n ...(request.view !== undefined ? { view: request.view } : {}),\n });\n const task = doc.page(page).render.image({\n viewport: request.viewport,\n ...request.layers,\n ...(request.format !== undefined ? { format: request.format } : {}),\n ...(request.quality !== undefined ? { quality: request.quality } : {}),\n });\n if (storeSignal.aborted) task.abort(reasonOf(storeSignal));\n else\n storeSignal.addEventListener('abort', () => task.abort(reasonOf(storeSignal)), {\n once: true,\n });\n return task; // AbortablePromise<PageImageHandle> is a Promise<PageImageHandle>\n },\n signal,\n );\n return result.then(\n (image) => {\n renderCompleted.emit({ page, key });\n return image;\n },\n (error: unknown) => {\n const mapped = toPluginError('render', error);\n if (\n !isPluginError(mapped, 'operation-cancelled') &&\n !isPluginError(mapped, 'instance-closed')\n ) {\n renderFailed.emit({ page, key, error: toPluginErrorInfo(mapped) });\n }\n throw mapped;\n },\n );\n }\n\n /** The host door: conformed to the render points. Async so refusals reject. */\n async function renderSource(\n page: PageRef,\n { scale, view, signal, ...options }: RenderSourceOptions & { signal?: AbortSignal },\n ): Promise<PageImageHandle> {\n ctx.assertAllowed(RENDER_SCOPE, 'render.renderSource');\n const layers = pictureLayers(options, 'render.renderSource');\n const viewport = conformViewport(page, scale);\n const strategy = currentStrategy();\n const key = rasterKey(\n page.objectNumber,\n viewport.kind === 'width' ? viewport.width : 0,\n layers,\n strategy.format,\n undefined,\n );\n return acquireRaster(\n page,\n key,\n {\n viewport,\n layers,\n format: strategy.format,\n quality: strategy.quality,\n view,\n },\n signal,\n );\n }\n\n /** The public door: the requested size, exactly. Async so refusals reject. */\n async function renderPage(\n pageArgument: PageRef | number,\n options: RenderPageOptions = {},\n ): Promise<PageImageHandle> {\n ctx.assertAllowed(RENDER_SCOPE, 'render.renderPage');\n const { ref: page, size } = ctx.pageOf(pageArgument);\n const width = Math.max(1, Math.round(options.width ?? (options.scale ?? 1) * size.width));\n const layers = pictureLayers(options, 'render.renderPage');\n const strategy = currentStrategy();\n const format = options.format ?? strategy.format;\n const quality = options.quality ?? strategy.quality;\n // Exact-size renders share the store with the conformed door: a width\n // that matches a conformed raster (same format, default quality) is the\n // same key, so the cached image serves it.\n const key = rasterKey(page.objectNumber, width, layers, format, options.quality);\n return ctx.cancellable(\n options.signal,\n acquireRaster(\n page,\n key,\n {\n viewport: { kind: 'width', width },\n layers,\n format,\n quality,\n view: undefined,\n },\n options.signal,\n ),\n );\n }\n\n async function renderPages(\n pages: readonly (PageRef | number)[],\n options: RenderPagesOptions = {},\n ): Promise<BatchResult<PageRender, PageRef | number>> {\n ctx.assertAllowed(RENDER_SCOPE, 'render.renderPages');\n const concurrency = Math.max(1, options.concurrency ?? DEFAULT_BATCH_CONCURRENCY);\n // Each entry keeps its place in the input, so the result reads in the\n // order given whatever order the renders finished in.\n const applied: { index: number; render: PageRender }[] = [];\n const failed: { index: number; ref: PageRef | number; error: PluginErrorInfo }[] = [];\n const queue = pages.map((page, index) => ({ page, index }));\n const worker = async () => {\n for (let next = queue.shift(); next; next = queue.shift()) {\n const { page, index } = next;\n try {\n const image = await renderPage(page, options);\n applied.push({ index, render: { page: ctx.pageOf(page).ref, image } });\n } catch (error) {\n failed.push({\n index,\n ref: page,\n error: toPluginErrorInfo(toPluginError('render', error)),\n });\n }\n }\n };\n const batch = Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, worker));\n await ctx.cancellable(options.signal, batch);\n const byInput = (left: { index: number }, right: { index: number }) => left.index - right.index;\n return {\n applied: applied.sort(byInput).map(({ render }) => render),\n skipped: [],\n failed: failed.sort(byInput).map(({ ref, error }) => ({ ref, error })),\n };\n }\n\n // ── appearance pictures: the owners' items, shared between their views ──\n async function renderAppearances(\n page: PageRef,\n { family, scale, items, signal }: AppearancesOptions,\n ): Promise<readonly AppearancePicture[]> {\n ctx.assertAllowed(\n family === 'widgets' ? FORMS_SCOPE : ANNOTATIONS_SCOPE,\n 'render.renderAppearances',\n );\n const pageObjectNumber = ctx.pageOf(page).ref.objectNumber;\n const versions = new Map(items.map((item) => [annotationKey(item.ref), item.version]));\n const asked = [...versions].map(([item, version]) => `${item}@${version}`).sort();\n const group = `${family}|${pageObjectNumber}`;\n const pictures = appearances.acquire(\n group,\n `${group}|s${scale}|${asked.join(',')}`,\n async (fetchSignal) => {\n // The look at rest only, in every state: a check box keeps both its\n // pictures, and the looks under the pointer and pressed aren't drawn.\n const pageHandle = ctx.doc.page(toPageRef(pageObjectNumber));\n const options = { viewport: { kind: 'scale' as const, scale }, modes: ['normal' as const] };\n const task =\n family === 'widgets'\n ? pageHandle.forms.renderAppearances(options)\n : pageHandle.annotations.renderAppearances(options);\n if (fetchSignal.aborted) task.abort(reasonOf(fetchSignal));\n else\n fetchSignal.addEventListener('abort', () => task.abort(reasonOf(fetchSignal)), {\n once: true,\n });\n // The asked items' pictures, each tagged with the version it was asked at.\n const drawn = (await task).appearances.flatMap((picture): AppearancePicture[] => {\n const version = versions.get(annotationKey(picture.ref));\n return version === undefined ? [] : [{ ...picture, version }];\n });\n recentAppearances.remember(family, drawn, (picture) => annotationKey(picture.ref));\n return drawn;\n },\n signal,\n );\n return ctx.cancellable(\n signal,\n pictures.catch((error: unknown) => {\n throw toPluginError('render', error);\n }),\n );\n }\n\n // ── invalidation: the one place the ledger bumps and onInvalidated fires ──\n function publishInvalidation(\n pageObjectNumbers: readonly PageObjectNumber[],\n scope: InvalidateScope,\n origin: EventOrigin | null,\n ): void {\n if (pageObjectNumbers.length === 0) return;\n ctx.state.update(invalidatePages, pageObjectNumbers, scope);\n // Every view's plan for these pages points at old-epoch pixels: re-plan\n // now, so `getPlan` reads fresh on the subscribers' next look.\n for (const pageObjectNumber of pageObjectNumbers) replanPage(pageObjectNumber);\n invalidated.emit({\n pages: pageObjectNumbers.map((pageObjectNumber) => toPageRef(pageObjectNumber)),\n scope,\n origin,\n });\n }\n\n function invalidate({ pages, scope = 'content' }: InvalidateOptions = {}): void {\n const named = pages?.flatMap((page) => ctx.getPage(page)?.ref.objectNumber ?? []);\n publishInvalidation(named ?? allPageObjectNumbers(), scope, null);\n }\n\n // ── tiles: one manager, per-view demand handles with pure reads ──\n // Tile resolutions arrive in bursts (a want set landing): coalesce the\n // re-plans per frame, so N arrivals become one re-plan and one wake-up.\n const pendingAdvance = new Set<PageObjectNumber>();\n let advanceScheduled = false;\n const wake = (pageObjectNumber: PageObjectNumber) => {\n pendingAdvance.add(pageObjectNumber);\n if (advanceScheduled) return;\n advanceScheduled = true;\n ctx.clock.nextFrame(() => {\n advanceScheduled = false;\n const advanced = [...pendingAdvance];\n pendingAdvance.clear();\n for (const advancedPage of advanced) replanPage(advancedPage);\n });\n };\n\n const tiles = new TileManager({\n store,\n getOptions: resolvedOptions,\n getPolicy: renderPolicy,\n getPageSize: (pageObjectNumber) => ctx.getPage(toPageRef(pageObjectNumber))?.size,\n getEpoch: epochOf,\n after: ctx.clock.after,\n fetchTile: async (view, pageObjectNumber, rect: Rect, scale, layers, signal) => {\n // The same refusal the engine would send, without the round trip: a\n // denied session's viewport would otherwise be refused once per tile.\n ctx.assertAllowed(RENDER_SCOPE, 'render.tile');\n const page = toPageRef(pageObjectNumber);\n const strategy = currentStrategy();\n const task = ctx.doc\n .with({ priority: 'high', view })\n .page(page)\n .render.image({\n target: { kind: 'rect', rect },\n viewport: { kind: 'scale', scale },\n ...layers,\n ...(strategy.format !== undefined ? { format: strategy.format } : {}),\n ...(strategy.quality !== undefined ? { quality: strategy.quality } : {}),\n });\n if (signal.aborted) task.abort(reasonOf(signal));\n else signal.addEventListener('abort', () => task.abort(reasonOf(signal)), { once: true });\n return task;\n },\n onAdvance: wake,\n debug: (message: string) => {\n if (resolvedOptions().debug) console.debug(`[render] ${message}`);\n },\n });\n\n /** One handle per view id, reference-counted; demands and plans per page. */\n const views = new Map<\n string,\n { handle: ViewDemand; refs: number; pages: Map<PageObjectNumber, PageDemand> }\n >();\n\n /** Re-plan a page for every view that wants it (schedules the next want\n * set), and wake readers once when any plan changed. */\n function replanPage(pageObjectNumber: PageObjectNumber): void {\n let changed = false;\n for (const [viewId, view] of views) {\n const entry = view.pages.get(pageObjectNumber);\n if (!entry) continue;\n const plan = tiles.plan(viewId, pageObjectNumber, entry.demand, entry.layers);\n if (plan !== entry.plan) {\n entry.plan = plan;\n changed = true;\n }\n }\n if (changed) ctx.notify();\n }\n\n function createViewDemand(viewId: string): ViewDemand {\n const existing = views.get(viewId);\n if (existing) {\n existing.refs += 1;\n return existing.handle;\n }\n const pages = new Map<PageObjectNumber, PageDemand>();\n const handle: ViewDemand = {\n setDemand: (page, demand, options) => {\n const pageObjectNumber = page.objectNumber;\n const layers = layersOf(options ?? {});\n const previous = pages.get(pageObjectNumber);\n const plan = tiles.plan(viewId, pageObjectNumber, demand, layers);\n pages.set(pageObjectNumber, { demand, layers, plan });\n if (plan !== previous?.plan) ctx.notify();\n },\n getPlan: (page) => pages.get(page.objectNumber)?.plan ?? EMPTY_TILE_PLAN,\n markPainted: (page, key) => tiles.sourcePainted(viewId, page.objectNumber, key),\n markUnpainted: (page, key) => tiles.sourceUnpainted(viewId, page.objectNumber, key),\n release: (page) => {\n pages.delete(page.objectNumber);\n tiles.releasePage(viewId, page.objectNumber);\n },\n dispose: () => {\n const view = views.get(viewId);\n if (!view || view.refs === 0) return;\n view.refs -= 1;\n if (view.refs > 0) return;\n // The last reference releases the view's pages. The entry itself stays\n // (a few ids per document) so a handle that is re-acquired or reused\n // after a dispose (React's development double-mount) keeps working.\n for (const pageObjectNumber of [...view.pages.keys()]) {\n tiles.releasePage(viewId, pageObjectNumber);\n }\n view.pages.clear();\n },\n };\n views.set(viewId, { handle, refs: 1, pages });\n return handle;\n }\n\n // What the layers paint with: the same object until a setting it reads changes.\n const paintSettings = memo(\n () => [resolvedOptions().tiles.enabled] as const,\n (enabled): PaintSettings => Object.freeze({ tiles: enabled }),\n );\n\n // A settings change reaches every view's plans at once: re-plan what they show.\n ctx.listen(settings.api.onSettingsChanged, () => {\n const shown = new Set<PageObjectNumber>();\n for (const view of views.values()) for (const page of view.pages.keys()) shown.add(page);\n for (const page of shown) replanPage(page);\n });\n\n const api: RenderHostCapability = {\n // ── public lens ──\n ...settings.api,\n canRender,\n renderPage,\n renderThumbnail: (page, { maxWidth, ...options }) =>\n renderPage(page, { width: maxWidth, ...options }),\n renderPages,\n getRenderPolicy: renderPolicy,\n getRenderEpoch: (page, layers = {}) => {\n const info = ctx.getPage(page);\n return info ? epochOf(info.ref.objectNumber, layersOf(layers)) : 0;\n },\n invalidate,\n onInvalidated: invalidated.on,\n\n // ── host lens ──\n renderSource,\n getSourceKey: (page, { scale, view: _view, ...options }) => {\n const viewport = conformViewport(page, scale);\n return rasterKey(\n page.objectNumber,\n viewport.kind === 'width' ? viewport.width : 0,\n layersOf(options),\n currentStrategy().format,\n undefined,\n );\n },\n conformViewport,\n getPaintSettings: paintSettings,\n getLayerRights: rights,\n createViewDemand,\n renderAppearances,\n keptAppearances: (family, item) => recentAppearances.of(family, annotationKey(item)),\n getAppearanceScale: (renderScale) => snapAppearanceScale(renderPolicy(), renderScale),\n onRenderCompleted: renderCompleted.on,\n onRenderFailed: renderFailed.on,\n };\n\n return {\n api,\n connect() {\n // Confirmed pixel-changing facts, own or remote, and the stream\n // notice that some were lost.\n ctx.listen(ctx.doc.events, (event) => {\n const change = pixelChangeOf(event, allPageObjectNumbers);\n if (!change) return;\n publishInvalidation(change.pages, change.scope, 'origin' in event ? event.origin : null);\n });\n },\n };\n}\n","import { definePlugin } from '@embedpdf/core';\nimport { RENDER_DEFAULTS, type RenderConfig } from './contract';\nimport { createRenderController } from './controller';\nimport { RenderToken } from './host-contract';\nimport { initialRenderState } from './model';\n\n/**\n * Document-scoped page rasters. The only render-policy consumer in the\n * client stack: the kernel materializes the engine's advertised render\n * policy on the document, the controller conforms desired scales to it and\n * collapses same-key asks in its raster store, and the tile manager turns\n * host-supplied demand into a retention-safe paint plan over the same store.\n * State is the per-page raster-version ledger.\n */\nexport const renderPlugin = (config?: RenderConfig) =>\n definePlugin({\n id: 'render',\n scope: 'document',\n token: RenderToken,\n state: initialRenderState,\n settings: { defaults: RENDER_DEFAULTS, registered: config },\n create: createRenderController,\n });\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { _ as PageViewDemand, a as PageRender, b as TilesOptions, c as RenderConfig, d as RenderPageOptions, f as RenderPagesOptions, g as FullPageOptions, h as RenderToken, i as PageLayerOptions, l as RenderFormat, m as RenderThumbnailOptions, n as InvalidateScope, o as RENDER_DEFAULTS, p as RenderSettings, r as PageImage, s as RenderCapability, t as InvalidateOptions, u as RenderInvalidatedEvent, x as samePageViewDemand } from "./contract-uc5nOice.cjs";
|
|
2
|
+
import { c as RenderHostCapability } from "./host-contract-CTOQn0Xz.cjs";
|
|
3
|
+
import { PageObjectNumber } from "@embedpdf/core";
|
|
4
|
+
//#region src/model.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Per-page versions of what a page picture can draw: the page content, its
|
|
7
|
+
* annotations and its form fields. Fed by the document event stream and by
|
|
8
|
+
* the `invalidate` verb: a confirmed pixel-changing fact, own or remote,
|
|
9
|
+
* bumps the touched pages, and anything holding a rendered bitmap refetches.
|
|
10
|
+
* A picture's version is the sum of the parts it draws, and is part of every
|
|
11
|
+
* raster and tile key.
|
|
12
|
+
*/
|
|
13
|
+
interface RenderState {
|
|
14
|
+
/** Content versions, bumped by content facts (redaction, flatten). */
|
|
15
|
+
readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
16
|
+
/** Annotation versions, bumped by annotation facts (widgets excepted). */
|
|
17
|
+
readonly annotationEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
18
|
+
/** Form field versions, bumped by form facts: a fill, a widget's move or look, a signing. */
|
|
19
|
+
readonly fieldEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
2
22
|
//#region src/render.plugin.d.ts
|
|
3
23
|
/**
|
|
4
|
-
* Document-scoped. The
|
|
5
|
-
*
|
|
6
|
-
* policy
|
|
7
|
-
* same-
|
|
8
|
-
* demand into a retention-safe paint plan over the
|
|
9
|
-
* per-page
|
|
10
|
-
* built-in map and the `invalidate` verb), the policy latch, and the tile
|
|
11
|
-
* wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch
|
|
12
|
-
* exactly when those change.
|
|
24
|
+
* Document-scoped page rasters. The only render-policy consumer in the
|
|
25
|
+
* client stack: the kernel materializes the engine's advertised render
|
|
26
|
+
* policy on the document, the controller conforms desired scales to it and
|
|
27
|
+
* collapses same-key asks in its raster store, and the tile manager turns
|
|
28
|
+
* host-supplied demand into a retention-safe paint plan over the same store.
|
|
29
|
+
* State is the per-page raster-version ledger.
|
|
13
30
|
*/
|
|
14
|
-
declare const renderPlugin: (
|
|
31
|
+
declare const renderPlugin: (config?: RenderConfig) => import("@embedpdf/core").PluginDef<RenderState, RenderHostCapability, RenderSettings>;
|
|
15
32
|
//#endregion
|
|
16
|
-
export { type FullPageOptions,
|
|
33
|
+
export { type FullPageOptions, InvalidateOptions, InvalidateScope, PageImage, PageLayerOptions, PageRender, type PageViewDemand, type PageViewDemand as ViewDemandInput, RENDER_DEFAULTS, RenderCapability, RenderConfig, RenderFormat, RenderInvalidatedEvent, RenderPageOptions, RenderPagesOptions, RenderSettings, RenderThumbnailOptions, RenderToken, type TilesOptions, renderPlugin, samePageViewDemand };
|
|
17
34
|
//# sourceMappingURL=index.d.cts.map
|