@embedpdf/react 3.0.0-next.1 → 3.0.0-next.11

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.
Files changed (132) hide show
  1. package/dist/AnnotationListSnapshot-C1U3XUxP-6kGwNoxy.d.cts +546 -0
  2. package/dist/AnnotationListSnapshot-C1U3XUxP-6kGwNoxy.d.ts +546 -0
  3. package/dist/actions.cjs +42 -0
  4. package/dist/actions.cjs.map +1 -0
  5. package/dist/actions.d.cts +18 -0
  6. package/dist/actions.d.ts +18 -0
  7. package/dist/actions.js +34 -0
  8. package/dist/actions.js.map +1 -0
  9. package/dist/anchored.cjs +89 -0
  10. package/dist/anchored.cjs.map +1 -0
  11. package/dist/anchored.d.cts +45 -0
  12. package/dist/anchored.d.ts +45 -0
  13. package/dist/anchored.js +60 -0
  14. package/dist/anchored.js.map +1 -0
  15. package/dist/{annotation-B6jwfTCh.d.cts → annotation-BCED_p9H.d.cts} +56 -69
  16. package/dist/annotation-anchors-CetwM1g7.js +15 -0
  17. package/dist/annotation-anchors-CetwM1g7.js.map +1 -0
  18. package/dist/annotation-anchors-DuYRBJLl.cjs +26 -0
  19. package/dist/annotation-anchors-DuYRBJLl.cjs.map +1 -0
  20. package/dist/annotation-hooks-BB5ZhPpf.cjs +16 -0
  21. package/dist/annotation-hooks-BB5ZhPpf.cjs.map +1 -0
  22. package/dist/annotation-hooks-Df8EmzDO.js +11 -0
  23. package/dist/annotation-hooks-Df8EmzDO.js.map +1 -0
  24. package/dist/{annotation-nUMefyjw.d.ts → annotation-k38KDlIZ.d.ts} +57 -70
  25. package/dist/annotation-menu.cjs +37 -88
  26. package/dist/annotation-menu.cjs.map +1 -1
  27. package/dist/annotation-menu.d.cts +28 -2
  28. package/dist/annotation-menu.d.ts +28 -2
  29. package/dist/annotation-menu.js +28 -79
  30. package/dist/annotation-menu.js.map +1 -1
  31. package/dist/annotation.cjs +738 -16
  32. package/dist/annotation.cjs.map +1 -0
  33. package/dist/annotation.d.cts +2 -2
  34. package/dist/annotation.d.ts +2 -2
  35. package/dist/annotation.js +723 -3
  36. package/dist/annotation.js.map +1 -0
  37. package/dist/commands.js +1 -0
  38. package/dist/commands.js.map +1 -1
  39. package/dist/form-BwrLFpKN.js +805 -0
  40. package/dist/form-BwrLFpKN.js.map +1 -0
  41. package/dist/form-D-ZSfGmB.cjs +836 -0
  42. package/dist/form-D-ZSfGmB.cjs.map +1 -0
  43. package/dist/form.cjs +8 -551
  44. package/dist/form.d.cts +4 -21
  45. package/dist/form.d.ts +4 -21
  46. package/dist/form.js +3 -544
  47. package/dist/index.cjs +49 -16
  48. package/dist/index.d.cts +17 -10
  49. package/dist/index.d.ts +17 -10
  50. package/dist/index.js +14 -9
  51. package/dist/interaction.cjs +17 -4
  52. package/dist/interaction.cjs.map +1 -1
  53. package/dist/interaction.d.cts +2 -2
  54. package/dist/interaction.d.ts +2 -2
  55. package/dist/interaction.js +7 -5
  56. package/dist/interaction.js.map +1 -1
  57. package/dist/link.cjs +70 -7
  58. package/dist/link.cjs.map +1 -1
  59. package/dist/link.d.cts +12 -3
  60. package/dist/link.d.ts +12 -3
  61. package/dist/link.js +73 -10
  62. package/dist/link.js.map +1 -1
  63. package/dist/metadata.js +1 -0
  64. package/dist/metadata.js.map +1 -1
  65. package/dist/page-edit.js +1 -0
  66. package/dist/page-edit.js.map +1 -1
  67. package/dist/page-view.cjs +62 -38
  68. package/dist/page-view.cjs.map +1 -1
  69. package/dist/page-view.js +59 -37
  70. package/dist/page-view.js.map +1 -1
  71. package/dist/redaction.js +1 -0
  72. package/dist/redaction.js.map +1 -1
  73. package/dist/render.cjs +67 -50
  74. package/dist/render.cjs.map +1 -1
  75. package/dist/render.d.cts +6 -19
  76. package/dist/render.d.ts +6 -19
  77. package/dist/render.js +71 -52
  78. package/dist/render.js.map +1 -1
  79. package/dist/rolldown-runtime-BBjsoOtd.js +27 -0
  80. package/dist/{runtime-CMxIuF2O.d.cts → runtime-DE76hlnl.d.ts} +32 -11
  81. package/dist/{runtime-DV98q7I0.d.ts → runtime-umHA_xHt.d.cts} +32 -11
  82. package/dist/runtime.cjs +25 -5
  83. package/dist/runtime.cjs.map +1 -1
  84. package/dist/runtime.d.cts +2 -2
  85. package/dist/runtime.d.ts +2 -2
  86. package/dist/runtime.js +26 -6
  87. package/dist/runtime.js.map +1 -1
  88. package/dist/scrollbar.cjs +3 -3
  89. package/dist/scrollbar.cjs.map +1 -1
  90. package/dist/scrollbar.d.cts +1 -1
  91. package/dist/scrollbar.d.ts +1 -1
  92. package/dist/scrollbar.js +1 -1
  93. package/dist/scrollbar.js.map +1 -1
  94. package/dist/search.cjs +36 -19
  95. package/dist/search.cjs.map +1 -1
  96. package/dist/search.d.cts +1 -1
  97. package/dist/search.d.ts +1 -1
  98. package/dist/search.js +36 -19
  99. package/dist/search.js.map +1 -1
  100. package/dist/selection.cjs +217 -22
  101. package/dist/selection.cjs.map +1 -1
  102. package/dist/selection.d.cts +60 -2
  103. package/dist/selection.d.ts +60 -2
  104. package/dist/selection.js +214 -27
  105. package/dist/selection.js.map +1 -1
  106. package/dist/shell.js +1 -0
  107. package/dist/shell.js.map +1 -1
  108. package/dist/stage.cjs +49 -148
  109. package/dist/stage.cjs.map +1 -1
  110. package/dist/stage.d.cts +18 -5
  111. package/dist/stage.d.ts +18 -5
  112. package/dist/stage.js +51 -149
  113. package/dist/stage.js.map +1 -1
  114. package/dist/stamp.js +1 -0
  115. package/dist/stamp.js.map +1 -1
  116. package/dist/toolbar.cjs +4 -4
  117. package/dist/toolbar.cjs.map +1 -1
  118. package/dist/toolbar.d.cts +1 -1
  119. package/dist/toolbar.d.ts +1 -1
  120. package/dist/toolbar.js +2 -1
  121. package/dist/toolbar.js.map +1 -1
  122. package/dist/views.js +1 -0
  123. package/dist/views.js.map +1 -1
  124. package/package.json +33 -26
  125. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.cts +0 -2593
  126. package/dist/AnnotationListSnapshot-W0RCsyDE-dWna4TTJ.d.ts +0 -2593
  127. package/dist/annotation-Ct6dFtzJ.cjs +0 -913
  128. package/dist/annotation-Ct6dFtzJ.cjs.map +0 -1
  129. package/dist/annotation-DjGWJljY.js +0 -829
  130. package/dist/annotation-DjGWJljY.js.map +0 -1
  131. package/dist/form.cjs.map +0 -1
  132. package/dist/form.js.map +0 -1
