@embedpdf/react 3.0.0-next.5 → 3.0.0-next.7

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 (43) hide show
  1. package/dist/annotation.d.cts +1 -1
  2. package/dist/annotation.d.ts +1 -1
  3. package/dist/index.cjs +14 -2
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +4 -4
  7. package/dist/interaction.cjs +14 -1
  8. package/dist/interaction.cjs.map +1 -1
  9. package/dist/interaction.d.cts +2 -2
  10. package/dist/interaction.d.ts +2 -2
  11. package/dist/interaction.js +4 -3
  12. package/dist/interaction.js.map +1 -1
  13. package/dist/page-view.cjs +5 -3
  14. package/dist/page-view.cjs.map +1 -1
  15. package/dist/page-view.js +6 -4
  16. package/dist/page-view.js.map +1 -1
  17. package/dist/render.cjs +67 -50
  18. package/dist/render.cjs.map +1 -1
  19. package/dist/render.d.cts +6 -19
  20. package/dist/render.d.ts +6 -19
  21. package/dist/render.js +70 -52
  22. package/dist/render.js.map +1 -1
  23. package/dist/{runtime-CMxIuF2O.d.cts → runtime-Bh_9OtWo.d.ts} +12 -3
  24. package/dist/{runtime-DV98q7I0.d.ts → runtime-D4TXEICX.d.cts} +12 -3
  25. package/dist/runtime.cjs +2 -1
  26. package/dist/runtime.cjs.map +1 -1
  27. package/dist/runtime.d.cts +1 -1
  28. package/dist/runtime.d.ts +1 -1
  29. package/dist/runtime.js +2 -1
  30. package/dist/runtime.js.map +1 -1
  31. package/dist/selection.cjs +144 -0
  32. package/dist/selection.cjs.map +1 -1
  33. package/dist/selection.d.cts +26 -1
  34. package/dist/selection.d.ts +26 -1
  35. package/dist/selection.js +149 -6
  36. package/dist/selection.js.map +1 -1
  37. package/dist/stage.cjs +14 -145
  38. package/dist/stage.cjs.map +1 -1
  39. package/dist/stage.d.cts +18 -5
  40. package/dist/stage.d.ts +18 -5
  41. package/dist/stage.js +15 -146
  42. package/dist/stage.js.map +1 -1
  43. package/package.json +23 -23
@@ -1,5 +1,5 @@
1
1
  import { t as AnnotationDTO } from "./AnnotationListSnapshot-CHjkcIaL-CMHGL8-D.cjs";
2
- import { o as PageContextValue } from "./runtime-CMxIuF2O.cjs";
2
+ import { o as PageContextValue } from "./runtime-D4TXEICX.cjs";
3
3
  import * as React from "react";
4
4
  import { AnnotationFlags, AnnotationProps, AnnotationProps as AnnotationProps$1, AnnotationPropsPatch, Border, CreationDraftAnchor, CreationDraftAnchor as CreationDraftAnchor$1, Geom, LineEnding, LineEndings, PropKey, PropSpec, Rect, RenderItem, RenderItem as RenderItem$1, Style, TextAlign, TextStyle, Vec } from "@embedpdf/core-annotation";
5
5
  import { Behavior, FilePickerProvider, SelectionFlags, SelectionFlags as SelectionFlags$1, SelectionProps, SelectionProps as SelectionProps$1 } from "@embedpdf/plugin-annotation";
@@ -1,5 +1,5 @@
1
1
  import { t as AnnotationDTO } from "./AnnotationListSnapshot-CHjkcIaL-CMHGL8-D.js";
2
- import { o as PageContextValue } from "./runtime-DV98q7I0.js";
2
+ import { o as PageContextValue } from "./runtime-Bh_9OtWo.js";
3
3
  import * as React from "react";
4
4
  import { Behavior, FilePickerProvider, SelectionFlags, SelectionFlags as SelectionFlags$1, SelectionProps, SelectionProps as SelectionProps$1 } from "@embedpdf/plugin-annotation";
5
5
  import { AnnotationFlags, AnnotationProps, AnnotationProps as AnnotationProps$1, AnnotationPropsPatch, Border, CreationDraftAnchor, CreationDraftAnchor as CreationDraftAnchor$1, Geom, LineEnding, LineEndings, PropKey, PropSpec, Rect, RenderItem, RenderItem as RenderItem$1, Style, TextAlign, TextStyle, Vec } from "@embedpdf/core-annotation";
package/dist/index.cjs CHANGED
@@ -2,11 +2,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./rolldown-runtime-DDXJien1.cjs");
3
3
  const require_runtime = require("./runtime.cjs");
4
4
  const require_anchored = require("./anchored.cjs");
5
- const require_interaction = require("./interaction.cjs");
6
5
  const require_stage = require("./stage.cjs");
7
6
  const require_scrollbar = require("./scrollbar.cjs");
8
7
  const require_page_view = require("./page-view.cjs");
9
8
  const require_render = require("./render.cjs");
9
+ const require_interaction = require("./interaction.cjs");
10
10
  const require_selection = require("./selection.cjs");
11
11
  const require_annotation = require("./annotation.cjs");
12
12
  const require_annotation_menu = require("./annotation-menu.cjs");
@@ -40,10 +40,10 @@ exports.RenderLayer = require_render.RenderLayer;
40
40
  exports.Scrollbar = require_scrollbar.Scrollbar;
41
41
  exports.SearchLayer = require_search.SearchLayer;
42
42
  exports.SelectionClipboard = require_selection.SelectionClipboard;
43
+ exports.SelectionHandles = require_selection.SelectionHandles;
43
44
  exports.SelectionLayer = require_selection.SelectionLayer;
44
45
  exports.SelectionMenu = require_selection.SelectionMenu;
45
46
  exports.Stage = require_stage.Stage;
46
- exports.TileLayer = require_render.TileLayer;
47
47
  exports.Toolbar = require_toolbar.Toolbar;
48
48
  exports.Viewer = require_runtime.Viewer;
