@embedpdf/viewer-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 (39) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +77 -2
  3. package/dist/component.svelte +105 -0
  4. package/dist/component.svelte.d.ts +4 -0
  5. package/dist/config.d.ts +20 -0
  6. package/dist/config.js +37 -0
  7. package/dist/core.d.ts +33 -0
  8. package/dist/core.js +29 -0
  9. package/dist/hooks.d.ts +21 -0
  10. package/dist/hooks.js +27 -0
  11. package/dist/index.d.ts +28 -0
  12. package/dist/index.js +25 -0
  13. package/dist/parts.d.ts +15 -0
  14. package/dist/parts.js +12 -0
  15. package/dist/props.d.ts +45 -0
  16. package/dist/props.js +1 -0
  17. package/dist/slots/Panel.svelte +15 -0
  18. package/dist/slots/Panel.svelte.d.ts +4 -0
  19. package/dist/slots/Region.svelte +15 -0
  20. package/dist/slots/Region.svelte.d.ts +4 -0
  21. package/dist/slots/ToolbarItem.svelte +27 -0
  22. package/dist/slots/ToolbarItem.svelte.d.ts +4 -0
  23. package/dist/slots/item-context.svelte.d.ts +27 -0
  24. package/dist/slots/item-context.svelte.js +73 -0
  25. package/dist/slots/props.d.ts +22 -0
  26. package/dist/slots/props.js +1 -0
  27. package/dist/slots.d.ts +12 -0
  28. package/dist/slots.js +11 -0
  29. package/dist/viewer-components/Button.svelte +22 -0
  30. package/dist/viewer-components/Button.svelte.d.ts +4 -0
  31. package/dist/viewer-components/Menu.svelte +13 -0
  32. package/dist/viewer-components/Menu.svelte.d.ts +4 -0
  33. package/dist/viewer-components/MenuItem.svelte +15 -0
  34. package/dist/viewer-components/MenuItem.svelte.d.ts +4 -0
  35. package/dist/viewer-components/props.d.ts +31 -0
  36. package/dist/viewer-components/props.js +1 -0
  37. package/dist/viewer-components.d.ts +10 -0
  38. package/dist/viewer-components.js +9 -0
  39. package/package.json +67 -3
