@embedpdf/web 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.
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ interface PickFileOptions {
7
7
  }
8
8
  /** Open the file dialog; resolve the picked file, or null if cancelled. */
9
9
  declare function pickImageFile(options?: PickFileOptions): Promise<File | null>;
10
- /** The same dialog with NO type filter — any file (attachments). */
10
+ /** The same dialog with no type filter — any file (attachments). */
11
11
  declare const pickFile: (options?: PickFileOptions) => Promise<File | null>;
12
12
  /**
13
13
  * Hand bytes to the user as a browser download (a one-shot `<a download>`).
@@ -15,50 +15,153 @@ declare const pickFile: (options?: PickFileOptions) => Promise<File | null>;
15
15
  * capability's `downloadAttachment` or the document `attachments.download`.
16
16
  */
17
17
  declare function saveFile(bytes: Uint8Array | Blob, name: string, mimeType?: string): void;
18
+ /**
19
+ * The browser's default for a plugin's file prompt (the annotation plugin's
20
+ * `FilePickerProvider`): the file dialog, honouring the request's `accept`
21
+ * filter. A picked `File` carries its own name and type, so it goes straight
22
+ * through as the engine's file source; a dismissed dialog answers `null`.
23
+ */
24
+ declare function pickRequestedFile(request: {
25
+ accept?: string | null;
26
+ }): Promise<{
27
+ data: File;
28
+ } | null>;
29
+ /** Where a provider goes: the annotation plugin's port (its capability satisfies it). */
30
+ interface FilePickerPort<Provider> {
31
+ /** Install the provider, `null` to make click-then-pick tools do nothing. Returns its removal. */
32
+ setFilePickerProvider(provider: Provider | null): () => void;
33
+ }
34
+ /**
35
+ * Install `provider` as a document's file picker, until the call it returns
36
+ * removes it. A document has one port, so a second install while the first
37
+ * is there silently replaces it: `onRepeat` hears of it (an adapter warns in
38
+ * development, saying where to install it once).
39
+ */
40
+ declare function installFilePickerProvider<Provider>(annotation: FilePickerPort<Provider>, provider: Provider | null, onRepeat: () => void): () => void;
18
41
  //#endregion
19
- //#region src/clipboard.d.ts
42
+ //#region src/clock.d.ts
43
+ /**
44
+ * The browser's time, for the kernel: `createKernel({ clock: browserClock() })`.
45
+ * Timers, and frames from `requestAnimationFrame`, so plugins can animate and
46
+ * merge their wake-ups into one per painted frame. Structurally `HostClock`
47
+ * from `@embedpdf/core`, declared here so this package stays dependency-free.
48
+ */
49
+ /** Structural twin of `HostClock` (`@embedpdf/core`). */
50
+ interface WebClock {
51
+ now(): number;
52
+ after(ms: number, run: () => void): () => void;
53
+ nextFrame?(run: (timeMs: number) => void): () => void;
54
+ }
55
+ /**
56
+ * The browser's clock. A page without `requestAnimationFrame` (some test
57
+ * environments) gets a clock without frames, and plugins then skip animation.
58
+ */
59
+ declare function browserClock(): WebClock;
60
+ //#endregion
61
+ //#region src/printer.d.ts
20
62
  /**
21
- * Clipboard wiring for text selection — the environmental half of copy.
63
+ * The browser's print dialog, for the kernel: `createKernel({ printer: browserPrinter() })`.
22
64
  *
23
- * The selection plugin is deliberately DOM-free: its public capability ends
24
- * at `readText()` (data). Everything that touches `navigator.clipboard` or
25
- * the native `copy` event lives HERE, structurally typed so this module
26
- * stays a pure DOM utility with no EmbedPDF dependencies (the selection
27
- * capability satisfies {@link ClipboardSelectionSource} as-is).
65
+ * Where the browser's own PDF viewer prints a PDF file (a desktop browser with it on), the
66
+ * printer takes the kernel's PDF copy and the viewer prints it from a hidden frame: sharp text
67
+ * and lines. Everywhere it prints pictures: one sheet per page, the page's picture on a sheet of
68
+ * the page's own size (`@page`), shrunk to fit when the browser prints on other paper. A desktop
69
+ * browser prints the sheets from a hidden frame. iOS prints a frame as a picture of the page
70
+ * around it, so there the sheets print from the page itself, hidden on screen. Safari prints only
71
+ * from a tap, so there the printer says it `needsTap`.
72
+ *
73
+ * Structurally `HostPrinter` from `@embedpdf/core`, declared here so this package stays
74
+ * dependency-free.
75
+ */
76
+ /** Structural twin of `PrintSheet` (`@embedpdf/core`). */
77
+ interface WebPrintSheet {
78
+ readonly image: Blob;
79
+ readonly widthPt: number;
80
+ readonly heightPt: number;
81
+ }
82
+ /** Structural twin of `StagedPrint` (`@embedpdf/core`). */
83
+ interface WebStagedPrint {
84
+ print(): Promise<void>;
85
+ dispose(): void;
86
+ }
87
+ /** Structural twin of `HostPrinter` (`@embedpdf/core`). */
88
+ interface WebPrinter {
89
+ readonly needsTap?: boolean;
90
+ stage(sheets: readonly WebPrintSheet[], options: {
91
+ readonly name: string;
92
+ readonly signal?: AbortSignal;
93
+ }): Promise<WebStagedPrint>;
94
+ stagePdf?(pdf: Uint8Array, options: {
95
+ readonly name: string;
96
+ readonly signal?: AbortSignal;
97
+ }): Promise<WebStagedPrint>;
98
+ }
99
+ /** The browser's printer: it prints PDFs where the browser's own PDF viewer does. */
100
+ declare function browserPrinter(): WebPrinter;
101
+ //#endregion
102
+ //#region src/css-text.d.ts
103
+ /**
104
+ * Style records as CSS text. This package hands styles out as records with
105
+ * camelCase names (`strokeWidth`, `fontFamily`) and lengths with their units,
106
+ * the shape a style object takes in React, Vue and Angular. A framework whose
107
+ * `style` is a string (Svelte) turns a record into CSS text with
108
+ * {@link cssText}: `stroke-width: …; font-family: …`. Paint values are
109
+ * `var(--epdf-…, …)`, which only work in `style`, never in an SVG
110
+ * presentation attribute.
28
111
  */
112
+ /** A style record: CSS property names in camelCase, each a value or left out. */
113
+ type StyleRecord<Style> = { readonly [Name in keyof Style]?: string | number | null; };
114
+ /** A style record as CSS text, leaving out what is `undefined` or `null`. */
115
+ declare function cssText<Style extends StyleRecord<Style>>(style: Style): string;
116
+ //#endregion
117
+ //#region src/clipboard.d.ts
29
118
  /** The slice of the selection capability clipboard wiring needs. */
30
119
  interface ClipboardSelectionSource {
31
120
  hasSelection(): boolean;
32
121
  canCopy(): boolean;
33
122
  readText(): Promise<string>;
34
- onChange(cb: () => void): () => void;
35
- onCommit(cb: () => void): () => void;
123
+ onChanged(listener: () => void): () => void;
124
+ onCommitted(listener: () => void): () => void;
36
125
  }
37
126
  interface SelectionClipboardOptions {
38
127
  /**
39
128
  * Prefetch the selected text when the selection settles (default true).
40
- * This is what makes the NATIVE copy path work: a `copy` event handler
129
+ * This is what makes the native copy path work: a `copy` event handler
41
130
  * must call `clipboardData.setData` synchronously — awaiting a page-text
42
131
  * read inside it is too late — and it also keeps the async path instant.
43
132
  * The fetch is one versioned, cached, permission-gated read per page;
44
133
  * pass false to make copying strictly on-demand.
45
134
  */
46
135
  prefetch?: boolean;
47
- /** Event target to listen on. Defaults to `document`. */
48
- target?: Document;
136
+ /**
137
+ * Whose keys and copies these are (default `document`, the whole page). An element takes
138
+ * them only while focus is inside it, for a page with more than one viewer; a copy from the
139
+ * browser's Edit menu included, which reaches the page's body when nothing is selected there.
140
+ */
141
+ target?: Document | HTMLElement;
142
+ /**
143
+ * Called when Ctrl/Cmd+C or a copy (Edit → Copy) meets a selection the user may not copy.
144
+ * The browser's own copy is prevented either way; this is where to say so.
145
+ */
146
+ onRefused?: () => void;
49
147
  }
50
148
  /**
51
149
  * Wire clipboard copy for a selection. Two delivery paths, both fed by the
52
150
  * prefetched text:
53
151
  *
54
152
  * - the native `copy` event (menu Edit→Copy, or ctrl/cmd+C while the page
55
- * has a DOM selection or focused editable — e.g. the viewer shell's
56
- * focus sink): answered SYNCHRONOUSLY from the cache;
153
+ * has a DOM selection): answered synchronously from the cache;
57
154
  * - ctrl/cmd+C with no DOM selection (canvas-rendered viewers usually
58
155
  * have none, and browsers don't dispatch `copy` then): a keydown
59
156
  * fallback writes via the async Clipboard API inside the keystroke's
60
157
  * user-activation window.
61
158
  *
159
+ * A selection the user may not copy is never copied by either path, nor by
160
+ * the browser: the key and the copy are prevented, and `onRefused` says so.
161
+ * Keys and copies in a text field are the field's, and left alone. With an
162
+ * element as `target`, only the keys and copies made while focus is inside it
163
+ * are its own.
164
+ *
62
165
  * Returns the unwire function. Mount once per document view (not per page).
63
166
  */
64
167
  declare function wireSelectionClipboard(selection: ClipboardSelectionSource, options?: SelectionClipboardOptions): () => void;