49
49
  Object.defineProperty(exports, "copySelection", {
@@ -114,6 +114,18 @@ exports.useTool = require_interaction.useTool;
114
114
  exports.useToolCursor = require_interaction.useToolCursor;
115
115
  exports.useViews = require_views.useViews;
116
116
  exports.useZoom = require_stage.useZoom;
117
+ Object.defineProperty(exports, "vibrationFeedback", {
118
+ enumerable: true,
119
+ get: function() {
120
+ return _embedpdf_web.vibrationFeedback;
121
+ }
122
+ });
123
+ Object.defineProperty(exports, "wkFeedback", {
124
+ enumerable: true,
125
+ get: function() {
126
+ return _embedpdf_web.wkFeedback;
127
+ }
128
+ });
117
129
  var _embedpdf_core = require("@embedpdf/core");
118
130
  Object.keys(_embedpdf_core).forEach(function(k) {
119
131
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AnchorTarget, Anchored, AnchoredPlacement, AnchoredProps, ProjectorBinding, ProjectorProvider, ViewProjector, useProjectorBinding } from "./anchored.cjs";
2
2
  import { AnnotationDraftMenu, AnnotationDraftMenuProps, AnnotationMenu, AnnotationMenuProps } from "./annotation-menu.cjs";
3
- 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";
3
+ 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-D4TXEICX.cjs";
4
4
  import { AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationProps, AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, Border, CreationDraftAnchor, Geom, LineEnding, LineEndings, PropKey, PropSpec, RenderItem, SelectionAnchor, SelectionFlags, SelectionProps, Style, TextAlign, TextStyle, filePickerProvider, sameAnchor, sameCreationDraftAnchor, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useFilePickerProvider, useSelectionFlags, useSelectionProps } from "./annotation.cjs";
5
5
  import { isMacPlatform, useCommand, useCommandShortcuts, useCommands } from "./commands.cjs";
6
6
  import { FormLayer, FormScriptingUiHandlers, useForm, useFormField, useFormScriptingProvider } from "./form.cjs";
@@ -8,9 +8,9 @@ import { useI18n, useLocale, useT } from "./i18n.cjs";
8
8
  import { Stage, StageProps, StageTokenProp, useLayout, usePageList, usePages, useStage, useStageSettings, useZoom } from "./stage.cjs";
9
9
  import { Scrollbar, ScrollbarAxis, ScrollbarProps, useScrollMetrics } from "./scrollbar.cjs";
10
10
  import { PageView, PageViewProps } from "./page-view.cjs";
11
- import { RenderLayer, RenderLayerProps, TileLayer, TileLayerProps } from "./render.cjs";
12
- import { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor } from "./interaction.cjs";
13
- import { SelectionClipboard, SelectionClipboardProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection } from "./selection.cjs";
11
+ import { RenderLayer, RenderLayerProps } from "./render.cjs";
12
+ import { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback } from "./interaction.cjs";
13
+ import { SelectionClipboard, SelectionClipboardProps, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection } from "./selection.cjs";
14
14
  import { LinkLayer, LinkLayerProps, useLink } from "./link.cjs";
15
15
  import { UseViews, useViews } from "./views.cjs";
16
16
  import { usePageEditor } from "./page-edit.cjs";
@@ -35,4 +35,4 @@ export * from "@embedpdf/plugin-redaction";
35
35
  export * from "@embedpdf/plugin-stamp";
36
36
  export * from "@embedpdf/plugin-shell";
37
37
  export * from "@embedpdf/core-ui";
38
- export { type AnchorTarget, Anchored, type AnchoredPlacement, AnchoredProps, AnnotationDraftMenu, AnnotationDraftMenuProps, type AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationMenu, AnnotationMenuProps, type AnnotationProps, type AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, type Border, CollapsedGroupView, type CreationDraftAnchor, CustomSlotCtx, DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, FormLayer, type FormScriptingUiHandlers, type Geom, GroupDisclosureView, type LineEnding, type LineEndings, LinkLayer, LinkLayerProps, MenusHandle, OverflowMenuView, PageContextValue, PagePointerSource, PageProvider, PageView, PageViewProps, ProjectorBinding, ProjectorProvider, type PropKey, type PropSpec, type RenderItem, RenderLayer, RenderLayerProps, Scrollbar, ScrollbarAxis, ScrollbarProps, SearchLayer, SelectionAnchor, SelectionClipboard, SelectionClipboardProps, type SelectionFlags, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, type SelectionProps, Stage, StageProps, StageTokenProp, StripView, StripViewGroup, type Style, SurfaceHandle, type TextAlign, type TextStyle, TileLayer, TileLayerProps, ToolCursorImage, ToolCursorSpec, Toolbar, ToolbarProps, UseViews, type ViewProjector, Viewer, ViewerProps, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom };
38
+ export { type AnchorTarget, Anchored, type AnchoredPlacement, AnchoredProps, AnnotationDraftMenu, AnnotationDraftMenuProps, type AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationMenu, AnnotationMenuProps, type AnnotationProps, type AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, type Border, CollapsedGroupView, type CreationDraftAnchor, CustomSlotCtx, DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, FormLayer, type FormScriptingUiHandlers, type Geom, GroupDisclosureView, type LineEnding, type LineEndings, LinkLayer, LinkLayerProps, MenusHandle, OverflowMenuView, PageContextValue, PagePointerSource, PageProvider, PageView, PageViewProps, ProjectorBinding, ProjectorProvider, type PropKey, type PropSpec, type RenderItem, RenderLayer, RenderLayerProps, Scrollbar, ScrollbarAxis, ScrollbarProps, SearchLayer, SelectionAnchor, SelectionClipboard, SelectionClipboardProps, type SelectionFlags, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, type SelectionProps, Stage, StageProps, StageTokenProp, StripView, StripViewGroup, type Style, SurfaceHandle, type TextAlign, type TextStyle, ToolCursorImage, ToolCursorSpec, Toolbar, ToolbarProps, UseViews, type ViewProjector, Viewer, ViewerProps, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom, vibrationFeedback, wkFeedback };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AnchorTarget, Anchored, AnchoredPlacement, AnchoredProps, ProjectorBinding, ProjectorProvider, ViewProjector, useProjectorBinding } from "./anchored.js";
2
2
  import { AnnotationDraftMenu, AnnotationDraftMenuProps, AnnotationMenu, AnnotationMenuProps } from "./annotation-menu.js";
3
- 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";
3
+ 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-Bh_9OtWo.js";
4
4
  import { AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationProps, AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, Border, CreationDraftAnchor, Geom, LineEnding, LineEndings, PropKey, PropSpec, RenderItem, SelectionAnchor, SelectionFlags, SelectionProps, Style, TextAlign, TextStyle, filePickerProvider, sameAnchor, sameCreationDraftAnchor, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useFilePickerProvider, useSelectionFlags, useSelectionProps } from "./annotation.js";
5
5
  import { isMacPlatform, useCommand, useCommandShortcuts, useCommands } from "./commands.js";
6
6
  import { FormLayer, FormScriptingUiHandlers, useForm, useFormField, useFormScriptingProvider } from "./form.js";
@@ -8,9 +8,9 @@ import { useI18n, useLocale, useT } from "./i18n.js";
8
8
  import { Stage, StageProps, StageTokenProp, useLayout, usePageList, usePages, useStage, useStageSettings, useZoom } from "./stage.js";
9
9
  import { Scrollbar, ScrollbarAxis, ScrollbarProps, useScrollMetrics } from "./scrollbar.js";
10
10
  import { PageView, PageViewProps } from "./page-view.js";
11
- import { RenderLayer, RenderLayerProps, TileLayer, TileLayerProps } from "./render.js";
12
- import { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor } from "./interaction.js";
13
- import { SelectionClipboard, SelectionClipboardProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection } from "./selection.js";
11
+ import { RenderLayer, RenderLayerProps } from "./render.js";
12
+ import { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback } from "./interaction.js";
13
+ import { SelectionClipboard, SelectionClipboardProps, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, copySelection, useSelection } from "./selection.js";
14
14
  import { LinkLayer, LinkLayerProps, useLink } from "./link.js";
15
15
  import { UseViews, useViews } from "./views.js";
16
16
  import { usePageEditor } from "./page-edit.js";
@@ -35,4 +35,4 @@ export * from "@embedpdf/plugin-stamp";
35
35
  export * from "@embedpdf/plugin-commands";
36
36
  export * from "@embedpdf/plugin-shell";
37
37
  export * from "@embedpdf/core-ui";
38
- export { type AnchorTarget, Anchored, type AnchoredPlacement, AnchoredProps, AnnotationDraftMenu, AnnotationDraftMenuProps, type AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationMenu, AnnotationMenuProps, type AnnotationProps, type AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, type Border, CollapsedGroupView, type CreationDraftAnchor, CustomSlotCtx, DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, FormLayer, type FormScriptingUiHandlers, type Geom, GroupDisclosureView, type LineEnding, type LineEndings, LinkLayer, LinkLayerProps, MenusHandle, OverflowMenuView, PageContextValue, PagePointerSource, PageProvider, PageView, PageViewProps, ProjectorBinding, ProjectorProvider, type PropKey, type PropSpec, type RenderItem, RenderLayer, RenderLayerProps, Scrollbar, ScrollbarAxis, ScrollbarProps, SearchLayer, SelectionAnchor, SelectionClipboard, SelectionClipboardProps, type SelectionFlags, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, type SelectionProps, Stage, StageProps, StageTokenProp, StripView, StripViewGroup, type Style, SurfaceHandle, type TextAlign, type TextStyle, TileLayer, TileLayerProps, ToolCursorImage, ToolCursorSpec, Toolbar, ToolbarProps, UseViews, type ViewProjector, Viewer, ViewerProps, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom };
38
+ export { type AnchorTarget, Anchored, type AnchoredPlacement, AnchoredProps, AnnotationDraftMenu, AnnotationDraftMenuProps, type AnnotationFlags, AnnotationLayer, AnnotationLayerProps, AnnotationMenu, AnnotationMenuProps, type AnnotationProps, type AnnotationPropsPatch, AnnotationRenderer, AnnotationRendererProps, type Border, CollapsedGroupView, type CreationDraftAnchor, CustomSlotCtx, DocumentGate, DocumentGateProps, DocumentScope, DocumentScopeProps, EmbedPDF, FormLayer, type FormScriptingUiHandlers, type Geom, GroupDisclosureView, type LineEnding, type LineEndings, LinkLayer, LinkLayerProps, MenusHandle, OverflowMenuView, PageContextValue, PagePointerSource, PageProvider, PageView, PageViewProps, ProjectorBinding, ProjectorProvider, type PropKey, type PropSpec, type RenderItem, RenderLayer, RenderLayerProps, Scrollbar, ScrollbarAxis, ScrollbarProps, SearchLayer, SelectionAnchor, SelectionClipboard, SelectionClipboardProps, type SelectionFlags, SelectionHandles, SelectionHandlesProps, SelectionLayer, SelectionLayerProps, SelectionMenu, SelectionMenuProps, type SelectionProps, Stage, StageProps, StageTokenProp, StripView, StripViewGroup, type Style, SurfaceHandle, type TextAlign, type TextStyle, ToolCursorImage, ToolCursorSpec, Toolbar, ToolbarProps, UseViews, type ViewProjector, Viewer, ViewerProps, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom, vibrationFeedback, wkFeedback };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { DocumentGate, DocumentScope, EmbedPDF, PageProvider, Viewer, makePageContext, shallowArray, useActiveDocumentId, useCapability, useDocumentId, useDocumentStatus, useDocuments, useKernel, useKernelValue, useOptionalCapability, useOptionalSelector, usePage, useSelector } from "./runtime.js";
2
2
  import { Anchored, ProjectorProvider, useProjectorBinding } from "./anchored.js";
3
- import { PagePointerSource, createClickCounter, useTool, useToolCursor } from "./interaction.js";
4
3
  import { Stage, useLayout, usePageList, usePages, useStage, useStageSettings, useZoom } from "./stage.js";
5
4
  import { Scrollbar, useScrollMetrics } from "./scrollbar.js";
6
5
  import { PageView } from "./page-view.js";
7
- import { RenderLayer, TileLayer } from "./render.js";
8
- import { SelectionClipboard, SelectionLayer, SelectionMenu, copySelection, useSelection } from "./selection.js";
6
+ import { RenderLayer } from "./render.js";
7
+ import { PagePointerSource, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback } from "./interaction.js";
8
+ import { SelectionClipboard, SelectionHandles, SelectionLayer, SelectionMenu, copySelection, useSelection } from "./selection.js";
9
9
  import { AnnotationLayer, filePickerProvider, sameAnchor, sameCreationDraftAnchor, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useFilePickerProvider, useSelectionFlags, useSelectionProps } from "./annotation.js";
10
10
  import { AnnotationDraftMenu, AnnotationMenu } from "./annotation-menu.js";
11
11
  import { LinkLayer, useLink } from "./link.js";
@@ -35,4 +35,4 @@ export * from "@embedpdf/plugin-stamp";
35
35
  export * from "@embedpdf/plugin-commands";
36
36
  export * from "@embedpdf/plugin-shell";
37
37
  export * from "@embedpdf/core-ui";
38
- export { Anchored, AnnotationDraftMenu, AnnotationLayer, AnnotationMenu, DocumentGate, DocumentScope, EmbedPDF, FormLayer, LinkLayer, PagePointerSource, PageProvider, PageView, ProjectorProvider, RenderLayer, Scrollbar, SearchLayer, SelectionClipboard, SelectionLayer, SelectionMenu, Stage, TileLayer, Toolbar, Viewer, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom };
38
+ export { Anchored, AnnotationDraftMenu, AnnotationLayer, AnnotationMenu, DocumentGate, DocumentScope, EmbedPDF, FormLayer, LinkLayer, PagePointerSource, PageProvider, PageView, ProjectorProvider, RenderLayer, Scrollbar, SearchLayer, SelectionClipboard, SelectionHandles, SelectionLayer, SelectionMenu, Stage, Toolbar, Viewer, copySelection, createClickCounter, filePickerProvider, isMacPlatform, makePageContext, sameAnchor, sameCreationDraftAnchor, shallowArray, useActiveDocumentId, useAnnotation, useAnnotationDefaults, useAnnotationSelected, useAnnotationSelection, useArmStampAsset, useCapability, useCommand, useCommandShortcuts, useCommands, useDocumentId, useDocumentStatus, useDocuments, useFilePickerProvider, useForm, useFormField, useFormScriptingProvider, useI18n, useKernel, useKernelValue, useLayout, useLink, useLocale, useMenus, useMetadata, useOptionalCapability, useOptionalSelector, usePage, usePageEditor, usePageList, usePages, usePendingRedactions, useProjectorBinding, useRedaction, useScrollMetrics, useSearch, useSearchState, useSelection, useSelectionFlags, useSelectionProps, useSelector, useShell, useStage, useStageSettings, useStamp, useStampAssetPreviewUrl, useStampAssets, useStampLibraries, useStripView, useSurface, useT, useTool, useToolCursor, useViews, useZoom, vibrationFeedback, wkFeedback };
@@ -57,7 +57,8 @@ function PagePointerSource() {
57
57
  },
58
58
  project: (pon) => pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null,
59
59
  modifiers: mods(e),
60
- clickCount
60
+ clickCount,
61
+ pointerType: e.pointerType || "mouse"
61
62
  };
62
63
  };
