@embedpdf/plugin-render 2.15.0 → 3.0.0-next.0
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/LICENSE +192 -21
- package/dist/index.cjs +709 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -0
- package/dist/index.d.ts +233 -1
- package/dist/index.js +703 -137
- package/dist/index.js.map +1 -1
- package/package.json +19 -58
- package/dist/lib/index.d.ts +0 -6
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/render-plugin.d.ts +0 -21
- package/dist/lib/types.d.ts +0 -46
- package/dist/preact/adapter.d.ts +0 -5
- package/dist/preact/core.d.ts +0 -1
- package/dist/preact/index.cjs +0 -2
- package/dist/preact/index.cjs.map +0 -1
- package/dist/preact/index.d.ts +0 -1
- package/dist/preact/index.js +0 -85
- package/dist/preact/index.js.map +0 -1
- package/dist/react/adapter.d.ts +0 -2
- package/dist/react/core.d.ts +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -84
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/index.d.ts +0 -1
- package/dist/shared/components/render-layer.d.ts +0 -35
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/use-render.d.ts +0 -11
- package/dist/shared/index.d.ts +0 -3
- package/dist/shared-preact/components/index.d.ts +0 -1
- package/dist/shared-preact/components/render-layer.d.ts +0 -35
- package/dist/shared-preact/hooks/index.d.ts +0 -1
- package/dist/shared-preact/hooks/use-render.d.ts +0 -11
- package/dist/shared-preact/index.d.ts +0 -3
- package/dist/shared-react/components/index.d.ts +0 -1
- package/dist/shared-react/components/render-layer.d.ts +0 -35
- package/dist/shared-react/hooks/index.d.ts +0 -1
- package/dist/shared-react/hooks/use-render.d.ts +0 -11
- package/dist/shared-react/index.d.ts +0 -3
- package/dist/svelte/components/RenderLayer.svelte.d.ts +0 -24
- package/dist/svelte/components/index.d.ts +0 -1
- package/dist/svelte/hooks/index.d.ts +0 -1
- package/dist/svelte/hooks/use-render.d.ts +0 -11
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -3
- package/dist/svelte/index.js +0 -121
- package/dist/svelte/index.js.map +0 -1
- package/dist/vue/components/index.d.ts +0 -1
- package/dist/vue/components/render-layer.vue.d.ts +0 -21
- package/dist/vue/hooks/index.d.ts +0 -1
- package/dist/vue/hooks/use-render.d.ts +0 -3
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.ts +0 -3
- package/dist/vue/index.js +0 -112
- package/dist/vue/index.js.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/lib/render-plugin.ts","../src/lib/manifest.ts","../src/lib/index.ts"],"sourcesContent":["import { BasePlugin, PluginRegistry } from '@embedpdf/core';\nimport {\n RenderCapability,\n RenderPageOptions,\n RenderPageRectOptions,\n RenderPluginConfig,\n RenderScope,\n} from './types';\n\n/**\n * Render Plugin - Simplified version that relies on core state for refresh tracking\n *\n * Key insight: Page refresh tracking is in DocumentState.pageRefreshVersions\n * This allows ANY plugin to observe page refreshes, not just the render plugin.\n */\nexport class RenderPlugin extends BasePlugin<RenderPluginConfig, RenderCapability> {\n static readonly id = 'render' as const;\n\n private config: RenderPluginConfig;\n\n constructor(id: string, registry: PluginRegistry, config: RenderPluginConfig) {\n super(id, registry);\n this.config = config;\n }\n\n // No onDocumentLoadingStarted or onDocumentClosed needed!\n\n protected buildCapability(): RenderCapability {\n return {\n // Active document operations\n renderPage: (options: RenderPageOptions) => this.renderPage(options),\n renderPageRect: (options: RenderPageRectOptions) => this.renderPageRect(options),\n renderPageRaw: (options: RenderPageOptions) => this.renderPageRaw(options),\n renderPageRectRaw: (options: RenderPageRectOptions) => this.renderPageRectRaw(options),\n\n // Document-scoped operations\n forDocument: (documentId: string) => this.createRenderScope(documentId),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createRenderScope(documentId: string): RenderScope {\n return {\n renderPage: (options: RenderPageOptions) => this.renderPage(options, documentId),\n renderPageRect: (options: RenderPageRectOptions) => this.renderPageRect(options, documentId),\n renderPageRaw: (options: RenderPageOptions) => this.renderPageRaw(options, documentId),\n renderPageRectRaw: (options: RenderPageRectOptions) =>\n this.renderPageRectRaw(options, documentId),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Core Operations\n // ─────────────────────────────────────────────────────────\n\n private renderPage({ pageIndex, options }: RenderPageOptions, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n\n if (!coreDoc?.document) {\n throw new Error(`Document ${id} not loaded`);\n }\n\n const page = coreDoc.document.pages.find((p) => p.index === pageIndex);\n if (!page) {\n throw new Error(`Page ${pageIndex} not found in document ${id}`);\n }\n\n const mergedOptions = {\n ...(options ?? {}),\n withForms: options?.withForms ?? this.config.withForms ?? false,\n withAnnotations: options?.withAnnotations ?? this.config.withAnnotations ?? false,\n imageType: options?.imageType ?? this.config.defaultImageType ?? 'image/png',\n imageQuality: options?.imageQuality ?? this.config.defaultImageQuality ?? 0.92,\n };\n\n return this.engine.renderPage(coreDoc.document, page, mergedOptions);\n }\n\n private renderPageRect({ pageIndex, rect, options }: RenderPageRectOptions, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n\n if (!coreDoc?.document) {\n throw new Error(`Document ${id} not loaded`);\n }\n\n const page = coreDoc.document.pages.find((p) => p.index === pageIndex);\n if (!page) {\n throw new Error(`Page ${pageIndex} not found in document ${id}`);\n }\n\n const mergedOptions = {\n ...(options ?? {}),\n withForms: options?.withForms ?? this.config.withForms ?? false,\n withAnnotations: options?.withAnnotations ?? this.config.withAnnotations ?? false,\n imageType: options?.imageType ?? this.config.defaultImageType ?? 'image/png',\n imageQuality: options?.imageQuality ?? this.config.defaultImageQuality ?? 0.92,\n };\n\n return this.engine.renderPageRect(coreDoc.document, page, rect, mergedOptions);\n }\n\n // ─────────────────────────────────────────────────────────\n // Raw Rendering (returns ImageDataLike, skips encoding)\n // ─────────────────────────────────────────────────────────\n\n private renderPageRaw({ pageIndex, options }: RenderPageOptions, documentId?: string) {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n\n if (!coreDoc?.document) {\n throw new Error(`Document ${id} not loaded`);\n }\n\n const page = coreDoc.document.pages.find((p) => p.index === pageIndex);\n if (!page) {\n throw new Error(`Page ${pageIndex} not found in document ${id}`);\n }\n\n const mergedOptions = {\n ...(options ?? {}),\n withForms: options?.withForms ?? this.config.withForms ?? false,\n withAnnotations: options?.withAnnotations ?? this.config.withAnnotations ?? false,\n };\n\n return this.engine.renderPageRaw(coreDoc.document, page, mergedOptions);\n }\n\n private renderPageRectRaw(\n { pageIndex, rect, options }: RenderPageRectOptions,\n documentId?: string,\n ) {\n const id = documentId ?? this.getActiveDocumentId();\n const coreDoc = this.coreState.core.documents[id];\n\n if (!coreDoc?.document) {\n throw new Error(`Document ${id} not loaded`);\n }\n\n const page = coreDoc.document.pages.find((p) => p.index === pageIndex);\n if (!page) {\n throw new Error(`Page ${pageIndex} not found in document ${id}`);\n }\n\n const mergedOptions = {\n ...(options ?? {}),\n withForms: options?.withForms ?? this.config.withForms ?? false,\n withAnnotations: options?.withAnnotations ?? this.config.withAnnotations ?? false,\n };\n\n return this.engine.renderPageRectRaw(coreDoc.document, page, rect, mergedOptions);\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async initialize(_config: RenderPluginConfig): Promise<void> {\n this.logger.info('RenderPlugin', 'Initialize', 'Render plugin initialized');\n }\n\n async destroy(): Promise<void> {\n super.destroy();\n }\n}\n","import { PluginManifest } from '@embedpdf/core';\nimport { RenderPluginConfig } from './types';\n\nexport const RENDER_PLUGIN_ID = 'render';\n\nexport const manifest: PluginManifest<RenderPluginConfig> = {\n id: RENDER_PLUGIN_ID,\n name: 'Render Plugin',\n version: '1.0.0',\n provides: ['render'],\n requires: [],\n optional: [],\n defaultConfig: {},\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { RenderPluginConfig } from './types';\nimport { RenderPlugin } from './render-plugin';\nimport { manifest, RENDER_PLUGIN_ID } from './manifest';\n\nexport const RenderPluginPackage: PluginPackage<RenderPlugin, RenderPluginConfig> = {\n manifest,\n create: (registry, config) => new RenderPlugin(RENDER_PLUGIN_ID, registry, config),\n reducer: () => {},\n initialState: {},\n};\n\nexport * from './render-plugin';\nexport * from './types';\n"],"names":["_RenderPlugin","BasePlugin","constructor","id","registry","config","super","this","buildCapability","renderPage","options","renderPageRect","renderPageRaw","renderPageRectRaw","forDocument","documentId","createRenderScope","pageIndex","getActiveDocumentId","coreDoc","coreState","core","documents","document","Error","page","pages","find","p","index","mergedOptions","withForms","withAnnotations","imageType","defaultImageType","imageQuality","defaultImageQuality","engine","rect","initialize","_config","logger","info","destroy","RenderPlugin","RENDER_PLUGIN_ID","RenderPluginPackage","manifest","name","version","provides","requires","optional","defaultConfig","create","reducer","initialState"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["CONTINUOUS_RENDER_POLICY"],"sources":["../src/paint-plan.ts","../src/raster-store.ts","../src/tiles.ts","../src/tile-manager.ts","../src/capability.ts","../src/effects.ts","../src/reducer.ts","../src/types.ts","../src/render.plugin.ts"],"sourcesContent":["import type { PageImageHandle } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n/**\n * The demand a page HOST supplies through dependency inversion:\n * plugin-render defines the shape, producers fill it. The Stage's page\n * host knows the camera and supplies `visibleRect`/`velocity`; a\n * stage-less `<PageView>` supplies neither — absent `visibleRect` means\n * \"assume the whole page is visible\", which a thumbnail-sized demand\n * turns into \"no tiles engage\" by arithmetic, not configuration.\n */\nexport interface PageViewDemand {\n /** Desired device pixels across the page's unrotated content width. */\n desiredDeviceWidth: number;\n /** Visible page region — y-down page points. ABSENT = whole page. */\n visibleRect?: Rect;\n /** Scroll velocity in page points/s — prefetch direction bias only. */\n velocity?: { dx: number; dy: number };\n}\n\n/**\n * One tile the layer should have in the DOM. `key` is the reconciliation\n * identity (stable across plan recomputes — keyed lists preserve the DOM\n * node, which IS the retention mechanism); `rect` is y-down page points\n * inside the unrotated content box; `z` stacks by resolution so arriving\n * sharper tiles occlude retained coarser ones per region.\n */\nexport interface TilePaintSource {\n key: string;\n scale: number;\n rect: Rect;\n z: number;\n handle: PageImageHandle;\n}\n\n/**\n * What a TileLayer paints right now. `paint` draws ONLY from resolved\n * rasters — retained generations live here until the release rules fire;\n * \"loading\" never reaches the DOM. `fetching` is diagnostic (badge/tests).\n */\nexport interface TilePaintPlan {\n /** Tiles engaged at all? False = the base rung fully covers demand. */\n engaged: boolean;\n paint: TilePaintSource[];\n /** Keys currently in flight (want-set members not yet resolved). */\n fetching: string[];\n /** Memoization stamp — a new object appears only when this changes. */\n stamp: string;\n}\n\nexport const EMPTY_TILE_PLAN: TilePaintPlan = {\n engaged: false,\n paint: [],\n fetching: [],\n stamp: 'empty',\n};\n\n/** Resolved tiling configuration (renderPlugin options + defaults). */\nexport interface TilingConfig {\n /** Tile edge in device px. Default 512. */\n tileSize?: number;\n /**\n * Client pyramid used while the policy doesn't advertise `tiles`.\n * Default `[1, 2, 4, 8, 16, 32]` — ×2 aligned.\n */\n scales?: readonly number[];\n /**\n * Sharpness deficit (desired ÷ supplied-by-base) above which tiles\n * engage. Default 1.25 — small CSS upscales read fine; past that the\n * ladder cap is visible and the pyramid takes over.\n */\n engageAt?: number;\n /** Prefetch ring around the visible rect. */\n prefetch?: {\n /** Extra coverage per side, in fractions of the visible rect. Default 0.5. */\n margin?: number;\n /** Stretch the ring toward scroll direction. Default true. */\n velocityBias?: boolean;\n };\n /**\n * Hysteresis for LEVEL changes (a zoom settling between pyramid rungs):\n * tile fetches for a NEW level wait this long; pan-driven fetches at the\n * current level fire immediately. Default 150ms; 0 disables.\n */\n settleMs?: number;\n}\n\nexport interface ResolvedTiling {\n tileSize: number;\n scales: readonly number[];\n engageAt: number;\n prefetchMargin: number;\n velocityBias: boolean;\n settleMs: number;\n}\n\nexport function resolveTiling(config: TilingConfig | undefined): ResolvedTiling {\n return {\n tileSize: config?.tileSize ?? 512,\n scales: config?.scales ?? [1, 2, 4, 8, 16, 32],\n engageAt: config?.engageAt ?? 1.25,\n prefetchMargin: config?.prefetch?.margin ?? 0.5,\n velocityBias: config?.prefetch?.velocityBias ?? true,\n settleMs: config?.settleMs ?? 150,\n };\n}\n","import type { PageImageHandle } from '@embedpdf/core';\n\n/**\n * Client-side mirror of the server's one-door read-through: per-key\n * SINGLEFLIGHT + LRU over encoded page images.\n *\n * The key is the raster's canonical identity (conformed viewport +\n * annotations flag + epoch — see `renderSourceKey`), so the lattice does the\n * caching work: every ask inside one rung is the same key, and rung\n * re-crossings (zoom 1280 → 2560 → back) resolve instantly from the LRU\n * instead of re-rendering. Epoch bumps mint new keys; stale-epoch entries\n * age out through the LRU (they are never re-requested).\n *\n * Consumer aborts are REFCOUNTED: a caller abandoning a fetch (camera moved,\n * layer unmounted) detaches without killing it for other consumers; the\n * underlying engine call aborts only when the LAST in-flight consumer leaves.\n * Resolved entries are kept (evictable by LRU) — an abort after resolution\n * is a no-op by design.\n */\nexport class RasterStore {\n private readonly entries = new Map<string, Entry>();\n private tick = 0;\n\n constructor(private readonly maxEntries = 48) {}\n\n acquire(\n key: string,\n fetch: (signal: AbortSignal) => Promise<PageImageHandle>,\n signal?: AbortSignal,\n ): Promise<PageImageHandle> {\n const existing = this.entries.get(key);\n if (existing) {\n existing.used = ++this.tick;\n return this.attach(key, existing, signal);\n }\n\n const abort = new AbortController();\n const entry: Entry = {\n promise: undefined as unknown as Promise<PageImageHandle>,\n refs: 0,\n abort,\n used: ++this.tick,\n };\n entry.promise = fetch(abort.signal).then(\n (handle) => {\n entry.handle = handle;\n return handle;\n },\n (err) => {\n // Failed fetches must not be sticky — drop so the next ask retries.\n if (this.entries.get(key) === entry) this.entries.delete(key);\n throw err;\n },\n );\n // Swallow the shared promise's rejection when no consumer is attached\n // (everyone aborted) — each consumer observes it via its own attachment.\n entry.promise.catch(() => {});\n this.entries.set(key, entry);\n this.evict();\n return this.attach(key, entry, signal);\n }\n\n /** Resolved handle if present — synchronous peek for paint-time reuse. */\n peek(key: string): PageImageHandle | undefined {\n const entry = this.entries.get(key);\n if (entry?.handle) entry.used = ++this.tick;\n return entry?.handle;\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n private attach(key: string, entry: Entry, signal?: AbortSignal): Promise<PageImageHandle> {\n if (signal?.aborted) return Promise.reject(abortReason(signal));\n // EVERY consumer holds a ref while the fetch is in flight — including\n // signal-less ones (\"I never abandon\"), otherwise a signal-bearing\n // sibling's abort could kill a fetch someone else is still awaiting.\n entry.refs += 1;\n if (!signal) {\n const release = () => {\n entry.refs -= 1;\n };\n entry.promise.then(release, release);\n return entry.promise;\n }\n let settled = false;\n return new Promise<PageImageHandle>((resolve, reject) => {\n const onAbort = () => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n // Last live consumer walking away from an UNRESOLVED fetch kills it;\n // resolved entries stay for the LRU (that's the cache).\n if (entry.refs <= 0 && entry.handle === undefined) {\n entry.abort.abort(abortReason(signal));\n if (this.entries.get(key) === entry) this.entries.delete(key);\n }\n reject(abortReason(signal));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n entry.promise.then(\n (handle) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n resolve(handle);\n },\n (err) => {\n if (settled) return;\n settled = true;\n entry.refs -= 1;\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n }\n\n private evict(): void {\n if (this.entries.size <= this.maxEntries) return;\n // Oldest-first over evictable entries: resolved and consumer-free.\n const candidates = [...this.entries.entries()]\n .filter(([, e]) => e.refs <= 0 && e.handle !== undefined)\n .sort((a, b) => a[1].used - b[1].used);\n for (const [key] of candidates) {\n if (this.entries.size <= this.maxEntries) return;\n this.entries.delete(key);\n }\n }\n}\n\ninterface Entry {\n promise: Promise<PageImageHandle>;\n handle?: PageImageHandle;\n refs: number;\n abort: AbortController;\n used: number;\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('aborted', 'AbortError');\n}\n","import type { PdfRect } from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\n// One owner for space math: rect intersection lives in core-geometry\n// (the stage's visibility computation uses the same helper).\nexport { intersectRects } from '@embedpdf/core-geometry';\n\n/**\n * Pure tile-grid math.\n *\n * The pyramid is ALIGNED: one origin (the page's top-left), a fixed tile\n * size in device pixels, and ×2 scale steps. Alignment is what makes the\n * retention bookkeeping index arithmetic instead of rectangle geometry —\n * a level-s tile is covered by exactly the level-2s tiles whose indices\n * fall in its doubled range, so occlusion/release checks are O(1).\n *\n * Spaces: tile COORDS live on the device grid at their level's scale\n * (tileSize² device px per tile, constant per-job cost by construction);\n * PAINT rects are y-down page points (the viewer convention — the layer\n * multiplies by one container transform); ENGINE rects are y-up PDF user\n * space (the `target: {kind:'rect'}` contract).\n */\n\nexport interface PageSizePt {\n width: number;\n height: number;\n}\n\nexport interface TileCoord {\n ix: number;\n iy: number;\n}\n\nexport interface TileGrid {\n /** Device px per PDF point at this pyramid level. */\n scale: number;\n /** Tile edge in device px. */\n tileSize: number;\n cols: number;\n rows: number;\n}\n\nexport function tileGrid(page: PageSizePt, scale: number, tileSize: number): TileGrid {\n return {\n scale,\n tileSize,\n cols: Math.max(1, Math.ceil((page.width * scale) / tileSize)),\n rows: Math.max(1, Math.ceil((page.height * scale) / tileSize)),\n };\n}\n\n/** One tile's edge in page points at its level. */\nconst tileSpanPt = (grid: TileGrid): number => grid.tileSize / grid.scale;\n\n/**\n * The tiles of `grid` intersecting a y-down page-point rect, clamped to\n * the page. An empty intersection yields an empty list.\n */\nexport function tilesInRect(grid: TileGrid, page: PageSizePt, rect: Rect): TileCoord[] {\n const span = tileSpanPt(grid);\n const x0 = Math.max(0, Math.floor(rect.x / span));\n const y0 = Math.max(0, Math.floor(rect.y / span));\n const x1 = Math.min(grid.cols - 1, Math.ceil((rect.x + rect.width) / span) - 1);\n const y1 = Math.min(grid.rows - 1, Math.ceil((rect.y + rect.height) / span) - 1);\n const out: TileCoord[] = [];\n for (let iy = y0; iy <= y1; iy++) {\n for (let ix = x0; ix <= x1; ix++) out.push({ ix, iy });\n }\n return out;\n}\n\n/** Paint rect: y-down page points, clamped to the page edge. */\nexport function tilePaintRect(grid: TileGrid, page: PageSizePt, c: TileCoord): Rect {\n const span = tileSpanPt(grid);\n const x = c.ix * span;\n const y = c.iy * span;\n return {\n x,\n y,\n width: Math.min(span, page.width - x),\n height: Math.min(span, page.height - y),\n };\n}\n\n/** Engine target rect: y-UP PDF user space (top > bottom). */\nexport function tileEngineRect(grid: TileGrid, page: PageSizePt, c: TileCoord): PdfRect {\n const paint = tilePaintRect(grid, page, c);\n return {\n left: paint.x,\n right: paint.x + paint.width,\n top: page.height - paint.y,\n bottom: page.height - (paint.y + paint.height),\n };\n}\n\n/**\n * Does a set of PAINTED want-level tiles cover `region` (y-down points)?\n * The retention release check: a retained source may leave the paint list\n * once its visible footprint answers true here. Pure index arithmetic on\n * the aligned grid — the whole reason the pyramid is aligned.\n */\nexport function regionCovered(\n grid: TileGrid,\n page: PageSizePt,\n region: Rect,\n isPainted: (c: TileCoord) => boolean,\n): boolean {\n if (region.width <= 0 || region.height <= 0) return true;\n for (const c of tilesInRect(grid, page, region)) {\n if (!isPainted(c)) return false;\n }\n return true;\n}\n\n/**\n * Inflate a visible rect by a prefetch margin (fractions of the rect's own\n * size per side), optionally velocity-biased: the margin stretches toward\n * the direction of travel and shrinks behind it. Pure — the producer's\n * velocity is just data.\n */\nexport function inflateRect(\n rect: Rect,\n margin: number,\n velocity?: { dx: number; dy: number },\n): Rect {\n const mx = rect.width * margin;\n const my = rect.height * margin;\n let left = mx;\n let right = mx;\n let up = my;\n let down = my;\n if (velocity) {\n // Direction buckets only — raw magnitudes would make the want set\n // churn with every pointer sample.\n if (velocity.dx > 0) {\n right = mx * 2;\n left = mx / 2;\n } else if (velocity.dx < 0) {\n left = mx * 2;\n right = mx / 2;\n }\n if (velocity.dy > 0) {\n down = my * 2;\n up = my / 2;\n } else if (velocity.dy < 0) {\n up = my * 2;\n down = my / 2;\n }\n }\n return {\n x: rect.x - left,\n y: rect.y - up,\n width: rect.width + left + right,\n height: rect.height + up + down,\n };\n}\n\n/** Snap UP through a pyramid's sorted scales; cap at the top. */\nexport function snapToPyramid(scales: readonly number[], needed: number): number {\n const sorted = [...scales].sort((a, b) => a - b);\n return sorted.find((s) => s >= needed) ?? sorted[sorted.length - 1]!;\n}\n","import {\n snapFullPageViewport,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PdfRect,\n} from '@embedpdf/core';\nimport type { Rect } from '@embedpdf/core-geometry';\n\nimport {\n EMPTY_TILE_PLAN,\n type PageViewDemand,\n type ResolvedTiling,\n type TilePaintPlan,\n type TilePaintSource,\n} from './paint-plan';\nimport type { RasterStore } from './raster-store';\nimport {\n inflateRect,\n intersectRects,\n regionCovered,\n snapToPyramid,\n tileGrid,\n tilesInRect,\n tileEngineRect,\n tilePaintRect,\n type PageSizePt,\n type TileCoord,\n type TileGrid,\n} from './tiles';\n\n/**\n * The tile retention state machine. One instance per\n * plugin instance (per document), all levels and pages in one place, over\n * the SAME RasterStore the base renders use.\n *\n * Invariant it enforces: every screen region paints the sharpest PAINTED\n * pixels available; quality per region only goes up until the want set\n * resolves. Mechanics:\n * - want vs paint: `plan()` schedules fetches for the want set (P0\n * visible first, P1 prefetch when P0 is fully resolved) and returns a\n * paint list drawn ONLY from resolved entries — current level AND\n * retained older generations.\n * - release-on-occlusion: when a want-level tile reports PAINTED (the\n * layer's img onload — the decode boundary), retained sources whose\n * visible footprint is covered by painted want tiles leave the paint\n * list. Their bytes stay in the RasterStore (demotion, not eviction) —\n * zoom-back re-promotes from cache.\n * - epoch exception: an invalidation bump means retained pixels are\n * WRONG — everything of the old epoch drops immediately.\n *\n * `plan()` is called from selectors: it MUST NOT dispatch. Fetch kickoff\n * is idempotent (the store singleflights); resolution handlers dispatch\n * the wake-up (`onAdvance`) that makes subscribed layers recompute.\n */\nexport class TileManager {\n private readonly pages = new Map<number, PageTileState>();\n\n constructor(\n private readonly deps: {\n store: RasterStore;\n config: ResolvedTiling;\n /** The document fact off the kernel registry — never null: the kernel\n * materializes it (continuous fallback) before the doc publishes. */\n getPolicy(): EngineRenderPolicy;\n getPageSize(pon: number): PageSizePt | undefined;\n getEpoch(pon: number, includeAnnotations: boolean): number;\n fetchTile(\n pon: number,\n rect: PdfRect,\n scale: number,\n includeAnnotations: boolean,\n signal: AbortSignal,\n ): Promise<PageImageHandle>;\n /** Wake subscribed layers (dispatches PAINT_ADVANCED outside plan()). */\n onAdvance(pon: number): void;\n },\n ) {}\n\n plan(pon: number, demand: PageViewDemand, includeAnnotations: boolean): TilePaintPlan {\n const { config } = this.deps;\n const policy = this.deps.getPolicy();\n const page = this.deps.getPageSize(pon);\n if (!page) return EMPTY_TILE_PLAN;\n\n const epoch = this.deps.getEpoch(pon, includeAnnotations);\n const state = this.pageState(pon);\n\n // Epoch exception: old-epoch pixels are WRONG, not blurry — drop all.\n if (state.epoch !== epoch) {\n this.abortAll(state);\n state.entries.clear();\n state.epoch = epoch;\n state.wantScale = null;\n state.planCache = null;\n }\n\n // Engagement: deficit of the base rung vs demand. Under `continuous`\n // the base supplies exactly what was asked — deficit 1, never engaged\n // (v2 exactness); memory protection there is the POLICY's job\n // (localEngine({ renderPolicy })), not this manager's.\n const baseSupplied =\n policy.kind === 'lattice'\n ? widthOf(snapFullPageViewport(policy, { kind: 'width', width: demand.desiredDeviceWidth }))\n : demand.desiredDeviceWidth;\n const deficit = demand.desiredDeviceWidth / baseSupplied;\n if (deficit <= config.engageAt) {\n if (state.entries.size || state.wantScale !== null) {\n this.abortAll(state);\n state.entries.clear();\n state.wantScale = null;\n state.planCache = null;\n }\n return EMPTY_TILE_PLAN;\n }\n\n // Level selection: use the advertised policy pyramid when available,\n // otherwise use client defaults — same snapped requests either way.\n const pyramid = policy.kind === 'lattice' && policy.tiles ? policy.tiles.scales : config.scales;\n const wantScale = snapToPyramid(pyramid, demand.desiredDeviceWidth / page.width);\n const grid = tileGrid(page, wantScale, config.tileSize);\n\n const visible = demand.visibleRect ?? { x: 0, y: 0, width: page.width, height: page.height };\n state.lastVisible = visible;\n const p0 = tilesInRect(grid, page, visible);\n const ring = inflateRect(\n visible,\n config.prefetchMargin,\n config.velocityBias ? demand.velocity : undefined,\n );\n const p0Keys = new Set(p0.map((c) => coordKey(c)));\n const p1 = tilesInRect(grid, page, ring).filter((c) => !p0Keys.has(coordKey(c)));\n\n this.schedule(pon, state, page, grid, wantScale, includeAnnotations, epoch, p0, p1);\n\n // Paint list: resolved entries intersecting the visible rect, coarser\n // levels first (painter's algorithm — sharper occludes per region).\n // The memo key holds SNAPPED values only (identity law): raw demand\n // width matters solely through engagement and the level, both already\n // folded in — zoom inside a level is plan-stable by construction.\n const demandKey = `${wantScale}|${rectKey(visible)}|e${epoch}`;\n if (\n state.planCache &&\n state.planCache.demandKey === demandKey &&\n state.planCache.version === state.version\n ) {\n return state.planCache.plan;\n }\n const paint: TilePaintSource[] = [];\n const fetching: string[] = [];\n for (const entry of state.entries.values()) {\n const visIntersect = intersectRects(entry.rect, visible);\n if (entry.handle) {\n if (visIntersect.width > 0 && visIntersect.height > 0) {\n paint.push({\n key: entry.key,\n scale: entry.scale,\n rect: entry.rect,\n z: zOf(pyramid, entry.scale),\n handle: entry.handle,\n });\n }\n } else if (entry.scale === wantScale) {\n fetching.push(entry.key);\n }\n }\n paint.sort((a, b) => a.z - b.z || a.key.localeCompare(b.key));\n const plan: TilePaintPlan = {\n engaged: true,\n paint,\n fetching: fetching.sort(),\n stamp: `${demandKey}|v${state.version}`,\n };\n state.planCache = { demandKey, version: state.version, plan };\n return plan;\n }\n\n /** The layer's decode-boundary report: this key's <img> actually painted. */\n sourcePainted(pon: number, key: string): void {\n const state = this.pages.get(pon);\n const entry = state?.entries.get(key);\n if (!state || !entry || entry.painted) return;\n entry.painted = true;\n this.releaseOccluded(state, entry);\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n }\n\n /** A lens unmounted its TileLayer: stop fetching, drop bookkeeping.\n * Resolved bytes stay in the RasterStore for a re-mount. */\n releasePage(pon: number): void {\n const state = this.pages.get(pon);\n if (!state) return;\n this.abortAll(state);\n this.pages.delete(pon);\n }\n\n private pageState(pon: number): PageTileState {\n let state = this.pages.get(pon);\n if (!state) {\n state = {\n epoch: -1,\n wantScale: null,\n entries: new Map(),\n version: 0,\n planCache: null,\n settleTimer: null,\n pendingLevel: null,\n lastVisible: null,\n };\n this.pages.set(pon, state);\n }\n return state;\n }\n\n private schedule(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n wantScale: number,\n includeAnnotations: boolean,\n epoch: number,\n p0: TileCoord[],\n p1: TileCoord[],\n ): void {\n const { config } = this.deps;\n const firstEngage = state.wantScale === null;\n const levelChanged = state.wantScale !== null && state.wantScale !== wantScale;\n state.wantScale = wantScale;\n\n // Abort in-flight fetches that left the want set (pan away, level\n // change) — resolved entries are retention's business, not ours.\n const wanted = new Set(\n [...p0, ...p1].map((c) => this.tileKey(pon, wantScale, c, includeAnnotations, epoch)),\n );\n for (const [key, entry] of state.entries) {\n if (entry.handle === undefined && !wanted.has(key)) {\n entry.abort?.abort();\n state.entries.delete(key);\n }\n }\n\n const kickoff = () => {\n const allP0Ready = this.ensureFetches(pon, state, page, grid, includeAnnotations, epoch, p0);\n // P1 strictly after P0: prefetch never competes with on-screen tiles.\n if (allP0Ready) this.ensureFetches(pon, state, page, grid, includeAnnotations, epoch, p1);\n };\n\n // Level-change hysteresis: a zoom passing THROUGH levels shouldn't\n // fetch each intermediate one. First engagement fires immediately —\n // there's nothing on screen above the base yet.\n if (levelChanged && config.settleMs > 0) {\n state.pendingLevel = wantScale;\n if (state.settleTimer !== null) clearTimeout(state.settleTimer);\n state.settleTimer = setTimeout(() => {\n state.settleTimer = null;\n if (state.pendingLevel === wantScale) kickoff();\n }, config.settleMs);\n return;\n }\n if (state.settleTimer !== null && !levelChanged && !firstEngage) {\n // Same level again before the timer fired — the zoom came back;\n // cancel the pending level fetch.\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n state.pendingLevel = null;\n }\n kickoff();\n }\n\n /** Start missing fetches; true when every coord is already resolved. */\n private ensureFetches(\n pon: number,\n state: PageTileState,\n page: PageSizePt,\n grid: TileGrid,\n includeAnnotations: boolean,\n epoch: number,\n coords: TileCoord[],\n ): boolean {\n let allReady = true;\n for (const coord of coords) {\n const key = this.tileKey(pon, grid.scale, coord, includeAnnotations, epoch);\n const existing = state.entries.get(key);\n if (existing) {\n if (existing.handle === undefined) allReady = false;\n continue;\n }\n allReady = false;\n const abort = new AbortController();\n const entry: TileEntry = {\n key,\n scale: grid.scale,\n coord,\n rect: tilePaintRect(grid, page, coord),\n painted: false,\n abort,\n };\n state.entries.set(key, entry);\n this.deps.store\n .acquire(\n key,\n (signal) =>\n this.deps.fetchTile(\n pon,\n tileEngineRect(grid, page, coord),\n grid.scale,\n includeAnnotations,\n signal,\n ),\n abort.signal,\n )\n .then(\n (handle) => {\n if (state.entries.get(key) !== entry) return; // aborted/superseded\n entry.handle = handle;\n state.version += 1;\n state.planCache = null;\n this.deps.onAdvance(pon);\n },\n () => {\n if (state.entries.get(key) === entry) state.entries.delete(key);\n },\n );\n }\n return allReady;\n }\n\n /**\n * The release rule: after `painted` lands on a want-level tile, retained\n * sources (any other level) whose footprint ∩ painted-tile region is\n * covered by PAINTED want-level tiles leave the paint list. Index\n * arithmetic via the aligned grid — no rectangle geometry.\n */\n private releaseOccluded(state: PageTileState, painted: TileEntry): void {\n const wantScale = state.wantScale;\n if (wantScale === null || painted.scale !== wantScale) return;\n const page = this.deps.getPageSize(pagePonOf(painted.key));\n if (!page) return;\n const grid = tileGrid(page, wantScale, this.deps.config.tileSize);\n const paintedAt = (c: TileCoord) => {\n const key = this.tileKey(\n pagePonOf(painted.key),\n wantScale,\n c,\n annotationsOf(painted.key),\n state.epoch,\n );\n return state.entries.get(key)?.painted === true;\n };\n for (const [key, entry] of state.entries) {\n if (entry.scale === wantScale || entry.handle === undefined) continue;\n const overlap = intersectRects(entry.rect, painted.rect);\n if (overlap.width <= 0 || overlap.height <= 0) continue;\n // VISIBLE footprint only: an edge parent whose\n // offscreen children were never fetched must still release once its\n // on-screen region is covered — and its bytes stay in the store, so\n // a pan that re-exposes the rest re-promotes from cache.\n const region = state.lastVisible ? intersectRects(entry.rect, state.lastVisible) : entry.rect;\n if (regionCovered(grid, page, region, paintedAt)) {\n state.entries.delete(key);\n }\n }\n }\n\n private abortAll(state: PageTileState): void {\n if (state.settleTimer !== null) {\n clearTimeout(state.settleTimer);\n state.settleTimer = null;\n }\n for (const entry of state.entries.values()) {\n if (entry.handle === undefined) entry.abort?.abort();\n }\n }\n\n private tileKey(\n pon: number,\n scale: number,\n c: TileCoord,\n includeAnnotations: boolean,\n epoch: number,\n ): string {\n return `t:${pon}|s${scale}|${c.ix},${c.iy}|a${includeAnnotations ? 1 : 0}|e${epoch}`;\n }\n}\n\ninterface TileEntry {\n key: string;\n scale: number;\n coord: TileCoord;\n /** y-down page points. */\n rect: Rect;\n handle?: PageImageHandle;\n painted: boolean;\n abort?: AbortController;\n}\n\ninterface PageTileState {\n epoch: number;\n wantScale: number | null;\n entries: Map<string, TileEntry>;\n /** Bumped on ready/painted/drop — the plan memo key. */\n version: number;\n planCache: { demandKey: string; version: number; plan: TilePaintPlan } | null;\n settleTimer: ReturnType<typeof setTimeout> | null;\n pendingLevel: number | null;\n /** Last visible rect from plan() — the release rule's \"on screen\". */\n lastVisible: Rect | null;\n}\n\nconst widthOf = (v: { kind: string; width?: number }): number =>\n v.kind === 'width' && v.width !== undefined ? v.width : Number.POSITIVE_INFINITY;\n\nconst coordKey = (c: TileCoord): string => `${c.ix},${c.iy}`;\nconst rectKey = (r: Rect): string =>\n `${Math.round(r.x)},${Math.round(r.y)},${Math.round(r.width)},${Math.round(r.height)}`;\n\n/** Stacking: position in the ascending pyramid (coarse under fine). */\nconst zOf = (pyramid: readonly number[], scale: number): number => {\n const sorted = [...pyramid].sort((a, b) => a - b);\n const idx = sorted.indexOf(scale);\n return idx === -1 ? sorted.findIndex((s) => s > scale) : idx;\n};\n\nconst pagePonOf = (key: string): number => Number(key.slice(2, key.indexOf('|')));\nconst annotationsOf = (key: string): boolean => key.includes('|a1|');\n","import {\n CONTINUOUS_RENDER_POLICY,\n snapFullPageViewport,\n type EngineRenderPolicy,\n type PageObjectNumber,\n type PageRenderViewport,\n type PluginContext,\n} from '@embedpdf/core';\n\nimport { resolveTiling } from './paint-plan';\nimport { RasterStore } from './raster-store';\nimport { TileManager } from './tile-manager';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * The render capability: the ONE policy consumer in the client stack\n * (the three-layer rule). The engine never snaps; framework\n * layers never see the policy; everything between — conforming a desired\n * scale to the deployment lattice, collapsing same-rung asks in the raster\n * store, exposing stable source keys — happens here.\n */\nexport function createRenderCapability(\n ctx: PluginContext<RenderState, RenderAction>,\n options: RenderPluginOptions = {},\n): RenderCapability {\n // Document-lifetime cache; keys embed the canonical viewport + epoch, so\n // the lattice is the cache axis and staleness is a new key, never a flush.\n const store = new RasterStore();\n\n // The policy is a DOCUMENT FACT on the kernel's registry — materialized\n // before publish (one lifecycle, in the kernel), interpreted by the pure\n // engine-core snap helpers (one interpretation). This plugin just reads it.\n const policy = (): EngineRenderPolicy => ctx.document()?.renderPolicy ?? CONTINUOUS_RENDER_POLICY;\n\n const pageOf = (pon: PageObjectNumber) =>\n (ctx.document()?.pages ?? []).find((p) => p.pageObjectNumber === pon);\n\n const pageWidthOf = (pon: PageObjectNumber): number => {\n const page = pageOf(pon);\n if (!page) throw new Error(`render: unknown page object number ${pon}`);\n return page.size.width;\n };\n\n const conformViewport = (pon: PageObjectNumber, scale: number): PageRenderViewport => {\n const p = policy();\n if (p.kind !== 'lattice') return { kind: 'scale', scale };\n // scale × pageWidth = desired device width; the ONE snap implementation\n // converts and snaps UP to the canonical ladder width.\n return snapFullPageViewport(p, { kind: 'scale', scale }, { pageWidth: pageWidthOf(pon) });\n };\n\n const viewportKey = (viewport: PageRenderViewport): string =>\n viewport.kind === 'width' ? `w${viewport.width}` : `s${viewport.scale ?? 1}`;\n\n const epochOf = (pon: PageObjectNumber, includeAnnotations: boolean): number => {\n const s = ctx.getState();\n const content = s.contentEpochs[pon] ?? 0;\n if (!includeAnnotations) return content;\n return content + (s.annotatedEpochs[pon] ?? 0);\n };\n\n // Tiling shares THIS store, THIS policy, THIS ledger — one scheduler,\n // one budget, one invalidation truth.\n const tiles = new TileManager({\n store,\n config: resolveTiling(options.tiling),\n getPolicy: policy,\n getPageSize: (pon) => pageOf(pon)?.size,\n getEpoch: epochOf,\n fetchTile: (pon, rect, scale, includeAnnotations, signal) => {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const task = doc.page(pon).render.image({\n target: { kind: 'rect', rect },\n viewport: { kind: 'scale', scale },\n includeAnnotations,\n });\n if (signal.aborted) task.abort(signal.reason);\n else signal.addEventListener('abort', () => task.abort(signal.reason), { once: true });\n return task;\n },\n onAdvance: (pon) => ctx.dispatch({ type: 'PAINT_ADVANCED', pon }),\n });\n\n return {\n renderPage(pon, { scale, includeAnnotations, signal }) {\n const doc = ctx.doc;\n if (!doc) return Promise.reject(new Error('render: no document bound'));\n const annotations = includeAnnotations ?? true;\n const viewport = conformViewport(pon, scale);\n const key = `${pon}|${viewportKey(viewport)}|a${annotations ? 1 : 0}|e${epochOf(pon, annotations)}`;\n return store.acquire(\n key,\n (storeSignal) => {\n const task = doc.page(pon).render.image({ viewport, includeAnnotations: annotations });\n if (storeSignal.aborted) task.abort(storeSignal.reason);\n else\n storeSignal.addEventListener('abort', () => task.abort(storeSignal.reason), {\n once: true,\n });\n return task; // AbortablePromise<PageImageHandle> is a Promise<PageImageHandle>\n },\n signal,\n );\n },\n renderSourceKey(pon, { scale, includeAnnotations }) {\n const annotations = includeAnnotations ?? true;\n const viewport = conformViewport(pon, scale);\n return `${pon}|${viewportKey(viewport)}|a${annotations ? 1 : 0}|e${epochOf(pon, annotations)}`;\n },\n conformViewport,\n renderPolicy: policy,\n tilePlan(pon, demand, opts) {\n return tiles.plan(pon, demand, opts?.includeAnnotations ?? true);\n },\n tilePainted(pon, key) {\n tiles.sourcePainted(pon, key);\n },\n releaseTiles(pon) {\n tiles.releasePage(pon);\n },\n renderEpoch(pon, includeAnnotations = true) {\n // The sum of two monotonic counters is itself a valid monotonic version:\n // a content bump reaches BOTH products; an annotation bump only this one.\n return epochOf(pon, includeAnnotations);\n },\n invalidate({ pons, scope = 'content' } = {}) {\n const target = pons ?? (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n if (target.length) ctx.dispatch({ type: 'INVALIDATE', scope, pons: target });\n },\n };\n}\n","/**\n * Makes page rasters REACTIVE — the same event-stream pattern the metadata\n * and annotation plugins use.\n *\n * A render bakes whatever the engine knows into the bitmap, so it goes stale\n * the moment a confirmed mutation changes those pixels. This effect is the\n * BUILT-IN door for such facts: it subscribes to the document event stream\n * and bumps the touched pages' ledger (see reducer.ts); layers key their\n * fetch on `renderEpoch(pon)` and refetch on the bump. The OTHER door is the\n * capability's `invalidate` verb — for plugins whose mutation vocabulary\n * this map doesn't know (redaction, text edit, third-party).\n *\n * Deliberately ORIGIN-AGNOSTIC (unlike the annotation plugin's remote-only\n * fold): a baked raster is stale whether YOU moved the highlight or a\n * collaborator did — the event model's exactly-once guarantee means one bump\n * either way. And because events fire only on CONFIRMED mutations, a drag\n * invalidates once at commit — optimistic previews live in the overlay, never\n * here.\n */\nimport type { DocumentEvent, EffectContext, PageObjectNumber } from '@embedpdf/core';\nimport type { RenderAction, RenderState } from './types';\n\n/**\n * Which pages' ANNOTATED raster a confirmed mutation repaints — the whole\n * event→scope map. Everything the engine emits today is appearance-scoped;\n * when it grows content-mutation events (redaction.applied, content.edited),\n * they get a case here dispatching scope 'content'. Over-invalidation is\n * acceptable (a z-order move that changes nothing repaints one thumb);\n * under-invalidation is the bug.\n */\nexport function annotatedPons(\n event: DocumentEvent,\n allPons: () => PageObjectNumber[],\n): PageObjectNumber[] {\n switch (event.type) {\n case 'annotation.created':\n case 'annotation.updated':\n case 'annotation.deleted':\n case 'annotation.moved': // z-order move — baked stacking can change\n return [event.pageObjectNumber];\n // A field's widgets can live on several pages; the results name exactly\n // the widgets whose appearance changed, each with its page.\n case 'form.valueChanged':\n case 'form.effectsApplied':\n return event.changedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldDeleted':\n return event.removedWidgets.map((w) => w.pageObjectNumber);\n case 'form.fieldCreated':\n case 'form.fieldUpdated':\n case 'form.widgetAttached':\n case 'form.widgetDetached':\n return event.field.widgets.map((w) => w.pageObjectNumber);\n // Coarse results (counts only, no per-widget detail) — repaint every page.\n case 'form.imported':\n case 'form.repaired':\n return allPons();\n // pages.* replace the page registry: the kernel's `revision` bump already\n // re-keys every layout, and metadata never touches pixels.\n default:\n return [];\n }\n}\n\nexport function registerRenderEffects(ctx: EffectContext<RenderState, RenderAction>): void {\n const doc = ctx.doc;\n if (!doc) return;\n const allPons = () => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Content-scope facts: the page ITSELF changed. A redaction apply\n // destroys page content, so every applied page's base raster is stale —\n // and content strictly contains annotations, so annotated readers bump\n // too (see reducer.ts). Origin-agnostic like the rest of this map.\n if (event.type === 'redaction.applied') {\n const pons = event.results\n .filter((r) => r.status === 'applied')\n .map((r) => r.pageObjectNumber);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'content', pons });\n return;\n }\n const pons = annotatedPons(event, allPons);\n if (pons.length) ctx.dispatch({ type: 'INVALIDATE', scope: 'annotations', pons });\n });\n ctx.cleanup(unsubscribe);\n}\n","import type { RenderAction, RenderState } from './types';\n\nexport const initialRenderState = (): RenderState => ({\n contentEpochs: {},\n annotatedEpochs: {},\n paintVersions: {},\n});\n\nconst bump = (\n epochs: Readonly<Record<number, number>>,\n pons: readonly number[],\n): Record<number, number> => {\n const next: Record<number, number> = { ...epochs };\n for (const pon of pons) next[pon] = (next[pon] ?? 0) + 1;\n return next;\n};\n\n/** Pure. Two transitions: bump the touched pages' ledger for the fact's\n * scope (one action per fact, so a batch result bumps each pon once — a\n * 'content' fact bumps ONLY the content ledger; annotated readers sum both,\n * which is how content invalidation reaches them too), and the tile\n * wake-up counter. The render POLICY is deliberately NOT here — it's a\n * document fact on `DocumentMeta`, materialized by the kernel at open. */\nexport const renderReducer = (state: RenderState, a: RenderAction): RenderState => {\n if (a.type === 'PAINT_ADVANCED') {\n // A tile resolution/paint advanced the (store-external) tile state —\n // this bump only wakes subscribed layers to recompute `tilePlan`.\n return { ...state, paintVersions: bump(state.paintVersions, [a.pon]) };\n }\n if (a.type !== 'INVALIDATE' || a.pons.length === 0) return state;\n return a.scope === 'content'\n ? { ...state, contentEpochs: bump(state.contentEpochs, a.pons) }\n : { ...state, annotatedEpochs: bump(state.annotatedEpochs, a.pons) };\n};\n","import {\n createCapabilityToken,\n type EngineRenderPolicy,\n type PageImageHandle,\n type PageObjectNumber,\n type PageRenderViewport,\n} from '@embedpdf/core';\n\nimport type { PageViewDemand, TilePaintPlan, TilingConfig } from './paint-plan';\n\n/** Options for `renderPlugin()`. Tiling is capability + economics here;\n * WHICH view tiles is composition (mount a TileLayer per lens), and\n * WHETHER it engages is demand arithmetic — three levels, three owners. */\nexport interface RenderPluginOptions {\n tiling?: TilingConfig;\n}\n\nexport interface RenderPageOptions {\n /** Device px per PDF point (use the page transform's `renderScale`). */\n scale: number;\n /**\n * Bake annotations into the page bitmap. Default true. Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't painted twice\n * (once baked, once by the overlay).\n */\n includeAnnotations?: boolean;\n /** Abort the render (camera moved / layer unmounted). */\n signal?: AbortSignal;\n}\n\n/**\n * The two invalidation scopes — every pixel-changing fact is one of them:\n *\n * 'annotations' — only baked APPEARANCES changed (an annotation mutated, a\n * form widget re-baked). Base renders keep their pixels.\n * 'content' — the PAGE ITSELF changed (redaction applied, text edited).\n * Invalidates everything: no mutation can change base pixels\n * yet leave an annotated raster valid, so content strictly\n * contains annotations.\n */\nexport type InvalidateScope = 'content' | 'annotations';\n\n/**\n * Per-page versions of the two raster products a page has — base\n * (`includeAnnotations: false`) and annotated. Fed by the document event\n * stream (see effects.ts) and by the `invalidate` verb: a confirmed\n * pixel-changing fact — own or remote — bumps the touched pages, and anything\n * holding a rendered bitmap (a thumbnail rail) refetches.\n */\nexport interface RenderState {\n /** Base-raster versions — bumped by CONTENT facts (redaction, text edit). */\n readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;\n /** Appearance versions — bumped by ANNOTATION facts (annotations, form widgets). */\n readonly annotatedEpochs: Readonly<Record<PageObjectNumber, number>>;\n /**\n * Tile paint-plan wake-ups. The tile manager's state (fetch/painted/\n * retention) lives OUTSIDE the store — it holds live handles and abort\n * controllers — so resolutions bump this counter to make subscribed\n * layers recompute `tilePlan`. The value itself carries no meaning.\n */\n readonly paintVersions: Readonly<Record<PageObjectNumber, number>>;\n}\n\nexport type RenderAction =\n | {\n type: 'INVALIDATE';\n scope: InvalidateScope;\n pons: readonly PageObjectNumber[];\n }\n | { type: 'PAINT_ADVANCED'; pon: PageObjectNumber };\n\nexport interface RenderCapability {\n /**\n * Render a page (by its durable pon) to an ENCODED image. Abortable. Encoded\n * output is identical for local & cloud and cheap over the wire (vs. raw RGBA).\n *\n * CONFORMS to the deployment policy's three-layer rule:\n * under a lattice the desired `scale` converts to the canonical ladder width\n * via `snapFullPageViewport`; under `continuous` it passes through exactly.\n * Same-key requests collapse in the plugin's raster store (singleflight +\n * LRU), so repeated asks inside one rung cost one engine call.\n */\n renderPage(pon: PageObjectNumber, options: RenderPageOptions): Promise<PageImageHandle>;\n /**\n * The identity of the raster `renderPage` would produce for these options —\n * canonical viewport + annotations flag + epoch, as one stable string.\n * Layers key their fetch effect on THIS instead of the raw scale: inside a\n * rung the key never changes (zoom 1.2→1.5 under a [1,2]-ish lattice is a\n * no-op — no refetch, no DOM churn); it changes exactly at rung crossings\n * and epoch bumps. Always available: the kernel materializes the policy on\n * `DocumentMeta` before the document publishes.\n */\n renderSourceKey(\n pon: PageObjectNumber,\n options: { scale: number; includeAnnotations?: boolean },\n ): string;\n /**\n * The canonical viewport a desired scale conforms to for this page —\n * width-kind on a lattice, the exact scale under `continuous`.\n */\n conformViewport(pon: PageObjectNumber, scale: number): PageRenderViewport;\n /** The document's advertised policy (sugar over `DocumentMeta.renderPolicy`). */\n renderPolicy(): EngineRenderPolicy;\n /**\n * The tile paint plan for a page under a host-supplied demand. Tiling is a\n * STRATEGY inside this plugin, not a sibling.\n * Memoized: the same object returns until the demand, an epoch, or a\n * tile resolution actually changes it, so layers can subscribe with\n * plain `Object.is`. Calling it schedules the want-set fetches (visible\n * first, prefetch ring after) — idempotent, store-deduped. Engagement\n * is pure arithmetic: it fires only when the policy's ladder caps the\n * base below the demand; a thumbnail-sized demand never engages.\n */\n tilePlan(\n pon: PageObjectNumber,\n demand: PageViewDemand,\n opts?: { includeAnnotations?: boolean },\n ): TilePaintPlan;\n /**\n * The painter's decode-boundary report: the `<img>` for this plan key\n * actually painted (onload). Retained coarser generations covered by\n * painted want-set tiles release on this signal — never on fetch\n * completion, which would flash during decode.\n */\n tilePainted(pon: PageObjectNumber, key: string): void;\n /** A lens unmounted its TileLayer: abort in-flight tile fetches and drop\n * the page's tile bookkeeping (resolved bytes stay cached). */\n releaseTiles(pon: PageObjectNumber): void;\n /**\n * Version of the raster the given options would produce. Key a long-lived\n * render on it: when it bumps, refetch. Base renders version on content\n * facts; annotated renders on content AND annotation facts. Bumps only on\n * CONFIRMED mutations — never optimistically — so a drag invalidates once,\n * at commit.\n */\n renderEpoch(pon: PageObjectNumber, includeAnnotations?: boolean): number;\n /**\n * Declare that page pixels changed — the open door for facts the built-in\n * event map doesn't know (a plugin's own mutation vocabulary: redaction,\n * text edit, anything third-party). Call at CONFIRMATION (after the engine\n * write resolves), never for optimistic previews — those belong in overlay\n * layers. `pons` omitted = every page; `scope` defaults to 'content'\n * (repaint everything) because a caller who doesn't say is safest repainted\n * fully. Redundant with a mapped engine event? Harmless — one extra refetch.\n */\n invalidate(opts?: { pons?: readonly PageObjectNumber[]; scope?: InvalidateScope }): void;\n}\n\nexport const RenderToken = createCapabilityToken<RenderCapability>('render');\n","import { definePlugin } from '@embedpdf/core';\nimport { createRenderCapability } from './capability';\nimport { registerRenderEffects } from './effects';\nimport { initialRenderState, renderReducer } from './reducer';\nimport { RenderToken } from './types';\nimport type { RenderAction, RenderCapability, RenderPluginOptions, RenderState } from './types';\n\n/**\n * Document-scoped. The ONE policy consumer in the client stack: the doc-bind\n * effect resolves the engine's advertised render\n * policy; `renderPage` conforms desired scales to it and collapses\n * same-rung asks in the raster store; the tile manager turns host-supplied\n * demand into a retention-safe paint plan over the SAME store. State is the\n * per-page ledger — raster versions (two doors: the document event stream's\n * built-in map and the `invalidate` verb), the policy latch, and the tile\n * wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch\n * exactly when those change.\n */\nexport const renderPlugin = (options: RenderPluginOptions = {}) =>\n definePlugin<RenderState, RenderAction, RenderCapability>({\n id: 'render',\n scope: 'document',\n token: RenderToken,\n initialState: initialRenderState,\n reduce: renderReducer,\n capability: (ctx) => createRenderCapability(ctx, options),\n effects: registerRenderEffects,\n });\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { EngineRenderPolicy, PageImageHandle, PageObjectNumber, PageRenderViewport } from "@embedpdf/core";
|
|
2
|
+
import { Rect } from "@embedpdf/core-geometry";
|
|
3
|
+
//#region src/paint-plan.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The demand a page HOST supplies through dependency inversion:
|
|
6
|
+
* plugin-render defines the shape, producers fill it. The Stage's page
|
|
7
|
+
* host knows the camera and supplies `visibleRect`/`velocity`; a
|
|
8
|
+
* stage-less `<PageView>` supplies neither — absent `visibleRect` means
|
|
9
|
+
* "assume the whole page is visible", which a thumbnail-sized demand
|
|
10
|
+
* turns into "no tiles engage" by arithmetic, not configuration.
|
|
11
|
+
*/
|
|
12
|
+
interface PageViewDemand {
|
|
13
|
+
/** Desired device pixels across the page's unrotated content width. */
|
|
14
|
+
desiredDeviceWidth: number;
|
|
15
|
+
/** Visible page region — y-down page points. ABSENT = whole page. */
|
|
16
|
+
visibleRect?: Rect;
|
|
17
|
+
/** Scroll velocity in page points/s — prefetch direction bias only. */
|
|
18
|
+
velocity?: {
|
|
19
|
+
dx: number;
|
|
20
|
+
dy: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One tile the layer should have in the DOM. `key` is the reconciliation
|
|
25
|
+
* identity (stable across plan recomputes — keyed lists preserve the DOM
|
|
26
|
+
* node, which IS the retention mechanism); `rect` is y-down page points
|
|
27
|
+
* inside the unrotated content box; `z` stacks by resolution so arriving
|
|
28
|
+
* sharper tiles occlude retained coarser ones per region.
|
|
29
|
+
*/
|
|
30
|
+
interface TilePaintSource {
|
|
31
|
+
key: string;
|
|
32
|
+
scale: number;
|
|
33
|
+
rect: Rect;
|
|
34
|
+
z: number;
|
|
35
|
+
handle: PageImageHandle;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What a TileLayer paints right now. `paint` draws ONLY from resolved
|
|
39
|
+
* rasters — retained generations live here until the release rules fire;
|
|
40
|
+
* "loading" never reaches the DOM. `fetching` is diagnostic (badge/tests).
|
|
41
|
+
*/
|
|
42
|
+
interface TilePaintPlan {
|
|
43
|
+
/** Tiles engaged at all? False = the base rung fully covers demand. */
|
|
44
|
+
engaged: boolean;
|
|
45
|
+
paint: TilePaintSource[];
|
|
46
|
+
/** Keys currently in flight (want-set members not yet resolved). */
|
|
47
|
+
fetching: string[];
|
|
48
|
+
/** Memoization stamp — a new object appears only when this changes. */
|
|
49
|
+
stamp: string;
|
|
50
|
+
}
|
|
51
|
+
/** Resolved tiling configuration (renderPlugin options + defaults). */
|
|
52
|
+
interface TilingConfig {
|
|
53
|
+
/** Tile edge in device px. Default 512. */
|
|
54
|
+
tileSize?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Client pyramid used while the policy doesn't advertise `tiles`.
|
|
57
|
+
* Default `[1, 2, 4, 8, 16, 32]` — ×2 aligned.
|
|
58
|
+
*/
|
|
59
|
+
scales?: readonly number[];
|
|
60
|
+
/**
|
|
61
|
+
* Sharpness deficit (desired ÷ supplied-by-base) above which tiles
|
|
62
|
+
* engage. Default 1.25 — small CSS upscales read fine; past that the
|
|
63
|
+
* ladder cap is visible and the pyramid takes over.
|
|
64
|
+
*/
|
|
65
|
+
engageAt?: number;
|
|
66
|
+
/** Prefetch ring around the visible rect. */
|
|
67
|
+
prefetch?: {
|
|
68
|
+
/** Extra coverage per side, in fractions of the visible rect. Default 0.5. */
|
|
69
|
+
margin?: number;
|
|
70
|
+
/** Stretch the ring toward scroll direction. Default true. */
|
|
71
|
+
velocityBias?: boolean;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Hysteresis for LEVEL changes (a zoom settling between pyramid rungs):
|
|
75
|
+
* tile fetches for a NEW level wait this long; pan-driven fetches at the
|
|
76
|
+
* current level fire immediately. Default 150ms; 0 disables.
|
|
77
|
+
*/
|
|
78
|
+
settleMs?: number;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/types.d.ts
|
|
82
|
+
/** Options for `renderPlugin()`. Tiling is capability + economics here;
|
|
83
|
+
* WHICH view tiles is composition (mount a TileLayer per lens), and
|
|
84
|
+
* WHETHER it engages is demand arithmetic — three levels, three owners. */
|
|
85
|
+
interface RenderPluginOptions {
|
|
86
|
+
tiling?: TilingConfig;
|
|
87
|
+
}
|
|
88
|
+
interface RenderPageOptions {
|
|
89
|
+
/** Device px per PDF point (use the page transform's `renderScale`). */
|
|
90
|
+
scale: number;
|
|
91
|
+
/**
|
|
92
|
+
* Bake annotations into the page bitmap. Default true. Pass false when an
|
|
93
|
+
* <AnnotationLayer> owns annotation rendering, so they aren't painted twice
|
|
94
|
+
* (once baked, once by the overlay).
|
|
95
|
+
*/
|
|
96
|
+
includeAnnotations?: boolean;
|
|
97
|
+
/** Abort the render (camera moved / layer unmounted). */
|
|
98
|
+
signal?: AbortSignal;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The two invalidation scopes — every pixel-changing fact is one of them:
|
|
102
|
+
*
|
|
103
|
+
* 'annotations' — only baked APPEARANCES changed (an annotation mutated, a
|
|
104
|
+
* form widget re-baked). Base renders keep their pixels.
|
|
105
|
+
* 'content' — the PAGE ITSELF changed (redaction applied, text edited).
|
|
106
|
+
* Invalidates everything: no mutation can change base pixels
|
|
107
|
+
* yet leave an annotated raster valid, so content strictly
|
|
108
|
+
* contains annotations.
|
|
109
|
+
*/
|
|
110
|
+
type InvalidateScope = 'content' | 'annotations';
|
|
111
|
+
/**
|
|
112
|
+
* Per-page versions of the two raster products a page has — base
|
|
113
|
+
* (`includeAnnotations: false`) and annotated. Fed by the document event
|
|
114
|
+
* stream (see effects.ts) and by the `invalidate` verb: a confirmed
|
|
115
|
+
* pixel-changing fact — own or remote — bumps the touched pages, and anything
|
|
116
|
+
* holding a rendered bitmap (a thumbnail rail) refetches.
|
|
117
|
+
*/
|
|
118
|
+
interface RenderState {
|
|
119
|
+
/** Base-raster versions — bumped by CONTENT facts (redaction, text edit). */
|
|
120
|
+
readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
121
|
+
/** Appearance versions — bumped by ANNOTATION facts (annotations, form widgets). */
|
|
122
|
+
readonly annotatedEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
123
|
+
/**
|
|
124
|
+
* Tile paint-plan wake-ups. The tile manager's state (fetch/painted/
|
|
125
|
+
* retention) lives OUTSIDE the store — it holds live handles and abort
|
|
126
|
+
* controllers — so resolutions bump this counter to make subscribed
|
|
127
|
+
* layers recompute `tilePlan`. The value itself carries no meaning.
|
|
128
|
+
*/
|
|
129
|
+
readonly paintVersions: Readonly<Record<PageObjectNumber, number>>;
|
|
130
|
+
}
|
|
131
|
+
type RenderAction = {
|
|
132
|
+
type: 'INVALIDATE';
|
|
133
|
+
scope: InvalidateScope;
|
|
134
|
+
pons: readonly PageObjectNumber[];
|
|
135
|
+
} | {
|
|
136
|
+
type: 'PAINT_ADVANCED';
|
|
137
|
+
pon: PageObjectNumber;
|
|
138
|
+
};
|
|
139
|
+
interface RenderCapability {
|
|
140
|
+
/**
|
|
141
|
+
* Render a page (by its durable pon) to an ENCODED image. Abortable. Encoded
|
|
142
|
+
* output is identical for local & cloud and cheap over the wire (vs. raw RGBA).
|
|
143
|
+
*
|
|
144
|
+
* CONFORMS to the deployment policy's three-layer rule:
|
|
145
|
+
* under a lattice the desired `scale` converts to the canonical ladder width
|
|
146
|
+
* via `snapFullPageViewport`; under `continuous` it passes through exactly.
|
|
147
|
+
* Same-key requests collapse in the plugin's raster store (singleflight +
|
|
148
|
+
* LRU), so repeated asks inside one rung cost one engine call.
|
|
149
|
+
*/
|
|
150
|
+
renderPage(pon: PageObjectNumber, options: RenderPageOptions): Promise<PageImageHandle>;
|
|
151
|
+
/**
|
|
152
|
+
* The identity of the raster `renderPage` would produce for these options —
|
|
153
|
+
* canonical viewport + annotations flag + epoch, as one stable string.
|
|
154
|
+
* Layers key their fetch effect on THIS instead of the raw scale: inside a
|
|
155
|
+
* rung the key never changes (zoom 1.2→1.5 under a [1,2]-ish lattice is a
|
|
156
|
+
* no-op — no refetch, no DOM churn); it changes exactly at rung crossings
|
|
157
|
+
* and epoch bumps. Always available: the kernel materializes the policy on
|
|
158
|
+
* `DocumentMeta` before the document publishes.
|
|
159
|
+
*/
|
|
160
|
+
renderSourceKey(pon: PageObjectNumber, options: {
|
|
161
|
+
scale: number;
|
|
162
|
+
includeAnnotations?: boolean;
|
|
163
|
+
}): string;
|
|
164
|
+
/**
|
|
165
|
+
* The canonical viewport a desired scale conforms to for this page —
|
|
166
|
+
* width-kind on a lattice, the exact scale under `continuous`.
|
|
167
|
+
*/
|
|
168
|
+
conformViewport(pon: PageObjectNumber, scale: number): PageRenderViewport;
|
|
169
|
+
/** The document's advertised policy (sugar over `DocumentMeta.renderPolicy`). */
|
|
170
|
+
renderPolicy(): EngineRenderPolicy;
|
|
171
|
+
/**
|
|
172
|
+
* The tile paint plan for a page under a host-supplied demand. Tiling is a
|
|
173
|
+
* STRATEGY inside this plugin, not a sibling.
|
|
174
|
+
* Memoized: the same object returns until the demand, an epoch, or a
|
|
175
|
+
* tile resolution actually changes it, so layers can subscribe with
|
|
176
|
+
* plain `Object.is`. Calling it schedules the want-set fetches (visible
|
|
177
|
+
* first, prefetch ring after) — idempotent, store-deduped. Engagement
|
|
178
|
+
* is pure arithmetic: it fires only when the policy's ladder caps the
|
|
179
|
+
* base below the demand; a thumbnail-sized demand never engages.
|
|
180
|
+
*/
|
|
181
|
+
tilePlan(pon: PageObjectNumber, demand: PageViewDemand, opts?: {
|
|
182
|
+
includeAnnotations?: boolean;
|
|
183
|
+
}): TilePaintPlan;
|
|
184
|
+
/**
|
|
185
|
+
* The painter's decode-boundary report: the `<img>` for this plan key
|
|
186
|
+
* actually painted (onload). Retained coarser generations covered by
|
|
187
|
+
* painted want-set tiles release on this signal — never on fetch
|
|
188
|
+
* completion, which would flash during decode.
|
|
189
|
+
*/
|
|
190
|
+
tilePainted(pon: PageObjectNumber, key: string): void;
|
|
191
|
+
/** A lens unmounted its TileLayer: abort in-flight tile fetches and drop
|
|
192
|
+
* the page's tile bookkeeping (resolved bytes stay cached). */
|
|
193
|
+
releaseTiles(pon: PageObjectNumber): void;
|
|
194
|
+
/**
|
|
195
|
+
* Version of the raster the given options would produce. Key a long-lived
|
|
196
|
+
* render on it: when it bumps, refetch. Base renders version on content
|
|
197
|
+
* facts; annotated renders on content AND annotation facts. Bumps only on
|
|
198
|
+
* CONFIRMED mutations — never optimistically — so a drag invalidates once,
|
|
199
|
+
* at commit.
|
|
200
|
+
*/
|
|
201
|
+
renderEpoch(pon: PageObjectNumber, includeAnnotations?: boolean): number;
|
|
202
|
+
/**
|
|
203
|
+
* Declare that page pixels changed — the open door for facts the built-in
|
|
204
|
+
* event map doesn't know (a plugin's own mutation vocabulary: redaction,
|
|
205
|
+
* text edit, anything third-party). Call at CONFIRMATION (after the engine
|
|
206
|
+
* write resolves), never for optimistic previews — those belong in overlay
|
|
207
|
+
* layers. `pons` omitted = every page; `scope` defaults to 'content'
|
|
208
|
+
* (repaint everything) because a caller who doesn't say is safest repainted
|
|
209
|
+
* fully. Redundant with a mapped engine event? Harmless — one extra refetch.
|
|
210
|
+
*/
|
|
211
|
+
invalidate(opts?: {
|
|
212
|
+
pons?: readonly PageObjectNumber[];
|
|
213
|
+
scope?: InvalidateScope;
|
|
214
|
+
}): void;
|
|
215
|
+
}
|
|
216
|
+
declare const RenderToken: import("@embedpdf/core").CapabilityToken<RenderCapability>;
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/render.plugin.d.ts
|
|
219
|
+
/**
|
|
220
|
+
* Document-scoped. The ONE policy consumer in the client stack: the doc-bind
|
|
221
|
+
* effect resolves the engine's advertised render
|
|
222
|
+
* policy; `renderPage` conforms desired scales to it and collapses
|
|
223
|
+
* same-rung asks in the raster store; the tile manager turns host-supplied
|
|
224
|
+
* demand into a retention-safe paint plan over the SAME store. State is the
|
|
225
|
+
* per-page ledger — raster versions (two doors: the document event stream's
|
|
226
|
+
* built-in map and the `invalidate` verb), the policy latch, and the tile
|
|
227
|
+
* wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch
|
|
228
|
+
* exactly when those change.
|
|
229
|
+
*/
|
|
230
|
+
declare const renderPlugin: (options?: RenderPluginOptions) => import("@embedpdf/core").PluginDef<RenderState, RenderAction, RenderCapability>;
|
|
231
|
+
//#endregion
|
|
232
|
+
export { type InvalidateScope, type PageViewDemand, type RenderCapability, type RenderPluginOptions, type RenderState, RenderToken, type TilePaintPlan, type TilePaintSource, type TilingConfig, renderPlugin };
|
|
233
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,233 @@
|
|
|
1
|
-
|
|
1
|
+
import { EngineRenderPolicy, PageImageHandle, PageObjectNumber, PageRenderViewport } from "@embedpdf/core";
|
|
2
|
+
import { Rect } from "@embedpdf/core-geometry";
|
|
3
|
+
//#region src/paint-plan.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The demand a page HOST supplies through dependency inversion:
|
|
6
|
+
* plugin-render defines the shape, producers fill it. The Stage's page
|
|
7
|
+
* host knows the camera and supplies `visibleRect`/`velocity`; a
|
|
8
|
+
* stage-less `<PageView>` supplies neither — absent `visibleRect` means
|
|
9
|
+
* "assume the whole page is visible", which a thumbnail-sized demand
|
|
10
|
+
* turns into "no tiles engage" by arithmetic, not configuration.
|
|
11
|
+
*/
|
|
12
|
+
interface PageViewDemand {
|
|
13
|
+
/** Desired device pixels across the page's unrotated content width. */
|
|
14
|
+
desiredDeviceWidth: number;
|
|
15
|
+
/** Visible page region — y-down page points. ABSENT = whole page. */
|
|
16
|
+
visibleRect?: Rect;
|
|
17
|
+
/** Scroll velocity in page points/s — prefetch direction bias only. */
|
|
18
|
+
velocity?: {
|
|
19
|
+
dx: number;
|
|
20
|
+
dy: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One tile the layer should have in the DOM. `key` is the reconciliation
|
|
25
|
+
* identity (stable across plan recomputes — keyed lists preserve the DOM
|
|
26
|
+
* node, which IS the retention mechanism); `rect` is y-down page points
|
|
27
|
+
* inside the unrotated content box; `z` stacks by resolution so arriving
|
|
28
|
+
* sharper tiles occlude retained coarser ones per region.
|
|
29
|
+
*/
|
|
30
|
+
interface TilePaintSource {
|
|
31
|
+
key: string;
|
|
32
|
+
scale: number;
|
|
33
|
+
rect: Rect;
|
|
34
|
+
z: number;
|
|
35
|
+
handle: PageImageHandle;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What a TileLayer paints right now. `paint` draws ONLY from resolved
|
|
39
|
+
* rasters — retained generations live here until the release rules fire;
|
|
40
|
+
* "loading" never reaches the DOM. `fetching` is diagnostic (badge/tests).
|
|
41
|
+
*/
|
|
42
|
+
interface TilePaintPlan {
|
|
43
|
+
/** Tiles engaged at all? False = the base rung fully covers demand. */
|
|
44
|
+
engaged: boolean;
|
|
45
|
+
paint: TilePaintSource[];
|
|
46
|
+
/** Keys currently in flight (want-set members not yet resolved). */
|
|
47
|
+
fetching: string[];
|
|
48
|
+
/** Memoization stamp — a new object appears only when this changes. */
|
|
49
|
+
stamp: string;
|
|
50
|
+
}
|
|
51
|
+
/** Resolved tiling configuration (renderPlugin options + defaults). */
|
|
52
|
+
interface TilingConfig {
|
|
53
|
+
/** Tile edge in device px. Default 512. */
|
|
54
|
+
tileSize?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Client pyramid used while the policy doesn't advertise `tiles`.
|
|
57
|
+
* Default `[1, 2, 4, 8, 16, 32]` — ×2 aligned.
|
|
58
|
+
*/
|
|
59
|
+
scales?: readonly number[];
|
|
60
|
+
/**
|
|
61
|
+
* Sharpness deficit (desired ÷ supplied-by-base) above which tiles
|
|
62
|
+
* engage. Default 1.25 — small CSS upscales read fine; past that the
|
|
63
|
+
* ladder cap is visible and the pyramid takes over.
|
|
64
|
+
*/
|
|
65
|
+
engageAt?: number;
|
|
66
|
+
/** Prefetch ring around the visible rect. */
|
|
67
|
+
prefetch?: {
|
|
68
|
+
/** Extra coverage per side, in fractions of the visible rect. Default 0.5. */
|
|
69
|
+
margin?: number;
|
|
70
|
+
/** Stretch the ring toward scroll direction. Default true. */
|
|
71
|
+
velocityBias?: boolean;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Hysteresis for LEVEL changes (a zoom settling between pyramid rungs):
|
|
75
|
+
* tile fetches for a NEW level wait this long; pan-driven fetches at the
|
|
76
|
+
* current level fire immediately. Default 150ms; 0 disables.
|
|
77
|
+
*/
|
|
78
|
+
settleMs?: number;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/types.d.ts
|
|
82
|
+
/** Options for `renderPlugin()`. Tiling is capability + economics here;
|
|
83
|
+
* WHICH view tiles is composition (mount a TileLayer per lens), and
|
|
84
|
+
* WHETHER it engages is demand arithmetic — three levels, three owners. */
|
|
85
|
+
interface RenderPluginOptions {
|
|
86
|
+
tiling?: TilingConfig;
|
|
87
|
+
}
|
|
88
|
+
interface RenderPageOptions {
|
|
89
|
+
/** Device px per PDF point (use the page transform's `renderScale`). */
|
|
90
|
+
scale: number;
|
|
91
|
+
/**
|
|
92
|
+
* Bake annotations into the page bitmap. Default true. Pass false when an
|
|
93
|
+
* <AnnotationLayer> owns annotation rendering, so they aren't painted twice
|
|
94
|
+
* (once baked, once by the overlay).
|
|
95
|
+
*/
|
|
96
|
+
includeAnnotations?: boolean;
|
|
97
|
+
/** Abort the render (camera moved / layer unmounted). */
|
|
98
|
+
signal?: AbortSignal;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The two invalidation scopes — every pixel-changing fact is one of them:
|
|
102
|
+
*
|
|
103
|
+
* 'annotations' — only baked APPEARANCES changed (an annotation mutated, a
|
|
104
|
+
* form widget re-baked). Base renders keep their pixels.
|
|
105
|
+
* 'content' — the PAGE ITSELF changed (redaction applied, text edited).
|
|
106
|
+
* Invalidates everything: no mutation can change base pixels
|
|
107
|
+
* yet leave an annotated raster valid, so content strictly
|
|
108
|
+
* contains annotations.
|
|
109
|
+
*/
|
|
110
|
+
type InvalidateScope = 'content' | 'annotations';
|
|
111
|
+
/**
|
|
112
|
+
* Per-page versions of the two raster products a page has — base
|
|
113
|
+
* (`includeAnnotations: false`) and annotated. Fed by the document event
|
|
114
|
+
* stream (see effects.ts) and by the `invalidate` verb: a confirmed
|
|
115
|
+
* pixel-changing fact — own or remote — bumps the touched pages, and anything
|
|
116
|
+
* holding a rendered bitmap (a thumbnail rail) refetches.
|
|
117
|
+
*/
|
|
118
|
+
interface RenderState {
|
|
119
|
+
/** Base-raster versions — bumped by CONTENT facts (redaction, text edit). */
|
|
120
|
+
readonly contentEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
121
|
+
/** Appearance versions — bumped by ANNOTATION facts (annotations, form widgets). */
|
|
122
|
+
readonly annotatedEpochs: Readonly<Record<PageObjectNumber, number>>;
|
|
123
|
+
/**
|
|
124
|
+
* Tile paint-plan wake-ups. The tile manager's state (fetch/painted/
|
|
125
|
+
* retention) lives OUTSIDE the store — it holds live handles and abort
|
|
126
|
+
* controllers — so resolutions bump this counter to make subscribed
|
|
127
|
+
* layers recompute `tilePlan`. The value itself carries no meaning.
|
|
128
|
+
*/
|
|
129
|
+
readonly paintVersions: Readonly<Record<PageObjectNumber, number>>;
|
|
130
|
+
}
|
|
131
|
+
type RenderAction = {
|
|
132
|
+
type: 'INVALIDATE';
|
|
133
|
+
scope: InvalidateScope;
|
|
134
|
+
pons: readonly PageObjectNumber[];
|
|
135
|
+
} | {
|
|
136
|
+
type: 'PAINT_ADVANCED';
|
|
137
|
+
pon: PageObjectNumber;
|
|
138
|
+
};
|
|
139
|
+
interface RenderCapability {
|
|
140
|
+
/**
|
|
141
|
+
* Render a page (by its durable pon) to an ENCODED image. Abortable. Encoded
|
|
142
|
+
* output is identical for local & cloud and cheap over the wire (vs. raw RGBA).
|
|
143
|
+
*
|
|
144
|
+
* CONFORMS to the deployment policy's three-layer rule:
|
|
145
|
+
* under a lattice the desired `scale` converts to the canonical ladder width
|
|
146
|
+
* via `snapFullPageViewport`; under `continuous` it passes through exactly.
|
|
147
|
+
* Same-key requests collapse in the plugin's raster store (singleflight +
|
|
148
|
+
* LRU), so repeated asks inside one rung cost one engine call.
|
|
149
|
+
*/
|
|
150
|
+
renderPage(pon: PageObjectNumber, options: RenderPageOptions): Promise<PageImageHandle>;
|
|
151
|
+
/**
|
|
152
|
+
* The identity of the raster `renderPage` would produce for these options —
|
|
153
|
+
* canonical viewport + annotations flag + epoch, as one stable string.
|
|
154
|
+
* Layers key their fetch effect on THIS instead of the raw scale: inside a
|
|
155
|
+
* rung the key never changes (zoom 1.2→1.5 under a [1,2]-ish lattice is a
|
|
156
|
+
* no-op — no refetch, no DOM churn); it changes exactly at rung crossings
|
|
157
|
+
* and epoch bumps. Always available: the kernel materializes the policy on
|
|
158
|
+
* `DocumentMeta` before the document publishes.
|
|
159
|
+
*/
|
|
160
|
+
renderSourceKey(pon: PageObjectNumber, options: {
|
|
161
|
+
scale: number;
|
|
162
|
+
includeAnnotations?: boolean;
|
|
163
|
+
}): string;
|
|
164
|
+
/**
|
|
165
|
+
* The canonical viewport a desired scale conforms to for this page —
|
|
166
|
+
* width-kind on a lattice, the exact scale under `continuous`.
|
|
167
|
+
*/
|
|
168
|
+
conformViewport(pon: PageObjectNumber, scale: number): PageRenderViewport;
|
|
169
|
+
/** The document's advertised policy (sugar over `DocumentMeta.renderPolicy`). */
|
|
170
|
+
renderPolicy(): EngineRenderPolicy;
|
|
171
|
+
/**
|
|
172
|
+
* The tile paint plan for a page under a host-supplied demand. Tiling is a
|
|
173
|
+
* STRATEGY inside this plugin, not a sibling.
|
|
174
|
+
* Memoized: the same object returns until the demand, an epoch, or a
|
|
175
|
+
* tile resolution actually changes it, so layers can subscribe with
|
|
176
|
+
* plain `Object.is`. Calling it schedules the want-set fetches (visible
|
|
177
|
+
* first, prefetch ring after) — idempotent, store-deduped. Engagement
|
|
178
|
+
* is pure arithmetic: it fires only when the policy's ladder caps the
|
|
179
|
+
* base below the demand; a thumbnail-sized demand never engages.
|
|
180
|
+
*/
|
|
181
|
+
tilePlan(pon: PageObjectNumber, demand: PageViewDemand, opts?: {
|
|
182
|
+
includeAnnotations?: boolean;
|
|
183
|
+
}): TilePaintPlan;
|
|
184
|
+
/**
|
|
185
|
+
* The painter's decode-boundary report: the `<img>` for this plan key
|
|
186
|
+
* actually painted (onload). Retained coarser generations covered by
|
|
187
|
+
* painted want-set tiles release on this signal — never on fetch
|
|
188
|
+
* completion, which would flash during decode.
|
|
189
|
+
*/
|
|
190
|
+
tilePainted(pon: PageObjectNumber, key: string): void;
|
|
191
|
+
/** A lens unmounted its TileLayer: abort in-flight tile fetches and drop
|
|
192
|
+
* the page's tile bookkeeping (resolved bytes stay cached). */
|
|
193
|
+
releaseTiles(pon: PageObjectNumber): void;
|
|
194
|
+
/**
|
|
195
|
+
* Version of the raster the given options would produce. Key a long-lived
|
|
196
|
+
* render on it: when it bumps, refetch. Base renders version on content
|
|
197
|
+
* facts; annotated renders on content AND annotation facts. Bumps only on
|
|
198
|
+
* CONFIRMED mutations — never optimistically — so a drag invalidates once,
|
|
199
|
+
* at commit.
|
|
200
|
+
*/
|
|
201
|
+
renderEpoch(pon: PageObjectNumber, includeAnnotations?: boolean): number;
|
|
202
|
+
/**
|
|
203
|
+
* Declare that page pixels changed — the open door for facts the built-in
|
|
204
|
+
* event map doesn't know (a plugin's own mutation vocabulary: redaction,
|
|
205
|
+
* text edit, anything third-party). Call at CONFIRMATION (after the engine
|
|
206
|
+
* write resolves), never for optimistic previews — those belong in overlay
|
|
207
|
+
* layers. `pons` omitted = every page; `scope` defaults to 'content'
|
|
208
|
+
* (repaint everything) because a caller who doesn't say is safest repainted
|
|
209
|
+
* fully. Redundant with a mapped engine event? Harmless — one extra refetch.
|
|
210
|
+
*/
|
|
211
|
+
invalidate(opts?: {
|
|
212
|
+
pons?: readonly PageObjectNumber[];
|
|
213
|
+
scope?: InvalidateScope;
|
|
214
|
+
}): void;
|
|
215
|
+
}
|
|
216
|
+
declare const RenderToken: import("@embedpdf/core").CapabilityToken<RenderCapability>;
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/render.plugin.d.ts
|
|
219
|
+
/**
|
|
220
|
+
* Document-scoped. The ONE policy consumer in the client stack: the doc-bind
|
|
221
|
+
* effect resolves the engine's advertised render
|
|
222
|
+
* policy; `renderPage` conforms desired scales to it and collapses
|
|
223
|
+
* same-rung asks in the raster store; the tile manager turns host-supplied
|
|
224
|
+
* demand into a retention-safe paint plan over the SAME store. State is the
|
|
225
|
+
* per-page ledger — raster versions (two doors: the document event stream's
|
|
226
|
+
* built-in map and the `invalidate` verb), the policy latch, and the tile
|
|
227
|
+
* wake-up counter. Layers key on `renderSourceKey`/`tilePlan` and refetch
|
|
228
|
+
* exactly when those change.
|
|
229
|
+
*/
|
|
230
|
+
declare const renderPlugin: (options?: RenderPluginOptions) => import("@embedpdf/core").PluginDef<RenderState, RenderAction, RenderCapability>;
|
|
231
|
+
//#endregion
|
|
232
|
+
export { type InvalidateScope, type PageViewDemand, type RenderCapability, type RenderPluginOptions, type RenderState, RenderToken, type TilePaintPlan, type TilePaintSource, type TilingConfig, renderPlugin };
|
|
233
|
+
//# sourceMappingURL=index.d.ts.map
|