@@ -1,8 +1,11 @@
1
+ import { AnyPlugin, CapabilityToken, Engine, EngineFactory, EventHook, InitialDocument, Kernel } from "@embedpdf/core";
1
2
  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
- import { PageViewDemand } from "@embedpdf/plugin-render";
5
- //#region src/runtime.d.ts
4
+ import { PageViewDemand } from "@embedpdf/plugin-render/contract";
5
+ declare namespace runtime_d_exports {
6
+ export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useCapabilityEvent, 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). */
@@ -66,6 +69,14 @@ declare function useSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) =
66
69
  * resolves to `fallback` for one frame, then re-renders against the new state.
67
70
  */
68
71
  declare function useOptionalSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) => R, fallback: R, isEqual?: (a: R, b: R) => boolean): R;
72
+ /**
73
+ * Subscribe to a capability's {@link EventHook} for the mounted lifetime —
74
+ * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events
75
+ * carry occurrences, never state (a late subscriber that needs the current
76
+ * value uses `useSelector`). The handler rides a ref, so a fresh closure per
77
+ * render never resubscribes. Null-safe: no plugin/document → no subscription.
78
+ */
79
+ declare function useCapabilityEvent<C, T>(token: CapabilityToken<C>, select: (cap: C) => EventHook<T>, handler: (event: T) => void): void;
69
80
  /** The document registry (open/close/active/list), reactive. */
70
81
  declare function useDocuments(): {
71
82
  docs: import("@embedpdf/core").DocInfo[];
@@ -160,18 +171,19 @@ interface PageContextValue {
160
171
  frame: PageFrame;
161
172
  /**
162
173
  * The single bridge between PDF points, view px, and device px for this page.
163
- * Layers do ALL coordinate work through it — `pageToContent` to place content-
174
+ * Layers do ALL coordinate work through it — `toPixels` to place content-
164
175
  * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for
165
176
  * page-relative sizing. Never re-derive `x * scale` or `* dpr`.
166
177
  */
167
178
  transform: PageTransform;
168
- /** Client (screen) point → PDF point — the one platform-bound hit-test. */
169
- toPagePoint(clientX: number, clientY: number): Point;
170
- /** PDF/content point → client (screen) px — the exact inverse of `toPagePoint`
179
+ /** Client (screen) point → the viewer's coordinates (content point) — the
180
+ * one platform-bound hit-test. */
181
+ toContentPoint(clientX: number, clientY: number): Point;
182
+ /** Content point → client (screen) px — the exact inverse of `toContentPoint`
171
183
  * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a
172
184
  * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */
173
185
  toClientPoint(p: Point): Point;
174
- /** PDF/content rect → client (screen) px AABB. Rect analog of `toClientPoint`
186
+ /** Content rect → client (screen) px AABB. Rect analog of `toClientPoint`
175
187
  * for upright viewport-space UI that frames a selected page region. */
176
188
  toClientRect(rect: Rect): Rect;
177
189
  /**
@@ -186,10 +198,19 @@ interface PageContextValue {
186
198
  * a thumbnail-sized demand turns into "never engages" by arithmetic).
187
199
  */
188
200
  getViewDemand?: () => PageViewDemand;
201
+ /**
202
+ * The hosting VIEW's identity — the stage lens id (`stage.lensId()`) or a
203
+ * per-instance PageView id. IDENTITY, not an option: per-view raster
204
+ * planning (tiles) keys its state by this, so two views showing the SAME
205
+ * page never fight over one plan (a thumbnail rail's never-engaging demand
206
+ * must not disturb the main view's tiles). Every page context host must
207
+ * say which view it is.
208
+ */
209
+ view: string;
189
210
  }
190
211
  declare const PageProvider: React.Provider<PageContextValue | null>;
191
212
  declare function usePage(): PageContextValue;
192
- declare function makePageContext(documentId: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
213
+ declare function makePageContext(documentId: string, view: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
193
214
  //#endregion
194
- export { useSelector as S, useKernel as _, EmbedPDF as a, useOptionalSelector as b, Viewer as c, shallowArray as d, useActiveDocumentId as f, useDocuments as g, useDocumentStatus as h, DocumentScopeProps as i, ViewerProps as l, useDocumentId as m, DocumentGateProps as n, PageContextValue as o, useCapability as p, DocumentScope as r, PageProvider as s, DocumentGate as t, makePageContext as u, useKernelValue as v, usePage as x, useOptionalCapability as y };
195
- //# sourceMappingURL=runtime-CMxIuF2O.d.cts.map
215
+ export { usePage as C, useOptionalSelector as S, useDocumentStatus as _, EmbedPDF as a, useKernelValue as b, Viewer as c, runtime_d_exports as d, shallowArray as f, useDocumentId as g, useCapabilityEvent 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, useDocuments as v, useSelector as w, useOptionalCapability as x, useKernel as y };
216
+ //# sourceMappingURL=runtime-DE76hlnl.d.ts.map
@@ -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, EventHook, InitialDocument, Kernel } from "@embedpdf/core";
3
3
  import { PageFrame, PageTransform, Point, Rect } from "@embedpdf/core-geometry";
4
- import { PageViewDemand } from "@embedpdf/plugin-render";
5
- //#region src/runtime.d.ts
4
+ import { PageViewDemand } from "@embedpdf/plugin-render/contract";
5
+ declare namespace runtime_d_exports {
6
+ export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useCapabilityEvent, 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). */
@@ -66,6 +69,14 @@ declare function useSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) =
66
69
  * resolves to `fallback` for one frame, then re-renders against the new state.
67
70
  */
68
71
  declare function useOptionalSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) => R, fallback: R, isEqual?: (a: R, b: R) => boolean): R;
72
+ /**
73
+ * Subscribe to a capability's {@link EventHook} for the mounted lifetime —
74
+ * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events
75
+ * carry occurrences, never state (a late subscriber that needs the current
76
+ * value uses `useSelector`). The handler rides a ref, so a fresh closure per
77
+ * render never resubscribes. Null-safe: no plugin/document → no subscription.
78
+ */
79
+ declare function useCapabilityEvent<C, T>(token: CapabilityToken<C>, select: (cap: C) => EventHook<T>, handler: (event: T) => void): void;
69
80
  /** The document registry (open/close/active/list), reactive. */
