@embedpdf/angular 3.0.0-next.14 → 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 (87) hide show
  1. package/README.md +309 -0
  2. package/dist/README.md +309 -0
  3. package/dist/actions/index.d.ts +34 -16
  4. package/dist/anchored/index.d.ts +67 -0
  5. package/dist/annotation/index.d.ts +602 -0
  6. package/dist/attachment/index.d.ts +29 -0
  7. package/dist/commands/index.d.ts +109 -0
  8. package/dist/fesm2022/embedpdf-angular-actions.mjs +117 -36
  9. package/dist/fesm2022/embedpdf-angular-actions.mjs.map +1 -1
  10. package/dist/fesm2022/embedpdf-angular-anchored.mjs +148 -0
  11. package/dist/fesm2022/embedpdf-angular-anchored.mjs.map +1 -0
  12. package/dist/fesm2022/embedpdf-angular-annotation.mjs +1933 -0
  13. package/dist/fesm2022/embedpdf-angular-annotation.mjs.map +1 -0
  14. package/dist/fesm2022/embedpdf-angular-attachment.mjs +67 -0
  15. package/dist/fesm2022/embedpdf-angular-attachment.mjs.map +1 -0
  16. package/dist/fesm2022/embedpdf-angular-commands.mjs +235 -0
  17. package/dist/fesm2022/embedpdf-angular-commands.mjs.map +1 -0
  18. package/dist/fesm2022/embedpdf-angular-form.mjs +1074 -0
  19. package/dist/fesm2022/embedpdf-angular-form.mjs.map +1 -0
  20. package/dist/fesm2022/embedpdf-angular-history.mjs +54 -0
  21. package/dist/fesm2022/embedpdf-angular-history.mjs.map +1 -0
  22. package/dist/fesm2022/embedpdf-angular-i18n.mjs +100 -0
  23. package/dist/fesm2022/embedpdf-angular-i18n.mjs.map +1 -0
  24. package/dist/fesm2022/embedpdf-angular-interaction.mjs +157 -0
  25. package/dist/fesm2022/embedpdf-angular-interaction.mjs.map +1 -0
  26. package/dist/fesm2022/embedpdf-angular-link.mjs +306 -0
  27. package/dist/fesm2022/embedpdf-angular-link.mjs.map +1 -0
  28. package/dist/fesm2022/embedpdf-angular-measurement.mjs +106 -0
  29. package/dist/fesm2022/embedpdf-angular-measurement.mjs.map +1 -0
  30. package/dist/fesm2022/embedpdf-angular-metadata.mjs +77 -0
  31. package/dist/fesm2022/embedpdf-angular-metadata.mjs.map +1 -0
  32. package/dist/fesm2022/embedpdf-angular-outline.mjs +76 -0
  33. package/dist/fesm2022/embedpdf-angular-outline.mjs.map +1 -0
  34. package/dist/fesm2022/embedpdf-angular-page-edit.mjs +68 -0
  35. package/dist/fesm2022/embedpdf-angular-page-edit.mjs.map +1 -0
  36. package/dist/fesm2022/embedpdf-angular-page-view.mjs +297 -0
  37. package/dist/fesm2022/embedpdf-angular-page-view.mjs.map +1 -0
  38. package/dist/fesm2022/embedpdf-angular-redaction.mjs +93 -0
  39. package/dist/fesm2022/embedpdf-angular-redaction.mjs.map +1 -0
  40. package/dist/fesm2022/embedpdf-angular-render.mjs +331 -69
  41. package/dist/fesm2022/embedpdf-angular-render.mjs.map +1 -1
  42. package/dist/fesm2022/embedpdf-angular-runtime.mjs +1151 -390
  43. package/dist/fesm2022/embedpdf-angular-runtime.mjs.map +1 -1
  44. package/dist/fesm2022/embedpdf-angular-search.mjs +251 -0
  45. package/dist/fesm2022/embedpdf-angular-search.mjs.map +1 -0
  46. package/dist/fesm2022/embedpdf-angular-selection.mjs +477 -0
  47. package/dist/fesm2022/embedpdf-angular-selection.mjs.map +1 -0
  48. package/dist/fesm2022/embedpdf-angular-shell.mjs +177 -0
  49. package/dist/fesm2022/embedpdf-angular-shell.mjs.map +1 -0
  50. package/dist/fesm2022/embedpdf-angular-signature.mjs +120 -0
  51. package/dist/fesm2022/embedpdf-angular-signature.mjs.map +1 -0
  52. package/dist/fesm2022/embedpdf-angular-stage.mjs +688 -288
  53. package/dist/fesm2022/embedpdf-angular-stage.mjs.map +1 -1
  54. package/dist/fesm2022/embedpdf-angular-stamp.mjs +169 -0
  55. package/dist/fesm2022/embedpdf-angular-stamp.mjs.map +1 -0
  56. package/dist/fesm2022/embedpdf-angular-testing.mjs +89 -0
  57. package/dist/fesm2022/embedpdf-angular-testing.mjs.map +1 -0
  58. package/dist/fesm2022/embedpdf-angular-toolbar.mjs +1123 -0
  59. package/dist/fesm2022/embedpdf-angular-toolbar.mjs.map +1 -0
  60. package/dist/fesm2022/embedpdf-angular-view-manager.mjs +69 -0
  61. package/dist/fesm2022/embedpdf-angular-view-manager.mjs.map +1 -0
  62. package/dist/fesm2022/embedpdf-angular.mjs +25 -3
  63. package/dist/fesm2022/embedpdf-angular.mjs.map +1 -1
  64. package/dist/form/index.d.ts +79 -0
  65. package/dist/history/index.d.ts +28 -0
  66. package/dist/i18n/index.d.ts +52 -0
  67. package/dist/index.d.ts +22 -0
  68. package/dist/interaction/index.d.ts +58 -0
  69. package/dist/link/index.d.ts +77 -0
  70. package/dist/measurement/index.d.ts +46 -0
  71. package/dist/metadata/index.d.ts +51 -0
  72. package/dist/outline/index.d.ts +32 -0
  73. package/dist/page-edit/index.d.ts +27 -0
  74. package/dist/page-view/index.d.ts +94 -0
  75. package/dist/redaction/index.d.ts +38 -0
  76. package/dist/render/index.d.ts +45 -6
  77. package/dist/runtime/index.d.ts +579 -199
  78. package/dist/search/index.d.ts +71 -0
  79. package/dist/selection/index.d.ts +141 -0
  80. package/dist/shell/index.d.ts +88 -0
  81. package/dist/signature/index.d.ts +45 -0
  82. package/dist/stage/index.d.ts +242 -141
  83. package/dist/stamp/index.d.ts +58 -0
  84. package/dist/testing/index.d.ts +53 -0
  85. package/dist/toolbar/index.d.ts +257 -0
  86. package/dist/view-manager/index.d.ts +23 -0
  87. package/package.json +145 -14
@@ -1,266 +1,646 @@
1
1
  import * as _embedpdf_core from '@embedpdf/core';
2
- import { Engine, EngineFactory, AnyPlugin, InitialDocument, Kernel, DocInfo, CapabilityToken } from '@embedpdf/core';
2
+ import { Engine, InitialDocument, Identity, ViewerPageSettings, Kernel, AnyPlugin, ViewerSettings, EventHook, CapabilityToken, DeepPartial, SettingsOf, SettingsChangedEvent, StateDeclaration, PageInfo, LockedDocumentInfo, FailedDocumentInfo, DocumentInfo, PageRef } from '@embedpdf/core';
3
3
  export * from '@embedpdf/core';