63
64
  let dragging = false;
@@ -140,6 +141,18 @@ exports.PagePointerSource = PagePointerSource;
140
141
  exports.createClickCounter = createClickCounter;
141
142
  exports.useTool = useTool;
142
143
  exports.useToolCursor = useToolCursor;
144
+ Object.defineProperty(exports, "vibrationFeedback", {
145
+ enumerable: true,
146
+ get: function() {
147
+ return _embedpdf_web.vibrationFeedback;
148
+ }
149
+ });
150
+ Object.defineProperty(exports, "wkFeedback", {
151
+ enumerable: true,
152
+ get: function() {
153
+ return _embedpdf_web.wkFeedback;
154
+ }
155
+ });
143
156
  Object.keys(_embedpdf_plugin_interaction).forEach(function(k) {
144
157
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
145
158
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.cjs","names":["usePage","useCapability","InteractionToken","useSelector"],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"interaction.cjs","names":["usePage","useCapability","InteractionToken","useSelector"],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\n// The browser feedback providers live in @embedpdf/web (the plugin is\n// DOM-free); re-exported here so app code has one import for the feature.\nexport { vibrationFeedback, wkFeedback } from '@embedpdf/web';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n pointerType: (e.pointerType || 'mouse') as PointerSample['pointerType'],\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { SvgCursorOptions } from "@embedpdf/web";
1
+ import { SvgCursorOptions, vibrationFeedback, wkFeedback } from "@embedpdf/web";
2
2
  export * from "@embedpdf/plugin-interaction";
3
3
  //#region src/interaction.d.ts
4
4
  /**
@@ -42,5 +42,5 @@ interface ToolCursorSpec {
42
42
  */
43
43
  declare function useToolCursor(spec: ToolCursorSpec | null): void;
44
44
  //#endregion
45
- export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor };
45
+ export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback };
46
46
  //# sourceMappingURL=interaction.d.cts.map
@@ -1,4 +1,4 @@
1
- import { SvgCursorOptions } from "@embedpdf/web";
1
+ import { SvgCursorOptions, vibrationFeedback, wkFeedback } from "@embedpdf/web";
2
2
  export * from "@embedpdf/plugin-interaction";
3
3
  //#region src/interaction.d.ts
4
4
  /**
@@ -42,5 +42,5 @@ interface ToolCursorSpec {
42
42
  */
43
43
  declare function useToolCursor(spec: ToolCursorSpec | null): void;
44
44
  //#endregion
45
- export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor };
45
+ export { PagePointerSource, ToolCursorImage, ToolCursorSpec, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback };
46
46
  //# sourceMappingURL=interaction.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { useCapability, usePage, useSelector } from "./runtime.js";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useRef } from "react";
4
- import { svgCursor } from "@embedpdf/web";
4
+ import { svgCursor, vibrationFeedback, wkFeedback } from "@embedpdf/web";
5
5
  import { InteractionToken } from "@embedpdf/plugin-interaction";
6
6
  export * from "@embedpdf/plugin-interaction";
7
7
  //#region src/interaction.tsx
@@ -55,7 +55,8 @@ function PagePointerSource() {
55
55
  },
56
56
  project: (pon) => pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null,
57
57
  modifiers: mods(e),
58
- clickCount
58
+ clickCount,
59
+ pointerType: e.pointerType || "mouse"
59
60
  };
60
61
  };
61
62
  let dragging = false;
@@ -134,6 +135,6 @@ function useToolCursor(spec) {
134
135
  }, [interaction, key]);
135
136
  }
136
137
  //#endregion
137
- export { PagePointerSource, createClickCounter, useTool, useToolCursor };
138
+ export { PagePointerSource, createClickCounter, useTool, useToolCursor, vibrationFeedback, wkFeedback };
138
139
 