package/LICENSE ADDED
@@ -0,0 +1,192 @@
1
+ Copyright 2026 CloudPDF LTD
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use files in this distribution except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ https://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ -----------------------------------------------------------------------
16
+
17
+ Apache License
18
+ Version 2.0, January 2004
19
+ http://www.apache.org/licenses/
20
+
21
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
22
+
23
+ 1. Definitions.
24
+
25
+ "License" shall mean the terms and conditions for use, reproduction,
26
+ and distribution as defined by Sections 1 through 9 of this document.
27
+
28
+ "Licensor" shall mean the copyright owner or entity authorized by
29
+ the copyright owner that is granting the License.
30
+
31
+ "Legal Entity" shall mean the union of the acting entity and all
32
+ other entities that control, are controlled by, or are under common
33
+ control with that entity. For the purposes of this definition,
34
+ "control" means (i) the power, direct or indirect, to cause the
35
+ direction or management of such entity, whether by contract or
36
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
37
+ outstanding shares, or (iii) beneficial ownership of such entity.
38
+
39
+ "You" (or "Your") shall mean an individual or Legal Entity
40
+ exercising permissions granted by this License.
41
+
42
+ "Source" form shall mean the preferred form for making modifications,
43
+ including but not limited to software source code, documentation
44
+ source, and configuration files.
45
+
46
+ "Object" form shall mean any form resulting from mechanical
47
+ transformation or translation of a Source form, including but
48
+ not limited to compiled object code, generated documentation,
49
+ and conversions to other media types.
50
+
51
+ "Work" shall mean the work of authorship, whether in Source or
52
+ Object form, made available under the License, as indicated by a
53
+ copyright notice that is included in or attached to the work
54
+ (an example is provided in the Appendix below).
55
+
56
+ "Derivative Works" shall mean any work, whether in Source or Object
57
+ form, that is based on (or derived from) the Work and for which the
58
+ editorial revisions, annotations, elaborations, or other modifications
59
+ represent, as a whole, an original work of authorship. For the purposes
60
+ of this License, Derivative Works shall not include works that remain
61
+ separable from, or merely link (or bind by name) to the interfaces of,
62
+ the Work and Derivative Works thereof.
63
+
64
+ "Contribution" shall mean any work of authorship, including
65
+ the original version of the Work and any modifications or additions
66
+ to that Work or Derivative Works thereof, that is intentionally
67
+ submitted to Licensor for inclusion in the Work by the copyright owner
68
+ or by an individual or Legal Entity authorized to submit on behalf of
69
+ the copyright owner. For the purposes of this definition, "submitted"
70
+ means any form of electronic, verbal, or written communication sent
71
+ to the Licensor or its representatives, including but not limited to
72
+ communication on electronic mailing lists, source code control systems,
73
+ and issue tracking systems that are managed by, or on behalf of, the
74
+ Licensor for the purpose of discussing and improving the Work, but
75
+ excluding communication that is conspicuously marked or otherwise
76
+ designated in writing by the copyright owner as "Not a Contribution."
77
+
78
+ "Contributor" shall mean Licensor and any individual or Legal Entity
79
+ on behalf of whom a Contribution has been received by Licensor and
80
+ subsequently incorporated within the Work.
81
+
82
+ 2. Grant of Copyright License. Subject to the terms and conditions of
83
+ this License, each Contributor hereby grants to You a perpetual,
84
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
85
+ copyright license to reproduce, prepare Derivative Works of,
86
+ publicly display, publicly perform, sublicense, and distribute the
87
+ Work and such Derivative Works in Source or Object form.
88
+
89
+ 3. Grant of Patent License. Subject to the terms and conditions of
90
+ this License, each Contributor hereby grants to You a perpetual,
91
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
92
+ (except as stated in this section) patent license to make, have made,
93
+ use, offer to sell, sell, import, and otherwise transfer the Work,
94
+ where such license applies only to those patent claims licensable
95
+ by such Contributor that are necessarily infringed by their
96
+ Contribution(s) alone or by combination of their Contribution(s)
97
+ with the Work to which such Contribution(s) was submitted. If You
98
+ institute patent litigation against any entity (including a
99
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
100
+ or a Contribution incorporated within the Work constitutes direct
101
+ or contributory patent infringement, then any patent licenses
102
+ granted to You under this License for that Work shall terminate
103
+ as of the date such litigation is filed.
104
+
105
+ 4. Redistribution. You may reproduce and distribute copies of the
106
+ Work or Derivative Works thereof in any medium, with or without
107
+ modifications, and in Source or Object form, provided that You
108
+ meet the following conditions:
109
+
110
+ (a) You must give any other recipients of the Work or
111
+ Derivative Works a copy of this License; and
112
+
113
+ (b) You must cause any modified files to carry prominent notices
114
+ stating that You changed the files; and
115
+
116
+ (c) You must retain, in the Source form of any Derivative Works
117
+ that You distribute, all copyright, patent, trademark, and
118
+ attribution notices from the Source form of the Work,
119
+ excluding those notices that do not pertain to any part of
120
+ the Derivative Works; and
121
+
122
+ (d) If the Work includes a "NOTICE" text file as part of its
123
+ distribution, then any Derivative Works that You distribute must
124
+ include a readable copy of the attribution notices contained
125
+ within such NOTICE file, excluding those notices that do not
126
+ pertain to any part of the Derivative Works, in at least one
127
+ of the following places: within a NOTICE text file distributed
128
+ as part of the Derivative Works; within the Source form or
129
+ documentation, if provided along with the Derivative Works; or,
130
+ within a display generated by the Derivative Works, if and
131
+ wherever such third-party notices normally appear. The contents
132
+ of the NOTICE file are for informational purposes only and
133
+ do not modify the License. You may add Your own attribution
134
+ notices within Derivative Works that You distribute, alongside
135
+ or as an addendum to the NOTICE text from the Work, provided
136
+ that such additional attribution notices cannot be construed
137
+ as modifying the License.
138
+
139
+ You may add Your own copyright statement to Your modifications and
140
+ may provide additional or different license terms and conditions
141
+ for use, reproduction, or distribution of Your modifications, or
142
+ for any such Derivative Works as a whole, provided Your use,
143
+ reproduction, and distribution of the Work otherwise complies with
144
+ the conditions stated in this License.
145
+
146
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
147
+ any Contribution intentionally submitted for inclusion in the Work
148
+ by You to the Licensor shall be under the terms and conditions of
149
+ this License, without any additional terms or conditions.
150
+ Notwithstanding the above, nothing herein shall supersede or modify
151
+ the terms of any separate license agreement you may have executed
152
+ with Licensor regarding such Contributions.
153
+
154
+ 6. Trademarks. This License does not grant permission to use the trade
155
+ names, trademarks, service marks, or product names of the Licensor,
156
+ except as required for reasonable and customary use in describing the
157
+ origin of the Work and reproducing the content of the NOTICE file.
158
+
159
+ 7. Disclaimer of Warranty. Unless required by applicable law or
160
+ agreed to in writing, Licensor provides the Work (and each
161
+ Contributor provides its Contributions) on an "AS IS" BASIS,
162
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
163
+ implied, including, without limitation, any warranties or conditions
164
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
165
+ PARTICULAR PURPOSE. You are solely responsible for determining the
166
+ appropriateness of using or redistributing the Work and assume any
167
+ risks associated with Your exercise of permissions under this License.
168
+
169
+ 8. Limitation of Liability. In no event and under no legal theory,
170
+ whether in tort (including negligence), contract, or otherwise,
171
+ unless required by applicable law (such as deliberate and grossly
172
+ negligent acts) or agreed to in writing, shall any Contributor be
173
+ liable to You for damages, including any direct, indirect, special,
174
+ incidental, or consequential damages of any character arising as a
175
+ result of this License or out of the use or inability to use the
176
+ Work (including but not limited to damages for loss of goodwill,
177
+ work stoppage, computer failure or malfunction, or any and all
178
+ other commercial damages or losses), even if such Contributor
179
+ has been advised of the possibility of such damages.
180
+
181
+ 9. Accepting Warranty or Additional Liability. While redistributing
182
+ the Work or Derivative Works thereof, You may choose to offer,
183
+ and charge a fee for, acceptance of support, warranty, indemnity,
184
+ or other liability obligations and/or rights consistent with this
185
+ License. However, in accepting such obligations, You may act only
186
+ on Your own behalf and on Your sole responsibility, not on behalf
187
+ of any other Contributor, and only if You agree to indemnify,
188
+ defend, and hold each Contributor harmless for any liability
189
+ incurred by, or claims asserted against, such Contributor by reason
190
+ of your accepting any such warranty or additional liability.
191
+
192
+ END OF TERMS AND CONDITIONS
package/README.md CHANGED
@@ -1,3 +1,78 @@
1
- # Temporary Holding Version
1
+ # @embedpdf/viewer-svelte
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The full EmbedPDF viewer as a Svelte 5 component: a thin wrapper around the
4
+ `<embedpdf-viewer>` custom element from [`@embedpdf/viewer`](https://www.npmjs.com/package/@embedpdf/viewer).
5
+
6
+ ```bash
7
+ npm install @embedpdf/viewer-svelte
8
+ ```
9
+
10
+ ```svelte
11
+ <script lang="ts">
12
+ import { PDFViewer } from '@embedpdf/viewer-svelte';
13
+ </script>
14
+
15
+ <PDFViewer src="/report.pdf" style="height: 100vh" />
16
+ ```
17
+
18
+ No engine or wasm configuration is needed: the built-in local engine (PDFium
19
+ compiled to WebAssembly, running in a Web Worker) is the default, and your
20
+ bundler ships `embedpdf.wasm` inside your own build. Self-hosting, strict-CSP
21
+ worker delivery, and injecting a different engine implementation are all
22
+ available through the `engine` prop — see the docs.
23
+
24
+ ## Your own components
25
+
26
+ `ToolbarItem`, `Panel` and `Region` put your component in the place the layout
27
+ makes for it, while it stays in **your** Svelte app — your context, your
28
+ state, your CSS:
29
+
30
+ ```svelte
31
+ <script lang="ts">
32
+ import { PDFViewer, ToolbarItem, type Layout } from '@embedpdf/viewer-svelte';
33
+ import DocPicker from './DocPicker.svelte';
34
+
35
+ const layout = (layout: Layout) => layout.add({ custom: 'doc-picker' }, { to: 'main', section: 'start' });
36
+ </script>
37
+
38
+ <PDFViewer src="/report.pdf" {layout}>
39
+ <ToolbarItem id="doc-picker"><DocPicker /></ToolbarItem>
40
+ </PDFViewer>
41
+ ```
42
+
43
+ `useToolbarItem()` tells it where it sits, and `Button`, `Menu` and `MenuItem`
44
+ are the viewer's own, for yours.
45
+
46
+ ## Driving the viewer
47
+
48
+ ```svelte
49
+ <PDFViewer src="/report.pdf" onready={(viewer) => viewer.commands.execute('zoom:in')} />
50
+ ```
51
+
52
+ `onready` hands you the `viewer`: each plugin's API by its name
53
+ (`viewer.get('search')`, or its token), `viewer.documents`, `viewer.commands`
54
+ and the `watch` reactivity primitive. Inside the viewer, a component of yours
55
+ gets it with `usePDFViewer()`, and `useCommand(id)` gives it a command as a
56
+ button needs it (label, icon, shortcut, state and `run()`). The headless
57
+ functions of `@embedpdf/svelte` work there too. `ondocumentchange` and
58
+ `onpanelresize` say when the active document changes and when someone resizes
59
+ a panel. When the viewer unmounts, its `viewer` is done: its listeners stop and
60
+ its calls refuse.
61
+
62
+ `src`, `theme`, `locale`, `disable` and `readOnly` follow their props while the
63
+ viewer runs; every other prop is read once, so wrap the viewer in `{#key}` to
64
+ start over with a changed one.
65
+
66
+ ## Server-side rendering
67
+
68
+ Safe to import and render on the server (SvelteKit included, no
69
+ `{#if browser}`): `<PDFViewer>` renders the empty `<embedpdf-viewer>` element,
70
+ and the viewer starts in the browser.
71
+
72
+ ## Documentation
73
+
74
+ Full guides and API reference: https://www.embedpdf.com/docs
75
+
76
+ ## License
77
+
78
+ Apache-2.0
@@ -0,0 +1,105 @@
1
+ <!--
2
+ <PDFViewer>: the Svelte face of the <embedpdf-viewer> custom element, and engine-blind: it
3
+ imports the viewer for types only, so it holds no runtime edge to any engine. Which engine ships
4
+ is decided by the entry that re-exports it: `./index.ts` imports `@embedpdf/viewer` (the built-in
5
+ local engine, registered as the default), `./core.ts` imports `@embedpdf/viewer/core` (no default
6
+ engine: you give one, and PDFium is not in the module graph). That one import is the whole
7
+ difference between the two doors, and it lives only in those two files.
8
+
9
+ It renders the element and gives it the config before the element's deferred first mount, so the
10
+ viewer starts once, with it. `src`, `theme`, `locale`, `disable` and `readOnly` follow their
11
+ props: the element changes them in place, comparing by value. Every other prop is read once: a
12
+ later change is ignored (development warns), and a `{#key}` block mounts a new viewer with it.
13
+ `onready`, `ondocumentchange` and `onpanelresize` call the latest prop.
14
+
15
+ Your components are its children, light DOM the browser shows in the viewer's slots while they
16
+ stay in your Svelte tree, with your context, state and CSS. They mount once the viewer is ready,
17
+ inside its kernel, so the headless functions of `@embedpdf/svelte` in them read this viewer, and
18
+ `usePDFViewer()` gives them its `viewer`. When the viewer goes they unmount with it, and mount
19
+ again inside the next one. On the server it renders the empty element, and the viewer starts in
20
+ the browser.
21
+ -->
22
+ <script lang="ts">
23
+ import { DEV } from 'esm-env';
24
+ import { untrack } from 'svelte';
25
+ // The headless runtime carries no engine: it gives the children the viewer's kernel.
26
+ import { KernelProvider, type Kernel } from '@embedpdf/svelte/runtime';
27
+ // `ElementConfig` is the kernel's config, the widest of all, with the engine seam `unknown`: the
28
+ // shared implementation sits above every door, and each entry narrows it by assignment.
29
+ import type { ElementConfig, Viewer } from '@embedpdf/viewer/core';
30
+ import { changedReadOnceKey, liveConfigOf, readOnceWarning } from './config.js';
31
+ import { setViewerContext } from './hooks.js';
32
+ import type { PDFViewerImplProps } from './props.js';
33
+
34
+ let {
35
+ class: className,
36
+ style,
37
+ children,
38
+ element = $bindable(null),
39
+ onready,
40
+ ondocumentchange,
41
+ onpanelresize,
42
+ ...config
43
+ }: PDFViewerImplProps = $props();
44
+
45
+ // The viewer once it's ready: the children render inside its kernel. Before that the viewer has
46
+ // no slots to show them in.
47
+ let viewer = $state.raw<Viewer | null>(null);
48
+ setViewerContext(() => viewer);
49
+
50
+ /** Where the viewer keeps its kernel (`createViewer` in `@embedpdf/viewer-chrome`). */
51
+ const VIEWER_KERNEL = Symbol.for('@embedpdf/viewer/kernel');
52
+ const kernel = $derived(
53
+ viewer ? ((viewer as unknown as Partial<Record<symbol, Kernel>>)[VIEWER_KERNEL] ?? null) : null,
54
+ );
55
+
56
+ // The config the viewer starts with: the props as they are now, read once. The callbacks reach
57
+ // the element once too, as functions that read the prop when they're called, so an inline
58
+ // handler that changes is the one called.
59
+ const initial = untrack(() => ({ ...config }));
60
+ const started: ElementConfig = {
61
+ ...initial,
62
+ onReady: (ready) => {
63
+ viewer = ready;
64
+ onready?.(ready);
65
+ },
66
+ onDocumentChange: (documentId) => ondocumentchange?.(documentId),
67
+ onPanelResize: (event) => onpanelresize?.(event),
68
+ };
69
+
70
+ // The element's attributes and properties. Svelte sets a property while it creates the element,
71
+ // before the element is on the page, so `config` is there for its first mount; the live keys are
72
+ // set again when they change, and the element compares them by value (an inline array is no
73
+ // change). On the server there are no properties: it renders the empty element.
74
+ const attributes = $derived({
75
+ class: className,
76
+ style,
77
+ ...(typeof document === 'undefined' ? {} : { config: started, ...liveConfigOf(config) }),
78
+ });
79
+
80
+ // Every other prop is read once: say so, once, when one changes after the viewer started.
81
+ // A development build, as Svelte itself reads it.
82
+ if (DEV) {
83
+ let warned = false;
84
+ $effect(() => {
85
+ if (warned) return;
86
+ const key = changedReadOnceKey(initial, { ...config });
87
+ if (key === null) return;
88
+ warned = true;
89
+ console.warn(readOnceWarning(key));
90
+ });
91
+ }
92
+ </script>
93
+
94
+ <!-- The element waits for the config set here, also over the server's HTML before hydration. -->
95
+ <embedpdf-viewer bind:this={element} data-epdf-wrapped="" {...attributes}>
96
+ {#if viewer}
97
+ {#key viewer}
98
+ {#if kernel}
99
+ <KernelProvider {kernel}>{@render children?.()}</KernelProvider>
100
+ {:else}
101
+ {@render children?.()}
102
+ {/if}
103
+ {/key}
104
+ {/if}
105
+ </embedpdf-viewer>
@@ -0,0 +1,4 @@
1
+ import type { PDFViewerImplProps } from './props.js';
2
+ declare const Component: import("svelte").Component<PDFViewerImplProps, {}, "element">;
3
+ type Component = ReturnType<typeof Component>;
4
+ export default Component;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * How `<PDFViewer>` treats its config: the live keys it hands on to the running viewer, and the
3
+ * rest, read once, with a warning in development when one changes.
4
+ */
5
+ import type { LiveConfigKey } from '@embedpdf/viewer/core';
6
+ /** The live keys of `config`, as the element's properties of the same names take them. */
7
+ export declare function liveConfigOf(config: Partial<Record<LiveConfigKey, unknown>>): {
8
+ src: unknown;
9
+ theme: unknown;
10
+ locale: unknown;
11
+ disable: unknown;
12
+ readOnly: unknown;
13
+ };
14
+ /**
15
+ * The first key, other than the live ones, whose value isn't the one the viewer started with, or
16
+ * null. By identity: Svelte keeps a prop's value until what it's made from changes.
17
+ */
18
+ export declare function changedReadOnceKey(started: object, current: object): string | null;
19
+ /** What development says when a read-once key changes. */
20
+ export declare const readOnceWarning: (key: string) => string;
package/dist/config.js ADDED
@@ -0,0 +1,37 @@
1
+ /** The keys the element follows while it runs: `<PDFViewer>` hands their changes on. */
2
+ const LIVE_KEYS = {
3
+ src: true,
4
+ theme: true,
5
+ locale: true,
6
+ disable: true,
7
+ readOnly: true,
8
+ };
9
+ /** The live keys of `config`, as the element's properties of the same names take them. */
10
+ export function liveConfigOf(config) {
11
+ return {
12
+ src: config.src,
13
+ theme: config.theme,
14
+ locale: config.locale,
15
+ disable: config.disable,
16
+ readOnly: config.readOnly,
17
+ };
18
+ }
19
+ /**
20
+ * The first key, other than the live ones, whose value isn't the one the viewer started with, or
21
+ * null. By identity: Svelte keeps a prop's value until what it's made from changes.
22
+ */
23
+ export function changedReadOnceKey(started, current) {
24
+ const before = started;
25
+ const after = current;
26
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
27
+ if (key in LIVE_KEYS)
28
+ continue;
29
+ if (!Object.is(before[key], after[key]))
30
+ return key;
31
+ }
32
+ return null;
33
+ }
34
+ /** What development says when a read-once key changes. */
35
+ export const readOnceWarning = (key) => `[embedpdf] <PDFViewer>: \`${key}\` is read once, when the viewer starts, so the change is ` +
36
+ 'ignored. Pass a stable value (module scope, or $state that keeps it), or put the viewer in a ' +
37
+ '{#key} block to mount a new one with it.';
package/dist/core.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte/core — the engine-agnostic Svelte door.
3
+ *
4
+ * ```svelte
5
+ * <script lang="ts">
6
+ * import { PDFViewer } from '@embedpdf/viewer-svelte/core';
7
+ * import { cloudEngine } from '@cloudpdf/engine';
8
+ * </script>
9
+ *
10
+ * <PDFViewer engine={() => cloudEngine({ baseUrl, token })} {documents} />
11
+ * ```
12
+ *
13
+ * The same `<PDFViewer>` as the main entry, but with no default engine: `engine` is required, and
14
+ * takes only a real implementation (an `Engine`, or a function the viewer owns the lifetime of).
15
+ * In exchange the local PDFium engine (wasm, worker source, main-thread recipe) never enters your
16
+ * module graph: nothing is stubbed or aliased away, it is not imported. Its options are absent
17
+ * from the type for the same reason: there is no built-in engine here for `{ assetsUrl }` to
18
+ * configure.
19
+ *
20
+ * App code that wants the batteries-included viewer imports `@embedpdf/viewer-svelte`; this entry
21
+ * is for builds that always give their own engine, and is what the CloudPDF Svelte wrapper is
22
+ * built on.
23
+ */
24
+ import '@embedpdf/viewer/core';
25
+ import type { ViewerConfig } from '@embedpdf/viewer/core';
26
+ import type { Component } from 'svelte';
27
+ import { type PDFViewerPropsOf } from './parts.js';
28
+ export * from '@embedpdf/viewer/core';
29
+ export * from './parts.js';
30
+ /** Props on this door: `engine` is required, and only a real engine satisfies it. */
31
+ export type PDFViewerProps = PDFViewerPropsOf<ViewerConfig>;
32
+ /** The shared component, narrowed to this door's config: the wider props type assigns to it. */
33
+ export declare const PDFViewer: Component<PDFViewerProps, {}, 'element'>;
package/dist/core.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte/core — the engine-agnostic Svelte door.
3
+ *
4
+ * ```svelte
5
+ * <script lang="ts">
6
+ * import { PDFViewer } from '@embedpdf/viewer-svelte/core';
7
+ * import { cloudEngine } from '@cloudpdf/engine';
8
+ * </script>
9
+ *
10
+ * <PDFViewer engine={() => cloudEngine({ baseUrl, token })} {documents} />
11
+ * ```
12
+ *
13
+ * The same `<PDFViewer>` as the main entry, but with no default engine: `engine` is required, and
14
+ * takes only a real implementation (an `Engine`, or a function the viewer owns the lifetime of).
15
+ * In exchange the local PDFium engine (wasm, worker source, main-thread recipe) never enters your
16
+ * module graph: nothing is stubbed or aliased away, it is not imported. Its options are absent
17
+ * from the type for the same reason: there is no built-in engine here for `{ assetsUrl }` to
18
+ * configure.
19
+ *
20
+ * App code that wants the batteries-included viewer imports `@embedpdf/viewer-svelte`; this entry
21
+ * is for builds that always give their own engine, and is what the CloudPDF Svelte wrapper is
22
+ * built on.
23
+ */
24
+ import '@embedpdf/viewer/core';
25
+ import { PDFViewer as PDFViewerImpl } from './parts.js';
26
+ export * from '@embedpdf/viewer/core';
27
+ export * from './parts.js';
28
+ /** The shared component, narrowed to this door's config: the wider props type assigns to it. */
29
+ export const PDFViewer = PDFViewerImpl;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The functions for components inside `<PDFViewer>`: the `viewer`, and a command as a button
3
+ * needs it. They work in the components you give the viewer, which mount once it's live, inside
4
+ * it. The headless functions of `@embedpdf/svelte` work there too, for any plugin's API and state.
5
+ */
6
+ import type { Viewer } from '@embedpdf/viewer/core';
7
+ /**
8
+ * A command as a button needs it, for the active document, as `{ current }`: its `label`, `icon`
9
+ * and `shortcut`, whether it's `enabled`, `active` and `visible`, and `run()`. The headless
10
+ * function, which reads the kernel `<PDFViewer>` gives its children: an id the viewer doesn't
11
+ * have is neither visible nor enabled.
12
+ */
13
+ export { useCommand } from '@embedpdf/svelte/commands';
14
+ /** For `<PDFViewer>`: give the components inside it the viewer `current()` says is live. */
15
+ export declare function setViewerContext(current: () => Viewer | null): void;
16
+ /**
17
+ * The `viewer`, in a component inside `<PDFViewer>`: `get(name)`, `documents`, `commands`,
18
+ * `watch` and `forDocument`. Your components mount once the viewer is live, so it is always
19
+ * there. Outside the viewer, keep the one `onready` gives you; called there, this throws.
20
+ */
21
+ export declare function usePDFViewer(): Viewer;
package/dist/hooks.js ADDED
@@ -0,0 +1,27 @@
1
+ import { getContext, hasContext, setContext, untrack } from 'svelte';
2
+ /**
3
+ * A command as a button needs it, for the active document, as `{ current }`: its `label`, `icon`
4
+ * and `shortcut`, whether it's `enabled`, `active` and `visible`, and `run()`. The headless
5
+ * function, which reads the kernel `<PDFViewer>` gives its children: an id the viewer doesn't
6
+ * have is neither visible nor enabled.
7
+ */
8
+ export { useCommand } from '@embedpdf/svelte/commands';
9
+ const VIEWER = Symbol('embedpdf.pdf-viewer');
10
+ /** For `<PDFViewer>`: give the components inside it the viewer `current()` says is live. */
11
+ export function setViewerContext(current) {
12
+ setContext(VIEWER, current);
13
+ }
14
+ /**
15
+ * The `viewer`, in a component inside `<PDFViewer>`: `get(name)`, `documents`, `commands`,
16
+ * `watch` and `forDocument`. Your components mount once the viewer is live, so it is always
17
+ * there. Outside the viewer, keep the one `onready` gives you; called there, this throws.
18
+ */
19
+ export function usePDFViewer() {
20
+ // Untracked: the components inside mount again for a new viewer, so this one never changes.
21
+ const viewer = hasContext(VIEWER) ? untrack(getContext(VIEWER)) : null;
22
+ if (!viewer) {
23
+ throw new Error('[embedpdf] usePDFViewer() works in a component inside <PDFViewer>. Outside the viewer, ' +
24
+ 'keep the viewer its onready gives you.');
25
+ }
26
+ return viewer;
27
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte — the full viewer as a Svelte component, with the built-in local engine
3
+ * (PDFium wasm in a worker) as the default.
4
+ *
5
+ * ```svelte
6
+ * <script lang="ts">
7
+ * import { PDFViewer } from '@embedpdf/viewer-svelte';
8
+ * </script>
9
+ *
10
+ * <PDFViewer src="/report.pdf" style="height: 100vh" />
11
+ * ```
12
+ *
13
+ * Zero config: no `engine` prop, no bundler setup. This is the door app code should use. Builds
14
+ * that always give their own engine (a cloud deployment, a self-hosted `@cloudpdf/server`) import
15
+ * `@embedpdf/viewer-svelte/core` instead, which is the same component with PDFium absent.
16
+ *
17
+ * The component itself lives in `./component.svelte` and is engine-blind; the one import below is
18
+ * what makes this entry the local-engine door.
19
+ */
20
+ import '@embedpdf/viewer';
21
+ import type { ViewerConfig } from '@embedpdf/viewer';
22
+ import type { Component } from 'svelte';
23
+ import { type PDFViewerPropsOf } from './parts.js';
24
+ export * from '@embedpdf/viewer';
25
+ export * from './parts.js';
26
+ export type PDFViewerProps = PDFViewerPropsOf<ViewerConfig>;
27
+ /** The shared component, narrowed to this door's config: the wider props type assigns to it. */
28
+ export declare const PDFViewer: Component<PDFViewerProps, {}, 'element'>;
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte — the full viewer as a Svelte component, with the built-in local engine
3
+ * (PDFium wasm in a worker) as the default.
4
+ *
5
+ * ```svelte
6
+ * <script lang="ts">
7
+ * import { PDFViewer } from '@embedpdf/viewer-svelte';
8
+ * </script>
9
+ *
10
+ * <PDFViewer src="/report.pdf" style="height: 100vh" />
11
+ * ```
12
+ *
13
+ * Zero config: no `engine` prop, no bundler setup. This is the door app code should use. Builds
14
+ * that always give their own engine (a cloud deployment, a self-hosted `@cloudpdf/server`) import
15
+ * `@embedpdf/viewer-svelte/core` instead, which is the same component with PDFium absent.
16
+ *
17
+ * The component itself lives in `./component.svelte` and is engine-blind; the one import below is
18
+ * what makes this entry the local-engine door.
19
+ */
20
+ import '@embedpdf/viewer';
21
+ import { PDFViewer as PDFViewerImpl } from './parts.js';
22
+ export * from '@embedpdf/viewer';
23
+ export * from './parts.js';
24
+ /** The shared component, narrowed to this door's config: the wider props type assigns to it. */
25
+ export const PDFViewer = PDFViewerImpl;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte/component — the wrapper's door-blind parts: `<PDFViewer>` at the widest
3
+ * config (the kernel's `ElementConfig`), and the components and hooks every door shares.
4
+ *
5
+ * A door imports its engine's door, re-exports these, and narrows `<PDFViewer>` to its own
6
+ * `ViewerConfig` by assignment: this package's two (`.` with the built-in engine, `./core` with
7
+ * none) and `@cloudpdf/viewer-svelte` with the cloud engine (docs/conventions/viewer-doors.md).
8
+ */
9
+ export { default as PDFViewer } from './component.svelte';
10
+ export type { PDFViewerExtras, PDFViewerPropsOf } from './props.js';
11
+ export { useCommand, usePDFViewer } from './hooks.js';
12
+ export { Panel, Region, ToolbarItem, useToolbarItem } from './slots.js';
13
+ export type { PanelProps, RegionName, RegionProps, ToolbarItemProps } from './slots.js';
14
+ export { Button, Menu, MenuItem } from './viewer-components.js';
15
+ export type { ButtonProps, MenuItemProps, MenuProps } from './viewer-components.js';
package/dist/parts.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @embedpdf/viewer-svelte/component — the wrapper's door-blind parts: `<PDFViewer>` at the widest
3
+ * config (the kernel's `ElementConfig`), and the components and hooks every door shares.
4
+ *
5
+ * A door imports its engine's door, re-exports these, and narrows `<PDFViewer>` to its own
6
+ * `ViewerConfig` by assignment: this package's two (`.` with the built-in engine, `./core` with
7
+ * none) and `@cloudpdf/viewer-svelte` with the cloud engine (docs/conventions/viewer-doors.md).
8
+ */
9
+ export { default as PDFViewer } from './component.svelte';
10
+ export { useCommand, usePDFViewer } from './hooks.js';
11
+ export { Panel, Region, ToolbarItem, useToolbarItem } from './slots.js';
12
+ export { Button, Menu, MenuItem } from './viewer-components.js';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The props of `<PDFViewer>`: every key of the viewer's config, its callbacks under their Svelte
3
+ * names, and what places it on your page. Types only from the viewer, as in `./component.svelte`:
4
+ * this module is shared by both doors.
5
+ */
6
+ import type { ElementConfig, EmbedPdfViewerElement, PanelResizeEvent, Viewer } from '@embedpdf/viewer/core';
7
+ import type { Snippet } from 'svelte';
8
+ /** The framework-side props: everything that is not viewer config. */
9
+ export interface PDFViewerExtras {
10
+ class?: string;
11
+ style?: string;
12
+ /** Your components, shown where their slot says: `<ToolbarItem>`, `<Panel>`, `<Region>`. */
13
+ children?: Snippet;
14
+ /** The `<embedpdf-viewer>` element, for its imperative surface: `bind:element`. */
15
+ element?: EmbedPdfViewerElement | null;
16
+ /**
17
+ * Called with the `viewer` once it's live, before any document has opened: capabilities
18
+ * resolve from then on, and document-scoped calls wait for their document.
19
+ */
20
+ onready?: (viewer: Viewer) => void;
21
+ /**
22
+ * Called with the active document's id whenever it changes: the first document that opens, a
23
+ * switch to another tab, and `null` once the last one closes.
24
+ */
25
+ ondocumentchange?: (documentId: string | null) => void;
26
+ /**
27
+ * Called when the user has resized a panel, with its id and new width in px: once a drag of its
28
+ * edge ends, and on each arrow key there.
29
+ */
30
+ onpanelresize?: (event: PanelResizeEvent) => void;
31
+ }
32
+ /** The config's callbacks, which `<PDFViewer>` takes under their Svelte names. */
33
+ type ConfigCallback = 'onReady' | 'onDocumentChange' | 'onPanelResize';
34
+ /**
35
+ * The props of `<PDFViewer>` for a door whose config is `Config`: each key of the config is a prop,
36
+ * except its callbacks, which are `onready`, `ondocumentchange` and `onpanelresize` here, with
37
+ * `class`, `style`, `children` and `element` next to them.
38
+ */
39
+ export type PDFViewerPropsOf<Config extends object> = Omit<Config, ConfigCallback> & PDFViewerExtras;
40
+ /**
41
+ * Props for the shared implementation. Each entry re-exports `<PDFViewer>` narrowed to its door's
42
+ * config: `PDFViewerProps` in `./index.ts` (engine optional) and `./core.ts` (engine required).
43
+ */
44
+ export type PDFViewerImplProps = PDFViewerPropsOf<ElementConfig>;
45
+ export {};
package/dist/props.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ <!--
2
+ What goes inside one of your panels, the one `addPanel(id, …)` adds to the layout: the viewer
3
+ draws its frame, its header and the way it opens. It renders as a child of the <embedpdf-viewer>
4
+ element with the slot `panel:<id>`, in an element that adds no box.
5
+ -->
6
+ <script lang="ts">
7
+ import type { PanelProps } from './props.js';
8
+
9
+ let { id, children }: PanelProps = $props();
10
+
11
+ // Spread, `slot` is the element's attribute: written out, Svelte reads it as a slot of its own.
12
+ const slot = $derived({ slot: `panel:${id}` });
13
+ </script>
14
+
15
+ <div {...slot} style="display: contents">{@render children?.()}</div>
@@ -0,0 +1,4 @@
1
+ import type { PanelProps } from './props.js';
2
+ declare const Panel: import("svelte").Component<PanelProps, {}, "">;
3
+ type Panel = ReturnType<typeof Panel>;
4
+ export default Panel;
@@ -0,0 +1,15 @@
1
+ <!--
2
+ Your component in one of the viewer's regions: `header`, `footer`, `tabs` or `empty`. It renders
3
+ as a child of the <embedpdf-viewer> element with the region's name for its slot, in an element
4
+ that adds no box.
5
+ -->
6
+ <script lang="ts">
7
+ import type { RegionProps } from './props.js';
8
+
9
+ let { name, children }: RegionProps = $props();
10
+
11
+ // Spread, `slot` is the element's attribute: written out, Svelte reads it as a slot of its own.
12
+ const slot = $derived({ slot: name });
13
+ </script>
14
+
15
+ <div {...slot} style="display: contents">{@render children?.()}</div>
@@ -0,0 +1,4 @@
1
+ import type { RegionProps } from './props.js';
2
+ declare const Region: import("svelte").Component<RegionProps, {}, "">;
3
+ type Region = ReturnType<typeof Region>;
4
+ export default Region;
@@ -0,0 +1,27 @@
1
+ <!--
2
+ Your component in the toolbar, where the layout's `{ custom: id }` makes room for it. It renders
3
+ as a child of the <embedpdf-viewer> element with the slot `item:<id>`, in an element that adds
4
+ no box, so it stays in your Svelte tree and lays out in the bar. `useToolbarItem()` inside it
5
+ says where the item sits.
6
+ -->
7
+ <script lang="ts">
8
+ import { untrack } from 'svelte';
9
+ import { provideToolbarItem } from './item-context.svelte.js';
10
+ import type { ToolbarItemProps } from './props.js';
11
+
12
+ let { id, children }: ToolbarItemProps = $props();
13
+
14
+ const place = provideToolbarItem();
15
+ let element: HTMLDivElement | undefined = $state();
16
+ $effect(() => {
17
+ const target = element;
18
+ if (!target) return;
19
+ // Reading the attributes writes the context, which this effect must not depend on.
20
+ return untrack(() => place.follow(target));
21
+ });
22
+
23
+ // Spread, `slot` is the element's attribute: written out, Svelte reads it as a slot of its own.
24
+ const slot = $derived({ slot: `item:${id}` });
25
+ </script>
26
+
27
+ <div bind:this={element} {...slot} style="display: contents">{@render children?.()}</div>
@@ -0,0 +1,4 @@
1
+ import type { ToolbarItemProps } from './props.js';
2
+ declare const ToolbarItem: import("svelte").Component<ToolbarItemProps, {}, "">;
3
+ type ToolbarItem = ReturnType<typeof ToolbarItem>;
4
+ export default ToolbarItem;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Where a toolbar item sits, as the viewer tells it. The viewer keeps that on the item's element as
3
+ * `data-orientation`, `data-edge`, `data-variant` and `data-size`; `ToolbarItem` reads them, live,
4
+ * into a reactive object for `useToolbarItem()`. A change reaches your component in the microtasks
5
+ * after the viewer writes it, before the browser lays the bar out, so the toolbar measures your
6
+ * component in the variant it picked.
7
+ */
8
+ import type { ToolbarItemContext, ToolbarItemEdge, ViewerSize } from '@embedpdf/viewer/core';
9
+ /** An item's context as a reactive object: each field reads the latest the viewer wrote. */
10
+ export declare class ToolbarItemPlace implements ToolbarItemContext {
11
+ #private;
12
+ get orientation(): ToolbarItemContext['orientation'];
13
+ get edge(): ToolbarItemEdge;
14
+ get variant(): string | null;
15
+ get size(): ViewerSize;
16
+ /** Read what the viewer says on `element`, now and whenever it writes it. Returns the stop. */
17
+ follow(element: Element): () => void;
18
+ }
19
+ /** For `ToolbarItem`: the context of the item it makes, for the components inside it. */
20
+ export declare function provideToolbarItem(): ToolbarItemPlace;
21
+ /**
22
+ * Where the toolbar item this component is in sits, as a reactive object: which way its bar runs
23
+ * (`orientation`), the bar's place (`edge`), which of the item's `variants` the toolbar picked
24
+ * (`variant`), and the viewer's size (`size`). Read the fields where you use them
25
+ * (`item.orientation`), so they stay live. Works inside a `ToolbarItem`.
26
+ */
27
+ export declare function useToolbarItem(): ToolbarItemContext;
@@ -0,0 +1,73 @@
1
+ import { getContext, hasContext, setContext } from 'svelte';
2
+ const EDGES = ['top', 'bottom', 'start', 'end', 'float', 'selection'];
3
+ const SIZES = ['compact', 'medium', 'expanded'];
4
+ /** Before the viewer says where the item is: the main bar's place. */
5
+ const AT_THE_TOP = {
6
+ orientation: 'horizontal',
7
+ edge: 'top',
8
+ variant: null,
9
+ size: 'expanded',
10
+ };
11
+ const ATTRIBUTES = ['data-orientation', 'data-edge', 'data-variant', 'data-size'];
12
+ /** Where the item whose element is `element` sits, as the viewer wrote it there. */
13
+ function toolbarItemContextOf(element) {
14
+ const edge = element.getAttribute('data-edge');
15
+ const size = element.getAttribute('data-size');
16
+ return {
17
+ orientation: element.getAttribute('data-orientation') === 'vertical' ? 'vertical' : 'horizontal',
18
+ edge: edge && EDGES.includes(edge) ? edge : AT_THE_TOP.edge,
19
+ variant: element.getAttribute('data-variant'),
20
+ size: size && SIZES.includes(size) ? size : AT_THE_TOP.size,
21
+ };
22
+ }
23
+ const sameContext = (left, right) => left.orientation === right.orientation &&
24
+ left.edge === right.edge &&
25
+ left.variant === right.variant &&
26
+ left.size === right.size;
27
+ /** An item's context as a reactive object: each field reads the latest the viewer wrote. */
28
+ export class ToolbarItemPlace {
29
+ #current = $state.raw(AT_THE_TOP);
30
+ get orientation() {
31
+ return this.#current.orientation;
32
+ }
33
+ get edge() {
34
+ return this.#current.edge;
35
+ }
36
+ get variant() {
37
+ return this.#current.variant;
38
+ }
39
+ get size() {
40
+ return this.#current.size;
41
+ }
42
+ /** Read what the viewer says on `element`, now and whenever it writes it. Returns the stop. */
43
+ follow(element) {
44
+ const read = () => {
45
+ const next = toolbarItemContextOf(element);
46
+ if (!sameContext(this.#current, next))
47
+ this.#current = next;
48
+ };
49
+ read();
50
+ const observer = new MutationObserver(read);
51
+ observer.observe(element, { attributes: true, attributeFilter: ATTRIBUTES });
52
+ return () => observer.disconnect();
53
+ }
54
+ }
55
+ const TOOLBAR_ITEM = Symbol('embedpdf.toolbar-item');
56
+ /** For `ToolbarItem`: the context of the item it makes, for the components inside it. */
57
+ export function provideToolbarItem() {
58
+ const place = new ToolbarItemPlace();
59
+ setContext(TOOLBAR_ITEM, place);
60
+ return place;
61
+ }
62
+ /**
63
+ * Where the toolbar item this component is in sits, as a reactive object: which way its bar runs
64
+ * (`orientation`), the bar's place (`edge`), which of the item's `variants` the toolbar picked
65
+ * (`variant`), and the viewer's size (`size`). Read the fields where you use them
66
+ * (`item.orientation`), so they stay live. Works inside a `ToolbarItem`.
67
+ */
68
+ export function useToolbarItem() {
69
+ if (!hasContext(TOOLBAR_ITEM)) {
70
+ throw new Error('[embedpdf] useToolbarItem() works inside a <ToolbarItem>: it says where that item sits.');
71
+ }
72
+ return getContext(TOOLBAR_ITEM);
73
+ }
@@ -0,0 +1,22 @@
1
+ /** The props of your own components' places in the viewer: `ToolbarItem`, `Panel`, `Region`. */
2
+ import type { Snippet } from 'svelte';
3
+ export interface ToolbarItemProps {
4
+ /** The item's id: `{ custom: id }` in the layout. */
5
+ id: string;
6
+ children?: Snippet;
7
+ }
8
+ export interface PanelProps {
9
+ /** The panel's id, as `addPanel(id, …)` names it in the layout. */
10
+ id: string;
11
+ children?: Snippet;
12
+ }
13
+ /**
14
+ * A place around the viewer for your own components: `header` above everything, `footer` below
15
+ * everything, `tabs` in place of the document tabs, `empty` over the pages while no document is
16
+ * open.
17
+ */
18
+ export type RegionName = 'header' | 'footer' | 'tabs' | 'empty';
19
+ export interface RegionProps {
20
+ name: RegionName;
21
+ children?: Snippet;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Your own components in the viewer: `ToolbarItem`, `Panel` and `Region` put your component in
3
+ * the place the viewer makes for it (`item:<id>`, `panel:<id>`, or a region's name). It renders as
4
+ * a child of the `<embedpdf-viewer>` element, so it stays in your Svelte tree, with your context,
5
+ * your state and your CSS; the browser shows it in that place. `useToolbarItem()` tells a
6
+ * component inside a `ToolbarItem` where it sits.
7
+ */
8
+ export { default as ToolbarItem } from './slots/ToolbarItem.svelte';
9
+ export { default as Panel } from './slots/Panel.svelte';
10
+ export { default as Region } from './slots/Region.svelte';
11
+ export { useToolbarItem } from './slots/item-context.svelte.js';
12
+ export type { PanelProps, RegionName, RegionProps, ToolbarItemProps } from './slots/props.js';
package/dist/slots.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Your own components in the viewer: `ToolbarItem`, `Panel` and `Region` put your component in
3
+ * the place the viewer makes for it (`item:<id>`, `panel:<id>`, or a region's name). It renders as
4
+ * a child of the `<embedpdf-viewer>` element, so it stays in your Svelte tree, with your context,
5
+ * your state and your CSS; the browser shows it in that place. `useToolbarItem()` tells a
6
+ * component inside a `ToolbarItem` where it sits.
7
+ */
8
+ export { default as ToolbarItem } from './slots/ToolbarItem.svelte';
9
+ export { default as Panel } from './slots/Panel.svelte';
10
+ export { default as Region } from './slots/Region.svelte';
11
+ export { useToolbarItem } from './slots/item-context.svelte.js';
@@ -0,0 +1,22 @@
1
+ <!--
2
+ A toolbar button, the viewer's own: `<epdf-button>`, which the viewer draws like the buttons in
3
+ its bars. `command` makes it that command's button; or give it an `icon`, a `label` and an
4
+ `onclick`. Use it inside the viewer: in a `ToolbarItem`, a `Panel` or a `Region`.
5
+ -->
6
+ <script lang="ts">
7
+ import type { ButtonProps } from './props.js';
8
+
9
+ let { command, icon, label, onclick, class: className, style }: ButtonProps = $props();
10
+ </script>
11
+
12
+ <!-- What's left out is null: the element writes its properties to attributes, `undefined` too.
13
+ It draws a real button in its own shadow root, which takes the keys and fires the click. -->
14
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
15
+ <epdf-button
16
+ command={command ?? null}
17
+ icon={icon ?? null}
18
+ label={label ?? null}
19
+ {onclick}
20
+ class={className}
21
+ {style}
22
+ ></epdf-button>
@@ -0,0 +1,4 @@
1
+ import type { ButtonProps } from './props.js';
2
+ declare const Button: import("svelte").Component<ButtonProps, {}, "">;
3
+ type Button = ReturnType<typeof Button>;
4
+ export default Button;
@@ -0,0 +1,13 @@
1
+ <!--
2
+ A button that opens a menu of `MenuItem`s, the viewer's own `<epdf-menu>`: it opens the way the
3
+ viewer's menus do, away from its bar, flipped when there's no room, and up from the bottom on a
4
+ phone.
5
+ -->
6
+ <script lang="ts">
7
+ import type { MenuProps } from './props.js';
8
+
9
+ let { icon, label, children }: MenuProps = $props();
10
+ </script>
11
+
12
+ <!-- What's left out is null: the element writes its properties to attributes, `undefined` too. -->
13
+ <epdf-menu icon={icon ?? null} {label}>{@render children?.()}</epdf-menu>
@@ -0,0 +1,4 @@
1
+ import type { MenuProps } from './props.js';
2
+ declare const Menu: import("svelte").Component<MenuProps, {}, "">;
3
+ type Menu = ReturnType<typeof Menu>;
4
+ export default Menu;
@@ -0,0 +1,15 @@
1
+ <!--
2
+ A row in a `Menu`, the viewer's own `<epdf-menu-item>`: its text is the row's. `command` makes it
3
+ that command's row; picking it runs the command, and calls `onselect` either way (the element's
4
+ `select` event).
5
+ -->
6
+ <script lang="ts">
7
+ import type { MenuItemProps } from './props.js';
8
+
9
+ let { command, icon, onselect, children }: MenuItemProps = $props();
10
+ </script>
11
+
12
+ <!-- What's left out is null: the element writes its properties to attributes, `undefined` too. -->
13
+ <epdf-menu-item command={command ?? null} icon={icon ?? null} {onselect}
14
+ >{@render children?.()}</epdf-menu-item
15
+ >
@@ -0,0 +1,4 @@
1
+ import type { MenuItemProps } from './props.js';
2
+ declare const MenuItem: import("svelte").Component<MenuItemProps, {}, "">;
3
+ type MenuItem = ReturnType<typeof MenuItem>;
4
+ export default MenuItem;
@@ -0,0 +1,31 @@
1
+ /** The props of the viewer's own components for yours: `Button`, `Menu` and `MenuItem`. */
2
+ import type { Snippet } from 'svelte';
3
+ export interface ButtonProps {
4
+ /** Makes it this command's button: its icon, tooltip and state, and it runs it. */
5
+ command?: string;
6
+ /** An icon by name, the viewer's or yours, for a button of your own. */
7
+ icon?: string;
8
+ /** The name of a button of your own, for its tooltip and screen readers. */
9
+ label?: string;
10
+ onclick?: (event: MouseEvent) => void;
11
+ class?: string;
12
+ style?: string;
13
+ }
14
+ export interface MenuProps {
15
+ /** The icon of the button that opens it, by name. */
16
+ icon?: string;
17
+ /** Its name, for the button's tooltip and screen readers. */
18
+ label: string;
19
+ /** Its rows: `MenuItem`s. */
20
+ children?: Snippet;
21
+ }
22
+ export interface MenuItemProps {
23
+ /** Makes it this command's row: its icon, label and state, and picking it runs it. */
24
+ command?: string;
25
+ /** An icon by name, the viewer's or yours. */
26
+ icon?: string;
27
+ /** Called when the row is picked. */
28
+ onselect?: () => void;
29
+ /** Its text. */
30
+ children?: Snippet;
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The viewer's own components, for yours: `Button`, `Menu` and `MenuItem` render the viewer's
3
+ * `<epdf-button>`, `<epdf-menu>` and `<epdf-menu-item>` elements, which the viewer draws like its
4
+ * own toolbar buttons and menus. Use them inside the viewer: in a `ToolbarItem`, a `Panel` or a
5
+ * `Region`. The elements come with the viewer, so these import nothing from it.
6
+ */
7
+ export { default as Button } from './viewer-components/Button.svelte';
8
+ export { default as Menu } from './viewer-components/Menu.svelte';
9
+ export { default as MenuItem } from './viewer-components/MenuItem.svelte';
10
+ export type { ButtonProps, MenuItemProps, MenuProps } from './viewer-components/props.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The viewer's own components, for yours: `Button`, `Menu` and `MenuItem` render the viewer's
3
+ * `<epdf-button>`, `<epdf-menu>` and `<epdf-menu-item>` elements, which the viewer draws like its
4
+ * own toolbar buttons and menus. Use them inside the viewer: in a `ToolbarItem`, a `Panel` or a
5
+ * `Region`. The elements come with the viewer, so these import nothing from it.
6
+ */
7
+ export { default as Button } from './viewer-components/Button.svelte';
8
+ export { default as Menu } from './viewer-components/Menu.svelte';
9
+ export { default as MenuItem } from './viewer-components/MenuItem.svelte';
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "@embedpdf/viewer-svelte",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "3.0.0-next.16",
4
+ "private": false,
5
+ "description": "Svelte wrapper for the full viewer: the PDFViewer component around the embedpdf-viewer custom element. Your own components (ToolbarItem, Panel, Region) are light-DOM children the viewer shows in its slots while they stay in your Svelte tree (your context, your CSS). The viewer inside is the Preact-compiled artifact.",
6
+ "type": "module",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "svelte": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./core": {
15
+ "types": "./dist/core.d.ts",
16
+ "svelte": "./dist/core.js",
17
+ "default": "./dist/core.js"
18
+ },
19
+ "./component": {
20
+ "types": "./dist/parts.d.ts",
21
+ "svelte": "./dist/parts.js",
22
+ "default": "./dist/parts.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "dependencies": {
30
+ "esm-env": "^1.2.2",
31
+ "@embedpdf/viewer": "3.0.0-next.16",
32
+ "@embedpdf/svelte": "3.0.0-next.16"
33
+ },
34
+ "peerDependencies": {
35
+ "svelte": "^5.29.0"
36
+ },
37
+ "devDependencies": {
38
+ "@sveltejs/package": "^2.5.0",
39
+ "@sveltejs/vite-plugin-svelte": "^6.0.0",
40
+ "@types/node": "^22.0.0",
41
+ "playwright-core": "^1.63.0",
42
+ "svelte": "^5.39.5",
43
+ "svelte-check": "^4.3.0",
44
+ "typescript": "~5.8.3",
45
+ "vite": "^6.3.5",
46
+ "vitest": "^2.1.9",
47
+ "@embedpdf/tooling-build": "0.0.0"
48
+ },
49
+ "license": "Apache-2.0",
50
+ "sideEffects": [
51
+ "./src/index.ts",
52
+ "./src/core.ts",
53
+ "./dist/index.js",
54
+ "./dist/core.js"
55
+ ],
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/embedpdf/embed-pdf-viewer.git",
62
+ "directory": "packages/viewer/svelte"
63
+ },
64
+ "scripts": {
65
+ "build": "svelte-package --input src --output dist && node ../../../tooling/build/src/fully-specify-imports.mjs && node ../../../tooling/build/src/check-framework-package.mjs",
66
+ "clean": "rimraf dist",
67
+ "typecheck": "svelte-check --tsconfig ./tsconfig.json --threshold error && svelte-check --tsconfig ./test/tsconfig.json --threshold error",
68
+ "test": "vitest run"
69
+ }
6
70
  }