4
+ import { PaintedParts, PagePart, SvgShape, EpdfTheme } from '@embedpdf/web';
5
+ export { EpdfTheme, mountWebFont, saveFile } from '@embedpdf/web';
4
6
  import * as i0 from '@angular/core';
5
- import { OnDestroy, Signal, EnvironmentProviders, InjectionToken, TemplateRef, OnInit } from '@angular/core';
7
+ import { Type, Provider, InjectionToken, Signal, TemplateRef } from '@angular/core';
8
+ import { Observable } from 'rxjs';
6
9
  import { PageFrame, PageTransform, Point, Rect } from '@embedpdf/core-geometry';
10
+ import { PageViewDemand } from '@embedpdf/plugin-render/contract';
7
11
 
8
- /** One boot document — the KERNEL's shared `InitialDocument` shape (same as
9
- * the React adapter's), aliased under the package's Epdf naming. */
10
- type EpdfInitialDocument = InitialDocument;
12
+ /** A setup mistake, with its code: `EPDF-101`. */
13
+ declare class EpdfError extends Error {
14
+ readonly code: `EPDF-${number}`;
15
+ readonly name = "EpdfError";
16
+ constructor(code: `EPDF-${number}`, problem: string);
17
+ }
18
+ /** EPDF-101: a service, component or directive of the viewer, with no viewer above it. */
19
+ declare const noViewerError: (what: string) => EpdfError;
20
+ /** EPDF-102: a plugin's service, where no viewer above it has that plugin. */
21
+ declare const noPluginError: (service: string, feature: string) => EpdfError;
22
+ /** EPDF-103: something that draws on a page, outside a page. */
23
+ declare const outsidePageError: (what: string) => EpdfError;
24
+ /** EPDF-104: part of a Stage, outside one. */
25
+ declare const outsideStageError: (what: string) => EpdfError;
26
+ /** EPDF-105: a call that needs the engine, made while rendering on the server. */
27
+ declare const onServerError: (what: string) => EpdfError;
28
+
29
+ /**
30
+ * What `provideEmbedPdf(config, ...features)` takes: the config (the engine, the documents to
31
+ * open, the viewer's settings, or a kernel someone else made) and one feature per plugin
32
+ * (`withStage()`, `withSearch()`). Both are read once, when the viewer starts.
33
+ */
34
+
35
+ /**
36
+ * The engine: an instance, or a function that makes one.
37
+ *
38
+ * - An instance is borrowed: the viewer uses it and never destroys it, so one engine made at
39
+ * module scope can serve every viewer and route.
40
+ * - A function is the viewer's own: the viewer calls it in the browser when it starts, and
41
+ * destroys the engine when the component or route that provides the viewer goes away. It may
42
+ * return a promise, so `() => import('@embedpdf/engine').then((m) => m.localEngine())` keeps
43
+ * the engine out of the page's first download.
44
+ */
45
+ type EngineSource = Engine | (() => Engine | Promise<Engine>);
11
46
  interface EmbedPdfConfig {
47
+ readonly engine: EngineSource;
12
48
  /**
13
- * The engine, as an instance or a thunk. Engines construct synchronously and
14
- * boot lazily (`localEngine()` allocates nothing until first use), so
15
- * ownership follows the SHAPE of what you pass:
16
- *
17
- * - An **instance** is BORROWED: used as-is, never destroyed here. The
18
- * common path — a module-scope `const engine = localEngine()` shared
19
- * across viewers and route changes. The host calls `engine.warmup?.()`
20
- * when the kernel materializes so the boot overlaps initialization.
21
- * - A **thunk** (`() => localEngine()`) is HOST-OWNED: the host calls it
22
- * when the kernel materializes and `destroy()`s the result on teardown.
23
- * Use it for per-host isolation.
24
- *
25
- * Init-only: the kernel is never rebuilt for a new engine — tear the host
26
- * down (destroy the viewer / injector) and create a new one.
49
+ * Documents to open when the viewer starts. Every tab appears at once; the one marked
50
+ * `active`, else the first, becomes the active document. One that fails shows `error` in its
51
+ * tab and doesn't stop the others.
52
+ */
53
+ readonly initialDocuments?: readonly InitialDocument[];
54
+ /**
55
+ * Who the user is, for every document opened without an `identity` of its own. The local
56
+ * engine only: the cloud engine reads it from the document's token. Change it later with
57
+ * `inject(EpdfViewer).updateSettings({ identity })`.
58
+ */
59
+ readonly identity?: Identity;
60
+ /**
61
+ * What the user may do, as permissions, in every document opened without a `scope` of its
62
+ * own. Left out, they may do everything.
63
+ */
64
+ readonly scope?: readonly string[];
65
+ /** The color every part without a color of its own follows. CSS `--epdf-accent` wins over it. */
66
+ readonly accent?: string;
67
+ /** How pages look: `background` before the picture arrives, and the `shadow` under each page. */
68
+ readonly page?: Partial<ViewerPageSettings>;
69
+ }
70
+ /**
71
+ * A kernel someone else created and owns, for `provideEmbedPdf({ kernel })`: every service,
72
+ * component and directive under that injector works against it, and the injector neither starts
73
+ * nor destroys it. The full viewer's Angular wrapper gives the templates you put in it one of
74
+ * these, with the viewer's kernel. Its plugins are the kernel's: a feature's `plugins` are not
75
+ * registered again, while its services, providers and `setup` work as with a kernel of its own.
76
+ */
77
+ interface EmbedPdfKernelConfig {
78
+ readonly kernel: Kernel;
79
+ }
80
+ /**
81
+ * One feature of `provideEmbedPdf()`: the plugins it registers, and the Angular side of them.
82
+ * A plugin's entry point exports its feature as `withX(options)`.
83
+ */
84
+ interface EmbedPdfFeature {
85
+ readonly plugins: readonly AnyPlugin[];
86
+ /**
87
+ * The plugin's services (`EpdfSearch`). They are provided next to the viewer, and again
88
+ * under every `[epdfDocumentScope]`, where they act on that document.
27
89
  */
28
- engine: Engine | EngineFactory;
29
- /** Init-only, like `engine`. */
30
- plugins: AnyPlugin[];
31
- /** Documents to open on startup (with optional tab names). They open in the
32
- * BACKGROUND after start and stream into the registry — `injectDocuments()`
33
- * is reactive; per-document loading UI is the Stage's job. A source that
34
- * fails to open lands in `error` and does NOT block the ones after it. */
35
- initialDocuments?: EpdfInitialDocument[];
90
+ readonly services?: readonly Type<unknown>[];
91
+ /** Anything else the feature puts next to the viewer. */
92
+ readonly providers?: readonly Provider[];
93
+ /**
94
+ * Runs once in the browser, right after the viewer is created, in the injection context of
95
+ * the providers: it may `inject()` services and create effects, which live as long as the
96
+ * viewer.
97
+ */
98
+ readonly setup?: () => void;
99
+ }
100
+ /** What the viewer is doing: starting, running, or failed to start (see `EpdfViewer.error()`). */
101
+ type EpdfViewerStatus = 'starting' | 'ready' | 'error';
102
+
103
+ /**
104
+ * The DI tokens the runtime's pieces share, in a file of their own so the pieces that provide
105
+ * them and the ones that read them never import each other.
106
+ */
107
+
108
+ /**
109
+ * The document a part of the template talks to: `[epdfDocumentScope]`, or a Stage that opened
110
+ * its own `[document]`. A null id follows the active document.
111
+ */
112
+ interface EpdfDocumentScopeRef {
113
+ readonly id: Signal<string | null>;
114
+ }
115
+ declare const EPDF_DOCUMENT_SCOPE: InjectionToken<EpdfDocumentScopeRef>;
116
+ /** What `provideEmbedPdf()` was given, for the viewer it creates. */
117
+ interface EmbedPdfSetup {
118
+ readonly config: EmbedPdfConfig | EmbedPdfKernelConfig;
119
+ readonly features: readonly EmbedPdfFeature[];
120
+ }
121
+ declare const EPDF_SETUP: InjectionToken<EmbedPdfSetup>;
122
+
123
+ /** A plugin's settings, readable and changeable before the kernel exists and after. */
124
+ interface PluginSettingsBinding<T> {
125
+ readonly current: Signal<T>;
126
+ update(change: DeepPartial<T>): void;
127
+ reset(): void;
36
128
  }