139
140
  //# sourceMappingURL=interaction.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.js","names":[],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"interaction.js","names":[],"sources":["../src/interaction.tsx"],"sourcesContent":["/**\n * The React surface for @embedpdf/plugin-interaction.\n *\n * <PagePointerSource> is the ONE pointer listener per page: it converts events to\n * page space via PageContext.toPagePoint and forwards normalized samples to the\n * hub. It binds only to the page context, so it works identically inside a\n * virtualized <Stage> page and a standalone <PageView>. Features never attach\n * their own pointer listeners — they register handlers with the hub.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-interaction';\n// The browser feedback providers live in @embedpdf/web (the plugin is\n// DOM-free); re-exported here so app code has one import for the feature.\nexport { vibrationFeedback, wkFeedback } from '@embedpdf/web';\nimport * as React from 'react';\nimport { useEffect, useRef } from 'react';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport type { Modifiers, PointerSample } from '@embedpdf/plugin-interaction';\nimport { svgCursor } from '@embedpdf/web';\nimport type { SvgCursorOptions } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nconst mods = (e: PointerEvent): Modifiers => ({\n shift: e.shiftKey,\n alt: e.altKey,\n ctrl: e.ctrlKey,\n meta: e.metaKey,\n});\n\n/**\n * Robust multi-click counter. `pointerdown.detail` is 0/1 in several browsers, so\n * we count clicks ourselves from timing + proximity — the standard double/triple\n * detection. Input normalization belongs in the adapter; the hub/handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistPx = 6) {\n let last = 0;\n let lx = 0;\n let ly = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count = now - last <= maxGapMs && Math.hypot(x - lx, y - ly) <= maxDistPx ? count + 1 : 1;\n last = now;\n lx = x;\n ly = y;\n return count;\n };\n}\n\nexport function PagePointerSource() {\n const page = usePage();\n const interaction = useCapability(InteractionToken);\n const cursor = useSelector(InteractionToken, (c) => c.cursor());\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const clicks = createClickCounter();\n const sample = (\n phase: PointerSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n ): PointerSample => {\n const r = el.getBoundingClientRect();\n return {\n phase,\n viewport: { x: e.clientX - r.left, y: e.clientY - r.top },\n // `scale`/`rotation`/`zoom` carry the same per-page environmental\n // context the Stage source resolves via `pageAt` — read off the page\n // transform so a standalone <PageView> drives handlers identically.\n page: {\n pon: page.pon,\n point: page.toPagePoint(e.clientX, e.clientY),\n scale: page.transform.viewScale,\n rotation: page.transform.rotation,\n zoom: page.transform.zoom,\n },\n // A per-page source can only project onto its OWN page — toPagePoint is\n // already unclamped (the drag listener lives on window), so a gesture\n // anchored here keeps tracking past the page bounds.\n project: (pon) => (pon === page.pon ? page.toPagePoint(e.clientX, e.clientY) : null),\n modifiers: mods(e),\n clickCount,\n pointerType: (e.pointerType || 'mouse') as PointerSample['pointerType'],\n };\n };\n let dragging = false;\n\n const down = (e: PointerEvent) => {\n if (e.button !== 0) return;\n dragging = true;\n interaction.dispatch(sample('down', e, clicks(Date.now(), e.clientX, e.clientY)));\n };\n // hover (no gesture): drive cursor feedback only — fires from the element\n const hover = (e: PointerEvent) => {\n if (dragging) return;\n interaction.dispatch(sample('move', e));\n };\n // active drag: track on window so it survives leaving the page bounds\n const drag = (e: PointerEvent) => {\n if (!dragging) return;\n interaction.dispatch(sample('move', e));\n };\n const up = (e: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n interaction.dispatch(sample('up', e));\n };\n\n el.addEventListener('pointerdown', down);\n el.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n el.removeEventListener('pointerdown', down);\n el.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n }, [interaction, page]);\n\n // Sits on top as the page's event surface; visual layers below use pointerEvents:none.\n return <div ref={ref} style={{ position: 'absolute', inset: 0, cursor, touchAction: 'none' }} />;\n}\n\n/** Read + switch the active tool (for a toolbar). */\nexport function useTool() {\n const interaction = useCapability(InteractionToken);\n const activeToolId = useSelector(InteractionToken, (c) => c.activeToolId());\n return {\n activeToolId,\n activate: interaction.activateTool,\n tools: interaction.tools(),\n };\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS\n * cursor string ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/** What {@link useToolCursor} installs: the tool to reskin + its keyword map.\n * Apps build `svg` from the SAME icon their toolbar renders, so the cursor\n * and the button can never drift apart. */\nexport interface ToolCursorSpec {\n toolId: string;\n /** keyword → cursor: restyle the keywords this tool can show — its declared\n * base ('crosshair', 'copy') and hover claims ('text' over text) — in the\n * tool's identity. Keywords the map omits render as-is: a markup tool's\n * 'default' base stays the bare arrow, a foreign 'move' claim drops the\n * icon. An SVG value defaults its keyword `fallback` to the keyword it\n * replaces. */\n cursors: Record<string, ToolCursorImage>;\n}\n\nconst toCursor = (img: ToolCursorImage, fallback?: string): string =>\n typeof img === 'string'\n ? img\n : svgCursor(img.fallback === undefined && fallback ? { ...img, fallback } : img);\n\n/**\n * Give a tool IMAGE cursors — the armed-tool indicator. The cursor is the only\n * zero-latency pointer-locked pixel the platform has, and the hub already\n * arbitrates it: unmapped hover claims beat it over annotations/text, page\n * gaps fall back to the tool's `gapCursor`, and other UI (menus, form fill\n * controls) carries its own CSS cursor — so nothing chases the pointer in\n * DOM. `null` installs nothing. A re-render with new content rebuilds the\n * cursor (live recolor from tool defaults); unmount restores the tool's\n * declared cursors.\n */\nexport function useToolCursor(spec: ToolCursorSpec | null): void {\n const interaction = useCapability(InteractionToken);\n // Key the effect by VALUE: specs are built inline in render, and a\n // fresh-but-identical object must not thrash the skin.\n const key = spec && JSON.stringify(spec);\n const ref = useRef(spec);\n ref.current = spec;\n useEffect(() => {\n const s = ref.current;\n if (!s) return;\n interaction.setToolCursor(\n s.toolId,\n Object.fromEntries(Object.entries(s.cursors).map(([k, v]) => [k, toCursor(v, k)])),\n );\n return () => interaction.setToolCursor(s.toolId, null);\n }, [interaction, key]);\n}\n"],"mappings":"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"}
@@ -51,12 +51,14 @@ function PageView({ page, documentId, width = 240, pageFrame = _embedpdf_core_ge
51
51
  width,
52
52
  dpr
53
53
  ]);
