@embedpdf/react 3.0.0-next.0 → 3.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnnotationListSnapshot-DDxHpJQU-BG32YXxG.d.cts +347 -0
- package/dist/AnnotationListSnapshot-DDxHpJQU-BG32YXxG.d.ts +347 -0
- package/dist/anchored.cjs +89 -0
- package/dist/anchored.cjs.map +1 -0
- package/dist/anchored.d.cts +45 -0
- package/dist/anchored.d.ts +45 -0
- package/dist/anchored.js +60 -0
- package/dist/anchored.js.map +1 -0
- package/dist/annotation-menu.cjs +33 -84
- package/dist/annotation-menu.cjs.map +1 -1
- package/dist/annotation-menu.d.cts +28 -2
- package/dist/annotation-menu.d.ts +28 -2
- package/dist/annotation-menu.js +27 -78
- package/dist/annotation-menu.js.map +1 -1
- package/dist/annotation.cjs +752 -16
- package/dist/annotation.cjs.map +1 -0
- package/dist/annotation.d.cts +159 -2
- package/dist/annotation.d.ts +159 -2
- package/dist/annotation.js +737 -3
- package/dist/annotation.js.map +1 -0
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/form.cjs +3 -3
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.cts +2 -2
- package/dist/form.d.ts +2 -2
- package/dist/form.js +3 -3
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +34 -5
- package/dist/index.d.cts +14 -8
- package/dist/index.d.ts +14 -8
- package/dist/index.js +8 -6
- package/dist/interaction.cjs +17 -4
- package/dist/interaction.cjs.map +1 -1
- package/dist/interaction.d.cts +2 -2
- package/dist/interaction.d.ts +2 -2
- package/dist/interaction.js +7 -5
- package/dist/interaction.js.map +1 -1
- package/dist/link.cjs +29 -6
- package/dist/link.cjs.map +1 -1
- package/dist/link.d.cts +12 -3
- package/dist/link.d.ts +12 -3
- package/dist/link.js +31 -8
- package/dist/link.js.map +1 -1
- package/dist/metadata.js +1 -0
- package/dist/metadata.js.map +1 -1
- package/dist/page-edit.js +1 -0
- package/dist/page-edit.js.map +1 -1
- package/dist/page-view.cjs +62 -38
- package/dist/page-view.cjs.map +1 -1
- package/dist/page-view.js +59 -37
- package/dist/page-view.js.map +1 -1
- package/dist/redaction.js +1 -0
- package/dist/redaction.js.map +1 -1
- package/dist/render.cjs +67 -50
- package/dist/render.cjs.map +1 -1
- package/dist/render.d.cts +6 -19
- package/dist/render.d.ts +6 -19
- package/dist/render.js +71 -52
- package/dist/render.js.map +1 -1
- package/dist/rolldown-runtime-BBjsoOtd.js +27 -0
- package/dist/{runtime-DV98q7I0.d.ts → runtime-Cvi0wQ4d.d.cts} +23 -10
- package/dist/{runtime-CMxIuF2O.d.cts → runtime-DXxr8T4L.d.ts} +23 -10
- package/dist/runtime.cjs +6 -5
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +7 -5
- package/dist/runtime.js.map +1 -1
- package/dist/search.cjs +36 -19
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +36 -19
- package/dist/search.js.map +1 -1
- package/dist/selection.cjs +217 -22
- package/dist/selection.cjs.map +1 -1
- package/dist/selection.d.cts +60 -2
- package/dist/selection.d.ts +60 -2
- package/dist/selection.js +214 -27
- package/dist/selection.js.map +1 -1
- package/dist/shell.js +1 -0
- package/dist/shell.js.map +1 -1
- package/dist/stage.cjs +47 -146
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +18 -5
- package/dist/stage.d.ts +18 -5
- package/dist/stage.js +50 -148
- package/dist/stage.js.map +1 -1
- package/dist/stamp.js +1 -0
- package/dist/stamp.js.map +1 -1
- package/dist/toolbar.js +1 -0
- package/dist/toolbar.js.map +1 -1
- package/dist/views.js +1 -0
- package/dist/views.js.map +1 -1
- package/package.json +27 -26
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +0 -2593
- package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +0 -2593
- package/dist/annotation-B6jwfTCh.d.cts +0 -173
- package/dist/annotation-Ct6dFtzJ.cjs +0 -913
- package/dist/annotation-Ct6dFtzJ.cjs.map +0 -1
- package/dist/annotation-DjGWJljY.js +0 -829
- package/dist/annotation-DjGWJljY.js.map +0 -1
- package/dist/annotation-nUMefyjw.d.ts +0 -173
package/dist/render.cjs
CHANGED
|
@@ -4,11 +4,14 @@ const require_runtime = require("./runtime.cjs");
|
|
|
4
4
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
5
|
let react = require("react");
|
|
6
6
|
react = require_rolldown_runtime.__toESM(react, 1);
|
|
7
|
+
let _embedpdf_web = require("@embedpdf/web");
|
|
7
8
|
let _embedpdf_plugin_render = require("@embedpdf/plugin-render");
|
|
8
9
|
//#region src/render.tsx
|
|
9
|
-
|
|
10
|
+
let warnedTileSize = false;
|
|
11
|
+
function RenderLayer({ annotations = true, tiles = true } = {}) {
|
|
10
12
|
const page = require_runtime.usePage();
|
|
11
13
|
const render = require_runtime.useCapability(_embedpdf_plugin_render.RenderToken);
|
|
14
|
+
const settings = render.paintSettings();
|
|
12
15
|
const ref = (0, react.useRef)(null);
|
|
13
16
|
const sourceKey = require_runtime.useSelector(_embedpdf_plugin_render.RenderToken, (c) => c.renderSourceKey(page.pon, {
|
|
14
17
|
scale: page.transform.renderScale,
|
|
@@ -41,7 +44,7 @@ function RenderLayer({ annotations = true } = {}) {
|
|
|
41
44
|
page.pon,
|
|
42
45
|
sourceKey
|
|
43
46
|
]);
|
|
44
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
47
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
45
48
|
ref,
|
|
46
49
|
alt: "",
|
|
47
50
|
draggable: false,
|
|
@@ -52,84 +55,98 @@ function RenderLayer({ annotations = true } = {}) {
|
|
|
52
55
|
height: "100%",
|
|
53
56
|
pointerEvents: "none"
|
|
54
57
|
}
|
|
55
|
-
})
|
|
58
|
+
}), tiles && settings.tiles ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TilePlane, {
|
|
59
|
+
annotations,
|
|
60
|
+
fadeMs: settings.fadeMs
|
|
61
|
+
}) : null] });
|
|
56
62
|
}
|
|
57
63
|
/**
|
|
58
|
-
*
|
|
64
|
+
* The sharp plane above the base — the plugin's retention-safe paint plan
|
|
65
|
+
* as keyed <img>s. Each tile reports painted after its first presentation
|
|
66
|
+
* opportunity (and its inverse on unmount), so retained coarser generations
|
|
67
|
+
* release only when their replacement is truly compositable.
|
|
59
68
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
69
|
+
* Tiles are placed in VIEW (CSS px) space directly — never in page points
|
|
70
|
+
* under a scaled container. Blink quantizes layout lengths to 1/64 CSS px
|
|
71
|
+
* BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform
|
|
72
|
+
* turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible
|
|
73
|
+
* seams and per-zoom-step letter shifts that grow with depth. In view
|
|
74
|
+
* space the same quantization is a fixed ~1/64 CSS px at every zoom.
|
|
75
|
+
* (v2 placed tiles in screen px for exactly this reason.)
|
|
66
76
|
*/
|
|
67
|
-
function
|
|
77
|
+
function TilePlane({ annotations, fadeMs }) {
|
|
68
78
|
const page = require_runtime.usePage();
|
|
69
79
|
const render = require_runtime.useCapability(_embedpdf_plugin_render.RenderToken);
|
|
70
80
|
const demand = page.getViewDemand?.() ?? { desiredDeviceWidth: page.transform.deviceWidth };
|
|
71
|
-
const
|
|
72
|
-
|
|
81
|
+
const tiles = render.tilesFor(page.view);
|
|
82
|
+
const plan = require_runtime.useSelector(_embedpdf_plugin_render.RenderToken, () => tiles.plan(page.pon, demand, { includeAnnotations: annotations }));
|
|
83
|
+
(0, react.useEffect)(() => () => tiles.release(page.pon), [tiles, page.pon]);
|
|
73
84
|
if (plan.paint.length === 0) return null;
|
|
74
85
|
const t = page.transform;
|
|
75
|
-
const
|
|
76
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
86
|
+
const s = t.viewScale;
|
|
87
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
77
88
|
style: {
|
|
78
89
|
position: "absolute",
|
|
79
90
|
left: 0,
|
|
80
91
|
top: 0,
|
|
81
|
-
width: t.contentWidth
|
|
82
|
-
height: t.contentHeight
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
pointerEvents: "none"
|
|
92
|
+
width: t.contentWidth,
|
|
93
|
+
height: t.contentHeight,
|
|
94
|
+
pointerEvents: "none",
|
|
95
|
+
isolation: "isolate"
|
|
86
96
|
},
|
|
87
|
-
children: plan.paint.map((source) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TileImg, {
|
|
97
|
+
children: [fadeMs > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }` }) : null, plan.paint.map((source) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TileImg, {
|
|
88
98
|
source,
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
view: {
|
|
100
|
+
x: source.rect.x * s,
|
|
101
|
+
y: source.rect.y * s,
|
|
102
|
+
width: source.rect.width * s,
|
|
103
|
+
height: source.rect.height * s
|
|
104
|
+
},
|
|
105
|
+
fadeMs,
|
|
106
|
+
onPainted: () => tiles.painted(page.pon, source.key),
|
|
107
|
+
onUnpainted: () => tiles.unpainted(page.pon, source.key)
|
|
108
|
+
}, source.key))]
|
|
91
109
|
});
|
|
92
110
|
}
|
|
93
|
-
/** One tile:
|
|
94
|
-
|
|
111
|
+
/** One tile: geometry plus the shared browser image binding. The binding
|
|
112
|
+
* hides the incomplete image, reports painted after its first presentation
|
|
113
|
+
* opportunity, and inverts that report when the element leaves the DOM. */
|
|
114
|
+
function TileImg({ source, view, fadeMs, onPainted, onUnpainted }) {
|
|
95
115
|
const ref = (0, react.useRef)(null);
|
|
96
|
-
(0, react.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (controller.signal.aborted) {
|
|
103
|
-
obj.revoke();
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
revoke = obj.revoke;
|
|
107
|
-
if (ref.current) ref.current.src = obj.url;
|
|
108
|
-
} catch {}
|
|
109
|
-
})();
|
|
110
|
-
return () => {
|
|
111
|
-
controller.abort();
|
|
112
|
-
revoke?.();
|
|
113
|
-
};
|
|
116
|
+
(0, react.useLayoutEffect)(() => {
|
|
117
|
+
if (!ref.current) return;
|
|
118
|
+
return (0, _embedpdf_web.bindPaintedImage)(ref.current, source.handle, {
|
|
119
|
+
onPainted,
|
|
120
|
+
onUnpainted
|
|
121
|
+
});
|
|
114
122
|
}, [source.handle]);
|
|
115
123
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
116
124
|
ref,
|
|
117
125
|
alt: "",
|
|
118
126
|
draggable: false,
|
|
119
|
-
onLoad:
|
|
127
|
+
onLoad: (e) => {
|
|
128
|
+
const w = e.currentTarget.naturalWidth;
|
|
129
|
+
if (w > 0 && !warnedTileSize) {
|
|
130
|
+
const expected = Math.round(source.rect.width * source.scale);
|
|
131
|
+
if (Math.abs(w - expected) > 1) {
|
|
132
|
+
warnedTileSize = true;
|
|
133
|
+
console.warn(`[render] tile bitmap ${w}px wide does not match its rect (expected ~${expected}px) — stale raster identity? key=${source.key}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
120
137
|
style: {
|
|
121
138
|
position: "absolute",
|
|
122
|
-
left:
|
|
123
|
-
top:
|
|
124
|
-
width:
|
|
125
|
-
height:
|
|
126
|
-
zIndex: source.z
|
|
139
|
+
left: view.x,
|
|
140
|
+
top: view.y,
|
|
141
|
+
width: view.width,
|
|
142
|
+
height: view.height,
|
|
143
|
+
zIndex: source.z,
|
|
144
|
+
...fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null
|
|
127
145
|
}
|
|
128
146
|
});
|
|
129
147
|
}
|
|
130
148
|
//#endregion
|
|
131
149
|
exports.RenderLayer = RenderLayer;
|
|
132
|
-
exports.TileLayer = TileLayer;
|
|
133
150
|
Object.keys(_embedpdf_plugin_render).forEach(function(k) {
|
|
134
151
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
135
152
|
enumerable: true,
|
package/dist/render.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.cjs","names":["usePage","useCapability","RenderToken","useSelector"],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer + TileLayer — the React views of @embedpdf/plugin-render.\n *\n * RenderLayer paints the BASE rung to an <img> from the engine's ENCODED\n * image() (identical for local & cloud). TileLayer paints the tile paint-plan\n * ABOVE it when the deployment lattice caps the base below the demand — mount\n * it per lens (the main stage yes, a thumbnail rail no); where the demand is\n * small it engages nothing. Both are dumb painters: every decision (policy\n * conformance, want sets, retention, release) is plugin-render's.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n}\n\nexport function RenderLayer({ annotations = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const ref = useRef<HTMLImageElement>(null);\n // ONE dependency: the raster's canonical identity — conformed viewport +\n // annotations flag + epoch. Inside a lattice\n // rung, zoom changes don't move it: no refetch, no DOM churn — the stage's\n // CSS transform does the scaling. It changes exactly at rung crossings and\n // on CONFIRMED mutations (epoch bumps at commit, never mid-gesture). Under\n // `continuous` it embeds the exact scale — v2 behavior byte-for-byte. The\n // policy behind it is a document fact the kernel materialized before\n // publish, so the key is always computable.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the policy (lattice → ladder width,\n // continuous → this exact scale) and collapses same-key asks in its\n // raster store. A stale-closure scale is harmless by construction:\n // any scale in this rung produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n );\n}\n\nexport interface TileLayerProps {\n /**\n * Bake annotations into tile bitmaps (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering — the composition that keeps\n * tile artifacts annotation-version-free (CDN-stable across edits).\n */\n annotations?: boolean;\n}\n\n/**\n * TileLayer — the sharp plane above the base rung.\n *\n * Mounting it is the per-lens OPT-IN; whether it spends anything is demand\n * arithmetic (a thumbnail rail's demand never engages). The paint list is the\n * plugin's retention-safe plan: keyed <img>s inside ONE container that carries\n * the page scale as a single transform (one rounding context — no tile seams),\n * with `onLoad` reporting the decode boundary back so retained coarser\n * generations release only when their replacement is truly on screen.\n */\nexport function TileLayer({ annotations = true }: TileLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n const plan = useSelector(RenderToken, (c) =>\n c.tilePlan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // Lens unmounted: stop in-flight tile fetches; resolved bytes stay cached.\n useEffect(() => () => render.releaseTiles(page.pon), [render, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const scale = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth / scale,\n height: t.contentHeight / scale,\n transform: `scale(${scale})`,\n transformOrigin: '0 0',\n pointerEvents: 'none',\n }}\n >\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n onPainted={() => render.tilePainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: object-URL lifecycle + the painted (decode-boundary) report. */\nfunction TileImg({ source, onPainted }: { source: TilePaintSource; onPainted: () => void }) {\n const ref = useRef<HTMLImageElement>(null);\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n const obj = await source.handle.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (plan moved on) */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={onPainted}\n style={{\n position: 'absolute',\n left: source.rect.x,\n top: source.rect.y,\n width: source.rect.width,\n height: source.rect.height,\n zIndex: source.z,\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render.cjs","names":["usePage","useCapability","RenderToken","useSelector"],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer — the React view of @embedpdf/plugin-render. The page raster\n * is ONE concern with two planes:\n *\n * - the BASE plane: a whole-page <img> at the plugin's resolved render\n * points — the exact settled demand capped at the pixel budget on a\n * continuous (local) engine, the advertised ladder on a lattice (cloud)\n * deployment. Always present; the instant backdrop.\n * - the TILE plane: the plugin's retention-safe paint plan above it,\n * engaging by demand arithmetic when the view wants more pixels than\n * the base may spend. A thumbnail-sized demand engages nothing — the\n * arithmetic is the configuration.\n *\n * The layer is a DUMB painter, deliberately thin: plain <img>s bound through\n * the shared browser adapter, with painted/unpainted reports around their\n * visible lifetime. Every decision — strategy ∧ policy conformance, want\n * sets, level settling, retention, release — is plugin-render's; anything\n * mid-gesture simply CSS-scales until the plugin hands down new pixels.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { bindPaintedImage } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n /**\n * Mount the tile plane (default true). Whether it SPENDS anything is\n * demand arithmetic — leave it on; pass false only for a lens that must\n * never tile even under deep zoom.\n */\n tiles?: boolean;\n}\n\nlet warnedTileSize = false;\n\nexport function RenderLayer({ annotations = true, tiles = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const settings = render.paintSettings();\n const ref = useRef<HTMLImageElement>(null);\n\n // ONE dependency: the raster's canonical identity — conformed width +\n // annotations flag + epoch. Under a lattice it moves only at rung\n // crossings; under exact mode it tracks the demand and is CONSTANT above\n // the budget — so the deep-zoom backdrop never refetches, and the sub-\n // budget range refetches per settled demand exactly like v2 did.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the resolved render points and\n // collapses same-key asks in its raster store. A stale-closure scale\n // is harmless by construction: any scale mapping to this key\n // produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n // Imperative src on a STABLE element: the browser keeps the old\n // bitmap until the new one decodes, and nothing ever re-requests the\n // old URL — so revoke-on-cleanup is safe here by construction.\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n\n return (\n <>\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n {tiles && settings.tiles ? (\n <TilePlane annotations={annotations} fadeMs={settings.fadeMs} />\n ) : null}\n </>\n );\n}\n\n/**\n * The sharp plane above the base — the plugin's retention-safe paint plan\n * as keyed <img>s. Each tile reports painted after its first presentation\n * opportunity (and its inverse on unmount), so retained coarser generations\n * release only when their replacement is truly compositable.\n *\n * Tiles are placed in VIEW (CSS px) space directly — never in page points\n * under a scaled container. Blink quantizes layout lengths to 1/64 CSS px\n * BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform\n * turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible\n * seams and per-zoom-step letter shifts that grow with depth. In view\n * space the same quantization is a fixed ~1/64 CSS px at every zoom.\n * (v2 placed tiles in screen px for exactly this reason.)\n */\nfunction TilePlane({ annotations, fadeMs }: { annotations: boolean; fadeMs: number }) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n // Level settling lives in the plugin (its settle gate), pan stays live.\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n // THIS view's tile surface: state is per view × page, so a thumbnail\n // rail's never-engaging demand cannot disturb the main lens's plan (shared\n // state made the main view lose its tiles whenever a rail opened). The\n // handle is reference-stable per view — a clean dependency.\n const tiles = render.tilesFor(page.view);\n const plan = useSelector(RenderToken, () =>\n tiles.plan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // View unmounted its plane: stop in-flight fetches; resolved bytes stay cached.\n useEffect(() => () => tiles.release(page.pon), [tiles, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const s = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth,\n height: t.contentHeight,\n pointerEvents: 'none',\n // STACKING CONTRACT: tile <img>s carry zIndex ranks (coarse under\n // fine) that exist only while generations are mixed — mid-zoom. This\n // container MUST be a stacking context so those ranks stay internal;\n // without it they join the Stage's context and paint above every\n // z-auto sibling (annotations, page chrome, menus) exactly while\n // zooming. The old scaled container created one implicitly via its\n // transform; `isolation` is the explicit, no-side-effect way.\n isolation: 'isolate',\n }}\n >\n {fadeMs > 0 ? (\n <style>{`@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }`}</style>\n ) : null}\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n view={{\n x: source.rect.x * s,\n y: source.rect.y * s,\n width: source.rect.width * s,\n height: source.rect.height * s,\n }}\n fadeMs={fadeMs}\n onPainted={() => tiles.painted(page.pon, source.key)}\n onUnpainted={() => tiles.unpainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: geometry plus the shared browser image binding. The binding\n * hides the incomplete image, reports painted after its first presentation\n * opportunity, and inverts that report when the element leaves the DOM. */\nfunction TileImg({\n source,\n view,\n fadeMs,\n onPainted,\n onUnpainted,\n}: {\n source: TilePaintSource;\n /** Placement rect in VIEW (CSS px) space. */\n view: { x: number; y: number; width: number; height: number };\n fadeMs: number;\n onPainted: () => void;\n onUnpainted: () => void;\n}) {\n const ref = useRef<HTMLImageElement>(null);\n useLayoutEffect(() => {\n if (!ref.current) return;\n return bindPaintedImage(ref.current, source.handle, { onPainted, onUnpainted });\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the handle is\n // the bitmap's identity; the report callbacks carry stable values.\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={(e) => {\n // A raster whose intrinsic size disagrees with its rect would be\n // silently stretched into place — the stale-handle bug class.\n const w = e.currentTarget.naturalWidth;\n if (w > 0 && !warnedTileSize) {\n const expected = Math.round(source.rect.width * source.scale);\n if (Math.abs(w - expected) > 1) {\n warnedTileSize = true;\n console.warn(\n `[render] tile bitmap ${w}px wide does not match its rect ` +\n `(expected ~${expected}px) — stale raster identity? key=${source.key}`,\n );\n }\n }\n }}\n style={{\n position: 'absolute',\n left: view.x,\n top: view.y,\n width: view.width,\n height: view.height,\n zIndex: source.z,\n ...(fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null),\n }}\n />\n );\n}\n"],"mappings":"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"}
|
package/dist/render.d.cts
CHANGED
|
@@ -6,27 +6,14 @@ interface RenderLayerProps {
|
|
|
6
6
|
* <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.
|
|
7
7
|
*/
|
|
8
8
|
annotations?: boolean;
|
|
9
|
-
}
|
|
10
|
-
declare function RenderLayer({ annotations }?: RenderLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
interface TileLayerProps {
|
|
12
9
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* tile
|
|
10
|
+
* Mount the tile plane (default true). Whether it SPENDS anything is
|
|
11
|
+
* demand arithmetic — leave it on; pass false only for a lens that must
|
|
12
|
+
* never tile even under deep zoom.
|
|
16
13
|
*/
|
|
17
|
-
|
|
14
|
+
tiles?: boolean;
|
|
18
15
|
}
|
|
19
|
-
|
|
20
|
-
* TileLayer — the sharp plane above the base rung.
|
|
21
|
-
*
|
|
22
|
-
* Mounting it is the per-lens OPT-IN; whether it spends anything is demand
|
|
23
|
-
* arithmetic (a thumbnail rail's demand never engages). The paint list is the
|
|
24
|
-
* plugin's retention-safe plan: keyed <img>s inside ONE container that carries
|
|
25
|
-
* the page scale as a single transform (one rounding context — no tile seams),
|
|
26
|
-
* with `onLoad` reporting the decode boundary back so retained coarser
|
|
27
|
-
* generations release only when their replacement is truly on screen.
|
|
28
|
-
*/
|
|
29
|
-
declare function TileLayer({ annotations }?: TileLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
16
|
+
declare function RenderLayer({ annotations, tiles }?: RenderLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
30
17
|
//#endregion
|
|
31
|
-
export { RenderLayer, RenderLayerProps
|
|
18
|
+
export { RenderLayer, RenderLayerProps };
|
|
32
19
|
//# sourceMappingURL=render.d.cts.map
|
package/dist/render.d.ts
CHANGED
|
@@ -6,27 +6,14 @@ interface RenderLayerProps {
|
|
|
6
6
|
* <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.
|
|
7
7
|
*/
|
|
8
8
|
annotations?: boolean;
|
|
9
|
-
}
|
|
10
|
-
declare function RenderLayer({ annotations }?: RenderLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
interface TileLayerProps {
|
|
12
9
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* tile
|
|
10
|
+
* Mount the tile plane (default true). Whether it SPENDS anything is
|
|
11
|
+
* demand arithmetic — leave it on; pass false only for a lens that must
|
|
12
|
+
* never tile even under deep zoom.
|
|
16
13
|
*/
|
|
17
|
-
|
|
14
|
+
tiles?: boolean;
|
|
18
15
|
}
|
|
19
|
-
|
|
20
|
-
* TileLayer — the sharp plane above the base rung.
|
|
21
|
-
*
|
|
22
|
-
* Mounting it is the per-lens OPT-IN; whether it spends anything is demand
|
|
23
|
-
* arithmetic (a thumbnail rail's demand never engages). The paint list is the
|
|
24
|
-
* plugin's retention-safe plan: keyed <img>s inside ONE container that carries
|
|
25
|
-
* the page scale as a single transform (one rounding context — no tile seams),
|
|
26
|
-
* with `onLoad` reporting the decode boundary back so retained coarser
|
|
27
|
-
* generations release only when their replacement is truly on screen.
|
|
28
|
-
*/
|
|
29
|
-
declare function TileLayer({ annotations }?: TileLayerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
16
|
+
declare function RenderLayer({ annotations, tiles }?: RenderLayerProps): import("react/jsx-runtime").JSX.Element;
|
|
30
17
|
//#endregion
|
|
31
|
-
export { RenderLayer, RenderLayerProps
|
|
18
|
+
export { RenderLayer, RenderLayerProps };
|
|
32
19
|
//# sourceMappingURL=render.d.ts.map
|
package/dist/render.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import "./rolldown-runtime-BBjsoOtd.js";
|
|
1
2
|
import { useCapability, usePage, useSelector } from "./runtime.js";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
5
|
+
import { bindPaintedImage } from "@embedpdf/web";
|
|
4
6
|
import { RenderToken } from "@embedpdf/plugin-render";
|
|
5
7
|
export * from "@embedpdf/plugin-render";
|
|
6
8
|
//#region src/render.tsx
|
|
7
|
-
|
|
9
|
+
let warnedTileSize = false;
|
|
10
|
+
function RenderLayer({ annotations = true, tiles = true } = {}) {
|
|
8
11
|
const page = usePage();
|
|
9
12
|
const render = useCapability(RenderToken);
|
|
13
|
+
const settings = render.paintSettings();
|
|
10
14
|
const ref = useRef(null);
|
|
11
15
|
const sourceKey = useSelector(RenderToken, (c) => c.renderSourceKey(page.pon, {
|
|
12
16
|
scale: page.transform.renderScale,
|
|
@@ -39,7 +43,7 @@ function RenderLayer({ annotations = true } = {}) {
|
|
|
39
43
|
page.pon,
|
|
40
44
|
sourceKey
|
|
41
45
|
]);
|
|
42
|
-
return /* @__PURE__ */ jsx("img", {
|
|
46
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("img", {
|
|
43
47
|
ref,
|
|
44
48
|
alt: "",
|
|
45
49
|
draggable: false,
|
|
@@ -50,82 +54,97 @@ function RenderLayer({ annotations = true } = {}) {
|
|
|
50
54
|
height: "100%",
|
|
51
55
|
pointerEvents: "none"
|
|
52
56
|
}
|
|
53
|
-
})
|
|
57
|
+
}), tiles && settings.tiles ? /* @__PURE__ */ jsx(TilePlane, {
|
|
58
|
+
annotations,
|
|
59
|
+
fadeMs: settings.fadeMs
|
|
60
|
+
}) : null] });
|
|
54
61
|
}
|
|
55
62
|
/**
|
|
56
|
-
*
|
|
63
|
+
* The sharp plane above the base — the plugin's retention-safe paint plan
|
|
64
|
+
* as keyed <img>s. Each tile reports painted after its first presentation
|
|
65
|
+
* opportunity (and its inverse on unmount), so retained coarser generations
|
|
66
|
+
* release only when their replacement is truly compositable.
|
|
57
67
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
68
|
+
* Tiles are placed in VIEW (CSS px) space directly — never in page points
|
|
69
|
+
* under a scaled container. Blink quantizes layout lengths to 1/64 CSS px
|
|
70
|
+
* BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform
|
|
71
|
+
* turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible
|
|
72
|
+
* seams and per-zoom-step letter shifts that grow with depth. In view
|
|
73
|
+
* space the same quantization is a fixed ~1/64 CSS px at every zoom.
|
|
74
|
+
* (v2 placed tiles in screen px for exactly this reason.)
|
|
64
75
|
*/
|
|
65
|
-
function
|
|
76
|
+
function TilePlane({ annotations, fadeMs }) {
|
|
66
77
|
const page = usePage();
|
|
67
78
|
const render = useCapability(RenderToken);
|
|
68
79
|
const demand = page.getViewDemand?.() ?? { desiredDeviceWidth: page.transform.deviceWidth };
|
|
69
|
-
const
|
|
70
|
-
|
|
80
|
+
const tiles = render.tilesFor(page.view);
|
|
81
|
+
const plan = useSelector(RenderToken, () => tiles.plan(page.pon, demand, { includeAnnotations: annotations }));
|
|
82
|
+
useEffect(() => () => tiles.release(page.pon), [tiles, page.pon]);
|
|
71
83
|
if (plan.paint.length === 0) return null;
|
|
72
84
|
const t = page.transform;
|
|
73
|
-
const
|
|
74
|
-
return /* @__PURE__ */
|
|
85
|
+
const s = t.viewScale;
|
|
86
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
75
87
|
style: {
|
|
76
88
|
position: "absolute",
|
|
77
89
|
left: 0,
|
|
78
90
|
top: 0,
|
|
79
|
-
width: t.contentWidth
|
|
80
|
-
height: t.contentHeight
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
pointerEvents: "none"
|
|
91
|
+
width: t.contentWidth,
|
|
92
|
+
height: t.contentHeight,
|
|
93
|
+
pointerEvents: "none",
|
|
94
|
+
isolation: "isolate"
|
|
84
95
|
},
|
|
85
|
-
children: plan.paint.map((source) => /* @__PURE__ */ jsx(TileImg, {
|
|
96
|
+
children: [fadeMs > 0 ? /* @__PURE__ */ jsx("style", { children: `@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }` }) : null, plan.paint.map((source) => /* @__PURE__ */ jsx(TileImg, {
|
|
86
97
|
source,
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
view: {
|
|
99
|
+
x: source.rect.x * s,
|
|
100
|
+
y: source.rect.y * s,
|
|
101
|
+
width: source.rect.width * s,
|
|
102
|
+
height: source.rect.height * s
|
|
103
|
+
},
|
|
104
|
+
fadeMs,
|
|
105
|
+
onPainted: () => tiles.painted(page.pon, source.key),
|
|
106
|
+
onUnpainted: () => tiles.unpainted(page.pon, source.key)
|
|
107
|
+
}, source.key))]
|
|
89
108
|
});
|
|
90
109
|
}
|
|
91
|
-
/** One tile:
|
|
92
|
-
|
|
110
|
+
/** One tile: geometry plus the shared browser image binding. The binding
|
|
111
|
+
* hides the incomplete image, reports painted after its first presentation
|
|
112
|
+
* opportunity, and inverts that report when the element leaves the DOM. */
|
|
113
|
+
function TileImg({ source, view, fadeMs, onPainted, onUnpainted }) {
|
|
93
114
|
const ref = useRef(null);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (controller.signal.aborted) {
|
|
101
|
-
obj.revoke();
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
revoke = obj.revoke;
|
|
105
|
-
if (ref.current) ref.current.src = obj.url;
|
|
106
|
-
} catch {}
|
|
107
|
-
})();
|
|
108
|
-
return () => {
|
|
109
|
-
controller.abort();
|
|
110
|
-
revoke?.();
|
|
111
|
-
};
|
|
115
|
+
useLayoutEffect(() => {
|
|
116
|
+
if (!ref.current) return;
|
|
117
|
+
return bindPaintedImage(ref.current, source.handle, {
|
|
118
|
+
onPainted,
|
|
119
|
+
onUnpainted
|
|
120
|
+
});
|
|
112
121
|
}, [source.handle]);
|
|
113
122
|
return /* @__PURE__ */ jsx("img", {
|
|
114
123
|
ref,
|
|
115
124
|
alt: "",
|
|
116
125
|
draggable: false,
|
|
117
|
-
onLoad:
|
|
126
|
+
onLoad: (e) => {
|
|
127
|
+
const w = e.currentTarget.naturalWidth;
|
|
128
|
+
if (w > 0 && !warnedTileSize) {
|
|
129
|
+
const expected = Math.round(source.rect.width * source.scale);
|
|
130
|
+
if (Math.abs(w - expected) > 1) {
|
|
131
|
+
warnedTileSize = true;
|
|
132
|
+
console.warn(`[render] tile bitmap ${w}px wide does not match its rect (expected ~${expected}px) — stale raster identity? key=${source.key}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
118
136
|
style: {
|
|
119
137
|
position: "absolute",
|
|
120
|
-
left:
|
|
121
|
-
top:
|
|
122
|
-
width:
|
|
123
|
-
height:
|
|
124
|
-
zIndex: source.z
|
|
138
|
+
left: view.x,
|
|
139
|
+
top: view.y,
|
|
140
|
+
width: view.width,
|
|
141
|
+
height: view.height,
|
|
142
|
+
zIndex: source.z,
|
|
143
|
+
...fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null
|
|
125
144
|
}
|
|
126
145
|
});
|
|
127
146
|
}
|
|
128
147
|
//#endregion
|
|
129
|
-
export { RenderLayer
|
|
148
|
+
export { RenderLayer };
|
|
130
149
|
|
|
131
150
|
//# sourceMappingURL=render.js.map
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","names":[],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer + TileLayer — the React views of @embedpdf/plugin-render.\n *\n * RenderLayer paints the BASE rung to an <img> from the engine's ENCODED\n * image() (identical for local & cloud). TileLayer paints the tile paint-plan\n * ABOVE it when the deployment lattice caps the base below the demand — mount\n * it per lens (the main stage yes, a thumbnail rail no); where the demand is\n * small it engages nothing. Both are dumb painters: every decision (policy\n * conformance, want sets, retention, release) is plugin-render's.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n}\n\nexport function RenderLayer({ annotations = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const ref = useRef<HTMLImageElement>(null);\n // ONE dependency: the raster's canonical identity — conformed viewport +\n // annotations flag + epoch. Inside a lattice\n // rung, zoom changes don't move it: no refetch, no DOM churn — the stage's\n // CSS transform does the scaling. It changes exactly at rung crossings and\n // on CONFIRMED mutations (epoch bumps at commit, never mid-gesture). Under\n // `continuous` it embeds the exact scale — v2 behavior byte-for-byte. The\n // policy behind it is a document fact the kernel materialized before\n // publish, so the key is always computable.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the policy (lattice → ladder width,\n // continuous → this exact scale) and collapses same-key asks in its\n // raster store. A stale-closure scale is harmless by construction:\n // any scale in this rung produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n );\n}\n\nexport interface TileLayerProps {\n /**\n * Bake annotations into tile bitmaps (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering — the composition that keeps\n * tile artifacts annotation-version-free (CDN-stable across edits).\n */\n annotations?: boolean;\n}\n\n/**\n * TileLayer — the sharp plane above the base rung.\n *\n * Mounting it is the per-lens OPT-IN; whether it spends anything is demand\n * arithmetic (a thumbnail rail's demand never engages). The paint list is the\n * plugin's retention-safe plan: keyed <img>s inside ONE container that carries\n * the page scale as a single transform (one rounding context — no tile seams),\n * with `onLoad` reporting the decode boundary back so retained coarser\n * generations release only when their replacement is truly on screen.\n */\nexport function TileLayer({ annotations = true }: TileLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n const plan = useSelector(RenderToken, (c) =>\n c.tilePlan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // Lens unmounted: stop in-flight tile fetches; resolved bytes stay cached.\n useEffect(() => () => render.releaseTiles(page.pon), [render, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const scale = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth / scale,\n height: t.contentHeight / scale,\n transform: `scale(${scale})`,\n transformOrigin: '0 0',\n pointerEvents: 'none',\n }}\n >\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n onPainted={() => render.tilePainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: object-URL lifecycle + the painted (decode-boundary) report. */\nfunction TileImg({ source, onPainted }: { source: TilePaintSource; onPainted: () => void }) {\n const ref = useRef<HTMLImageElement>(null);\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n const obj = await source.handle.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (plan moved on) */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={onPainted}\n style={{\n position: 'absolute',\n left: source.rect.x,\n top: source.rect.y,\n width: source.rect.width,\n height: source.rect.height,\n zIndex: source.z,\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render.js","names":[],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer — the React view of @embedpdf/plugin-render. The page raster\n * is ONE concern with two planes:\n *\n * - the BASE plane: a whole-page <img> at the plugin's resolved render\n * points — the exact settled demand capped at the pixel budget on a\n * continuous (local) engine, the advertised ladder on a lattice (cloud)\n * deployment. Always present; the instant backdrop.\n * - the TILE plane: the plugin's retention-safe paint plan above it,\n * engaging by demand arithmetic when the view wants more pixels than\n * the base may spend. A thumbnail-sized demand engages nothing — the\n * arithmetic is the configuration.\n *\n * The layer is a DUMB painter, deliberately thin: plain <img>s bound through\n * the shared browser adapter, with painted/unpainted reports around their\n * visible lifetime. Every decision — strategy ∧ policy conformance, want\n * sets, level settling, retention, release — is plugin-render's; anything\n * mid-gesture simply CSS-scales until the plugin hands down new pixels.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { bindPaintedImage } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n /**\n * Mount the tile plane (default true). Whether it SPENDS anything is\n * demand arithmetic — leave it on; pass false only for a lens that must\n * never tile even under deep zoom.\n */\n tiles?: boolean;\n}\n\nlet warnedTileSize = false;\n\nexport function RenderLayer({ annotations = true, tiles = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const settings = render.paintSettings();\n const ref = useRef<HTMLImageElement>(null);\n\n // ONE dependency: the raster's canonical identity — conformed width +\n // annotations flag + epoch. Under a lattice it moves only at rung\n // crossings; under exact mode it tracks the demand and is CONSTANT above\n // the budget — so the deep-zoom backdrop never refetches, and the sub-\n // budget range refetches per settled demand exactly like v2 did.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the resolved render points and\n // collapses same-key asks in its raster store. A stale-closure scale\n // is harmless by construction: any scale mapping to this key\n // produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n // Imperative src on a STABLE element: the browser keeps the old\n // bitmap until the new one decodes, and nothing ever re-requests the\n // old URL — so revoke-on-cleanup is safe here by construction.\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n\n return (\n <>\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n {tiles && settings.tiles ? (\n <TilePlane annotations={annotations} fadeMs={settings.fadeMs} />\n ) : null}\n </>\n );\n}\n\n/**\n * The sharp plane above the base — the plugin's retention-safe paint plan\n * as keyed <img>s. Each tile reports painted after its first presentation\n * opportunity (and its inverse on unmount), so retained coarser generations\n * release only when their replacement is truly compositable.\n *\n * Tiles are placed in VIEW (CSS px) space directly — never in page points\n * under a scaled container. Blink quantizes layout lengths to 1/64 CSS px\n * BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform\n * turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible\n * seams and per-zoom-step letter shifts that grow with depth. In view\n * space the same quantization is a fixed ~1/64 CSS px at every zoom.\n * (v2 placed tiles in screen px for exactly this reason.)\n */\nfunction TilePlane({ annotations, fadeMs }: { annotations: boolean; fadeMs: number }) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n // Level settling lives in the plugin (its settle gate), pan stays live.\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n // THIS view's tile surface: state is per view × page, so a thumbnail\n // rail's never-engaging demand cannot disturb the main lens's plan (shared\n // state made the main view lose its tiles whenever a rail opened). The\n // handle is reference-stable per view — a clean dependency.\n const tiles = render.tilesFor(page.view);\n const plan = useSelector(RenderToken, () =>\n tiles.plan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // View unmounted its plane: stop in-flight fetches; resolved bytes stay cached.\n useEffect(() => () => tiles.release(page.pon), [tiles, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const s = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth,\n height: t.contentHeight,\n pointerEvents: 'none',\n // STACKING CONTRACT: tile <img>s carry zIndex ranks (coarse under\n // fine) that exist only while generations are mixed — mid-zoom. This\n // container MUST be a stacking context so those ranks stay internal;\n // without it they join the Stage's context and paint above every\n // z-auto sibling (annotations, page chrome, menus) exactly while\n // zooming. The old scaled container created one implicitly via its\n // transform; `isolation` is the explicit, no-side-effect way.\n isolation: 'isolate',\n }}\n >\n {fadeMs > 0 ? (\n <style>{`@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }`}</style>\n ) : null}\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n view={{\n x: source.rect.x * s,\n y: source.rect.y * s,\n width: source.rect.width * s,\n height: source.rect.height * s,\n }}\n fadeMs={fadeMs}\n onPainted={() => tiles.painted(page.pon, source.key)}\n onUnpainted={() => tiles.unpainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: geometry plus the shared browser image binding. The binding\n * hides the incomplete image, reports painted after its first presentation\n * opportunity, and inverts that report when the element leaves the DOM. */\nfunction TileImg({\n source,\n view,\n fadeMs,\n onPainted,\n onUnpainted,\n}: {\n source: TilePaintSource;\n /** Placement rect in VIEW (CSS px) space. */\n view: { x: number; y: number; width: number; height: number };\n fadeMs: number;\n onPainted: () => void;\n onUnpainted: () => void;\n}) {\n const ref = useRef<HTMLImageElement>(null);\n useLayoutEffect(() => {\n if (!ref.current) return;\n return bindPaintedImage(ref.current, source.handle, { onPainted, onUnpainted });\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the handle is\n // the bitmap's identity; the report callbacks carry stable values.\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={(e) => {\n // A raster whose intrinsic size disagrees with its rect would be\n // silently stretched into place — the stale-handle bug class.\n const w = e.currentTarget.naturalWidth;\n if (w > 0 && !warnedTileSize) {\n const expected = Math.round(source.rect.width * source.scale);\n if (Math.abs(w - expected) > 1) {\n warnedTileSize = true;\n console.warn(\n `[render] tile bitmap ${w}px wide does not match its rect ` +\n `(expected ~${expected}px) — stale raster identity? key=${source.key}`,\n );\n }\n }\n }}\n style={{\n position: 'absolute',\n left: view.x,\n top: view.y,\n width: view.width,\n height: view.height,\n zIndex: source.z,\n ...(fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null),\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __exportAll = (all, no_symbols) => {
|
|
7
|
+
let target = {};
|
|
8
|
+
for (var name in all) __defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
return target;
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
|
+
key = keys[i];
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
19
|
+
get: ((k) => from[k]).bind(null, key),
|
|
20
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
26
|
+
//#endregion
|
|
27
|
+
export { __reExport as n, __exportAll as t };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { AnyPlugin, CapabilityToken, Engine, EngineFactory, InitialDocument, Kernel } from "@embedpdf/core";
|
|
2
1
|
import * as React from "react";
|
|
2
|
+
import { AnyPlugin, CapabilityToken, Engine, EngineFactory, InitialDocument, Kernel } from "@embedpdf/core";
|
|
3
3
|
import { PageFrame, PageTransform, Point, Rect } from "@embedpdf/core-geometry";
|
|
4
4
|
import { PageViewDemand } from "@embedpdf/plugin-render";
|
|
5
|
-
|
|
5
|
+
declare namespace runtime_d_exports {
|
|
6
|
+
export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
|
|
7
|
+
}
|
|
8
|
+
import * as import__embedpdf_core from "@embedpdf/core";
|
|
6
9
|
declare function useKernel(): Kernel;
|
|
7
10
|
declare const shallowArray: <T>(a: readonly T[], b: readonly T[]) => boolean;
|
|
8
11
|
/** Read a value derived from the kernel, cached by equality (no tearing loop). */
|
|
@@ -160,18 +163,19 @@ interface PageContextValue {
|
|
|
160
163
|
frame: PageFrame;
|
|
161
164
|
/**
|
|
162
165
|
* The single bridge between PDF points, view px, and device px for this page.
|
|
163
|
-
* Layers do ALL coordinate work through it — `
|
|
166
|
+
* Layers do ALL coordinate work through it — `toPixels` to place content-
|
|
164
167
|
* space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for
|
|
165
168
|
* page-relative sizing. Never re-derive `x * scale` or `* dpr`.
|
|
166
169
|
*/
|
|
167
170
|
transform: PageTransform;
|
|
168
|
-
/** Client (screen) point →
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
/** Client (screen) point → the viewer's coordinates (content point) — the
|
|
172
|
+
* one platform-bound hit-test. */
|
|
173
|
+
toContentPoint(clientX: number, clientY: number): Point;
|
|
174
|
+
/** Content point → client (screen) px — the exact inverse of `toContentPoint`
|
|
171
175
|
* (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a
|
|
172
176
|
* page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */
|
|
173
177
|
toClientPoint(p: Point): Point;
|
|
174
|
-
/**
|
|
178
|
+
/** Content rect → client (screen) px AABB. Rect analog of `toClientPoint`
|
|
175
179
|
* for upright viewport-space UI that frames a selected page region. */
|
|
176
180
|
toClientRect(rect: Rect): Rect;
|
|
177
181
|
/**
|
|
@@ -186,10 +190,19 @@ interface PageContextValue {
|
|
|
186
190
|
* a thumbnail-sized demand turns into "never engages" by arithmetic).
|
|
187
191
|
*/
|
|
188
192
|
getViewDemand?: () => PageViewDemand;
|
|
193
|
+
/**
|
|
194
|
+
* The hosting VIEW's identity — the stage lens id (`stage.lensId()`) or a
|
|
195
|
+
* per-instance PageView id. IDENTITY, not an option: per-view raster
|
|
196
|
+
* planning (tiles) keys its state by this, so two views showing the SAME
|
|
197
|
+
* page never fight over one plan (a thumbnail rail's never-engaging demand
|
|
198
|
+
* must not disturb the main view's tiles). Every page context host must
|
|
199
|
+
* say which view it is.
|
|
200
|
+
*/
|
|
201
|
+
view: string;
|
|
189
202
|
}
|
|
190
203
|
declare const PageProvider: React.Provider<PageContextValue | null>;
|
|
191
204
|
declare function usePage(): PageContextValue;
|
|
192
|
-
declare function makePageContext(documentId: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
|
|
205
|
+
declare function makePageContext(documentId: string, view: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
|
|
193
206
|
//#endregion
|
|
194
|
-
export { useSelector as S,
|
|
195
|
-
//# sourceMappingURL=runtime-
|
|
207
|
+
export { useSelector as C, usePage as S, useDocuments as _, EmbedPDF as a, useOptionalCapability as b, Viewer as c, runtime_d_exports as d, shallowArray as f, useDocumentStatus as g, useDocumentId as h, DocumentScopeProps as i, ViewerProps as l, useCapability as m, DocumentGateProps as n, PageContextValue as o, useActiveDocumentId as p, DocumentScope as r, PageProvider as s, DocumentGate as t, makePageContext as u, useKernel as v, useOptionalSelector as x, useKernelValue as y };
|
|
208
|
+
//# sourceMappingURL=runtime-Cvi0wQ4d.d.cts.map
|