@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,192 @@
1
+ <!--
2
+ One page on its own, with no Stage: the same layers, rotation and chrome frame as a `<Stage>`
3
+ page, but no camera, scrolling or zoom, and no need for the stage plugin.
4
+
5
+ It builds the page's transform from a width, and provides the same page context a Stage page
6
+ does, so every layer (`<RenderLayer>`, the selection, search matches) works here unchanged. It
7
+ provides a projection too, measured from the page element in client space, so anchored UI (a
8
+ selection menu) works here as well: it sits on `<body>`, where no card or scrolling list clips it.
9
+ -->
10
+ <script lang="ts">
11
+ import { toPageRef } from '@embedpdf/core';
12
+ import { pageTransform, type PageFrame } from '@embedpdf/core-geometry';
13
+ import {
14
+ clientPageProjector,
15
+ livePageContext,
16
+ makePageContext,
17
+ observeClientGeometry,
18
+ pageSurfaceLayout,
19
+ pageViewTransformInput,
20
+ paint,
21
+ } from '@embedpdf/web';
22
+ import {
23
+ setProjectorBinding,
24
+ setShownPages,
25
+ type ProjectorBinding,
26
+ type ShownPages,
27
+ } from '../anchored/context.js';
28
+ import { InteractionToken as InteractionHostToken } from '@embedpdf/plugin-interaction/contract/host';
29
+ import PagePointerSource from '../interaction/PagePointerSource.svelte';
30
+ import DocumentScope from '../runtime/DocumentScope.svelte';
31
+ import { useViewerSettings } from '../runtime/documents.svelte.js';
32
+ import { setPageContext } from '../runtime/page.js';
33
+ import { useDocumentId, useKernelValue, useOptionalCapability } from '../runtime/readers.svelte.js';
34
+ import type { PageViewProps } from './props.js';
35
+
36
+ let {
37
+ page: wanted,
38
+ documentId,
39
+ fallback,
40
+ width = 240,
41
+ pageFrame: framePatch,
42
+ children,
43
+ pageChrome,
44
+ class: className,
45
+ style,
46
+ }: PageViewProps = $props();
47
+
48
+ // The document a <DocumentScope> around it names, else the active one.
49
+ const inScope = useDocumentId();
50
+ const docId = $derived(documentId ?? inScope.current);
51
+ // With the interaction plugin registered, the page view is the page's pointer surface.
52
+ const interaction = useOptionalCapability(InteractionHostToken);
53
+
54
+ // The page asked for: by its ref (which follows it when pages move) or by its index.
55
+ const wantedObjectNumber = $derived(typeof wanted === 'number' ? null : wanted.objectNumber);
56
+ const wantedIndex = $derived(typeof wanted === 'number' ? wanted : null);
57
+
58
+ // The page's entry in the document's page list, subscribed: a rotation or a move redraws this
59
+ // surface as it redraws a Stage page. Entries keep their identity until the page changes.
60
+ const base = useKernelValue((kernel) => {
61
+ if (!docId) return null;
62
+ const pages = kernel.documents.listPages(docId);
63
+ const found =
64
+ wantedObjectNumber !== null
65
+ ? pages.find((info) => info.ref.objectNumber === wantedObjectNumber)
66
+ : pages[wantedIndex ?? 0];
67
+ return found ?? null;
68
+ });
69
+
70
+ const pageIndex = $derived(base.current?.index ?? wantedIndex ?? 0);
71
+ // The page's address is the document's; a stand-in (object number = index + 1) only until the
72
+ // page list is known, and the fallback shows until then anyway. Derived from the number, so
73
+ // the context's `ref` keeps its identity while the number stays (layers key work on it).
74
+ const pageObjectNumber = $derived(
75
+ base.current?.ref.objectNumber ?? wantedObjectNumber ?? pageIndex + 1,
76
+ );
77
+ const pageRef = $derived(toPageRef(pageObjectNumber));
78
+
79
+ // The sides left out reserve nothing; built from the four numbers, so an equal frame passed
80
+ // again as a new object changes nothing.
81
+ const frameTop = $derived(framePatch?.top ?? 0);
82
+ const frameRight = $derived(framePatch?.right ?? 0);
83
+ const frameBottom = $derived(framePatch?.bottom ?? 0);
84
+ const frameLeft = $derived(framePatch?.left ?? 0);
85
+ const frame: PageFrame = $derived({
86
+ top: frameTop,
87
+ right: frameRight,
88
+ bottom: frameBottom,
89
+ left: frameLeft,
90
+ });
91
+
92
+ // What the transform depends on, each a value of its own, so an entry that changed something
93
+ // else keeps the transform.
94
+ const pageWidth = $derived(base.current?.size.width ?? null);
95
+ const pageHeight = $derived(base.current?.size.height ?? null);
96
+ const userUnit = $derived(base.current?.userUnit ?? 1);
97
+ const rotation = $derived(base.current?.rotation ?? 0);
98
+
99
+ // No camera: the transform comes from the width asked for (`pageViewTransformInput` from
100
+ // `@embedpdf/web`, the same in every framework).
101
+ const transform = $derived(
102
+ pageTransform(
103
+ pageViewTransformInput(
104
+ pageWidth !== null && pageHeight !== null
105
+ ? { size: { width: pageWidth, height: pageHeight }, rotation, userUnit }
106
+ : null,
107
+ width,
108
+ typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1,
109
+ ),
110
+ ),
111
+ );
112
+
113
+ // The page content box, once it's in the DOM: the client conversions measure it, and the
114
+ // projection answers nothing until it's there.
115
+ let contentElement = $state<HTMLDivElement | null>(null);
116
+
117
+ // This instance's view identity: two page views of one page (a compare strip) plan their
118
+ // rasters apart, as two stage lenses do.
119
+ const viewId = $props.id();
120
+ const context = $derived(
121
+ makePageContext(docId ?? '', `page-view:${viewId}`, pageRef, pageIndex, frame, transform, () =>
122
+ contentElement!.getBoundingClientRect(),
123
+ ),
124
+ );
125
+ const live = livePageContext(() => context);
126
+ setPageContext(live);
127
+
128
+ // The projection: no camera, so anchored UI is placed by measuring the page element in client
129
+ // space. `mounted` reads the element, a state value, so a placement computed before the
130
+ // element existed is computed again once it does. A new context (a new transform or page) is
131
+ // the binding's revision; `observeClientGeometry` reports what no state announces (the
132
+ // document scrolled, the window resized).
133
+ const projectorBinding: ProjectorBinding = $derived({
134
+ projector: clientPageProjector(context, () => contentElement !== null),
135
+ revision: context,
136
+ subscribe: observeClientGeometry,
137
+ });
138
+ setProjectorBinding(() => projectorBinding);
139
+ // A page view shows its one page.
140
+ const shownPages: ShownPages = $derived(new Set([pageObjectNumber]));
141
+ setShownPages(() => shownPages);
142
+
143
+ // The viewer's `page` settings; `--epdf-page-*` CSS variables win over them.
144
+ const look = useViewerSettings((settings) => settings.page);
145
+
146
+ // The outer box, the shadow at the footprint, and the content box centred on it and turned
147
+ // about its centre, as on a Stage page.
148
+ const layout = $derived(pageSurfaceLayout(transform, frame));
149
+ </script>
150
+
151
+ {#if docId && base.current}
152
+ <DocumentScope id={docId}>
153
+ <div
154
+ class={className}
155
+ style="position: relative; width: {layout.outer.width}px; height: {layout.outer
156
+ .height}px;{style ? ` ${style}` : ''}"
157
+ >
158
+ <!-- The drop shadow only: at the footprint, with no fill, so it never shows behind the
159
+ picture, and it stays put while the page turns. -->
160
+ <div
161
+ style:position="absolute"
162
+ style:left="{layout.shadow.left}px"
163
+ style:top="{layout.shadow.top}px"
164
+ style:width="{layout.shadow.width}px"
165
+ style:height="{layout.shadow.height}px"
166
+ style:box-shadow={paint('page-shadow', look.current.shadow)}
167
+ ></div>
168
+ <!-- The page: its background and its layers as one box, the only thing the rotation turns.
169
+ Native text and image selection is off: the selection layer draws its own. -->
170
+ <div
171
+ bind:this={contentElement}
172
+ style="user-select: none; -webkit-user-select: none"
173
+ style:position="absolute"
174
+ style:left="{layout.content.left}px"
175
+ style:top="{layout.content.top}px"
176
+ style:width="{layout.content.width}px"
177
+ style:height="{layout.content.height}px"
178
+ style:background={paint('page-background', look.current.background)}
179
+ style:transform={layout.turn ?? undefined}
180
+ >
181
+ <!-- The page's pointer surface, below the layers, like the Stage's own listener: a press
182
+ on a layer that lets the pointer through reaches the tools, a link keeps its own. -->
183
+ {#if interaction.current}<PagePointerSource />{/if}
184
+ {@render children(live)}
185
+ </div>
186
+ <!-- The page chrome fills the outer box and never turns. -->
187
+ {@render pageChrome?.(live)}
188
+ </div>
189
+ </DocumentScope>
190
+ {:else}
191
+ {@render fallback?.()}
192
+ {/if}
@@ -0,0 +1,4 @@
1
+ import type { PageViewProps } from './props.js';
2
+ declare const PageView: import("svelte").Component<PageViewProps, {}, "">;
3
+ type PageView = ReturnType<typeof PageView>;
4
+ export default PageView;
@@ -0,0 +1,32 @@
1
+ /** The props of `<PageView>`. */
2
+ import type { Snippet } from 'svelte';
3
+ import type { PageRef } from '@embedpdf/core';
4
+ import type { PageFrame } from '@embedpdf/core-geometry';
5
+ import type { PageContextValue } from '../runtime/page.js';
6
+ export interface PageViewProps {
7
+ /** The page: its `ref`, which follows it when pages move, or its index, from 0. */
8
+ page: PageRef | number;
9
+ /** Which document to show. Defaults to the active document. */
10
+ documentId?: string;
11
+ /** Shown while the document or the page is not available yet (default: nothing). */
12
+ fallback?: Snippet;
13
+ /** The width of the page upright, in pixels: a page turned a quarter is that tall. Default 240. */
14
+ width?: number;
15
+ /** Space reserved around the page for your own labels, in pixels per side; the sides left out are 0. */
16
+ pageFrame?: Partial<PageFrame>;
17
+ /**
18
+ * Page-space content (`<RenderLayer>`, the selection, search matches): what you write between
19
+ * `<PageView>` and `</PageView>`, or `{#snippet children(page)}` when it needs the page. It turns
20
+ * with the page, and positions are PDF points.
21
+ */
22
+ children: Snippet<[page: PageContextValue]>;
23
+ /**
24
+ * Box-space chrome (a label, a border) drawn into the outer box, page and `pageFrame`. It never
25
+ * turns, like `<Stage>`'s `pageChrome`.
26
+ */
27
+ pageChrome?: Snippet<[page: PageContextValue]>;
28
+ /** On the outer box, after its own. */
29
+ class?: string;
30
+ /** On the outer box, after its own. */
31
+ style?: string;
32
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @embedpdf/svelte/page-view — one page on its own, with no Stage.
3
+ *
4
+ * `<PageView>` shows a page anywhere in an app (a preview in a card, a page picker) with the same
5
+ * layers, rotation and chrome frame as a Stage page, and anchored UI that works inside it.
6
+ */
7
+ export { default as PageView } from './page-view/PageView.svelte';
8
+ export type { PageViewProps } from './page-view/props.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @embedpdf/svelte/page-view — one page on its own, with no Stage.
3
+ *
4
+ * `<PageView>` shows a page anywhere in an app (a preview in a card, a page picker) with the same
5
+ * layers, rotation and chrome frame as a Stage page, and anchored UI that works inside it.
6
+ */
7
+ export { default as PageView } from './page-view/PageView.svelte';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The redaction plugin's readers: `useRedaction()` (the API), `useRedactionState()`,
3
+ * `useRedactionSettings()`, `useRedactionEvent()`, and `usePendingRedactions()` for a list of the
4
+ * marks waiting to be applied.
5
+ */
6
+ import type { EventHook } from '@embedpdf/core';
7
+ import { redactionState } from '@embedpdf/plugin-redaction';
8
+ import type { RedactionCapability, RedactionMark, RedactionMarkFilter } from '@embedpdf/plugin-redaction';
9
+ import { type StateReader } from '../runtime/state.svelte.js';
10
+ import { type CurrentValue, type MaybeGetter } from '../runtime/values.svelte.js';
11
+ /**
12
+ * The redaction API (marking, the pending marks, applying them) of the nearest
13
+ * `<DocumentScope>`'s document, else the active one. Outside a ready document every method throws
14
+ * `not-ready`.
15
+ */
16
+ export declare function useRedaction(): RedactionCapability;
17
+ /** Subscribe to one redaction event while the component lives: `useRedactionEvent((redaction) => redaction.onApplied, handler)`. */
18
+ export declare function useRedactionEvent<T>(select: (redaction: RedactionCapability) => EventHook<T>, handler: (event: T) => void): void;
19
+ /**
20
+ * The redaction state: how many marks wait, whether marks are being applied, and the last apply's
21
+ * result (the page's State table, declared once in `redactionState`), as a reactive object
22
+ * (`state.pendingCount`). With a selector, the value it picks as `{ current }`. Empty without a
23
+ * document.
24
+ *
25
+ * Typed through the declaration, so the published types name it from `@embedpdf/plugin-redaction`, a
26
+ * dependency of this package, and not from the engine package `lastResult`'s type comes from, which
27
+ * isn't one.
28
+ */
29
+ export declare const useRedactionState: StateReader<ReturnType<typeof redactionState.read>>;
30
+ /** The redaction settings (`overlay`), with or without a document. */
31
+ export declare const useRedactionSettings: import("../index.js").SettingsReader<import("@embedpdf/plugin-redaction").RedactionSettings & object>;
32
+ /**
33
+ * The marks waiting to be applied, in page order, or one page's (its ref or its index; a function
34
+ * to follow a prop), as `{ current }`. The plugin's lists keep their identity until the marks
35
+ * change, so a list redraws only then. Empty without a document.
36
+ */
37
+ export declare function usePendingRedactions(filter?: MaybeGetter<RedactionMarkFilter | undefined>): CurrentValue<readonly RedactionMark[]>;
@@ -0,0 +1,38 @@
1
+ import { RedactionToken, redactionState } from '@embedpdf/plugin-redaction';
2
+ import { shallowArray, useCapability, useCapabilityEvent, useOptionalSelector, } from '../runtime/readers.svelte.js';
3
+ import { settingsReader, stateReader } from '../runtime/state.svelte.js';
4
+ import { valueOf } from '../runtime/values.svelte.js';
5
+ /**
6
+ * The redaction API (marking, the pending marks, applying them) of the nearest
7
+ * `<DocumentScope>`'s document, else the active one. Outside a ready document every method throws
8
+ * `not-ready`.
9
+ */
10
+ export function useRedaction() {
11
+ return useCapability(RedactionToken);
12
+ }
13
+ /** Subscribe to one redaction event while the component lives: `useRedactionEvent((redaction) => redaction.onApplied, handler)`. */
14
+ export function useRedactionEvent(select, handler) {
15
+ useCapabilityEvent(RedactionToken, select, handler);
16
+ }
17
+ /**
18
+ * The redaction state: how many marks wait, whether marks are being applied, and the last apply's
19
+ * result (the page's State table, declared once in `redactionState`), as a reactive object
20
+ * (`state.pendingCount`). With a selector, the value it picks as `{ current }`. Empty without a
21
+ * document.
22
+ *
23
+ * Typed through the declaration, so the published types name it from `@embedpdf/plugin-redaction`, a
24
+ * dependency of this package, and not from the engine package `lastResult`'s type comes from, which
25
+ * isn't one.
26
+ */
27
+ export const useRedactionState = stateReader(redactionState);
28
+ /** The redaction settings (`overlay`), with or without a document. */
29
+ export const useRedactionSettings = settingsReader(RedactionToken);
30
+ const NO_MARKS = Object.freeze([]);
31
+ /**
32
+ * The marks waiting to be applied, in page order, or one page's (its ref or its index; a function
33
+ * to follow a prop), as `{ current }`. The plugin's lists keep their identity until the marks
34
+ * change, so a list redraws only then. Empty without a document.
35
+ */
36
+ export function usePendingRedactions(filter) {
37
+ return useOptionalSelector(RedactionToken, (redaction) => redaction.listPending(valueOf(filter)), NO_MARKS, shallowArray);
38
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @embedpdf/svelte/redaction — marking content and removing it for good.
3
+ *
4
+ * Marking rides the annotation plugin (the composed `redact` tool draws on its
5
+ * `<AnnotationLayer>`); these readers surface the workflow around it:
6
+ *
7
+ * const redaction = useRedaction();
8
+ * const state = useRedactionState(); // state.pendingCount, state.applying
9
+ * await redaction.applyAll(); // irreversible: confirm first
10
+ */
11
+ export * from '@embedpdf/plugin-redaction';
12
+ export { usePendingRedactions, useRedaction, useRedactionEvent, useRedactionSettings, useRedactionState, } from './redaction/readers.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @embedpdf/svelte/redaction — marking content and removing it for good.
3
+ *
4
+ * Marking rides the annotation plugin (the composed `redact` tool draws on its
5
+ * `<AnnotationLayer>`); these readers surface the workflow around it:
6
+ *
7
+ * const redaction = useRedaction();
8
+ * const state = useRedactionState(); // state.pendingCount, state.applying
9
+ * await redaction.applyAll(); // irreversible: confirm first
10
+ */
11
+ // Registration travels with the UI.
12
+ export * from '@embedpdf/plugin-redaction';
13
+ export { usePendingRedactions, useRedaction, useRedactionEvent, useRedactionSettings, useRedactionState, } from './redaction/readers.js';
@@ -0,0 +1,116 @@
1
+ <!--
2
+ The page raster, as two planes:
3
+
4
+ - the base plane: one whole-page image at the plugin's resolved render points (the exact
5
+ settled demand capped at the pixel budget on a local engine, the advertised ladder on a
6
+ cloud deployment). Always there; the instant backdrop.
7
+ - the tile plane: the plugin's paint plan above it, which engages only when the view wants
8
+ more pixels than the base may spend. A thumbnail-sized demand engages nothing.
9
+
10
+ The layer only paints: every decision (which pixels, when, what to keep) is the render
11
+ plugin's. Anything mid-gesture is scaled by CSS until the plugin hands down new pixels.
12
+ -->
13
+ <script lang="ts">
14
+ import { untrack } from 'svelte';
15
+ import { RenderToken } from '@embedpdf/plugin-render/contract/host';
16
+ import { partsDrawnTwice, pictureLayerOptionsOf } from '@embedpdf/web';
17
+ import { devWarn } from '../runtime/dev.js';
18
+ import { usePage } from '../runtime/page.js';
19
+ import { usePaintedParts } from '../runtime/page-layers.svelte.js';
20
+ import { useOptionalCapability, useOptionalSelector } from '../runtime/readers.svelte.js';
21
+ import type { RenderLayerProps } from './props.js';
22
+ import TilePlane from './TilePlane.svelte';
23
+
24
+ let { annotations, formFields, tiles = true }: RenderLayerProps = $props();
25
+
26
+ const page = usePage();
27
+ // The layer is a host of the render plugin: it paints conformed sources and drives a view's
28
+ // tile demand. The host lens is the same runtime token, typed wider.
29
+ const render = useOptionalCapability(RenderToken);
30
+ // The same object until a setting it reads changes.
31
+ const settings = useOptionalSelector(
32
+ RenderToken,
33
+ (renderer) => renderer.getPaintSettings(),
34
+ null,
35
+ );
36
+
37
+ const painted = usePaintedParts(() => page.ref);
38
+ const may = useOptionalSelector(RenderToken, (renderer) => renderer.getLayerRights(), null);
39
+ const parts = $derived({ annotations, formFields });
40
+ /** The parts the picture draws; `null` until the page's layers have said what they paint. */
41
+ const layers = $derived(
42
+ painted.current && may.current
43
+ ? pictureLayerOptionsOf(painted.current, parts, may.current)
44
+ : null,
45
+ );
46
+ $effect(() => {
47
+ if (painted.current && partsDrawnTwice(painted.current, parts).length > 0) {
48
+ devWarn(
49
+ 'render-layer-draws-what-a-layer-paints',
50
+ '<RenderLayer annotations> or <RenderLayer formFields> draws what an <AnnotationLayer> ' +
51
+ 'or a <FormLayer> on the page paints too, so it shows twice. Leave the prop out: the ' +
52
+ 'picture leaves out what a layer paints.',
53
+ );
54
+ }
55
+ });
56
+
57
+ // The raster's identity: its conformed width, the parts it draws and the epoch. Under a
58
+ // ladder it moves only at rung crossings; on a local engine it follows the demand up to the
59
+ // budget, so the deep-zoom backdrop never fetches again.
60
+ const sourceKey = useOptionalSelector(
61
+ RenderToken,
62
+ (renderer) =>
63
+ layers
64
+ ? renderer.getSourceKey(page.ref, { scale: page.transform.renderScale, ...layers })
65
+ : null,
66
+ null,
67
+ );
68
+
69
+ let image: HTMLImageElement | undefined;
70
+
71
+ $effect(() => {
72
+ const renderer = render.current;
73
+ const key = sourceKey.current;
74
+ const ref = page.ref;
75
+ const drawn = layers;
76
+ if (!renderer || key === null || !drawn) return;
77
+ const controller = new AbortController();
78
+ let revoke: (() => void) | undefined;
79
+ // Any scale that maps to this key gives this key's request: the plugin conforms it.
80
+ const options = untrack(() => ({
81
+ scale: page.transform.renderScale,
82
+ ...drawn,
83
+ view: page.view,
84
+ }));
85
+ void (async () => {
86
+ try {
87
+ const raster = await renderer.renderSource(ref, { ...options, signal: controller.signal });
88
+ const objectUrl = await raster.objectUrl().abortWith(controller.signal);
89
+ if (controller.signal.aborted) {
90
+ objectUrl.revoke();
91
+ return;
92
+ }
93
+ revoke = objectUrl.revoke;
94
+ // The source is set on a stable element: the browser keeps the old picture until the new
95
+ // one decodes, and never asks for the old URL again, so revoking it on cleanup is safe.
96
+ if (image) image.src = objectUrl.url;
97
+ } catch {
98
+ // Cancelled (the camera moved, the layer unmounted) or the render failed.
99
+ }
100
+ })();
101
+ return () => {
102
+ controller.abort();
103
+ revoke?.();
104
+ };
105
+ });
106
+ </script>
107
+
108
+ <img
109
+ bind:this={image}
110
+ alt=""
111
+ draggable="false"
112
+ style="position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none"
113
+ />
114
+ {#if tiles && settings.current?.tiles && layers}
115
+ <TilePlane {layers} />
116
+ {/if}
@@ -0,0 +1,4 @@
1
+ import type { RenderLayerProps } from './props.js';
2
+ declare const RenderLayer: import("svelte").Component<RenderLayerProps, {}, "">;
3
+ type RenderLayer = ReturnType<typeof RenderLayer>;
4
+ export default RenderLayer;
@@ -0,0 +1,65 @@
1
+ <!--
2
+ One tile: its place, and the shared browser binding (`bindPaintedImage`), which hides the
3
+ image until it's complete, reports it painted after its first chance to show, and reports the
4
+ inverse when it leaves the DOM.
5
+ -->
6
+ <script lang="ts" module>
7
+ let warnedTileSize = false;
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ import { untrack } from 'svelte';
12
+ import type { TilePaintSource } from '@embedpdf/plugin-render/contract/host';
13
+ import { bindPaintedImage } from '@embedpdf/web';
14
+
15
+ let {
16
+ source,
17
+ viewScale,
18
+ onPainted,
19
+ onUnpainted,
20
+ }: {
21
+ source: TilePaintSource;
22
+ /** View pixels per page point: tiles are placed in view pixels. */
23
+ viewScale: number;
24
+ onPainted: () => void;
25
+ onUnpainted: () => void;
26
+ } = $props();
27
+
28
+ let image: HTMLImageElement | undefined;
29
+
30
+ // Bound per bitmap: the handle is the tile's identity. A new plan hands the same tile a new
31
+ // `source` object; only a new handle binds again (a new URL, hidden until it has loaded).
32
+ const handle = $derived(source.handle);
33
+ $effect(() => {
34
+ const picture = handle;
35
+ if (!image) return;
36
+ return untrack(() => bindPaintedImage(image!, picture, { onPainted, onUnpainted }));
37
+ });
38
+
39
+ /** A raster whose size disagrees with its box would be stretched into place: say so once. */
40
+ function checkSize(event: Event) {
41
+ const naturalWidth = (event.currentTarget as HTMLImageElement).naturalWidth;
42
+ if (naturalWidth <= 0 || warnedTileSize) return;
43
+ const expected = Math.round(source.rect.width * source.scale);
44
+ if (Math.abs(naturalWidth - expected) > 1) {
45
+ warnedTileSize = true;
46
+ console.warn(
47
+ `[render] tile bitmap ${naturalWidth}px wide does not match its rect ` +
48
+ `(expected ~${expected}px) — stale raster identity? key=${source.key}`,
49
+ );
50
+ }
51
+ }
52
+ </script>
53
+
54
+ <img
55
+ bind:this={image}
56
+ alt=""
57
+ draggable="false"
58
+ onload={checkSize}
59
+ style:position="absolute"
60
+ style:left="{source.rect.x * viewScale}px"
61
+ style:top="{source.rect.y * viewScale}px"
62
+ style:width="{source.rect.width * viewScale}px"
63
+ style:height="{source.rect.height * viewScale}px"
64
+ style:z-index={source.z}
65
+ />
@@ -0,0 +1,11 @@
1
+ import type { TilePaintSource } from '@embedpdf/plugin-render/contract/host';
2
+ type $$ComponentProps = {
3
+ source: TilePaintSource;
4
+ /** View pixels per page point: tiles are placed in view pixels. */
5
+ viewScale: number;
6
+ onPainted: () => void;
7
+ onUnpainted: () => void;
8
+ };
9
+ declare const TileImage: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type TileImage = ReturnType<typeof TileImage>;
11
+ export default TileImage;
@@ -0,0 +1,88 @@
1
+ <!--
2
+ The sharp plane above the base: the render plugin's paint plan for this view and page, as
3
+ keyed images. Each tile reports itself painted after its first chance to show (and the inverse
4
+ when it leaves), so older, coarser tiles are released only once their replacement shows.
5
+
6
+ Tiles are placed in view pixels, never in page points under a scaled container: browsers round
7
+ layout lengths to 1/64 of a CSS pixel before transforms apply, and under a large zoom transform
8
+ that rounding becomes visible seams. In view pixels it stays 1/64 of a pixel at every zoom.
9
+ -->
10
+ <script lang="ts">
11
+ import { untrack } from 'svelte';
12
+ import {
13
+ RenderToken,
14
+ type PageLayerOptions,
15
+ type ViewDemand,
16
+ } from '@embedpdf/plugin-render/contract/host';
17
+ import { usePage } from '../runtime/page.js';
18
+ import { useKernelValue, useOptionalCapability } from '../runtime/readers.svelte.js';
19
+ import TileImage from './TileImage.svelte';
20
+
21
+ let { layers }: { layers: PageLayerOptions } = $props();
22
+
23
+ const page = usePage();
24
+ const render = useOptionalCapability(RenderToken);
25
+
26
+ // Which page and which view this plane plans for. The page context is a new value whenever the
27
+ // camera changes it; these change only when the page or the view does (a `<PageView>` can be
28
+ // given another page), so the handle and the page's claim on it follow ownership, never the
29
+ // camera.
30
+ const pageRef = $derived(page.ref);
31
+ const viewId = $derived(page.view);
32
+
33
+ // This view's tile surface: planning state is per view and page, so a thumbnail rail's demand
34
+ // can't disturb the main view's tiles. Reference-counted: one `dispose` per `createViewDemand`.
35
+ let view = $state.raw<ViewDemand | null>(null);
36
+ $effect(() => {
37
+ const renderer = render.current;
38
+ const id = viewId;
39
+ if (!renderer) return;
40
+ const created = untrack(() => renderer.createViewDemand(id));
41
+ view = created;
42
+ return () => created.dispose();
43
+ });
44
+
45
+ // Demand in, plan out: setting the demand is the one call that schedules fetches and plans
46
+ // again. Before the DOM updates, and again on every camera frame (the page's view demand reads
47
+ // the frame), so a move plans before the browser paints.
48
+ $effect.pre(() => {
49
+ const surface = view;
50
+ if (!surface) return;
51
+ const demand = page.getViewDemand?.() ?? { desiredDeviceWidth: page.transform.deviceWidth };
52
+ const ref = pageRef;
53
+ const drawn = layers;
54
+ untrack(() => surface.setDemand(ref, demand, drawn));
55
+ });
56
+
57
+ // The same object until the demand, an epoch or an arriving tile changes it.
58
+ const plan = useKernelValue(() => view?.getPlan(pageRef) ?? null);
59
+
60
+ // Leaving the page (another page, or the plane unmounts) stops its in-flight fetches; the bytes
61
+ // already here stay cached.
62
+ $effect(() => {
63
+ const surface = view;
64
+ const ref = pageRef;
65
+ if (!surface) return;
66
+ return () => surface.release(ref);
67
+ });
68
+ </script>
69
+
70
+ {#if plan.current && plan.current.paint.length > 0}
71
+ <!-- A stacking context of its own: tiles carry z-index ranks (coarse under fine) while two
72
+ generations mix, mid-zoom, and without it those ranks would paint above the annotations,
73
+ the page chrome and the menus. -->
74
+ <div
75
+ style="position: absolute; left: 0; top: 0; pointer-events: none; isolation: isolate"
76
+ style:width="{page.transform.contentWidth}px"
77
+ style:height="{page.transform.contentHeight}px"
78
+ >
79
+ {#each plan.current.paint as source (source.key)}
80
+ <TileImage
81
+ {source}
82
+ viewScale={page.transform.viewScale}
83
+ onPainted={() => view?.markPainted(pageRef, source.key)}
84
+ onUnpainted={() => view?.markUnpainted(pageRef, source.key)}
85
+ />
86
+ {/each}
87
+ </div>
88
+ {/if}
@@ -0,0 +1,7 @@
1
+ import { type PageLayerOptions } from '@embedpdf/plugin-render/contract/host';
2
+ type $$ComponentProps = {
3
+ layers: PageLayerOptions;
4
+ };
5
+ declare const TilePlane: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type TilePlane = ReturnType<typeof TilePlane>;
7
+ export default TilePlane;