54
- const ctx = (0, react.useMemo)(() => require_runtime.makePageContext(docId ?? "", pon, page, pageFrame, transform, () => ref.current.getBoundingClientRect()), [
54
+ const viewId = (0, react.useId)();
55
+ const ctx = (0, react.useMemo)(() => require_runtime.makePageContext(docId ?? "", `page-view:${viewId}`, pon, page, pageFrame, transform, () => ref.current.getBoundingClientRect()), [
55
56
  docId,
56
57
  pon,
57
58
  page,
58
59
  pageFrame,
59
- transform
60
+ transform,
61
+ viewId
60
62
  ]);
61
63
  const projector = (0, react.useMemo)(() => ({
62
64
  space: "client",
@@ -99,7 +101,7 @@ function PageView({ page, documentId, width = 240, pageFrame = _embedpdf_core_ge
99
101
  top: pageFrame.top,
100
102
  width: t.viewWidth,
101
103
  height: t.viewHeight,
102
- boxShadow: "0 6px 18px rgba(0,0,0,.18)"
104
+ boxShadow: "var(--epdf-page-shadow, 0 6px 18px rgba(0,0,0,.18))"
103
105
  } }),
104
106
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
105
107
  ref,
@@ -1 +1 @@
1
- {"version":3,"file":"page-view.cjs","names":["NO_FRAME","useKernel","useActiveDocumentId","makePageContext","observeClientGeometry","DocumentScope","ProjectorProvider","PageProvider"],"sources":["../src/page-view.tsx"],"sourcesContent":["/**\n * <PageView> — a single page surface with NO Stage.\n *\n * Same layers + rotation + chrome frame as a `<Stage>` page, but no\n * camera/scroll/zoom and, crucially, NO dependency on `@embedpdf/plugin-stage`.\n * It builds its own `PageTransform` from a target content width and shares the\n * exact `PageContext` seam, so every layer (RenderLayer, AnnotationLayer, …)\n * works here identically — and it provides the measured `ViewProjector`, so\n * anchored UI (`<AnnotationMenu>`, `<SelectionMenu>`) works here too, portalled\n * and clipping-immune.\n */\nimport * as React from 'react';\nimport { useMemo, useRef } from 'react';\nimport { NO_FRAME, pageTransform, type PageFrame } from '@embedpdf/core-geometry';\nimport { observeClientGeometry } from '@embedpdf/web';\nimport { ProjectorProvider, type ProjectorBinding, type ViewProjector } from './anchored';\nimport {\n DocumentScope,\n makePageContext,\n PageProvider,\n useActiveDocumentId,\n useKernel,\n} from './runtime';\n\nexport interface PageViewProps {\n page: number;\n /** Which document to show. Defaults to the active document. */\n documentId?: string;\n /** Target width for the page CONTENT; the display box is the rotated footprint. */\n width?: number;\n /** Reserved chrome bands around the page (screen px) — same model as `<Stage>`. */\n pageFrame?: PageFrame;\n /** Page-space content (rotates with the page). */\n children: React.ReactNode;\n /** Box-space chrome (label, border, …) — never rotated. Mirrors `<Stage pageChrome>`. */\n pageChrome?: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/** A single page surface with NO Stage — same layers + rotation + chrome frame,\n * no camera/scroll/zoom. */\nexport function PageView({\n page,\n documentId,\n width = 240,\n pageFrame = NO_FRAME,\n children,\n pageChrome,\n style,\n}: PageViewProps) {\n const kernel = useKernel();\n const active = useActiveDocumentId();\n const ref = useRef<HTMLDivElement>(null);\n const docId = documentId ?? active;\n const meta = docId ? kernel.getState().core.documents[docId] : undefined;\n const base = meta?.pages[page];\n const pon = base?.pageObjectNumber ?? page + 1;\n const rotation = base?.rotation ?? 0;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n // Standalone (no Stage/camera): build the page's transform from the target\n // content `width` directly. `scale` is view px per point = width / pageWidthPts.\n const transform = useMemo(\n () =>\n pageTransform({\n pageSize: base\n ? { width: base.size.width, height: base.size.height }\n : { width: 1, height: 1 },\n rotation,\n scale: base ? width / base.size.width : 1,\n // Physical 100% on the web: 1pt = 96/72 CSS px, times the page's\n // /UserUnit — so `transform.zoom` is meaningful even without a Stage\n // (a thumbnail-sized PageView reads as zoomed out, as it should).\n baseScale: (96 / 72) * (base?.userUnit ?? 1),\n dpr,\n }),\n [base?.size.width, base?.size.height, base?.userUnit, rotation, width, dpr],\n );\n const ctx = useMemo(\n () =>\n makePageContext(docId ?? '', pon, page, pageFrame, transform, () =>\n ref.current!.getBoundingClientRect(),\n ),\n [docId, pon, page, pageFrame, transform],\n );\n // The PageView's ViewProjector: no camera, so anchored UI positions by\n // MEASURING the DOM (client space → portal + position:fixed, immune to\n // ancestor overflow clipping). `toScreen` answers null until the page\n // element has committed — <Anchored> forces one post-commit pass to pick\n // it up. The binding's revision covers state-driven changes (a new\n // transform/page); `observeClientGeometry` covers the genuinely\n // browser-driven ones (document scroll, window resize) that no state\n // change announces.\n const projector = useMemo<ViewProjector>(\n () => ({\n space: 'client',\n toScreen: (p, rect) => (p === ctx.pon && ref.current ? ctx.toClientRect(rect) : null),\n toScreenPoint: (p, at) => (p === ctx.pon && ref.current ? ctx.toClientPoint(at) : null),\n viewEnv: (p) =>\n p === ctx.pon\n ? {\n scale: ctx.transform.viewScale,\n rotation: ctx.transform.rotation,\n zoom: ctx.transform.zoom,\n }\n : null,\n }),\n [ctx],\n );\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: ctx, subscribe: observeClientGeometry }),\n [projector, ctx],\n );\n if (!docId || !meta || !base) return null;\n const t = transform;\n const outerW = t.viewWidth + pageFrame.left + pageFrame.right;\n const outerH = t.viewHeight + pageFrame.top + pageFrame.bottom;\n const contentLeft = pageFrame.left + (t.viewWidth - t.contentWidth) / 2;\n const contentTop = pageFrame.top + (t.viewHeight - t.contentHeight) / 2;\n return (\n <DocumentScope id={docId}>\n <ProjectorProvider value={projectorBinding}>\n <div style={{ position: 'relative', width: outerW, height: outerH, ...style }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — transparent, axis-aligned, can't leak behind the bitmap */}\n <div\n style={{\n position: 'absolute',\n left: pageFrame.left,\n top: pageFrame.top,\n width: t.viewWidth,\n height: t.viewHeight,\n boxShadow: '0 6px 18px rgba(0,0,0,.18)',\n }}\n />\n {/* white backing + content as ONE box; rotation 0 carries no transform */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: contentLeft,\n top: contentTop,\n width: t.contentWidth,\n height: t.contentHeight,\n background: '#fff',\n transform: rotation ? `rotate(${rotation}deg)` : undefined,\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {children}\n </div>\n {pageChrome}\n </PageProvider>\n </div>\n </ProjectorProvider>\n </DocumentScope>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"page-view.cjs","names":["NO_FRAME","useKernel","useActiveDocumentId","makePageContext","observeClientGeometry","DocumentScope","ProjectorProvider","PageProvider"],"sources":["../src/page-view.tsx"],"sourcesContent":["/**\n * <PageView> — a single page surface with NO Stage.\n *\n * Same layers + rotation + chrome frame as a `<Stage>` page, but no\n * camera/scroll/zoom and, crucially, NO dependency on `@embedpdf/plugin-stage`.\n * It builds its own `PageTransform` from a target content width and shares the\n * exact `PageContext` seam, so every layer (RenderLayer, AnnotationLayer, …)\n * works here identically — and it provides the measured `ViewProjector`, so\n * anchored UI (`<AnnotationMenu>`, `<SelectionMenu>`) works here too, portalled\n * and clipping-immune.\n */\nimport * as React from 'react';\nimport { useId, useMemo, useRef } from 'react';\nimport { NO_FRAME, pageTransform, type PageFrame } from '@embedpdf/core-geometry';\nimport { observeClientGeometry } from '@embedpdf/web';\nimport { ProjectorProvider, type ProjectorBinding, type ViewProjector } from './anchored';\nimport {\n DocumentScope,\n makePageContext,\n PageProvider,\n useActiveDocumentId,\n useKernel,\n} from './runtime';\n\nexport interface PageViewProps {\n page: number;\n /** Which document to show. Defaults to the active document. */\n documentId?: string;\n /** Target width for the page CONTENT; the display box is the rotated footprint. */\n width?: number;\n /** Reserved chrome bands around the page (screen px) — same model as `<Stage>`. */\n pageFrame?: PageFrame;\n /** Page-space content (rotates with the page). */\n children: React.ReactNode;\n /** Box-space chrome (label, border, …) — never rotated. Mirrors `<Stage pageChrome>`. */\n pageChrome?: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/** A single page surface with NO Stage — same layers + rotation + chrome frame,\n * no camera/scroll/zoom. */\nexport function PageView({\n page,\n documentId,\n width = 240,\n pageFrame = NO_FRAME,\n children,\n pageChrome,\n style,\n}: PageViewProps) {\n const kernel = useKernel();\n const active = useActiveDocumentId();\n const ref = useRef<HTMLDivElement>(null);\n const docId = documentId ?? active;\n const meta = docId ? kernel.getState().core.documents[docId] : undefined;\n const base = meta?.pages[page];\n const pon = base?.pageObjectNumber ?? page + 1;\n const rotation = base?.rotation ?? 0;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n // Standalone (no Stage/camera): build the page's transform from the target\n // content `width` directly. `scale` is view px per point = width / pageWidthPts.\n const transform = useMemo(\n () =>\n pageTransform({\n pageSize: base\n ? { width: base.size.width, height: base.size.height }\n : { width: 1, height: 1 },\n rotation,\n scale: base ? width / base.size.width : 1,\n // Physical 100% on the web: 1pt = 96/72 CSS px, times the page's\n // /UserUnit — so `transform.zoom` is meaningful even without a Stage\n // (a thumbnail-sized PageView reads as zoomed out, as it should).\n baseScale: (96 / 72) * (base?.userUnit ?? 1),\n dpr,\n }),\n [base?.size.width, base?.size.height, base?.userUnit, rotation, width, dpr],\n );\n // This instance's VIEW identity: two PageViews of the same page (a compare\n // strip) must plan rasters independently, like two stage lenses do.\n const viewId = useId();\n const ctx = useMemo(\n () =>\n makePageContext(\n docId ?? '',\n `page-view:${viewId}`,\n pon,\n page,\n pageFrame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n ),\n [docId, pon, page, pageFrame, transform, viewId],\n );\n // The PageView's ViewProjector: no camera, so anchored UI positions by\n // MEASURING the DOM (client space → portal + position:fixed, immune to\n // ancestor overflow clipping). `toScreen` answers null until the page\n // element has committed — <Anchored> forces one post-commit pass to pick\n // it up. The binding's revision covers state-driven changes (a new\n // transform/page); `observeClientGeometry` covers the genuinely\n // browser-driven ones (document scroll, window resize) that no state\n // change announces.\n const projector = useMemo<ViewProjector>(\n () => ({\n space: 'client',\n toScreen: (p, rect) => (p === ctx.pon && ref.current ? ctx.toClientRect(rect) : null),\n toScreenPoint: (p, at) => (p === ctx.pon && ref.current ? ctx.toClientPoint(at) : null),\n viewEnv: (p) =>\n p === ctx.pon\n ? {\n scale: ctx.transform.viewScale,\n rotation: ctx.transform.rotation,\n zoom: ctx.transform.zoom,\n }\n : null,\n }),\n [ctx],\n );\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: ctx, subscribe: observeClientGeometry }),\n [projector, ctx],\n );\n if (!docId || !meta || !base) return null;\n const t = transform;\n const outerW = t.viewWidth + pageFrame.left + pageFrame.right;\n const outerH = t.viewHeight + pageFrame.top + pageFrame.bottom;\n const contentLeft = pageFrame.left + (t.viewWidth - t.contentWidth) / 2;\n const contentTop = pageFrame.top + (t.viewHeight - t.contentHeight) / 2;\n return (\n <DocumentScope id={docId}>\n <ProjectorProvider value={projectorBinding}>\n <div style={{ position: 'relative', width: outerW, height: outerH, ...style }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — transparent, axis-aligned, can't leak behind the bitmap */}\n <div\n style={{\n position: 'absolute',\n left: pageFrame.left,\n top: pageFrame.top,\n width: t.viewWidth,\n height: t.viewHeight,\n boxShadow: 'var(--epdf-page-shadow, 0 6px 18px rgba(0,0,0,.18))',\n }}\n />\n {/* white backing + content as ONE box; rotation 0 carries no transform */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: contentLeft,\n top: contentTop,\n width: t.contentWidth,\n height: t.contentHeight,\n background: '#fff',\n transform: rotation ? `rotate(${rotation}deg)` : undefined,\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {children}\n </div>\n {pageChrome}\n </PageProvider>\n </div>\n </ProjectorProvider>\n </DocumentScope>\n );\n}\n"],"mappings":"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"}
package/dist/page-view.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DocumentScope, PageProvider, makePageContext, useActiveDocumentId, useKernel } from "./runtime.js";
2
2
  import { ProjectorProvider } from "./anchored.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
- import { useMemo, useRef } from "react";
4
+ import { useId, useMemo, useRef } from "react";
5
5
  import { observeClientGeometry } from "@embedpdf/web";
6
6
  import { NO_FRAME, pageTransform } from "@embedpdf/core-geometry";
7
7
  //#region src/page-view.tsx
@@ -37,12 +37,14 @@ function PageView({ page, documentId, width = 240, pageFrame = NO_FRAME, childre
37
37
  width,
38
38
  dpr
39
39
  ]);
40
- const ctx = useMemo(() => makePageContext(docId ?? "", pon, page, pageFrame, transform, () => ref.current.getBoundingClientRect()), [
40
+ const viewId = useId();
41
+ const ctx = useMemo(() => makePageContext(docId ?? "", `page-view:${viewId}`, pon, page, pageFrame, transform, () => ref.current.getBoundingClientRect()), [
41
42
  docId,
42
43
  pon,
43
44
  page,
44
45
  pageFrame,
45
- transform
46
+ transform,
47
+ viewId
46
48
  ]);
47
49
  const projector = useMemo(() => ({
48
50
  space: "client",
@@ -85,7 +87,7 @@ function PageView({ page, documentId, width = 240, pageFrame = NO_FRAME, childre
85
87
  top: pageFrame.top,
86
88
  width: t.viewWidth,
87
89
  height: t.viewHeight,
88
- boxShadow: "0 6px 18px rgba(0,0,0,.18)"
90
+ boxShadow: "var(--epdf-page-shadow, 0 6px 18px rgba(0,0,0,.18))"
89
91
  } }),
90
92
  /* @__PURE__ */ jsx("div", {
91
93
  ref,
@@ -1 +1 @@
1
- {"version":3,"file":"page-view.js","names":[],"sources":["../src/page-view.tsx"],"sourcesContent":["/**\n * <PageView> — a single page surface with NO Stage.\n *\n * Same layers + rotation + chrome frame as a `<Stage>` page, but no\n * camera/scroll/zoom and, crucially, NO dependency on `@embedpdf/plugin-stage`.\n * It builds its own `PageTransform` from a target content width and shares the\n * exact `PageContext` seam, so every layer (RenderLayer, AnnotationLayer, …)\n * works here identically — and it provides the measured `ViewProjector`, so\n * anchored UI (`<AnnotationMenu>`, `<SelectionMenu>`) works here too, portalled\n * and clipping-immune.\n */\nimport * as React from 'react';\nimport { useMemo, useRef } from 'react';\nimport { NO_FRAME, pageTransform, type PageFrame } from '@embedpdf/core-geometry';\nimport { observeClientGeometry } from '@embedpdf/web';\nimport { ProjectorProvider, type ProjectorBinding, type ViewProjector } from './anchored';\nimport {\n DocumentScope,\n makePageContext,\n PageProvider,\n useActiveDocumentId,\n useKernel,\n} from './runtime';\n\nexport interface PageViewProps {\n page: number;\n /** Which document to show. Defaults to the active document. */\n documentId?: string;\n /** Target width for the page CONTENT; the display box is the rotated footprint. */\n width?: number;\n /** Reserved chrome bands around the page (screen px) — same model as `<Stage>`. */\n pageFrame?: PageFrame;\n /** Page-space content (rotates with the page). */\n children: React.ReactNode;\n /** Box-space chrome (label, border, …) — never rotated. Mirrors `<Stage pageChrome>`. */\n pageChrome?: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/** A single page surface with NO Stage — same layers + rotation + chrome frame,\n * no camera/scroll/zoom. */\nexport function PageView({\n page,\n documentId,\n width = 240,\n pageFrame = NO_FRAME,\n children,\n pageChrome,\n style,\n}: PageViewProps) {\n const kernel = useKernel();\n const active = useActiveDocumentId();\n const ref = useRef<HTMLDivElement>(null);\n const docId = documentId ?? active;\n const meta = docId ? kernel.getState().core.documents[docId] : undefined;\n const base = meta?.pages[page];\n const pon = base?.pageObjectNumber ?? page + 1;\n const rotation = base?.rotation ?? 0;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n // Standalone (no Stage/camera): build the page's transform from the target\n // content `width` directly. `scale` is view px per point = width / pageWidthPts.\n const transform = useMemo(\n () =>\n pageTransform({\n pageSize: base\n ? { width: base.size.width, height: base.size.height }\n : { width: 1, height: 1 },\n rotation,\n scale: base ? width / base.size.width : 1,\n // Physical 100% on the web: 1pt = 96/72 CSS px, times the page's\n // /UserUnit — so `transform.zoom` is meaningful even without a Stage\n // (a thumbnail-sized PageView reads as zoomed out, as it should).\n baseScale: (96 / 72) * (base?.userUnit ?? 1),\n dpr,\n }),\n [base?.size.width, base?.size.height, base?.userUnit, rotation, width, dpr],\n );\n const ctx = useMemo(\n () =>\n makePageContext(docId ?? '', pon, page, pageFrame, transform, () =>\n ref.current!.getBoundingClientRect(),\n ),\n [docId, pon, page, pageFrame, transform],\n );\n // The PageView's ViewProjector: no camera, so anchored UI positions by\n // MEASURING the DOM (client space → portal + position:fixed, immune to\n // ancestor overflow clipping). `toScreen` answers null until the page\n // element has committed — <Anchored> forces one post-commit pass to pick\n // it up. The binding's revision covers state-driven changes (a new\n // transform/page); `observeClientGeometry` covers the genuinely\n // browser-driven ones (document scroll, window resize) that no state\n // change announces.\n const projector = useMemo<ViewProjector>(\n () => ({\n space: 'client',\n toScreen: (p, rect) => (p === ctx.pon && ref.current ? ctx.toClientRect(rect) : null),\n toScreenPoint: (p, at) => (p === ctx.pon && ref.current ? ctx.toClientPoint(at) : null),\n viewEnv: (p) =>\n p === ctx.pon\n ? {\n scale: ctx.transform.viewScale,\n rotation: ctx.transform.rotation,\n zoom: ctx.transform.zoom,\n }\n : null,\n }),\n [ctx],\n );\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: ctx, subscribe: observeClientGeometry }),\n [projector, ctx],\n );\n if (!docId || !meta || !base) return null;\n const t = transform;\n const outerW = t.viewWidth + pageFrame.left + pageFrame.right;\n const outerH = t.viewHeight + pageFrame.top + pageFrame.bottom;\n const contentLeft = pageFrame.left + (t.viewWidth - t.contentWidth) / 2;\n const contentTop = pageFrame.top + (t.viewHeight - t.contentHeight) / 2;\n return (\n <DocumentScope id={docId}>\n <ProjectorProvider value={projectorBinding}>\n <div style={{ position: 'relative', width: outerW, height: outerH, ...style }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — transparent, axis-aligned, can't leak behind the bitmap */}\n <div\n style={{\n position: 'absolute',\n left: pageFrame.left,\n top: pageFrame.top,\n width: t.viewWidth,\n height: t.viewHeight,\n boxShadow: '0 6px 18px rgba(0,0,0,.18)',\n }}\n />\n {/* white backing + content as ONE box; rotation 0 carries no transform */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: contentLeft,\n top: contentTop,\n width: t.contentWidth,\n height: t.contentHeight,\n background: '#fff',\n transform: rotation ? `rotate(${rotation}deg)` : undefined,\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {children}\n </div>\n {pageChrome}\n </PageProvider>\n </div>\n </ProjectorProvider>\n </DocumentScope>\n );\n}\n"],"mappings":";;;;;;;;;AAyCA,SAAgB,SAAS,EACvB,MACA,YACA,QAAQ,KACR,YAAY,UACZ,UACA,YACA,SACgB;CAChB,MAAM,SAAS,UAAU;CACzB,MAAM,SAAS,oBAAoB;CACnC,MAAM,MAAM,OAAuB,IAAI;CACvC,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,OAAO,SAAS,CAAC,CAAC,KAAK,UAAU,SAAS,KAAA;CAC/D,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,MAAM,MAAM,oBAAoB,OAAO;CAC7C,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,MAAM,OAAO,WAAW,cAAc,OAAO,oBAAoB,IAAI;CAG3E,MAAM,YAAY,cAEd,cAAc;EACZ,UAAU,OACN;GAAE,OAAO,KAAK,KAAK;GAAO,QAAQ,KAAK,KAAK;EAAO,IACnD;GAAE,OAAO;GAAG,QAAQ;EAAE;EAC1B;EACA,OAAO,OAAO,QAAQ,KAAK,KAAK,QAAQ;EAIxC,WAAY,KAAK,MAAO,MAAM,YAAY;EAC1C;CACF,CAAC,GACH;EAAC,MAAM,KAAK;EAAO,MAAM,KAAK;EAAQ,MAAM;EAAU;EAAU;EAAO;CAAG,CAC5E;CACA,MAAM,MAAM,cAER,gBAAgB,SAAS,IAAI,KAAK,MAAM,WAAW,iBACjD,IAAI,QAAS,sBAAsB,CACrC,GACF;EAAC;EAAO;EAAK;EAAM;EAAW;CAAS,CACzC;CASA,MAAM,YAAY,eACT;EACL,OAAO;EACP,WAAW,GAAG,SAAU,MAAM,IAAI,OAAO,IAAI,UAAU,IAAI,aAAa,IAAI,IAAI;EAChF,gBAAgB,GAAG,OAAQ,MAAM,IAAI,OAAO,IAAI,UAAU,IAAI,cAAc,EAAE,IAAI;EAClF,UAAU,MACR,MAAM,IAAI,MACN;GACE,OAAO,IAAI,UAAU;GACrB,UAAU,IAAI,UAAU;GACxB,MAAM,IAAI,UAAU;EACtB,IACA;CACR,IACA,CAAC,GAAG,CACN;CACA,MAAM,mBAAmB,eAChB;EAAE;EAAW,UAAU;EAAK,WAAW;CAAsB,IACpE,CAAC,WAAW,GAAG,CACjB;CACA,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,OAAO;CACrC,MAAM,IAAI;CACV,MAAM,SAAS,EAAE,YAAY,UAAU,OAAO,UAAU;CACxD,MAAM,SAAS,EAAE,aAAa,UAAU,MAAM,UAAU;CACxD,MAAM,cAAc,UAAU,QAAQ,EAAE,YAAY,EAAE,gBAAgB;CACtE,MAAM,aAAa,UAAU,OAAO,EAAE,aAAa,EAAE,iBAAiB;CACtE,OACE,oBAAC,eAAD;EAAe,IAAI;EACjB,UAAA,oBAAC,mBAAD;GAAmB,OAAO;GAC1B,UAAA,oBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAY,OAAO;KAAQ,QAAQ;KAAQ,GAAG;IAAM;IAC1E,UAAA,qBAAC,cAAD;KAAc,OAAO;KAArB,UAAA;MAEE,oBAAC,OAAD,EACE,OAAO;OACL,UAAU;OACV,MAAM,UAAU;OAChB,KAAK,UAAU;OACf,OAAO,EAAE;OACT,QAAQ,EAAE;OACV,WAAW;MACb,EACD,CAAA;MAED,oBAAC,OAAD;OACO;OACL,OAAO;QACL,UAAU;QACV,MAAM;QACN,KAAK;QACL,OAAO,EAAE;QACT,QAAQ,EAAE;QACV,YAAY;QACZ,WAAW,WAAW,UAAU,SAAS,QAAQ,KAAA;QACjD,YAAY;QACZ,kBAAkB;OACpB;OAEC;MACE,CAAA;MACJ;KACW;;GACX,CAAA;EACc,CAAA;CACN,CAAA;AAEnB"}
1
+ {"version":3,"file":"page-view.js","names":[],"sources":["../src/page-view.tsx"],"sourcesContent":["/**\n * <PageView> — a single page surface with NO Stage.\n *\n * Same layers + rotation + chrome frame as a `<Stage>` page, but no\n * camera/scroll/zoom and, crucially, NO dependency on `@embedpdf/plugin-stage`.\n * It builds its own `PageTransform` from a target content width and shares the\n * exact `PageContext` seam, so every layer (RenderLayer, AnnotationLayer, …)\n * works here identically — and it provides the measured `ViewProjector`, so\n * anchored UI (`<AnnotationMenu>`, `<SelectionMenu>`) works here too, portalled\n * and clipping-immune.\n */\nimport * as React from 'react';\nimport { useId, useMemo, useRef } from 'react';\nimport { NO_FRAME, pageTransform, type PageFrame } from '@embedpdf/core-geometry';\nimport { observeClientGeometry } from '@embedpdf/web';\nimport { ProjectorProvider, type ProjectorBinding, type ViewProjector } from './anchored';\nimport {\n DocumentScope,\n makePageContext,\n PageProvider,\n useActiveDocumentId,\n useKernel,\n} from './runtime';\n\nexport interface PageViewProps {\n page: number;\n /** Which document to show. Defaults to the active document. */\n documentId?: string;\n /** Target width for the page CONTENT; the display box is the rotated footprint. */\n width?: number;\n /** Reserved chrome bands around the page (screen px) — same model as `<Stage>`. */\n pageFrame?: PageFrame;\n /** Page-space content (rotates with the page). */\n children: React.ReactNode;\n /** Box-space chrome (label, border, …) — never rotated. Mirrors `<Stage pageChrome>`. */\n pageChrome?: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/** A single page surface with NO Stage — same layers + rotation + chrome frame,\n * no camera/scroll/zoom. */\nexport function PageView({\n page,\n documentId,\n width = 240,\n pageFrame = NO_FRAME,\n children,\n pageChrome,\n style,\n}: PageViewProps) {\n const kernel = useKernel();\n const active = useActiveDocumentId();\n const ref = useRef<HTMLDivElement>(null);\n const docId = documentId ?? active;\n const meta = docId ? kernel.getState().core.documents[docId] : undefined;\n const base = meta?.pages[page];\n const pon = base?.pageObjectNumber ?? page + 1;\n const rotation = base?.rotation ?? 0;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n // Standalone (no Stage/camera): build the page's transform from the target\n // content `width` directly. `scale` is view px per point = width / pageWidthPts.\n const transform = useMemo(\n () =>\n pageTransform({\n pageSize: base\n ? { width: base.size.width, height: base.size.height }\n : { width: 1, height: 1 },\n rotation,\n scale: base ? width / base.size.width : 1,\n // Physical 100% on the web: 1pt = 96/72 CSS px, times the page's\n // /UserUnit — so `transform.zoom` is meaningful even without a Stage\n // (a thumbnail-sized PageView reads as zoomed out, as it should).\n baseScale: (96 / 72) * (base?.userUnit ?? 1),\n dpr,\n }),\n [base?.size.width, base?.size.height, base?.userUnit, rotation, width, dpr],\n );\n // This instance's VIEW identity: two PageViews of the same page (a compare\n // strip) must plan rasters independently, like two stage lenses do.\n const viewId = useId();\n const ctx = useMemo(\n () =>\n makePageContext(\n docId ?? '',\n `page-view:${viewId}`,\n pon,\n page,\n pageFrame,\n transform,\n () => ref.current!.getBoundingClientRect(),\n ),\n [docId, pon, page, pageFrame, transform, viewId],\n );\n // The PageView's ViewProjector: no camera, so anchored UI positions by\n // MEASURING the DOM (client space → portal + position:fixed, immune to\n // ancestor overflow clipping). `toScreen` answers null until the page\n // element has committed — <Anchored> forces one post-commit pass to pick\n // it up. The binding's revision covers state-driven changes (a new\n // transform/page); `observeClientGeometry` covers the genuinely\n // browser-driven ones (document scroll, window resize) that no state\n // change announces.\n const projector = useMemo<ViewProjector>(\n () => ({\n space: 'client',\n toScreen: (p, rect) => (p === ctx.pon && ref.current ? ctx.toClientRect(rect) : null),\n toScreenPoint: (p, at) => (p === ctx.pon && ref.current ? ctx.toClientPoint(at) : null),\n viewEnv: (p) =>\n p === ctx.pon\n ? {\n scale: ctx.transform.viewScale,\n rotation: ctx.transform.rotation,\n zoom: ctx.transform.zoom,\n }\n : null,\n }),\n [ctx],\n );\n const projectorBinding = useMemo<ProjectorBinding>(\n () => ({ projector, revision: ctx, subscribe: observeClientGeometry }),\n [projector, ctx],\n );\n if (!docId || !meta || !base) return null;\n const t = transform;\n const outerW = t.viewWidth + pageFrame.left + pageFrame.right;\n const outerH = t.viewHeight + pageFrame.top + pageFrame.bottom;\n const contentLeft = pageFrame.left + (t.viewWidth - t.contentWidth) / 2;\n const contentTop = pageFrame.top + (t.viewHeight - t.contentHeight) / 2;\n return (\n <DocumentScope id={docId}>\n <ProjectorProvider value={projectorBinding}>\n <div style={{ position: 'relative', width: outerW, height: outerH, ...style }}>\n <PageProvider value={ctx}>\n {/* drop shadow ONLY — transparent, axis-aligned, can't leak behind the bitmap */}\n <div\n style={{\n position: 'absolute',\n left: pageFrame.left,\n top: pageFrame.top,\n width: t.viewWidth,\n height: t.viewHeight,\n boxShadow: 'var(--epdf-page-shadow, 0 6px 18px rgba(0,0,0,.18))',\n }}\n />\n {/* white backing + content as ONE box; rotation 0 carries no transform */}\n <div\n ref={ref}\n style={{\n position: 'absolute',\n left: contentLeft,\n top: contentTop,\n width: t.contentWidth,\n height: t.contentHeight,\n background: '#fff',\n transform: rotation ? `rotate(${rotation}deg)` : undefined,\n userSelect: 'none',\n WebkitUserSelect: 'none',\n }}\n >\n {children}\n </div>\n {pageChrome}\n </PageProvider>\n </div>\n </ProjectorProvider>\n </DocumentScope>\n );\n}\n"],"mappings":";;;;;;;;;AAyCA,SAAgB,SAAS,EACvB,MACA,YACA,QAAQ,KACR,YAAY,UACZ,UACA,YACA,SACgB;CAChB,MAAM,SAAS,UAAU;CACzB,MAAM,SAAS,oBAAoB;CACnC,MAAM,MAAM,OAAuB,IAAI;CACvC,MAAM,QAAQ,cAAc;CAC5B,MAAM,OAAO,QAAQ,OAAO,SAAS,CAAC,CAAC,KAAK,UAAU,SAAS,KAAA;CAC/D,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,MAAM,MAAM,oBAAoB,OAAO;CAC7C,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,MAAM,OAAO,WAAW,cAAc,OAAO,oBAAoB,IAAI;CAG3E,MAAM,YAAY,cAEd,cAAc;EACZ,UAAU,OACN;GAAE,OAAO,KAAK,KAAK;GAAO,QAAQ,KAAK,KAAK;EAAO,IACnD;GAAE,OAAO;GAAG,QAAQ;EAAE;EAC1B;EACA,OAAO,OAAO,QAAQ,KAAK,KAAK,QAAQ;EAIxC,WAAY,KAAK,MAAO,MAAM,YAAY;EAC1C;CACF,CAAC,GACH;EAAC,MAAM,KAAK;EAAO,MAAM,KAAK;EAAQ,MAAM;EAAU;EAAU;EAAO;CAAG,CAC5E;CAGA,MAAM,SAAS,MAAM;CACrB,MAAM,MAAM,cAER,gBACE,SAAS,IACT,aAAa,UACb,KACA,MACA,WACA,iBACM,IAAI,QAAS,sBAAsB,CAC3C,GACF;EAAC;EAAO;EAAK;EAAM;EAAW;EAAW;CAAM,CACjD;CASA,MAAM,YAAY,eACT;EACL,OAAO;EACP,WAAW,GAAG,SAAU,MAAM,IAAI,OAAO,IAAI,UAAU,IAAI,aAAa,IAAI,IAAI;EAChF,gBAAgB,GAAG,OAAQ,MAAM,IAAI,OAAO,IAAI,UAAU,IAAI,cAAc,EAAE,IAAI;EAClF,UAAU,MACR,MAAM,IAAI,MACN;GACE,OAAO,IAAI,UAAU;GACrB,UAAU,IAAI,UAAU;GACxB,MAAM,IAAI,UAAU;EACtB,IACA;CACR,IACA,CAAC,GAAG,CACN;CACA,MAAM,mBAAmB,eAChB;EAAE;EAAW,UAAU;EAAK,WAAW;CAAsB,IACpE,CAAC,WAAW,GAAG,CACjB;CACA,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,OAAO;CACrC,MAAM,IAAI;CACV,MAAM,SAAS,EAAE,YAAY,UAAU,OAAO,UAAU;CACxD,MAAM,SAAS,EAAE,aAAa,UAAU,MAAM,UAAU;CACxD,MAAM,cAAc,UAAU,QAAQ,EAAE,YAAY,EAAE,gBAAgB;CACtE,MAAM,aAAa,UAAU,OAAO,EAAE,aAAa,EAAE,iBAAiB;CACtE,OACE,oBAAC,eAAD;EAAe,IAAI;EACjB,UAAA,oBAAC,mBAAD;GAAmB,OAAO;GAC1B,UAAA,oBAAC,OAAD;IAAK,OAAO;KAAE,UAAU;KAAY,OAAO;KAAQ,QAAQ;KAAQ,GAAG;IAAM;IAC1E,UAAA,qBAAC,cAAD;KAAc,OAAO;KAArB,UAAA;MAEE,oBAAC,OAAD,EACE,OAAO;OACL,UAAU;OACV,MAAM,UAAU;OAChB,KAAK,UAAU;OACf,OAAO,EAAE;OACT,QAAQ,EAAE;OACV,WAAW;MACb,EACD,CAAA;MAED,oBAAC,OAAD;OACO;OACL,OAAO;QACL,UAAU;QACV,MAAM;QACN,KAAK;QACL,OAAO,EAAE;QACT,QAAQ,EAAE;QACV,YAAY;QACZ,WAAW,WAAW,UAAU,SAAS,QAAQ,KAAA;QACjD,YAAY;QACZ,kBAAkB;OACpB;OAEC;MACE,CAAA;MACJ;KACW;;GACX,CAAA;EACc,CAAA;CACN,CAAA;AAEnB"}