70
81
  declare function useDocuments(): {
71
82
  docs: import("@embedpdf/core").DocInfo[];
@@ -160,18 +171,19 @@ interface PageContextValue {
160
171
  frame: PageFrame;
161
172
  /**
162
173
  * The single bridge between PDF points, view px, and device px for this page.
163
- * Layers do ALL coordinate work through it — `pageToContent` to place content-
174
+ * Layers do ALL coordinate work through it — `toPixels` to place content-
164
175
  * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for
165
176
  * page-relative sizing. Never re-derive `x * scale` or `* dpr`.
166
177
  */
167
178
  transform: PageTransform;
168
- /** Client (screen) point → PDF point — the one platform-bound hit-test. */
169
- toPagePoint(clientX: number, clientY: number): Point;
170
- /** PDF/content point → client (screen) px — the exact inverse of `toPagePoint`
179
+ /** Client (screen) point → the viewer's coordinates (content point) — the
180
+ * one platform-bound hit-test. */
181
+ toContentPoint(clientX: number, clientY: number): Point;
182
+ /** Content point → client (screen) px — the exact inverse of `toContentPoint`
171
183
  * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a
172
184
  * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */
173
185
  toClientPoint(p: Point): Point;
174
- /** PDF/content rect → client (screen) px AABB. Rect analog of `toClientPoint`
186
+ /** Content rect → client (screen) px AABB. Rect analog of `toClientPoint`
175
187
  * for upright viewport-space UI that frames a selected page region. */
176
188
  toClientRect(rect: Rect): Rect;
177
189
  /**
@@ -186,10 +198,19 @@ interface PageContextValue {
186
198
  * a thumbnail-sized demand turns into "never engages" by arithmetic).
187
199
  */
188
200
  getViewDemand?: () => PageViewDemand;
201
+ /**
202
+ * The hosting VIEW's identity — the stage lens id (`stage.lensId()`) or a
203
+ * per-instance PageView id. IDENTITY, not an option: per-view raster
204
+ * planning (tiles) keys its state by this, so two views showing the SAME
205
+ * page never fight over one plan (a thumbnail rail's never-engaging demand
206
+ * must not disturb the main view's tiles). Every page context host must
207
+ * say which view it is.
208
+ */
209
+ view: string;
189
210
  }
190
211
  declare const PageProvider: React.Provider<PageContextValue | null>;
191
212
  declare function usePage(): PageContextValue;
192
- declare function makePageContext(documentId: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
213
+ declare function makePageContext(documentId: string, view: string, pon: number, pageIndex: number, frame: PageFrame, transform: PageTransform, getRect: () => DOMRect, getViewDemand?: () => PageViewDemand): PageContextValue;
193
214
  //#endregion
194
- export { useSelector as S, useKernel as _, EmbedPDF as a, useOptionalSelector as b, Viewer as c, shallowArray as d, useActiveDocumentId as f, useDocuments as g, useDocumentStatus as h, DocumentScopeProps as i, ViewerProps as l, useDocumentId as m, DocumentGateProps as n, PageContextValue as o, useCapability as p, DocumentScope as r, PageProvider as s, DocumentGate as t, makePageContext as u, useKernelValue as v, usePage as x, useOptionalCapability as y };
195
- //# sourceMappingURL=runtime-DV98q7I0.d.ts.map
215
+ export { usePage as C, useOptionalSelector as S, useDocumentStatus as _, EmbedPDF as a, useKernelValue as b, Viewer as c, runtime_d_exports as d, shallowArray as f, useDocumentId as g, useCapabilityEvent 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, useDocuments as v, useSelector as w, useOptionalCapability as x, useKernel as y };
216
+ //# sourceMappingURL=runtime-umHA_xHt.d.cts.map
package/dist/runtime.cjs CHANGED
@@ -128,6 +128,24 @@ function useOptionalSelector(token, select, fallback, isEqual = Object.is) {
128
128
  };
129
129
  return (0, react.useSyncExternalStore)(kernel.subscribe, get, get);
130
130
  }
131
+ /**
132
+ * Subscribe to a capability's {@link EventHook} for the mounted lifetime —
133
+ * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events
134
+ * carry occurrences, never state (a late subscriber that needs the current
135
+ * value uses `useSelector`). The handler rides a ref, so a fresh closure per
136
+ * render never resubscribes. Null-safe: no plugin/document → no subscription.
137
+ */
138
+ function useCapabilityEvent(token, select, handler) {
139
+ const cap = useOptionalCapability(token);
140
+ const handlerRef = (0, react.useRef)(handler);
141
+ handlerRef.current = handler;
142
+ const selectRef = (0, react.useRef)(select);
143
+ selectRef.current = select;
144
+ (0, react.useEffect)(() => {
145
+ if (!cap) return;
146
+ return selectRef.current(cap)((event) => handlerRef.current(event));
147
+ }, [cap]);
148
+ }
131
149
  /** The document registry (open/close/active/list), reactive. */
132
150
  function useDocuments() {
133
151
  const kernel = useKernel();
@@ -248,24 +266,25 @@ function usePage() {
248
266
  if (!c) throw new Error("usePage must be used inside <PageView> or a <Stage> page");
249
267
  return c;
250
268
  }
251
- function makePageContext(documentId, pon, pageIndex, frame, transform, getRect, getViewDemand) {
269
+ function makePageContext(documentId, view, pon, pageIndex, frame, transform, getRect, getViewDemand) {
252
270
  return {
253
271
  documentId,
272
+ view,
254
273
  pon,
255
274
  pageIndex,
256
275
  frame,
257
276
  transform,
258
277
  ...getViewDemand ? { getViewDemand } : {},
259
- toPagePoint: (cx, cy) => {
278
+ toContentPoint: (cx, cy) => {
260
279
  const r = getRect();
261
- return transform.viewToPage({
280
+ return transform.viewToContent({
262
281
  x: cx - r.left,
263
282
  y: cy - r.top
264
283
  });
265
284
  },
266
285
  toClientPoint: (p) => {
267
286
  const r = getRect();
268
- const v = transform.pageToView(p);
287
+ const v = transform.contentToView(p);
269
288
  return {
270
289
  x: r.left + v.x,
271
290
  y: r.top + v.y
@@ -273,7 +292,7 @@ function makePageContext(documentId, pon, pageIndex, frame, transform, getRect,
273
292
  },
274
293
  toClientRect: (rect) => {
275
294
  const r = getRect();
276
- const v = transform.pageToViewRect(rect);
295
+ const v = transform.contentToViewRect(rect);
277
296
  return {
278
297
  x: r.left + v.x,
279
298
  y: r.top + v.y,
@@ -293,6 +312,7 @@ exports.makePageContext = makePageContext;
293
312
  exports.shallowArray = shallowArray;
294
313
  exports.useActiveDocumentId = useActiveDocumentId;
295
314
  exports.useCapability = useCapability;
315
+ exports.useCapabilityEvent = useCapabilityEvent;
296
316
  exports.useDocumentId = useDocumentId;
297
317
  exports.useDocumentStatus = useDocumentStatus;
298
318
  exports.useDocuments = useDocuments;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.cjs","names":["docInfoListEquals"],"sources":["../src/runtime.tsx"],"sourcesContent":["/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document), and provides the page coordinate context. Every plugin and layer rides\n * on this — there is no per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\nimport * as React from 'react';\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createKernel, docInfoListEquals } from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n Engine,\n EngineFactory,\n InitialDocument,\n Kernel,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — NOT from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageFrame, PageTransform, Point, Rect } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const k = useContext(KernelCtx);\n if (!k) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return k;\n}\n\nexport const shallowArray = <T,>(a: readonly T[], b: readonly T[]): boolean =>\n a === b || (a.length === b.length && a.every((x, i) => x === b[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (k: Kernel) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((k) => k.documents.activeId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useContext(DocumentScopeCtx);\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has NO document (empty workspace, docs still opening). */\n fallback?: React.ReactNode;\n children: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a READY document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives OUTSIDE the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it, or reads through `useOptionalSelector`.\n * A `loading`/`locked`/`error` tab renders `fallback` — so the gate's\n * fallback doubles as the per-tab boot state; richer chrome (a password\n * prompt, an error pane) branches on `useDocumentStatus()` beside the gate.\n * Sibling of <DocumentScope>, which picks WHICH document; this one handles\n * WHETHER.\n */\nexport function DocumentGate({ fallback = null, children }: DocumentGateProps) {\n const docId = useDocumentId();\n const ready = useKernelValue((k) => (docId ? k.documents.get(docId)?.status === 'ready' : false));\n return <>{ready ? children : fallback}</>;\n}\n\n/** Lifecycle status of this subtree's document (loading/locked/ready/error),\n * or null with no document. The password prompt and error panes key off it. */\nexport function useDocumentStatus() {\n const docId = useDocumentId();\n return useKernelValue((k) => (docId ? (k.documents.get(docId)?.status ?? null) : null));\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a REACTIVE read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible. Fail-fast: while unresolvable, this re-runs the\n * strict resolver so the kernel's truthful reason (`no capability` / `no\n * document` / `document is loading|locked`) is what throws.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const scoped = useContext(DocumentScopeCtx);\n const cap = useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n return cap ?? kernel.capability(token, scoped ?? undefined);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useContext(DocumentScopeCtx);\n return useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n}\n\n/** Subscribe to a selector over a (document-resolved) capability. */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(cap);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n * `useSelector` stays strict (fail-fast) for code that KNOWS a document exists\n * — e.g. anything inside a <DocumentGate>.\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n fallback: R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (cap === null) {\n next = fallback;\n } else {\n try {\n next = select(cap);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/** The document registry (open/close/active/list), reactive. */\nexport function useDocuments() {\n const kernel = useKernel();\n const docs = useKernelValue((k) => k.documents.list(), docInfoListEquals);\n const activeId = useActiveDocumentId();\n return {\n docs,\n activeId,\n open: kernel.documents.open,\n unlock: kernel.documents.unlock,\n close: kernel.documents.close,\n setActive: kernel.documents.setActive,\n move: kernel.documents.move,\n swap: kernel.documents.swap,\n download: kernel.documents.download,\n downloadLayer: kernel.documents.downloadLayer,\n };\n}\n\n// `InitialDocument` is the KERNEL's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the SHAPE of what you pass:\n *\n * - An **instance** (`engine={engine}`) is BORROWED: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer calls `engine.warmup?.()`\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is VIEWER-OWNED: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n children: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an EFFECT-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen) exactly as before. The one exception is the very first\n * render, which happens before the effect: it renders nothing. Children mount\n * once `start()` resolves — which never touches the engine, so the shell is\n * alive while WASM compiles or the transport connects. `initialDocuments`\n * open in the BACKGROUND and stream into the registry (`useDocuments()` is\n * reactive); per-document loading UI is the Stage's job, not a root gate.\n *\n * ENGINE OWNERSHIP. When `engine` is a thunk, the Viewer OWNS it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is BORROWED and never destroyed here; the Viewer only calls `warmup?.()` so\n * the WASM/transport boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport function Viewer({\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n children,\n}: ViewerProps) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n const warned = useRef(false);\n if (process.env.NODE_ENV !== 'production' && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so kick `warmup()`\n // to overlap the WASM/transport boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n engine.warmup?.();\n let kernel: Kernel;\n try {\n kernel = createKernel({ engine, plugins: captured.plugins });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), THEN the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n}\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends ONLY on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n */\nexport interface PageContextValue {\n documentId: string;\n /** Durable page identity (PDF object number) — use for keys / render / annotations. */\n pon: number;\n /** Display index (page N) — use for ordering / human-facing page numbers. */\n pageIndex: number;\n /**\n * Reserved chrome bands around the page (screen px per side). The page-chrome\n * slot renders into the outer box (content + frame); these thicknesses size\n * the bands — a label in the bottom band is `bottom:0; height: frame.bottom`.\n */\n frame: PageFrame;\n /**\n * The single bridge between PDF points, view px, and device px for this page.\n * Layers do ALL coordinate work through it — `pageToContent` to place content-\n * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for\n * page-relative sizing. Never re-derive `x * scale` or `* dpr`.\n */\n transform: PageTransform;\n /** Client (screen) point → PDF point — the one platform-bound hit-test. */\n toPagePoint(clientX: number, clientY: number): Point;\n /** PDF/content point → client (screen) px — the exact inverse of `toPagePoint`\n * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a\n * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */\n toClientPoint(p: Point): Point;\n /** PDF/content rect → client (screen) px AABB. Rect analog of `toClientPoint`\n * for upright viewport-space UI that frames a selected page region. */\n toClientRect(rect: Rect): Rect;\n /**\n * The page-view DEMAND for raster planning uses dependency inversion:\n * plugin-render defines the shape; the host that CREATED this\n * context fills it — as a PULL. The Stage host's getter closes over the\n * stage capability and reads `VisiblePage.visibleRect` live at call time\n * (visibility is the STAGE's data; adapters never re-derive camera math or\n * cache a copy). Three states, three meanings: a real sub-rect (visible),\n * a ZERO rect (stage host, page currently off-screen — want nothing), and\n * an undefined getter (stage-less `<PageView>` — whole page visible, which\n * a thumbnail-sized demand turns into \"never engages\" by arithmetic).\n */\n getViewDemand?: () => PageViewDemand;\n}\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const c = useContext(PageCtx);\n if (!c) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return c;\n}\n\nexport function makePageContext(\n documentId: string,\n pon: number,\n pageIndex: number,\n frame: PageFrame,\n transform: PageTransform,\n getRect: () => DOMRect,\n getViewDemand?: () => PageViewDemand,\n): PageContextValue {\n return {\n documentId,\n pon,\n pageIndex,\n frame,\n transform,\n ...(getViewDemand ? { getViewDemand } : {}),\n toPagePoint: (cx, cy) => {\n // `getRect()` is the rotated content wrapper's axis-aligned bounding box =\n // the page's DISPLAY box on screen. Convert client → box-local view px,\n // then invert rotation + scale via the transform (verified once in geometry,\n // not re-derived per framework adapter).\n const r = getRect();\n return transform.viewToPage({ x: cx - r.left, y: cy - r.top });\n },\n toClientPoint: (p) => {\n // Exact inverse of `toPagePoint`: page/content point → display-box view px\n // (rotation applied by the transform), offset by the same live display-box\n // origin. So the two can never drift, in either <Stage> or <PageView>.\n const r = getRect();\n const v = transform.pageToView(p);\n return { x: r.left + v.x, y: r.top + v.y };\n },\n toClientRect: (rect) => {\n const r = getRect();\n const v = transform.pageToViewRect(rect);\n return { x: r.left + v.x, y: r.top + v.y, width: v.width, height: v.height };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.cjs","names":["docInfoListEquals"],"sources":["../src/runtime.tsx"],"sourcesContent":["/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document), and provides the page coordinate context. Every plugin and layer rides\n * on this — there is no per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\nimport * as React from 'react';\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createKernel, docInfoListEquals } from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n Engine,\n EngineFactory,\n EventHook,\n InitialDocument,\n Kernel,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — NOT from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageFrame, PageTransform, Point, Rect } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render/contract';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const k = useContext(KernelCtx);\n if (!k) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return k;\n}\n\nexport const shallowArray = <T,>(a: readonly T[], b: readonly T[]): boolean =>\n a === b || (a.length === b.length && a.every((x, i) => x === b[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (k: Kernel) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((k) => k.documents.activeId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useContext(DocumentScopeCtx);\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has NO document (empty workspace, docs still opening). */\n fallback?: React.ReactNode;\n children: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a READY document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives OUTSIDE the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it, or reads through `useOptionalSelector`.\n * A `loading`/`locked`/`error` tab renders `fallback` — so the gate's\n * fallback doubles as the per-tab boot state; richer chrome (a password\n * prompt, an error pane) branches on `useDocumentStatus()` beside the gate.\n * Sibling of <DocumentScope>, which picks WHICH document; this one handles\n * WHETHER.\n */\nexport function DocumentGate({ fallback = null, children }: DocumentGateProps) {\n const docId = useDocumentId();\n const ready = useKernelValue((k) => (docId ? k.documents.get(docId)?.status === 'ready' : false));\n return <>{ready ? children : fallback}</>;\n}\n\n/** Lifecycle status of this subtree's document (loading/locked/ready/error),\n * or null with no document. The password prompt and error panes key off it. */\nexport function useDocumentStatus() {\n const docId = useDocumentId();\n return useKernelValue((k) => (docId ? (k.documents.get(docId)?.status ?? null) : null));\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a REACTIVE read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible. Fail-fast: while unresolvable, this re-runs the\n * strict resolver so the kernel's truthful reason (`no capability` / `no\n * document` / `document is loading|locked`) is what throws.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const scoped = useContext(DocumentScopeCtx);\n const cap = useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n return cap ?? kernel.capability(token, scoped ?? undefined);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useContext(DocumentScopeCtx);\n return useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n}\n\n/** Subscribe to a selector over a (document-resolved) capability. */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(cap);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n * `useSelector` stays strict (fail-fast) for code that KNOWS a document exists\n * — e.g. anything inside a <DocumentGate>.\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n fallback: R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (cap === null) {\n next = fallback;\n } else {\n try {\n next = select(cap);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Subscribe to a capability's {@link EventHook} for the mounted lifetime —\n * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events\n * carry occurrences, never state (a late subscriber that needs the current\n * value uses `useSelector`). The handler rides a ref, so a fresh closure per\n * render never resubscribes. Null-safe: no plugin/document → no subscription.\n */\nexport function useCapabilityEvent<C, T>(\n token: CapabilityToken<C>,\n select: (cap: C) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n const cap = useOptionalCapability(token);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n const selectRef = useRef(select);\n selectRef.current = select;\n useEffect(() => {\n if (!cap) return;\n return selectRef.current(cap)((event) => handlerRef.current(event));\n }, [cap]);\n}\n\n/** The document registry (open/close/active/list), reactive. */\nexport function useDocuments() {\n const kernel = useKernel();\n const docs = useKernelValue((k) => k.documents.list(), docInfoListEquals);\n const activeId = useActiveDocumentId();\n return {\n docs,\n activeId,\n open: kernel.documents.open,\n unlock: kernel.documents.unlock,\n close: kernel.documents.close,\n setActive: kernel.documents.setActive,\n move: kernel.documents.move,\n swap: kernel.documents.swap,\n download: kernel.documents.download,\n downloadLayer: kernel.documents.downloadLayer,\n };\n}\n\n// `InitialDocument` is the KERNEL's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the SHAPE of what you pass:\n *\n * - An **instance** (`engine={engine}`) is BORROWED: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer calls `engine.warmup?.()`\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is VIEWER-OWNED: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n children: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an EFFECT-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen) exactly as before. The one exception is the very first\n * render, which happens before the effect: it renders nothing. Children mount\n * once `start()` resolves — which never touches the engine, so the shell is\n * alive while WASM compiles or the transport connects. `initialDocuments`\n * open in the BACKGROUND and stream into the registry (`useDocuments()` is\n * reactive); per-document loading UI is the Stage's job, not a root gate.\n *\n * ENGINE OWNERSHIP. When `engine` is a thunk, the Viewer OWNS it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is BORROWED and never destroyed here; the Viewer only calls `warmup?.()` so\n * the WASM/transport boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport function Viewer({\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n children,\n}: ViewerProps) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n const warned = useRef(false);\n if (process.env.NODE_ENV !== 'production' && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so kick `warmup()`\n // to overlap the WASM/transport boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n engine.warmup?.();\n let kernel: Kernel;\n try {\n kernel = createKernel({ engine, plugins: captured.plugins });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), THEN the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n}\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends ONLY on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n */\nexport interface PageContextValue {\n documentId: string;\n /** Durable page identity (PDF object number) — use for keys / render / annotations. */\n pon: number;\n /** Display index (page N) — use for ordering / human-facing page numbers. */\n pageIndex: number;\n /**\n * Reserved chrome bands around the page (screen px per side). The page-chrome\n * slot renders into the outer box (content + frame); these thicknesses size\n * the bands — a label in the bottom band is `bottom:0; height: frame.bottom`.\n */\n frame: PageFrame;\n /**\n * The single bridge between PDF points, view px, and device px for this page.\n * Layers do ALL coordinate work through it — `toPixels` to place content-\n * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for\n * page-relative sizing. Never re-derive `x * scale` or `* dpr`.\n */\n transform: PageTransform;\n /** Client (screen) point → the viewer's coordinates (content point) — the\n * one platform-bound hit-test. */\n toContentPoint(clientX: number, clientY: number): Point;\n /** Content point → client (screen) px — the exact inverse of `toContentPoint`\n * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a\n * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */\n toClientPoint(p: Point): Point;\n /** Content rect → client (screen) px AABB. Rect analog of `toClientPoint`\n * for upright viewport-space UI that frames a selected page region. */\n toClientRect(rect: Rect): Rect;\n /**\n * The page-view DEMAND for raster planning uses dependency inversion:\n * plugin-render defines the shape; the host that CREATED this\n * context fills it — as a PULL. The Stage host's getter closes over the\n * stage capability and reads `VisiblePage.visibleRect` live at call time\n * (visibility is the STAGE's data; adapters never re-derive camera math or\n * cache a copy). Three states, three meanings: a real sub-rect (visible),\n * a ZERO rect (stage host, page currently off-screen — want nothing), and\n * an undefined getter (stage-less `<PageView>` — whole page visible, which\n * a thumbnail-sized demand turns into \"never engages\" by arithmetic).\n */\n getViewDemand?: () => PageViewDemand;\n /**\n * The hosting VIEW's identity — the stage lens id (`stage.lensId()`) or a\n * per-instance PageView id. IDENTITY, not an option: per-view raster\n * planning (tiles) keys its state by this, so two views showing the SAME\n * page never fight over one plan (a thumbnail rail's never-engaging demand\n * must not disturb the main view's tiles). Every page context host must\n * say which view it is.\n */\n view: string;\n}\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const c = useContext(PageCtx);\n if (!c) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return c;\n}\n\nexport function makePageContext(\n documentId: string,\n view: string,\n pon: number,\n pageIndex: number,\n frame: PageFrame,\n transform: PageTransform,\n getRect: () => DOMRect,\n getViewDemand?: () => PageViewDemand,\n): PageContextValue {\n return {\n documentId,\n view,\n pon,\n pageIndex,\n frame,\n transform,\n ...(getViewDemand ? { getViewDemand } : {}),\n toContentPoint: (cx, cy) => {\n // `getRect()` is the rotated content wrapper's axis-aligned bounding box =\n // the page's DISPLAY box on screen. Convert client → box-local view px,\n // then invert rotation + scale via the transform (verified once in geometry,\n // not re-derived per framework adapter).\n const r = getRect();\n return transform.viewToContent({ x: cx - r.left, y: cy - r.top });\n },\n toClientPoint: (p) => {\n // Exact inverse of `toContentPoint`: page/content point → display-box view px\n // (rotation applied by the transform), offset by the same live display-box\n // origin. So the two can never drift, in either <Stage> or <PageView>.\n const r = getRect();\n const v = transform.contentToView(p);\n return { x: r.left + v.x, y: r.top + v.y };\n },\n toClientRect: (rect) => {\n const r = getRect();\n const v = transform.contentToViewRect(rect);\n return { x: r.left + v.x, y: r.top + v.y, width: v.width, height: v.height };\n },\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { S as useSelector, _ as useKernel, a as EmbedPDF, b as useOptionalSelector, c as Viewer, d as shallowArray, f as useActiveDocumentId, g as useDocuments, h as useDocumentStatus, i as DocumentScopeProps, l as ViewerProps, m as useDocumentId, n as DocumentGateProps, o as PageContextValue, p as useCapability, r as DocumentScope, s as PageProvider, t as DocumentGate, u as makePageContext, v as useKernelValue, x as usePage, y as useOptionalCapability } from "./runtime-CMxIuF2O.cjs";
1
+ import { C as usePage, S as useOptionalSelector, _ as useDocumentStatus, a as EmbedPDF, b as useKernelValue, c as Viewer, f as shallowArray, g as useDocumentId, h as useCapabilityEvent, i as DocumentScopeProps, l as ViewerProps, m as useCapability, n as DocumentGateProps, o as PageContextValue, p as useActiveDocumentId, r as DocumentScope, s as PageProvider, t as DocumentGate, u as makePageContext, v as useDocuments, w as useSelector, x as useOptionalCapability, y as useKernel } from "./runtime-umHA_xHt.cjs";
2
2
  export * from "@embedpdf/core";
3
- export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
3
+ export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useCapabilityEvent, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
package/dist/runtime.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { S as useSelector, _ as useKernel, a as EmbedPDF, b as useOptionalSelector, c as Viewer, d as shallowArray, f as useActiveDocumentId, g as useDocuments, h as useDocumentStatus, i as DocumentScopeProps, l as ViewerProps, m as useDocumentId, n as DocumentGateProps, o as PageContextValue, p as useCapability, r as DocumentScope, s as PageProvider, t as DocumentGate, u as makePageContext, v as useKernelValue, x as usePage, y as useOptionalCapability } from "./runtime-DV98q7I0.js";
1
+ import { C as usePage, S as useOptionalSelector, _ as useDocumentStatus, a as EmbedPDF, b as useKernelValue, c as Viewer, f as shallowArray, g as useDocumentId, h as useCapabilityEvent, i as DocumentScopeProps, l as ViewerProps, m as useCapability, n as DocumentGateProps, o as PageContextValue, p as useActiveDocumentId, r as DocumentScope, s as PageProvider, t as DocumentGate, u as makePageContext, v as useDocuments, w as useSelector, x as useOptionalCapability, y as useKernel } from "./runtime-DE76hlnl.js";
2
2
  export * from "@embedpdf/core";
3
- export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
3
+ export { DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, PageContextValue, PageProvider, Viewer, ViewerProps, makePageContext, shallowArray, useActiveDocumentId, useCapability, useCapabilityEvent, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
package/dist/runtime.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "./rolldown-runtime-BBjsoOtd.js";
1
2
  import { Fragment, jsx } from "react/jsx-runtime";
2
3
  import { createKernel, docInfoListEquals } from "@embedpdf/core";
3
4
  import { createContext, useContext, useEffect, useRef, useState, useSyncExternalStore } from "react";
@@ -126,6 +127,24 @@ function useOptionalSelector(token, select, fallback, isEqual = Object.is) {
126
127
  };
127
128
  return useSyncExternalStore(kernel.subscribe, get, get);
128
129
  }
130
+ /**
131
+ * Subscribe to a capability's {@link EventHook} for the mounted lifetime —
132
+ * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events
133
+ * carry occurrences, never state (a late subscriber that needs the current
134
+ * value uses `useSelector`). The handler rides a ref, so a fresh closure per
135
+ * render never resubscribes. Null-safe: no plugin/document → no subscription.
136
+ */
137
+ function useCapabilityEvent(token, select, handler) {
138
+ const cap = useOptionalCapability(token);
139
+ const handlerRef = useRef(handler);
140
+ handlerRef.current = handler;
141
+ const selectRef = useRef(select);
142
+ selectRef.current = select;
143
+ useEffect(() => {
144
+ if (!cap) return;
145
+ return selectRef.current(cap)((event) => handlerRef.current(event));
146
+ }, [cap]);
147
+ }
129
148
  /** The document registry (open/close/active/list), reactive. */
130
149
  function useDocuments() {
131
150
  const kernel = useKernel();
@@ -246,24 +265,25 @@ function usePage() {
246
265
  if (!c) throw new Error("usePage must be used inside <PageView> or a <Stage> page");
247
266
  return c;
248
267
  }
249
- function makePageContext(documentId, pon, pageIndex, frame, transform, getRect, getViewDemand) {
268
+ function makePageContext(documentId, view, pon, pageIndex, frame, transform, getRect, getViewDemand) {
250
269
  return {
251
270
  documentId,
271
+ view,
252
272
  pon,
253
273
  pageIndex,
254
274
  frame,
255
275
  transform,
256
276
  ...getViewDemand ? { getViewDemand } : {},
257
- toPagePoint: (cx, cy) => {
277
+ toContentPoint: (cx, cy) => {
258
278
  const r = getRect();
259
- return transform.viewToPage({
279
+ return transform.viewToContent({
260
280
  x: cx - r.left,
261
281
  y: cy - r.top
262
282
  });
263
283
  },
264
284
  toClientPoint: (p) => {
265
285
  const r = getRect();
266
- const v = transform.pageToView(p);
286
+ const v = transform.contentToView(p);
267
287
  return {
268
288
  x: r.left + v.x,
269
289
  y: r.top + v.y
@@ -271,7 +291,7 @@ function makePageContext(documentId, pon, pageIndex, frame, transform, getRect,
271
291
  },
272
292
  toClientRect: (rect) => {
273
293
  const r = getRect();
274
- const v = transform.pageToViewRect(rect);
294
+ const v = transform.contentToViewRect(rect);
275
295
  return {
276
296
  x: r.left + v.x,
277
297
  y: r.top + v.y,
@@ -282,6 +302,6 @@ function makePageContext(documentId, pon, pageIndex, frame, transform, getRect,
282
302
  };
283
303
  }
284
304
  //#endregion
285
- export { DocumentGate, DocumentScope, EmbedPDF, PageProvider, Viewer, makePageContext, shallowArray, useActiveDocumentId, useCapability, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
305
+ export { DocumentGate, DocumentScope, EmbedPDF, PageProvider, Viewer, makePageContext, shallowArray, useActiveDocumentId, useCapability, useCapabilityEvent, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector };
286
306
 
287
307
  //# sourceMappingURL=runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","names":[],"sources":["../src/runtime.tsx"],"sourcesContent":["/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document), and provides the page coordinate context. Every plugin and layer rides\n * on this — there is no per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\nimport * as React from 'react';\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createKernel, docInfoListEquals } from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n Engine,\n EngineFactory,\n InitialDocument,\n Kernel,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — NOT from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageFrame, PageTransform, Point, Rect } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const k = useContext(KernelCtx);\n if (!k) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return k;\n}\n\nexport const shallowArray = <T,>(a: readonly T[], b: readonly T[]): boolean =>\n a === b || (a.length === b.length && a.every((x, i) => x === b[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (k: Kernel) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((k) => k.documents.activeId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useContext(DocumentScopeCtx);\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has NO document (empty workspace, docs still opening). */\n fallback?: React.ReactNode;\n children: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a READY document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives OUTSIDE the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it, or reads through `useOptionalSelector`.\n * A `loading`/`locked`/`error` tab renders `fallback` — so the gate's\n * fallback doubles as the per-tab boot state; richer chrome (a password\n * prompt, an error pane) branches on `useDocumentStatus()` beside the gate.\n * Sibling of <DocumentScope>, which picks WHICH document; this one handles\n * WHETHER.\n */\nexport function DocumentGate({ fallback = null, children }: DocumentGateProps) {\n const docId = useDocumentId();\n const ready = useKernelValue((k) => (docId ? k.documents.get(docId)?.status === 'ready' : false));\n return <>{ready ? children : fallback}</>;\n}\n\n/** Lifecycle status of this subtree's document (loading/locked/ready/error),\n * or null with no document. The password prompt and error panes key off it. */\nexport function useDocumentStatus() {\n const docId = useDocumentId();\n return useKernelValue((k) => (docId ? (k.documents.get(docId)?.status ?? null) : null));\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a REACTIVE read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible. Fail-fast: while unresolvable, this re-runs the\n * strict resolver so the kernel's truthful reason (`no capability` / `no\n * document` / `document is loading|locked`) is what throws.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const scoped = useContext(DocumentScopeCtx);\n const cap = useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n return cap ?? kernel.capability(token, scoped ?? undefined);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useContext(DocumentScopeCtx);\n return useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n}\n\n/** Subscribe to a selector over a (document-resolved) capability. */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(cap);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n * `useSelector` stays strict (fail-fast) for code that KNOWS a document exists\n * — e.g. anything inside a <DocumentGate>.\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n fallback: R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (cap === null) {\n next = fallback;\n } else {\n try {\n next = select(cap);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/** The document registry (open/close/active/list), reactive. */\nexport function useDocuments() {\n const kernel = useKernel();\n const docs = useKernelValue((k) => k.documents.list(), docInfoListEquals);\n const activeId = useActiveDocumentId();\n return {\n docs,\n activeId,\n open: kernel.documents.open,\n unlock: kernel.documents.unlock,\n close: kernel.documents.close,\n setActive: kernel.documents.setActive,\n move: kernel.documents.move,\n swap: kernel.documents.swap,\n download: kernel.documents.download,\n downloadLayer: kernel.documents.downloadLayer,\n };\n}\n\n// `InitialDocument` is the KERNEL's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the SHAPE of what you pass:\n *\n * - An **instance** (`engine={engine}`) is BORROWED: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer calls `engine.warmup?.()`\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is VIEWER-OWNED: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n children: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an EFFECT-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen) exactly as before. The one exception is the very first\n * render, which happens before the effect: it renders nothing. Children mount\n * once `start()` resolves — which never touches the engine, so the shell is\n * alive while WASM compiles or the transport connects. `initialDocuments`\n * open in the BACKGROUND and stream into the registry (`useDocuments()` is\n * reactive); per-document loading UI is the Stage's job, not a root gate.\n *\n * ENGINE OWNERSHIP. When `engine` is a thunk, the Viewer OWNS it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is BORROWED and never destroyed here; the Viewer only calls `warmup?.()` so\n * the WASM/transport boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport function Viewer({\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n children,\n}: ViewerProps) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n const warned = useRef(false);\n if (process.env.NODE_ENV !== 'production' && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so kick `warmup()`\n // to overlap the WASM/transport boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n engine.warmup?.();\n let kernel: Kernel;\n try {\n kernel = createKernel({ engine, plugins: captured.plugins });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), THEN the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n}\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends ONLY on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n */\nexport interface PageContextValue {\n documentId: string;\n /** Durable page identity (PDF object number) — use for keys / render / annotations. */\n pon: number;\n /** Display index (page N) — use for ordering / human-facing page numbers. */\n pageIndex: number;\n /**\n * Reserved chrome bands around the page (screen px per side). The page-chrome\n * slot renders into the outer box (content + frame); these thicknesses size\n * the bands — a label in the bottom band is `bottom:0; height: frame.bottom`.\n */\n frame: PageFrame;\n /**\n * The single bridge between PDF points, view px, and device px for this page.\n * Layers do ALL coordinate work through it — `pageToContent` to place content-\n * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for\n * page-relative sizing. Never re-derive `x * scale` or `* dpr`.\n */\n transform: PageTransform;\n /** Client (screen) point → PDF point — the one platform-bound hit-test. */\n toPagePoint(clientX: number, clientY: number): Point;\n /** PDF/content point → client (screen) px — the exact inverse of `toPagePoint`\n * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a\n * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */\n toClientPoint(p: Point): Point;\n /** PDF/content rect → client (screen) px AABB. Rect analog of `toClientPoint`\n * for upright viewport-space UI that frames a selected page region. */\n toClientRect(rect: Rect): Rect;\n /**\n * The page-view DEMAND for raster planning uses dependency inversion:\n * plugin-render defines the shape; the host that CREATED this\n * context fills it — as a PULL. The Stage host's getter closes over the\n * stage capability and reads `VisiblePage.visibleRect` live at call time\n * (visibility is the STAGE's data; adapters never re-derive camera math or\n * cache a copy). Three states, three meanings: a real sub-rect (visible),\n * a ZERO rect (stage host, page currently off-screen — want nothing), and\n * an undefined getter (stage-less `<PageView>` — whole page visible, which\n * a thumbnail-sized demand turns into \"never engages\" by arithmetic).\n */\n getViewDemand?: () => PageViewDemand;\n}\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const c = useContext(PageCtx);\n if (!c) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return c;\n}\n\nexport function makePageContext(\n documentId: string,\n pon: number,\n pageIndex: number,\n frame: PageFrame,\n transform: PageTransform,\n getRect: () => DOMRect,\n getViewDemand?: () => PageViewDemand,\n): PageContextValue {\n return {\n documentId,\n pon,\n pageIndex,\n frame,\n transform,\n ...(getViewDemand ? { getViewDemand } : {}),\n toPagePoint: (cx, cy) => {\n // `getRect()` is the rotated content wrapper's axis-aligned bounding box =\n // the page's DISPLAY box on screen. Convert client → box-local view px,\n // then invert rotation + scale via the transform (verified once in geometry,\n // not re-derived per framework adapter).\n const r = getRect();\n return transform.viewToPage({ x: cx - r.left, y: cy - r.top });\n },\n toClientPoint: (p) => {\n // Exact inverse of `toPagePoint`: page/content point → display-box view px\n // (rotation applied by the transform), offset by the same live display-box\n // origin. So the two can never drift, in either <Stage> or <PageView>.\n const r = getRect();\n const v = transform.pageToView(p);\n return { x: r.left + v.x, y: r.top + v.y };\n },\n toClientRect: (rect) => {\n const r = getRect();\n const v = transform.pageToViewRect(rect);\n return { x: r.left + v.x, y: r.top + v.y, width: v.width, height: v.height };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../src/runtime.tsx"],"sourcesContent":["/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document), and provides the page coordinate context. Every plugin and layer rides\n * on this — there is no per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\nimport * as React from 'react';\nimport {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createKernel, docInfoListEquals } from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n Engine,\n EngineFactory,\n EventHook,\n InitialDocument,\n Kernel,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — NOT from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageFrame, PageTransform, Point, Rect } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render/contract';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const k = useContext(KernelCtx);\n if (!k) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return k;\n}\n\nexport const shallowArray = <T,>(a: readonly T[], b: readonly T[]): boolean =>\n a === b || (a.length === b.length && a.every((x, i) => x === b[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (k: Kernel) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((k) => k.documents.activeId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useContext(DocumentScopeCtx);\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has NO document (empty workspace, docs still opening). */\n fallback?: React.ReactNode;\n children: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a READY document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives OUTSIDE the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it, or reads through `useOptionalSelector`.\n * A `loading`/`locked`/`error` tab renders `fallback` — so the gate's\n * fallback doubles as the per-tab boot state; richer chrome (a password\n * prompt, an error pane) branches on `useDocumentStatus()` beside the gate.\n * Sibling of <DocumentScope>, which picks WHICH document; this one handles\n * WHETHER.\n */\nexport function DocumentGate({ fallback = null, children }: DocumentGateProps) {\n const docId = useDocumentId();\n const ready = useKernelValue((k) => (docId ? k.documents.get(docId)?.status === 'ready' : false));\n return <>{ready ? children : fallback}</>;\n}\n\n/** Lifecycle status of this subtree's document (loading/locked/ready/error),\n * or null with no document. The password prompt and error panes key off it. */\nexport function useDocumentStatus() {\n const docId = useDocumentId();\n return useKernelValue((k) => (docId ? (k.documents.get(docId)?.status ?? null) : null));\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a REACTIVE read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible. Fail-fast: while unresolvable, this re-runs the\n * strict resolver so the kernel's truthful reason (`no capability` / `no\n * document` / `document is loading|locked`) is what throws.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const scoped = useContext(DocumentScopeCtx);\n const cap = useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n return cap ?? kernel.capability(token, scoped ?? undefined);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useContext(DocumentScopeCtx);\n return useKernelValue((k) => k.tryCapability(token, scoped ?? undefined));\n}\n\n/** Subscribe to a selector over a (document-resolved) capability. */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(cap);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n * `useSelector` stays strict (fail-fast) for code that KNOWS a document exists\n * — e.g. anything inside a <DocumentGate>.\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (cap: C) => R,\n fallback: R,\n isEqual: (a: R, b: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const cap = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (cap === null) {\n next = fallback;\n } else {\n try {\n next = select(cap);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Subscribe to a capability's {@link EventHook} for the mounted lifetime —\n * `useCapabilityEvent(ActionsToken, (c) => c.onAction, handler)`. Events\n * carry occurrences, never state (a late subscriber that needs the current\n * value uses `useSelector`). The handler rides a ref, so a fresh closure per\n * render never resubscribes. Null-safe: no plugin/document → no subscription.\n */\nexport function useCapabilityEvent<C, T>(\n token: CapabilityToken<C>,\n select: (cap: C) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n const cap = useOptionalCapability(token);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n const selectRef = useRef(select);\n selectRef.current = select;\n useEffect(() => {\n if (!cap) return;\n return selectRef.current(cap)((event) => handlerRef.current(event));\n }, [cap]);\n}\n\n/** The document registry (open/close/active/list), reactive. */\nexport function useDocuments() {\n const kernel = useKernel();\n const docs = useKernelValue((k) => k.documents.list(), docInfoListEquals);\n const activeId = useActiveDocumentId();\n return {\n docs,\n activeId,\n open: kernel.documents.open,\n unlock: kernel.documents.unlock,\n close: kernel.documents.close,\n setActive: kernel.documents.setActive,\n move: kernel.documents.move,\n swap: kernel.documents.swap,\n download: kernel.documents.download,\n downloadLayer: kernel.documents.downloadLayer,\n };\n}\n\n// `InitialDocument` is the KERNEL's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the SHAPE of what you pass:\n *\n * - An **instance** (`engine={engine}`) is BORROWED: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer calls `engine.warmup?.()`\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is VIEWER-OWNED: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n children: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an EFFECT-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen) exactly as before. The one exception is the very first\n * render, which happens before the effect: it renders nothing. Children mount\n * once `start()` resolves — which never touches the engine, so the shell is\n * alive while WASM compiles or the transport connects. `initialDocuments`\n * open in the BACKGROUND and stream into the registry (`useDocuments()` is\n * reactive); per-document loading UI is the Stage's job, not a root gate.\n *\n * ENGINE OWNERSHIP. When `engine` is a thunk, the Viewer OWNS it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is BORROWED and never destroyed here; the Viewer only calls `warmup?.()` so\n * the WASM/transport boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport function Viewer({\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n children,\n}: ViewerProps) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n const warned = useRef(false);\n if (process.env.NODE_ENV !== 'production' && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so kick `warmup()`\n // to overlap the WASM/transport boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n engine.warmup?.();\n let kernel: Kernel;\n try {\n kernel = createKernel({ engine, plugins: captured.plugins });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), THEN the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n}\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends ONLY on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n */\nexport interface PageContextValue {\n documentId: string;\n /** Durable page identity (PDF object number) — use for keys / render / annotations. */\n pon: number;\n /** Display index (page N) — use for ordering / human-facing page numbers. */\n pageIndex: number;\n /**\n * Reserved chrome bands around the page (screen px per side). The page-chrome\n * slot renders into the outer box (content + frame); these thicknesses size\n * the bands — a label in the bottom band is `bottom:0; height: frame.bottom`.\n */\n frame: PageFrame;\n /**\n * The single bridge between PDF points, view px, and device px for this page.\n * Layers do ALL coordinate work through it — `toPixels` to place content-\n * space overlays, `renderScale`/`deviceWidth` to render, `contentWidth` for\n * page-relative sizing. Never re-derive `x * scale` or `* dpr`.\n */\n transform: PageTransform;\n /** Client (screen) point → the viewer's coordinates (content point) — the\n * one platform-bound hit-test. */\n toContentPoint(clientX: number, clientY: number): Point;\n /** Content point → client (screen) px — the exact inverse of `toContentPoint`\n * (rotation applied). Lets viewport-space UI (e.g. a selection menu) anchor to a\n * page point WITHOUT a Stage camera, so it works the same in `<PageView>`. */\n toClientPoint(p: Point): Point;\n /** Content rect → client (screen) px AABB. Rect analog of `toClientPoint`\n * for upright viewport-space UI that frames a selected page region. */\n toClientRect(rect: Rect): Rect;\n /**\n * The page-view DEMAND for raster planning uses dependency inversion:\n * plugin-render defines the shape; the host that CREATED this\n * context fills it — as a PULL. The Stage host's getter closes over the\n * stage capability and reads `VisiblePage.visibleRect` live at call time\n * (visibility is the STAGE's data; adapters never re-derive camera math or\n * cache a copy). Three states, three meanings: a real sub-rect (visible),\n * a ZERO rect (stage host, page currently off-screen — want nothing), and\n * an undefined getter (stage-less `<PageView>` — whole page visible, which\n * a thumbnail-sized demand turns into \"never engages\" by arithmetic).\n */\n getViewDemand?: () => PageViewDemand;\n /**\n * The hosting VIEW's identity — the stage lens id (`stage.lensId()`) or a\n * per-instance PageView id. IDENTITY, not an option: per-view raster\n * planning (tiles) keys its state by this, so two views showing the SAME\n * page never fight over one plan (a thumbnail rail's never-engaging demand\n * must not disturb the main view's tiles). Every page context host must\n * say which view it is.\n */\n view: string;\n}\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const c = useContext(PageCtx);\n if (!c) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return c;\n}\n\nexport function makePageContext(\n documentId: string,\n view: string,\n pon: number,\n pageIndex: number,\n frame: PageFrame,\n transform: PageTransform,\n getRect: () => DOMRect,\n getViewDemand?: () => PageViewDemand,\n): PageContextValue {\n return {\n documentId,\n view,\n pon,\n pageIndex,\n frame,\n transform,\n ...(getViewDemand ? { getViewDemand } : {}),\n toContentPoint: (cx, cy) => {\n // `getRect()` is the rotated content wrapper's axis-aligned bounding box =\n // the page's DISPLAY box on screen. Convert client → box-local view px,\n // then invert rotation + scale via the transform (verified once in geometry,\n // not re-derived per framework adapter).\n const r = getRect();\n return transform.viewToContent({ x: cx - r.left, y: cy - r.top });\n },\n toClientPoint: (p) => {\n // Exact inverse of `toContentPoint`: page/content point → display-box view px\n // (rotation applied by the transform), offset by the same live display-box\n // origin. So the two can never drift, in either <Stage> or <PageView>.\n const r = getRect();\n const v = transform.contentToView(p);\n return { x: r.left + v.x, y: r.top + v.y };\n },\n toClientRect: (rect) => {\n const r = getRect();\n const v = transform.contentToViewRect(rect);\n return { x: r.left + v.x, y: r.top + v.y, width: v.width, height: v.height };\n },\n };\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ 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_plugin_stage = require("@embedpdf/plugin-stage");
7
+ let _embedpdf_plugin_stage_contract = require("@embedpdf/plugin-stage/contract");
8
8
  //#region src/scrollbar.tsx
9
9
  /**
10
10
  * Headless scrollbar for a Stage lens — a pure view of `stage.scrollMetrics()`.
@@ -32,7 +32,7 @@ let _embedpdf_plugin_stage = require("@embedpdf/plugin-stage");
32
32
  */
33
33
  /** Live scroll metrics for a stage lens (reference-stable; see
34
34
  * `StageCapability.scrollMetrics`). The raw material for custom scroll UI. */
35
- function useScrollMetrics(token = _embedpdf_plugin_stage.StageToken) {
35
+ function useScrollMetrics(token = _embedpdf_plugin_stage_contract.StageToken) {
36
36
  return require_runtime.useSelector(token, (c) => c.scrollMetrics());
37
37
  }
38
38
  const clamp01 = (v) => Math.min(1, Math.max(0, v));
@@ -62,7 +62,7 @@ const geometry = (m, vertical, trackPx, minThumbSize) => {
62
62
  thumbPos: maxOffset > 0 ? offset / maxOffset * travel : 0
63
63
  };
64
64
  };
65
- function Scrollbar({ axis, token = _embedpdf_plugin_stage.StageToken, autoHide = 1200, minThumbSize = 24, trackPress = "page", className, style, thumbClassName, thumbStyle }) {
65
+ function Scrollbar({ axis, token = _embedpdf_plugin_stage_contract.StageToken, autoHide = 1200, minThumbSize = 24, trackPress = "page", className, style, thumbClassName, thumbStyle }) {
66
66
  const stage = require_runtime.useCapability(token);
67
67
  const m = useScrollMetrics(token);
68
68
  const vertical = axis === "y";