@embedpdf/plugin-render 3.0.0-next.14 → 3.0.0-next.15

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.
@@ -0,0 +1,18 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_contract = require("../contract-CGcu-84H.cjs");
3
+ //#region src/host-contract.ts
4
+ /**
5
+ * @embedpdf/plugin-render/contract/host — the host lens.
6
+ *
7
+ * What view layers need to paint: the conforming render door and its source
8
+ * keys, the resolved paint settings, and a per-view tile surface whose reads
9
+ * are pure. Same runtime token as the public one, typed wider. Never use
10
+ * this from application code.
11
+ */
12
+ const RenderToken = (0, require("@embedpdf/core").createHostToken)(require_contract.RenderToken);
13
+ //#endregion
14
+ exports.RENDER_DEFAULTS = require_contract.RENDER_DEFAULTS;
15
+ exports.RenderToken = RenderToken;
16
+ exports.samePageViewDemand = require_contract.samePageViewDemand;
17
+
18
+ //# sourceMappingURL=host.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.cjs","names":["PublicRenderToken"],"sources":["../../src/host-contract.ts"],"sourcesContent":["/**\n * @embedpdf/plugin-render/contract/host — the host lens.\n *\n * What view layers need to paint: the conforming render door and its source\n * keys, the resolved paint settings, and a per-view tile surface whose reads\n * are pure. Same runtime token as the public one, typed wider. Never use\n * this from application code.\n */\nimport {\n createHostToken,\n type AnnotationAppearanceImage,\n type AnnotationRef,\n type EventHook,\n type OperationOptions,\n type PageLayerRights,\n type PageRef,\n type PageRenderViewport,\n type PluginErrorInfo,\n} from '@embedpdf/core';\nimport {\n RenderToken as PublicRenderToken,\n type PageImage,\n type PageLayerOptions,\n type RenderCapability,\n} from './contract';\nimport type { PageViewDemand, TilePaintPlan } from './paint-plan';\n\nexport * from './contract';\nexport type { TilePaintPlan, TilePaintSource } from './paint-plan';\n\n/** Layer-facing paint settings (resolved config the view layers need). Reference-stable. */\nexport interface PaintSettings {\n /** Whether the tile plane is enabled at all (`tiles: false` opts out). */\n readonly tiles: boolean;\n}\n\nexport interface RenderSourceOptions extends PageLayerOptions {\n /** Device px per PDF point (the page transform's `renderScale`). */\n scale: number;\n /**\n * The view the picture is for (the page context's `view`): the engine ranks\n * the render by where the page is in that view (`doc.with({ view })`).\n */\n view?: string;\n}\n\n/**\n * One view's tile surface. Identity (the view) is bound at creation; every\n * call addresses that view's own state for the given page, so two views\n * showing the same page plan independently — a thumbnail rail's\n * never-engaging demand cannot disturb the main view's tiles.\n *\n * Reads are pure: `setDemand` is the one call that schedules fetches and\n * re-plans; `getPlan` returns the paint plan that demand produced (memoized: the\n * same object until the demand, an epoch, or a tile arrival changes it, so\n * layers subscribe with plain `Object.is`). Handles are reference-stable per\n * view id and reference-counted: every `createViewDemand` pairs with one\n * `dispose`.\n */\nexport interface ViewDemand {\n /** What this view wants for a page right now. Idempotent; schedules the want set. */\n setDemand(page: PageRef, demand: PageViewDemand, options?: PageLayerOptions): void;\n /** The current paint plan for a page under this view's demand. Pure. */\n getPlan(page: PageRef): TilePaintPlan;\n /** The image for this plan key had a presentation opportunity. */\n markPainted(page: PageRef, key: string): void;\n /** The inverse report: this plan key's element left the DOM. */\n markUnpainted(page: PageRef, key: string): void;\n /** This view unmounted its tile plane for the page: abort in-flight tile\n * fetches, drop its own bookkeeping (resolved bytes stay cached). */\n release(page: PageRef): void;\n /** Release every page of this view and drop the handle's reference. */\n dispose(): void;\n}\n\n/** Whose pictures: a page's annotations, or its form widgets. */\nexport type AppearanceFamily = 'annotations' | 'widgets';\n\n/** One of an owner's items on a page, at the version of its picture. */\nexport interface AppearanceItem {\n readonly ref: AnnotationRef;\n /** Moves when the engine draws the item again (`pictureVersion`): its pictures are fetched again. */\n readonly version: number;\n}\n\n/** What {@link RenderHostCapability.renderAppearances} takes. */\nexport interface AppearancesOptions extends OperationOptions {\n readonly family: AppearanceFamily;\n /** Device px per PDF point, conformed through {@link RenderHostCapability.getAppearanceScale}. */\n readonly scale: number;\n /** The owner's items on the page whose pictures it wants, each at its version. */\n readonly items: readonly AppearanceItem[];\n}\n\n/** The engine's picture of an item in one of its states, and the version of the item it shows. */\nexport interface AppearancePicture extends AnnotationAppearanceImage {\n readonly version: number;\n}\n\nexport interface RenderCompletedEvent {\n readonly page: PageRef;\n readonly key: string;\n}\n\nexport interface RenderFailedEvent {\n readonly page: PageRef;\n readonly key: string;\n readonly error: PluginErrorInfo;\n}\n\nexport interface RenderHostCapability extends RenderCapability {\n /**\n * The viewer door: render a page at the scale a view shows it, conformed\n * through the resolved render points (strategy ∧ policy): the exact\n * demand capped at the pixel budget under `continuous`, the advertised\n * ladder under a lattice — with same-key asks collapsing in the raster\n * store. Layers key their fetch on {@link getSourceKey}.\n */\n renderSource(page: PageRef, options: RenderSourceOptions & OperationOptions): Promise<PageImage>;\n /**\n * The identity of the raster `renderSource` would produce — conformed\n * width + the parts it draws + epoch (+ format), as one stable string.\n * Under a lattice it moves only at rung crossings; under exact-mode\n * `continuous` it tracks the demand and is constant above the budget.\n */\n getSourceKey(page: PageRef, options: RenderSourceOptions): string;\n /** The canonical viewport a desired scale conforms to for this page (always width-kind). */\n conformViewport(page: PageRef, scale: number): PageRenderViewport;\n getPaintSettings(): PaintSettings;\n /**\n * What the user may read of the parts a picture draws beside the page\n * content, so a view that leaves one part to a layer of its own can still\n * ask for the other. The same object until a right changes.\n */\n getLayerRights(): PageLayerRights;\n /** This view's tile surface. Stable per view id; pair with `dispose`. */\n createViewDemand(viewId: string): ViewDemand;\n /**\n * The engine's pictures of the owner's items on a page (`family`): each\n * item's look at rest, in every state it stores, each labelled with its\n * state and tagged with the item's version, so a checkbox switches picture\n * with no request, and a layer can tell a picture older than its item.\n * The owner keys the ask on its items' versions: asks for the same page,\n * family, scale and items share one fetch, and the last few sets of each\n * page are kept. Rejects `permission-denied` without `doc.annotate.read`\n * (annotations) or `doc.forms.read` (widgets).\n */\n renderAppearances(\n page: PageRef,\n options: AppearancesOptions,\n ): Promise<readonly AppearancePicture[]>;\n /**\n * An item's last pictures, every state, kept a while after it left its page:\n * an undo that brings it back shows them at once. Empty for one never drawn.\n */\n keptAppearances(family: AppearanceFamily, item: AnnotationRef): readonly AppearancePicture[];\n /** The appearance scale a render scale conforms to under the deployment's policy. */\n getAppearanceScale(renderScale: number): number;\n /** A base raster resolved / failed (the conforming and exact doors alike). */\n readonly onRenderCompleted: EventHook<RenderCompletedEvent>;\n readonly onRenderFailed: EventHook<RenderFailedEvent>;\n}\n\nexport const RenderToken = createHostToken<RenderHostCapability>(PublicRenderToken);\n"],"mappings":";;;;;;;;;;;AAmKA,MAAa,eAAA,2BAAA,CAAA,CAAA,gBAAA,CAAoDA,iBAAAA,WAAiB"}
@@ -0,0 +1,3 @@
1
+ import { _ as PageViewDemand, a as PageRender, b as TilesOptions, c as RenderConfig, d as RenderPageOptions, f as RenderPagesOptions, g as FullPageOptions, 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, v as TilePaintPlan, x as samePageViewDemand, y as TilePaintSource } from "../contract-uc5nOice.cjs";
2
+ import { a as PaintSettings, c as RenderHostCapability, d as ViewDemand, i as AppearancesOptions, l as RenderSourceOptions, n as AppearanceItem, o as RenderCompletedEvent, r as AppearancePicture, s as RenderFailedEvent, t as AppearanceFamily, u as RenderToken } from "../host-contract-CTOQn0Xz.cjs";
3
+ export { AppearanceFamily, AppearanceItem, AppearancePicture, AppearancesOptions, type FullPageOptions, InvalidateOptions, InvalidateScope, PageImage, PageLayerOptions, PageRender, type PageViewDemand, type PageViewDemand as ViewDemandInput, PaintSettings, RENDER_DEFAULTS, RenderCapability, RenderCompletedEvent, RenderConfig, RenderFailedEvent, RenderFormat, RenderHostCapability, RenderInvalidatedEvent, RenderPageOptions, RenderPagesOptions, RenderSettings, RenderSourceOptions, RenderThumbnailOptions, RenderToken, type TilePaintPlan, type TilePaintSource, type TilesOptions, ViewDemand, samePageViewDemand };
@@ -0,0 +1,3 @@
1
+ import { _ as PageViewDemand, a as PageRender, b as TilesOptions, c as RenderConfig, d as RenderPageOptions, f as RenderPagesOptions, g as FullPageOptions, 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, v as TilePaintPlan, x as samePageViewDemand, y as TilePaintSource } from "../contract-uc5nOice.js";
2
+ import { a as PaintSettings, c as RenderHostCapability, d as ViewDemand, i as AppearancesOptions, l as RenderSourceOptions, n as AppearanceItem, o as RenderCompletedEvent, r as AppearancePicture, s as RenderFailedEvent, t as AppearanceFamily, u as RenderToken } from "../host-contract-CsjZjWjb.js";
3
+ export { AppearanceFamily, AppearanceItem, AppearancePicture, AppearancesOptions, type FullPageOptions, InvalidateOptions, InvalidateScope, PageImage, PageLayerOptions, PageRender, type PageViewDemand, type PageViewDemand as ViewDemandInput, PaintSettings, RENDER_DEFAULTS, RenderCapability, RenderCompletedEvent, RenderConfig, RenderFailedEvent, RenderFormat, RenderHostCapability, RenderInvalidatedEvent, RenderPageOptions, RenderPagesOptions, RenderSettings, RenderSourceOptions, RenderThumbnailOptions, RenderToken, type TilePaintPlan, type TilePaintSource, type TilesOptions, ViewDemand, samePageViewDemand };
@@ -0,0 +1,16 @@
1
+ import { a as samePageViewDemand, n as RenderToken$1, t as RENDER_DEFAULTS } from "../contract-Dy5TiQDy.js";
2
+ import { createHostToken } from "@embedpdf/core";
3
+ //#region src/host-contract.ts
4
+ /**
5
+ * @embedpdf/plugin-render/contract/host — the host lens.
6
+ *
7
+ * What view layers need to paint: the conforming render door and its source
8
+ * keys, the resolved paint settings, and a per-view tile surface whose reads
9
+ * are pure. Same runtime token as the public one, typed wider. Never use
10
+ * this from application code.
11
+ */
12
+ const RenderToken = createHostToken(RenderToken$1);
13
+ //#endregion
14
+ export { RENDER_DEFAULTS, RenderToken, samePageViewDemand };
15
+
16
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","names":["PublicRenderToken"],"sources":["../../src/host-contract.ts"],"sourcesContent":["/**\n * @embedpdf/plugin-render/contract/host — the host lens.\n *\n * What view layers need to paint: the conforming render door and its source\n * keys, the resolved paint settings, and a per-view tile surface whose reads\n * are pure. Same runtime token as the public one, typed wider. Never use\n * this from application code.\n */\nimport {\n createHostToken,\n type AnnotationAppearanceImage,\n type AnnotationRef,\n type EventHook,\n type OperationOptions,\n type PageLayerRights,\n type PageRef,\n type PageRenderViewport,\n type PluginErrorInfo,\n} from '@embedpdf/core';\nimport {\n RenderToken as PublicRenderToken,\n type PageImage,\n type PageLayerOptions,\n type RenderCapability,\n} from './contract';\nimport type { PageViewDemand, TilePaintPlan } from './paint-plan';\n\nexport * from './contract';\nexport type { TilePaintPlan, TilePaintSource } from './paint-plan';\n\n/** Layer-facing paint settings (resolved config the view layers need). Reference-stable. */\nexport interface PaintSettings {\n /** Whether the tile plane is enabled at all (`tiles: false` opts out). */\n readonly tiles: boolean;\n}\n\nexport interface RenderSourceOptions extends PageLayerOptions {\n /** Device px per PDF point (the page transform's `renderScale`). */\n scale: number;\n /**\n * The view the picture is for (the page context's `view`): the engine ranks\n * the render by where the page is in that view (`doc.with({ view })`).\n */\n view?: string;\n}\n\n/**\n * One view's tile surface. Identity (the view) is bound at creation; every\n * call addresses that view's own state for the given page, so two views\n * showing the same page plan independently — a thumbnail rail's\n * never-engaging demand cannot disturb the main view's tiles.\n *\n * Reads are pure: `setDemand` is the one call that schedules fetches and\n * re-plans; `getPlan` returns the paint plan that demand produced (memoized: the\n * same object until the demand, an epoch, or a tile arrival changes it, so\n * layers subscribe with plain `Object.is`). Handles are reference-stable per\n * view id and reference-counted: every `createViewDemand` pairs with one\n * `dispose`.\n */\nexport interface ViewDemand {\n /** What this view wants for a page right now. Idempotent; schedules the want set. */\n setDemand(page: PageRef, demand: PageViewDemand, options?: PageLayerOptions): void;\n /** The current paint plan for a page under this view's demand. Pure. */\n getPlan(page: PageRef): TilePaintPlan;\n /** The image for this plan key had a presentation opportunity. */\n markPainted(page: PageRef, key: string): void;\n /** The inverse report: this plan key's element left the DOM. */\n markUnpainted(page: PageRef, key: string): void;\n /** This view unmounted its tile plane for the page: abort in-flight tile\n * fetches, drop its own bookkeeping (resolved bytes stay cached). */\n release(page: PageRef): void;\n /** Release every page of this view and drop the handle's reference. */\n dispose(): void;\n}\n\n/** Whose pictures: a page's annotations, or its form widgets. */\nexport type AppearanceFamily = 'annotations' | 'widgets';\n\n/** One of an owner's items on a page, at the version of its picture. */\nexport interface AppearanceItem {\n readonly ref: AnnotationRef;\n /** Moves when the engine draws the item again (`pictureVersion`): its pictures are fetched again. */\n readonly version: number;\n}\n\n/** What {@link RenderHostCapability.renderAppearances} takes. */\nexport interface AppearancesOptions extends OperationOptions {\n readonly family: AppearanceFamily;\n /** Device px per PDF point, conformed through {@link RenderHostCapability.getAppearanceScale}. */\n readonly scale: number;\n /** The owner's items on the page whose pictures it wants, each at its version. */\n readonly items: readonly AppearanceItem[];\n}\n\n/** The engine's picture of an item in one of its states, and the version of the item it shows. */\nexport interface AppearancePicture extends AnnotationAppearanceImage {\n readonly version: number;\n}\n\nexport interface RenderCompletedEvent {\n readonly page: PageRef;\n readonly key: string;\n}\n\nexport interface RenderFailedEvent {\n readonly page: PageRef;\n readonly key: string;\n readonly error: PluginErrorInfo;\n}\n\nexport interface RenderHostCapability extends RenderCapability {\n /**\n * The viewer door: render a page at the scale a view shows it, conformed\n * through the resolved render points (strategy ∧ policy): the exact\n * demand capped at the pixel budget under `continuous`, the advertised\n * ladder under a lattice — with same-key asks collapsing in the raster\n * store. Layers key their fetch on {@link getSourceKey}.\n */\n renderSource(page: PageRef, options: RenderSourceOptions & OperationOptions): Promise<PageImage>;\n /**\n * The identity of the raster `renderSource` would produce — conformed\n * width + the parts it draws + epoch (+ format), as one stable string.\n * Under a lattice it moves only at rung crossings; under exact-mode\n * `continuous` it tracks the demand and is constant above the budget.\n */\n getSourceKey(page: PageRef, options: RenderSourceOptions): string;\n /** The canonical viewport a desired scale conforms to for this page (always width-kind). */\n conformViewport(page: PageRef, scale: number): PageRenderViewport;\n getPaintSettings(): PaintSettings;\n /**\n * What the user may read of the parts a picture draws beside the page\n * content, so a view that leaves one part to a layer of its own can still\n * ask for the other. The same object until a right changes.\n */\n getLayerRights(): PageLayerRights;\n /** This view's tile surface. Stable per view id; pair with `dispose`. */\n createViewDemand(viewId: string): ViewDemand;\n /**\n * The engine's pictures of the owner's items on a page (`family`): each\n * item's look at rest, in every state it stores, each labelled with its\n * state and tagged with the item's version, so a checkbox switches picture\n * with no request, and a layer can tell a picture older than its item.\n * The owner keys the ask on its items' versions: asks for the same page,\n * family, scale and items share one fetch, and the last few sets of each\n * page are kept. Rejects `permission-denied` without `doc.annotate.read`\n * (annotations) or `doc.forms.read` (widgets).\n */\n renderAppearances(\n page: PageRef,\n options: AppearancesOptions,\n ): Promise<readonly AppearancePicture[]>;\n /**\n * An item's last pictures, every state, kept a while after it left its page:\n * an undo that brings it back shows them at once. Empty for one never drawn.\n */\n keptAppearances(family: AppearanceFamily, item: AnnotationRef): readonly AppearancePicture[];\n /** The appearance scale a render scale conforms to under the deployment's policy. */\n getAppearanceScale(renderScale: number): number;\n /** A base raster resolved / failed (the conforming and exact doors alike). */\n readonly onRenderCompleted: EventHook<RenderCompletedEvent>;\n readonly onRenderFailed: EventHook<RenderFailedEvent>;\n}\n\nexport const RenderToken = createHostToken<RenderHostCapability>(PublicRenderToken);\n"],"mappings":";;;;;;;;;;;AAmKA,MAAa,cAAc,gBAAsCA,aAAiB"}
@@ -0,0 +1,124 @@
1
+ let _embedpdf_core = require("@embedpdf/core");
2
+ //#region src/paint-plan.ts
3
+ /**
4
+ * Whether two demands ask for the same thing. A host builds a new demand object on every camera
5
+ * frame; a layer that compares with this sets it again (and plans again) only when it changed.
6
+ */
7
+ function samePageViewDemand(left, right) {
8
+ return left.desiredDeviceWidth === right.desiredDeviceWidth && left.velocity?.dx === right.velocity?.dx && left.velocity?.dy === right.velocity?.dy && (left.visibleRect === right.visibleRect || left.visibleRect !== void 0 && right.visibleRect !== void 0 && left.visibleRect.x === right.visibleRect.x && left.visibleRect.y === right.visibleRect.y && left.visibleRect.width === right.visibleRect.width && left.visibleRect.height === right.visibleRect.height);
9
+ }
10
+ const EMPTY_TILE_PLAN = {
11
+ engaged: false,
12
+ paint: [],
13
+ fetching: [],
14
+ stamp: "empty"
15
+ };
16
+ /** The default pixel budget of a whole-page picture, in device pixels. */
17
+ const DEFAULT_BUDGET_PX = 640;
18
+ /** The ×2 client pyramid used for lattice deployments without a tiles block. */
19
+ const DEFAULT_TILE_PYRAMID = [
20
+ 1,
21
+ 2,
22
+ 4,
23
+ 8,
24
+ 16,
25
+ 32
26
+ ];
27
+ /**
28
+ * The settings as the strategy reads them, every value filled in. The base
29
+ * budget filters an advertised deployment ladder only when the app chose a
30
+ * width other than the default 640.
31
+ */
32
+ function resolveRenderOptions(options) {
33
+ const tiles = options.tiles === false ? void 0 : options.tiles;
34
+ return {
35
+ fullPage: {
36
+ maxWidth: options.fullPage?.maxWidth ?? DEFAULT_BUDGET_PX,
37
+ maxWidthExplicit: options.fullPage?.maxWidth !== void 0 && options.fullPage.maxWidth !== DEFAULT_BUDGET_PX,
38
+ quantize: options.fullPage?.quantize ?? "exact"
39
+ },
40
+ tiles: {
41
+ enabled: options.tiles !== false,
42
+ size: tiles?.size ?? 512,
43
+ quantize: tiles?.quantize ?? "exact",
44
+ maxScale: tiles?.maxScale ?? 128,
45
+ bleedPx: tiles?.bleed ?? 1,
46
+ engageAt: tiles?.engageAt,
47
+ prefetchMargin: tiles?.prefetch?.margin ?? .5,
48
+ velocityBias: tiles?.prefetch?.velocityBias ?? true,
49
+ settleMs: tiles?.settleMs ?? 150,
50
+ fallbackPyramid: DEFAULT_TILE_PYRAMID
51
+ },
52
+ format: options.format,
53
+ quality: options.quality,
54
+ debug: options.debug ?? false
55
+ };
56
+ }
57
+ //#endregion
58
+ //#region src/token.ts
59
+ /** The package token — created once here; `contract.ts` re-exports it and
60
+ * `host-contract.ts` widens it. */
61
+ const RenderToken = (0, _embedpdf_core.createCapabilityToken)("render", {
62
+ hint: `add renderPlugin() from '@embedpdf/plugin-render' to your plugins list`,
63
+ promises: {
64
+ renderPage: true,
65
+ renderThumbnail: true,
66
+ renderPages: true
67
+ }
68
+ });
69
+ //#endregion
70
+ //#region src/contract.ts
71
+ /** What the render settings are when the app registers none. */
72
+ const RENDER_DEFAULTS = {
73
+ fullPage: {
74
+ maxWidth: 640,
75
+ quantize: "exact"
76
+ },
77
+ tiles: {
78
+ size: 512,
79
+ quantize: "exact",
80
+ maxScale: 128,
81
+ bleed: 1,
82
+ prefetch: {
83
+ margin: .5,
84
+ velocityBias: true
85
+ },
86
+ settleMs: 150
87
+ },
88
+ format: void 0,
89
+ quality: void 0,
90
+ debug: false
91
+ };
92
+ //#endregion
93
+ Object.defineProperty(exports, "EMPTY_TILE_PLAN", {
94
+ enumerable: true,
95
+ get: function() {
96
+ return EMPTY_TILE_PLAN;
97
+ }
98
+ });
99
+ Object.defineProperty(exports, "RENDER_DEFAULTS", {
100
+ enumerable: true,
101
+ get: function() {
102
+ return RENDER_DEFAULTS;
103
+ }
104
+ });
105
+ Object.defineProperty(exports, "RenderToken", {
106
+ enumerable: true,
107
+ get: function() {
108
+ return RenderToken;
109
+ }
110
+ });
111
+ Object.defineProperty(exports, "resolveRenderOptions", {
112
+ enumerable: true,
113
+ get: function() {
114
+ return resolveRenderOptions;
115
+ }
116
+ });
117
+ Object.defineProperty(exports, "samePageViewDemand", {
118
+ enumerable: true,
119
+ get: function() {
120
+ return samePageViewDemand;
121
+ }
122
+ });
123
+
124
+ //# sourceMappingURL=contract-CGcu-84H.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-CGcu-84H.cjs","names":[],"sources":["../src/paint-plan.ts","../src/token.ts","../src/contract.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 means the 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 * Whether two demands ask for the same thing. A host builds a new demand object on every camera\n * frame; a layer that compares with this sets it again (and plans again) only when it changed.\n */\nexport function samePageViewDemand(left: PageViewDemand, right: PageViewDemand): boolean {\n return (\n left.desiredDeviceWidth === right.desiredDeviceWidth &&\n left.velocity?.dx === right.velocity?.dx &&\n left.velocity?.dy === right.velocity?.dy &&\n (left.visibleRect === right.visibleRect ||\n (left.visibleRect !== undefined &&\n right.visibleRect !== undefined &&\n left.visibleRect.x === right.visibleRect.x &&\n left.visibleRect.y === right.visibleRect.y &&\n left.visibleRect.width === right.visibleRect.width &&\n left.visibleRect.height === right.visibleRect.height))\n );\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}\n\n/** The default pixel budget of a whole-page picture, in device pixels. */\nconst DEFAULT_BUDGET_PX = 640;\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 /** 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\n/**\n * The settings as the strategy reads them, every value filled in. The base\n * budget filters an advertised deployment ladder only when the app chose a\n * width other than the default 640.\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 ?? DEFAULT_BUDGET_PX,\n maxWidthExplicit:\n options.fullPage?.maxWidth !== undefined && options.fullPage.maxWidth !== DEFAULT_BUDGET_PX,\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 fallbackPyramid: DEFAULT_TILE_PYRAMID,\n },\n format: options.format,\n quality: options.quality,\n debug: options.debug ?? false,\n };\n}\n","/** The package token — created once here; `contract.ts` re-exports it and\n * `host-contract.ts` widens it. */\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { RenderCapability } from './contract';\n\nexport const RenderToken = createCapabilityToken<RenderCapability>('render', {\n hint: `add renderPlugin() from '@embedpdf/plugin-render' to your plugins list`,\n // Without a document, an adapter's stand-in rejects these with `not-ready`, as the\n // capability would; the type asks for every member that returns a promise.\n promises: {\n renderPage: true,\n renderThumbnail: true,\n renderPages: true,\n },\n});\n","/**\n * @embedpdf/plugin-render/contract — the public render vocabulary.\n *\n * Page rasters for developers: exact-size renders, thumbnails, batches, the\n * deployment's render policy, and invalidation (both directions: the\n * `invalidate` verb in, `onInvalidated` out). Everything a view layer needs\n * to paint (conformed source keys, tile plans) is the host lens,\n * `@embedpdf/plugin-render/contract/host`.\n */\nimport {\n type BatchResult,\n type DeepPartial,\n type EngineRenderPolicy,\n type EventHook,\n type EventOrigin,\n type OperationOptions,\n type PageImageHandle,\n type PageImageOptions,\n type PageRef,\n type SettingsApi,\n} from '@embedpdf/core';\nimport type { FullPageOptions, PageViewDemand, TilesOptions } from './paint-plan';\n\nexport type { FullPageOptions, PageViewDemand, TilesOptions } from './paint-plan';\nexport { samePageViewDemand } from './paint-plan';\n\n// ── settings ────────────────────────────────────────────────────────────────\n\n/** Encode format the engine can produce for a raster. */\nexport type RenderFormat = NonNullable<PageImageOptions['format']>;\n\n/**\n * The render strategy: what the viewer chooses to spend, and which render\n * points it uses when the engine permits anything. The shape mirrors the\n * deployment policy (`policy.fullPage` / `policy.tiles`), and the one\n * composition rule is: a strategy value applies as written under a\n * `continuous` policy, and the advertised lattice wins when there is one,\n * so the same settings run unchanged against local and cloud. They belong to\n * the plugin as registered: `updateSettings()` changes every document.\n */\nexport interface RenderSettings {\n /** Base plane: the pixel budget + render points. */\n readonly fullPage: FullPageOptions;\n /** Tile plane strategy; `false` disables the plane entirely. */\n readonly tiles: TilesOptions | false;\n /**\n * Encode format for both planes. Unset → engine default (png local, the\n * deployment's format on cloud). `'bmp'` is the local fast path — no\n * compression, no encoder-worker round trip; under a lattice it conforms\n * to `policy.formats`.\n */\n readonly format: RenderFormat | undefined;\n /** WebP quality from 0 (smallest) to 1 (best); PNG and BMP ignore it. */\n readonly quality: number | undefined;\n /** Diagnostic logging of tile scheduling and fetch outcomes (console `debug`). */\n readonly debug: boolean;\n}\n\n/** What the render settings are when the app registers none. */\nexport const RENDER_DEFAULTS: RenderSettings = {\n fullPage: { maxWidth: 640, quantize: 'exact' },\n tiles: {\n size: 512,\n quantize: 'exact',\n maxScale: 128,\n bleed: 1,\n prefetch: { margin: 0.5, velocityBias: true },\n settleMs: 150,\n },\n format: undefined,\n quality: undefined,\n debug: false,\n};\n\n/** What `renderPlugin(config)` takes: any of the settings, merged over the defaults. */\nexport type RenderConfig = DeepPartial<RenderSettings>;\n\n// ── the raster vocabulary ───────────────────────────────────────────────────\n\n/**\n * What a picture draws beside the page content. An option left out draws\n * what the user may read: the annotations with `doc.annotate.read`, the form\n * fields with `doc.forms.read`, and no form fields when `includeAnnotations`\n * is `false`. `true` for something the user may not read is refused\n * (`permission-denied`). Leave out what you paint yourself: a layer that\n * paints annotations passes `includeAnnotations: false`, which leaves the\n * form fields out too unless you ask for them (`includeFormFields: true`).\n */\nexport interface PageLayerOptions {\n /** Draw the annotations (form fields' widgets excepted). */\n includeAnnotations?: boolean;\n /** Draw the form fields, each in the state its field shows. */\n includeFormFields?: boolean;\n}\n\n/**\n * An encoded page image: `source` is bytes or a URL, `format`/`contentType`\n * say what they are, `width`/`height` its pixel size, and\n * `objectUrl()` mints a revocable object URL for an `<img>`.\n */\nexport type PageImage = PageImageHandle;\n\nexport interface RenderPageOptions extends OperationOptions, PageLayerOptions {\n /** Exact output width in device pixels (height keeps the page's aspect). */\n width?: number;\n /** Device pixels per PDF point; ignored when `width` is given. Default 1. */\n scale?: number;\n /** Per-call encode overrides; default to the plugin's strategy. */\n format?: RenderFormat;\n quality?: number;\n}\n\nexport interface RenderThumbnailOptions extends OperationOptions, PageLayerOptions {\n /** The thumbnail's width in device pixels. */\n maxWidth: number;\n}\n\nexport interface RenderPagesOptions extends RenderPageOptions {\n /** Renders in flight at once (default 4). */\n concurrency?: number;\n}\n\n/** One entry of a batch render. */\nexport interface PageRender {\n readonly page: PageRef;\n readonly image: PageImage;\n}\n\n/**\n * The invalidation scopes — every pixel-changing fact is one of them:\n *\n * 'annotations' — an annotation changed. Pictures without annotations keep\n * their pixels.\n * 'fields' — a form field changed: a fill, a widget's place or look,\n * a signing. Pictures without form fields keep their pixels.\n * 'content' — the page itself changed (redaction applied, flattened).\n * Invalidates every picture of the page.\n */\nexport type InvalidateScope = 'content' | 'annotations' | 'fields';\n\nexport interface InvalidateOptions {\n /** The pages whose pixels changed, by ref or index; omitted = every page. */\n pages?: readonly (PageRef | number)[];\n /** Defaults to `'content'` — a caller who doesn't say is safest repainted fully. */\n scope?: InvalidateScope;\n}\n\n// ── events ──────────────────────────────────────────────────────────────────\n\n/** Pixels changed on these pages: a confirmed document mutation (own or\n * remote) or an `invalidate` call. Anything holding a rendered bitmap refetches. */\nexport interface RenderInvalidatedEvent {\n readonly pages: readonly PageRef[];\n readonly scope: InvalidateScope;\n /**\n * Where the document mutation came from. Null when a caller requested the\n * invalidation through {@link RenderCapability.invalidate}.\n */\n readonly origin: EventOrigin | null;\n}\n\n// ── the public capability ───────────────────────────────────────────────────\n\nexport interface RenderCapability extends SettingsApi<RenderSettings> {\n /**\n * Would page rasters be served to this session (`doc.render`)? When false\n * every render refuses locally with `permission-denied` — no doomed engine\n * round-trips — and a host can show its \"no preview\" state instead.\n */\n canRender(): boolean;\n /**\n * Render one page, by its ref or its index, at the requested size. The\n * size is honoured exactly (nothing is snapped to the viewer's render\n * points); the shared raster cache serves the call when it already holds a\n * matching image. Rejects `permission-denied` without `doc.render`,\n * `not-found` for a page that is not in this document,\n * `operation-cancelled` when `signal` fires.\n */\n renderPage(page: PageRef | number, options?: RenderPageOptions): Promise<PageImage>;\n /** Sugar for a small raster at `maxWidth` device pixels. Rejects like `renderPage`. */\n renderThumbnail(page: PageRef | number, options: RenderThumbnailOptions): Promise<PageImage>;\n /**\n * Render several pages with bounded concurrency; best-effort per page: each\n * rendered page with its image, in the order given, and each page that\n * failed (as given) with why. Rejects `permission-denied` without\n * `doc.render`, `operation-cancelled` when `signal` fires.\n */\n renderPages(\n pages: readonly (PageRef | number)[],\n options?: RenderPagesOptions,\n ): Promise<BatchResult<PageRender, PageRef | number>>;\n /** The deployment's advertised render policy (a document fact). */\n getRenderPolicy(): EngineRenderPolicy;\n /**\n * The version of the picture the given options would produce. Key a\n * long-lived render on it: when it bumps, refetch. A picture versions on\n * content facts, plus annotation facts when it draws annotations and form\n * facts when it draws form fields. Bumps only on confirmed mutations —\n * never optimistically. 0 for a page that isn't in the document.\n */\n getRenderEpoch(page: PageRef | number, layers?: PageLayerOptions): 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, anything\n * third-party). Call at confirmation, never for optimistic previews.\n * Fires `onInvalidated`; a page that isn't in the document is skipped.\n */\n invalidate(options?: InvalidateOptions): void;\n /** Pages' pixels changed and they will redraw. */\n readonly onInvalidated: EventHook<RenderInvalidatedEvent>;\n}\n\nexport { RenderToken } from './token';\n\n// Re-exported for hosts that describe what a view wants (`PageContext.getViewDemand`).\nexport type { PageViewDemand as ViewDemandInput };\n"],"mappings":";;;;;;AAwBA,SAAgB,mBAAmB,MAAsB,OAAgC;CACvF,OACE,KAAK,uBAAuB,MAAM,sBAClC,KAAK,UAAU,OAAO,MAAM,UAAU,MACtC,KAAK,UAAU,OAAO,MAAM,UAAU,OACrC,KAAK,gBAAgB,MAAM,eACzB,KAAK,gBAAgB,KAAA,KACpB,MAAM,gBAAgB,KAAA,KACtB,KAAK,YAAY,MAAM,MAAM,YAAY,KACzC,KAAK,YAAY,MAAM,MAAM,YAAY,KACzC,KAAK,YAAY,UAAU,MAAM,YAAY,SAC7C,KAAK,YAAY,WAAW,MAAM,YAAY;AAEtD;AAgCA,MAAa,kBAAiC;CAC5C,SAAS;CACT,OAAO,CAAC;CACR,UAAU,CAAC;CACX,OAAO;AACT;;AAyEA,MAAM,oBAAoB;;AAG1B,MAAa,uBAA0C;CAAC;CAAG;CAAG;CAAG;CAAG;CAAI;AAAE;;;;;;AAmC1E,SAAgB,qBAAqB,SAMX;CACxB,MAAM,QAAQ,QAAQ,UAAU,QAAQ,KAAA,IAAY,QAAQ;CAC5D,OAAO;EACL,UAAU;GACR,UAAU,QAAQ,UAAU,YAAY;GACxC,kBACE,QAAQ,UAAU,aAAa,KAAA,KAAa,QAAQ,SAAS,aAAa;GAC5E,UAAU,QAAQ,UAAU,YAAY;EAC1C;EACA,OAAO;GACL,SAAS,QAAQ,UAAU;GAC3B,MAAM,OAAO,QAAQ;GACrB,UAAU,OAAO,YAAY;GAC7B,UAAU,OAAO,YAAY;GAC7B,SAAS,OAAO,SAAS;GACzB,UAAU,OAAO;GACjB,gBAAgB,OAAO,UAAU,UAAU;GAC3C,cAAc,OAAO,UAAU,gBAAgB;GAC/C,UAAU,OAAO,YAAY;GAC7B,iBAAiB;EACnB;EACA,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB,OAAO,QAAQ,SAAS;CAC1B;AACF;;;;;AClNA,MAAa,eAAA,GAAA,eAAA,sBAAA,CAAsD,UAAU;CAC3E,MAAM;CAGN,UAAU;EACR,YAAY;EACZ,iBAAiB;EACjB,aAAa;CACf;AACF,CAAC;;;;AC4CD,MAAa,kBAAkC;CAC7C,UAAU;EAAE,UAAU;EAAK,UAAU;CAAQ;CAC7C,OAAO;EACL,MAAM;EACN,UAAU;EACV,UAAU;EACV,OAAO;EACP,UAAU;GAAE,QAAQ;GAAK,cAAc;EAAK;EAC5C,UAAU;CACZ;CACA,QAAQ,KAAA;CACR,SAAS,KAAA;CACT,OAAO;AACT"}
@@ -0,0 +1,95 @@
1
+ import { createCapabilityToken } from "@embedpdf/core";
2
+ //#region src/paint-plan.ts
3
+ /**
4
+ * Whether two demands ask for the same thing. A host builds a new demand object on every camera
5
+ * frame; a layer that compares with this sets it again (and plans again) only when it changed.
6
+ */
7
+ function samePageViewDemand(left, right) {
8
+ return left.desiredDeviceWidth === right.desiredDeviceWidth && left.velocity?.dx === right.velocity?.dx && left.velocity?.dy === right.velocity?.dy && (left.visibleRect === right.visibleRect || left.visibleRect !== void 0 && right.visibleRect !== void 0 && left.visibleRect.x === right.visibleRect.x && left.visibleRect.y === right.visibleRect.y && left.visibleRect.width === right.visibleRect.width && left.visibleRect.height === right.visibleRect.height);
9
+ }
10
+ const EMPTY_TILE_PLAN = {
11
+ engaged: false,
12
+ paint: [],
13
+ fetching: [],
14
+ stamp: "empty"
15
+ };
16
+ /** The default pixel budget of a whole-page picture, in device pixels. */
17
+ const DEFAULT_BUDGET_PX = 640;
18
+ /** The ×2 client pyramid used for lattice deployments without a tiles block. */
19
+ const DEFAULT_TILE_PYRAMID = [
20
+ 1,
21
+ 2,
22
+ 4,
23
+ 8,
24
+ 16,
25
+ 32
26
+ ];
27
+ /**
28
+ * The settings as the strategy reads them, every value filled in. The base
29
+ * budget filters an advertised deployment ladder only when the app chose a
30
+ * width other than the default 640.
31
+ */
32
+ function resolveRenderOptions(options) {
33
+ const tiles = options.tiles === false ? void 0 : options.tiles;
34
+ return {
35
+ fullPage: {
36
+ maxWidth: options.fullPage?.maxWidth ?? DEFAULT_BUDGET_PX,
37
+ maxWidthExplicit: options.fullPage?.maxWidth !== void 0 && options.fullPage.maxWidth !== DEFAULT_BUDGET_PX,
38
+ quantize: options.fullPage?.quantize ?? "exact"
39
+ },
40
+ tiles: {
41
+ enabled: options.tiles !== false,
42
+ size: tiles?.size ?? 512,
43
+ quantize: tiles?.quantize ?? "exact",
44
+ maxScale: tiles?.maxScale ?? 128,
45
+ bleedPx: tiles?.bleed ?? 1,
46
+ engageAt: tiles?.engageAt,
47
+ prefetchMargin: tiles?.prefetch?.margin ?? .5,
48
+ velocityBias: tiles?.prefetch?.velocityBias ?? true,
49
+ settleMs: tiles?.settleMs ?? 150,
50
+ fallbackPyramid: DEFAULT_TILE_PYRAMID
51
+ },
52
+ format: options.format,
53
+ quality: options.quality,
54
+ debug: options.debug ?? false
55
+ };
56
+ }
57
+ //#endregion
58
+ //#region src/token.ts
59
+ /** The package token — created once here; `contract.ts` re-exports it and
60
+ * `host-contract.ts` widens it. */
61
+ const RenderToken = createCapabilityToken("render", {
62
+ hint: `add renderPlugin() from '@embedpdf/plugin-render' to your plugins list`,
63
+ promises: {
64
+ renderPage: true,
65
+ renderThumbnail: true,
66
+ renderPages: true
67
+ }
68
+ });
69
+ //#endregion
70
+ //#region src/contract.ts
71
+ /** What the render settings are when the app registers none. */
72
+ const RENDER_DEFAULTS = {
73
+ fullPage: {
74
+ maxWidth: 640,
75
+ quantize: "exact"
76
+ },
77
+ tiles: {
78
+ size: 512,
79
+ quantize: "exact",
80
+ maxScale: 128,
81
+ bleed: 1,
82
+ prefetch: {
83
+ margin: .5,
84
+ velocityBias: true
85
+ },
86
+ settleMs: 150
87
+ },
88
+ format: void 0,
89
+ quality: void 0,
90
+ debug: false
91
+ };
92
+ //#endregion
93
+ export { samePageViewDemand as a, resolveRenderOptions as i, RenderToken as n, EMPTY_TILE_PLAN as r, RENDER_DEFAULTS as t };
94
+
95
+ //# sourceMappingURL=contract-Dy5TiQDy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-Dy5TiQDy.js","names":[],"sources":["../src/paint-plan.ts","../src/token.ts","../src/contract.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 means the 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 * Whether two demands ask for the same thing. A host builds a new demand object on every camera\n * frame; a layer that compares with this sets it again (and plans again) only when it changed.\n */\nexport function samePageViewDemand(left: PageViewDemand, right: PageViewDemand): boolean {\n return (\n left.desiredDeviceWidth === right.desiredDeviceWidth &&\n left.velocity?.dx === right.velocity?.dx &&\n left.velocity?.dy === right.velocity?.dy &&\n (left.visibleRect === right.visibleRect ||\n (left.visibleRect !== undefined &&\n right.visibleRect !== undefined &&\n left.visibleRect.x === right.visibleRect.x &&\n left.visibleRect.y === right.visibleRect.y &&\n left.visibleRect.width === right.visibleRect.width &&\n left.visibleRect.height === right.visibleRect.height))\n );\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}\n\n/** The default pixel budget of a whole-page picture, in device pixels. */\nconst DEFAULT_BUDGET_PX = 640;\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 /** 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\n/**\n * The settings as the strategy reads them, every value filled in. The base\n * budget filters an advertised deployment ladder only when the app chose a\n * width other than the default 640.\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 ?? DEFAULT_BUDGET_PX,\n maxWidthExplicit:\n options.fullPage?.maxWidth !== undefined && options.fullPage.maxWidth !== DEFAULT_BUDGET_PX,\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 fallbackPyramid: DEFAULT_TILE_PYRAMID,\n },\n format: options.format,\n quality: options.quality,\n debug: options.debug ?? false,\n };\n}\n","/** The package token — created once here; `contract.ts` re-exports it and\n * `host-contract.ts` widens it. */\nimport { createCapabilityToken } from '@embedpdf/core';\n\nimport type { RenderCapability } from './contract';\n\nexport const RenderToken = createCapabilityToken<RenderCapability>('render', {\n hint: `add renderPlugin() from '@embedpdf/plugin-render' to your plugins list`,\n // Without a document, an adapter's stand-in rejects these with `not-ready`, as the\n // capability would; the type asks for every member that returns a promise.\n promises: {\n renderPage: true,\n renderThumbnail: true,\n renderPages: true,\n },\n});\n","/**\n * @embedpdf/plugin-render/contract — the public render vocabulary.\n *\n * Page rasters for developers: exact-size renders, thumbnails, batches, the\n * deployment's render policy, and invalidation (both directions: the\n * `invalidate` verb in, `onInvalidated` out). Everything a view layer needs\n * to paint (conformed source keys, tile plans) is the host lens,\n * `@embedpdf/plugin-render/contract/host`.\n */\nimport {\n type BatchResult,\n type DeepPartial,\n type EngineRenderPolicy,\n type EventHook,\n type EventOrigin,\n type OperationOptions,\n type PageImageHandle,\n type PageImageOptions,\n type PageRef,\n type SettingsApi,\n} from '@embedpdf/core';\nimport type { FullPageOptions, PageViewDemand, TilesOptions } from './paint-plan';\n\nexport type { FullPageOptions, PageViewDemand, TilesOptions } from './paint-plan';\nexport { samePageViewDemand } from './paint-plan';\n\n// ── settings ────────────────────────────────────────────────────────────────\n\n/** Encode format the engine can produce for a raster. */\nexport type RenderFormat = NonNullable<PageImageOptions['format']>;\n\n/**\n * The render strategy: what the viewer chooses to spend, and which render\n * points it uses when the engine permits anything. The shape mirrors the\n * deployment policy (`policy.fullPage` / `policy.tiles`), and the one\n * composition rule is: a strategy value applies as written under a\n * `continuous` policy, and the advertised lattice wins when there is one,\n * so the same settings run unchanged against local and cloud. They belong to\n * the plugin as registered: `updateSettings()` changes every document.\n */\nexport interface RenderSettings {\n /** Base plane: the pixel budget + render points. */\n readonly fullPage: FullPageOptions;\n /** Tile plane strategy; `false` disables the plane entirely. */\n readonly tiles: TilesOptions | false;\n /**\n * Encode format for both planes. Unset → engine default (png local, the\n * deployment's format on cloud). `'bmp'` is the local fast path — no\n * compression, no encoder-worker round trip; under a lattice it conforms\n * to `policy.formats`.\n */\n readonly format: RenderFormat | undefined;\n /** WebP quality from 0 (smallest) to 1 (best); PNG and BMP ignore it. */\n readonly quality: number | undefined;\n /** Diagnostic logging of tile scheduling and fetch outcomes (console `debug`). */\n readonly debug: boolean;\n}\n\n/** What the render settings are when the app registers none. */\nexport const RENDER_DEFAULTS: RenderSettings = {\n fullPage: { maxWidth: 640, quantize: 'exact' },\n tiles: {\n size: 512,\n quantize: 'exact',\n maxScale: 128,\n bleed: 1,\n prefetch: { margin: 0.5, velocityBias: true },\n settleMs: 150,\n },\n format: undefined,\n quality: undefined,\n debug: false,\n};\n\n/** What `renderPlugin(config)` takes: any of the settings, merged over the defaults. */\nexport type RenderConfig = DeepPartial<RenderSettings>;\n\n// ── the raster vocabulary ───────────────────────────────────────────────────\n\n/**\n * What a picture draws beside the page content. An option left out draws\n * what the user may read: the annotations with `doc.annotate.read`, the form\n * fields with `doc.forms.read`, and no form fields when `includeAnnotations`\n * is `false`. `true` for something the user may not read is refused\n * (`permission-denied`). Leave out what you paint yourself: a layer that\n * paints annotations passes `includeAnnotations: false`, which leaves the\n * form fields out too unless you ask for them (`includeFormFields: true`).\n */\nexport interface PageLayerOptions {\n /** Draw the annotations (form fields' widgets excepted). */\n includeAnnotations?: boolean;\n /** Draw the form fields, each in the state its field shows. */\n includeFormFields?: boolean;\n}\n\n/**\n * An encoded page image: `source` is bytes or a URL, `format`/`contentType`\n * say what they are, `width`/`height` its pixel size, and\n * `objectUrl()` mints a revocable object URL for an `<img>`.\n */\nexport type PageImage = PageImageHandle;\n\nexport interface RenderPageOptions extends OperationOptions, PageLayerOptions {\n /** Exact output width in device pixels (height keeps the page's aspect). */\n width?: number;\n /** Device pixels per PDF point; ignored when `width` is given. Default 1. */\n scale?: number;\n /** Per-call encode overrides; default to the plugin's strategy. */\n format?: RenderFormat;\n quality?: number;\n}\n\nexport interface RenderThumbnailOptions extends OperationOptions, PageLayerOptions {\n /** The thumbnail's width in device pixels. */\n maxWidth: number;\n}\n\nexport interface RenderPagesOptions extends RenderPageOptions {\n /** Renders in flight at once (default 4). */\n concurrency?: number;\n}\n\n/** One entry of a batch render. */\nexport interface PageRender {\n readonly page: PageRef;\n readonly image: PageImage;\n}\n\n/**\n * The invalidation scopes — every pixel-changing fact is one of them:\n *\n * 'annotations' — an annotation changed. Pictures without annotations keep\n * their pixels.\n * 'fields' — a form field changed: a fill, a widget's place or look,\n * a signing. Pictures without form fields keep their pixels.\n * 'content' — the page itself changed (redaction applied, flattened).\n * Invalidates every picture of the page.\n */\nexport type InvalidateScope = 'content' | 'annotations' | 'fields';\n\nexport interface InvalidateOptions {\n /** The pages whose pixels changed, by ref or index; omitted = every page. */\n pages?: readonly (PageRef | number)[];\n /** Defaults to `'content'` — a caller who doesn't say is safest repainted fully. */\n scope?: InvalidateScope;\n}\n\n// ── events ──────────────────────────────────────────────────────────────────\n\n/** Pixels changed on these pages: a confirmed document mutation (own or\n * remote) or an `invalidate` call. Anything holding a rendered bitmap refetches. */\nexport interface RenderInvalidatedEvent {\n readonly pages: readonly PageRef[];\n readonly scope: InvalidateScope;\n /**\n * Where the document mutation came from. Null when a caller requested the\n * invalidation through {@link RenderCapability.invalidate}.\n */\n readonly origin: EventOrigin | null;\n}\n\n// ── the public capability ───────────────────────────────────────────────────\n\nexport interface RenderCapability extends SettingsApi<RenderSettings> {\n /**\n * Would page rasters be served to this session (`doc.render`)? When false\n * every render refuses locally with `permission-denied` — no doomed engine\n * round-trips — and a host can show its \"no preview\" state instead.\n */\n canRender(): boolean;\n /**\n * Render one page, by its ref or its index, at the requested size. The\n * size is honoured exactly (nothing is snapped to the viewer's render\n * points); the shared raster cache serves the call when it already holds a\n * matching image. Rejects `permission-denied` without `doc.render`,\n * `not-found` for a page that is not in this document,\n * `operation-cancelled` when `signal` fires.\n */\n renderPage(page: PageRef | number, options?: RenderPageOptions): Promise<PageImage>;\n /** Sugar for a small raster at `maxWidth` device pixels. Rejects like `renderPage`. */\n renderThumbnail(page: PageRef | number, options: RenderThumbnailOptions): Promise<PageImage>;\n /**\n * Render several pages with bounded concurrency; best-effort per page: each\n * rendered page with its image, in the order given, and each page that\n * failed (as given) with why. Rejects `permission-denied` without\n * `doc.render`, `operation-cancelled` when `signal` fires.\n */\n renderPages(\n pages: readonly (PageRef | number)[],\n options?: RenderPagesOptions,\n ): Promise<BatchResult<PageRender, PageRef | number>>;\n /** The deployment's advertised render policy (a document fact). */\n getRenderPolicy(): EngineRenderPolicy;\n /**\n * The version of the picture the given options would produce. Key a\n * long-lived render on it: when it bumps, refetch. A picture versions on\n * content facts, plus annotation facts when it draws annotations and form\n * facts when it draws form fields. Bumps only on confirmed mutations —\n * never optimistically. 0 for a page that isn't in the document.\n */\n getRenderEpoch(page: PageRef | number, layers?: PageLayerOptions): 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, anything\n * third-party). Call at confirmation, never for optimistic previews.\n * Fires `onInvalidated`; a page that isn't in the document is skipped.\n */\n invalidate(options?: InvalidateOptions): void;\n /** Pages' pixels changed and they will redraw. */\n readonly onInvalidated: EventHook<RenderInvalidatedEvent>;\n}\n\nexport { RenderToken } from './token';\n\n// Re-exported for hosts that describe what a view wants (`PageContext.getViewDemand`).\nexport type { PageViewDemand as ViewDemandInput };\n"],"mappings":";;;;;;AAwBA,SAAgB,mBAAmB,MAAsB,OAAgC;CACvF,OACE,KAAK,uBAAuB,MAAM,sBAClC,KAAK,UAAU,OAAO,MAAM,UAAU,MACtC,KAAK,UAAU,OAAO,MAAM,UAAU,OACrC,KAAK,gBAAgB,MAAM,eACzB,KAAK,gBAAgB,KAAA,KACpB,MAAM,gBAAgB,KAAA,KACtB,KAAK,YAAY,MAAM,MAAM,YAAY,KACzC,KAAK,YAAY,MAAM,MAAM,YAAY,KACzC,KAAK,YAAY,UAAU,MAAM,YAAY,SAC7C,KAAK,YAAY,WAAW,MAAM,YAAY;AAEtD;AAgCA,MAAa,kBAAiC;CAC5C,SAAS;CACT,OAAO,CAAC;CACR,UAAU,CAAC;CACX,OAAO;AACT;;AAyEA,MAAM,oBAAoB;;AAG1B,MAAa,uBAA0C;CAAC;CAAG;CAAG;CAAG;CAAG;CAAI;AAAE;;;;;;AAmC1E,SAAgB,qBAAqB,SAMX;CACxB,MAAM,QAAQ,QAAQ,UAAU,QAAQ,KAAA,IAAY,QAAQ;CAC5D,OAAO;EACL,UAAU;GACR,UAAU,QAAQ,UAAU,YAAY;GACxC,kBACE,QAAQ,UAAU,aAAa,KAAA,KAAa,QAAQ,SAAS,aAAa;GAC5E,UAAU,QAAQ,UAAU,YAAY;EAC1C;EACA,OAAO;GACL,SAAS,QAAQ,UAAU;GAC3B,MAAM,OAAO,QAAQ;GACrB,UAAU,OAAO,YAAY;GAC7B,UAAU,OAAO,YAAY;GAC7B,SAAS,OAAO,SAAS;GACzB,UAAU,OAAO;GACjB,gBAAgB,OAAO,UAAU,UAAU;GAC3C,cAAc,OAAO,UAAU,gBAAgB;GAC/C,UAAU,OAAO,YAAY;GAC7B,iBAAiB;EACnB;EACA,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB,OAAO,QAAQ,SAAS;CAC1B;AACF;;;;;AClNA,MAAa,cAAc,sBAAwC,UAAU;CAC3E,MAAM;CAGN,UAAU;EACR,YAAY;EACZ,iBAAiB;EACjB,aAAa;CACf;AACF,CAAC;;;;AC4CD,MAAa,kBAAkC;CAC7C,UAAU;EAAE,UAAU;EAAK,UAAU;CAAQ;CAC7C,OAAO;EACL,MAAM;EACN,UAAU;EACV,UAAU;EACV,OAAO;EACP,UAAU;GAAE,QAAQ;GAAK,cAAc;EAAK;EAC5C,UAAU;CACZ;CACA,QAAQ,KAAA;CACR,SAAS,KAAA;CACT,OAAO;AACT"}