37
- /** `starting` covers construction through `kernel.start()`; `ready` means the
38
- * workspace is live (documents may still be opening in the background). */
39
- type EpdfKernelStatus = 'starting' | 'ready' | 'error';
40
- declare class EpdfKernelHost implements OnDestroy {
41
- private readonly browser;
42
- private config;
43
- private _kernel;
44
- /** The engine backing the kernel, and whether WE created it (a recipe) and
45
- * therefore must destroy it. A borrowed instance is left untouched. */
46
- private _engine;
129
+ declare class EpdfKernelHost {
130
+ private readonly setup;
131
+ private readonly injector;
132
+ /** Whether this is a browser. On the server the host creates no kernel. */
133
+ readonly browser: boolean;
134
+ /** The kernel `provideEmbedPdf({ kernel })` was given: someone else's, used as it is. */
135
+ private readonly given;
136
+ /** Every plugin the features register, in their order: none for a kernel given, which has its own. */
137
+ readonly plugins: readonly AnyPlugin[];
138
+ private readonly kernelState;
139
+ private readonly revisionState;
140
+ private readonly statusState;
141
+ private readonly errorState;
142
+ private readonly listeners;
143
+ private readonly viewerSettingsBeforeStart;
144
+ private readonly pluginSettings;
145
+ private readonly pluginSettingsBeforeStart;
146
+ private engine;
47
147
  private ownsEngine;
48
- private unsubscribe;
49
- private initialDocuments;
50
- private booted;
148
+ private stopListening;
51
149
  private destroyed;
52
- /** Bumped on every kernel notification — THE bridge from kernel-world to signals. */
53
- private readonly tick;
54
- /** Boot lifecycle. `ready`/`error` are derived sugar over it. */
55
- readonly status: i0.WritableSignal<EpdfKernelStatus>;
56
- /** The STARTUP failure, if any. Per-document open failures are tab state
57
- * (`DocInfo.status === 'error'`), not host state. */
58
- readonly error: i0.WritableSignal<unknown>;
59
- /** True once `kernel.start()` resolved — which never touches the engine, so
60
- * the shell (and every workspace capability: i18n, view-manager, …) is alive
61
- * while WASM compiles or the transport connects. Workspace chrome may render
62
- * before this; gate on it (or on the document gate) where it matters. */
63
- readonly ready: Signal<boolean>;
64
- /** Register the (deferred) config. Called exactly once, by `EpdfViewer`'s
65
- * constructor or `provideEmbedPdf`'s factory. */
66
- connect(config: () => EmbedPdfConfig): void;
67
- /** The kernel — materialized on first read (see the construction rule above). */
68
- get kernel(): Kernel;
69
- /** Start the kernel and open initial documents in the background. Idempotent;
70
- * a no-op on the server and after destroy. */
71
- boot(): void;
72
- /** Read a value derived from the kernel, cached by equality — Angular's
73
- * `useKernelValue`. The selector re-runs per kernel notification (cheap);
74
- * `equal` decides whether dependents ever see it. */
75
- value<R>(select: (kernel: Kernel) => R, equal?: (a: R, b: R) => boolean): Signal<R>;
76
- ngOnDestroy(): void;
150
+ /**
151
+ * The kernel, once it exists: null while a lazily loaded engine is on its way, and on the
152
+ * server, where only a kernel given is there.
153
+ */
154
+ readonly kernel: Signal<Kernel | null>;
155
+ /** Goes up on every change of the kernel's state. */
156
+ readonly revision: Signal<number>;
157
+ /** A kernel given says it itself: someone else starts it. */
158
+ readonly status: Signal<EpdfViewerStatus>;
159
+ /** Why the viewer failed to start, when `status()` is `'error'`. */
160
+ readonly error: Signal<unknown>;
161
+ /** The viewer's own settings, before the kernel exists and after. */
162
+ readonly viewerSettings: Signal<ViewerSettings>;
163
+ constructor();
164
+ /**
165
+ * A value read from the kernel, as a signal: `select` runs again after each change of the
166
+ * kernel's state, and the signal changes only when `equal` says the value did. `empty` is its
167
+ * value while there is no kernel.
168
+ */
169
+ read<R>(select: (kernel: Kernel) => R, empty: () => R, equal?: (left: R, right: R) => boolean): Signal<R>;
170
+ /** Call `listener` after each change of the kernel's state, and once when the kernel arrives. */
171
+ subscribe(listener: () => void): () => void;
172
+ /**
173
+ * An event of the kernel itself (its settings, a plugin's settings store), as an RxJS
174
+ * stream. A subscription made before the kernel exists starts listening when it arrives.
175
+ */
176
+ stream<T>(pick: (kernel: Kernel) => EventHook<T>): Observable<T>;
177
+ /** Whether a plugin of this viewer provides `token` (the documents are always there). */
178
+ provides(token: CapabilityToken<unknown>): boolean;
179
+ /** Whether the plugin of this viewer that provides `token` has settings. */
180
+ hasSettings(token: CapabilityToken<unknown>): boolean;
181
+ /**
182
+ * The kernel, for `call` (`'zoomIn()'`) on `capability` (`'stage'`), which needs it now:
183
+ * `not-ready` while the engine is still on its way, EPDF-105 on the server.
184
+ */
185
+ requireKernel(capability: string, call: string): Kernel;
186
+ updateViewerSettings(change: DeepPartial<ViewerSettings>): void;
187
+ resetViewerSettings(): void;
188
+ /**
189
+ * A plugin's settings, with or without a document, and before the kernel too: they belong to
190
+ * the plugin as the app registered it. One binding per plugin, shared by every service.
191
+ */
192
+ settingsOf<T extends object>(token: CapabilityToken<unknown>): PluginSettingsBinding<T>;
193
+ /** A registered plugin's settings, kept from before the kernel exists. */
194
+ private registeredSettingsOf;
195
+ /** A plugin's settings in a kernel given, which is there from the start. */
196
+ private givenSettingsOf;
197
+ private pluginFor;
198
+ /** Follow a kernel someone else made: its changes, and nothing of its lifetime. */
199
+ private adopt;
200
+ private start;
201
+ /** Create and start the kernel over `engine`. An engine this viewer made is destroyed with it. */
202
+ private attach;
203
+ private changed;
204
+ private fail;
205
+ private runFeatureSetups;
206
+ private destroy;
77
207
  static ɵfac: i0.ɵɵFactoryDeclaration<EpdfKernelHost, never>;
78
208
  static ɵprov: i0.ɵɵInjectableDeclaration<EpdfKernelHost>;
79
209
  }
210
+
80
211
  /**
81
- * Host the kernel in an environment injector (route or application providers).
82
- * The kernel's lifetime is the injector's — a route-level workspace tears down
83
- * when the route does. Unlocks chrome OUTSIDE any viewer subtree: a toolbar in
84
- * the app header and the stage in the main outlet share this one kernel.
212
+ * How the adapter reaches a plugin: its capability token, resolved for one document (the one in
213
+ * scope, else the active one) after every change of the kernel. A `CapabilityBinding` turns that
214
+ * into what Angular code wants: signals for values, late-bound methods for calls, and RxJS
215
+ * streams for events. Services, the Stage and the page layers are all built on it.
216
+ *
217
+ * Resolution is a read, not a cache: a document becomes resolvable when it's ready, while its
218
+ * id stays the same, so a capability looked up once by id would go stale.
85
219
  */
86
- declare function provideEmbedPdf(config: EmbedPdfConfig): EnvironmentProviders;
87
220
 
221
+ /** The viewer above this injection context; EPDF-101, naming `what`, when there is none. */
222
+ declare function injectKernelHost(what: string): EpdfKernelHost;
88
223
  /**
89
- * DI tokens shared by the runtime's primitives and directives — split out so
90
- * `inject.ts` (consumers) and `scope.ts` (providers) never import each other.
224
+ * The document this injection context talks to: the id the nearest `[epdfDocumentScope]` (or a
225
+ * Stage with a `[document]`) gives, or null to follow the active document.
91
226
  */
92
-
93
- /** The document a subtree is bound to (provided by `[epdfDocumentScope]`).
94
- * Absent => the subtree follows the ACTIVE document. */
95
- interface EpdfDocumentScopeRef {
96
- readonly id: Signal<string>;
227
+ declare function injectDocumentScope(): Signal<string | null>;
228
+ /**
229
+ * A plugin's capability for this injection context's document, as a signal: null while it
230
+ * can't resolve (no such plugin, no document, or one that isn't ready yet). For plugin authors;
231
+ * apps use a plugin's service (`inject(EpdfSearch)`). Call it in an injection context.
232
+ */
233
+ declare function injectCapability<Capability>(token: CapabilityToken<Capability>): Signal<Capability | null>;
234
+ /** One capability token, for one document, as signals, calls and streams. */
235
+ declare class CapabilityBinding<Capability> {
236
+ readonly host: EpdfKernelHost;
237
+ private readonly token;
238
+ private readonly documentId;
239
+ /** The capability, or null while it can't resolve. */
240
+ readonly capability: Signal<Capability | null>;
241
+ /**
242
+ * @param token the token to resolve; a function, because a component's token is an input
243
+ * @param documentId the document to resolve it for, or null for the active one
244
+ */
245
+ constructor(host: EpdfKernelHost, token: () => CapabilityToken<Capability>, documentId: () => string | null);
246
+ /**
247
+ * A value read through the capability, as a signal that changes only when `equal` says the
248
+ * value did. `empty` while the capability can't resolve, and for the one read that can land
249
+ * between a document closing and its capability going away.
250
+ */
251
+ select<R>(read: (capability: Capability) => R, empty: R, equal?: (left: R, right: R) => boolean): Signal<R>;
252
+ /**
253
+ * The capability, for `call`, which needs it now. Without a document a document plugin
254
+ * refuses with `not-ready` ("no document is open"), the same refusal every framework's
255
+ * stand-in gives (`standInFor` in `@embedpdf/core`); a token no plugin provides throws the
256
+ * kernel's reason, because that is a setup mistake.
257
+ */
258
+ require(call: string): Capability;
259
+ /**
260
+ * The capability's method `name`, looked up when it's called, so a method kept in a field
261
+ * acts on the document current at that moment. The same function for the binding's lifetime.
262
+ *
263
+ * A read (a method named `get*`, `list*`, `is*`, `has*` or `can*`: `canUpdate()`,
264
+ * `getZoomLevel()`) is state too: called in a template, a `computed()` or an `effect()`, it's
265
+ * read again after each change of the kernel and of the document in scope, and wakes its
266
+ * reader only when its answer changes. So a button it disables follows the selection, the
267
+ * active document and its permissions. Other methods track nothing: an `effect()` that calls a
268
+ * verb doesn't run again because the verb changed the document.
269
+ *
270
+ * Without a document a method refuses with `not-ready`: one that returns a promise (the token's
271
+ * `promises`) returns the refusal as a rejected promise, so `.catch()` sees it; others throw.
272
+ */
273
+ method<Name extends keyof Capability>(name: Name): Capability[Name];
274
+ /**
275
+ * The method `name` of the capability's namespace `namespace` (`custom.update()`), late-bound
276
+ * the way {@link method} is. A service gives a namespace an object of its own
277
+ * (`metadata.custom`), built from these, {@link select} and {@link stream}.
278
+ */
279
+ namespaceMethod<Key extends keyof Capability, Name extends keyof Capability[Key]>(namespace: Key, name: Name): Capability[Key][Name];
280
+ /**
281
+ * One of the capability's events, as an RxJS stream. A subscription follows the document:
282
+ * when it changes (another tab, a document that opens or closes), it moves to the new
283
+ * document's capability. Nothing is emitted while there is none.
284
+ */
285
+ stream<T>(pick: (capability: Capability) => EventHook<T>): Observable<T>;
286
+ private resolve;
287
+ /** `owner(capability)[name]`, looked up when called; a read tracks what its answer reads. */
288
+ private late;
97
289
  }
98
- declare const EPDF_DOCUMENT_SCOPE: InjectionToken<EpdfDocumentScopeRef>;
99
290
 
100
291
  /**
101
- * The inject* primitives — Angular's spelling of the React runtime hooks
102
- * (`use*` → `inject*`, plain values → signals, same names otherwise).
292
+ * One service per plugin: `inject(EpdfSearch)` has the plugin's methods, every value of its
293
+ * State table as a signal (`search.hitCount()`), its events as RxJS streams
294
+ * (`search.completed$`), and, when the plugin has settings, `settings()`, `getSettings()`,
295
+ * `updateSettings()`, `resetSettings()` and `settingsChanged$`. `pluginService()` builds all of that from what the
296
+ * plugin declares already, so a plugin's service is a few lines; the package README's "Adding a
297
+ * plugin binding" shows one.
103
298
  *
104
- * All of them must run in an injection context (a constructor / field
105
- * initializer, like `inject()` itself). None of them read `host.kernel` during
106
- * construction — they return signals or lazy methods, so they are safe in
107
- * component-hosted mode where the kernel materializes after construction.
299
+ * A service acts on the document of the injector that created it: under `[epdfDocumentScope]`
300
+ * that document, else the active one. Its methods are looked up when they're called, so a
301
+ * method kept in a field acts on the document current at that moment.
108
302
  */
109
303
 
110
- declare const shallowArray: <T>(a: readonly T[], b: readonly T[]) => boolean;
111
- declare function injectKernelHost(): EpdfKernelHost;
112
- /** The raw kernel — materializes it immediately. Do NOT call while a component
113
- * is being constructed in component-hosted mode (inputs are not set yet);
114
- * prefer the signal-returning primitives, which defer the first kernel read. */
115
- declare function injectKernel(): Kernel;
116
- /** Read a value derived from the kernel, cached by equality — `useKernelValue`. */
117
- declare function injectKernelValue<R>(select: (kernel: Kernel) => R, equal?: (a: R, b: R) => boolean): Signal<R>;
118
- declare function injectActiveDocumentId(): Signal<string | null>;
119
- /** The document id for this injector subtree: the nearest `[epdfDocumentScope]`,
120
- * else the active document. */
121
- declare function injectDocumentId(): Signal<string | null>;
304
+ /** The names of a capability's events: its `on…` members. */
305
+ type EventName<Capability> = {
306
+ [Key in keyof Capability]-?: Key extends `on${string}` ? Capability[Key] extends EventHook<infer _Payload> ? Key : never : never;
307
+ }[keyof Capability];
308
+ /** An event's stream on a service: `onCompleted` is `completed$`. */
309
+ type StreamName<Event> = Event extends `on${infer Name}` ? `${Uncapitalize<Name>}$` : never;
310
+ type EventOf<Hook> = Hook extends EventHook<infer Event> ? Event : never;
311
+ /** The settings members, for a capability that has the settings calls. */
312
+ type SettingsMembers<Capability> = [SettingsOf<Capability>] extends [never] ? unknown : {
313
+ /** The plugin's settings, with or without a document. */
314
+ readonly settings: Signal<SettingsOf<Capability>>;
315
+ /** Fires once for each call that changed a setting. */
316
+ readonly settingsChanged$: Observable<SettingsChangedEvent<SettingsOf<Capability>>>;
317
+ } & Pick<Capability, ('getSettings' | 'updateSettings' | 'resetSettings') & keyof Capability>;
318
+ /** What a plugin service built by `pluginService()` has, on top of the members its class adds. */
319
+ type PluginServiceMembers<Capability, State extends object, Method extends keyof Capability, Event extends EventName<Capability>> = {
320
+ readonly [Key in Method]-?: Capability[Key];
321
+ } & {
322
+ readonly [Key in keyof State]-?: Signal<State[Key]>;
323
+ } & {
324
+ readonly [Key in Event as StreamName<Key>]: Observable<EventOf<Capability[Key]>>;
325
+ } & SettingsMembers<Capability>;
122
326
  /**
123
- * Resolve a capability against a REACTIVE token — for components that take the
124
- * token as an input (`injectCapability` is the fixed-token sugar). Resolution
125
- * is a REACTIVE read (`tryCapability` through the kernel's one change
126
- * stream), not a computed over the document id — under the request-time
127
- * lifecycle a document can become resolvable while its id stays the same, so
128
- * any id-keyed cache goes stale; subscribing makes staleness structurally
129
- * impossible. Fail-fast like React's `useCapability`: while unresolvable,
130
- * this re-runs the strict resolver so the kernel's truthful reason (`no
131
- * capability` / `no document` / `document is loading|locked`) is what throws.
327
+ * The class `pluginService()` returns, which a plugin's service extends. It has a name so the
328
+ * built declarations say `PluginServiceClass<SearchCapability, …>` and the app's compiler works
329
+ * out each member from the plugin's own types, instead of spelling out every member's type,
330
+ * which would name packages the app may not have installed.
132
331
  */
133
- declare function injectCapabilityFor<T>(token: () => CapabilityToken<T>): Signal<T>;
134
- declare function injectCapability<T>(token: CapabilityToken<T>): Signal<T>;
135
- /** Like `injectCapabilityFor`, but null while the token can't resolve (no
136
- * plugin, no document, or a document that isn't ready yet). */
137
- declare function injectOptionalCapabilityFor<T>(token: () => CapabilityToken<T>): Signal<T | null>;
138
- declare function injectOptionalCapability<T>(token: CapabilityToken<T>): Signal<T | null>;
139
- /** Subscribe to a selector over a (document-resolved) capability. */
140
- declare function injectSelectorFor<C, R>(token: () => CapabilityToken<C>, select: (cap: C) => R, equal?: (a: R, b: R) => boolean): Signal<R>;
141
- declare function injectSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) => R, equal?: (a: R, b: R) => boolean): Signal<R>;
332
+ type PluginServiceClass<Capability, State extends object, Method extends keyof Capability, Event extends EventName<Capability>> = abstract new () => EpdfPluginService<Capability> & PluginServiceMembers<Capability, State, Method, Event>;
333
+ interface PluginServiceSpec<Capability, State extends object, Method extends keyof Capability, Event extends EventName<Capability>> {
334
+ /** The service's class name, as errors show it: `'EpdfSearch'`. */
335
+ readonly name: string;
336
+ /**
337
+ * The feature that registers the plugin, as errors show it: `'withSearch()'`. Left out for
338
+ * the viewer's own services, which `provideEmbedPdf()` always has.
339
+ */
340
+ readonly feature?: string;
341
+ readonly token: CapabilityToken<Capability>;
342
+ /** The plugin's State table (`defineState`): each field becomes a signal of the same name. */
343
+ readonly state?: StateDeclaration<Capability, State>;
344
+ /**
345
+ * For a plugin that loads its state on demand: the verb that loads it (its declaration's
346
+ * `ensure`), called once a ready document's capability is in scope, for every document the
347
+ * service comes to read. Taken from `state` when the spec has one.
348
+ */
349
+ readonly ensure?: (capability: Capability) => Promise<void>;
350
+ /** The plugin's Methods table: each becomes a method of the same name. */
351
+ readonly methods: readonly Method[];
352
+ /** The plugin's events: `onCompleted` becomes the stream `completed$`. */
353
+ readonly events?: readonly Event[];
354
+ }
355
+ /**
356
+ * The base every plugin service extends. Its members are for the service's own class, which
357
+ * adds what the generic part can't: a signal with arguments (`hitsOn(page)`), a namespace
358
+ * (`custom.fields()`), a helper that needs the caller's injection context.
359
+ */
360
+ declare abstract class EpdfPluginService<Capability> {
361
+ /** The plugin's capability for this service's document. */
362
+ protected readonly binding: CapabilityBinding<Capability>;
363
+ protected constructor(spec: {
364
+ name: string;
365
+ feature?: string;
366
+ token: CapabilityToken<Capability>;
367
+ });
368
+ }
369
+ /**
370
+ * The base class of a plugin's service, built from the plugin's declarations:
371
+ *
372
+ * @Injectable({ providedIn: 'root' })
373
+ * export class EpdfSearch extends pluginService({
374
+ * name: 'EpdfSearch',
375
+ * feature: 'withSearch()',
376
+ * token: SearchToken,
377
+ * state: searchState,
378
+ * methods: ['search', 'clear', 'goToHit'],
379
+ * events: ['onCompleted'],
380
+ * }) {}
381
+ *
382
+ * `providedIn: 'root'` is only the fallback that turns a missing viewer into EPDF-102: the
383
+ * service's feature provides it next to the viewer, and `[epdfDocumentScope]` again.
384
+ */
385
+ declare function pluginService<Capability, State extends object = Record<never, never>, Method extends keyof Capability = never, Event extends EventName<Capability> = never>(spec: PluginServiceSpec<Capability, State, Method, Event>): PluginServiceClass<Capability, State, Method, Event>;
386
+
142
387
  /**
143
- * Null-safe `injectSelector`: `fallback` whenever the token can't resolve — no
144
- * provider, or a document-scoped token with no document. For chrome that stays
145
- * mounted across the empty-workspace state (a zoom readout, a mode band).
146
- * `injectSelector` stays strict (fail-fast) for code that KNOWS a document
147
- * exists — e.g. anything behind the document gate.
388
+ * The viewer itself: its settings, and whether it started. The config given to
389
+ * `provideEmbedPdf()` is read once; what may change while the app runs changes here:
390
+ *
391
+ * effect(() => viewer.updateSettings({ identity: auth.identity() }));
148
392
  *
149
- * The `select` guard also swallows reads through a capability whose document
150
- * closed between the store notification and this read — that teardown race
151
- * resolves to `fallback` for one frame, then recomputes against the new state.
393
+ * Documents opened after a change of `identity` or `scope` use the new value; open ones keep
394
+ * theirs.
395
+ */
396
+ declare class EpdfViewer {
397
+ private readonly host;
398
+ /** The viewer's settings: `identity`, `scope`, `accent` and `page`. */
399
+ readonly settings: Signal<ViewerSettings>;
400
+ /** `'starting'`, `'ready'`, or `'error'` when the engine or a plugin failed to start. */
401
+ readonly status: Signal<EpdfViewerStatus>;
402
+ /** Why the viewer failed to start, when `status()` is `'error'`. */
403
+ readonly error: Signal<unknown>;
404
+ /** Fires once for each call that changed a setting. */
405
+ readonly settingsChanged$: Observable<SettingsChangedEvent<ViewerSettings>>;
406
+ /** Merge `changes` into the viewer's settings: `page` merges, `identity` and `scope` replace. */
407
+ updateSettings(changes: DeepPartial<ViewerSettings>): void;
408
+ /** Go back to the settings `provideEmbedPdf()` was given. */
409
+ resetSettings(): void;
410
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfViewer, never>;
411
+ static ɵprov: i0.ɵɵInjectableDeclaration<EpdfViewer>;
412
+ }
413
+ declare const EpdfDocuments_base: PluginServiceClass<_embedpdf_core.DocumentsCapability, _embedpdf_core.DocumentsState, "print" | "open" | "preparePrint" | "download" | "downloadLayer" | "close" | "move" | "canOpenFiles" | "openAll" | "retry" | "rename" | "unlock" | "closeAll" | "setActive" | "getActiveId" | "getActive" | "list" | "get" | "has" | "getCount" | "getOrder" | "setOrder" | "swap" | "getPrintQuality" | "canDownload" | "canPrint" | "listPages" | "getPage" | "getPageIndex" | "getRevision", "onOpened" | "onOpenFailed" | "onLocked" | "onClosed" | "onActiveChanged" | "onPagesChanged" | "onUnsavedChangesChanged">;
414
+ /**
415
+ * The documents: open, close, unlock, retry and download them, and every tab with the active
416
+ * one as signals (`documents()`, `activeId()`). Under `[epdfDocumentScope]` the calls that
417
+ * leave out the document use that one: `download()` downloads the document in scope.
418
+ */
419
+ declare class EpdfDocuments extends EpdfDocuments_base {
420
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfDocuments, never>;
421
+ static ɵprov: i0.ɵɵInjectableDeclaration<EpdfDocuments>;
422
+ }
423
+ declare const EpdfDocument_base: PluginServiceClass<_embedpdf_core.DocumentsCapability, _embedpdf_core.DocumentInfo, never, never>;
424
+ /**
425
+ * The document this part of the template talks to: the one `[epdfDocumentScope]` names, else
426
+ * the active one. Its fields are signals (`id()`, `name()`, `status()`, `pageCount()`,
427
+ * `hasUnsavedChanges()`, `passwordProvided()`, `error()`), and `pages()` its pages. With no
428
+ * document it reads as one still on its way: an empty `id` and `status() === 'loading'`.
152
429
  */