@@ -69,29 +172,67 @@ declare function wireSelectionClipboard(selection: ClipboardSelectionSource, opt
69
172
  * with what was copied ('' when nothing was — nothing is written then, so an
70
173
  * empty selection never clobbers the user's clipboard).
71
174
  */
72
- declare function copySelection(selection: ClipboardSelectionSource): Promise<string>;
175
+ declare function copySelection(selection: Pick<ClipboardSelectionSource, 'readText'>): Promise<string>;
73
176
  //#endregion
74
- //#region src/anchored-position.d.ts
177
+ //#region src/command-shortcuts.d.ts
178
+ /** Why a matched command won't run whatever the moment, as the commands plugin says it. */
179
+ interface ShortcutRefusal {
180
+ /** Turned off in this viewer, or not allowed by the scope. */
181
+ readonly reason: 'turned-off' | 'not-allowed';
182
+ /** A sentence for the user. */
183
+ readonly message: string;
184
+ }
185
+ /** The part of the commands capability shortcuts need. */
186
+ interface ShortcutCommands {
187
+ /**
188
+ * The command a keystroke's shortcut names, with its refusal when it's refused, or `null`.
189
+ * With `inTextField`, only a command that runs from text fields.
190
+ */
191
+ matchStroke(stroke: KeyboardEvent, options: {
192
+ isMac: boolean;
193
+ inTextField: boolean;
194
+ }): {
195
+ readonly commandId: string;
196
+ readonly refusal: ShortcutRefusal | null;
197
+ } | null;
198
+ canExecute(id: string): boolean;
199
+ execute(id: string): Promise<unknown>;
200
+ }
201
+ interface CommandShortcutOptions {
202
+ /** Whether `Mod` is Cmd (a Mac) or Ctrl. Default: what the browser says. */
203
+ isMac?: boolean;
204
+ /** Where to listen. Default: `window`. */
205
+ target?: Pick<Window, 'addEventListener' | 'removeEventListener'>;
206
+ /** Called when a key's command is turned off or not allowed, with why: say so to the user. */
207
+ onRefused?: (commandId: string, refusal: ShortcutRefusal) => void;
208
+ }
209
+ /** Whether this is an Apple platform, where `Mod` is Cmd and shortcuts show as `⌘K`. */
210
+ declare const isMacPlatform: () => boolean;
211
+ /**
212
+ * Run a command when its shortcut is pressed, and return the function that stops listening.
213
+ * Keys typed into a text field are left alone, except a command's that says it runs from text
214
+ * fields (`shortcutInTextFields`, as Find does). A command that's turned off or not allowed
215
+ * still takes its key, so the browser doesn't do something else with it (Cmd+P printing the
216
+ * page), and `onRefused` says why. A key whose command only can't run right now keeps its
217
+ * browser default.
218
+ */
219
+ declare function bindCommandShortcuts(commands: ShortcutCommands, options?: CommandShortcutOptions): () => void;
220
+ //#endregion
221
+ //#region src/page-ref.d.ts
75
222
  /**
76
- * Anchored-overlay geometry for UI floating over page content (selection
77
- * menus, draft menus, popovers): the projector SNAPSHOT contract, the
78
- * anchor shape, and the placement math. Pure values and pure functions over
79
- * structural rect/point shapes — no EmbedPDF types, no framework, no
80
- * reactive lifecycle — so every framework adapter (React, Vue, Svelte,
81
- * Angular) shares ONE implementation and binds it with its NATIVE
82
- * reactivity (context revision, signals, computed, $derived).
83
- *
84
- * The boundary laws:
85
- * - Placement policy lives HERE, above the projector seam: a projector
86
- * only knows geometry — never what is being positioned nor where it
87
- * prefers to sit.
88
- * - A projector describes the CURRENT projection; it never implements a
89
- * framework-style reactive lifecycle. State-driven changes (the Stage
90
- * camera) must reach consumers through the framework's own render
91
- * cycle so surface and overlay commit TOGETHER; only genuinely
92
- * browser-driven changes (a PageView moving because the document
93
- * scrolled) use an external listener ({@link observeClientGeometry}).
223
+ * A page address — structurally the kernel's `PageRef` from `@embedpdf/core`
224
+ * (`{ kind: 'objectNumber', objectNumber }`), mirrored here like the
225
+ * rect/point shapes so this package stays free of EmbedPDF imports. Every
226
+ * `PageRef` the kernel or a plugin hands out satisfies it; the surfaces pass
227
+ * it through untouched (a stage's `pageAt` result, an anchor's page) — the
228
+ * number is never read here.
94
229
  */
230
+ interface PageRef {
231
+ readonly kind: 'objectNumber';
232
+ readonly objectNumber: number;
233
+ }
234
+ //#endregion
235
+ //#region src/anchored-position.d.ts
95
236
  interface AnchoredRect {
96
237
  x: number;
97
238
  y: number;
@@ -102,36 +243,68 @@ interface AnchoredPoint {
102
243
  x: number;
103
244
  y: number;
104
245
  }
105
- type AnchoredPlacement = 'top' | 'right' | 'bottom' | 'left';
246
+ /** A side of the box. */
247
+ type AnchoredSide = 'top' | 'right' | 'bottom' | 'left';
248
+ /**
249
+ * Where anchored UI sits: a side of the box, centred on it, or lined up with
250
+ * the side's start or end (`'top-end'`: above the box, right edges lined up).
251
+ */
252
+ type AnchoredPlacement = AnchoredSide | `${AnchoredSide}-${'start' | 'end'}`;
253
+ /** How anchored UI is placed: where, how far from the box, and whether it may move to fit. */
254
+ interface AnchoredOptions {
255
+ placement: AnchoredPlacement;
256
+ /** Screen px between the box and the UI; negative overlaps the box. */
257
+ gap: number;
258
+ /** Stays where `placement` puts it: never flips, never moves to stay in view. */
259
+ pinned?: boolean;
260
+ }
106
261
  /** CSS-ready output: absolute/fixed `left`/`top` plus a centering transform. */
107
262
  interface AnchoredPosition {
108
263
  left: number;
109
264
  top: number;
110
265
  transform: string;
266
+ /** Where it sits: the placement asked for, or its opposite side when that had no room. */
267
+ placement: AnchoredPlacement;
268
+ }
269
+ interface AnchoredSize {
270
+ width: number;
271
+ height: number;
111
272
  }
112
273
  /**
113
- * Place an upright element around `box` (screen px). `avoid` is a screen
114
- * point the element must clear (e.g. the rotate knob): the element extends
115
- * ONLY the edge it sits on, and ONLY when the point protrudes past that
116
- * edge — so it clears the obstacle without ever shifting off-centre on the
117
- * other axis. When the point is on another side (e.g. a 90° shape, knob at
118
- * mid-height for a `top` placement) the edge is untouched and the element
119
- * stays centred on `box`.
120
- */
121
- /** What anchored UI attaches to: a content-space rect on a page, plus
122
- * optional content-space points the UI must clear (e.g. a rotate knob).
274
+ * What placement needs to keep anchored UI on screen: the element's size, and
275
+ * the area it must stay inside, in the projector's space (the Stage's own box,
276
+ * or the browser window).
277
+ */
278
+ interface AnchoredFit {
279
+ size: AnchoredSize;
280
+ view: AnchoredRect;
281
+ }
282
+ /** How a page is shown right now: its view scale, its rotation on screen, and its zoom (1 = 100%). */
283
+ interface PageViewEnv {
284
+ scale: number;
285
+ rotation: 0 | 90 | 180 | 270;
286
+ zoom: number;
287
+ }
288
+ /** What anchored UI attaches to: a page-space rect on a page, plus
289
+ * optional page-space points the UI must clear (e.g. a rotate knob).
123
290
  * Structural — plugin anchor reads satisfy it without importing this
124
291
  * package. */
125
292
  interface AnchorTarget {
126
- pon: number;
293
+ page: PageRef;
127
294
  bounds: AnchoredRect;
295
+ /**
296
+ * Where the box is in a given view, for a box whose size or orientation
297
+ * depends on it (a note that keeps its size on screen). The projection
298
+ * calls it, so the anchor itself never has to follow the view.
299
+ */
300
+ boundsIn?: (view: PageViewEnv) => AnchoredRect | null;
128
301
  avoid?: AnchoredPoint[];
129
302
  }
130
303
  /**
131
- * A page surface's PROJECTION SNAPSHOT: how a content-space rect on a page
304
+ * A page surface's projection snapshot: how a page-space rect on a page
132
305
  * becomes screen coordinates, right now. Provided by `<Stage>`
133
306
  * (camera-driven, pure state, no DOM reads) and `<PageView>` (DOM-measured).
134
- * Deliberately NO subscribe here — when projection changes is a framework
307
+ * Deliberately no subscribe here — when projection changes is a framework
135
308
  * binding concern (see the module doc), not part of the snapshot.
136
309
  */
137
310
  interface ViewProjector {
@@ -144,38 +317,163 @@ interface ViewProjector {
144
317
  * position:fixed, so no ancestor overflow can clip. (PageView)
145
318
  */
146
319
  space: 'overlay' | 'client';
147
- /** Content-space rect on a page → coords in `space`. Null: not projectable
320
+ /** Page-space rect on a page → coords in `space`. Null: not projectable
148
321
  * right now (page not shown / not measurable yet). */
149
- toScreen(pon: number, rect: AnchoredRect): AnchoredRect | null;
150
- toScreenPoint(pon: number, at: AnchoredPoint): AnchoredPoint | null;
322
+ toScreen(page: PageRef, rect: AnchoredRect): AnchoredRect | null;
323
+ toScreenPoint(page: PageRef, at: AnchoredPoint): AnchoredPoint | null;
151
324
  /** The page's live view facts (for anchor reads that need them — e.g. the
152
325
  * screen-constant rotate-knob stalk), or null when the page isn't shown. */
153
- viewEnv(pon: number): {
154
- scale: number;
155
- rotation: 0 | 90 | 180 | 270;
156
- zoom: number;
157
- } | null;
326
+ viewEnv(page: PageRef): PageViewEnv | null;
327
+ /** The area anchored UI can be seen in, in `space`: the surface's box, or
328
+ * the window. Null while it isn't measurable yet. */
329
+ view(): AnchoredRect | null;
158
330
  }
159
331
  /**
160
- * THE anchored projection: anchor → screen position, in one pure call —
332
+ * The anchored projection: anchor → screen position, in one pure call —
161
333
  * projection, avoid-point transform, and the shared placement policy.
162
334
  * Frameworks recompute this inside their native reactive primitive
163
335
  * (a React render, a Vue `computed`, an Angular signal, a Svelte
164
336
  * `$derived`); nothing here schedules anything.
337
+ *
338
+ * Null when there is nothing to show: the page isn't shown, or the box is so
339
+ * far out of view that the UI around it can't reach into it.
165
340
  */
166
- declare function projectAnchoredTarget(projector: ViewProjector, anchor: AnchorTarget, placement: AnchoredPlacement, gap: number): AnchoredPosition | null;
341
+ declare function projectAnchoredTarget(projector: ViewProjector, anchor: AnchorTarget, { placement, gap, pinned }: AnchoredOptions, fit?: AnchoredFit | null): AnchoredPosition | null;
167
342
  /**
168
- * The one genuinely BROWSER-driven invalidation: an element in normal
343
+ * The one genuinely browser-driven invalidation: an element in normal
169
344
  * document flow moves when the document scrolls or the window resizes —
170
345
  * no state change announces it. `<PageView>`'s binding registers this;
171
346
  * the Stage never does (its camera is state, delivered through render).
172
347
  */
173
348
  declare function observeClientGeometry(callback: () => void): () => void;
349
+ /**
350
+ * The area anchored UI stays inside: the surface's own box (`'overlay'` space, the element's
351
+ * positioned parent) or the window (`'client'` space). Null while the element has no
352
+ * positioned parent to measure.
353
+ */
354
+ declare function anchoredViewOf(element: HTMLElement, space: ViewProjector['space']): AnchoredRect | null;
355
+ /** Whether two measurements give the same sizes, so a placement made from the first still holds. */
356
+ declare function sameAnchoredFit(left: AnchoredFit | null, right: AnchoredFit): boolean;
357
+ /**
358
+ * Measure anchored UI's `element` and the area it stays inside ({@link anchoredViewOf}) now, and
359
+ * again whenever either resizes: `report` hears every measurement (keep it when
360
+ * {@link sameAnchoredFit} says it changed). A camera move changes neither, so it measures nothing.
361
+ * Returns the function that stops measuring.
362
+ */
363
+ declare function observeAnchoredFit(element: HTMLElement, space: ViewProjector['space'], report: (fit: AnchoredFit) => void): () => void;
364
+ /**
365
+ * Place an upright element around `box` (screen px). `avoid` is a screen
366
+ * point the element must clear (e.g. the rotate knob): the element extends
367
+ * only the edge it sits on, and only when the point protrudes past that
368
+ * edge — so it clears the obstacle without ever shifting along the side.
369
+ * When the point is on another side (e.g. a 90° shape, knob at mid-height
370
+ * for a `top` placement) the edge is untouched.
371
+ */
174
372
  declare function positionAnchoredRect(box: AnchoredRect, placement: AnchoredPlacement, gap: number, avoid?: AnchoredPoint | null): AnchoredPosition;
373
+ /**
374
+ * Place an element of a known size around `box`, inside `fit.view`. It sits
375
+ * where `placement` says when it fits there; otherwise on the opposite side,
376
+ * lined up the same way, when that fits or has more room. Then, while `box`
377
+ * is in view, it moves along both axes to stay inside the view, `gap` from
378
+ * its edges; UI whose box scrolled out of view goes with it. The result is
379
+ * the element's top-left corner, with no transform.
380
+ */
381
+ declare function fitAnchoredRect(box: AnchoredRect, placement: AnchoredPlacement, gap: number, fit: AnchoredFit, avoid?: AnchoredPoint | null): AnchoredPosition;
382
+ //#endregion
383
+ //#region src/annotation-frame.d.ts
384
+ /**
385
+ * An annotation's frame in pixels. The annotation plugin hands out each
386
+ * render item's frame (a box, its turn, and how large the page draws it) and
387
+ * the raster inside it, in page points; these put them in the page layer's
388
+ * pixels, with the CSS turn about the middle. Plain geometry over structural
389
+ * shapes, the same for every framework adapter, so none of them converts,
390
+ * turns or scales anything itself.
391
+ */
392
+ /** A box in page points and its turn, degrees clockwise about its middle. */
393
+ interface TurnedBox {
394
+ box: {
395
+ x: number;
396
+ y: number;
397
+ width: number;
398
+ height: number;
399
+ };
400
+ rotation: number;
401
+ }
402
+ /** A frame: its box and turn, and how large the page draws it relative to its own size. */
403
+ interface FrameBox extends TurnedBox {
404
+ scale: number;
405
+ }
406
+ /** What a page layer converts with: its transform's `toPixels`, its turn, and its zoom (1 = 100%). */
407
+ interface PageLayerTransform {
408
+ toPixels(point: {
409
+ x: number;
410
+ y: number;
411
+ }): {
412
+ x: number;
413
+ y: number;
414
+ };
415
+ rotation: number;
416
+ zoom: number;
417
+ }
418
+ /** A box in pixels and the CSS that turns it about its middle (`undefined` for no turn). */
419
+ interface PixelBox {
420
+ left: number;
421
+ top: number;
422
+ width: number;
423
+ height: number;
424
+ transform: string | undefined;
425
+ }
426
+ /** A frame in the page layer's pixels, and what a look drawn inside it needs. */
427
+ interface FramePixels extends PixelBox {
428
+ /** Its turn on screen: its own and the page's, degrees clockwise. */
429
+ rotationOnScreen: number;
430
+ /**
431
+ * How large the frame is on screen relative to the annotation at its 100%
432
+ * size: the zoom, or for a body that keeps its size on screen, the zoom
433
+ * capped at 1. A look drawn at `design` size is scaled by this.
434
+ */
435
+ scale: number;
436
+ /** The frame's size at the annotation's 100% size, in pixels: the size a look is drawn at. */
437
+ design: {
438
+ width: number;
439
+ height: number;
440
+ };
441
+ }
442
+ /** A box inside its frame as CSS percentages of the frame, so it lands in place at any size. */
443
+ interface FrameFraction {
444
+ left: string;
445
+ top: string;
446
+ width: string;
447
+ height: string;
448
+ transform: string | undefined;
449
+ }
450
+ /** A frame in the page layer's pixels: where it is, its turn, and the size and scale a look is drawn at. */
451
+ declare function frameInPixels(frame: FrameBox, page: PageLayerTransform): FramePixels;
452
+ /**
453
+ * A raster inside its frame, as CSS percentages of the frame. `raster`'s box
454
+ * is relative to the frame's box, as the annotation plugin hands it out.
455
+ */
456
+ declare function rasterInFrame(raster: TurnedBox, frame: TurnedBox): FrameFraction;
457
+ /** The frame a renderer's look draws into: the size to draw at, its turn on screen, and its scale. */
458
+ interface AnnotationLookFrame {
459
+ width: number;
460
+ height: number;
461
+ /** Its turn on screen, the page's and its own, degrees clockwise. */
462
+ rotation: number;
463
+ /** How much the layer scales what the look draws. */
464
+ scale: number;
465
+ }
466
+ /**
467
+ * The frame a look draws into. A `scaled` look (the default) draws at the
468
+ * annotation's 100% size (`design`) inside an element of that size, which
469
+ * the layer scales by `scale` from its top-left corner; an unscaled look
470
+ * draws at the frame's size on screen.
471
+ */
472
+ declare function lookFrameOf(frame: FramePixels, scaled: boolean): AnnotationLookFrame;
175
473
  //#endregion
176
474
  //#region src/cursor.d.ts
177
475
  /**
178
- * SVG → CSS cursor string. The armed-tool indicator is the CURSOR itself (the
476
+ * SVG → CSS cursor string. The armed-tool indicator is the cursor itself (the
179
477
  * only zero-latency pointer-locked pixel the web has — the OS composites it),
180
478
  * so an app hands its toolbar icon here and assigns the result via the
181
479
  * interaction hub's `setToolCursor`.
@@ -207,14 +505,43 @@ interface SvgCursorOptions {
207
505
  }
208
506
  /** Build the CSS cursor value: `url("data:image/svg+xml,…") x y, fallback`. */
209
507
  declare function svgCursor({ svg, hotspot, fallback }: SvgCursorOptions): string;
508
+ /** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS cursor ('crosshair', 'url(…) 4 4, copy'). */
509
+ type ToolCursorImage = SvgCursorOptions | string;
510
+ /**
511
+ * A tool's cursors: the tool, and its cursors by keyword. Build `svg` from the same icon your
512
+ * toolbar shows, so the cursor and the button never drift apart.
513
+ */
514
+ interface ToolCursorSpec {
515
+ toolId: string;
516
+ /**
517
+ * keyword → cursor: replace the keywords this tool can show (its own cursor, 'crosshair' or
518
+ * 'copy', and hover claims, 'text' over text) with the tool's look. Keywords left out show as
519
+ * they are: a markup tool's 'default' stays the bare arrow, a 'move' claim drops the icon. An
520
+ * SVG value's `fallback` defaults to the keyword it replaces.
521
+ */
522
+ cursors: Record<string, ToolCursorImage>;
523
+ }
524
+ /**
525
+ * A tool's cursors as CSS cursor values, for the interaction hub's `setToolCursor(toolId,
526
+ * cursors)`: a string stays as it is, an SVG becomes its `url(…)` cursor, falling back to the
527
+ * keyword it replaces unless it names its own fallback.
528
+ */
529
+ declare function toolCursorsOf(cursors: Record<string, ToolCursorImage>): Record<string, string>;
210
530
  //#endregion
211
531
  //#region src/external-uri.d.ts
212
532
  /**
213
- * Validate a link URI for opening in the browser. Returns the URI when its
214
- * scheme is allowed (scheme-relative and bare URIs resolve as https), else
215
- * `null`. Never throws — a malformed URI is just an unopenable one.
533
+ * Validate a link URI for opening in the browser. Returns the absolute URI to open when its
534
+ * scheme is allowed (scheme-relative and bare `www.` URIs become https), else `null`. Never
535
+ * throws — a malformed URI is just an unopenable one.
216
536
  */
217
537
  declare function sanitizeExternalUri(uri: string, allowedSchemes?: Iterable<string>): string | null;
538
+ /**
539
+ * Open a link target in a new tab when its address is allowed (`http`,
540
+ * `https`, `mailto`, `tel`); anything else is refused. True when it opened.
541
+ * Call it inside the user's click (the link plugin's `activate()` does), so
542
+ * the click is still the gesture that opens the tab.
543
+ */
544
+ declare function openExternalUri(uri: string): boolean;
218
545
  //#endregion
219
546
  //#region src/actions-ui.d.ts
220
547
  /** Structural twin of plugin-actions' `ActionOrigin`. */
@@ -226,20 +553,20 @@ interface ActionsUiEffectContext {
226
553
  }
227
554
  /** Structural twin of plugin-actions' `ActionUiAdapter`. */
228
555
  interface ActionsUiAdapterShape {
229
- openUri(uri: string, opts: {
556
+ openUri(uri: string, options: {
230
557
  isMap: boolean;
231
558
  origin: ActionsUiOrigin;
232
559
  }): void;
233
- print(opts?: ActionsUiEffectContext): void;
234
- alert?(message: string, opts: ActionsUiEffectContext & {
560
+ print(options?: ActionsUiEffectContext): void;
561
+ alert?(message: string, options: ActionsUiEffectContext & {
235
562
  icon: number;
236
563
  title?: string;
237
564
  }): void;
238
- gotoPage?(page: number, opts: ActionsUiEffectContext): void;
565
+ gotoPage?(page: number, options: ActionsUiEffectContext): void;
239
566
  }
240
567
  interface DefaultActionsUiAdapterOptions {
241
568
  /**
242
- * Per-handler overrides. Pass a FUNCTION for late binding (a binding's
569
+ * Per-handler overrides. Pass a function for late binding (a binding's
243
570
  * mutable handlers ref) — it is consulted on every effect, so a fresh
244
571
  * closure never needs a reinstall.
245
572
  */
@@ -250,12 +577,18 @@ interface DefaultActionsUiAdapterOptions {
250
577
  declare const createDefaultActionsUiAdapter: (options?: DefaultActionsUiAdapterOptions) => ActionsUiAdapterShape;
251
578
  //#endregion
252
579
  //#region src/painted-image.d.ts
253
- /** A browser image source whose object URL has an explicit lifetime. */
580
+ /**
581
+ * A browser image source whose object URL has an explicit lifetime: an
582
+ * engine `PageImageHandle`, whose `objectUrl()` is cancellable and follows an
583
+ * `AbortSignal` through `abortWith()`.
584
+ */
254
585
  interface ObjectUrlImageSource {
255
- objectUrl(signal?: AbortSignal): Promise<{
256
- url: string;
257
- revoke(): void;
258
- }>;
586
+ objectUrl(): {
587
+ abortWith(signal: AbortSignal): PromiseLike<{
588
+ url: string;
589
+ revoke(): void;
590
+ }>;
591
+ };
259
592
  }
260
593
  interface PaintedImageCallbacks {
261
594
  onPainted(): void;
@@ -280,9 +613,9 @@ declare function bindPaintedImage(image: HTMLImageElement, source: ObjectUrlImag
280
613
  * stays plugin-free, per the layering law).
281
614
  *
282
615
  * Honesty about reach: `navigator.vibrate` works on Android browsers and is
283
- * NOT implemented in iOS Safari — there the default provider is a silent
616
+ * not implemented in iOS Safari — there the default provider is a silent
284
617
  * no-op until Apple ships a haptics API. (The community switch-control hacks
285
- * were evaluated and deliberately NOT shipped: Apple patched programmatic
618
+ * were evaluated and deliberately not shipped: Apple patched programmatic
286
619
  * triggering in iOS 26.5, leaving only tap-on-control tricks that don't fit a
287
620
  * viewer's gesture moments.) The real iPhone haptic today comes through a
288
621
  * native shell: a WKWebView embedder installs a script message handler and
@@ -303,7 +636,7 @@ interface WebPlatformFeedback {
303
636
  declare const vibrationFeedback: WebPlatformFeedback;
304
637
  /**
305
638
  * A provider for native iOS shells: forwards each call to a WKWebView script
306
- * message handler the HOST APP installed under `handlerName` — an explicit
639
+ * message handler the host app installed under `handlerName` — an explicit
307
640
  * contract between the embedder and their own app, never a global this library
308
641
  * sniffs on its own. The message shape is
309
642
  * `{ family: 'selection' } | { family: 'impact', weight } | { family: 'notify', kind }`;
@@ -315,40 +648,40 @@ declare const wkFeedback: (handlerName: string, fallback?: WebPlatformFeedback)
315
648
  //#endregion
316
649
  //#region src/stage-gestures.d.ts
317
650
  /**
318
- * Stage gesture controller — the ONE DOM input binding for a Stage surface,
651
+ * Stage gesture controller — the one DOM input binding for a Stage surface,
319
652
  * shared by every framework adapter (React, Angular, …) so the feel can never
320
653
  * drift between them.
321
654
  *
322
655
  * The premise (see the stage plugin's camera doctrine): desktop inputs arrive
323
656
  * with physics already applied by the OS — momentum lives in the wheel stream,
324
- * a trackpad pinch is one pre-arbitrated gesture stream. Touch arrives RAW:
657
+ * a trackpad pinch is one pre-arbitrated gesture stream. Touch arrives raw:
325
658
  * with `touch-action: none` the platform scroller is out of the loop, so the
326
659
  * ballistics (velocity → fling) and the arbitration (is this contact a scroll,
327
660
  * a pinch, a tap, or a tool gesture?) must be synthesized here.
328
661
  *
329
- * Arbitration is MODALITY-AWARE:
662
+ * Arbitration is modality-aware:
330
663
  * - touch — navigation-first: one finger pans (whatever tool is armed), two
331
664
  * fingers pinch-zoom around their centroid, release velocity flings, a tap
332
665
  * forwards as a click, a double-tap zoom-toggles, a long-press hands the
333
666
  * gesture to the interaction hub (text selection), and a second finger
334
- * landing mid-tool-gesture CANCELS it into a pinch (the Notes/Procreate
667
+ * landing mid-tool-gesture cancels it into a pinch (the Notes/Procreate
335
668
  * convention).
336
669
  * - mouse/pen — tool-first, exactly the pre-existing behavior: with a hub,
337
670
  * every down/move/up forwards (pan is the pan tool's job); without one,
338
671
  * dragging pans. Wheel and Safari-trackpad gesture events are unchanged.
339
672
  *
340
- * Camera writes are rAF-COALESCED: pointer events only update gesture state;
673
+ * Camera writes are rAF-coalesced: pointer events only update gesture state;
341
674
  * one animation-frame tick applies at most one pan and one zoom per frame,
342
675
  * inside the host's begin/endGesture transaction. Events may arrive at 120 Hz;
343
676
  * the camera moves at display rate.
344
677
  *
345
- * Dependency note: this module speaks to the stage through the STRUCTURAL
346
- * {@link StageGestureHost} interface (satisfied by `StageCapability`) and to
678
+ * Dependency note: this module speaks to the stage through the structural
679
+ * {@link StageGestureHost} interface (satisfied by `StageHostCapability`) and to
347
680
  * the interaction hub through {@link StageGestureSink} (a closure the adapter
348
681
  * builds) — @embedpdf/web stays free of plugin imports, per the layering law.
349
682
  */
350
683
  type StagePointerKind = 'mouse' | 'pen' | 'touch';
351
- /** What the controller needs from the camera — `StageCapability` satisfies it. */
684
+ /** What the controller needs from the camera — `StageHostCapability` satisfies it. */
352
685
  interface StageGestureHost {
353
686
  panBy(dxScreen: number, dyScreen: number): void;
354
687
  zoomAround(screenPt: {
@@ -362,7 +695,7 @@ interface StageGestureHost {
362
695
  /** Momentum pan from a release velocity in screen px/s. */
363
696
  fling(velocityX: number, velocityY: number): void;
364
697
  /** True while a tween/fling runs — a touch-down then is a "catch", not a tap. */
365
- cameraInMotion(): boolean;
698
+ isMoving(): boolean;
366
699
  doubleTapZoom(screenPt: {
367
700
  x: number;
368
701
  y: number;
@@ -370,25 +703,25 @@ interface StageGestureHost {
370
703
  }
371
704
  /**
372
705
  * Where non-navigation gestures go — the adapter's bridge to the interaction
373
- * hub. Every callback receives the ORIGINAL PointerEvent so the adapter can
706
+ * hub. Every callback receives the original PointerEvent so the adapter can
374
707
  * resolve pages/points exactly as it always has. Omit the sink entirely for a
375
708
  * hub-less (built-in pan) stage.
376
709
  */
377
710
  interface StageGestureSink {
378
- down(e: PointerEvent, clickCount: number): void;
379
- move(e: PointerEvent): void;
380
- up(e: PointerEvent): void;
711
+ down(event: PointerEvent, clickCount: number): void;
712
+ move(event: PointerEvent): void;
713
+ up(event: PointerEvent): void;
381
714
  /** The gesture was taken over by navigation (second finger → pinch) or
382
715
  * cancelled by the system — abort, don't commit. */
383
- cancel(e: PointerEvent): void;
716
+ cancel(event: PointerEvent): void;
384
717
  /** Pointer travel with no gesture in flight — cursor feedback. */
385
- hover(e: PointerEvent): void;
718
+ hover(event: PointerEvent): void;
386
719
  /** A touch press held still: hand the gesture to the hub (the adapter
387
720
  * typically forwards it as a word-select down). Subsequent move/up arrive
388
721
  * via {@link move}/{@link up}. */
389
- longPress(e: PointerEvent): void;
722
+ longPress(event: PointerEvent): void;
390
723
  /**
391
- * Touch-consent pre-flight, asked at touch-down BEFORE the contact is
724
+ * Touch-consent pre-flight, asked at touch-down before the contact is
392
725
  * classified: does a tool have standing to own this contact? True when the
393
726
  * armed tool takes fingers wholesale (a drawing tool) or something under
394
727
  * the point claims its drags (a selected annotation's body or handles).
@@ -396,7 +729,7 @@ interface StageGestureSink {
396
729
  * where a second finger still cancels it into a pinch. Must be a pure
397
730
  * read. Absent = never; the contact navigates.
398
731
  */
399
- claimsPoint?(e: PointerEvent): boolean;
732
+ claimsPoint?(event: PointerEvent): boolean;
400
733
  }
401
734
  /** The wheel fields the zoom classifier reads (see `./wheel`'s `WheelSample`). */
402
735
  interface StageWheelSample {
@@ -411,7 +744,7 @@ interface StageGestureOptions {
411
744
  * swallowed, never page-zooming the browser. Default true. */
412
745
  zoomGestures?: boolean;
413
746
  /** The wheel → zoom-factor classifier. Defaults to this package's
414
- * `wheelZoomFactor` (browser wheel classification lives HERE, with the rest
747
+ * `wheelZoomFactor` (browser wheel classification lives here, with the rest
415
748
  * of the browser input handling); inject to override or to fake in tests. */
416
749
  wheelZoomFactor?: (sample: StageWheelSample) => number;
417
750
  /** Tool routing for non-navigation gestures; omit for built-in-pan stages. */
@@ -429,7 +762,7 @@ interface StageGestureOptions {
429
762
  * Release velocity from a trail of pointer samples: the mean velocity over the
430
763
  * trailing `windowMs` (first-to-last inside the window). Null when the trail is
431
764
  * too thin or too stale to trust — the standard "held still, then let go"
432
- * case, which must NOT fling. Pure; exported for tests.
765
+ * case, which must not fling. Pure; exported for tests.
433
766
  */
434
767
  declare function computeReleaseVelocity(samples: ReadonlyArray<{
435
768
  t: number;
@@ -440,7 +773,7 @@ declare function computeReleaseVelocity(samples: ReadonlyArray<{
440
773
  vy: number;
441
774
  } | null;
442
775
  /** Attach the gesture controller to a Stage container. Returns the detach fn. */
443
- declare function createStageGestureController(el: HTMLElement, host: StageGestureHost, options: StageGestureOptions): () => void;
776
+ declare function createStageGestureController(element: HTMLElement, host: StageGestureHost, options: StageGestureOptions): () => void;
444
777
  //#endregion
445
778
  //#region src/selection-handles.d.ts
446
779
  /**
@@ -450,12 +783,12 @@ declare function createStageGestureController(el: HTMLElement, host: StageGestur
450
783
  * `handles` module; this file never imports it, per the layering law:
451
784
  * structural interfaces only).
452
785
  *
453
- * The load-bearing subtlety this module owns: the pointer-DOWN shield must be
454
- * a NATIVE listener. The stage's gesture controller listens natively on the
786
+ * The load-bearing subtlety this module owns: the pointer-down shield must be
787
+ * a native listener. The stage's gesture controller listens natively on the
455
788
  * container, so a framework-synthetic stopPropagation (React runs its
456
- * handlers at the root, AFTER the container's native ones) would be too late
789
+ * handlers at the root, after the container's native ones) would be too late
457
790
  * — the stage would already be panning underneath the handle drag. Once the
458
- * down is captured here, movement tracks by CLIENT DELTAS from the handle's
791
+ * down is captured here, movement tracks by client deltas from the handle's
459
792
  * own base point: no DOM geometry reads, and the client↔overlay conversion
460
793
  * cancels out.
461
794
  */
@@ -472,7 +805,7 @@ interface SelectionHandleSession {
472
805
  interface AttachSelectionHandleOptions {
473
806
  /**
474
807
  * Called at pointer-down to arm a drag: return the drag session plus the
475
- * handle's current BASE point in overlay space (the bar's midpoint — the
808
+ * handle's current base point in overlay space (the bar's midpoint — the
476
809
  * point the user grabbed). Return null to decline (endpoint vanished, page
477
810
  * not laid out) — the press then does nothing and nothing is captured.
478
811
  */
@@ -482,30 +815,30 @@ interface AttachSelectionHandleOptions {
482
815
  } | null;
483
816
  }
484
817
  /** Bind one handle element. Returns the detach fn. */
485
- declare function attachSelectionHandle(el: HTMLElement, options: AttachSelectionHandleOptions): () => void;
818
+ declare function attachSelectionHandle(element: HTMLElement, options: AttachSelectionHandleOptions): () => void;
486
819
  //#endregion
487
820
  //#region src/stage-surface.d.ts
488
821
  interface SurfacePoint {
489
822
  x: number;
490
823
  y: number;
491
824
  }
492
- /** What the binding needs from the stage — `StageCapability` satisfies it. */
825
+ /** What the binding needs from the stage — `StageHostCapability` satisfies it. */
493
826
  interface StageSurfaceHost extends StageGestureHost {
494
- setViewport(size: {
827
+ setViewportSize(size: {
495
828
  width: number;
496
829
  height: number;
497
830
  }): void;
498
831
  setDevicePixelRatio(ratio: number): void;
499
832
  /** Viewport point → the page under it (with per-page display context), or null over a gap. */
500
- pageAt(screen: SurfacePoint): {
501
- pon: number;
833
+ getPageAt(screen: SurfacePoint): {
834
+ ref: PageRef;
502
835
  point: SurfacePoint;
503
836
  scale?: number;
504
837
  rotation?: 0 | 90 | 180 | 270;
505
838
  zoom?: number;
506
839
  } | null;
507
- /** Viewport point → a SPECIFIC page's content space, unclamped (frame-stable projection). */
508
- pointOnPage(pon: number, screen: SurfacePoint): SurfacePoint | null;
840
+ /** Viewport point → a specific page's page space, unclamped (frame-stable projection). */
841
+ viewportToPage(page: PageRef, screen: SurfacePoint): SurfacePoint | null;
509
842
  }
510
843
  /**
511
844
  * The page-resolved sample the binding hands the hub — structurally a
@@ -517,13 +850,13 @@ interface StageSurfaceSample {
517
850
  phase: 'down' | 'move' | 'up' | 'cancel';
518
851
  viewport: SurfacePoint;
519
852
  page?: {
520
- pon: number;
853
+ ref: PageRef;
521
854
  point: SurfacePoint;
522
855
  scale?: number;
523
856
  rotation?: 0 | 90 | 180 | 270;
524
857
  zoom?: number;
525
858
  };
526
- project: (pon: number) => SurfacePoint | null;
859
+ project: (page: PageRef) => SurfacePoint | null;
527
860
  modifiers: {
528
861
  shift: boolean;
529
862
  alt: boolean;
@@ -535,11 +868,11 @@ interface StageSurfaceSample {
535
868
  gesture?: 'long-press';
536
869
  source?: string;
537
870
  }
538
- /** What the binding needs from the interaction hub — `InteractionCapability` satisfies it. */
871
+ /** What the binding needs from the interaction hub — `InteractionHostCapability` satisfies it. */
539
872
  interface StageSurfaceHub {
540
- dispatch(sample: StageSurfaceSample): void;
541
- activeTool(): {
542
- touchDirect?: boolean;
873
+ dispatchPointer(sample: StageSurfaceSample): void;
874
+ getActiveTool(): {
875
+ touch?: 'draw' | 'tap';
543
876
  };
544
877
  wouldClaimTouch(sample: StageSurfaceSample): boolean;
545
878
  }
@@ -547,7 +880,7 @@ interface StageSurfaceOptions {
547
880
  /** Route pointer input to the interaction hub (page-resolved samples) instead
548
881
  * of built-in drag-to-pan. Omit/null for a hub-less stage. */
549
882
  hub?: StageSurfaceHub | null;
550
- /** The lens identity stamped on every sample (`StageCapability.lensId()`),
883
+ /** The lens identity stamped on every sample (`StageHostCapability.getLensId()`),
551
884
  * so lens-scoped handlers only see their own stage's input. */
552
885
  source?: string;
553
886
  /** Ambient zoom gestures (see {@link StageGestureOptions}). Default true. */
@@ -556,7 +889,7 @@ interface StageSurfaceOptions {
556
889
  wheelZoomFactor?: (sample: StageWheelSample) => number;
557
890
  }
558
891
  /** Attach the full surface binding. Returns the detach fn. */
559
- declare function createStageSurface(el: HTMLElement, stage: StageSurfaceHost, options?: StageSurfaceOptions): () => void;
892
+ declare function createStageSurface(element: HTMLElement, stage: StageSurfaceHost, options?: StageSurfaceOptions): () => void;
560
893
  //#endregion
561
894
  //#region src/wheel.d.ts
562
895
  /**
@@ -566,14 +899,14 @@ declare function createStageSurface(el: HTMLElement, stage: StageSurfaceHost, op
566
899
  * A ctrl/cmd wheel event is one of three physically different inputs that
567
900
  * happen to share an event type, each with its own unit system:
568
901
  *
569
- * • trackpad PINCH — browsers (Chrome/Edge/Firefox) synthesize ctrl+wheel
902
+ * • trackpad pinch — browsers (Chrome/Edge/Firefox) synthesize ctrl+wheel
570
903
  * px-deltas scaled to the de-facto convention that `exp(-Δ/100)` tracks
571
904
  * the physical finger scale 1:1. Anything weaker feels dead (the content
572
905
  * must track the fingers — the Figma/maps feel).
573
- * • mouse NOTCHES — coarse steps: ±100/±120 px in Chrome, lines/pages mode
906
+ * • mouse notches — coarse steps: ±100/±120 px in Chrome, lines/pages mode
574
907
  * in Firefox. The pinch mapping would triple per click; a notch instead
575
908
  * steps by 1.2, the same ratio as the zoom buttons.
576
- * • cmd + continuous SCROLL — real scroll px, big and streaming (with
909
+ * • cmd + continuous scroll — real scroll px, big and streaming (with
577
910
  * momentum): a gentle scrub coefficient. A cmd+mouse notch (±120 px)
578
911
  * lands within 0.3% of the 1.2 button step, so it needs no special case.
579
912
  *
@@ -588,7 +921,7 @@ interface WheelSample {
588
921
  metaKey: boolean;
589
922
  }
590
923
  /** Zoom factor for a ctrl/cmd wheel event (the caller decides zoom vs pan). */
591
- declare function wheelZoomFactor(e: WheelSample): number;
924
+ declare function wheelZoomFactor(sample: WheelSample): number;
592
925
  //#endregion
593
926
  //#region src/byte-store.d.ts
594
927
  /**
@@ -614,11 +947,11 @@ interface IndexedDbByteStoreOptions {
614
947
  storeName?: string;
615
948
  }
616
949
  /** Open (creating on first use) one IndexedDB object store keyed by id. */
617
- declare function indexedDbByteStore(dbName: string, opts?: IndexedDbByteStoreOptions): ByteStore;
950
+ declare function indexedDbByteStore(dbName: string, options?: IndexedDbByteStoreOptions): ByteStore;
618
951
  //#endregion
619
952
  //#region src/rich-text-editor.d.ts
620
953
  /**
621
- * Rich text editor binding — the ONE contentEditable mechanics module for a
954
+ * Rich text editor binding — the one contentEditable mechanics module for a
622
955
  * FreeText annotation, shared by every framework adapter (React, Angular,
623
956
  * Vue, Svelte…) so editing can never drift between them:
624
957
  *
@@ -631,7 +964,7 @@ declare function indexedDbByteStore(dbName: string, opts?: IndexedDbByteStoreOpt
631
964
  * • hold re-renders during IME composition, paste as plain text, forward
632
965
  * Cmd/Ctrl+B/I/U as commands
633
966
  *
634
- * The browser owns typing; this module only translates. The POLICY — what a
967
+ * The browser owns typing; this module only translates. The policy — what a
635
968
  * command does to the document, when to commit, what a family maps to —
636
969
  * belongs to the host (the annotation plugin), reached through the
637
970
  * structural {@link RichTextEditorHost}: @embedpdf/web imports no plugin,
@@ -642,9 +975,9 @@ declare function indexedDbByteStore(dbName: string, opts?: IndexedDbByteStoreOpt
642
975
  * runs concatenated, a hard break inside a run is a `\r` too. The same
643
976
  * arithmetic the core's run algebra uses.
644
977
  *
645
- * Known limits (by design, recorded in the plan): the browser's native undo
646
- * stack does not span a host-driven restyle; DOM line wrapping approximates
647
- * the engine's layout, the appearance stream is the truth.
978
+ * Known limits, by design: the browser's native undo stack does not span a
979
+ * host-driven restyle; DOM line wrapping approximates the engine's layout,
980
+ * the appearance stream is the truth.
648
981
  */
649
982
  type RichTextEditorDecoration = 'underline' | 'line-through' | 'word';
650
983
  type RichTextEditorScript = 'normal' | 'sub' | 'super';
@@ -689,12 +1022,12 @@ interface RichTextEditorHost {
689
1022
  /** A keyboard command the host applies to the current selection. */
690
1023
  onCommand(command: RichTextEditorCommand): void;
691
1024
  /** The CSS family list for a PDF family ("Helvetica" → a web stack, a
692
- * registered family → itself, mounted as a @font-face). */
1025
+ * registered family → the family its face is mounted under, see `mountWebFont`). */
693
1026
  cssFontFamily(family: string): string;
694
1027
  }
695
1028
  interface RichTextEditorProps {
696
1029
  document: RichTextEditorDocument;
697
- /** Screen px per content unit (the page scale): sizes are content points. */
1030
+ /** Screen px per page unit (the page scale): sizes are page points. */
698
1031
  scale: number;
699
1032
  }
700
1033
  interface RichTextEditorBinding {
@@ -768,26 +1101,166 @@ declare function offsetOfPosition(root: EditorNode, position: EditorPosition): n
768
1101
  * else the empty block / the slot after a hard break the offset names. */
769
1102
  declare function positionOfOffset(root: EditorNode, offset: number): EditorPosition | null;
770
1103
  /** Bind one contentEditable element. Returns the Svelte-action-shaped handle. */
771
- declare function attachRichTextEditor(el: HTMLElement, host: RichTextEditorHost, initial: RichTextEditorProps): RichTextEditorBinding;
1104
+ declare function attachRichTextEditor(element: HTMLElement, host: RichTextEditorHost, initial: RichTextEditorProps): RichTextEditorBinding;
772
1105
  //#endregion
773
1106
  //#region src/web-font.d.ts
774
1107
  /**
775
1108
  * Mount a registered font for the DOM: the engine lays text out with the
776
1109
  * font's bytes, the browser needs the same face as a `@font-face` so the
777
1110
  * live editor and the vector renderer show the glyphs the appearance stream
778
- * will bake. The family name is the font's KEY — the CSS family the
779
- * annotation plugin emits for a registered font (`cssFontFamily`) — so
780
- * mounting under the key is what makes the two agree.
1111
+ * will bake. The face's family is {@link webFontFamily} of the key.
781
1112
  *
782
1113
  * Returns the unmount. Idempotent per (document, key): a second mount of a
783
1114
  * key already in the document's font set is a no-op that still returns an
784
- * unmount of its own (the face leaves when the LAST holder unmounts).
1115
+ * unmount of its own (the face leaves when the last holder unmounts).
785
1116
  */
786
1117
  declare function mountWebFont(key: string, data: ArrayBuffer | Uint8Array, options?: {
787
1118
  document?: Document;
788
1119
  weight?: number | string;
789
1120
  style?: 'normal' | 'italic';
790
1121
  }): Promise<() => void>;
1122
+ /**
1123
+ * A font program to mount: its CSS family, its style, and its bytes. The
1124
+ * structural twin of `@embedpdf/core-fonts`' `FontProgram`, whose family is
1125
+ * named by the program's hash, so it never shadows another font.
1126
+ */
1127
+ interface MountableFontProgram {
1128
+ readonly family: string;
1129
+ readonly weight: number;
1130
+ readonly style: 'normal' | 'italic';
1131
+ load(): Promise<Uint8Array>;
1132
+ }
1133
+ /**
1134
+ * Mount a program under its own family, as it is (already in the viewer's
1135
+ * namespace), in its own weight and style, so CSS asking for that style takes
1136
+ * it as it is and never fakes a bold or an italic. Refcounted per document
1137
+ * like {@link mountWebFont}; resolves with the unmount.
1138
+ */
1139
+ declare function mountFontProgram(program: MountableFontProgram, options?: {
1140
+ document?: Document;
1141
+ }): Promise<() => void>;
1142
+ /**
1143
+ * The programs one layer has mounted: each mounted once, however often it's
1144
+ * asked for, and all let go together when the layer goes (a program another
1145
+ * layer holds stays, refcounted per document). A mount after the release
1146
+ * holds again, for a layer that comes back (React's strict mode runs effects
1147
+ * twice).
1148
+ */
1149
+ declare function createFontMounts(options?: {
1150
+ document?: Document;
1151
+ }): {
1152
+ mount(programs: readonly MountableFontProgram[]): void;
1153
+ release(): void;
1154
+ };
1155
+ //#endregion
1156
+ //#region src/object-urls.d.ts
1157
+ /** Bytes and their type, as the stamp and annotation plugins hand out previews. */
1158
+ interface PictureBytes {
1159
+ readonly bytes: Uint8Array;
1160
+ readonly mimeType?: string | null;
1161
+ }
1162
+ /** An object URL, and how to release it. */
1163
+ interface ObjectUrl {
1164
+ readonly url: string;
1165
+ revoke(): void;
1166
+ }
1167
+ /** An object URL for bytes. Revoke it when nothing shows it any more. */
1168
+ declare function objectUrlOf(picture: PictureBytes): ObjectUrl;
1169
+ /**
1170
+ * Load a picture and hand its object URL to `onUrl` once it's there (nothing
1171
+ * for `null`). What it returns cancels: a load still running is dropped, and
1172
+ * the URL is revoked. Keep showing the previous URL until `onUrl` brings the
1173
+ * next one, so a swap never flickers.
1174
+ */
1175
+ declare function loadObjectUrl(load: () => Promise<PictureBytes | null>, onUrl: (url: string) => void): () => void;
1176
+ /**
1177
+ * One picture as its owner hands it out (the annotation plugin's or the form
1178
+ * plugin's `renderAppearances`): an item in one of its states, at its version.
1179
+ */
1180
+ interface AppearancePicture<Ref> {
1181
+ readonly ref: Ref;
1182
+ /** The state it draws (`/AS`), or `null` for an item without states. */
1183
+ readonly state: string | null;
1184
+ /** The box it was rendered into, in page points. */
1185
+ readonly rect: {
1186
+ x: number;
1187
+ y: number;
1188
+ width: number;
1189
+ height: number;
1190
+ };
1191
+ /** The version of its item it shows: an item at another version looks different now. */
1192
+ readonly version: number;
1193
+ readonly image: ObjectUrlImageSource;
1194
+ }
1195
+ /** A picture's URL, the box it was rendered into, and the version of its item it shows. */
1196
+ interface AppearanceUrl {
1197
+ url: string;
1198
+ box: {
1199
+ x: number;
1200
+ y: number;
1201
+ width: number;
1202
+ height: number;
1203
+ };
1204
+ version: number;
1205
+ }
1206
+ /**
1207
+ * The picture an item shows, from {@link loadAppearanceUrls}: the one of the
1208
+ * state it's in (`'Off'` when it names none), or its only picture when it has
1209
+ * no states; `null` until it's loaded.
1210
+ */
1211
+ declare function shownAppearance(urls: Readonly<Record<string, AppearanceUrl>>, itemKey: string, appearanceState: string | null): AppearanceUrl | null;
1212
+ /**
1213
+ * The engine's picture of an annotation as a renderer gets it (its
1214
+ * `appearance`): `{ url }` once {@link loadAppearanceUrls} has loaded the one
1215
+ * of the state it shows, else `null`.
1216
+ */
1217
+ declare function bakedAppearanceOf(urls: Readonly<Record<string, AppearanceUrl>>, itemKey: string, appearanceState: string | null): {
1218
+ url: string;
1219
+ } | null;
1220
+ /**
1221
+ * The URLs a layer shows, kept from one load to the next: a set stays valid
1222
+ * until the set after it is shown, so a picture that shows again before the
1223
+ * next set arrives (an annotation an undo brings back) still has a live URL.
1224
+ * One per layer, passed to every load; `release()` when the layer goes.
1225
+ */
1226
+ interface ShownUrls {
1227
+ /** Revoke the URLs shown now: the layer is going. A later load starts afresh. */
1228
+ release(): void;
1229
+ }
1230
+ /** What a layer shows, for {@link loadAppearanceUrls}. */
1231
+ declare function createShownUrls(): ShownUrls;
1232
+ /**
1233
+ * Load a page's pictures, every state of every item, and hand their URLs to
1234
+ * `onLoaded` once all of them are there; the set `shown` held before is
1235
+ * revoked then. {@link shownAppearance} picks an item's: a check box that
1236
+ * changes shows its other picture at once. What it returns cancels: the load
1237
+ * is aborted and the URLs it made are revoked, unless it already handed them
1238
+ * over. Start it again when the page's appearance epoch or the appearance
1239
+ * scale changes, never mid-gesture.
1240
+ */
1241
+ declare function loadAppearanceUrls<Ref>(shown: ShownUrls, load: (signal: AbortSignal) => Promise<readonly AppearancePicture<Ref>[]>, keyOf: (ref: Ref) => string, onLoaded: (urls: Record<string, AppearanceUrl>) => void): () => void;
1242
+ //#endregion
1243
+ //#region src/picture-or-painting.d.ts
1244
+ type PictureOrPainting<Painting> = {
1245
+ readonly kind: 'picture';
1246
+ readonly picture: AppearanceUrl;
1247
+ } | {
1248
+ readonly kind: 'painting';
1249
+ readonly painting: Painting;
1250
+ } | null;
1251
+ interface LookState<Painting> {
1252
+ /** The owner draws the item live now: a pending change, a widget's look not confirmed yet. */
1253
+ readonly pending: boolean;
1254
+ /** Its picture, for the state it shows; `null` when there is none. */
1255
+ readonly picture: AppearanceUrl | null;
1256
+ /** The item's picture version now. */
1257
+ readonly version: number;
1258
+ /** The page's pictures have loaded at least once. */
1259
+ readonly loaded: boolean;
1260
+ /** Its live drawing; `null` for an item that has none. */
1261
+ readonly paint: () => Painting | null;
1262
+ }
1263
+ declare function pictureOrPainting<Painting>(look: LookState<Painting>): PictureOrPainting<Painting>;
791
1264
  //#endregion
792
1265
  //#region src/web-font-metrics.d.ts
793
1266
  /**
@@ -848,5 +1321,2343 @@ declare function lineModelFor(cssFamily: string, measure?: (cssFamily: string) =
848
1321
  */
849
1322
  declare function firstLineShiftFor(cssFamily: string, fontSize: number, measure?: (cssFamily: string) => WebFontMetrics | null): number;
850
1323
  //#endregion
851
- export { type ActionsUiAdapterShape, type ActionsUiEffectContext, type ActionsUiOrigin, type AnchorTarget, type AnchoredPlacement, type AnchoredPoint, type AnchoredPosition, type AnchoredRect, type AttachSelectionHandleOptions, type ByteStore, type ClipboardSelectionSource, type DefaultActionsUiAdapterOptions, type EditorDocumentFactory, type EditorElement, type EditorNode, type EditorPosition, type EditorRoot, type EditorStyle, type IndexedDbByteStoreOptions, type LineModel, type ObjectUrlImageSource, type PaintedImageCallbacks, type PickFileOptions, type RichTextEditorAlign, type RichTextEditorBinding, type RichTextEditorCommand, type RichTextEditorDecoration, type RichTextEditorDocument, type RichTextEditorHost, type RichTextEditorParagraph, type RichTextEditorProps, type RichTextEditorRange, type RichTextEditorRun, type RichTextEditorScript, type RichTextEditorStyle, type SelectionClipboardOptions, type SelectionHandleSession, type StageGestureHost, type StageGestureOptions, type StageGestureSink, type StagePointerKind, type StageSurfaceHost, type StageSurfaceHub, type StageSurfaceOptions, type StageSurfaceSample, type StageWheelSample, type SvgCursorOptions, type ViewProjector, type WebFontMetrics, type WebPlatformFeedback, type WheelSample, applyFirstLineShift, attachRichTextEditor, attachSelectionHandle, bindPaintedImage, computeReleaseVelocity, copySelection, createDefaultActionsUiAdapter, createStageGestureController, createStageSurface, firstLineShiftFor, indexedDbByteStore, lineModelFor, mountWebFont, observeClientGeometry, offsetOfPosition, pickFile, pickImageFile, positionAnchoredRect, positionOfOffset, projectAnchoredTarget, renderRichText, sanitizeExternalUri, saveFile, serializeRichText, styleDeltaOf, svgCursor, vibrationFeedback, webFontMetrics, wheelZoomFactor, wireSelectionClipboard, wkFeedback };
1324
+ //#region src/theme.d.ts
1325
+ /**
1326
+ * The `--epdf-*` CSS variables that theme EmbedPDF, and the CSS value each
1327
+ * painted part uses. Every paint setting (a color, a line width, a dash, a
1328
+ * shadow, an opacity) can also come from a variable, and any variable wins over
1329
+ * any setting: a part looks for its own variable, then the variables of the
1330
+ * parts its default follows (another part, its plugin's accent, the viewer's
1331
+ * accent), and only then uses its setting's value. Parts you style yourself
1332
+ * have no setting, only a variable and a built-in look.
1333
+ *
1334
+ * {@link EPDF_VARIABLES} is plain data with no imports, so a script can read the
1335
+ * list without a browser. {@link epdfThemeVariables} turns a theme written with
1336
+ * setting names into the variables.
1337
+ */
1338
+ /**
1339
+ * A part other parts' defaults follow: an accent, or a part whose setting is
1340
+ * another part's default, such as the handles the rotation handle looks like.
1341
+ */
1342
+ type EpdfFollowedVariable = 'accent' | 'annotation-accent' | 'annotation-handle-fill' | 'annotation-handle-stroke';
1343
+ /** One themable part, named by its variable without the `--epdf-` prefix. */
1344
+ interface EpdfVariableDefinition {
1345
+ /**
1346
+ * The setting the variable overrides, as `<plugin>.<path>`: `viewer.` for the
1347
+ * viewer's own settings, `[]` for every entry of a list. Absent for a part you
1348
+ * style yourself, which only has CSS.
1349
+ */
1350
+ readonly setting?: string;
1351
+ /**
1352
+ * The part whose setting is this part's default, looked up right after the
1353
+ * part's own variable, and followed in turn: the rotation handle's stroke
1354
+ * follows the handles' stroke, which follows the annotation accent, which
1355
+ * follows the viewer's. So `--epdf-annotation-handle-stroke` reaches the
1356
+ * rotation handle before any accent does.
1357
+ */
1358
+ readonly follows?: EpdfFollowedVariable;
1359
+ /** A translucent default, the accent it follows at this percent: see {@link mixAccent}. */
1360
+ readonly accentPercent?: number;
1361
+ /**
1362
+ * The CSS value of each keyword, for a setting that takes keywords instead of
1363
+ * a CSS value.
1364
+ */
1365
+ readonly keywords?: Readonly<Record<string, string>>;
1366
+ /** What the part paints when neither its setting nor a variable is set. */
1367
+ readonly default: string;
1368
+ }
1369
+ /** Every theming variable, in the order the theming page lists them. */
1370
+ declare const EPDF_VARIABLES: {
1371
+ readonly accent: {
1372
+ readonly setting: "viewer.accent";
1373
+ readonly default: "#3858e9";
1374
+ };
1375
+ readonly 'page-background': {
1376
+ readonly setting: "viewer.page.background";
1377
+ readonly default: "#ffffff";
1378
+ };
1379
+ readonly 'page-shadow': {
1380
+ readonly setting: "viewer.page.shadow";
1381
+ readonly default: "0 6px 18px rgb(0 0 0 / 0.18)";
1382
+ };
1383
+ readonly 'annotation-accent': {
1384
+ readonly setting: "annotation.chrome.accent";
1385
+ readonly follows: "accent";
1386
+ readonly default: "#3858e9";
1387
+ };
1388
+ readonly 'annotation-outline': {
1389
+ readonly setting: "annotation.chrome.outline.color";
1390
+ readonly follows: "annotation-accent";
1391
+ readonly default: "#3858e9";
1392
+ };
1393
+ readonly 'annotation-outline-width': {
1394
+ readonly setting: "annotation.chrome.outline.width";
1395
+ readonly default: "1";
1396
+ };
1397
+ readonly 'annotation-outline-dash': {
1398
+ readonly setting: "annotation.chrome.outline.style";
1399
+ readonly keywords: {
1400
+ readonly solid: "none";
1401
+ readonly dashed: "4 3";
1402
+ };
1403
+ readonly default: "none";
1404
+ };
1405
+ readonly 'annotation-handle-fill': {
1406
+ readonly setting: "annotation.chrome.handles.fill";
1407
+ readonly default: "#ffffff";
1408
+ };
1409
+ readonly 'annotation-handle-stroke': {
1410
+ readonly setting: "annotation.chrome.handles.stroke";
1411
+ readonly follows: "annotation-accent";
1412
+ readonly default: "#3858e9";
1413
+ };
1414
+ readonly 'annotation-rotation-handle-fill': {
1415
+ readonly setting: "annotation.chrome.rotationHandle.fill";
1416
+ readonly follows: "annotation-handle-fill";
1417
+ readonly default: "#ffffff";
1418
+ };
1419
+ readonly 'annotation-rotation-handle-stroke': {
1420
+ readonly setting: "annotation.chrome.rotationHandle.stroke";
1421
+ readonly follows: "annotation-handle-stroke";
1422
+ readonly default: "#3858e9";
1423
+ };
1424
+ readonly 'annotation-guide': {
1425
+ readonly setting: "annotation.chrome.guides.color";
1426
+ readonly default: "#e91e63";
1427
+ };
1428
+ readonly 'annotation-rotation-guide': {
1429
+ readonly setting: "annotation.chrome.guides.rotationColor";
1430
+ readonly follows: "annotation-accent";
1431
+ readonly default: "#3858e9";
1432
+ };
1433
+ readonly 'annotation-guide-width': {
1434
+ readonly setting: "annotation.chrome.guides.width";
1435
+ readonly default: "1.5";
1436
+ };
1437
+ readonly 'annotation-guide-dash': {
1438
+ readonly setting: "annotation.chrome.guides.style";
1439
+ readonly keywords: {
1440
+ readonly solid: "none";
1441
+ readonly dashed: "4 3";
1442
+ };
1443
+ readonly default: "none";
1444
+ };
1445
+ readonly 'annotation-marquee-fill': {
1446
+ readonly setting: "annotation.chrome.marquee.fill";
1447
+ readonly follows: "annotation-accent";
1448
+ readonly accentPercent: 8;
1449
+ readonly default: "color-mix(in srgb, #3858e9 8%, transparent)";
1450
+ };
1451
+ readonly 'annotation-marquee-stroke': {
1452
+ readonly setting: "annotation.chrome.marquee.stroke";
1453
+ readonly follows: "annotation-accent";
1454
+ readonly default: "#3858e9";
1455
+ };
1456
+ readonly 'annotation-text-outline': {
1457
+ readonly setting: "annotation.chrome.textOutline";
1458
+ readonly follows: "annotation-accent";
1459
+ readonly default: "#3858e9";
1460
+ };
1461
+ readonly 'readout-background': {
1462
+ readonly setting: "annotation.chrome.readout.background";
1463
+ readonly default: "rgb(0 0 0 / 0.8)";
1464
+ };
1465
+ readonly 'readout-color': {
1466
+ readonly setting: "annotation.chrome.readout.color";
1467
+ readonly default: "#ffffff";
1468
+ };
1469
+ readonly 'ghost-opacity': {
1470
+ readonly setting: "annotation.tools[].ghost.opacity";
1471
+ readonly default: "0.5";
1472
+ };
1473
+ readonly 'text-selection': {
1474
+ readonly setting: "selection.color";
1475
+ readonly follows: "accent";
1476
+ readonly accentPercent: 35;
1477
+ readonly default: "color-mix(in srgb, #3858e9 35%, transparent)";
1478
+ };
1479
+ readonly 'text-selection-handle': {
1480
+ readonly setting: "selection.handles.color";
1481
+ readonly follows: "accent";
1482
+ readonly default: "#3858e9";
1483
+ };
1484
+ readonly 'text-selection-handle-shadow': {
1485
+ readonly setting: "selection.handles.shadow";
1486
+ readonly default: "0 1px 4px rgb(0 0 0 / 0.35)";
1487
+ };
1488
+ readonly 'search-highlight': {
1489
+ readonly setting: "search.highlight.color";
1490
+ readonly default: "#ffd500";
1491
+ };
1492
+ readonly 'search-highlight-active': {
1493
+ readonly setting: "search.highlight.activeColor";
1494
+ readonly default: "#ff9632";
1495
+ };
1496
+ readonly 'search-blend-mode': {
1497
+ readonly setting: "search.highlight.blendMode";
1498
+ readonly default: "multiply";
1499
+ };
1500
+ readonly 'form-focus': {
1501
+ readonly setting: "form.focus.color";
1502
+ readonly follows: "accent";
1503
+ readonly default: "#3858e9";
1504
+ };
1505
+ readonly 'form-field-border': {
1506
+ readonly setting: "form.fields.border";
1507
+ readonly follows: "accent";
1508
+ readonly accentPercent: 55;
1509
+ readonly default: "color-mix(in srgb, #3858e9 55%, transparent)";
1510
+ };
1511
+ readonly 'form-field-background': {
1512
+ readonly setting: "form.fields.background";
1513
+ readonly default: "rgb(255 255 255 / 0.92)";
1514
+ };
1515
+ readonly 'form-field-color': {
1516
+ readonly setting: "form.fields.color";
1517
+ readonly default: "#1f2a44";
1518
+ };
1519
+ readonly 'scrollbar-thumb': {
1520
+ readonly default: "rgb(0 0 0 / 0.4)";
1521
+ };
1522
+ readonly 'toolbar-surface': {
1523
+ readonly default: "#ffffff";
1524
+ };
1525
+ readonly 'toolbar-border': {
1526
+ readonly default: "rgb(0 0 0 / 0.15)";
1527
+ };
1528
+ readonly 'toolbar-active': {
1529
+ readonly default: "rgb(0 0 0 / 0.12)";
1530
+ };
1531
+ readonly 'font-mono': {
1532
+ readonly default: "ui-monospace, monospace";
1533
+ };
1534
+ };
1535
+ type Definitions = typeof EPDF_VARIABLES;
1536
+ /** A theming variable's name, without its `--epdf-` prefix. */
1537
+ type EpdfVariable = keyof Definitions;
1538
+ /** A variable that overrides a setting: its part paints with {@link paint}. */
1539
+ type EpdfSettingVariable = { [Name in EpdfVariable]: Definitions[Name] extends {
1540
+ readonly setting: string;
1541
+ } ? Name : never; }[EpdfVariable];
1542
+ /** A part you style yourself, which has no setting: it paints with {@link paintDefault}. */
1543
+ type EpdfCssOnlyVariable = Exclude<EpdfVariable, EpdfSettingVariable>;
1544
+ /** A part whose default is the accent at a percent: that default comes from {@link mixAccent}. */
1545
+ type EpdfTranslucentVariable = { [Name in EpdfVariable]: Definitions[Name] extends {
1546
+ readonly accentPercent: number;
1547
+ } ? Name : never; }[EpdfVariable];
1548
+ /**
1549
+ * The CSS value for a painted part: its own variable, then the variables of the
1550
+ * parts its default follows (the part it follows, its plugin's accent,
1551
+ * `--epdf-accent`), then the setting's value. Put it in a style (`style.stroke`),
1552
+ * not an SVG attribute, because attributes don't read `var()`.
1553
+ *
1554
+ * `paint('annotation-outline', '#3858e9')` is
1555
+ * `var(--epdf-annotation-outline, var(--epdf-annotation-accent, var(--epdf-accent, #3858e9)))`,
1556
+ * `paint('annotation-rotation-handle-fill', '#ffffff')` is
1557
+ * `var(--epdf-annotation-rotation-handle-fill, var(--epdf-annotation-handle-fill, #ffffff))`,
1558
+ * and `paint('search-highlight', '#ffd500')` is `var(--epdf-search-highlight, #ffd500)`.
1559
+ */
1560
+ declare function paint(name: EpdfSettingVariable, settingValue: string | number): string;
1561
+ /**
1562
+ * The default of a translucent part, such as "the accent at 35%": the accent it
1563
+ * follows, variables included, mixed with transparent. Use it as the setting's
1564
+ * value while the part's own setting is unset.
1565
+ *
1566
+ * The accent variables sit inside the mix, not in {@link paint}'s lookup: there
1567
+ * a set `--epdf-accent` would paint at full strength, and CSS can't mix a
1568
+ * variable only when it's set. So a part's own setting, when you give one, wins
1569
+ * over the accent variables, though not over the part's own variable.
1570
+ *
1571
+ * `mixAccent('text-selection', '#3858e9')` is
1572
+ * `color-mix(in srgb, var(--epdf-accent, #3858e9) 35%, transparent)`.
1573
+ */
1574
+ declare function mixAccent(name: EpdfTranslucentVariable, accentValue: string): string;
1575
+ /**
1576
+ * The CSS value for a part you style yourself, which has no setting: its
1577
+ * variable, then its built-in look.
1578
+ *
1579
+ * `paintDefault('scrollbar-thumb')` is `var(--epdf-scrollbar-thumb, rgb(0 0 0 / 0.4))`.
1580
+ */
1581
+ declare function paintDefault(name: EpdfCssOnlyVariable): string;
1582
+ /** A setting's path as a theme writes it: the viewer's own at the top, a list's entries as one. */
1583
+ type ThemePath<Setting extends string> = Setting extends `viewer.${infer Rest}` ? Rest : Setting extends `${infer Head}[]${infer Tail}` ? `${Head}${Tail}` : Setting;
1584
+ /** What a part takes: one of its keywords, or a CSS value. */
1585
+ type ThemeValue<Name extends EpdfSettingVariable> = Definitions[Name] extends {
1586
+ readonly keywords: infer Keywords;
1587
+ } ? keyof Keywords & string : string | number;
1588
+ /** One setting as a nested object: `'page.shadow'` is `{ page?: { shadow?: … } }`. */
1589
+ type ThemeBranch<Path extends string, Value> = Path extends `${infer Head}.${infer Rest}` ? { readonly [Key in Head]?: ThemeBranch<Rest, Value>; } : { readonly [Key in Path]?: Value; };
1590
+ type Intersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends ((value: infer Both) => void) ? Both : never;
1591
+ /**
1592
+ * A theme by setting names, every part optional: `{ accent: '#e91e63', search: { highlight:
1593
+ * { color: '#7dd3fc' } } }`. The viewer's own settings sit at the top; each plugin's under its
1594
+ * name, as the theming page lists them.
1595
+ */
1596
+ type EpdfTheme = Intersection<{ [Name in EpdfSettingVariable]: ThemeBranch<ThemePath<Definitions[Name]['setting']>, ThemeValue<Name>>; }[EpdfSettingVariable]>;
1597
+ /** The CSS variables of a theme, by their full name: `{ '--epdf-accent': '#e91e63' }`. */
1598
+ type EpdfThemeVariables = Readonly<Record<`--epdf-${EpdfVariable}`, string>>;
1599
+ /**
1600
+ * The CSS variables that set a theme, for a style on an element around the viewer: CSS variables
1601
+ * win over settings, and reach everything inside the element. A part left out keeps its own
1602
+ * look, so the parts that follow the accent follow a new `accent`.
1603
+ *
1604
+ * `epdfThemeVariables({ accent: '#e91e63', page: { shadow: 'none' } })` is
1605
+ * `{ '--epdf-accent': '#e91e63', '--epdf-page-shadow': 'none' }`.
1606
+ */
1607
+ declare function epdfThemeVariables(theme: EpdfTheme): Partial<EpdfThemeVariables>;
1608
+ //#endregion
1609
+ //#region src/page-pixels.d.ts
1610
+ /**
1611
+ * Page-space shapes in a page layer's pixels. Every layer over a page (the
1612
+ * annotation, form, link, selection and search layers) gets boxes and quads
1613
+ * in page points and places its elements in the page layer's own pixels,
1614
+ * always through the page's `toPixels`: never by multiplying with a scale of
1615
+ * its own. Plain geometry over structural shapes, the same for every
1616
+ * framework adapter.
1617
+ */
1618
+ /** A point in page points or in pixels. */
1619
+ interface PagePoint {
1620
+ x: number;
1621
+ y: number;
1622
+ }
1623
+ /** What a page layer converts with: its transform's `toPixels`. */
1624
+ interface PageToPixels {
1625
+ toPixels(point: PagePoint): PagePoint;
1626
+ }
1627
+ /** A box in a page layer's pixels, the shape a positioned element takes. */
1628
+ interface PixelRect {
1629
+ left: number;
1630
+ top: number;
1631
+ width: number;
1632
+ height: number;
1633
+ }
1634
+ /** A quad's four corners, as the engine names them. */
1635
+ interface PageQuad {
1636
+ upperLeft: PagePoint;
1637
+ upperRight: PagePoint;
1638
+ lowerRight: PagePoint;
1639
+ lowerLeft: PagePoint;
1640
+ }
1641
+ /** A box in page points as a box in the page layer's pixels. */
1642
+ declare function rectInPixels(rect: {
1643
+ x: number;
1644
+ y: number;
1645
+ width: number;
1646
+ height: number;
1647
+ }, page: PageToPixels): PixelRect;
1648
+ /**
1649
+ * A quad's corners in the page layer's pixels, in drawing order (upper left,
1650
+ * upper right, lower right, lower left). The map is affine, so mapping the
1651
+ * corners is exact, for turned text too.
1652
+ */
1653
+ declare function quadInPixels(quad: PageQuad, page: PageToPixels): PagePoint[];
1654
+ /** Points as an SVG `points` attribute: `"x,y x,y …"`. */
1655
+ declare function svgPoints(points: readonly PagePoint[]): string;
1656
+ /** Whether a quad is an upright box: drawn as a positioned box instead of a polygon. */
1657
+ declare function isUprightQuad(quad: PageQuad): boolean;
1658
+ //#endregion
1659
+ //#region src/event-isolation.d.ts
1660
+ /**
1661
+ * Keeping a page's own listeners out of controls drawn over it. The Stage
1662
+ * listens natively on an ancestor of every page: a press that bubbles to it
1663
+ * starts a tool gesture or reads as a click outside (ending an edit), and a
1664
+ * wheel pans or zooms the page. A control that owns its gestures stops them
1665
+ * with a native listener on itself, which runs during real DOM bubbling,
1666
+ * before the Stage's. A framework's own event system may run too late: React
1667
+ * dispatches from its root, above the Stage.
1668
+ */
1669
+ /**
1670
+ * Stop presses that begin inside `element` from reaching the page, and tell `onPress` about each
1671
+ * one. A control that needs the press itself (a link or a form field sending its PDF "mouse down"
1672
+ * action) must take it here: once the press stops at the element, a framework that dispatches
1673
+ * from its root (React) never sees it. Returns the detach.
1674
+ */
1675
+ declare function isolatePointerDown(element: HTMLElement, onPress?: (event: PointerEvent) => void): () => void;
1676
+ /**
1677
+ * Stop wheel events inside `element` from reaching the page, so the browser
1678
+ * scrolls the element itself (a list box) instead of the Stage panning or
1679
+ * zooming. Returns the detach.
1680
+ */
1681
+ declare function isolateWheel(element: HTMLElement): () => void;
1682
+ //#endregion
1683
+ //#region src/outside-press.d.ts
1684
+ /**
1685
+ * Call `onPress` when a pointer press starts anywhere in `element`'s document
1686
+ * outside `element`: how a menu closes when the user presses somewhere else,
1687
+ * in the viewer or on the page around it. Where the press landed is read from
1688
+ * its composed path: at the document, an event from inside a shadow root is
1689
+ * retargeted to the shadow host, so `element.contains(event.target)` would
1690
+ * call every press inside a shadow-rooted menu an outside one. Listens in the
1691
+ * capture phase, so a handler that stops the press can't keep a menu open.
1692
+ * Returns the stop.
1693
+ */
1694
+ declare function observeOutsidePress(element: Element, onPress: (event: PointerEvent) => void): () => void;
1695
+ //#endregion
1696
+ //#region src/anchor-equality.d.ts
1697
+ /**
1698
+ * Equality for the anchors plugins hand out. An anchor read (where a menu, a
1699
+ * badge or a selection handle attaches) returns a new object every time; a
1700
+ * framework adapter compares reads with these, so the UI attached to an
1701
+ * anchor renders again only when the anchor moved. The anchors are mirrored
1702
+ * structurally, so this package stays free of EmbedPDF imports.
1703
+ */
1704
+ interface AnchorPoint {
1705
+ readonly x: number;
1706
+ readonly y: number;
1707
+ }
1708
+ interface AnchorBox {
1709
+ readonly x: number;
1710
+ readonly y: number;
1711
+ readonly width: number;
1712
+ readonly height: number;
1713
+ }
1714
+ interface AnchorPage {
1715
+ readonly objectNumber: number;
1716
+ }
1717
+ /** A box on a page: a text selection's anchor, and the core of every other one. */
1718
+ interface PageBoundsAnchor {
1719
+ readonly page: AnchorPage;
1720
+ readonly bounds: AnchorBox;
1721
+ }
1722
+ /** The same page and box (a text selection's menu anchor). */
1723
+ declare function samePageBounds(left: PageBoundsAnchor | null, right: PageBoundsAnchor | null): boolean;
1724
+ /** The annotation selection's anchor: its page, its box, and its rotation handle when it has one. */
1725
+ interface SelectionAnchorShape extends PageBoundsAnchor {
1726
+ readonly rotationHandle?: AnchorPoint | null;
1727
+ }
1728
+ /** The same annotation selection anchor: page, box and rotation handle. */
1729
+ declare function sameSelectionAnchor(left: SelectionAnchorShape | null, right: SelectionAnchorShape | null): boolean;
1730
+ /** One annotation's anchor: page, box, and the view-dependent box of one that keeps its size on screen. */
1731
+ interface AnnotationAnchorShape extends PageBoundsAnchor {
1732
+ readonly boundsIn?: unknown;
1733
+ }
1734
+ /** The same annotation anchor: page, box, and how it follows the view. */
1735
+ declare function sameAnnotationAnchor(left: AnnotationAnchorShape | null, right: AnnotationAnchorShape | null): boolean;
1736
+ /** A shape being drawn click by click (a polygon): where it is and how far it got. */
1737
+ interface CreationDraftAnchorShape extends PageBoundsAnchor {
1738
+ readonly kind: string;
1739
+ readonly subtype: string;
1740
+ readonly pointCount: number;
1741
+ readonly minPoints: number;
1742
+ readonly canFinish: boolean;
1743
+ }
1744
+ /** The same creation draft: what it draws, its points so far, whether it can finish, and its box. */
1745
+ declare function sameCreationDraftAnchor(left: CreationDraftAnchorShape | null, right: CreationDraftAnchorShape | null): boolean;
1746
+ /** A turn in progress: the pointer's page point and the angle. */
1747
+ interface RotationAnchorShape {
1748
+ readonly page: AnchorPage;
1749
+ readonly at: AnchorPoint;
1750
+ readonly angle: number;
1751
+ }
1752
+ /** The same turn in progress: page, pointer and angle. */
1753
+ declare function sameRotationAnchor(left: RotationAnchorShape | null, right: RotationAnchorShape | null): boolean;
1754
+ interface AnchorQuad {
1755
+ readonly upperLeft: AnchorPoint;
1756
+ readonly upperRight: AnchorPoint;
1757
+ readonly lowerLeft: AnchorPoint;
1758
+ readonly lowerRight: AnchorPoint;
1759
+ }
1760
+ /** One end of a text selection: its page, the boundary glyph's quad, and the side of the glyph. */
1761
+ interface SelectionEndpointShape {
1762
+ readonly page: AnchorPage;
1763
+ readonly glyphQuad: AnchorQuad;
1764
+ readonly advance: unknown;
1765
+ }
1766
+ /** Both ends of a text selection, where its handles sit. */
1767
+ interface SelectionEndpointsShape {
1768
+ readonly start: SelectionEndpointShape;
1769
+ readonly end: SelectionEndpointShape;
1770
+ }
1771
+ /**
1772
+ * The same selection ends. The glyph quads compare corner by corner, so an
1773
+ * end that turns without moving its bounding box still counts as moved.
1774
+ */
1775
+ declare function sameSelectionEndpoints(left: SelectionEndpointsShape | null, right: SelectionEndpointsShape | null): boolean;
1776
+ //#endregion
1777
+ //#region src/annotation-scene.d.ts
1778
+ /** How a scene node paints: the structural twin of core-annotation's `Paint`. */
1779
+ interface ScenePaint {
1780
+ fill?: string;
1781
+ stroke?: string;
1782
+ /** Stroke width, in page points. */
1783
+ width?: number;
1784
+ opacity?: number;
1785
+ /** Stroke dash, in page points. */
1786
+ dash?: number[];
1787
+ blend?: string;
1788
+ lineCap?: 'round';
1789
+ join?: 'round';
1790
+ fillRule?: 'evenodd';
1791
+ }
1792
+ interface SceneBox {
1793
+ x: number;
1794
+ y: number;
1795
+ width: number;
1796
+ height: number;
1797
+ }
1798
+ interface ScenePoint {
1799
+ x: number;
1800
+ y: number;
1801
+ }
1802
+ /** One painted node: the structural twin of core-annotation's `SceneNode`. */
1803
+ type SceneShapeNode = ({
1804
+ kind: 'rect';
1805
+ rect: SceneBox;
1806
+ paint: ScenePaint;
1807
+ } | {
1808
+ kind: 'ellipse';
1809
+ rect: SceneBox;
1810
+ paint: ScenePaint;
1811
+ } | {
1812
+ kind: 'line';
1813
+ a: ScenePoint;
1814
+ b: ScenePoint;
1815
+ paint: ScenePaint;
1816
+ } | {
1817
+ kind: 'poly';
1818
+ points: ScenePoint[];
1819
+ closed: boolean;
1820
+ paint: ScenePaint;
1821
+ } | {
1822
+ kind: 'path';
1823
+ d: string;
1824
+ paint: ScenePaint;
1825
+ } | {
1826
+ kind: 'text';
1827
+ at: ScenePoint;
1828
+ text: string;
1829
+ fontSize: number;
1830
+ fontFamily?: string;
1831
+ rotation?: number;
1832
+ paint: ScenePaint;
1833
+ } | {
1834
+ kind: 'glyphs';
1835
+ at: ScenePoint;
1836
+ text: string;
1837
+ offsets: number[];
1838
+ fontSize: number;
1839
+ fontFamily: string;
1840
+ fontWeight?: number;
1841
+ fontStyle?: 'normal' | 'italic';
1842
+ rotation?: number;
1843
+ /** The face it draws in: whatever draws it mounts it. */
1844
+ program?: MountableFontProgram;
1845
+ paint: ScenePaint;
1846
+ }) & {
1847
+ /** Drawn only inside this upright box. */
1848
+ clip?: SceneBox;
1849
+ };
1850
+ /** The SVG elements a scene draws with; a nested `svg` clips what's inside it to its box. */
1851
+ type SvgShapeTag = 'rect' | 'ellipse' | 'line' | 'path' | 'text' | 'polygon' | 'polyline' | 'svg';
1852
+ /**
1853
+ * One SVG element: its tag, its attributes under their SVG names
1854
+ * (`stroke-width`, `fill-rule`; an attribute left out is not set), the text
1855
+ * inside it, and its blend mode, which is CSS (`mix-blend-mode` in its
1856
+ * `style`), never an attribute.
1857
+ */
1858
+ interface SvgShape {
1859
+ tag: SvgShapeTag;
1860
+ attributes: Readonly<Record<string, string | number>>;
1861
+ text?: string;
1862
+ blend?: string;
1863
+ /** What a clipping `svg` holds, in drawing order. */
1864
+ children?: SvgShape[];
1865
+ /** The face a `text` draws in: the renderer mounts it (`fontProgramsOf`). */
1866
+ program?: MountableFontProgram;
1867
+ }
1868
+ /** The faces shapes draw their text in, each once: a renderer mounts them while it shows the shapes. */
1869
+ declare function fontProgramsOf(shapes: readonly SvgShape[]): MountableFontProgram[];
1870
+ interface SvgShapesOptions {
1871
+ /**
1872
+ * The stroke miter limit, core-annotation's `MITER_LIMIT`: it must be the
1873
+ * one the core's bounds math uses, so a sharp corner spikes or bevels the
1874
+ * same way in both.
1875
+ */
1876
+ miterLimit: number;
1877
+ }
1878
+ /**
1879
+ * Each scene node as one SVG element, in drawing order, in the scene's own
1880
+ * page-point units. A clipped node comes inside an `svg` of its clip box,
1881
+ * which shows only what's inside it (no ids, so every framework draws it the
1882
+ * same).
1883
+ */
1884
+ declare function svgShapesOf(nodes: readonly SceneShapeNode[], options: SvgShapesOptions): SvgShape[];
1885
+ /**
1886
+ * The `viewBox` an item's scene draws in: its box, in page points. `null`
1887
+ * while the box has no area (a drawing at its first press): draw nothing then.
1888
+ *
1889
+ * The `<svg>` fills the item's frame, so the viewBox and the element's size
1890
+ * stay proportional (their ratio is the zoom). Never clamp either one: a
1891
+ * clamped element with a shrinking viewBox scales its content up, and a
1892
+ * cloudy border's scallops flood the page.
1893
+ */
1894
+ declare function sceneViewBox(box: SceneBox): string | null;
1895
+ /** How opaque a tool's ghost paints: `--epdf-ghost-opacity` wins over the tool's own. */
1896
+ declare function ghostOpacity(opacity: number): string;
1897
+ //#endregion
1898
+ //#region src/annotation-chrome.d.ts
1899
+ /** The chrome settings the paint reads: the structural twin of the annotation plugin's `ChromeSettings`. */
1900
+ interface ChromePaintSettings {
1901
+ readonly accent: string | null;
1902
+ readonly outline: {
1903
+ readonly color: string | null;
1904
+ readonly style: string;
1905
+ readonly width: number;
1906
+ };
1907
+ readonly handles: {
1908
+ readonly fill: string;
1909
+ readonly stroke: string | null;
1910
+ };
1911
+ readonly rotationHandle: {
1912
+ readonly fill: string | null;
1913
+ readonly stroke: string | null;
1914
+ };
1915
+ readonly guides: {
1916
+ readonly color: string;
1917
+ readonly rotationColor: string | null;
1918
+ readonly style: string;
1919
+ readonly width: number;
1920
+ };
1921
+ readonly marquee: {
1922
+ readonly fill: string | null;
1923
+ readonly stroke: string | null;
1924
+ };
1925
+ readonly textOutline: string | null;
1926
+ }
1927
+ /** A line's paint as CSS: its color, width and dash. */
1928
+ interface ChromeLinePaint {
1929
+ readonly stroke: string;
1930
+ readonly strokeWidth: string;
1931
+ readonly strokeDasharray: string;
1932
+ }
1933
+ /** A shape's paint as CSS: its fill and its stroke color. */
1934
+ interface ChromeShapePaint {
1935
+ readonly fill: string;
1936
+ readonly stroke: string;
1937
+ }
1938
+ /**
1939
+ * Every chrome color, width and dash as CSS, each its `--epdf-annotation-*`
1940
+ * variable first, then the setting. Put them in `style`: an SVG attribute
1941
+ * doesn't read `var()`.
1942
+ */
1943
+ interface AnnotationChromePaint {
1944
+ /** The selection box. */
1945
+ readonly outline: ChromeLinePaint;
1946
+ /** The resize and point handles. */
1947
+ readonly handle: ChromeShapePaint;
1948
+ /** The rotation handle; its stalk takes the `stroke`. */
1949
+ readonly rotationHandle: ChromeShapePaint;
1950
+ /** A snapped move's alignment guide. */
1951
+ readonly guide: ChromeLinePaint;
1952
+ /** The reference cross and the angle line while turning. */
1953
+ readonly rotationGuide: ChromeLinePaint;
1954
+ /** The box dragged to select. */
1955
+ readonly marquee: ChromeShapePaint;
1956
+ /** The outline color of a text box while someone types in it. */
1957
+ readonly textOutline: string;
1958
+ }
1959
+ /**
1960
+ * The chrome settings as paint. A color left `null` follows the part it
1961
+ * names, and in the end the accent: the chrome's own, else the viewer's.
1962
+ */
1963
+ declare function annotationChromePaint(chrome: ChromePaintSettings, viewerAccent: string): AnnotationChromePaint;
1964
+ /**
1965
+ * One chrome node as the annotation plugin lists it: the structural twin of
1966
+ * core-annotation's `ChromeNode`. `Role` is what a handle reshapes.
1967
+ */
1968
+ type ChromeNodeShape<Role extends string = string> = {
1969
+ kind: 'outline';
1970
+ rect: {
1971
+ x: number;
1972
+ y: number;
1973
+ width: number;
1974
+ height: number;
1975
+ };
1976
+ } | {
1977
+ kind: 'obb';
1978
+ corners: readonly PagePoint[];
1979
+ } | {
1980
+ kind: 'handle';
1981
+ at: PagePoint;
1982
+ rot?: number;
1983
+ role: Role;
1984
+ active: boolean;
1985
+ } | {
1986
+ kind: 'rotate-knob';
1987
+ at: PagePoint;
1988
+ from: PagePoint;
1989
+ } | {
1990
+ kind: 'guide';
1991
+ axis: 'x' | 'y';
1992
+ at: number;
1993
+ lo: number;
1994
+ hi: number;
1995
+ } | {
1996
+ kind: 'rotate-guides';
1997
+ lines: ReadonlyArray<{
1998
+ a: PagePoint;
1999
+ b: PagePoint;
2000
+ role: 'axis' | 'indicator';
2001
+ }>;
2002
+ } | {
2003
+ kind: 'marquee';
2004
+ rect: {
2005
+ x: number;
2006
+ y: number;
2007
+ width: number;
2008
+ height: number;
2009
+ };
2010
+ };
2011
+ /** A line in pixels. */
2012
+ interface ChromeLine {
2013
+ from: PagePoint;
2014
+ to: PagePoint;
2015
+ }
2016
+ /**
2017
+ * A chrome node in the page layer's pixels. What each one draws:
2018
+ * - `outline`, `marquee`: a rectangle, `box` (the marquee is always dashed `4 3`);
2019
+ * - `turned-outline`: a closed polygon through `points`, the outline of a turned selection;
2020
+ * - `handle`: a handle centred on `at`, turned by `rotation` (degrees clockwise) when square;
2021
+ * - `rotation-handle`: a round handle at `at`, its stalk from `from` on the box;
2022
+ * - `guide`: a crisp line, a snapped move's alignment;
2023
+ * - `rotation-guides`: the lines of a turn in progress, each at its `opacity`.
2024
+ */
2025
+ type ChromeInPixels<Role extends string = string> = {
2026
+ kind: 'outline';
2027
+ box: PixelRect;
2028
+ } | {
2029
+ kind: 'marquee';
2030
+ box: PixelRect;
2031
+ } | {
2032
+ kind: 'turned-outline';
2033
+ points: string;
2034
+ } | {
2035
+ kind: 'handle';
2036
+ at: PagePoint;
2037
+ rotation: number;
2038
+ role: Role;
2039
+ active: boolean;
2040
+ } | {
2041
+ kind: 'rotation-handle';
2042
+ at: PagePoint;
2043
+ from: PagePoint;
2044
+ } | ({
2045
+ kind: 'guide';
2046
+ } & ChromeLine) | {
2047
+ kind: 'rotation-guides';
2048
+ lines: Array<ChromeLine & {
2049
+ opacity: number;
2050
+ }>;
2051
+ };
2052
+ /**
2053
+ * Each chrome node in the page layer's pixels, in drawing order. The plugin
2054
+ * already sized the screen-constant parts (handle grab zones, the rotation
2055
+ * handle's offset) for the page's zoom; this only places them.
2056
+ */
2057
+ declare function chromeInPixels<Role extends string>(nodes: readonly ChromeNodeShape<Role>[], page: PageToPixels): ChromeInPixels<Role>[];
2058
+ //#endregion
2059
+ //#region src/text-box-editor.d.ts
2060
+ /** A text box as the annotation plugin lists it (`listTextItems`): the fields the editor reads. */
2061
+ interface TextBoxEditorItem<Ref> {
2062
+ readonly id: string;
2063
+ /** `null` for a box that isn't written yet: nothing to route typing to. */
2064
+ readonly ref: Ref | null;
2065
+ readonly richText: RichTextEditorDocument;
2066
+ /** True while someone types in this box. */
2067
+ readonly editing: boolean;
2068
+ }
2069
+ /** The annotation plugin's text-editing calls (its host capability satisfies it). */
2070
+ interface TextBoxEditorAnnotation<Ref> {
2071
+ draftRichText(ref: Ref, document: RichTextEditorDocument): void;
2072
+ setTextSelection(ref: Ref, range: RichTextEditorRange | null): void;
2073
+ readonly text: {
2074
+ toggleFormat(format: RichTextEditorCommand): unknown;
2075
+ };
2076
+ getCssFontFamily(family: string): string;
2077
+ getEditingId(): string | null;
2078
+ }
2079
+ /** An element made a text box's editor. */
2080
+ interface TextBoxEditor<Ref> {
2081
+ /**
2082
+ * The box as the plugin has it now, at `scale` pixels per point (or `null`
2083
+ * while there is none). Call it whenever either changes: the binding
2084
+ * redraws only when the text differs (so the caret never jumps while
2085
+ * typing), and focus follows `editing`.
2086
+ */
2087
+ update(item: TextBoxEditorItem<Ref> | null, scale: number): void;
2088
+ /** Remove the listeners and the rich-text binding. */
2089
+ detach(): void;
2090
+ }
2091
+ /**
2092
+ * Make `element` the editor of a text box. Typing shows at once and reaches
2093
+ * the engine in one write after a pause (or when the edit ends), not one
2094
+ * write per keystroke.
2095
+ */
2096
+ declare function attachTextBoxEditor<Ref>(element: HTMLElement, annotation: TextBoxEditorAnnotation<Ref>): TextBoxEditor<Ref>;
2097
+ /** Keeps an element the editor of a text box while what it edits with changes. */
2098
+ interface TextBoxEditorFollower<Ref> {
2099
+ /**
2100
+ * The element, the plugin, the box and the scale as they are now. Call it
2101
+ * whenever any of them changes: the editor attaches when there are an
2102
+ * element and a plugin, attaches again when either is another one (a
2103
+ * document that opens anew), and gets the box and the scale every time.
2104
+ */
2105
+ follow(element: HTMLElement | null, annotation: TextBoxEditorAnnotation<Ref> | null, item: TextBoxEditorItem<Ref> | null, scale: number): void;
2106
+ /** Let go of the element, when the component that drew it goes away. */
2107
+ detach(): void;
2108
+ }
2109
+ /**
2110
+ * A text box editor that follows its inputs: what each framework's binding
2111
+ * holds between renders. The update runs right after an attach, in the same
2112
+ * call, so the element never shows an editor without its text.
2113
+ */
2114
+ declare function createTextBoxEditorFollower<Ref>(): TextBoxEditorFollower<Ref>;
2115
+ /**
2116
+ * Pixels per point for an editor element you draw yourself (a renderer's own
2117
+ * text box): the box's width on the page over its width in points, or the
2118
+ * page's view scale while there is no box. Inside a look the layer scales
2119
+ * (`lookScale`, 1 outside one), the element draws at the annotation's 100%
2120
+ * size and the look's scale does the rest.
2121
+ */
2122
+ declare function textBoxEditorScaleOf(item: {
2123
+ box: {
2124
+ x: number;
2125
+ y: number;
2126
+ width: number;
2127
+ height: number;
2128
+ };
2129
+ } | null, page: PageToPixels & {
2130
+ viewScale: number;
2131
+ }, lookScale: number): number;
2132
+ /** A free text body's look, as the plugin hands it (`TextItem.css`). */
2133
+ interface TextBoxCss {
2134
+ fontFamily: string;
2135
+ /** In page points. */
2136
+ fontSize: number;
2137
+ color: string;
2138
+ fontWeight: number;
2139
+ fontStyle: string;
2140
+ textDecoration: string;
2141
+ align: 'left' | 'center' | 'right';
2142
+ /** The plate's inset from the box, in page points. */
2143
+ padding: number;
2144
+ }
2145
+ /** A text box's body style as CSS. */
2146
+ type TextBoxStyle = {
2147
+ fontFamily: string;
2148
+ /** In pixels, with its unit. */
2149
+ fontSize: string;
2150
+ color: string;
2151
+ fontWeight: number;
2152
+ fontStyle: string;
2153
+ textDecoration: string;
2154
+ textAlign: 'left' | 'center' | 'right';
2155
+ };
2156
+ /** The layer's own editor element for a free text box, as CSS. */
2157
+ type FreeTextBoxStyle = TextBoxStyle & {
2158
+ position: 'absolute';
2159
+ left: string;
2160
+ top: string;
2161
+ /** The plate's size times the editor's legible scale, in pixels with their unit. */
2162
+ width: string;
2163
+ height: string;
2164
+ boxSizing: 'border-box';
2165
+ /** The box's fill and border are the vector scene's, under this layer. */
2166
+ background: 'transparent';
2167
+ whiteSpace: 'pre-wrap';
2168
+ overflowWrap: 'break-word';
2169
+ overflowX: 'hidden';
2170
+ /** It scrolls while someone types, and clips otherwise, where the baked appearance clips. */
2171
+ overflowY: 'auto' | 'hidden';
2172
+ outline: string;
2173
+ cursor: 'text' | 'default';
2174
+ /** Draws the editor, laid out at a legible size, at the plate's size, turned like the box. */
2175
+ transform: string;
2176
+ transformOrigin: '0 0';
2177
+ /** Not typing: presses fall through to the shapes (select, move, resize). */
2178
+ pointerEvents: 'auto' | 'none';
2179
+ };
2180
+ /** The layer's own editor element for a free text box, and the scale its text is laid out at. */
2181
+ interface FreeTextBox {
2182
+ readonly style: FreeTextBoxStyle;
2183
+ /** Pixels per page point the editor lays its text out at: what its editor takes. */
2184
+ readonly scale: number;
2185
+ }
2186
+ /**
2187
+ * The layer's own editor element for a free text box: the engine's text
2188
+ * plate (`SetPlateRect` and its `re W n` clip), the box inset by its padding,
2189
+ * with no CSS padding, so its edge is the plate's: CSS padding doesn't clip
2190
+ * overflow, and scrolled lines would paint over the border band. The box
2191
+ * never grows with its text. It is laid out at a legible size and drawn back
2192
+ * at the plate's (`legible-text.ts`), and a turned box turns about its
2193
+ * centre, as the baked appearance does. While someone types it takes the
2194
+ * pointer and shows an `outline` of that color.
2195
+ */
2196
+ declare function freeTextBoxOf(item: {
2197
+ box: {
2198
+ x: number;
2199
+ y: number;
2200
+ width: number;
2201
+ height: number;
2202
+ };
2203
+ css: TextBoxCss;
2204
+ /** Clockwise, in degrees, about the box's centre. */
2205
+ rot?: number;
2206
+ editing: boolean;
2207
+ }, page: PageToPixels, outline: string): FreeTextBox;
2208
+ /** Your own element as a text box's editor, as CSS: it fills its parent. */
2209
+ type OwnTextBoxStyle = TextBoxStyle & {
2210
+ /** The parent's size times the editor's legible scale, in percent with its unit. */
2211
+ width: string;
2212
+ height: string;
2213
+ boxSizing: 'border-box';
2214
+ /** Draws the editor, laid out at a legible size, at its parent's size. */
2215
+ transform: string;
2216
+ transformOrigin: '0 0';
2217
+ /** While someone types, it takes the pointer (the caret, a drag over words). */
2218
+ pointerEvents?: 'auto';
2219
+ };
2220
+ /** Your own element as a text box's editor, and the scale its text is laid out at. */
2221
+ interface OwnTextBox {
2222
+ readonly style: OwnTextBoxStyle;
2223
+ /** Pixels per page point the editor lays its text out at: what its editor takes. */
2224
+ readonly scale: number;
2225
+ }
2226
+ /**
2227
+ * Your own element as the editor of a text box you draw yourself, at `scale`
2228
+ * pixels per point where it is ({@link textBoxEditorScaleOf}): the box's body
2229
+ * style, filling the element's parent, laid out at a legible size and drawn
2230
+ * back at the parent's (`legible-text.ts`). Draw the box's frame (its fill,
2231
+ * border and padding) on the parent: what's on the element scales with it.
2232
+ */
2233
+ declare function ownTextBoxOf(item: {
2234
+ css: TextBoxCss;
2235
+ editing: boolean;
2236
+ }, scale: number): OwnTextBox;
2237
+ //#endregion
2238
+ //#region src/legible-text.d.ts
2239
+ /**
2240
+ * Editors laid out at a legible size and drawn at the page's. iOS Safari zooms the page in when
2241
+ * a field takes focus with text under 16 CSS pixels, to 16 over that size, and it reads the
2242
+ * field's own computed font size: a transform doesn't change that size (CSS `zoom` does). Text
2243
+ * on a page is drawn at the page's scale, so a form field or a text box on a phone often has
2244
+ * smaller text. Such an editor is laid out {@link legibleScaleOf} times bigger (its text, lines
2245
+ * and box alike), and a `scale()` from its top-left corner draws it back at the page's size: the
2246
+ * same picture, with text iOS reads as legible. Every other browser draws it the same way.
2247
+ */
2248
+ /** The smallest text, in CSS pixels, a field takes focus with on iOS without the page zooming in. */
2249
+ declare const LEGIBLE_FONT_PX = 16;
2250
+ /**
2251
+ * How many times bigger to lay out an editor whose text is `fontPx` CSS pixels, so its text is
2252
+ * at least {@link LEGIBLE_FONT_PX}: 1 when it already is.
2253
+ */
2254
+ declare function legibleScaleOf(fontPx: number): number;
2255
+ /**
2256
+ * The transform that draws an element laid out `legible` times bigger back at its size, from its
2257
+ * top-left corner (`transformOrigin: '0 0'`), or null when it is laid out at its size.
2258
+ */
2259
+ declare function legibleShrinkOf(legible: number): string | null;
2260
+ //#endregion
2261
+ //#region src/annotation-renderers.d.ts
2262
+ /**
2263
+ * The annotation layer's renderers: which drawing each annotation gets, and
2264
+ * the behaviors that `interactive` renderers register. A renderer is a rule
2265
+ * ("draw these annotations with this component"); a framework adapter keeps
2266
+ * the component, and these decide everything around it, the same for every
2267
+ * framework:
2268
+ *
2269
+ * - {@link registerRendererBehaviors}: an `interactive` renderer takes the
2270
+ * pointer through a behavior the annotation plugin knows, registered once
2271
+ * per capability and renderer entry however many pages mount the layer.
2272
+ * - {@link annotationDrawingOf}: per annotation, the layer's own drawing, a
2273
+ * look of yours, or a sibling plugin's control (ownership beats looks).
2274
+ * - {@link editingTextKeyOf} and {@link layerTextBoxesOf}: which text box is
2275
+ * typed in, and which text boxes the layer draws rather than a look.
2276
+ *
2277
+ * The plugin's types are mirrored structurally, so this package stays free of
2278
+ * EmbedPDF imports.
2279
+ */
2280
+ /** A sibling plugin's renderer: it draws what the behavior `behavior` owns (the form plugin's controls). */
2281
+ interface BehaviorRendererEntry {
2282
+ behavior: string;
2283
+ }
2284
+ /** What an `interactive` function is asked: the annotation, and the active tool. */
2285
+ interface RendererInteractiveContext<Annotation> {
2286
+ annotation: Annotation;
2287
+ toolId: string;
2288
+ }
2289
+ /** A look of yours: draws the annotations `for` matches. */
2290
+ interface LookRendererEntry<Annotation> {
2291
+ /** A stable id for the registration (optional; generated). */
2292
+ id?: string;
2293
+ for(annotation: Annotation): boolean;
2294
+ /** Takes the pointer: always, or when the function says so (asked whenever it matters). */
2295
+ interactive?: boolean | ((context: RendererInteractiveContext<Annotation>) => boolean);
2296
+ }
2297
+ type RendererEntry<Annotation> = BehaviorRendererEntry | LookRendererEntry<Annotation>;
2298
+ /** The annotation plugin's behavior registry (its host capability satisfies it). */
2299
+ interface RendererBehaviorRegistry<Annotation> {
2300
+ registerBehavior(behavior: {
2301
+ id: string;
2302
+ matches(annotation: Annotation): boolean;
2303
+ engaged(annotation: Annotation): boolean;
2304
+ }): () => void;
2305
+ getBehaviorFor(annotation: Annotation): {
2306
+ id: string;
2307
+ } | null;
2308
+ }
2309
+ /**
2310
+ * Register a behavior for each `interactive` look in `renderers`, so the
2311
+ * plugin lets that renderer take the pointer. Call it when a layer mounts (and
2312
+ * again when its renderer list changes); call what it returns when it
2313
+ * unmounts. `activeToolId` is read whenever an `interactive` function is asked.
2314
+ */
2315
+ declare function registerRendererBehaviors<Annotation>(annotation: RendererBehaviorRegistry<Annotation>, renderers: readonly RendererEntry<Annotation>[], activeToolId: () => string): () => void;
2316
+ /** The behavior id a renderer entry answers for: a sibling plugin's own, or the one registered for it. */
2317
+ declare function rendererBehaviorId<Annotation>(annotation: object, renderer: RendererEntry<Annotation>): string | null;
2318
+ /** The first of your looks that draws this annotation, or `null`. */
2319
+ declare function lookRendererFor<Annotation, Entry extends RendererEntry<Annotation>>(record: Annotation, renderers: readonly Entry[] | undefined): Extract<Entry, {
2320
+ for: unknown;
2321
+ }> | null;
2322
+ /**
2323
+ * How the layer draws one annotation:
2324
+ * - `native`: its own drawing (the scene or the engine's raster). `inert`
2325
+ * when an engaged behavior's plugin owns the input with no renderer of
2326
+ * yours (a link's anchor takes the click).
2327
+ * - `owned`: a sibling plugin's renderer draws it and takes the input (the
2328
+ * form plugin's fill controls); it places its own controls.
2329
+ * - `look`: your renderer draws it, in the annotation's frame. `interactive`
2330
+ * when an engaged behavior gave it the pointer; otherwise it only draws,
2331
+ * and the frame is `inert` (no pointer, no focus) unless its text box is
2332
+ * being typed in, where the renderer's editor takes the keys.
2333
+ */
2334
+ type AnnotationDrawing<Entry> = {
2335
+ kind: 'native';
2336
+ inert: boolean;
2337
+ } | {
2338
+ kind: 'owned';
2339
+ entry: Extract<Entry, {
2340
+ behavior: string;
2341
+ }>;
2342
+ } | {
2343
+ kind: 'look';
2344
+ entry: Extract<Entry, {
2345
+ for: unknown;
2346
+ }>;
2347
+ interactive: boolean;
2348
+ inert: boolean;
2349
+ };
2350
+ /**
2351
+ * The drawing an annotation gets. Ownership beats looks: an engaged
2352
+ * behavior's renderer is authoritative; `for` rules apply only to what the
2353
+ * layer owns, and draw without the pointer. `typing` is whether this
2354
+ * annotation's text box is being typed in.
2355
+ */
2356
+ declare function annotationDrawingOf<Annotation, Entry extends RendererEntry<Annotation>>(record: Annotation, annotation: RendererBehaviorRegistry<Annotation>, renderers: readonly Entry[] | undefined, typing: boolean): AnnotationDrawing<Entry>;
2357
+ /**
2358
+ * The key of the text box being typed in (the one that is `editing` and has a
2359
+ * ref), or `null`. An annotation with this key is `typing` for
2360
+ * {@link annotationDrawingOf}: while it is, a look's own editor takes the keys.
2361
+ */
2362
+ declare function editingTextKeyOf<Ref>(texts: readonly {
2363
+ readonly editing: boolean;
2364
+ readonly ref: Ref | null;
2365
+ }[], keyOf: (ref: Ref) => string): string | null;
2366
+ /**
2367
+ * The text boxes the layer draws itself: all of them but the ones whose
2368
+ * annotation a look of yours draws, which that look edits with its own editor
2369
+ * (a renderer's rich text editor). `items` are the page's render items, which
2370
+ * carry each annotation; the same array comes back when there is no look.
2371
+ */
2372
+ declare function layerTextBoxesOf<Annotation, Ref, Text extends {
2373
+ readonly ref: Ref | null;
2374
+ }>(texts: readonly Text[], items: readonly {
2375
+ readonly ref: Ref | null;
2376
+ readonly annotation?: Annotation | null;
2377
+ }[], renderers: readonly RendererEntry<Annotation>[] | undefined, keyOf: (ref: Ref) => string): readonly Text[];
2378
+ //#endregion
2379
+ //#region src/comment-threads.d.ts
2380
+ /**
2381
+ * Comment threads with where their page is shown: the join of the annotation
2382
+ * plugin's threads and the document's page list that a comments panel
2383
+ * prints. A thread's identity stays its page's address; the index and the
2384
+ * label are presentation, following page moves and deletes.
2385
+ */
2386
+ /** Where a thread's page is shown now. */
2387
+ interface CommentThreadPage {
2388
+ /** Current 0-based display index of the thread's page; `-1` when the page is no longer in the document. */
2389
+ pageIndex: number;
2390
+ /**
2391
+ * The page's `/PageLabels` label when the PDF declares one ("iv", "A-2"),
2392
+ * else the 1-based position as a string; `'?'` for a page that's gone.
2393
+ * Print it as it is.
2394
+ */
2395
+ pageLabel: string;
2396
+ }
2397
+ /** A page as the document's page list has it: its address, display index and label. */
2398
+ interface CommentPageLayout {
2399
+ readonly ref: {
2400
+ readonly objectNumber: number;
2401
+ };
2402
+ readonly index: number;
2403
+ readonly label?: string | null;
2404
+ }
2405
+ /**
2406
+ * Each thread with its page's display index and label. A thread whose page
2407
+ * was just deleted reads `-1` and `'?'` for the one render before it goes.
2408
+ */
2409
+ declare function enrichCommentThreads<Thread extends {
2410
+ readonly page: {
2411
+ readonly objectNumber: number;
2412
+ };
2413
+ }>(threads: readonly Thread[], pages: readonly CommentPageLayout[]): Array<Thread & CommentThreadPage>;
2414
+ //#endregion
2415
+ //#region src/form-field.d.ts
2416
+ /** The form settings the colors read: the structural twin of the form plugin's `focus` and `fields`. */
2417
+ interface FormColorSettings {
2418
+ readonly focus: {
2419
+ readonly color: string | null;
2420
+ };
2421
+ readonly fields: {
2422
+ readonly border: string | null;
2423
+ readonly background: string;
2424
+ readonly color: string;
2425
+ };
2426
+ }
2427
+ /** The colors the viewer draws fields with, each its `--epdf-form-*` variable first, then the setting. */
2428
+ interface FormColors {
2429
+ /** The focus ring around the field being filled in. */
2430
+ readonly focus: string;
2431
+ /** The edge of a field without a border of its own. */
2432
+ readonly border: string;
2433
+ /** Inside a field without a background of its own, while it's filled in. */
2434
+ readonly background: string;
2435
+ /** The text of a field without a color of its own. */
2436
+ readonly text: string;
2437
+ }
2438
+ /** The form settings as colors: an unset focus color is the accent, an unset edge the accent at 55%. */
2439
+ declare function formColorsOf(settings: FormColorSettings, accent: string): FormColors;
2440
+ /** How a widget looks in the PDF: the structural twin of the form plugin's `FormWidgetLook`. */
2441
+ interface FieldLook {
2442
+ readonly border: string | null;
2443
+ /** In page points. */
2444
+ readonly borderWidth: number;
2445
+ readonly borderStyle: string;
2446
+ readonly background: string | null;
2447
+ readonly color: string | null;
2448
+ /** One of the 14 standard PDF fonts (`'helvetica-bold'`), or `null`. */
2449
+ readonly fontFamily: string | null;
2450
+ /** In page points; `0` or `null` fits the text to the box. */
2451
+ readonly fontSize: number | null;
2452
+ readonly textAlign: 'left' | 'center' | 'right';
2453
+ }
2454
+ /** A standard PDF font as CSS. */
2455
+ type CssFont = {
2456
+ fontFamily: string;
2457
+ fontWeight: number;
2458
+ fontStyle: 'normal' | 'italic';
2459
+ };
2460
+ /** One of the 14 standard PDF fonts as CSS: a family that looks like it, its weight and style. */
2461
+ declare function cssFontOf(pdfFont: string | null): CssFont;
2462
+ /** A field's text as CSS: its font, size, alignment and colors, the settings where it has none. */
2463
+ type FieldTextStyle = CssFont & {
2464
+ /** In pixels, with its unit. */
2465
+ fontSize: string;
2466
+ textAlign: 'left' | 'center' | 'right';
2467
+ color: string;
2468
+ background: string;
2469
+ };
2470
+ /** A text field as the layer's editor shows it while focused, in the field's own font. */
2471
+ type TextFieldStyle = FieldTextStyle & {
2472
+ /** A comb field's spacing (its box split into `maxLength` cells), in pixels with its unit. */
2473
+ letterSpacing?: string;
2474
+ };
2475
+ /**
2476
+ * The size of a text field's text in pixels at `frame`. A font size of 0 means "fit the box" in
2477
+ * PDF: the box height for one line, Acrobat's 12 pt for several.
2478
+ */
2479
+ declare function textFieldFontPxOf(field: {
2480
+ box: {
2481
+ width: number;
2482
+ };
2483
+ look: FieldLook;
2484
+ multiline: boolean;
2485
+ }, frame: PixelRect): number;
2486
+ /**
2487
+ * A text field's editor style at `frame`, with its lengths `legible` times bigger for an editor
2488
+ * laid out at a legible size (`legible-text.ts`).
2489
+ */
2490
+ declare function textFieldStyleOf(field: {
2491
+ box: {
2492
+ width: number;
2493
+ };
2494
+ look: FieldLook;
2495
+ multiline: boolean;
2496
+ comb: boolean;
2497
+ maxLength: number | null;
2498
+ }, frame: PixelRect, colors: FormColors, legible?: number): TextFieldStyle;
2499
+ /** A list box as the layer draws it: the field's text look, and its border. */
2500
+ type ListBoxStyle = FieldTextStyle & {
2501
+ /** In pixels, with its unit; never thinner than one pixel. */
2502
+ borderWidth: string;
2503
+ borderStyle: 'solid' | 'dashed';
2504
+ borderColor: string;
2505
+ };
2506
+ /** A list box's style: the field's own look, the settings where it has none (12 pt for a fitted size). */
2507
+ declare function listBoxStyleOf(field: {
2508
+ box: {
2509
+ width: number;
2510
+ };
2511
+ look: FieldLook;
2512
+ }, frame: PixelRect, colors: FormColors): ListBoxStyle;
2513
+ /** The PDF events a widget's `/AA` actions answer, as the actions plugin names them. */
2514
+ type WidgetEventKind = 'cursorEnter' | 'cursorExit' | 'mouseDown' | 'mouseUp' | 'focus' | 'blur';
2515
+ /**
2516
+ * Send a widget's pointer and focus events (`/AA`: enter, exit, down, up,
2517
+ * focus, blur) to `notify`, from native listeners on the widget's box. Put
2518
+ * them on the always-active box, never on the control inside: "may edit this
2519
+ * field" and "may receive PDF action events" are different rights, and a
2520
+ * session that may not fill still sees hover tooltips. Focus is `focusin` and
2521
+ * `focusout`, so the control's focus reaches the box, and a blur arrives
2522
+ * after the control's own blur handler started its commit. Returns the
2523
+ * detach.
2524
+ */
2525
+ declare function bindWidgetEvents(box: HTMLElement, notify: (event: WidgetEventKind) => void): () => void;
2526
+ /** What a toggle press writes with: the form plugin's `setValue`. */
2527
+ interface ToggleForm<Field> {
2528
+ setValue(field: Field, value: {
2529
+ value: string | null;
2530
+ }): Promise<unknown>;
2531
+ }
2532
+ /**
2533
+ * A checkbox or radio button pressed: a checked checkbox clears, a radio
2534
+ * button always selects its own value. The value is written first, then the
2535
+ * widget's action runs (Acrobat's order), so an action script reads the new
2536
+ * state; a read-only toggle writes nothing and still runs its action.
2537
+ */
2538
+ declare function pressToggle<Field>(form: ToggleForm<Field>, toggle: {
2539
+ fieldRef: Field;
2540
+ kind: 'checkbox' | 'radio';
2541
+ checked: boolean;
2542
+ exportValue: string;
2543
+ disabled: boolean;
2544
+ }, activate: () => void): void;
2545
+ /** The form plugin's draft calls for a text field (its host capability satisfies it). */
2546
+ interface TextFieldDrafts<Field> {
2547
+ draftText(field: Field, text: string): void;
2548
+ /** Resolves with how the write ended (`'rejected'`: the form's validation refused it), or `null`. */
2549
+ commitDraftText(field: Field): Promise<{
2550
+ readonly status: string;
2551
+ } | null>;
2552
+ discardDraftText(field: Field): void;
2553
+ }
2554
+ /** What a text field's editor shows: whether it has focus, and the text in it. */
2555
+ interface TextFieldEditorState {
2556
+ readonly focused: boolean;
2557
+ readonly draft: string;
2558
+ }
2559
+ /**
2560
+ * A text field being typed in, framework-free: the editor's `value` is
2561
+ * `draft`, and its events call the methods. Read the state with `getState`
2562
+ * and re-render on `subscribe`; the state object stays the same until it
2563
+ * changes.
2564
+ */
2565
+ interface TextFieldEditor {
2566
+ getState(): TextFieldEditorState;
2567
+ subscribe(listener: () => void): () => void;
2568
+ /** The field's value as the form has it now. The draft follows it, except while focused. */
2569
+ setValue(value: string): void;
2570
+ /** The editor took focus. */
2571
+ focus(): void;
2572
+ /** Someone typed: the draft shows it, and the plugin keeps it so a download writes it. */
2573
+ input(text: string): void;
2574
+ /**
2575
+ * A key went down. True when the editor should blur now: Escape (which
2576
+ * puts the value back) and, in a one-line field, Enter (which commits).
2577
+ */
2578
+ keyDown(key: string, multiline: boolean): boolean;
2579
+ /** The editor lost focus: commit what changed, discard what didn't or was cancelled. */
2580
+ blur(): void;
2581
+ }
2582
+ /**
2583
+ * The editing policy of one text field: typing drafts, blur or Enter
2584
+ * commits, Escape discards. A commit keeps the typed text until the field
2585
+ * shows it (at once, as the form shows a fill before the engine answers), so
2586
+ * a blur never shows the old value for a moment. A value the form's
2587
+ * validation refuses stays typed, as in Acrobat; a write the engine refuses
2588
+ * puts the field's value back.
2589
+ */
2590
+ declare function createTextFieldEditor<Field>(form: TextFieldDrafts<Field>, field: Field, value: string): TextFieldEditor;
2591
+ /**
2592
+ * A list box's selection, shown at once while its write is on its way. The
2593
+ * native control must stay the same element (never remounted), so its own
2594
+ * scroll position stays where the user left it.
2595
+ */
2596
+ interface OptimisticSelection {
2597
+ /** The selection the control shows. */
2598
+ get(): readonly string[];
2599
+ subscribe(listener: () => void): () => void;
2600
+ /** The selection the engine has now: the control shows it once it differs from the last one. */
2601
+ setConfirmed(values: readonly string[]): void;
2602
+ /** Show `values` now and write them; a write that fails puts the engine's selection back. */
2603
+ choose(values: string[], write: (values: string[]) => void | Promise<unknown>): void;
2604
+ }
2605
+ /** A list box's optimistic selection, starting at the engine's. */
2606
+ declare function createOptimisticSelection(confirmed: readonly string[]): OptimisticSelection;
2607
+ /** A native `<select>`, as {@link showSelectedOptions} reads it: its options. */
2608
+ interface SelectOptions {
2609
+ readonly options: ArrayLike<{
2610
+ readonly value: string;
2611
+ selected: boolean;
2612
+ }>;
2613
+ }
2614
+ /**
2615
+ * Show `values` as a native `<select>`'s selection, set on each option. A
2616
+ * `value` binding can't hold several values, and setting the options also
2617
+ * puts a choice back when its write fails before the framework renders
2618
+ * again. Setting the same selection again changes nothing the user sees,
2619
+ * scroll position included.
2620
+ */
2621
+ declare function showSelectedOptions(select: SelectOptions, values: readonly string[]): void;
2622
+ //#endregion
2623
+ //#region src/form-styles.d.ts
2624
+ /** A control that fills the widget's box it sits in: every native control the layer draws. */
2625
+ declare const FORM_CONTROL_FILL: Readonly<{
2626
+ readonly position: "absolute";
2627
+ readonly inset: "0";
2628
+ readonly width: "100%";
2629
+ readonly height: "100%";
2630
+ readonly boxSizing: "border-box";
2631
+ readonly margin: "0";
2632
+ }>;
2633
+ /** {@link FORM_CONTROL_FILL}'s shape. */
2634
+ type FormControlFill = typeof FORM_CONTROL_FILL;
2635
+ /** The box a widget's control sits in, placed over the widget on the page. */
2636
+ type WidgetBoxStyle = {
2637
+ position: 'absolute';
2638
+ left: string;
2639
+ top: string;
2640
+ width: string;
2641
+ height: string;
2642
+ /** Always the widget's event surface: the control inside gates the edits. */
2643
+ pointerEvents: 'auto';
2644
+ /** The edge of a field without a border of its own, so people see where to fill in. */
2645
+ boxShadow?: string;
2646
+ };
2647
+ /**
2648
+ * The style of a widget's box at `frame` (the widget's box in the page
2649
+ * layer's pixels). `edge: false` leaves the edge out, for a control that draws
2650
+ * its own (a list box) or never has one (a button).
2651
+ */
2652
+ declare function widgetBoxStyleOf(widget: {
2653
+ look: Pick<FieldLook, 'border'>;
2654
+ }, frame: PixelRect, colors: FormColors, { edge }?: {
2655
+ edge?: boolean;
2656
+ }): WidgetBoxStyle;
2657
+ /**
2658
+ * Where a text field's editor shows the text typed in it, so it sits where the engine will draw
2659
+ * it (the form plugin's `getTextEditorLayout`), in page points.
2660
+ */
2661
+ interface TextEditorLayout {
2662
+ readonly fontFamily: string;
2663
+ readonly fontWeight: number;
2664
+ readonly fontStyle: 'normal' | 'italic';
2665
+ readonly fontSize: number;
2666
+ readonly lineHeight: number;
2667
+ readonly padding: {
2668
+ readonly top: number;
2669
+ readonly right: number;
2670
+ readonly bottom: number;
2671
+ readonly left: number;
2672
+ };
2673
+ /** A comb's room between characters; it starts at the left padding, whatever the alignment. */
2674
+ readonly letterSpacing: number;
2675
+ readonly comb: boolean;
2676
+ }
2677
+ /** A text field's editor: see-through at rest, the field's own look while focused. */
2678
+ type TextFieldEditorStyle = TextFieldStyle & {
2679
+ position: 'absolute';
2680
+ left: '0';
2681
+ top: '0';
2682
+ /** The widget's box times the editor's legible scale, in percent with its unit. */
2683
+ width: string;
2684
+ height: string;
2685
+ boxSizing: 'border-box';
2686
+ margin: '0';
2687
+ /** Draws the editor, laid out at a legible size, at the size of the widget's box. */
2688
+ transform: string;
2689
+ transformOrigin: '0 0';
2690
+ border: 'none';
2691
+ outline: string;
2692
+ outlineOffset: string;
2693
+ padding: string;
2694
+ /** In pixels, with its unit: one line of the engine's layout. */
2695
+ lineHeight?: string;
2696
+ resize: 'none';
2697
+ cursor: 'text';
2698
+ /** 1 while focused, 0 at rest, where the field's picture shows through. */
2699
+ opacity: number;
2700
+ /** `none` for a disabled field: the box below runs the widget's action. */
2701
+ pointerEvents?: 'none';
2702
+ };
2703
+ /**
2704
+ * The style of a text field's editor, the `<input>` or `<textarea>` that is
2705
+ * always there: focused, it shows the field's text in its own font with a
2706
+ * focus ring; at rest it is see-through over the field's picture. A disabled
2707
+ * control would swallow clicks, and the box below runs the widget's action
2708
+ * (the read-only "button" pattern), so a disabled one lets them through.
2709
+ *
2710
+ * It fills the widget's box, laid out at a legible size and drawn back at the
2711
+ * box's (`legible-text.ts`), so iOS doesn't zoom the page in when it takes focus.
2712
+ *
2713
+ * With the engine's `layout` of the text being typed, the editor writes it
2714
+ * where the engine will draw it: in the engine's face (mounted), at the size
2715
+ * it draws (a fitted size resolved), in the box inside the border, a line of
2716
+ * the face's height (one line centred, as the engine centres it).
2717
+ */
2718
+ declare function textFieldEditorStyleOf(field: Parameters<typeof textFieldStyleOf>[0] & {
2719
+ disabled: boolean;
2720
+ }, frame: PixelRect, colors: FormColors, focused: boolean, layout?: TextEditorLayout | null): TextFieldEditorStyle;
2721
+ /** A list box: the visible native `<select>` in the field's own look. */
2722
+ type ListBoxControlStyle = FormControlFill & ListBoxStyle & {
2723
+ padding: string;
2724
+ borderRadius: string;
2725
+ outline: 'none';
2726
+ cursor: 'default' | 'pointer';
2727
+ /** `none` for a disabled field: the box below runs the widget's action. */
2728
+ pointerEvents?: 'none';
2729
+ };
2730
+ /**
2731
+ * The style of a list box's `<select>`, filling the widget's box: the field's
2732
+ * own look ({@link listBoxStyleOf}) with its own border, so the box adds no
2733
+ * edge. The focus ring is drawn above it ({@link formFocusRingStyleOf}).
2734
+ */
2735
+ declare function listBoxControlStyleOf(field: Parameters<typeof listBoxStyleOf>[0] & {
2736
+ disabled: boolean;
2737
+ }, frame: PixelRect, colors: FormColors): ListBoxControlStyle;
2738
+ /** The focus ring of a field whose control is see-through, or is the box itself. */
2739
+ type FormFocusRingStyle = {
2740
+ position: 'absolute';
2741
+ inset: string;
2742
+ zIndex: number;
2743
+ boxSizing: 'border-box';
2744
+ outline: string;
2745
+ outlineOffset: string;
2746
+ pointerEvents: 'none';
2747
+ };
2748
+ /**
2749
+ * The style of a field's focus ring in `color` (the form's focus color): an
2750
+ * element of its own above the field's picture and its control. An outline on
2751
+ * the box would sit under an opaque child, so a rectangular checkbox or a
2752
+ * dropdown would look unfocused while it has the focus.
2753
+ */
2754
+ declare function formFocusRingStyleOf(color: string): FormFocusRingStyle;
2755
+ //#endregion
2756
+ //#region src/link-anchor.d.ts
2757
+ /** A link as the link plugin lists it: the fields the anchor reads. */
2758
+ interface LinkAnchorShape {
2759
+ readonly target: {
2760
+ readonly kind: string;
2761
+ readonly uri?: string;
2762
+ };
2763
+ /** The full `/A` tree; a `/Next` after its root chains more actions. */
2764
+ readonly activate?: {
2765
+ readonly root: {
2766
+ readonly next: readonly unknown[];
2767
+ } | null;
2768
+ } | null;
2769
+ /** A link riding an editable annotation (its parent owns its pixels while annotations are edited). */
2770
+ readonly attached: boolean;
2771
+ }
2772
+ /**
2773
+ * The links a page layer shows anchors for. One owner per pixel: while the
2774
+ * active tool edits annotations, a link attached to an annotation stands
2775
+ * down so its parent can be selected and moved; a document's own links
2776
+ * navigate under every tool that enables link navigation.
2777
+ */
2778
+ declare function navigableLinksOf<Link extends LinkAnchorShape>(links: readonly Link[], annotationEditEnabled: boolean): readonly Link[];
2779
+ /**
2780
+ * The anchor's native `href`: only for a website the browser may open and
2781
+ * nothing chained after it. Blocked schemes, targets inside the document and
2782
+ * action chains activate through the plugin instead: a native navigation
2783
+ * would run the first action and silently drop the rest.
2784
+ */
2785
+ declare function linkHrefOf(link: LinkAnchorShape): string | null;
2786
+ /** A link's anchor, ready to draw. */
2787
+ interface LinkAnchor<Link> {
2788
+ readonly link: Link;
2789
+ /** Where the anchor sits, in the page layer's pixels. */
2790
+ readonly box: PixelRect;
2791
+ /** Its native `href` ({@link linkHrefOf}), or null. */
2792
+ readonly href: string | null;
2793
+ /** Its `title` and `aria-label`. */
2794
+ readonly label: string;
2795
+ }
2796
+ /**
2797
+ * The anchor for `link` on a page layer: its box from the link's `bounds`,
2798
+ * its `href`, and the label `labelOf` (the link plugin's `getLabel`) gives it.
2799
+ */
2800
+ declare function linkAnchorOf<Link extends LinkAnchorShape & {
2801
+ readonly bounds: {
2802
+ x: number;
2803
+ y: number;
2804
+ width: number;
2805
+ height: number;
2806
+ };
2807
+ }>(link: Link, page: PageToPixels, labelOf: (link: Link) => string): LinkAnchor<Link>;
2808
+ /**
2809
+ * Whether a click on an anchor with an `href` stays the browser's: a click
2810
+ * with a modifier key opens a background tab or a new window. Every other
2811
+ * click is prevented and follows the link through the plugin.
2812
+ */
2813
+ declare function isModifiedClick(event: {
2814
+ metaKey: boolean;
2815
+ ctrlKey: boolean;
2816
+ shiftKey: boolean;
2817
+ altKey: boolean;
2818
+ }): boolean;
2819
+ //#endregion
2820
+ //#region src/link-events.d.ts
2821
+ /**
2822
+ * What a page link's anchor tells the plugins: the context a click or a key
2823
+ * follows the link with (the link plugin's `activate()`), and the link's own
2824
+ * PDF events (`/AA`): entering and leaving through the layer's hover pump,
2825
+ * the press, the release, focus and blur through the actions plugin's
2826
+ * `dispatch()`. Only a link that is an annotation has events of its own. The
2827
+ * plugins' types are mirrored structurally and their refs stay opaque, so
2828
+ * this package stays free of EmbedPDF imports.
2829
+ */
2830
+ /** The events an anchor sends the actions plugin itself; entering and leaving go through the hover pump. */
2831
+ type LinkAnchorEvent = 'mouseDown' | 'mouseUp' | 'focus' | 'blur';
2832
+ /** A link's fields these read: its `/A` tree, the annotation it is, and which hover trees it has. */
2833
+ interface LinkEventShape<Ref, Action = unknown, HoverEvents = unknown> {
2834
+ readonly activate?: Action;
2835
+ readonly ref?: Ref;
2836
+ readonly hoverEvents?: HoverEvents;
2837
+ }
2838
+ /** The link as the source of an actions-plugin event. */
2839
+ interface LinkActionSource<Ref, Page> {
2840
+ readonly kind: 'link';
2841
+ readonly annotation: Ref;
2842
+ readonly page: Page;
2843
+ }
2844
+ /** The actions plugin, as an anchor uses it. */
2845
+ interface LinkActionDispatcher<Ref, Page> {
2846
+ dispatch(trigger: {
2847
+ readonly scope: 'annotation';
2848
+ readonly event: LinkAnchorEvent;
2849
+ readonly ref: Ref;
2850
+ readonly page: Page;
2851
+ readonly source: LinkActionSource<Ref, Page>;
2852
+ }): unknown;
2853
+ }
2854
+ /** One link layer's hover pump (`createHoverPump` in the actions plugin's contract). */
2855
+ interface LinkHoverPump<Ref, Page, HoverEvents> {
2856
+ hover(target: {
2857
+ readonly ref: Ref;
2858
+ readonly page: Page;
2859
+ readonly source: LinkActionSource<Ref, Page>;
2860
+ readonly events: HoverEvents;
2861
+ } | null): void;
2862
+ }
2863
+ /**
2864
+ * The context a click or a key follows `link` with: its `/A` tree (the
2865
+ * actions plugin runs it instead of the target), the annotation it is, its
2866
+ * page, and the view it was followed in (a page destination moves that one).
2867
+ */
2868
+ declare function linkActivateContextOf<Ref, Action, Page, Stage>(link: LinkEventShape<Ref, Action>, page: Page, stage: Stage | null): {
2869
+ activate?: Action;
2870
+ ref?: Ref;
2871
+ page: Page;
2872
+ stage?: Stage;
2873
+ };
2874
+ /**
2875
+ * Send `link`'s own event for a press, a release or a focus change: only for
2876
+ * a link that is an annotation, and only with the actions plugin.
2877
+ */
2878
+ declare function sendLinkEvent<Ref, Page>(actions: NoInfer<LinkActionDispatcher<Ref, Page>> | null, link: LinkEventShape<Ref>, page: Page, event: LinkAnchorEvent): void;
2879
+ /**
2880
+ * Tell the layer's hover pump the pointer entered `link`: only a link that is
2881
+ * an annotation with hover trees has enter and exit events. Leaving is
2882
+ * `pump.hover(null)`.
2883
+ */
2884
+ declare function hoverLink<Ref, Page, HoverEvents>(pump: NoInfer<LinkHoverPump<Ref, Page, HoverEvents>> | null, link: LinkEventShape<Ref, unknown, HoverEvents>, page: Page): void;
2885
+ //#endregion
2886
+ //#region src/search-highlights.d.ts
2887
+ /** A match as the search plugin lists it: the fields the paint reads. */
2888
+ interface SearchHitShape {
2889
+ /** Where the match starts in the document's text: with the line's index, its key. */
2890
+ readonly start: number;
2891
+ /** One per line the match spans, in page points. */
2892
+ readonly segments: readonly {
2893
+ readonly quad: PageQuad;
2894
+ }[];
2895
+ }
2896
+ /** One line of a match, ready to draw. */
2897
+ interface SearchHighlightPiece<Hit extends SearchHitShape> {
2898
+ readonly key: string;
2899
+ readonly hit: Hit;
2900
+ /** Whether it's a line of the active match. */
2901
+ readonly active: boolean;
2902
+ readonly fill: string;
2903
+ /** The line's box in the page layer's pixels when it's upright; null when it's turned. */
2904
+ readonly box: PixelRect | null;
2905
+ /** The four corners as SVG `points`, for a turned line; null when it's upright. */
2906
+ readonly points: string | null;
2907
+ }
2908
+ /** What {@link searchHighlightsOf} paints with. */
2909
+ interface SearchHighlightOptions<Hit extends SearchHitShape> {
2910
+ /** The search's active match (compared by identity), or null. */
2911
+ readonly active: Hit | null;
2912
+ /** The page layer's transform. */
2913
+ readonly page: PageToPixels;
2914
+ /** The fill of a match, and of the active one (`paint('search-highlight', …)`). */
2915
+ readonly color: string;
2916
+ readonly activeColor: string;
2917
+ }
2918
+ /** Every line of `hits` as a piece to draw, in hit order. */
2919
+ declare function searchHighlightsOf<Hit extends SearchHitShape>(hits: readonly Hit[], options: SearchHighlightOptions<Hit>): SearchHighlightPiece<Hit>[];
2920
+ //#endregion
2921
+ //#region src/press-click.d.ts
2922
+ /**
2923
+ * Telling a click from a drag on something drawn over a page (a search
2924
+ * match). The press still reaches the page, so a drag that starts there
2925
+ * selects text; only a press that stays put counts as a click.
2926
+ */
2927
+ /** A click detector for one layer: feed it the `pointerdown`, ask it at the `click`. */
2928
+ interface ClickDetector {
2929
+ /** A press began (`pointerdown`). */
2930
+ press(event: {
2931
+ clientX: number;
2932
+ clientY: number;
2933
+ pointerType: string;
2934
+ }): void;
2935
+ /** Whether this `click` is a click, not the end of a drag. Forgets the press. */
2936
+ isClick(event: {
2937
+ clientX: number;
2938
+ clientY: number;
2939
+ }): boolean;
2940
+ }
2941
+ declare function createClickDetector(): ClickDetector;
2942
+ //#endregion
2943
+ //#region src/page-projector.d.ts
2944
+ /** The page transform members a projection reads; `PageTransform` satisfies it. */
2945
+ interface ProjectedTransform {
2946
+ readonly viewScale: number;
2947
+ readonly rotation: 0 | 90 | 180 | 270;
2948
+ readonly zoom: number;
2949
+ }
2950
+ /**
2951
+ * The page surface a client projector measures: the page context's members. `transform` may be
2952
+ * the transform or a function that returns the current one (a signal).
2953
+ */
2954
+ interface ClientProjectedPage {
2955
+ readonly ref: PageRef;
2956
+ readonly transform: ProjectedTransform | (() => ProjectedTransform);
2957
+ toClientRect(rect: AnchoredRect): AnchoredRect;
2958
+ toClientPoint(point: {
2959
+ x: number;
2960
+ y: number;
2961
+ }): {
2962
+ x: number;
2963
+ y: number;
2964
+ };
2965
+ }
2966
+ /**
2967
+ * A client-space projector for one page. `mounted` says whether the page
2968
+ * element is in the document yet: until it is, every projection is `null`,
2969
+ * and the anchored UI waits for the first measurable pass. The view is the
2970
+ * window. Pair it with {@link observeClientGeometry}, which reports the
2971
+ * browser-driven moves (document scroll, window resize) no state announces.
2972
+ * Every member reads `page` when it runs, so a page context whose members
2973
+ * change keeps one projector.
2974
+ */
2975
+ declare function clientPageProjector(page: ClientProjectedPage, mounted: () => boolean): ViewProjector;
2976
+ /** What a Stage projection reads from the Stage lens; `StageHostCapability` satisfies it. */
2977
+ interface StageProjection {
2978
+ pageRectToViewport(page: PageRef, rect: AnchoredRect): AnchoredRect | null;
2979
+ getPageFrame(page: PageRef): {
2980
+ readonly transform: ProjectedTransform;
2981
+ } | null;
2982
+ getViewportSize(): {
2983
+ width: number;
2984
+ height: number;
2985
+ };
2986
+ }
2987
+ /**
2988
+ * The Stage's projector: anchored UI positions through the camera, in the Stage's own box
2989
+ * (`'overlay'` space). `stage()` gives the lens when a projection runs, or null without one
2990
+ * (nothing projects then). It reads only the Stage's state, so the binding's revision (the
2991
+ * visible pages) is what tells anchored UI to place itself again, in the same render as the
2992
+ * pages.
2993
+ */
2994
+ declare function stageViewProjector(stage: () => StageProjection | null): ViewProjector;
2995
+ //#endregion
2996
+ //#region src/page-context.d.ts
2997
+ interface ContextPoint {
2998
+ x: number;
2999
+ y: number;
3000
+ }
3001
+ interface ContextRect {
3002
+ x: number;
3003
+ y: number;
3004
+ width: number;
3005
+ height: number;
3006
+ }
3007
+ /** The bands reserved around a page for its chrome, in screen pixels per side. */
3008
+ interface PageFrameShape {
3009
+ top: number;
3010
+ right: number;
3011
+ bottom: number;
3012
+ left: number;
3013
+ }
3014
+ /** What the conversions need from the page transform; `PageTransform` satisfies it. */
3015
+ interface PageContextTransform {
3016
+ viewToPage(point: ContextPoint): ContextPoint;
3017
+ pageToView(point: ContextPoint): ContextPoint;
3018
+ pageToViewRect(rect: ContextRect): ContextRect;
3019
+ }
3020
+ /** Where the page's content box is on screen, in client pixels: `getBoundingClientRect()`. */
3021
+ interface ClientBox {
3022
+ readonly left: number;
3023
+ readonly top: number;
3024
+ }
3025
+ /** Between client (screen) pixels and points on one page. */
3026
+ interface PageClientSpace {
3027
+ /** A client (screen) point to a point on the page: the one platform-bound hit test. */
3028
+ toPagePoint(clientX: number, clientY: number): ContextPoint;
3029
+ /**
3030
+ * A point on the page to client pixels, the exact inverse of `toPagePoint` (rotation applied),
3031
+ * so UI in the view can anchor to a page point without a Stage camera.
3032
+ */
3033
+ toClientPoint(point: ContextPoint): ContextPoint;
3034
+ /** A box on the page to the client-pixel box around it: the box twin of `toClientPoint`. */
3035
+ toClientRect(rect: ContextRect): ContextRect;
3036
+ }
3037
+ /**
3038
+ * The page context. `Transform` is the page transform (`PageTransform`), `Demand` what the view
3039
+ * wants rendered (`PageViewDemand`).
3040
+ */
3041
+ interface PageContext<Transform extends PageContextTransform, Demand> extends PageClientSpace {
3042
+ documentId: string;
3043
+ /**
3044
+ * The page's durable address: use it for keys, rendering and annotations (read
3045
+ * `ref.objectNumber` where a map key is needed). The same object for the surface's lifetime,
3046
+ * so layers may key their work on it.
3047
+ */
3048
+ ref: PageRef;
3049
+ /** The display index (page N), from 0: for ordering and page numbers people read. */
3050
+ pageIndex: number;
3051
+ /**
3052
+ * The bands reserved around the page, in screen pixels per side. The page chrome draws into
3053
+ * the outer box (content and frame): a label in the bottom band is
3054
+ * `bottom: 0; height: frame.bottom`.
3055
+ */
3056
+ frame: PageFrameShape;
3057
+ /**
3058
+ * The one bridge between PDF points, view pixels and device pixels for this page: `toPixels`
3059
+ * to place content, `renderScale` and `deviceWidth` to render, `contentWidth` for sizes
3060
+ * relative to the page. Never multiply by the scale or the device pixel ratio yourself.
3061
+ */
3062
+ transform: Transform;
3063
+ /**
3064
+ * What the page's view wants rendered, as a pull the host fills: a Stage reads the page's
3065
+ * visible part live at call time (a zero box while it is off screen, see
3066
+ * {@link stagePageDemand}); a surface without a camera leaves it out, which means the whole
3067
+ * page.
3068
+ */
3069
+ getViewDemand?: () => Demand;
3070
+ /**
3071
+ * The hosting view's identity: the stage lens id (`stage.getLensId()`) or a per-instance
3072
+ * `PageView` id. Per-view raster planning (tiles) keys its state by it, so two views showing
3073
+ * the same page never fight over one plan.
3074
+ */
3075
+ view: string;
3076
+ }
3077
+ /**
3078
+ * The client-space conversions of a page whose content box `box()` measures on screen when a
3079
+ * conversion runs: client pixels to box-local view pixels, then the transform undoes the turn
3080
+ * and the scale. `toPagePoint` and `toClientPoint` read the same live box, so they never drift.
3081
+ */
3082
+ declare function pageClientSpace(transform: () => PageContextTransform, box: () => ClientBox): PageClientSpace;
3083
+ /**
3084
+ * Build a page context. `getRect` is the page's content box on screen (the turned content
3085
+ * element's bounding box), read when a conversion runs.
3086
+ */
3087
+ declare function makePageContext<Transform extends PageContextTransform, Demand = never>(documentId: string, view: string, ref: PageRef, pageIndex: number, frame: PageFrameShape, transform: Transform, getRect: () => ClientBox, getViewDemand?: () => Demand): PageContext<Transform, Demand>;
3088
+ /**
3089
+ * A page context that reads every member through `current()`: the same object for a surface's
3090
+ * lifetime, current in what it answers. For an adapter whose layers keep the object they were
3091
+ * given once (a Svelte component reads `page.transform` in a template and follows the zoom).
3092
+ */
3093
+ declare function livePageContext<Context extends PageContext<PageContextTransform, unknown>>(current: () => Context): Context;
3094
+ //#endregion
3095
+ //#region src/page-layers.d.ts
3096
+ /**
3097
+ * Which parts of a page the layers on it paint themselves. The annotation
3098
+ * layer paints the annotations, the form layer the form fields; while one is
3099
+ * on the page it says so here, and the page's picture (the render layer)
3100
+ * leaves that part out, so nothing is drawn twice and nobody passes a flag.
3101
+ *
3102
+ * Kept per page surface, by the page ref it hands its layers (`page.ref`):
3103
+ * each surface makes its own, and keeps it while it shows the page, through
3104
+ * zooms. Changes reach subscribers a microtask later, so a layer may say what
3105
+ * it paints from any point in its framework's lifecycle (React's insertion
3106
+ * effects included, which must not start updates); `painted()` is current at
3107
+ * once.
3108
+ */
3109
+ /** A part of a page a layer can paint instead of the page's picture. */
3110
+ type PagePart = 'annotations' | 'formFields';
3111
+ /** Which parts the layers on one page paint. */
3112
+ interface PaintedParts {
3113
+ readonly annotations: boolean;
3114
+ readonly formFields: boolean;
3115
+ }
3116
+ /** One page's layers: which parts they paint. */
3117
+ interface PageLayers {
3118
+ /** Say this layer paints `part`, until the function it returns is called. */
3119
+ paint(part: PagePart): () => void;
3120
+ /** Which parts are painted now: the same object for the same answer. */
3121
+ painted(): PaintedParts;
3122
+ /** Hear when `painted()` changes. Returns the unsubscribe. */
3123
+ subscribe(listener: () => void): () => void;
3124
+ }
3125
+ /** The layers of one page surface, by the page ref it hands its layers. */
3126
+ declare function pageLayersOf(pageRef: object): PageLayers;
3127
+ /** The parts a render layer was told to draw (`true`) or leave out (`false`). */
3128
+ interface PicturePartProps {
3129
+ readonly annotations?: boolean;
3130
+ readonly formFields?: boolean;
3131
+ }
3132
+ /** What the user may read of each part (the render plugin's `getLayerRights()`). */
3133
+ interface PartRights {
3134
+ readonly annotations: boolean;
3135
+ readonly formFields: boolean;
3136
+ }
3137
+ /** The picture's layer options, as the render plugin takes them. */
3138
+ interface PictureLayerOptions {
3139
+ includeAnnotations: boolean;
3140
+ includeFormFields: boolean;
3141
+ }
3142
+ /**
3143
+ * What the page's picture draws, as the render plugin's layer options: a part
3144
+ * the props name is drawn or left out as they say; any other part is drawn
3145
+ * when no layer on the page paints it and the user may read it. Both are
3146
+ * always given, so a picture that leaves the annotations to a layer still
3147
+ * draws the form fields.
3148
+ */
3149
+ declare function pictureLayerOptionsOf(painted: PaintedParts, props: PicturePartProps, may: PartRights): PictureLayerOptions;
3150
+ /**
3151
+ * The parts a picture draws that a layer on the page paints too, because the
3152
+ * props asked for them: drawn twice.
3153
+ */
3154
+ declare function partsDrawnTwice(painted: PaintedParts, props: PicturePartProps): PagePart[];
3155
+ //#endregion
3156
+ //#region src/page-surface.d.ts
3157
+ /** A box in its parent's pixels. */
3158
+ interface SurfaceBox {
3159
+ left: number;
3160
+ top: number;
3161
+ width: number;
3162
+ height: number;
3163
+ }
3164
+ /** The transform members a surface is laid out from; `PageTransform` satisfies it. */
3165
+ interface SurfaceTransform {
3166
+ readonly rotation: 0 | 90 | 180 | 270;
3167
+ readonly viewWidth: number;
3168
+ readonly viewHeight: number;
3169
+ readonly contentWidth: number;
3170
+ readonly contentHeight: number;
3171
+ }
3172
+ /** The three boxes of a page surface. */
3173
+ interface PageSurfaceLayout {
3174
+ /** The footprint and the reserved bands, in the surface's parent; the page chrome fills it. */
3175
+ outer: SurfaceBox;
3176
+ /** The drop shadow at the footprint, in the outer box. */
3177
+ shadow: SurfaceBox;
3178
+ /** The page's content box in the outer box, before it turns; its layers draw in it. */
3179
+ content: SurfaceBox;
3180
+ /** The CSS transform that turns the content box about its centre, or null when it's upright. */
3181
+ turn: string | null;
3182
+ }
3183
+ /**
3184
+ * Lay out a page surface. `footprint` is where the page's footprint sits in the surface's
3185
+ * parent (a Stage page's device-snapped `screenX`/`screenY`); the outer box sits one band further
3186
+ * out, so the content keeps its place. Without it the outer box is at 0, 0 (a `PageView`, placed
3187
+ * by the page around it).
3188
+ */
3189
+ declare function pageSurfaceLayout(transform: SurfaceTransform, frame: PageFrameShape, footprint?: {
3190
+ x: number;
3191
+ y: number;
3192
+ }): PageSurfaceLayout;
3193
+ /** What {@link stagePageDemand} reads from the Stage; `StageCapability` satisfies it. */
3194
+ interface StageDemandSource {
3195
+ listVisiblePages(): readonly {
3196
+ readonly ref: {
3197
+ readonly objectNumber: number;
3198
+ };
3199
+ readonly transform: {
3200
+ readonly deviceWidth: number;
3201
+ };
3202
+ readonly visibleRect: {
3203
+ x: number;
3204
+ y: number;
3205
+ width: number;
3206
+ height: number;
3207
+ };
3208
+ }[];
3209
+ }
3210
+ /** What a page's view wants rendered; the render plugin's `PageViewDemand` takes it. */
3211
+ interface StagePageDemand {
3212
+ desiredDeviceWidth: number;
3213
+ visibleRect: {
3214
+ x: number;
3215
+ y: number;
3216
+ width: number;
3217
+ height: number;
3218
+ };
3219
+ }
3220
+ /**
3221
+ * What a Stage page's view wants rendered, read live from the Stage when the render layer asks:
3222
+ * the page's visible part at its device width, or a zero box (want nothing) while the page is
3223
+ * off screen, at `offScreenDeviceWidth`. Visibility is the Stage's data, so no adapter derives
3224
+ * camera math or keeps a copy of it.
3225
+ */
3226
+ declare function stagePageDemand(stage: StageDemandSource, pageObjectNumber: number, offScreenDeviceWidth: number): StagePageDemand;
3227
+ /** The page a `PageView` shows, as its document's page list has it. */
3228
+ interface PageViewPage {
3229
+ readonly size: {
3230
+ readonly width: number;
3231
+ readonly height: number;
3232
+ };
3233
+ readonly rotation: 0 | 90 | 180 | 270;
3234
+ readonly userUnit: number;
3235
+ }
3236
+ /** The input of `pageTransform()` (`@embedpdf/core-geometry`) for a `PageView`. */
3237
+ interface PageViewTransformInput {
3238
+ pageSize: {
3239
+ width: number;
3240
+ height: number;
3241
+ };
3242
+ rotation: 0 | 90 | 180 | 270;
3243
+ scale: number;
3244
+ baseScale: number;
3245
+ dpr: number;
3246
+ }
3247
+ /**
3248
+ * What a `PageView` builds its page transform from: no camera, so the scale (view pixels per
3249
+ * point) is `width` over the page's width. The base scale is physical 100% on the web, a point
3250
+ * at 96/72 CSS pixels times the page's /UserUnit, so `transform.zoom` still means something: a
3251
+ * thumbnail-sized page reads as zoomed out. Without a page yet, a 1 × 1 placeholder.
3252
+ */
3253
+ declare function pageViewTransformInput(page: PageViewPage | null, width: number, dpr: number): PageViewTransformInput;
3254
+ //#endregion
3255
+ //#region src/page-pointer.d.ts
3256
+ interface PointerPoint {
3257
+ x: number;
3258
+ y: number;
3259
+ }
3260
+ /**
3261
+ * A multi-click counter. `pointerdown.detail` is 0 or 1 in several browsers, so clicks are
3262
+ * counted here from timing and distance: the standard double and triple click. Input is
3263
+ * normalized in the adapter; the hub and its handlers stay pure.
3264
+ */
3265
+ declare function createClickCounter(maxGapMs?: number, maxDistancePx?: number): (now: number, x: number, y: number) => number;
3266
+ /** What the listener reads from the page context when an event arrives. */
3267
+ interface PagePointerPage {
3268
+ readonly ref: PageRef;
3269
+ readonly transform: {
3270
+ readonly viewScale: number;
3271
+ readonly rotation: 0 | 90 | 180 | 270;
3272
+ readonly zoom: number;
3273
+ };
3274
+ toPagePoint(clientX: number, clientY: number): PointerPoint;
3275
+ }
3276
+ /** The sample the listener hands the hub: structurally the interaction plugin's `PointerSample`. */
3277
+ interface PagePointerSample {
3278
+ phase: 'down' | 'move' | 'up';
3279
+ viewport: PointerPoint;
3280
+ page: {
3281
+ ref: PageRef;
3282
+ point: PointerPoint;
3283
+ scale: number;
3284
+ rotation: 0 | 90 | 180 | 270;
3285
+ zoom: number;
3286
+ };
3287
+ project: (page: PageRef) => PointerPoint | null;
3288
+ modifiers: {
3289
+ shift: boolean;
3290
+ alt: boolean;
3291
+ ctrl: boolean;
3292
+ meta: boolean;
3293
+ };
3294
+ clickCount: number;
3295
+ pointerType: 'mouse' | 'pen' | 'touch';
3296
+ }
3297
+ /** What the listener needs from the interaction hub; `InteractionHostCapability` satisfies it. */
3298
+ interface PagePointerHub {
3299
+ dispatchPointer(sample: PagePointerSample): void;
3300
+ }
3301
+ /**
3302
+ * Listen to the pointer on `element` for the page `page()` gives when an event arrives (a zoom
3303
+ * changes it, and a drag carries on through it), and dispatch every event to `hub`. A press
3304
+ * starts on the element; while it lasts the window listens, so a drag goes on outside the page.
3305
+ * Returns the function that stops listening.
3306
+ */
3307
+ declare function attachPagePointer(element: HTMLElement, hub: PagePointerHub, page: () => PagePointerPage): () => void;
3308
+ //#endregion
3309
+ //#region src/scrollbar.d.ts
3310
+ /**
3311
+ * A Stage scrollbar's geometry and its press handling, shared by every framework's `Scrollbar`.
3312
+ *
3313
+ * The Stage gives its scroll position as a scrolling element has it (`scrollTop`,
3314
+ * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels). From that and the
3315
+ * measured track this module draws the thumb, and turns presses into Stage calls the way a native
3316
+ * bar does: a thumb drag keeps the point it grabbed under the pointer, and a press on the track
3317
+ * pages toward the pointer, repeating while held and stopping when the thumb reaches it. A drag
3318
+ * is frozen at the press (grab point, travel, range) and applied as relative pans: absolute in
3319
+ * feel on a bounded Stage, and steady on an unbounded one, whose range moves under the pointer.
3320
+ *
3321
+ * The adapters keep their reactive glue: the track's size, auto-hide, hover and the markup.
3322
+ */
3323
+ /** The scroll position a Stage reports (`ScrollMetrics` from the stage plugin satisfies it). */
3324
+ interface ScrollbarMetrics {
3325
+ readonly scrollLeft: number;
3326
+ readonly scrollTop: number;
3327
+ readonly scrollWidth: number;
3328
+ readonly scrollHeight: number;
3329
+ readonly clientWidth: number;
3330
+ readonly clientHeight: number;
3331
+ }
3332
+ /** The bar along one axis, in track pixels. */
3333
+ interface ScrollbarLayout {
3334
+ /** The view's length along the axis. */
3335
+ client: number;
3336
+ /** How far the view is scrolled. */
3337
+ offset: number;
3338
+ /** The farthest it can scroll. */
3339
+ maxOffset: number;
3340
+ thumbLength: number;
3341
+ /** How far the thumb can move along the track. */
3342
+ travel: number;
3343
+ thumbPosition: number;
3344
+ }
3345
+ /**
3346
+ * The thumb from the metrics and the track's length: one formula for drawing, dragging and
3347
+ * paging, so they never disagree. The thumb never gets shorter than `minThumbSize`, and its
3348
+ * position stays proportional over the travel that leaves.
3349
+ */
3350
+ declare function scrollbarLayout(metrics: ScrollbarMetrics, vertical: boolean, trackPx: number, minThumbSize: number): ScrollbarLayout;
3351
+ /** The Stage calls a scrollbar makes; `StageCapability` satisfies it. */
3352
+ interface ScrollbarStage {
3353
+ scrollTo(position: {
3354
+ top?: number;
3355
+ left?: number;
3356
+ }): void;
3357
+ scrollBy(delta: {
3358
+ top?: number;
3359
+ left?: number;
3360
+ }): void;
3361
+ panBy(dx: number, dy: number): void;
3362
+ }
3363
+ /** The bar as it is at a press: what the press handlers read. */
3364
+ interface ScrollbarTrack {
3365
+ readonly stage: ScrollbarStage;
3366
+ readonly vertical: boolean;
3367
+ /** The track element: pointer positions are measured from its start. */
3368
+ readonly element: Element;
3369
+ /** The layout drawn now. */
3370
+ readonly layout: ScrollbarLayout;
3371
+ /** The layout from the Stage's metrics as they are now, for each paging step. */
3372
+ liveLayout(): ScrollbarLayout;
3373
+ }
3374
+ /**
3375
+ * The pointer event a press handler reads: a DOM `PointerEvent`, or a framework's own event
3376
+ * with the same members (React's), so `stopPropagation` and `currentTarget` mean what the
3377
+ * framework's handlers mean.
3378
+ */
3379
+ interface ScrollbarPointerEvent {
3380
+ readonly button: number;
3381
+ readonly pointerId: number;
3382
+ readonly clientX: number;
3383
+ readonly clientY: number;
3384
+ readonly target: EventTarget | null;
3385
+ readonly currentTarget: EventTarget | null;
3386
+ preventDefault(): void;
3387
+ stopPropagation(): void;
3388
+ }
3389
+ /** A scrollbar's presses: wire the thumb's and the track's pointer events to these. */
3390
+ interface ScrollbarPresses {
3391
+ /** A press on the thumb starts a drag. */
3392
+ pressThumb(event: ScrollbarPointerEvent, track: ScrollbarTrack): void;
3393
+ /**
3394
+ * A press on the track itself: `'page'` steps 90% of a view toward the pointer, repeats while
3395
+ * held and stops at the pointer; `'jump'` puts the thumb under the pointer and goes on as a
3396
+ * drag. A press that reached the track from the thumb is the thumb's.
3397
+ */
3398
+ pressTrack(event: ScrollbarPointerEvent, track: ScrollbarTrack, mode: 'page' | 'jump'): void;
3399
+ /** A pointer move on the track (pointer capture sends them there): drags, and paging's target. */
3400
+ move(event: ScrollbarPointerEvent, track: ScrollbarTrack): void;
3401
+ /** The press ended: no drag, no repeat. Also call it when the bar goes away. */
3402
+ release(): void;
3403
+ }
3404
+ /**
3405
+ * One scrollbar's press handling. `onDragging` hears when a drag starts and ends, for the bar's
3406
+ * `data-dragging` and to keep it shown.
3407
+ */
3408
+ declare function createScrollbarPresses(onDragging: (dragging: boolean) => void): ScrollbarPresses;
3409
+ //#endregion
3410
+ //#region src/orientation.d.ts
3411
+ /**
3412
+ * Which way a row of controls runs, and which way its text reads: what a
3413
+ * toolbar measures along, and what its arrow keys move along.
3414
+ */
3415
+ /** A row of controls side by side, or one under another (`aria-orientation`'s values). */
3416
+ type Orientation = 'horizontal' | 'vertical';
3417
+ /** The writing direction an element lays out in: its computed CSS `direction`. */
3418
+ declare function directionOf(element: Element): 'ltr' | 'rtl';
3419
+ //#endregion
3420
+ //#region src/toolbar-measure.d.ts
3421
+ /** The keys the measurement layer reports lengths under, the ones the metrics look up. */
3422
+ declare const toolbarMeasureKey: {
3423
+ /** A unit (`NormalizedUnit.key`) at one variant. */
3424
+ readonly unit: (unitKey: string, variant: string) => string;
3425
+ /** A group in its collapsed form. */
3426
+ readonly group: (groupId: string) => string;
3427
+ /** A shed group's disclosure trigger, measured with the whole group behind it. */
3428
+ readonly groupTrigger: (groupId: string) => string;
3429
+ /** The "More" button. */
3430
+ readonly overflowTrigger: "t:";
3431
+ };
3432
+ /** The lengths a fit needs: the structural twin of core-ui's `FitMetrics`. */
3433
+ interface ToolbarFitMetrics {
3434
+ unit(key: string, variant: string): number | undefined;
3435
+ groupCollapsed(groupId: string): number | undefined;
3436
+ groupTrigger(groupId: string): number | undefined;
3437
+ readonly overflowTrigger: number;
3438
+ readonly gap: number;
3439
+ readonly separator: number;
3440
+ }
3441
+ /**
3442
+ * The measured lengths of one toolbar, kept apart per orientation: a toolbar
3443
+ * turned on its side never fits with the other axis's numbers.
3444
+ */
3445
+ interface ToolbarLengths {
3446
+ /**
3447
+ * A measured element's length along `orientation`, under its
3448
+ * {@link toolbarMeasureKey}. True when it changed by more than half a pixel:
3449
+ * render (and solve) again.
3450
+ */
3451
+ report(orientation: Orientation, key: string, length: number): boolean;
3452
+ /**
3453
+ * The lengths along `orientation` as fit metrics, for items `gap` px apart
3454
+ * and separators `separatorWidth` px thick (a separator's width is its
3455
+ * thickness, across a horizontal bar or down a vertical one). A separator is
3456
+ * one more flex child: its thickness plus one gap. Until the "More" button
3457
+ * is measured it counts as 32 px.
3458
+ */
3459
+ metrics(orientation: Orientation, gap: number, separatorWidth: number): ToolbarFitMetrics;
3460
+ }
3461
+ declare function createToolbarLengths(): ToolbarLengths;
3462
+ /**
3463
+ * Report an element's width (its border box) now and whenever it resizes. Put
3464
+ * it on every element of a horizontal toolbar's measurement layer, and on a
3465
+ * custom slot's `<slot>` socket, which is measured live where it renders.
3466
+ * Returns the detach.
3467
+ */
3468
+ declare function observeWidth(element: Element, onWidth: (width: number) => void): () => void;
3469
+ /** {@link observeWidth} for a vertical toolbar: an element's border-box height. */
3470
+ declare function observeHeight(element: Element, onHeight: (height: number) => void): () => void;
3471
+ /** An element's length along a toolbar: {@link observeWidth} or {@link observeHeight}. */
3472
+ declare function observeLength(element: Element, orientation: Orientation, onLength: (length: number) => void): () => void;
3473
+ /**
3474
+ * Report the width a toolbar has to fill (its content box) now and whenever
3475
+ * it changes by more than half a pixel. Returns the detach.
3476
+ */
3477
+ declare function observeContentWidth(element: HTMLElement, onWidth: (width: number) => void): () => void;
3478
+ /**
3479
+ * The height a vertical toolbar has to fill, like {@link observeContentWidth}.
3480
+ * The toolbar fits the height it's given, so its container needs one.
3481
+ */
3482
+ declare function observeContentHeight(element: HTMLElement, onHeight: (height: number) => void): () => void;
3483
+ /** The room along a toolbar: {@link observeContentWidth} or {@link observeContentHeight}. */
3484
+ declare function observeContentLength(element: HTMLElement, orientation: Orientation, onLength: (length: number) => void): () => void;
3485
+ //#endregion
3486
+ //#region src/focusable.d.ts
3487
+ /**
3488
+ * Which elements take keyboard focus, for the keyboard models (a toolbar's
3489
+ * stops, a menu's items, a dialog's tab order). Decided from the markup only,
3490
+ * never from layout, so it answers the same before the first paint: `hidden`,
3491
+ * `inert` and `disabled` count; a `display: none` from a stylesheet doesn't.
3492
+ */
3493
+ /** Whether an element can take focus now: focusable markup, not disabled, not hidden or inert. */
3494
+ declare function canTakeFocus(element: Element): boolean;
3495
+ /** Whether Tab stops at an element: it takes focus and its `tabIndex` isn't negative. */
3496
+ declare function isTabbable(element: Element): element is HTMLElement;
3497
+ /**
3498
+ * The elements inside `root` that can take focus, in the order they show: through a `<slot>`,
3499
+ * the page's elements the browser shows there (a web component's slotted children).
3500
+ */
3501
+ declare function focusablesIn(root: ParentNode): HTMLElement[];
3502
+ /** The elements inside `root` that Tab stops at, in document order. */
3503
+ declare function tabbablesIn(root: ParentNode): HTMLElement[];
3504
+ /**
3505
+ * The element that has focus, looking into shadow roots: the document's
3506
+ * `activeElement` is only the outermost shadow host. Null when nothing has
3507
+ * focus (the body).
3508
+ */
3509
+ declare function deepActiveElementOf(doc: Document): Element | null;
3510
+ /**
3511
+ * Whether the element with focus is `container` or inside it, as the page shows it: through
3512
+ * shadow roots, and from an element slotted into a shadow root to its slot, so a control
3513
+ * slotted into a viewer counts as inside the viewer, as its keys do.
3514
+ */
3515
+ declare function containsFocus(container: Element): boolean;
3516
+ /**
3517
+ * Whether a control says it can't be used now, through `disabled` or
3518
+ * `aria-disabled="true"`: the keyboard models pass over it.
3519
+ */
3520
+ declare function isUnavailable(element: Element): boolean;
3521
+ /**
3522
+ * Whether a focused control uses `key` itself, so a keyboard model around it
3523
+ * must leave the key alone: a text field moves its caret with Left, Right,
3524
+ * Home and End; a select changes with Up and Down; a slider takes them all.
3525
+ */
3526
+ declare function usesKey(element: Element, key: string): boolean;
3527
+ //#endregion
3528
+ //#region src/roving-focus.d.ts
3529
+ /** Where a key moves focus in a row. */
3530
+ type RovingMove = 'next' | 'previous' | 'first' | 'last';
3531
+ /** The move `key` asks for in a row running `orientation`, or null for a key the row leaves alone. */
3532
+ declare function rovingMoveOf(key: string, orientation: Orientation, direction: 'ltr' | 'rtl'): RovingMove | null;
3533
+ /**
3534
+ * The index `move` lands on among `count` stops, from `current` (-1 for
3535
+ * none), passing over the stops `available` rejects. With `loop`, next and
3536
+ * previous wrap around the ends; without it they stop there. -1 when there is
3537
+ * nowhere to go.
3538
+ */
3539
+ declare function rovingTargetOf(move: RovingMove, current: number, count: number, available: (index: number) => boolean, loop: boolean): number;
3540
+ interface RovingFocusOptions {
3541
+ /** Which arrows move: Left and Right for `'horizontal'`, Up and Down for `'vertical'`. */
3542
+ readonly orientation: Orientation;
3543
+ /** The stops, in order; read again on every key and every {@link RovingFocus.refresh}. */
3544
+ readonly stops: () => readonly HTMLElement[];
3545
+ /** The writing direction. Default: the container's computed `direction`, read on each key. */
3546
+ readonly direction?: () => 'ltr' | 'rtl';
3547
+ /** Whether next and previous wrap around at the ends. Default true. */
3548
+ readonly loop?: boolean;
3549
+ }
3550
+ /** A row's roving focus, from {@link attachRovingFocus}. */
3551
+ interface RovingFocus {
3552
+ /**
3553
+ * Put the one tab stop back after the stops changed: on the stop that had
3554
+ * focus last while it's still there and usable, else on the first usable
3555
+ * one. Call it after every render that can add, remove or disable a stop.
3556
+ */
3557
+ refresh(): void;
3558
+ /** Stop handling keys and focus. The stops keep their `tabIndex`. */
3559
+ detach(): void;
3560
+ }
3561
+ /**
3562
+ * Make the stops of `container` one tab stop with arrow keys between them.
3563
+ * Keys count only while a stop itself has focus, so keys inside a menu or
3564
+ * another control the row holds are theirs; a key with Alt, Control, Meta or
3565
+ * Shift, or one a handler already took (`defaultPrevented`), is left alone.
3566
+ */
3567
+ declare function attachRovingFocus(container: HTMLElement, options: RovingFocusOptions): RovingFocus;
3568
+ //#endregion
3569
+ //#region src/toolbar-focus.d.ts
3570
+ /** The attribute on the element around each item of a toolbar's live row. */
3571
+ declare const TOOLBAR_ITEM_ATTRIBUTE = "data-epdf-toolbar-item";
3572
+ /**
3573
+ * A toolbar's stops: the first element of each item that takes focus, in
3574
+ * order. A menu an item holds open is not part of its stop.
3575
+ */
3576
+ declare function toolbarStopsOf(toolbar: ParentNode): HTMLElement[];
3577
+ /** Arrow keys between a toolbar's items, along its orientation; `refresh()` after each render. */
3578
+ declare function attachToolbarFocus(toolbar: HTMLElement, orientation: Orientation): RovingFocus;
3579
+ //#endregion
3580
+ //#region src/menu-keys.d.ts
3581
+ /**
3582
+ * The item typeahead lands on: the first one from `current` on, wrapping, that
3583
+ * is available and whose label starts with `query` (any case). A one-letter
3584
+ * query starts after `current`, so typing the same letter again moves on, and
3585
+ * a query of one letter repeated ("ppp") cycles like that letter. -1 when no
3586
+ * item matches.
3587
+ */
3588
+ declare function typeaheadMatchOf(labels: readonly string[], current: number, query: string, available: (index: number) => boolean): number;
3589
+ /**
3590
+ * The words typeahead matches an item by: its `aria-label`, else its text,
3591
+ * without the marks a row may start with (a bullet, a check, an icon glyph).
3592
+ */
3593
+ declare function menuItemLabelOf(item: Element): string;
3594
+ interface MenuKeysOptions {
3595
+ /** The button that opened the menu: it says it's expanded, and gets focus back. */
3596
+ readonly trigger: HTMLElement | null;
3597
+ /** Close the menu; removing it is the caller's. */
3598
+ readonly close: () => void;
3599
+ /** The items, in order. Default: the menu's `menuitem`, `menuitemradio` and `menuitemcheckbox` elements. */
3600
+ readonly items?: () => readonly HTMLElement[];
3601
+ /** Which item takes focus when the menu opens. Default `'first'`. */
3602
+ readonly initialFocus?: 'first' | 'last' | 'none';
3603
+ }
3604
+ /**
3605
+ * Drive an open `menu` from the keyboard until the returned detach runs.
3606
+ * Detaching (the menu closed) gives focus back to the trigger when focus was
3607
+ * in the menu or went nowhere, and never when the user moved it elsewhere.
3608
+ */
3609
+ declare function attachMenuKeys(menu: HTMLElement, options: MenuKeysOptions): () => void;
3610
+ //#endregion
3611
+ //#region src/focus-trap.d.ts
3612
+ /**
3613
+ * Move focus into `container` unless it's there already: to `initialFocus`, else its first
3614
+ * control Tab stops at, else the container itself. Focus already inside stays where it is, so a
3615
+ * control that took focus as it appeared (a search field asking for the cursor) keeps it.
3616
+ */
3617
+ declare function moveFocusInto(container: HTMLElement, initialFocus?: HTMLElement | null): void;
3618
+ interface FocusTrapOptions {
3619
+ /**
3620
+ * What takes focus when the trap starts, unless focus is inside already. Default: the first
3621
+ * control Tab stops at.
3622
+ */
3623
+ readonly initialFocus?: HTMLElement | null;
3624
+ /** Whether focus goes back to where it was when the trap is released. Default true. */
3625
+ readonly returnFocus?: boolean;
3626
+ }
3627
+ /** Trap focus in `container` until the returned release runs. */
3628
+ declare function attachFocusTrap(container: HTMLElement, options?: FocusTrapOptions): () => void;
3629
+ //#endregion
3630
+ //#region src/surface-focus.d.ts
3631
+ /**
3632
+ * Putting the cursor in a panel when it asks. The shell raises a surface's `focusRequest` on
3633
+ * every `open(id, { focus: true })` and clears it when the surface closes; the element that
3634
+ * follows it takes focus whenever the request changes to a new value above 0, and when it starts
3635
+ * following while the request is up (the panel opened, or its code loaded, because of it). The
3636
+ * adapters' surface-focus hooks bind a follower to their reactivity; the rule lives here.
3637
+ */
3638
+ /** How an element takes focus when its surface asks. */
3639
+ interface SurfaceFocusOptions {
3640
+ /** Select the text of an input or a text area as it takes focus, so typing replaces it. */
3641
+ readonly select?: boolean;
3642
+ }
3643
+ /** One element following one surface's focus requests. */
3644
+ interface SurfaceFocusFollower {
3645
+ /**
3646
+ * Take the surface's request as it is now. `shell` is the capability it was read from (null
3647
+ * without a document): another document's shell starts over, as a new mount does. Without an
3648
+ * element yet, a request waits for it.
3649
+ */
3650
+ follow(element: HTMLElement | null, shell: object | null, request: number, options?: SurfaceFocusOptions): void;
3651
+ }
3652
+ declare function createSurfaceFocusFollower(): SurfaceFocusFollower;
3653
+ //#endregion
3654
+ //#region src/standard-commands-platform.d.ts
3655
+ /** What the standard commands need from the browser: the structural twin of `StandardCommandsPlatform`. */
3656
+ interface StandardCommandsBrowser {
3657
+ copySelection: typeof copySelection;
3658
+ saveFile(bytes: Uint8Array, fileName: string): void;
3659
+ }
3660
+ declare const standardCommandsBrowser: StandardCommandsBrowser;
3661
+ //#endregion
3662
+ export { type ActionsUiAdapterShape, type ActionsUiEffectContext, type ActionsUiOrigin, type AnchorTarget, type AnchoredFit, type AnchoredOptions, type AnchoredPlacement, type AnchoredPoint, type AnchoredPosition, type AnchoredRect, type AnchoredSide, type AnchoredSize, type AnnotationAnchorShape, type AnnotationChromePaint, type AnnotationDrawing, type AnnotationLookFrame, type AppearancePicture, type AppearanceUrl, type AttachSelectionHandleOptions, type BehaviorRendererEntry, type ByteStore, type ChromeInPixels, type ChromeLine, type ChromeLinePaint, type ChromeNodeShape, type ChromePaintSettings, type ChromeShapePaint, type ClickDetector, type ClientBox, type ClientProjectedPage, type ClipboardSelectionSource, type CommandShortcutOptions, type CommentPageLayout, type CommentThreadPage, type CreationDraftAnchorShape, type CssFont, type DefaultActionsUiAdapterOptions, EPDF_VARIABLES, type EditorDocumentFactory, type EditorElement, type EditorNode, type EditorPosition, type EditorRoot, type EditorStyle, type EpdfCssOnlyVariable, type EpdfFollowedVariable, type EpdfSettingVariable, type EpdfTheme, type EpdfThemeVariables, type EpdfTranslucentVariable, type EpdfVariable, type EpdfVariableDefinition, FORM_CONTROL_FILL, type FieldLook, type FieldTextStyle, type FilePickerPort, type FocusTrapOptions, type FormColorSettings, type FormColors, type FormControlFill, type FormFocusRingStyle, type FrameBox, type FrameFraction, type FramePixels, type FreeTextBox, type FreeTextBoxStyle, type IndexedDbByteStoreOptions, LEGIBLE_FONT_PX, type LineModel, type LinkActionDispatcher, type LinkActionSource, type LinkAnchor, type LinkAnchorEvent, type LinkAnchorShape, type LinkEventShape, type LinkHoverPump, type ListBoxControlStyle, type ListBoxStyle, type LookRendererEntry, type LookState, type MenuKeysOptions, type MountableFontProgram, type ObjectUrl, type ObjectUrlImageSource, type OptimisticSelection, type Orientation, type OwnTextBox, type OwnTextBoxStyle, type PageBoundsAnchor, type PageClientSpace, type PageContext, type PageContextTransform, type PageFrameShape, type PageLayerTransform, type PageLayers, type PagePart, type PagePoint, type PagePointerHub, type PagePointerPage, type PagePointerSample, type PageQuad, type PageRef, type PageSurfaceLayout, type PageToPixels, type PageViewEnv, type PageViewPage, type PageViewTransformInput, type PaintedImageCallbacks, type PaintedParts, type PartRights, type PickFileOptions, type PictureBytes, type PictureLayerOptions, type PictureOrPainting, type PicturePartProps, type PixelBox, type PixelRect, type ProjectedTransform, type RendererBehaviorRegistry, type RendererEntry, type RendererInteractiveContext, type RichTextEditorAlign, type RichTextEditorBinding, type RichTextEditorCommand, type RichTextEditorDecoration, type RichTextEditorDocument, type RichTextEditorHost, type RichTextEditorParagraph, type RichTextEditorProps, type RichTextEditorRange, type RichTextEditorRun, type RichTextEditorScript, type RichTextEditorStyle, type RotationAnchorShape, type RovingFocus, type RovingFocusOptions, type RovingMove, type ScenePaint, type SceneShapeNode, type ScrollbarLayout, type ScrollbarMetrics, type ScrollbarPointerEvent, type ScrollbarPresses, type ScrollbarStage, type ScrollbarTrack, type SearchHighlightOptions, type SearchHighlightPiece, type SearchHitShape, type SelectOptions, type SelectionAnchorShape, type SelectionClipboardOptions, type SelectionEndpointShape, type SelectionEndpointsShape, type SelectionHandleSession, type ShortcutCommands, type ShortcutRefusal, type ShownUrls, type StageDemandSource, type StageGestureHost, type StageGestureOptions, type StageGestureSink, type StagePageDemand, type StagePointerKind, type StageProjection, type StageSurfaceHost, type StageSurfaceHub, type StageSurfaceOptions, type StageSurfaceSample, type StageWheelSample, type StandardCommandsBrowser, type StyleRecord, type SurfaceBox, type SurfaceFocusFollower, type SurfaceFocusOptions, type SurfaceTransform, type SvgCursorOptions, type SvgShape, type SvgShapeTag, type SvgShapesOptions, TOOLBAR_ITEM_ATTRIBUTE, type TextBoxCss, type TextBoxEditor, type TextBoxEditorAnnotation, type TextBoxEditorFollower, type TextBoxEditorItem, type TextBoxStyle, type TextEditorLayout, type TextFieldDrafts, type TextFieldEditor, type TextFieldEditorState, type TextFieldEditorStyle, type TextFieldStyle, type ToggleForm, type ToolCursorImage, type ToolCursorSpec, type ToolbarFitMetrics, type ToolbarLengths, type TurnedBox, type ViewProjector, type WebClock, type WebFontMetrics, type WebPlatformFeedback, type WebPrintSheet, type WebPrinter, type WebStagedPrint, type WheelSample, type WidgetBoxStyle, type WidgetEventKind, anchoredViewOf, annotationChromePaint, annotationDrawingOf, applyFirstLineShift, attachFocusTrap, attachMenuKeys, attachPagePointer, attachRichTextEditor, attachRovingFocus, attachSelectionHandle, attachTextBoxEditor, attachToolbarFocus, bakedAppearanceOf, bindCommandShortcuts, bindPaintedImage, bindWidgetEvents, browserClock, browserPrinter, canTakeFocus, chromeInPixels, clientPageProjector, computeReleaseVelocity, containsFocus, copySelection, createClickCounter, createClickDetector, createDefaultActionsUiAdapter, createFontMounts, createOptimisticSelection, createScrollbarPresses, createShownUrls, createStageGestureController, createStageSurface, createSurfaceFocusFollower, createTextBoxEditorFollower, createTextFieldEditor, createToolbarLengths, cssFontOf, cssText, deepActiveElementOf, directionOf, editingTextKeyOf, enrichCommentThreads, epdfThemeVariables, firstLineShiftFor, fitAnchoredRect, focusablesIn, fontProgramsOf, formColorsOf, formFocusRingStyleOf, frameInPixels, freeTextBoxOf, ghostOpacity, hoverLink, indexedDbByteStore, installFilePickerProvider, isMacPlatform, isModifiedClick, isTabbable, isUnavailable, isUprightQuad, isolatePointerDown, isolateWheel, layerTextBoxesOf, legibleScaleOf, legibleShrinkOf, lineModelFor, linkActivateContextOf, linkAnchorOf, linkHrefOf, listBoxControlStyleOf, listBoxStyleOf, livePageContext, loadAppearanceUrls, loadObjectUrl, lookFrameOf, lookRendererFor, makePageContext, menuItemLabelOf, mixAccent, mountFontProgram, mountWebFont, moveFocusInto, navigableLinksOf, objectUrlOf, observeAnchoredFit, observeClientGeometry, observeContentHeight, observeContentLength, observeContentWidth, observeHeight, observeLength, observeOutsidePress, observeWidth, offsetOfPosition, openExternalUri, ownTextBoxOf, pageClientSpace, pageLayersOf, pageSurfaceLayout, pageViewTransformInput, paint, paintDefault, partsDrawnTwice, pickFile, pickImageFile, pickRequestedFile, pictureLayerOptionsOf, pictureOrPainting, positionAnchoredRect, positionOfOffset, pressToggle, projectAnchoredTarget, quadInPixels, rasterInFrame, rectInPixels, registerRendererBehaviors, renderRichText, rendererBehaviorId, rovingMoveOf, rovingTargetOf, sameAnchoredFit, sameAnnotationAnchor, sameCreationDraftAnchor, samePageBounds, sameRotationAnchor, sameSelectionAnchor, sameSelectionEndpoints, sanitizeExternalUri, saveFile, sceneViewBox, scrollbarLayout, searchHighlightsOf, sendLinkEvent, serializeRichText, showSelectedOptions, shownAppearance, stagePageDemand, stageViewProjector, standardCommandsBrowser, styleDeltaOf, svgCursor, svgPoints, svgShapesOf, tabbablesIn, textBoxEditorScaleOf, textFieldEditorStyleOf, textFieldFontPxOf, textFieldStyleOf, toolCursorsOf, toolbarMeasureKey, toolbarStopsOf, typeaheadMatchOf, usesKey, vibrationFeedback, webFontMetrics, wheelZoomFactor, widgetBoxStyleOf, wireSelectionClipboard, wkFeedback };
852
3663
  //# sourceMappingURL=index.d.ts.map