@embedpdf/svelte 0.0.0-stage → 3.0.0-next.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +312 -2
  3. package/dist/actions/readers.svelte.d.ts +29 -0
  4. package/dist/actions/readers.svelte.js +55 -0
  5. package/dist/actions.d.ts +13 -0
  6. package/dist/actions.js +13 -0
  7. package/dist/anchored/Anchored.svelte +24 -0
  8. package/dist/anchored/Anchored.svelte.d.ts +4 -0
  9. package/dist/anchored/PlacedAnchored.svelte +97 -0
  10. package/dist/anchored/PlacedAnchored.svelte.d.ts +4 -0
  11. package/dist/anchored/context.d.ts +31 -0
  12. package/dist/anchored/context.js +36 -0
  13. package/dist/anchored/props.d.ts +29 -0
  14. package/dist/anchored/props.js +1 -0
  15. package/dist/anchored.d.ts +12 -0
  16. package/dist/anchored.js +2 -0
  17. package/dist/annotation/AnnotationChrome.svelte +173 -0
  18. package/dist/annotation/AnnotationChrome.svelte.d.ts +11 -0
  19. package/dist/annotation/AnnotationDraftMenu.svelte +27 -0
  20. package/dist/annotation/AnnotationDraftMenu.svelte.d.ts +4 -0
  21. package/dist/annotation/AnnotationFrame.svelte +46 -0
  22. package/dist/annotation/AnnotationFrame.svelte.d.ts +15 -0
  23. package/dist/annotation/AnnotationLayer.svelte +241 -0
  24. package/dist/annotation/AnnotationLayer.svelte.d.ts +4 -0
  25. package/dist/annotation/AnnotationLook.svelte +67 -0
  26. package/dist/annotation/AnnotationLook.svelte.d.ts +21 -0
  27. package/dist/annotation/AnnotationMenu.svelte +47 -0
  28. package/dist/annotation/AnnotationMenu.svelte.d.ts +4 -0
  29. package/dist/annotation/AnnotationRotationBadge.svelte +52 -0
  30. package/dist/annotation/AnnotationRotationBadge.svelte.d.ts +4 -0
  31. package/dist/annotation/AnnotationScene.svelte +27 -0
  32. package/dist/annotation/AnnotationScene.svelte.d.ts +7 -0
  33. package/dist/annotation/FreeTextBox.svelte +51 -0
  34. package/dist/annotation/FreeTextBox.svelte.d.ts +9 -0
  35. package/dist/annotation/NativeDrawing.svelte +61 -0
  36. package/dist/annotation/NativeDrawing.svelte.d.ts +10 -0
  37. package/dist/annotation/StampGhost.svelte +79 -0
  38. package/dist/annotation/StampGhost.svelte.d.ts +7 -0
  39. package/dist/annotation/chrome-paint.svelte.d.ts +30 -0
  40. package/dist/annotation/chrome-paint.svelte.js +27 -0
  41. package/dist/annotation/props.d.ts +181 -0
  42. package/dist/annotation/props.js +1 -0
  43. package/dist/annotation/readers.svelte.d.ts +59 -0
  44. package/dist/annotation/readers.svelte.js +136 -0
  45. package/dist/annotation/state.d.ts +18 -0
  46. package/dist/annotation/state.js +20 -0
  47. package/dist/annotation/text-box.svelte.d.ts +28 -0
  48. package/dist/annotation/text-box.svelte.js +87 -0
  49. package/dist/annotation.d.ts +32 -0
  50. package/dist/annotation.js +31 -0
  51. package/dist/attachment/readers.d.ts +23 -0
  52. package/dist/attachment/readers.js +21 -0
  53. package/dist/attachment.d.ts +13 -0
  54. package/dist/attachment.js +14 -0
  55. package/dist/commands/readers.svelte.d.ts +50 -0
  56. package/dist/commands/readers.svelte.js +83 -0
  57. package/dist/commands.d.ts +9 -0
  58. package/dist/commands.js +9 -0
  59. package/dist/form/ButtonControl.svelte +38 -0
  60. package/dist/form/ButtonControl.svelte.d.ts +11 -0
  61. package/dist/form/ComboControl.svelte +52 -0
  62. package/dist/form/ComboControl.svelte.d.ts +11 -0
  63. package/dist/form/FieldPictures.svelte +145 -0
  64. package/dist/form/FieldPictures.svelte.d.ts +3 -0
  65. package/dist/form/FormFocusRing.svelte +25 -0
  66. package/dist/form/FormFocusRing.svelte.d.ts +8 -0
  67. package/dist/form/FormLayer.svelte +98 -0
  68. package/dist/form/FormLayer.svelte.d.ts +3 -0
  69. package/dist/form/ListControl.svelte +49 -0
  70. package/dist/form/ListControl.svelte.d.ts +11 -0
  71. package/dist/form/NativeListBox.svelte +57 -0
  72. package/dist/form/NativeListBox.svelte.d.ts +4 -0
  73. package/dist/form/SignatureControl.svelte +53 -0
  74. package/dist/form/SignatureControl.svelte.d.ts +11 -0
  75. package/dist/form/TextControl.svelte +107 -0
  76. package/dist/form/TextControl.svelte.d.ts +11 -0
  77. package/dist/form/ToggleControl.svelte +50 -0
  78. package/dist/form/ToggleControl.svelte.d.ts +11 -0
  79. package/dist/form/WidgetBox.svelte +82 -0
  80. package/dist/form/WidgetBox.svelte.d.ts +15 -0
  81. package/dist/form/props.d.ts +21 -0
  82. package/dist/form/props.js +1 -0
  83. package/dist/form/readers.svelte.d.ts +46 -0
  84. package/dist/form/readers.svelte.js +52 -0
  85. package/dist/form/widget.d.ts +16 -0
  86. package/dist/form/widget.js +20 -0
  87. package/dist/form.d.ts +11 -0
  88. package/dist/form.js +12 -0
  89. package/dist/history/readers.d.ts +22 -0
  90. package/dist/history/readers.js +21 -0
  91. package/dist/history.d.ts +10 -0
  92. package/dist/history.js +11 -0
  93. package/dist/i18n/readers.svelte.d.ts +32 -0
  94. package/dist/i18n/readers.svelte.js +30 -0
  95. package/dist/i18n.d.ts +8 -0
  96. package/dist/i18n.js +9 -0
  97. package/dist/index.d.ts +30 -0
  98. package/dist/index.js +30 -0
  99. package/dist/interaction/PagePointerSource.svelte +37 -0
  100. package/dist/interaction/PagePointerSource.svelte.d.ts +3 -0
  101. package/dist/interaction/readers.svelte.d.ts +26 -0
  102. package/dist/interaction/readers.svelte.js +47 -0
  103. package/dist/interaction.d.ts +11 -0
  104. package/dist/interaction.js +13 -0
  105. package/dist/link/LinkLayer.svelte +164 -0
  106. package/dist/link/LinkLayer.svelte.d.ts +4 -0
  107. package/dist/link/props.d.ts +17 -0
  108. package/dist/link/props.js +1 -0
  109. package/dist/link/readers.svelte.d.ts +18 -0
  110. package/dist/link/readers.svelte.js +38 -0
  111. package/dist/link.d.ts +11 -0
  112. package/dist/link.js +11 -0
  113. package/dist/measurement/readers.d.ts +44 -0
  114. package/dist/measurement/readers.js +46 -0
  115. package/dist/measurement.d.ts +12 -0
  116. package/dist/measurement.js +13 -0
  117. package/dist/metadata/readers.d.ts +22 -0
  118. package/dist/metadata/readers.js +22 -0
  119. package/dist/metadata.d.ts +12 -0
  120. package/dist/metadata.js +13 -0
  121. package/dist/outline/readers.d.ts +26 -0
  122. package/dist/outline/readers.js +22 -0
  123. package/dist/outline.d.ts +12 -0
  124. package/dist/outline.js +13 -0
  125. package/dist/page-edit/readers.d.ts +17 -0
  126. package/dist/page-edit/readers.js +18 -0
  127. package/dist/page-edit.d.ts +12 -0
  128. package/dist/page-edit.js +13 -0
  129. package/dist/page-view/PageView.svelte +192 -0
  130. package/dist/page-view/PageView.svelte.d.ts +4 -0
  131. package/dist/page-view/props.d.ts +32 -0
  132. package/dist/page-view/props.js +1 -0
  133. package/dist/page-view.d.ts +8 -0
  134. package/dist/page-view.js +7 -0
  135. package/dist/redaction/readers.d.ts +37 -0
  136. package/dist/redaction/readers.js +38 -0
  137. package/dist/redaction.d.ts +12 -0
  138. package/dist/redaction.js +13 -0
  139. package/dist/render/RenderLayer.svelte +116 -0
  140. package/dist/render/RenderLayer.svelte.d.ts +4 -0
  141. package/dist/render/TileImage.svelte +65 -0
  142. package/dist/render/TileImage.svelte.d.ts +11 -0
  143. package/dist/render/TilePlane.svelte +88 -0
  144. package/dist/render/TilePlane.svelte.d.ts +7 -0
  145. package/dist/render/props.d.ts +16 -0
  146. package/dist/render/props.js +1 -0
  147. package/dist/render/readers.svelte.d.ts +9 -0
  148. package/dist/render/readers.svelte.js +13 -0
  149. package/dist/render.d.ts +10 -0
  150. package/dist/render.js +10 -0
  151. package/dist/runtime/DocumentGate.svelte +26 -0
  152. package/dist/runtime/DocumentGate.svelte.d.ts +4 -0
  153. package/dist/runtime/DocumentScope.svelte +14 -0
  154. package/dist/runtime/DocumentScope.svelte.d.ts +4 -0
  155. package/dist/runtime/KernelProvider.svelte +33 -0
  156. package/dist/runtime/KernelProvider.svelte.d.ts +4 -0
  157. package/dist/runtime/SvgShapes.svelte +30 -0
  158. package/dist/runtime/SvgShapes.svelte.d.ts +8 -0
  159. package/dist/runtime/Viewer.svelte +130 -0
  160. package/dist/runtime/Viewer.svelte.d.ts +4 -0
  161. package/dist/runtime/binding.svelte.d.ts +33 -0
  162. package/dist/runtime/binding.svelte.js +71 -0
  163. package/dist/runtime/capability-handle.d.ts +6 -0
  164. package/dist/runtime/capability-handle.js +71 -0
  165. package/dist/runtime/dev.d.ts +10 -0
  166. package/dist/runtime/dev.js +23 -0
  167. package/dist/runtime/documents.svelte.d.ts +34 -0
  168. package/dist/runtime/documents.svelte.js +49 -0
  169. package/dist/runtime/page-layers.svelte.d.ts +20 -0
  170. package/dist/runtime/page-layers.svelte.js +40 -0
  171. package/dist/runtime/page.d.ts +10 -0
  172. package/dist/runtime/page.js +23 -0
  173. package/dist/runtime/props.d.ts +63 -0
  174. package/dist/runtime/props.js +1 -0
  175. package/dist/runtime/readers.svelte.d.ts +47 -0
  176. package/dist/runtime/readers.svelte.js +122 -0
  177. package/dist/runtime/state.svelte.d.ts +35 -0
  178. package/dist/runtime/state.svelte.js +90 -0
  179. package/dist/runtime/theme.d.ts +12 -0
  180. package/dist/runtime/theme.js +15 -0
  181. package/dist/runtime/values.svelte.d.ts +28 -0
  182. package/dist/runtime/values.svelte.js +51 -0
  183. package/dist/runtime.d.ts +26 -0
  184. package/dist/runtime.js +25 -0
  185. package/dist/search/SearchLayer.svelte +94 -0
  186. package/dist/search/SearchLayer.svelte.d.ts +4 -0
  187. package/dist/search/props.d.ts +11 -0
  188. package/dist/search/props.js +1 -0
  189. package/dist/search/readers.svelte.d.ts +41 -0
  190. package/dist/search/readers.svelte.js +40 -0
  191. package/dist/search.d.ts +11 -0
  192. package/dist/search.js +11 -0
  193. package/dist/selection/SelectionClipboard.svelte +35 -0
  194. package/dist/selection/SelectionClipboard.svelte.d.ts +4 -0
  195. package/dist/selection/SelectionHandles.svelte +192 -0
  196. package/dist/selection/SelectionHandles.svelte.d.ts +4 -0
  197. package/dist/selection/SelectionLayer.svelte +67 -0
  198. package/dist/selection/SelectionLayer.svelte.d.ts +3 -0
  199. package/dist/selection/SelectionMenu.svelte +36 -0
  200. package/dist/selection/SelectionMenu.svelte.d.ts +4 -0
  201. package/dist/selection/props.d.ts +26 -0
  202. package/dist/selection/props.js +1 -0
  203. package/dist/selection/readers.svelte.d.ts +31 -0
  204. package/dist/selection/readers.svelte.js +26 -0
  205. package/dist/selection.d.ts +16 -0
  206. package/dist/selection.js +18 -0
  207. package/dist/shell/readers.svelte.d.ts +40 -0
  208. package/dist/shell/readers.svelte.js +40 -0
  209. package/dist/shell/surface-focus.svelte.d.ts +16 -0
  210. package/dist/shell/surface-focus.svelte.js +33 -0
  211. package/dist/shell.d.ts +12 -0
  212. package/dist/shell.js +11 -0
  213. package/dist/signature/readers.svelte.d.ts +43 -0
  214. package/dist/signature/readers.svelte.js +46 -0
  215. package/dist/signature.d.ts +15 -0
  216. package/dist/signature.js +16 -0
  217. package/dist/stage/PageSurface.svelte +126 -0
  218. package/dist/stage/PageSurface.svelte.d.ts +18 -0
  219. package/dist/stage/Scrollbar.svelte +148 -0
  220. package/dist/stage/Scrollbar.svelte.d.ts +4 -0
  221. package/dist/stage/Stage.svelte +181 -0
  222. package/dist/stage/Stage.svelte.d.ts +4 -0
  223. package/dist/stage/StageScope.svelte +14 -0
  224. package/dist/stage/StageScope.svelte.d.ts +9 -0
  225. package/dist/stage/props.d.ts +71 -0
  226. package/dist/stage/props.js +1 -0
  227. package/dist/stage/readers.svelte.d.ts +39 -0
  228. package/dist/stage/readers.svelte.js +84 -0
  229. package/dist/stage/stage-scope.d.ts +18 -0
  230. package/dist/stage/stage-scope.js +35 -0
  231. package/dist/stage/two-way.svelte.d.ts +13 -0
  232. package/dist/stage/two-way.svelte.js +26 -0
  233. package/dist/stage.d.ts +17 -0
  234. package/dist/stage.js +15 -0
  235. package/dist/stamp/readers.svelte.d.ts +50 -0
  236. package/dist/stamp/readers.svelte.js +76 -0
  237. package/dist/stamp.d.ts +16 -0
  238. package/dist/stamp.js +18 -0
  239. package/dist/toolbar/DefaultCollapsed.svelte +39 -0
  240. package/dist/toolbar/DefaultCollapsed.svelte.d.ts +9 -0
  241. package/dist/toolbar/DefaultCommand.svelte +33 -0
  242. package/dist/toolbar/DefaultCommand.svelte.d.ts +8 -0
  243. package/dist/toolbar/DefaultGroupTrigger.svelte +39 -0
  244. package/dist/toolbar/DefaultGroupTrigger.svelte.d.ts +9 -0
  245. package/dist/toolbar/DefaultOverflowMenu.svelte +106 -0
  246. package/dist/toolbar/DefaultOverflowMenu.svelte.d.ts +9 -0
  247. package/dist/toolbar/DefaultOverflowTrigger.svelte +25 -0
  248. package/dist/toolbar/DefaultOverflowTrigger.svelte.d.ts +7 -0
  249. package/dist/toolbar/Measured.svelte +46 -0
  250. package/dist/toolbar/Measured.svelte.d.ts +11 -0
  251. package/dist/toolbar/NativeSlotSocket.svelte +43 -0
  252. package/dist/toolbar/NativeSlotSocket.svelte.d.ts +11 -0
  253. package/dist/toolbar/Toolbar.svelte +314 -0
  254. package/dist/toolbar/Toolbar.svelte.d.ts +4 -0
  255. package/dist/toolbar/paint.d.ts +3 -0
  256. package/dist/toolbar/paint.js +9 -0
  257. package/dist/toolbar/props.d.ts +48 -0
  258. package/dist/toolbar/props.js +1 -0
  259. package/dist/toolbar/strip.svelte.d.ts +14 -0
  260. package/dist/toolbar/strip.svelte.js +35 -0
  261. package/dist/toolbar/views.d.ts +14 -0
  262. package/dist/toolbar/views.js +1 -0
  263. package/dist/toolbar.d.ts +16 -0
  264. package/dist/toolbar.js +12 -0
  265. package/dist/view-manager/readers.d.ts +19 -0
  266. package/dist/view-manager/readers.js +17 -0
  267. package/dist/view-manager.d.ts +11 -0
  268. package/dist/view-manager.js +12 -0
  269. package/package.json +207 -3
