@embedpdf/react 3.0.0-next.6 → 3.0.0-next.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotation.d.cts +1 -1
- package/dist/annotation.d.ts +1 -1
- package/dist/index.cjs +14 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/interaction.cjs +14 -1
- package/dist/interaction.cjs.map +1 -1
- package/dist/interaction.d.cts +2 -2
- package/dist/interaction.d.ts +2 -2
- package/dist/interaction.js +4 -3
- package/dist/interaction.js.map +1 -1
- package/dist/page-view.cjs +5 -3
- package/dist/page-view.cjs.map +1 -1
- package/dist/page-view.js +6 -4
- package/dist/page-view.js.map +1 -1
- package/dist/render.cjs +5 -4
- package/dist/render.cjs.map +1 -1
- package/dist/render.js +5 -4
- package/dist/render.js.map +1 -1
- package/dist/{runtime-CMxIuF2O.d.cts → runtime-Bh_9OtWo.d.ts} +12 -3
- package/dist/{runtime-DV98q7I0.d.ts → runtime-D4TXEICX.d.cts} +12 -3
- package/dist/runtime.cjs +2 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -1
- package/dist/runtime.js.map +1 -1
- package/dist/selection.cjs +144 -0
- package/dist/selection.cjs.map +1 -1
- package/dist/selection.d.cts +26 -1
- package/dist/selection.d.ts +26 -1
- package/dist/selection.js +149 -6
- package/dist/selection.js.map +1 -1
- package/dist/stage.cjs +15 -146
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +18 -5
- package/dist/stage.d.ts +18 -5
- package/dist/stage.js +17 -148
- package/dist/stage.js.map +1 -1
- package/package.json +23 -23
package/dist/annotation.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as AnnotationDTO } from "./AnnotationListSnapshot-CHjkcIaL-CMHGL8-D.cjs";
|
|
2
|
-
import { o as PageContextValue } from "./runtime-
|
|
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";
|
package/dist/annotation.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as AnnotationDTO } from "./AnnotationListSnapshot-CHjkcIaL-CMHGL8-D.js";
|
|
2
|
-
import { o as PageContextValue } from "./runtime-
|
|
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,6 +40,7 @@ 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;
|
|
@@ -113,6 +114,18 @@ exports.useTool = require_interaction.useTool;
|
|
|
113
114
|
exports.useToolCursor = require_interaction.useToolCursor;
|
|
114
115
|
exports.useViews = require_views.useViews;
|
|
115
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
|
+
});
|
|
116
129
|
var _embedpdf_core = require("@embedpdf/core");
|
|
117
130
|
Object.keys(_embedpdf_core).forEach(function(k) {
|
|
118
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-
|
|
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";
|
|
@@ -9,8 +9,8 @@ import { Stage, StageProps, StageTokenProp, useLayout, usePageList, usePages, us
|
|
|
9
9
|
import { Scrollbar, ScrollbarAxis, ScrollbarProps, useScrollMetrics } from "./scrollbar.cjs";
|
|
10
10
|
import { PageView, PageViewProps } from "./page-view.cjs";
|
|
11
11
|
import { RenderLayer, RenderLayerProps } 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";
|
|
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, 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-
|
|
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";
|
|
@@ -9,8 +9,8 @@ import { Stage, StageProps, StageTokenProp, useLayout, usePageList, usePages, us
|
|
|
9
9
|
import { Scrollbar, ScrollbarAxis, ScrollbarProps, useScrollMetrics } from "./scrollbar.js";
|
|
10
10
|
import { PageView, PageViewProps } from "./page-view.js";
|
|
11
11
|
import { RenderLayer, RenderLayerProps } 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";
|
|
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, 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
6
|
import { RenderLayer } from "./render.js";
|
|
8
|
-
import {
|
|
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, 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 };
|
package/dist/interaction.cjs
CHANGED
|
@@ -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,
|
package/dist/interaction.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;AAuBA,MAAM,QAAQ,OAAgC;CAC5C,OAAO,EAAE;CACT,KAAK,EAAE;CACP,MAAM,EAAE;CACR,MAAM,EAAE;AACV;;;;;;AAOA,SAAgB,mBAAmB,WAAW,KAAK,YAAY,GAAG;CAChE,IAAI,OAAO;CACX,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,QAAQ;CACZ,QAAQ,KAAa,GAAW,MAAsB;EACpD,QAAQ,MAAM,QAAQ,YAAY,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,KAAK,YAAY,QAAQ,IAAI;EACxF,OAAO;EACP,KAAK;EACL,KAAK;EACL,OAAO;CACT;AACF;AAEA,SAAgB,oBAAoB;CAClC,MAAM,OAAOA,gBAAAA,QAAQ;CACrB,MAAM,cAAcC,gBAAAA,cAAcC,6BAAAA,gBAAgB;CAClD,MAAM,SAASC,gBAAAA,YAAYD,6BAAAA,mBAAmB,MAAM,EAAE,OAAO,CAAC;CAC9D,MAAM,OAAA,GAAA,MAAA,OAAA,CAA6B,IAAI;CAEvC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,KAAK,IAAI;EACf,IAAI,CAAC,IAAI;EACT,MAAM,SAAS,mBAAmB;EAClC,MAAM,UACJ,OACA,GACA,aAAa,MACK;GAClB,MAAM,IAAI,GAAG,sBAAsB;GACnC,OAAO;IACL;IACA,UAAU;KAAE,GAAG,EAAE,UAAU,EAAE;KAAM,GAAG,EAAE,UAAU,EAAE;IAAI;IAIxD,MAAM;KACJ,KAAK,KAAK;KACV,OAAO,KAAK,YAAY,EAAE,SAAS,EAAE,OAAO;KAC5C,OAAO,KAAK,UAAU;KACtB,UAAU,KAAK,UAAU;KACzB,MAAM,KAAK,UAAU;IACvB;IAIA,UAAU,QAAS,QAAQ,KAAK,MAAM,KAAK,YAAY,EAAE,SAAS,EAAE,OAAO,IAAI;IAC/E,WAAW,KAAK,CAAC;IACjB;IACA,aAAc,EAAE,eAAe;GACjC;EACF;EACA,IAAI,WAAW;EAEf,MAAM,QAAQ,MAAoB;GAChC,IAAI,EAAE,WAAW,GAAG;GACpB,WAAW;GACX,YAAY,SAAS,OAAO,QAAQ,GAAG,OAAO,KAAK,IAAI,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;EAClF;EAEA,MAAM,SAAS,MAAoB;GACjC,IAAI,UAAU;GACd,YAAY,SAAS,OAAO,QAAQ,CAAC,CAAC;EACxC;EAEA,MAAM,QAAQ,MAAoB;GAChC,IAAI,CAAC,UAAU;GACf,YAAY,SAAS,OAAO,QAAQ,CAAC,CAAC;EACxC;EACA,MAAM,MAAM,MAAoB;GAC9B,IAAI,CAAC,UAAU;GACf,WAAW;GACX,YAAY,SAAS,OAAO,MAAM,CAAC,CAAC;EACtC;EAEA,GAAG,iBAAiB,eAAe,IAAI;EACvC,GAAG,iBAAiB,eAAe,KAAK;EACxC,OAAO,iBAAiB,eAAe,IAAI;EAC3C,OAAO,iBAAiB,aAAa,EAAE;EACvC,aAAa;GACX,GAAG,oBAAoB,eAAe,IAAI;GAC1C,GAAG,oBAAoB,eAAe,KAAK;GAC3C,OAAO,oBAAoB,eAAe,IAAI;GAC9C,OAAO,oBAAoB,aAAa,EAAE;EAC5C;CACF,GAAG,CAAC,aAAa,IAAI,CAAC;CAGtB,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAU;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG;GAAQ,aAAa;EAAO;CAAI,CAAA;AACjG;;AAGA,SAAgB,UAAU;CACxB,MAAM,cAAcD,gBAAAA,cAAcC,6BAAAA,gBAAgB;CAElD,OAAO;EACL,cAFmBC,gBAAAA,YAAYD,6BAAAA,mBAAmB,MAAM,EAAE,aAAa,CAE5D;EACX,UAAU,YAAY;EACtB,OAAO,YAAY,MAAM;CAC3B;AACF;AAoBA,MAAM,YAAY,KAAsB,aACtC,OAAO,QAAQ,WACX,OAAA,GAAA,cAAA,UAAA,CACU,IAAI,aAAa,KAAA,KAAa,WAAW;CAAE,GAAG;CAAK;AAAS,IAAI,GAAG;;;;;;;;;;;AAYnF,SAAgB,cAAc,MAAmC;CAC/D,MAAM,cAAcD,gBAAAA,cAAcC,6BAAAA,gBAAgB;CAGlD,MAAM,MAAM,QAAQ,KAAK,UAAU,IAAI;CACvC,MAAM,OAAA,GAAA,MAAA,OAAA,CAAa,IAAI;CACvB,IAAI,UAAU;CACd,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,IAAI,IAAI;EACd,IAAI,CAAC,GAAG;EACR,YAAY,cACV,EAAE,QACF,OAAO,YAAY,OAAO,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CACnF;EACA,aAAa,YAAY,cAAc,EAAE,QAAQ,IAAI;CACvD,GAAG,CAAC,aAAa,GAAG,CAAC;AACvB"}
|
package/dist/interaction.d.cts
CHANGED
|
@@ -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
|
package/dist/interaction.d.ts
CHANGED
|
@@ -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
|
package/dist/interaction.js
CHANGED
|
@@ -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
|
package/dist/interaction.js.map
CHANGED
|
@@ -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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"}
|
package/dist/page-view.cjs
CHANGED
|
@@ -51,12 +51,14 @@ function PageView({ page, documentId, width = 240, pageFrame = _embedpdf_core_ge
|
|
|
51
51
|
width,
|
|
52
52
|
dpr
|
|
53
53
|
]);
|
|
54
|
-
const
|
|
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,
|
package/dist/page-view.cjs.map
CHANGED
|
@@ -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 ?? ''
|
|
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
|
|
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,
|
package/dist/page-view.js.map
CHANGED
|
@@ -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 ?? ''
|
|
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"}
|
package/dist/render.cjs
CHANGED
|
@@ -78,8 +78,9 @@ function TilePlane({ annotations, fadeMs }) {
|
|
|
78
78
|
const page = require_runtime.usePage();
|
|
79
79
|
const render = require_runtime.useCapability(_embedpdf_plugin_render.RenderToken);
|
|
80
80
|
const demand = page.getViewDemand?.() ?? { desiredDeviceWidth: page.transform.deviceWidth };
|
|
81
|
-
const
|
|
82
|
-
|
|
81
|
+
const tiles = render.tilesFor(page.view);
|
|
82
|
+
const plan = require_runtime.useSelector(_embedpdf_plugin_render.RenderToken, () => tiles.plan(page.pon, demand, { includeAnnotations: annotations }));
|
|
83
|
+
(0, react.useEffect)(() => () => tiles.release(page.pon), [tiles, page.pon]);
|
|
83
84
|
if (plan.paint.length === 0) return null;
|
|
84
85
|
const t = page.transform;
|
|
85
86
|
const s = t.viewScale;
|
|
@@ -102,8 +103,8 @@ function TilePlane({ annotations, fadeMs }) {
|
|
|
102
103
|
height: source.rect.height * s
|
|
103
104
|
},
|
|
104
105
|
fadeMs,
|
|
105
|
-
onPainted: () =>
|
|
106
|
-
onUnpainted: () =>
|
|
106
|
+
onPainted: () => tiles.painted(page.pon, source.key),
|
|
107
|
+
onUnpainted: () => tiles.unpainted(page.pon, source.key)
|
|
107
108
|
}, source.key))]
|
|
108
109
|
});
|
|
109
110
|
}
|
package/dist/render.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.cjs","names":["usePage","useCapability","RenderToken","useSelector"],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer — the React view of @embedpdf/plugin-render. The page raster\n * is ONE concern with two planes:\n *\n * - the BASE plane: a whole-page <img> at the plugin's resolved render\n * points — the exact settled demand capped at the pixel budget on a\n * continuous (local) engine, the advertised ladder on a lattice (cloud)\n * deployment. Always present; the instant backdrop.\n * - the TILE plane: the plugin's retention-safe paint plan above it,\n * engaging by demand arithmetic when the view wants more pixels than\n * the base may spend. A thumbnail-sized demand engages nothing — the\n * arithmetic is the configuration.\n *\n * The layer is a DUMB painter, deliberately thin: plain <img>s bound through\n * the shared browser adapter, with painted/unpainted reports around their\n * visible lifetime. Every decision — strategy ∧ policy conformance, want\n * sets, level settling, retention, release — is plugin-render's; anything\n * mid-gesture simply CSS-scales until the plugin hands down new pixels.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { bindPaintedImage } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n /**\n * Mount the tile plane (default true). Whether it SPENDS anything is\n * demand arithmetic — leave it on; pass false only for a lens that must\n * never tile even under deep zoom.\n */\n tiles?: boolean;\n}\n\nlet warnedTileSize = false;\n\nexport function RenderLayer({ annotations = true, tiles = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const settings = render.paintSettings();\n const ref = useRef<HTMLImageElement>(null);\n\n // ONE dependency: the raster's canonical identity — conformed width +\n // annotations flag + epoch. Under a lattice it moves only at rung\n // crossings; under exact mode it tracks the demand and is CONSTANT above\n // the budget — so the deep-zoom backdrop never refetches, and the sub-\n // budget range refetches per settled demand exactly like v2 did.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the resolved render points and\n // collapses same-key asks in its raster store. A stale-closure scale\n // is harmless by construction: any scale mapping to this key\n // produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n // Imperative src on a STABLE element: the browser keeps the old\n // bitmap until the new one decodes, and nothing ever re-requests the\n // old URL — so revoke-on-cleanup is safe here by construction.\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n\n return (\n <>\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n {tiles && settings.tiles ? (\n <TilePlane annotations={annotations} fadeMs={settings.fadeMs} />\n ) : null}\n </>\n );\n}\n\n/**\n * The sharp plane above the base — the plugin's retention-safe paint plan\n * as keyed <img>s. Each tile reports painted after its first presentation\n * opportunity (and its inverse on unmount), so retained coarser generations\n * release only when their replacement is truly compositable.\n *\n * Tiles are placed in VIEW (CSS px) space directly — never in page points\n * under a scaled container. Blink quantizes layout lengths to 1/64 CSS px\n * BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform\n * turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible\n * seams and per-zoom-step letter shifts that grow with depth. In view\n * space the same quantization is a fixed ~1/64 CSS px at every zoom.\n * (v2 placed tiles in screen px for exactly this reason.)\n */\nfunction TilePlane({ annotations, fadeMs }: { annotations: boolean; fadeMs: number }) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n // Level settling lives in the plugin (its settle gate), pan stays live.\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n const plan = useSelector(RenderToken, (c) =>\n c.tilePlan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // Lens unmounted: stop in-flight tile fetches; resolved bytes stay cached.\n useEffect(() => () => render.releaseTiles(page.pon), [render, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const s = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth,\n height: t.contentHeight,\n pointerEvents: 'none',\n // STACKING CONTRACT: tile <img>s carry zIndex ranks (coarse under\n // fine) that exist only while generations are mixed — mid-zoom. This\n // container MUST be a stacking context so those ranks stay internal;\n // without it they join the Stage's context and paint above every\n // z-auto sibling (annotations, page chrome, menus) exactly while\n // zooming. The old scaled container created one implicitly via its\n // transform; `isolation` is the explicit, no-side-effect way.\n isolation: 'isolate',\n }}\n >\n {fadeMs > 0 ? (\n <style>{`@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }`}</style>\n ) : null}\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n view={{\n x: source.rect.x * s,\n y: source.rect.y * s,\n width: source.rect.width * s,\n height: source.rect.height * s,\n }}\n fadeMs={fadeMs}\n onPainted={() => render.tilePainted(page.pon, source.key)}\n onUnpainted={() => render.tileUnpainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: geometry plus the shared browser image binding. The binding\n * hides the incomplete image, reports painted after its first presentation\n * opportunity, and inverts that report when the element leaves the DOM. */\nfunction TileImg({\n source,\n view,\n fadeMs,\n onPainted,\n onUnpainted,\n}: {\n source: TilePaintSource;\n /** Placement rect in VIEW (CSS px) space. */\n view: { x: number; y: number; width: number; height: number };\n fadeMs: number;\n onPainted: () => void;\n onUnpainted: () => void;\n}) {\n const ref = useRef<HTMLImageElement>(null);\n useLayoutEffect(() => {\n if (!ref.current) return;\n return bindPaintedImage(ref.current, source.handle, { onPainted, onUnpainted });\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the handle is\n // the bitmap's identity; the report callbacks carry stable values.\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={(e) => {\n // A raster whose intrinsic size disagrees with its rect would be\n // silently stretched into place — the stale-handle bug class.\n const w = e.currentTarget.naturalWidth;\n if (w > 0 && !warnedTileSize) {\n const expected = Math.round(source.rect.width * source.scale);\n if (Math.abs(w - expected) > 1) {\n warnedTileSize = true;\n console.warn(\n `[render] tile bitmap ${w}px wide does not match its rect ` +\n `(expected ~${expected}px) — stale raster identity? key=${source.key}`,\n );\n }\n }\n }}\n style={{\n position: 'absolute',\n left: view.x,\n top: view.y,\n width: view.width,\n height: view.height,\n zIndex: source.z,\n ...(fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null),\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render.cjs","names":["usePage","useCapability","RenderToken","useSelector"],"sources":["../src/render.tsx"],"sourcesContent":["/**\n * RenderLayer — the React view of @embedpdf/plugin-render. The page raster\n * is ONE concern with two planes:\n *\n * - the BASE plane: a whole-page <img> at the plugin's resolved render\n * points — the exact settled demand capped at the pixel budget on a\n * continuous (local) engine, the advertised ladder on a lattice (cloud)\n * deployment. Always present; the instant backdrop.\n * - the TILE plane: the plugin's retention-safe paint plan above it,\n * engaging by demand arithmetic when the view wants more pixels than\n * the base may spend. A thumbnail-sized demand engages nothing — the\n * arithmetic is the configuration.\n *\n * The layer is a DUMB painter, deliberately thin: plain <img>s bound through\n * the shared browser adapter, with painted/unpainted reports around their\n * visible lifetime. Every decision — strategy ∧ policy conformance, want\n * sets, level settling, retention, release — is plugin-render's; anything\n * mid-gesture simply CSS-scales until the plugin hands down new pixels.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-render';\nimport * as React from 'react';\nimport { useEffect, useLayoutEffect, useRef } from 'react';\nimport { RenderToken } from '@embedpdf/plugin-render';\nimport type { PageViewDemand, TilePaintSource } from '@embedpdf/plugin-render';\nimport { bindPaintedImage } from '@embedpdf/web';\nimport { useCapability, usePage, useSelector } from './runtime';\n\nexport interface RenderLayerProps {\n /**\n * Bake annotations into the page bitmap (default true). Pass false when an\n * <AnnotationLayer> owns annotation rendering, so they aren't drawn twice.\n */\n annotations?: boolean;\n /**\n * Mount the tile plane (default true). Whether it SPENDS anything is\n * demand arithmetic — leave it on; pass false only for a lens that must\n * never tile even under deep zoom.\n */\n tiles?: boolean;\n}\n\nlet warnedTileSize = false;\n\nexport function RenderLayer({ annotations = true, tiles = true }: RenderLayerProps = {}) {\n const page = usePage();\n const render = useCapability(RenderToken);\n const settings = render.paintSettings();\n const ref = useRef<HTMLImageElement>(null);\n\n // ONE dependency: the raster's canonical identity — conformed width +\n // annotations flag + epoch. Under a lattice it moves only at rung\n // crossings; under exact mode it tracks the demand and is CONSTANT above\n // the budget — so the deep-zoom backdrop never refetches, and the sub-\n // budget range refetches per settled demand exactly like v2 did.\n const sourceKey = useSelector(RenderToken, (c) =>\n c.renderSourceKey(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n }),\n );\n\n useEffect(() => {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n (async () => {\n try {\n // The capability conforms this to the resolved render points and\n // collapses same-key asks in its raster store. A stale-closure scale\n // is harmless by construction: any scale mapping to this key\n // produces this key's canonical request.\n const image = await render.renderPage(page.pon, {\n scale: page.transform.renderScale,\n includeAnnotations: annotations,\n signal: controller.signal,\n });\n const obj = await image.objectUrl(controller.signal);\n if (controller.signal.aborted) {\n obj.revoke();\n return;\n }\n revoke = obj.revoke;\n // Imperative src on a STABLE element: the browser keeps the old\n // bitmap until the new one decodes, and nothing ever re-requests the\n // old URL — so revoke-on-cleanup is safe here by construction.\n if (ref.current) ref.current.src = obj.url;\n } catch {\n /* aborted (camera moved / unmounted) or render failed */\n }\n })();\n return () => {\n controller.abort();\n revoke?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sourceKey IS the\n // render identity; scale/annotations/epoch are folded into it upstream.\n }, [render, page.pon, sourceKey]);\n\n return (\n <>\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n }}\n />\n {tiles && settings.tiles ? (\n <TilePlane annotations={annotations} fadeMs={settings.fadeMs} />\n ) : null}\n </>\n );\n}\n\n/**\n * The sharp plane above the base — the plugin's retention-safe paint plan\n * as keyed <img>s. Each tile reports painted after its first presentation\n * opportunity (and its inverse on unmount), so retained coarser generations\n * release only when their replacement is truly compositable.\n *\n * Tiles are placed in VIEW (CSS px) space directly — never in page points\n * under a scaled container. Blink quantizes layout lengths to 1/64 CSS px\n * BEFORE transforms apply, so a pt-space rect under a ×25 zoom transform\n * turns that harmless 1/64 into ±0.4px+ of per-tile misplacement — visible\n * seams and per-zoom-step letter shifts that grow with depth. In view\n * space the same quantization is a fixed ~1/64 CSS px at every zoom.\n * (v2 placed tiles in screen px for exactly this reason.)\n */\nfunction TilePlane({ annotations, fadeMs }: { annotations: boolean; fadeMs: number }) {\n const page = usePage();\n const render = useCapability(RenderToken);\n // Demand: the host's live camera view (Stage) or whole-page (PageView).\n // Level settling lives in the plugin (its settle gate), pan stays live.\n const demand: PageViewDemand = page.getViewDemand?.() ?? {\n desiredDeviceWidth: page.transform.deviceWidth,\n };\n // THIS view's tile surface: state is per view × page, so a thumbnail\n // rail's never-engaging demand cannot disturb the main lens's plan (shared\n // state made the main view lose its tiles whenever a rail opened). The\n // handle is reference-stable per view — a clean dependency.\n const tiles = render.tilesFor(page.view);\n const plan = useSelector(RenderToken, () =>\n tiles.plan(page.pon, demand, { includeAnnotations: annotations }),\n );\n // View unmounted its plane: stop in-flight fetches; resolved bytes stay cached.\n useEffect(() => () => tiles.release(page.pon), [tiles, page.pon]);\n if (plan.paint.length === 0) return null;\n const t = page.transform;\n const s = t.viewScale;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: t.contentWidth,\n height: t.contentHeight,\n pointerEvents: 'none',\n // STACKING CONTRACT: tile <img>s carry zIndex ranks (coarse under\n // fine) that exist only while generations are mixed — mid-zoom. This\n // container MUST be a stacking context so those ranks stay internal;\n // without it they join the Stage's context and paint above every\n // z-auto sibling (annotations, page chrome, menus) exactly while\n // zooming. The old scaled container created one implicitly via its\n // transform; `isolation` is the explicit, no-side-effect way.\n isolation: 'isolate',\n }}\n >\n {fadeMs > 0 ? (\n <style>{`@keyframes epdf-tile-in { from { opacity: 0 } to { opacity: 1 } }`}</style>\n ) : null}\n {plan.paint.map((source) => (\n <TileImg\n key={source.key}\n source={source}\n view={{\n x: source.rect.x * s,\n y: source.rect.y * s,\n width: source.rect.width * s,\n height: source.rect.height * s,\n }}\n fadeMs={fadeMs}\n onPainted={() => tiles.painted(page.pon, source.key)}\n onUnpainted={() => tiles.unpainted(page.pon, source.key)}\n />\n ))}\n </div>\n );\n}\n\n/** One tile: geometry plus the shared browser image binding. The binding\n * hides the incomplete image, reports painted after its first presentation\n * opportunity, and inverts that report when the element leaves the DOM. */\nfunction TileImg({\n source,\n view,\n fadeMs,\n onPainted,\n onUnpainted,\n}: {\n source: TilePaintSource;\n /** Placement rect in VIEW (CSS px) space. */\n view: { x: number; y: number; width: number; height: number };\n fadeMs: number;\n onPainted: () => void;\n onUnpainted: () => void;\n}) {\n const ref = useRef<HTMLImageElement>(null);\n useLayoutEffect(() => {\n if (!ref.current) return;\n return bindPaintedImage(ref.current, source.handle, { onPainted, onUnpainted });\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the handle is\n // the bitmap's identity; the report callbacks carry stable values.\n }, [source.handle]);\n return (\n <img\n ref={ref}\n alt=\"\"\n draggable={false}\n onLoad={(e) => {\n // A raster whose intrinsic size disagrees with its rect would be\n // silently stretched into place — the stale-handle bug class.\n const w = e.currentTarget.naturalWidth;\n if (w > 0 && !warnedTileSize) {\n const expected = Math.round(source.rect.width * source.scale);\n if (Math.abs(w - expected) > 1) {\n warnedTileSize = true;\n console.warn(\n `[render] tile bitmap ${w}px wide does not match its rect ` +\n `(expected ~${expected}px) — stale raster identity? key=${source.key}`,\n );\n }\n }\n }}\n style={{\n position: 'absolute',\n left: view.x,\n top: view.y,\n width: view.width,\n height: view.height,\n zIndex: source.z,\n ...(fadeMs > 0 ? { animation: `epdf-tile-in ${fadeMs}ms ease-out` } : null),\n }}\n />\n );\n}\n"],"mappings":"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"}
|