153
- declare function injectOptionalSelectorFor<C, R>(token: () => CapabilityToken<C>, select: (cap: C) => R, fallback: R, equal?: (a: R, b: R) => boolean): Signal<R>;
154
- declare function injectOptionalSelector<C, R>(token: CapabilityToken<C>, select: (cap: C) => R, fallback: R, equal?: (a: R, b: R) => boolean): Signal<R>;
155
- /** The document registry (open/close/active/list), reactive — `useDocuments`.
156
- * Methods late-bind the kernel, so this is construction-safe. */
157
- interface EpdfDocuments {
158
- docs: Signal<DocInfo[]>;
159
- activeId: Signal<string | null>;
160
- open: Kernel['documents']['open'];
161
- unlock: Kernel['documents']['unlock'];
162
- close: Kernel['documents']['close'];
163
- setActive: Kernel['documents']['setActive'];
164
- move: Kernel['documents']['move'];
165
- swap: Kernel['documents']['swap'];
166
- download: Kernel['documents']['download'];
167
- downloadLayer: Kernel['documents']['downloadLayer'];
430
+ declare class EpdfDocument extends EpdfDocument_base {
431
+ /**
432
+ * The document's pages, in order: each with its `ref`, `index`, `label`, size and rotation.
433
+ * Needs no Stage. The same array until a page is added, removed, moved or rotated; empty
434
+ * with no document.
435
+ */
436
+ readonly pages: Signal<readonly PageInfo[]>;
437
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfDocument, never>;
438
+ static ɵprov: i0.ɵɵInjectableDeclaration<EpdfDocument>;
168
439
  }
169
- declare function injectDocuments(): EpdfDocuments;
170
440
 
441
+ /**
442
+ * `provideEmbedPdf(config, ...features)`: the viewer as Angular providers, in Angular's own
443
+ * style (`provideRouter(routes, withViewTransitions())`).
444
+ *
445
+ * providers: [provideEmbedPdf({ engine }, withStage(), withRender(), withSearch())]
446
+ *
447
+ * It works in a component's `providers` (that component and everything in its template share
448
+ * one viewer, and the component can inject its services itself), in a route's `providers`, and
449
+ * in the app config. Each injector that provides it gets its own viewer, created in the browser
450
+ * and destroyed with the injector. `provideEmbedPdf({ kernel })` gives the injector a kernel
451
+ * someone else made instead, which it neither starts nor destroys.
452
+ */
453
+
454
+ /**
455
+ * Every service that acts on one document: the viewer's own, and each feature's, added when a
456
+ * `provideEmbedPdf()` call names the feature. `[epdfDocumentScope]` and a Stage list this array
457
+ * in their `providers`, so a service injected under them acts on their document.
458
+ *
459
+ * It is one array that grows, not a copy: Angular reads a directive's providers when its
460
+ * template is first created, which is after the `provideEmbedPdf()` calls in decorators and the
461
+ * app config ran. A service whose feature is first named later (a lazily loaded route's
462
+ * providers) is still provided by its viewer, and acts on the active document there.
463
+ */
464
+ declare const EPDF_SCOPED_SERVICES: Provider[];
465
+ /** The viewer, with the plugins its features register; or the viewer of a kernel given. */
466
+ declare function provideEmbedPdf(config: EmbedPdfConfig | EmbedPdfKernelConfig, ...features: EmbedPdfFeature[]): Provider[];
467
+
468
+ /**
469
+ * Binds everything inside the element to one document: services injected there, the Stage
470
+ * and its layers act on it instead of the active document.
471
+ */
171
472
  declare class EpdfDocumentScope implements EpdfDocumentScopeRef {
473
+ /** The document's id. */
172
474
  readonly id: i0.InputSignal<string>;
173
475
  static ɵfac: i0.ɵɵFactoryDeclaration<EpdfDocumentScope, never>;
174
476
  static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfDocumentScope, "[epdfDocumentScope]", never, { "id": { "alias": "epdfDocumentScope"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
175
477
  }
478
+ /** What the gate's content gets: `let document` is the ready document. */
479
+ interface EpdfDocumentGateContext {
480
+ $implicit: DocumentInfo;
481
+ }
482
+ /** What the `locked` template gets: `let-document` is the document waiting for its password. */
483
+ interface EpdfLockedDocumentContext {
484
+ $implicit: LockedDocumentInfo;
485
+ }
486
+ /** What the `error` template gets: `let-document` is the document that failed, with `error`. */
487
+ interface EpdfFailedDocumentContext {
488
+ $implicit: FailedDocumentInfo;
489
+ }
176
490
  /**
177
- * Render the host template only while this subtree has a document — the
178
- * structural way to say "this UI is defined over a document". Sibling of
179
- * `[epdfDocumentScope]`, which picks WHICH document; this one handles WHETHER.
491
+ * Renders its content only while this part of the template has a ready document, with that
492
+ * document as `let document`. Otherwise it renders `locked` for a document waiting for its
493
+ * password, `error` for one that couldn't be opened, and `fallback` for everything else (none
494
+ * open yet, still opening), each with the document as `let-document`. Without `locked` or
495
+ * `error` those states show `fallback` too.
180
496
  *
181
- * <section *epdfDocumentGate="; fallback: empty">…document UI…</section>
182
- * <ng-template #empty>Drop a PDF to get started</ng-template>
497
+ * <epdf-stage *epdfDocumentGate="let document; fallback: opening; locked: locked; error: failed">
498
+ * <ng-template #opening>Opening…</ng-template>
499
+ * <ng-template #locked let-document><app-password-form [document]="document" /></ng-template>
183
500
  *
184
- * The subtree stays mounted across a CHANGE of document (active-tab switch) —
185
- * only the has-a-document boolean creates/destroys it.
501
+ * The content stays mounted when another ready document becomes the active one; only its
502
+ * `document` changes.
186
503
  */
187
504
  declare class EpdfDocumentGate {
188
- /** Shown while this subtree has NO document (empty workspace, docs still opening). */
189
- readonly fallback: i0.InputSignal<TemplateRef<unknown> | null>;
505
+ /** Shown while there is no ready document: none is open, or it's still opening. */
506
+ readonly fallback: i0.InputSignal<TemplateRef<unknown> | null | undefined>;
507
+ /** Shown while the document waits for its password; `fallback` without it. */
508
+ readonly locked: i0.InputSignal<TemplateRef<EpdfLockedDocumentContext> | null | undefined>;
509
+ /** Shown when the document couldn't be opened; `fallback` without it. */
510
+ readonly error: i0.InputSignal<TemplateRef<EpdfFailedDocumentContext> | null | undefined>;
511
+ static ngTemplateContextGuard(_gate: EpdfDocumentGate, context: unknown): context is EpdfDocumentGateContext;
190
512
  constructor();
513
+ private templateFor;
191
514
  static ɵfac: i0.ɵɵFactoryDeclaration<EpdfDocumentGate, never>;
192
- static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfDocumentGate, "[epdfDocumentGate]", never, { "fallback": { "alias": "epdfDocumentGateFallback"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
193
- }
194
-
195
- declare class EpdfViewer implements OnInit {
196
- /** The engine, as an instance OR a thunk ({@link EngineFactory}). An
197
- * instance is borrowed (never destroyed here); a thunk is host-owned
198
- * (constructed then destroyed with the viewer). See EmbedPdfConfig.
199
- * Init-only: the kernel is built once from the first values; recreate the
200
- * viewer (e.g. with @if) to swap it. */
201
- readonly engine: i0.InputSignal<Engine | EngineFactory>;
202
- readonly plugins: i0.InputSignal<AnyPlugin[]>;
203
- readonly initialDocuments: i0.InputSignal<_embedpdf_core.InitialDocument[] | undefined>;
204
- /** The host this viewer provides — exposed so a template reference can read
205
- * `viewer.host.ready()` / `.status()` without extra imports. */
206
- readonly host: EpdfKernelHost;
207
- constructor();
208
- ngOnInit(): void;
209
- static ɵfac: i0.ɵɵFactoryDeclaration<EpdfViewer, never>;
210
- static ɵcmp: i0.ɵɵComponentDeclaration<EpdfViewer, "epdf-viewer", never, { "engine": { "alias": "engine"; "required": true; "isSignal": true; }; "plugins": { "alias": "plugins"; "required": true; "isSignal": true; }; "initialDocuments": { "alias": "initialDocuments"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
515
+ static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfDocumentGate, "[epdfDocumentGate]", never, { "fallback": { "alias": "epdfDocumentGateFallback"; "required": false; "isSignal": true; }; "locked": { "alias": "epdfDocumentGateLocked"; "required": false; "isSignal": true; }; "error": { "alias": "epdfDocumentGateError"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
211
516
  }
212
517
 
213
518
  /**
214
- * PageContext — the seam. A layer depends ONLY on this, never on the Stage, so
215
- * the same layer works inside a virtualized `<epdf-stage>` page and a future
216
- * standalone `<epdf-page-view>`.
519
+ * The page context: what a layer drawn on a page knows about that page. A layer reads only
520
+ * this, never the Stage, so the same layer works on a Stage's page and on a standalone page
521
+ * view. Inside `<ng-template epdfPage>`, `let-page` gives it to the template and `injectPage()`
522
+ * to a component.
217
523
  *
218
- * THE STABILITY INVARIANT (the one performance rule of this adapter): a page's
219
- * context is ONE STABLE OBJECT per mounted surface — identity never changes;
220
- * the volatile parts (`transform`, `frame`, `pageIndex`) are signals INSIDE it.
221
- * The per-page injector providing `EPDF_PAGE` is likewise created once. Camera
222
- * frames then flow as signal writes — `NgTemplateOutlet` never recreates the
223
- * embedded views, in-flight renders survive, and updates stay per-binding.
224
- * Never rebuild the context or the injector per frame.
524
+ * One context per page on screen, for as long as the page is shown: its identity never
525
+ * changes, and what moves (`pageIndex`, `frame`, `transform`) are signals inside it. The
526
+ * template outlet then keeps every layer while the camera moves, and a camera frame updates
527
+ * only the bindings that read the transform. Never build a new context per frame.
225
528
  *
226
- * Coordinate math lives in `@embedpdf/core-geometry`'s `PageTransform` — verified
227
- * once, not re-derived per framework adapter (`toContentPoint` mirrors React's
228
- * `makePageContext` exactly).
529
+ * The coordinate math is `PageTransform` from `@embedpdf/core-geometry`, and the client-space
530
+ * conversions are `pageClientSpace` from `@embedpdf/web`, both shared by every framework.
229
531
  */
230
532
 
231
533
  interface EpdfPageContext {
232
534
  readonly documentId: string;
233
- /** Durable page identity (PDF object number) — use for keys / render / annotations. */
234
- readonly pon: number;
235
- /** Display index (page N) — can shift under page reorders, hence a signal. */
535
+ /**
536
+ * The page's address, the same for as long as the page is shown: use it for keys and calls.
537
+ * An `<epdf-page-view>` whose `[page]` changes shows another page in the same context; its
538
+ * `ref` and `documentId` are read through signals there, so a `computed()` or an `effect()`
539
+ * that reads them follows.
540
+ */
541
+ readonly ref: PageRef;
542
+ /** The page's place in the document, from 0. It moves when pages are reordered. */
236
543
  readonly pageIndex: Signal<number>;
237
- /** Reserved chrome bands around the page (screen px per side). */
544
+ /** The bands reserved around the page for labels and buttons, in screen pixels per side. */
238
545
  readonly frame: Signal<PageFrame>;
239
- /** The single bridge between PDF points, view px, and device px for this
240
- * page. Layers do ALL coordinate work through it — never re-derive
241
- * `x * scale` or `* dpr`. Updates per camera frame. */
546
+ /**
547
+ * Between PDF points, view pixels and device pixels on this page: `pageToViewRect` places
548
+ * something on the page, `renderScale` and `deviceWidth` size a picture of it. Changes with
549
+ * every camera frame.
550
+ */
242
551
  readonly transform: Signal<PageTransform>;
243
- /** Client (screen) point → the viewer's coordinates (content point) — the
244
- * one platform-bound hit-test. */
245
- toContentPoint(clientX: number, clientY: number): Point;
246
- /** Content point → client (screen) px — the exact inverse of `toContentPoint`. */
247
- toClientPoint(p: Point): Point;
248
- /** Content rect → client (screen) px AABB. */
552
+ /**
553
+ * Which view shows the page (a Stage's lens id). Per-view work, such as which tiles to draw,
554
+ * is kept per view, so a thumbnail strip never disturbs the main view.
555
+ */
556
+ readonly view: string;
557
+ /** A point on the screen (client pixels) to a point on the page: the one hit test. */
558
+ toPagePoint(clientX: number, clientY: number): Point;
559
+ /** A point on the page to client pixels: the exact inverse of `toPagePoint`. */
560
+ toClientPoint(point: Point): Point;
561
+ /** A box on the page to the client-pixel box around it. */
249
562
  toClientRect(rect: Rect): Rect;
563
+ /**
564
+ * How much of the page the view wants drawn, read when asked: a Stage gives the part on
565
+ * screen (an empty rect when the page left the screen); a standalone page view gives none,
566
+ * which means the whole page.
567
+ */
568
+ readonly getViewDemand?: () => PageViewDemand;
250
569
  }
251
570
  declare const EPDF_PAGE: InjectionToken<EpdfPageContext>;
252
- declare function injectPage(): EpdfPageContext;
253
- /** Build a page context from its reactive parts. `documentId`/`pon` are thunks
254
- * because inputs aren't readable at construction — both are stable per surface. */
571
+ /** The page this component is drawn on; EPDF-103, naming `what`, outside a page. */
572
+ declare function injectPage(what?: string): EpdfPageContext;
573
+ /**
574
+ * A page context from its parts. `documentId` and `ref` are functions because a component's
575
+ * inputs aren't set yet when it's constructed; both stay the same for the page's lifetime.
576
+ */
255
577
  declare function createPageContext(parts: {
256
578
  documentId: () => string;
257
- pon: () => number;
579
+ ref: () => PageRef;
580
+ view: () => string;
258
581
  pageIndex: Signal<number>;
259
582
  frame: Signal<PageFrame>;
260
583
  transform: Signal<PageTransform>;
261
- /** The rotated content wrapper's LIVE bounding box (the page's display box). */
584
+ /** The page's box on screen: the turned content box's bounding box. */
262
585
  getRect: () => DOMRect;
586
+ getViewDemand?: () => PageViewDemand;
263
587
  }): EpdfPageContext;
264
588
 
265
- export { EPDF_DOCUMENT_SCOPE, EPDF_PAGE, EpdfDocumentGate, EpdfDocumentScope, EpdfKernelHost, EpdfViewer, createPageContext, injectActiveDocumentId, injectCapability, injectCapabilityFor, injectDocumentId, injectDocuments, injectKernel, injectKernelHost, injectKernelValue, injectOptionalCapability, injectOptionalCapabilityFor, injectOptionalSelector, injectOptionalSelectorFor, injectPage, injectSelector, injectSelectorFor, provideEmbedPdf, shallowArray };
266
- export type { EmbedPdfConfig, EpdfDocumentScopeRef, EpdfDocuments, EpdfInitialDocument, EpdfKernelStatus, EpdfPageContext };
589
+ /**
590
+ * The Angular side of `@embedpdf/web`'s page layers: a layer says, for as long as it's there,
591
+ * which part of its page it paints, and the render layer reads which parts are painted, to leave
592
+ * them out of the page's picture.
593
+ */
594
+
595
+ /**
596
+ * Say, for as long as this component is there, that it paints `part` of its page (`pageRef()`,
597
+ * the page context's `ref`; `null` while it paints nothing). Call it in an injection context.
598
+ */
599
+ declare function paintsPagePart(pageRef: () => object | null, part: PagePart): void;
600
+ /**
601
+ * Which parts of the page (`pageRef()`, the page context's `ref`) the layers on it paint: `null`
602
+ * until the first render is done, when every layer made with this one has said what it paints,
603
+ * so the page's picture is never asked for with a part another layer is about to take. Call it
604
+ * in an injection context.
605
+ */
606
+ declare function injectPaintedParts(pageRef: () => object): Signal<PaintedParts | null>;
607
+
608
+ /**
609
+ * An SVG element's attributes from a record of SVG names (`stroke-width`), as `@embedpdf/web`
610
+ * describes a scene node (templates can't spread). Set again when the record changes; an
611
+ * attribute that left the record is removed.
612
+ */
613
+ declare class EpdfSvgAttributes {
614
+ readonly attributes: i0.InputSignal<Readonly<Record<string, string | number>>>;
615
+ constructor();
616
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfSvgAttributes, never>;
617
+ static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfSvgAttributes, "[epdfSvgAttributes]", never, { "attributes": { "alias": "epdfSvgAttributes"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
618
+ }
619
+ /** The shapes, in drawing order, in the `<svg:g>` it's on, and the faces their text draws in. */
620
+ declare class EpdfSvgShapes {
621
+ readonly shapes: i0.InputSignal<readonly SvgShape[]>;
622
+ constructor();
623
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfSvgShapes, never>;
624
+ static ɵcmp: i0.ɵɵComponentDeclaration<EpdfSvgShapes, "g[epdfSvgShapes]", never, { "shapes": { "alias": "epdfSvgShapes"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
625
+ }
626
+
627
+ /** Warn once per key. Quiet in production. */
628
+ declare function devWarn(key: string, message: string): void;
629
+ /** For tests: forget every warning, so one can fire again. */
630
+ declare function resetDevWarnings(): void;
631
+
632
+ /**
633
+ * `epdfTheme()`: a theme written with setting names, as the CSS variables for the element
634
+ * around your viewer. The variables and their lookup are framework-free (`@embedpdf/web`); this
635
+ * types the result for Angular's `[style]` binding, which sets custom properties as they are.
636
+ */
637
+
638
+ /**
639
+ * The CSS variables of a theme: `<div [style]="theme()">` with
640
+ * `theme = computed(() => epdfTheme({ accent: this.accent() }))`. Everything inside follows
641
+ * them, and a CSS variable set in a stylesheet still wins over a setting.
642
+ */
643
+ declare function epdfTheme(theme: EpdfTheme): Record<string, string>;
644
+
645
+ export { CapabilityBinding, EPDF_DOCUMENT_SCOPE, EPDF_PAGE, EPDF_SCOPED_SERVICES, EPDF_SETUP, EpdfDocument, EpdfDocumentGate, EpdfDocumentScope, EpdfDocuments, EpdfError, EpdfKernelHost, EpdfPluginService, EpdfSvgAttributes, EpdfSvgShapes, EpdfViewer, createPageContext, devWarn, epdfTheme, injectCapability, injectDocumentScope, injectKernelHost, injectPage, injectPaintedParts, noPluginError, noViewerError, onServerError, outsidePageError, outsideStageError, paintsPagePart, pluginService, provideEmbedPdf, resetDevWarnings };
646
+ export type { EmbedPdfConfig, EmbedPdfFeature, EmbedPdfKernelConfig, EmbedPdfSetup, EngineSource, EpdfDocumentGateContext, EpdfDocumentScopeRef, EpdfFailedDocumentContext, EpdfLockedDocumentContext, EpdfPageContext, EpdfViewerStatus, EventName, PluginServiceClass, PluginServiceMembers, PluginServiceSpec, PluginSettingsBinding, StreamName };