@@ -0,0 +1,181 @@
1
+ <!--
2
+ The Stage: lays out the document's pages by the camera, keeps only the visible ones mounted,
3
+ and draws its content (the `children` snippet) on each; you bring the layers. Pointer, wheel and touch input go
4
+ through `@embedpdf/web`'s stage surface, so every framework has one feel.
5
+
6
+ Everything inside binds to this lens: a `useStageState()` in a page's chrome or a
7
+ `<Scrollbar>` in the overlay needs no token. The overlay also gets the Stage's projection, so
8
+ anchored UI follows the camera in the same flush as the pages.
9
+ -->
10
+ <script lang="ts">
11
+ import { untrack } from 'svelte';
12
+ import type { CapabilityToken } from '@embedpdf/core';
13
+ import type { PageFrame } from '@embedpdf/core-geometry';
14
+ import { createScrollHandler, DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';
15
+ import type { VisiblePage } from '@embedpdf/plugin-stage';
16
+ import type { StageHostCapability } from '@embedpdf/plugin-stage/contract/host';
17
+ import { InteractionToken as InteractionPublicToken } from '@embedpdf/plugin-interaction/contract';
18
+ import { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';
19
+ import { createStageSurface, stageViewProjector } from '@embedpdf/web';
20
+ import { setProjectorBinding, setShownPages, type ShownPages } from '../anchored/context.js';
21
+ import {
22
+ useCapabilityEvent,
23
+ useDocumentId,
24
+ useKernelValue,
25
+ useOptionalCapability,
26
+ useOptionalSelector,
27
+ } from '../runtime/readers.svelte.js';
28
+ import { declaredState } from '../runtime/state.svelte.js';
29
+ import { useKernelBinding } from '../runtime/binding.svelte.js';
30
+ import PageSurface from './PageSurface.svelte';
31
+ import type { StageProps } from './props.js';
32
+ import { setStageToken, stageTokenOf } from './stage-scope.js';
33
+ import { syncTwoWay } from './two-way.svelte.js';
34
+
35
+ let {
36
+ children,
37
+ pageChrome,
38
+ overlay,
39
+ tool = $bindable(),
40
+ onToolChange,
41
+ page = $bindable(),
42
+ zoom = $bindable(),
43
+ token: explicitToken,
44
+ class: className,
45
+ style,
46
+ }: StageProps = $props();
47
+
48
+ const token = stageTokenOf(() => explicitToken);
49
+ setStageToken(token);
50
+ // The surface is a host of the lens: it reports the viewport size, drives gestures and reads
51
+ // the lens id. The host contract is the same runtime token, typed wider.
52
+ const hostToken = () => token() as unknown as CapabilityToken<StageHostCapability>;
53
+ const stage = useOptionalCapability(hostToken);
54
+ const interaction = useOptionalCapability(InteractionToken);
55
+ const documentId = useDocumentId();
56
+
57
+ const NO_PAGES: readonly VisiblePage[] = [];
58
+ // The visible pages already fold in the camera (each carries its snapped screen position and
59
+ // transform), so panning gives a new list: no separate camera subscription.
60
+ const pages = useOptionalSelector(hostToken, (lens) => lens.listVisiblePages(), NO_PAGES);
61
+ const settings = useOptionalSelector(hostToken, (lens) => lens.getSettings(), DEFAULT_SETTINGS);
62
+ // The reserved bands around every page, compared by side so an equal frame changes nothing.
63
+ const frame = useOptionalSelector(
64
+ hostToken,
65
+ (lens) => lens.getSettings().pageFrame,
66
+ DEFAULT_SETTINGS.pageFrame,
67
+ (left: PageFrame, right: PageFrame) =>
68
+ left.top === right.top &&
69
+ left.right === right.right &&
70
+ left.bottom === right.bottom &&
71
+ left.left === right.left,
72
+ );
73
+
74
+ // How this view takes pointer input is its own settings: routed to the interaction plugin
75
+ // (which also registers this view's pan-scroll handler, scoped to it, so two Stages on one
76
+ // document never pan each other), or the Stage's own drag-to-pan without it.
77
+ const routed = $derived(settings.current.interaction && interaction.current !== null);
78
+ const panFallback = $derived(settings.current.panFallback);
79
+ const zoomGestures = $derived(settings.current.zoomGestures);
80
+ // The cursor the interaction plugin resolves (text, grab, …), on the viewport while routed.
81
+ const hubCursor = useKernelValue(() => interaction.current?.getCursor() ?? 'default');
82
+
83
+ let element: HTMLDivElement | undefined;
84
+
85
+ $effect(() => {
86
+ const lens = stage.current;
87
+ const hub = routed ? interaction.current : null;
88
+ const gestures = zoomGestures;
89
+ const fallback = panFallback;
90
+ if (!element || !lens) return;
91
+ return untrack(() => {
92
+ const detachSurface = createStageSurface(element!, lens, {
93
+ hub,
94
+ source: lens.getLensId(),
95
+ zoomGestures: gestures,
96
+ });
97
+ const offScroll = hub
98
+ ? hub.registerHandler(createScrollHandler(lens, hub, { panFallback: fallback }), {
99
+ source: lens.getLensId(),
100
+ })
101
+ : null;
102
+ return () => {
103
+ offScroll?.();
104
+ detachSurface();
105
+ };
106
+ });
107
+ });
108
+
109
+ // ── two-way values ─────────────────────────────────────────────────────────
110
+
111
+ // The bound tool wins whenever it's set, again when the document changes.
112
+ $effect(() => {
113
+ const wanted = tool;
114
+ const hub = interaction.current;
115
+ if (wanted === undefined || !hub) return;
116
+ untrack(() => {
117
+ if (hub.getActiveToolId() !== wanted) hub.activateTool(wanted);
118
+ });
119
+ });
120
+ useCapabilityEvent(
121
+ InteractionPublicToken,
122
+ (hub) => hub.onToolChanged,
123
+ (event) => {
124
+ if (tool !== undefined) tool = event.toolId;
125
+ onToolChange?.(event.toolId);
126
+ },
127
+ );
128
+
129
+ const binding = useKernelBinding();
130
+ const view = declaredState(binding, token, stageState.read, stageState.empty);
131
+ syncTwoWay({
132
+ stage: () => view().currentPageIndex,
133
+ bound: () => page,
134
+ ready: () => stage.current !== null,
135
+ write: (value) => (page = value),
136
+ apply: (value) => stage.current?.goToPage(value),
137
+ });
138
+ syncTwoWay({
139
+ stage: () => view().zoomLevel,
140
+ bound: () => zoom,
141
+ ready: () => stage.current !== null,
142
+ write: (value) => (zoom = value),
143
+ apply: (value) => stage.current?.zoomTo(value),
144
+ });
145
+
146
+ // ── what the overlay anchors to ───────────────────────────────────────────
147
+
148
+ // Anchored UI positions through the camera: pure state, no DOM reads. The visible pages are
149
+ // the binding's revision, so a camera change moves the pages and every anchored element in
150
+ // the same flush.
151
+ const projector = stageViewProjector(() => stage.current);
152
+ const projectorBinding = $derived({ projector, revision: pages.current });
153
+ setProjectorBinding(() => projectorBinding);
154
+ // The pages on screen as one value that changes only when a page comes or goes, so anchored
155
+ // UI on the other pages sits out every camera frame.
156
+ const shownKey = $derived(pages.current.map((visible) => visible.ref.objectNumber).join(','));
157
+ const shownPages: ShownPages = $derived(new Set(shownKey ? shownKey.split(',').map(Number) : []));
158
+ setShownPages(() => shownPages);
159
+ </script>
160
+
161
+ <div
162
+ bind:this={element}
163
+ class={className}
164
+ style="position: relative; overflow: hidden; touch-action: none;{routed
165
+ ? ` cursor: ${hubCursor.current};`
166
+ : ''}{style ? ` ${style}` : ''}"
167
+ >
168
+ {#if stage.current}
169
+ {#each pages.current as visible (visible.ref.objectNumber)}
170
+ <PageSurface
171
+ documentId={documentId.current ?? ''}
172
+ page={visible}
173
+ frame={frame.current}
174
+ stage={stage.current}
175
+ content={children}
176
+ chrome={pageChrome}
177
+ />
178
+ {/each}
179
+ {/if}
180
+ {@render overlay?.()}
181
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { StageProps } from './props.js';
2
+ declare const Stage: import("svelte").Component<StageProps, {}, "page" | "zoom" | "tool">;
3
+ type Stage = ReturnType<typeof Stage>;
4
+ export default Stage;
@@ -0,0 +1,14 @@
1
+ <!--
2
+ Binds everything inside to a stage lens: the stage readers and stage chrome inside use `token`
3
+ unless they are given one themselves.
4
+ -->
5
+ <script lang="ts">
6
+ import type { Snippet } from 'svelte';
7
+ import { setStageToken, type StageTokenProp } from './stage-scope.js';
8
+
9
+ let { token, children }: { token: StageTokenProp; children?: Snippet } = $props();
10
+
11
+ setStageToken(() => token);
12
+ </script>
13
+
14
+ {@render children?.()}
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type StageTokenProp } from './stage-scope.js';
3
+ type $$ComponentProps = {
4
+ token: StageTokenProp;
5
+ children?: Snippet;
6
+ };
7
+ declare const StageScope: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type StageScope = ReturnType<typeof StageScope>;
9
+ export default StageScope;
@@ -0,0 +1,71 @@
1
+ /** The props of the Stage's components: `<Stage>` and `<Scrollbar>`. */
2
+ import type { Snippet } from 'svelte';
3
+ import type { PageContextValue } from '../runtime/page.js';
4
+ import type { StageTokenProp } from './stage-scope.js';
5
+ export interface StageProps {
6
+ /**
7
+ * Page-space content for each visible page (`<RenderLayer>`, annotations, markers): what you
8
+ * write between `<Stage>` and `</Stage>`, or `{#snippet children(page)}` when it needs the page.
9
+ * It's drawn inside the page's content frame, so it turns with the page, and positions are PDF
10
+ * points.
11
+ */
12
+ children: Snippet<[page: PageContextValue]>;
13
+ /**
14
+ * Box-space chrome for each visible page (a page number, a selection border, a per-page
15
+ * button), drawn into the outer box (content and the reserved `pageFrame`). It never turns,
16
+ * and the reserved bands are plain regions: `bottom: 0; height: page.frame.bottom`.
17
+ */
18
+ pageChrome?: Snippet<[page: PageContextValue]>;
19
+ /** Viewport-space UI (menus, controls, `<Scrollbar>`), drawn above the pages. */
20
+ overlay?: Snippet;
21
+ /**
22
+ * The active tool, two-way (`bind:tool`): while it's set, the interaction plugin's active tool
23
+ * follows it, and a tool chosen elsewhere is written back. Leave it out for the default
24
+ * (`useInteraction().activateTool()`).
25
+ */
26
+ tool?: string;
27
+ /** Called on every tool change of this stage's document. */
28
+ onToolChange?: (toolId: string) => void;
29
+ /**
30
+ * The current page's index, from 0, two-way (`bind:page`): the Stage writes it as the reader
31
+ * moves, and goes to the page when you change it.
32
+ */
33
+ page?: number;
34
+ /**
35
+ * The zoom level, two-way (`bind:zoom`): `1` is 100%. The Stage writes it as the zoom changes,
36
+ * and zooms to it when you change it.
37
+ */
38
+ zoom?: number;
39
+ /** The stage lens to drive: the nearest `<StageScope>`'s, else the main view. */
40
+ token?: StageTokenProp;
41
+ class?: string;
42
+ style?: string;
43
+ }
44
+ export type ScrollbarAxis = 'x' | 'y';
45
+ export interface ScrollbarProps {
46
+ axis: ScrollbarAxis;
47
+ /** The stage lens to scroll: the nearest `<StageScope>` / `<Stage>`'s, else the main view. */
48
+ token?: StageTokenProp;
49
+ /**
50
+ * Hide `autoHide` milliseconds after the view stops moving (a drag or the pointer over it
51
+ * keeps it shown; a hidden bar ignores the pointer, like macOS). `false` always shows it.
52
+ * Published as `data-state="visible|hidden"`. Default 1200.
53
+ */
54
+ autoHide?: number | false;
55
+ /** The smallest thumb, in pixels, so it never vanishes on a long document. Default 24. */
56
+ minThumbSize?: number;
57
+ /**
58
+ * A press on the track: `'page'` steps 90% of a view toward the pointer and repeats while
59
+ * held, stopping at the pointer; `'jump'` centers the thumb at the pointer and drags from
60
+ * there. Default `'page'`.
61
+ */
62
+ trackPress?: 'page' | 'jump';
63
+ /**
64
+ * The track's class and style. The default style pins it to the right (y) or bottom (x) edge
65
+ * of the nearest positioned ancestor: the Stage, when it's in the overlay.
66
+ */
67
+ class?: string;
68
+ style?: string;
69
+ thumbClass?: string;
70
+ thumbStyle?: string;
71
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import type { CapabilityToken, EventHook } from '@embedpdf/core';
2
+ import { stageState } from '@embedpdf/plugin-stage';
3
+ import type { StageCapability, StageSettings } from '@embedpdf/plugin-stage';
4
+ import type { ScrollMetrics, StageHostCapability } from '@embedpdf/plugin-stage/contract/host';
5
+ import { type CurrentValue, type MaybeGetter } from '../runtime/values.svelte.js';
6
+ import { type StageTokenProp } from './stage-scope.js';
7
+ /** What `useStageState()` gives: the Stage page's State table. */
8
+ export type StageStateValue = (typeof stageState)['empty'];
9
+ /** A lens argument: a token, a function that returns one, or none (the scope's, else the main view). */
10
+ type LensArgument = MaybeGetter<StageTokenProp | undefined>;
11
+ /** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */
12
+ export declare function useStage(token?: LensArgument): StageCapability;
13
+ /** Subscribe to one stage event while the component lives: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */
14
+ export declare function useStageEvent<T>(select: (stage: StageCapability) => EventHook<T>, handler: (event: T) => void, token?: LensArgument): void;
15
+ /**
16
+ * The view's state: the zoom, the current page, the page count, the view rotation and the
17
+ * responsive rules that apply (declared once in `stageState`), as a reactive object
18
+ * (`state.zoomLevel`). With a selector, the value it picks as `{ current }`. Without a document
19
+ * it is `stageState.empty`.
20
+ */
21
+ export declare function useStageState(token?: StageTokenProp): Readonly<StageStateValue>;
22
+ export declare function useStageState<Selected>(select: (state: StageStateValue) => Selected, token?: LensArgument): CurrentValue<Selected>;
23
+ /**
24
+ * The view's settings, as a reactive object, or the value a selector picks as `{ current }`. They
25
+ * belong to the view, so without a document there is none yet and this reads the defaults.
26
+ * Change them with `useStage().updateSettings()`.
27
+ */
28
+ export declare function useStageSettings(token?: StageTokenProp): Readonly<StageSettings>;
29
+ export declare function useStageSettings<Selected>(select: (settings: StageSettings) => Selected, token?: LensArgument): CurrentValue<Selected>;
30
+ export declare const asHost: (token: CapabilityToken<StageCapability>) => CapabilityToken<StageHostCapability>;
31
+ /**
32
+ * The view's scroll position as a scrolling element has it: `scrollTop`, `scrollHeight`,
33
+ * `clientHeight` and their horizontal twins, in screen pixels, as a reactive object. The raw
34
+ * material for scroll UI of your own; all zero without a document.
35
+ */
36
+ export declare function useScrollMetrics(token?: LensArgument): Readonly<ScrollMetrics>;
37
+ /** {@link useScrollMetrics} for the lens `lens()` names now, for a component whose token is a prop. */
38
+ export declare function scrollMetricsOf(lens: () => StageTokenProp): Readonly<ScrollMetrics>;
39
+ export {};
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The Stage's readers: `useStage()` (the API), `useStageState()`, `useStageSettings()`,
3
+ * `useStageEvent()` and `useScrollMetrics()`. Each takes an optional token; without one it binds
4
+ * to the nearest `<StageScope>` / `<Stage>`, else the main view. A token that changes (a prop) is
5
+ * passed as a function: `useStage(() => token)`, and after a selector,
6
+ * `useStageState((state) => state.zoomLevel, () => token)`.
7
+ */
8
+ import { shallowEqual } from '@embedpdf/core';
9
+ import { DEFAULT_SETTINGS, stageState } from '@embedpdf/plugin-stage';
10
+ import { documentScopeOf, useKernelBinding } from '../runtime/binding.svelte.js';
11
+ import { useCapability, useCapabilityEvent } from '../runtime/readers.svelte.js';
12
+ import { declaredState, readRecord } from '../runtime/state.svelte.js';
13
+ import { derivedValue, valueOf, } from '../runtime/values.svelte.js';
14
+ import { stageTokenOf } from './stage-scope.js';
15
+ /** The lens a reader with this argument binds to, read on every use. */
16
+ const lensOf = (token) => stageTokenOf(() => valueOf(token));
17
+ /** The Stage's API (zoom, navigation, reveal, settings), for app chrome. */
18
+ export function useStage(token) {
19
+ return useCapability(lensOf(token));
20
+ }
21
+ /** Subscribe to one stage event while the component lives: `useStageEvent((stage) => stage.onZoomChanged, handler)`. */
22
+ export function useStageEvent(select, handler, token) {
23
+ useCapabilityEvent(lensOf(token), select, handler);
24
+ }
25
+ /**
26
+ * A reader's arguments: a selector, then an optional lens; or just a token. A function first is
27
+ * always the selector, so a token that changes goes second, as a function.
28
+ */
29
+ function selectorAndLens(first, second) {
30
+ return typeof first === 'function' ? [first, second] : [undefined, first ?? second];
31
+ }
32
+ export function useStageState(first, second) {
33
+ const [select, token] = selectorAndLens(first, second);
34
+ const binding = useKernelBinding();
35
+ const lens = lensOf(token);
36
+ const whole = declaredState(binding, lens, stageState.read, stageState.empty);
37
+ return readRecord(whole, Object.keys(stageState.empty), select);
38
+ }
39
+ export function useStageSettings(first, second) {
40
+ const [select, token] = selectorAndLens(first, second);
41
+ const binding = useKernelBinding();
42
+ const lens = lensOf(token);
43
+ const scoped = documentScopeOf();
44
+ const whole = derivedValue(() => {
45
+ binding.track();
46
+ const stage = binding.kernel.tryCapability(lens(), scoped() ?? undefined);
47
+ return stage?.getSettings() ?? DEFAULT_SETTINGS;
48
+ }, shallowEqual);
49
+ return readRecord(whole, Object.keys(DEFAULT_SETTINGS), select);
50
+ }
51
+ /** What a view with nothing to scroll measures: no document, or not placed yet. */
52
+ const NO_SCROLL = Object.freeze({
53
+ scrollLeft: 0,
54
+ scrollTop: 0,
55
+ scrollWidth: 0,
56
+ scrollHeight: 0,
57
+ clientWidth: 0,
58
+ clientHeight: 0,
59
+ scrollableX: false,
60
+ scrollableY: false,
61
+ });
62
+ // Scroll metrics live on the host lens (the same runtime token, typed wider): a scrollbar is
63
+ // chrome that drives the camera, not a document-level consumer.
64
+ export const asHost = (token) => token;
65
+ /**
66
+ * The view's scroll position as a scrolling element has it: `scrollTop`, `scrollHeight`,
67
+ * `clientHeight` and their horizontal twins, in screen pixels, as a reactive object. The raw
68
+ * material for scroll UI of your own; all zero without a document.
69
+ */
70
+ export function useScrollMetrics(token) {
71
+ return scrollMetricsOf(lensOf(token));
72
+ }
73
+ /** {@link useScrollMetrics} for the lens `lens()` names now, for a component whose token is a prop. */
74
+ export function scrollMetricsOf(lens) {
75
+ const binding = useKernelBinding();
76
+ const scoped = documentScopeOf();
77
+ // The capability keeps the same metrics object until a number moves.
78
+ const whole = derivedValue(() => {
79
+ binding.track();
80
+ const stage = binding.kernel.tryCapability(asHost(lens()), scoped() ?? undefined);
81
+ return stage?.getScrollMetrics() ?? NO_SCROLL;
82
+ });
83
+ return readRecord(whole, Object.keys(NO_SCROLL), undefined);
84
+ }
@@ -0,0 +1,18 @@
1
+ import type { CapabilityToken } from '@embedpdf/core';
2
+ import type { StageCapability } from '@embedpdf/plugin-stage/contract';
3
+ /** Which stage lens to bind to: the main `StageToken`, or a second view's own token. */
4
+ export type StageTokenProp = CapabilityToken<StageCapability>;
5
+ /** For `<Stage>` and `<StageScope>`: bind everything inside to the lens `token()` names. */
6
+ export declare function setStageToken(token: () => StageTokenProp): void;
7
+ /**
8
+ * The lens a stage reader binds to, as a function read on every use: the explicit token, else
9
+ * the nearest `<StageScope>` / `<Stage>`, else the main lens.
10
+ */
11
+ export declare function stageTokenOf(explicit?: () => StageTokenProp | undefined): () => StageTokenProp;
12
+ /**
13
+ * The lens stage chrome of your own binds to, as `{ current }`: `token`, else the nearest
14
+ * `<StageScope>` / `<Stage>`, else the main lens.
15
+ */
16
+ export declare function useStageToken(token?: StageTokenProp): {
17
+ readonly current: StageTokenProp;
18
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The stage lens a subtree binds to, so the stage readers and the stage-bound chrome
3
+ * (`<Scrollbar>`, `<SelectionHandles>`) need no token. `<Stage>` provides its own lens to its
4
+ * pages and overlay; an app wraps a second lens (a thumbnail rail) in `<StageScope token>`. An
5
+ * explicit token still wins everywhere.
6
+ *
7
+ * Apart from `./Stage.svelte`, so entries that only need the scope never pull the stage surface.
8
+ */
9
+ import { getContext, hasContext, setContext } from 'svelte';
10
+ import { StageToken } from '@embedpdf/plugin-stage/contract';
11
+ const STAGE_TOKEN = Symbol('embedpdf.stage-token');
12
+ /** For `<Stage>` and `<StageScope>`: bind everything inside to the lens `token()` names. */
13
+ export function setStageToken(token) {
14
+ setContext(STAGE_TOKEN, token);
15
+ }
16
+ /**
17
+ * The lens a stage reader binds to, as a function read on every use: the explicit token, else
18
+ * the nearest `<StageScope>` / `<Stage>`, else the main lens.
19
+ */
20
+ export function stageTokenOf(explicit) {
21
+ const scoped = hasContext(STAGE_TOKEN) ? getContext(STAGE_TOKEN) : null;
22
+ return () => explicit?.() ?? scoped?.() ?? StageToken;
23
+ }
24
+ /**
25
+ * The lens stage chrome of your own binds to, as `{ current }`: `token`, else the nearest
26
+ * `<StageScope>` / `<Stage>`, else the main lens.
27
+ */
28
+ export function useStageToken(token) {
29
+ const lens = stageTokenOf(() => token);
30
+ return {
31
+ get current() {
32
+ return lens();
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,13 @@
1
+ export interface TwoWayValue<T> {
2
+ /** The Stage's value now. */
3
+ stage: () => T;
4
+ /** The bound prop now; undefined while the owner gives none. */
5
+ bound: () => T | undefined;
6
+ /** Whether the Stage can take a value yet (it has a document). */
7
+ ready: () => boolean;
8
+ /** Write the Stage's value to the bound prop. */
9
+ write: (value: T) => void;
10
+ /** Move the Stage to the owner's value. */
11
+ apply: (value: T) => void;
12
+ }
13
+ export declare function syncTwoWay<T>(value: TwoWayValue<T>): void;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A value the Stage and its owner both write, for `bind:page` and `bind:zoom`: the owner's
3
+ * value goes in when it differs from the Stage's, and the Stage's goes out when it changes.
4
+ */
5
+ import { untrack } from 'svelte';
6
+ export function syncTwoWay(value) {
7
+ // In first: on mount, the owner's value is where the Stage goes, and only then does the
8
+ // Stage's own value come back out.
9
+ $effect(() => {
10
+ const wanted = value.bound();
11
+ if (wanted === undefined || !value.ready())
12
+ return;
13
+ untrack(() => {
14
+ if (!Object.is(wanted, value.stage()))
15
+ value.apply(wanted);
16
+ });
17
+ });
18
+ let last = untrack(value.stage);
19
+ $effect(() => {
20
+ const current = value.stage();
21
+ if (Object.is(current, last))
22
+ return;
23
+ last = current;
24
+ untrack(() => value.write(current));
25
+ });
26
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @embedpdf/svelte/stage — the Stage and its readers.
3
+ *
4
+ * `<Stage>` lays out and positions the visible pages by the camera, and hands each one to its
5
+ * content: you bring the layers. The readers are the plugin's four, `useStage()` (the API),
6
+ * `useStageState()`, `useStageSettings()` and `useStageEvent()`; `<Scrollbar>` and
7
+ * `useScrollMetrics()` are the Stage's too.
8
+ */
9
+ export * from '@embedpdf/plugin-stage';
10
+ export { default as Stage } from './stage/Stage.svelte';
11
+ export { default as StageScope } from './stage/StageScope.svelte';
12
+ export { default as Scrollbar } from './stage/Scrollbar.svelte';
13
+ export type { ScrollbarAxis, ScrollbarProps, StageProps } from './stage/props.js';
14
+ export { useStageToken } from './stage/stage-scope.js';
15
+ export type { StageTokenProp } from './stage/stage-scope.js';
16
+ export { useScrollMetrics, useStage, useStageEvent, useStageSettings, useStageState, } from './stage/readers.svelte.js';
17
+ export type { StageStateValue } from './stage/readers.svelte.js';
package/dist/stage.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @embedpdf/svelte/stage — the Stage and its readers.
3
+ *
4
+ * `<Stage>` lays out and positions the visible pages by the camera, and hands each one to its
5
+ * content: you bring the layers. The readers are the plugin's four, `useStage()` (the API),
6
+ * `useStageState()`, `useStageSettings()` and `useStageEvent()`; `<Scrollbar>` and
7
+ * `useScrollMetrics()` are the Stage's too.
8
+ */
9
+ // Registration travels with the UI.
10
+ export * from '@embedpdf/plugin-stage';
11
+ export { default as Stage } from './stage/Stage.svelte';
12
+ export { default as StageScope } from './stage/StageScope.svelte';
13
+ export { default as Scrollbar } from './stage/Scrollbar.svelte';
14
+ export { useStageToken } from './stage/stage-scope.js';
15
+ export { useScrollMetrics, useStage, useStageEvent, useStageSettings, useStageState, } from './stage/readers.svelte.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The stamp plugin's readers: `useStamp()` (the API), `useStampState()`, `useStampSettings()`,
3
+ * `useStampEvent()`, and the three a picker is built from: `useStampLibraries()`,
4
+ * `useStampAssets()` and `useStampAssetPreviewUrl()`.
5
+ *
6
+ * The plugin owns the libraries, their stamps and the cached previews; the one thing made here is
7
+ * the DOM resource a picker shows, the object URL of a stamp's preview.
8
+ */
9
+ import type { EventHook } from '@embedpdf/core';
10
+ import type { StampAsset, StampAssetFilter, StampCapability, StampLibrary, StampLibraryFilter } from '@embedpdf/plugin-stamp';
11
+ import { type CurrentValue, type MaybeGetter } from '../runtime/values.svelte.js';
12
+ /**
13
+ * The stamp API. Libraries are shared by every document; the placing verbs (`armAsset`,
14
+ * `placeAsset`, …) act on the nearest `<DocumentScope>`'s document, else the active one. A handle:
15
+ * keep it whole and call through it, `stamp.armAsset(id)`.
16
+ */
17
+ export declare function useStamp(): StampCapability;
18
+ /** Subscribe to one stamp event while the component lives: `useStampEvent((stamp) => stamp.onLibraryChanged, handler)`. */
19
+ export declare function useStampEvent<T>(select: (stamp: StampCapability) => EventHook<T>, handler: (event: T) => void): void;
20
+ /**
21
+ * The stamps' state for this component's document: the armed stamp (the page's State table,
22
+ * declared once in `stampState`), as a reactive object (`stampState.armedAsset`). With a
23
+ * selector, the value it picks as `{ current }`.
24
+ */
25
+ export declare const useStampState: import("../index.js").StateReader<{
26
+ armedAsset: StampAsset | null;
27
+ }>;
28
+ /** The stamp settings (`assetEngine`, `previewWidth`, `dynamic`), with or without a document. */
29
+ export declare const useStampSettings: import("../index.js").SettingsReader<import("@embedpdf/plugin-stamp").StampSettings & object>;
30
+ /**
31
+ * The libraries as `{ current }`, optionally of one kind or several (`{ kind: 'stamps' }`,
32
+ * `{ kind: ['stamps', 'legal-seals'] }`; a function to follow a prop). Every read is a new array,
33
+ * compared item by item, so the value changes only when a library is added, renamed or removed.
34
+ */
35
+ export declare function useStampLibraries(filter?: MaybeGetter<StampLibraryFilter | undefined>): CurrentValue<readonly StampLibrary[]>;
36
+ /**
37
+ * The stamps as `{ current }`: one library's (`{ libraryId }`), one kind's or category's, or every
38
+ * stamp, in the order a picker shows them. A filter that changes is a function:
39
+ * `useStampAssets(() => ({ libraryId }))`.
40
+ */
41
+ export declare function useStampAssets(filter?: MaybeGetter<StampAssetFilter | undefined>): CurrentValue<readonly StampAsset[]>;
42
+ /**
43
+ * An image URL for a stamp's cached preview (a gallery thumbnail) as `{ current }`, or null while
44
+ * there is none. An id that changes is a function: `useStampAssetPreviewUrl(() => assetId)`.
45
+ *
46
+ * The plugin holds the preview's bytes; the URL is a DOM resource, so it is made here and revoked
47
+ * when the preview changes or the component goes away, the rule `<AnnotationLayer>` follows for
48
+ * the pictures it shows.
49
+ */
50
+ export declare function useStampAssetPreviewUrl(assetId: MaybeGetter<string | null>): CurrentValue<string | null>;