@embedpdf/angular 3.0.0-next.14 → 3.0.0-next.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +309 -0
  2. package/dist/README.md +309 -0
  3. package/dist/actions/index.d.ts +34 -16
  4. package/dist/anchored/index.d.ts +67 -0
  5. package/dist/annotation/index.d.ts +602 -0
  6. package/dist/attachment/index.d.ts +29 -0
  7. package/dist/commands/index.d.ts +109 -0
  8. package/dist/fesm2022/embedpdf-angular-actions.mjs +117 -36
  9. package/dist/fesm2022/embedpdf-angular-actions.mjs.map +1 -1
  10. package/dist/fesm2022/embedpdf-angular-anchored.mjs +148 -0
  11. package/dist/fesm2022/embedpdf-angular-anchored.mjs.map +1 -0
  12. package/dist/fesm2022/embedpdf-angular-annotation.mjs +1933 -0
  13. package/dist/fesm2022/embedpdf-angular-annotation.mjs.map +1 -0
  14. package/dist/fesm2022/embedpdf-angular-attachment.mjs +67 -0
  15. package/dist/fesm2022/embedpdf-angular-attachment.mjs.map +1 -0
  16. package/dist/fesm2022/embedpdf-angular-commands.mjs +235 -0
  17. package/dist/fesm2022/embedpdf-angular-commands.mjs.map +1 -0
  18. package/dist/fesm2022/embedpdf-angular-form.mjs +1074 -0
  19. package/dist/fesm2022/embedpdf-angular-form.mjs.map +1 -0
  20. package/dist/fesm2022/embedpdf-angular-history.mjs +54 -0
  21. package/dist/fesm2022/embedpdf-angular-history.mjs.map +1 -0
  22. package/dist/fesm2022/embedpdf-angular-i18n.mjs +100 -0
  23. package/dist/fesm2022/embedpdf-angular-i18n.mjs.map +1 -0
  24. package/dist/fesm2022/embedpdf-angular-interaction.mjs +157 -0
  25. package/dist/fesm2022/embedpdf-angular-interaction.mjs.map +1 -0
  26. package/dist/fesm2022/embedpdf-angular-link.mjs +306 -0
  27. package/dist/fesm2022/embedpdf-angular-link.mjs.map +1 -0
  28. package/dist/fesm2022/embedpdf-angular-measurement.mjs +106 -0
  29. package/dist/fesm2022/embedpdf-angular-measurement.mjs.map +1 -0
  30. package/dist/fesm2022/embedpdf-angular-metadata.mjs +77 -0
  31. package/dist/fesm2022/embedpdf-angular-metadata.mjs.map +1 -0
  32. package/dist/fesm2022/embedpdf-angular-outline.mjs +76 -0
  33. package/dist/fesm2022/embedpdf-angular-outline.mjs.map +1 -0
  34. package/dist/fesm2022/embedpdf-angular-page-edit.mjs +68 -0
  35. package/dist/fesm2022/embedpdf-angular-page-edit.mjs.map +1 -0
  36. package/dist/fesm2022/embedpdf-angular-page-view.mjs +297 -0
  37. package/dist/fesm2022/embedpdf-angular-page-view.mjs.map +1 -0
  38. package/dist/fesm2022/embedpdf-angular-redaction.mjs +93 -0
  39. package/dist/fesm2022/embedpdf-angular-redaction.mjs.map +1 -0
  40. package/dist/fesm2022/embedpdf-angular-render.mjs +331 -69
  41. package/dist/fesm2022/embedpdf-angular-render.mjs.map +1 -1
  42. package/dist/fesm2022/embedpdf-angular-runtime.mjs +1151 -390
  43. package/dist/fesm2022/embedpdf-angular-runtime.mjs.map +1 -1
  44. package/dist/fesm2022/embedpdf-angular-search.mjs +251 -0
  45. package/dist/fesm2022/embedpdf-angular-search.mjs.map +1 -0
  46. package/dist/fesm2022/embedpdf-angular-selection.mjs +477 -0
  47. package/dist/fesm2022/embedpdf-angular-selection.mjs.map +1 -0
  48. package/dist/fesm2022/embedpdf-angular-shell.mjs +177 -0
  49. package/dist/fesm2022/embedpdf-angular-shell.mjs.map +1 -0
  50. package/dist/fesm2022/embedpdf-angular-signature.mjs +120 -0
  51. package/dist/fesm2022/embedpdf-angular-signature.mjs.map +1 -0
  52. package/dist/fesm2022/embedpdf-angular-stage.mjs +688 -288
  53. package/dist/fesm2022/embedpdf-angular-stage.mjs.map +1 -1
  54. package/dist/fesm2022/embedpdf-angular-stamp.mjs +169 -0
  55. package/dist/fesm2022/embedpdf-angular-stamp.mjs.map +1 -0
  56. package/dist/fesm2022/embedpdf-angular-testing.mjs +89 -0
  57. package/dist/fesm2022/embedpdf-angular-testing.mjs.map +1 -0
  58. package/dist/fesm2022/embedpdf-angular-toolbar.mjs +1123 -0
  59. package/dist/fesm2022/embedpdf-angular-toolbar.mjs.map +1 -0
  60. package/dist/fesm2022/embedpdf-angular-view-manager.mjs +69 -0
  61. package/dist/fesm2022/embedpdf-angular-view-manager.mjs.map +1 -0
  62. package/dist/fesm2022/embedpdf-angular.mjs +25 -3
  63. package/dist/fesm2022/embedpdf-angular.mjs.map +1 -1
  64. package/dist/form/index.d.ts +79 -0
  65. package/dist/history/index.d.ts +28 -0
  66. package/dist/i18n/index.d.ts +52 -0
  67. package/dist/index.d.ts +22 -0
  68. package/dist/interaction/index.d.ts +58 -0
  69. package/dist/link/index.d.ts +77 -0
  70. package/dist/measurement/index.d.ts +46 -0
  71. package/dist/metadata/index.d.ts +51 -0
  72. package/dist/outline/index.d.ts +32 -0
  73. package/dist/page-edit/index.d.ts +27 -0
  74. package/dist/page-view/index.d.ts +94 -0
  75. package/dist/redaction/index.d.ts +38 -0
  76. package/dist/render/index.d.ts +45 -6
  77. package/dist/runtime/index.d.ts +579 -199
  78. package/dist/search/index.d.ts +71 -0
  79. package/dist/selection/index.d.ts +141 -0
  80. package/dist/shell/index.d.ts +88 -0
  81. package/dist/signature/index.d.ts +45 -0
  82. package/dist/stage/index.d.ts +242 -141
  83. package/dist/stamp/index.d.ts +58 -0
  84. package/dist/testing/index.d.ts +53 -0
  85. package/dist/toolbar/index.d.ts +257 -0
  86. package/dist/view-manager/index.d.ts +23 -0
  87. package/package.json +145 -14
@@ -0,0 +1,109 @@
1
+ import * as _embedpdf_plugin_commands from '@embedpdf/plugin-commands';
2
+ import { BoundCommand, ExecuteResult, CommandId, CommandRefusal, CommandsConfig } from '@embedpdf/plugin-commands';
3
+ export * from '@embedpdf/plugin-commands';
4
+ import * as i0 from '@angular/core';
5
+ import { Signal } from '@angular/core';
6
+ import * as _embedpdf_angular_runtime from '@embedpdf/angular/runtime';
7
+ import { EmbedPdfFeature } from '@embedpdf/angular/runtime';
8
+
9
+ /**
10
+ * The commands every viewer has: zoom, pages, view rotation, `tool:<id>` for every tool, copy,
11
+ * delete, download and print, with shortcuts and labels in EmbedPDF's eight languages. Spread
12
+ * them into `withCommands({ commands })`.
13
+ */
14
+ declare const standardCommands: readonly (_embedpdf_plugin_commands.CommandDef | _embedpdf_plugin_commands.CommandFamily)[];
15
+ /** A shortcut as a tooltip shows it, for this platform: `'⌘K'` on a Mac, `'Ctrl+K'` elsewhere. */
16
+ declare function formatShortcut(shortcut: string, options?: {
17
+ isMac?: boolean;
18
+ }): string;
19
+ declare const EpdfCommands_base: _embedpdf_angular_runtime.PluginServiceClass<_embedpdf_plugin_commands.CommandsCapability, Record<never, never>, "execute" | "registerCommand" | "registerCommands" | "hasCommand" | "getCommand" | "listCommandIds" | "resolveCommand" | "listCommands" | "searchCommands" | "listShortcuts" | "canExecute" | "getDisabledCategories" | "isCategoryDisabled" | "disableCategory" | "enableCategory" | "setDisabledCategories", "onExecuted" | "onExecutionFailed">;
20
+ /**
21
+ * The commands: `execute()`, `canExecute()`, `resolveCommand()`, `searchCommands()`,
22
+ * `listShortcuts()`, `registerCommand()`, the categories, `executed$` and
23
+ * `executionFailed$`, and the settings. A call that leaves out the document is for this part
24
+ * of the template's document: the one `[epdfDocumentScope]` names, else the active one.
25
+ */
26
+ declare class EpdfCommands extends EpdfCommands_base {
27
+ /**
28
+ * One command as a signal, for what shows a command and isn't a button (a row of a shortcut
29
+ * sheet): its label, icon and state, `run()` and `shortcut`. Changes only when what the
30
+ * command shows does. Pass a function for an id that changes. A button uses the `epdfCommand`
31
+ * directive instead. Always a command: an id no plugin registered (or one asked for before
32
+ * the commands plugin is there) is labelled with its id, neither visible nor enabled, and an
33
+ * unknown one warns once in development.
34
+ */
35
+ commandOf(id: string | (() => string)): Signal<BoundCommand>;
36
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfCommands, never>;
37
+ static ɵprov: i0.ɵɵInjectableDeclaration<EpdfCommands>;
38
+ }
39
+ /** The commands plugin, with its settings: `withCommands({ commands: standardCommands })`. */
40
+ declare function withCommands(options?: CommandsConfig): EmbedPdfFeature;
41
+ /** A key whose command is turned off or not allowed, and why: what `(refused)` carries. */
42
+ interface RefusedShortcut {
43
+ readonly commandId: CommandId;
44
+ readonly refusal: CommandRefusal;
45
+ }
46
+ /**
47
+ * Every command's shortcut as a working key, anywhere on the page, for the active document.
48
+ * Keys typed into text fields are left alone, and so is a key whose command can't run right
49
+ * now. A command that's turned off or not allowed still takes its key, and `onRefused` says
50
+ * why. Add it next to `withCommands()`; for a page with more than one viewer, put
51
+ * `epdfCommandShortcuts` on the element around each one instead.
52
+ */
53
+ declare function withCommandShortcuts(options?: {
54
+ isMac?: boolean;
55
+ onRefused?: (commandId: CommandId, refusal: CommandRefusal) => void;
56
+ }): EmbedPdfFeature;
57
+ /**
58
+ * Every command's shortcut as a working key, while focus is inside this element: for a page
59
+ * with more than one viewer, or keys that should leave the rest of the page alone. Give the
60
+ * element a `tabindex` so a click on it gives it focus. A key whose command is turned off or
61
+ * not allowed fires `(refused)`.
62
+ *
63
+ * <div class="viewer" tabindex="0" epdfCommandShortcuts (refused)="say($event.refusal.message)">
64
+ */
65
+ declare class EpdfCommandShortcuts {
66
+ /** A key whose command is turned off or not allowed: say why. */
67
+ readonly refused: i0.OutputEmitterRef<RefusedShortcut>;
68
+ constructor();
69
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfCommandShortcuts, never>;
70
+ static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfCommandShortcuts, "[epdfCommandShortcuts]", never, {}, { "refused": "refused"; }, never, never, true, never>;
71
+ }
72
+ /**
73
+ * A button of yours that runs a command and shows its state: a click runs it, and the button's
74
+ * `disabled`, `aria-pressed`, `title` and `hidden` follow the command. The template reference
75
+ * gives the rest as signals:
76
+ *
77
+ * <button [epdfCommand]="'zoom:in'" #zoomIn="epdfCommand">
78
+ * {{ zoomIn.label() }} <kbd>{{ zoomIn.shortcut() }}</kbd>
79
+ * </button>
80
+ *
81
+ * It runs and reads the command for this part of the template's document. An unknown id
82
+ * hides the button.
83
+ */
84
+ declare class EpdfCommand {
85
+ /** The command's id: `'zoom:in'`. */
86
+ readonly id: i0.InputSignal<string>;
87
+ /** The command; an unknown id is neither visible nor enabled, so its button is hidden. */
88
+ readonly command: Signal<BoundCommand>;
89
+ /** What the user sees. */
90
+ readonly label: Signal<string>;
91
+ /** Your icon's name for it, when the command has one. */
92
+ readonly icon: Signal<string | undefined>;
93
+ /** Its first shortcut, formatted for the platform (`'⌘K'`), or `null`. */
94
+ readonly shortcut: Signal<string | null>;
95
+ /** Whether it can run now. */
96
+ readonly enabled: Signal<boolean>;
97
+ /** Whether it shows as pressed, such as the current tool. */
98
+ readonly active: Signal<boolean>;
99
+ /** Whether it shows at all. */
100
+ readonly visible: Signal<boolean>;
101
+ /** Run the command for this part of the template's document. Resolves `{ status }`, like `execute()`. */
102
+ run(): Promise<ExecuteResult>;
103
+ protected onClick(): void;
104
+ static ɵfac: i0.ɵɵFactoryDeclaration<EpdfCommand, never>;
105
+ static ɵdir: i0.ɵɵDirectiveDeclaration<EpdfCommand, "[epdfCommand]", ["epdfCommand"], { "id": { "alias": "epdfCommand"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
106
+ }
107
+
108
+ export { EpdfCommand, EpdfCommandShortcuts, EpdfCommands, formatShortcut, standardCommands, withCommandShortcuts, withCommands };
109
+ export type { RefusedShortcut };
@@ -1,56 +1,137 @@
1
- import { ActionsToken } from '@embedpdf/plugin-actions';
1
+ import { ActionsToken, actionsPlugin } from '@embedpdf/plugin-actions';
2
2
  export * from '@embedpdf/plugin-actions';
3
- import { effect } from '@angular/core';
3
+ import * as i0 from '@angular/core';
4
+ import { Injectable, untracked, inject, DestroyRef } from '@angular/core';
5
+ import { pluginService, injectKernelHost, devWarn, CapabilityBinding } from '@embedpdf/angular/runtime';
4
6
  import { StageToken } from '@embedpdf/plugin-stage/contract';
5
7
  import { createDefaultActionsUiAdapter } from '@embedpdf/web';
6
- import { injectOptionalCapability } from '@embedpdf/angular/runtime';
7
8
 
8
9
  /**
9
- * Angular's spelling of React's `useActionsUiAdapter` (the `use*` →
10
- * `inject*` law): installs the UI adapter for the active document's action
11
- * dispatcher. The DEFAULT policy — the origin×phase visibility matrix,
12
- * sanitizeExternalUri URI opens, browser print/alert fallbacks — is
13
- * `@embedpdf/web`'s `createDefaultActionsUiAdapter`, written ONCE for every
14
- * binding; this function is Angular glue only. The doc.print AUTHORITY gate
15
- * is upstream (the actions plugin) and not overridable.
10
+ * The actions plugin's service and features. The plugin runs a PDF's actions and has no DOM:
11
+ * opening a website, printing and showing a script's alert are the app's job, through a UI
12
+ * adapter. `withActionsUi()` installs the browser's defaults (a website opens in a new tab after
13
+ * `sanitizeExternalUri`, printing opens the print dialog), and you can replace any of them.
14
+ * Without it, an action that needs the adapter (a website, a print) is reported with a
15
+ * `no-adapter` diagnostic instead of running.
16
16
  *
17
- * Must run in an injection context (a constructor or field initializer —
18
- * the runtime `inject*` constraint). Pass a FUNCTION for late-bound
19
- * handlers; signal reads inside it are NOT tracked (the adapter consults it
20
- * per effect, the same contract as React's handlers ref). Installing an
21
- * adapter releases the document-open sequence latch, exactly as in React.
17
+ * The defaults themselves (which effects may show for which start, the URI check, the browser
18
+ * fallbacks) are `@embedpdf/web`'s `createDefaultActionsUiAdapter`, written once for every
19
+ * framework; this file is the Angular glue.
22
20
  */
23
- function injectActionsUiAdapter(handlers) {
24
- const actions = injectOptionalCapability(ActionsToken);
25
- const stage = injectOptionalCapability(StageToken);
26
- const overrides = typeof handlers === 'function' ? handlers : () => handlers;
27
- effect((onCleanup) => {
28
- const capability = actions();
29
- const stageCapability = stage();
30
- if (!capability)
31
- return; // no document yet — nothing to drive
21
+ /**
22
+ * The actions of the document in scope (`[epdfDocumentScope]`), else the active one:
23
+ * `executeNamed()`, `execute()`, `dispatch()`, `getActionTree()`, the checks (`canExecuteNamed()`,
24
+ * `isScriptingEnabled()`, …), the settings (`settings()`, `updateSettings()`, …, which work with
25
+ * no document) and the streams (`executed$`, `diagnosticReported$`, `scriptFailed$`, …). With
26
+ * no document every other call refuses with `not-ready`.
27
+ */
28
+ class EpdfActions extends pluginService({
29
+ name: 'EpdfActions',
30
+ feature: 'withActions()',
31
+ token: ActionsToken,
32
+ methods: [
33
+ 'executeNamed',
34
+ 'execute',
35
+ 'dispatch',
36
+ 'getActionTree',
37
+ 'isScriptingEnabled',
38
+ 'setUiAdapter',
39
+ 'setSubmitHandler',
40
+ 'registerExecutor',
41
+ 'runDocumentVerb',
42
+ 'prepareClose',
43
+ 'canExecute',
44
+ 'canExecuteNamed',
45
+ 'canDispatch',
46
+ ],
47
+ events: [
48
+ 'onExecuted',
49
+ 'onDiagnosticReported',
50
+ 'onScriptFailed',
51
+ 'onScriptDiagnosticReported',
52
+ 'onOpenSequenceCompleted',
53
+ ],
54
+ }) {
55
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfActions, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
56
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfActions, providedIn: 'root' });
57
+ }
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfActions, decorators: [{
59
+ type: Injectable,
60
+ args: [{ providedIn: 'root' }]
61
+ }] });
62
+ /** The actions plugin, for `provideEmbedPdf()`: `withActions({ javascript: { enabled: true } })`. */
63
+ function withActions(config) {
64
+ return { plugins: [actionsPlugin(config)], services: [EpdfActions] };
65
+ }
66
+ /**
67
+ * The UI adapter of the active document's actions: the browser's defaults, with `handlers` in
68
+ * place of any of them. `handlers` may be a function, which runs once in an injection context,
69
+ * so your handlers can use your services:
70
+ *
71
+ * withActionsUi(() => {
72
+ * const toasts = inject(Toasts);
73
+ * return { alert: (message) => toasts.show(message) };
74
+ * })
75
+ *
76
+ * It follows the active document, and the Stage showing it for page moves, and is installed in
77
+ * the same change that makes a document ready, before anything inside it can run an action.
78
+ * Installing it is also the first sign of a person that lets a document's opening actions run
79
+ * (`openSequence: 'auto'`), so a document opened in a background tab waits until it's shown.
80
+ * The `doc.print` permission is checked by the plugin, before any handler runs.
81
+ */
82
+ function withActionsUi(handlers) {
83
+ return { plugins: [], setup: () => installActionsUi(handlers) };
84
+ }
85
+ /** Keep the adapter installed on the active document's actions while the viewer lives. */
86
+ function installActionsUi(handlers) {
87
+ const host = injectKernelHost('withActionsUi()');
88
+ if (!host.provides(ActionsToken)) {
89
+ devWarn('actions-ui-without-actions', 'withActionsUi() has no actions plugin to give its handlers to. Add withActions() to provideEmbedPdf().');
90
+ return;
91
+ }
92
+ const overrides = typeof handlers === 'function' ? handlers() : handlers;
93
+ const activeDocument = () => null;
94
+ const actions = new CapabilityBinding(host, () => ActionsToken, activeDocument).capability;
95
+ const stage = new CapabilityBinding(host, () => StageToken, activeDocument).capability;
96
+ let installed = null;
97
+ // On every change of the kernel, not in an effect: the adapter is there in the same change
98
+ // that makes a document ready, so an action run as the document's UI appears finds it.
99
+ const follow = () => untracked(() => {
100
+ const current = actions();
101
+ const view = stage();
102
+ if (installed?.actions === current && installed.stage === view)
103
+ return;
104
+ installed?.remove();
105
+ installed = null;
106
+ if (!current)
107
+ return;
32
108
  const adapter = createDefaultActionsUiAdapter({
33
- overrides,
34
- goToPage: (page) => stageCapability?.goToPage(page),
109
+ overrides: () => overrides,
110
+ goToPage: (page) => view?.goToPage(page),
35
111
  });
36
- // Identity-safe by construction (the plugin's disposer only clears the
37
- // slot while THIS adapter is still current).
38
- onCleanup(capability.setUiAdapter(adapter));
112
+ installed = { actions: current, stage: view, remove: current.setUiAdapter(adapter) };
113
+ });
114
+ const stop = host.subscribe(follow);
115
+ follow();
116
+ inject(DestroyRef).onDestroy(() => {
117
+ stop();
118
+ installed?.remove();
39
119
  });
40
120
  }
41
121
 
42
122
  /**
43
- * @embedpdf/angular/actions — the action engine's UI port, Angular
44
- * spelling. `injectActionsUiAdapter()` installs the SHARED default policy
45
- * (`@embedpdf/web`'s `createDefaultActionsUiAdapter` — the same
46
- * origin×phase matrix React ships, written once so the two bindings can
47
- * never drift).
123
+ * @embedpdf/angular/actions: what a PDF may do by itself (links, buttons, form calculations,
124
+ * scripts), and how your app shows what its actions ask for.
125
+ *
126
+ * withActions(config) the plugin, for provideEmbedPdf()
127
+ * withActionsUi(handlers) the browser's defaults for websites, printing and alerts, or yours
128
+ * inject(EpdfActions) executeNamed(), execute(), the checks, the settings, the streams
48
129
  */
49
- // One-line-per-feature: registration travels with the UI.
130
+ // The plugin's types and helpers, so app code has one import for the feature.
50
131
 
51
132
  /**
52
133
  * Generated bundle index. Do not edit.
53
134
  */
54
135
 
55
- export { injectActionsUiAdapter };
136
+ export { EpdfActions, withActions, withActionsUi };
56
137
  //# sourceMappingURL=embedpdf-angular-actions.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"embedpdf-angular-actions.mjs","sources":["../../actions/src/actions-adapter.ts","../../actions/src/public_api.ts","../../actions/src/embedpdf-angular-actions.ts"],"sourcesContent":["import { effect } from '@angular/core';\nimport { ActionsToken, type ActionUiAdapter } from '@embedpdf/plugin-actions';\nimport { StageToken } from '@embedpdf/plugin-stage/contract';\nimport { createDefaultActionsUiAdapter } from '@embedpdf/web';\n\nimport { injectOptionalCapability } from '@embedpdf/angular/runtime';\n\n/** Override any subset of the default adapter policy. */\nexport type ActionsUiHandlers = Partial<ActionUiAdapter>;\n\n/**\n * Angular's spelling of React's `useActionsUiAdapter` (the `use*` →\n * `inject*` law): installs the UI adapter for the active document's action\n * dispatcher. The DEFAULT policy — the origin×phase visibility matrix,\n * sanitizeExternalUri URI opens, browser print/alert fallbacks — is\n * `@embedpdf/web`'s `createDefaultActionsUiAdapter`, written ONCE for every\n * binding; this function is Angular glue only. The doc.print AUTHORITY gate\n * is upstream (the actions plugin) and not overridable.\n *\n * Must run in an injection context (a constructor or field initializer —\n * the runtime `inject*` constraint). Pass a FUNCTION for late-bound\n * handlers; signal reads inside it are NOT tracked (the adapter consults it\n * per effect, the same contract as React's handlers ref). Installing an\n * adapter releases the document-open sequence latch, exactly as in React.\n */\nexport function injectActionsUiAdapter(\n handlers?: ActionsUiHandlers | (() => ActionsUiHandlers | undefined),\n): void {\n const actions = injectOptionalCapability(ActionsToken);\n const stage = injectOptionalCapability(StageToken);\n const overrides = typeof handlers === 'function' ? handlers : (): typeof handlers => handlers;\n effect((onCleanup) => {\n const capability = actions();\n const stageCapability = stage();\n if (!capability) return; // no document yet — nothing to drive\n const adapter: ActionUiAdapter = createDefaultActionsUiAdapter({\n overrides,\n goToPage: (page) => stageCapability?.goToPage(page),\n });\n // Identity-safe by construction (the plugin's disposer only clears the\n // slot while THIS adapter is still current).\n onCleanup(capability.setUiAdapter(adapter));\n });\n}\n","/**\n * @embedpdf/angular/actions — the action engine's UI port, Angular\n * spelling. `injectActionsUiAdapter()` installs the SHARED default policy\n * (`@embedpdf/web`'s `createDefaultActionsUiAdapter` — the same\n * origin×phase matrix React ships, written once so the two bindings can\n * never drift).\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-actions';\nexport * from './actions-adapter';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;;;;AAUA;;;;;;;;;;;;;;AAcG;AACG,SAAU,sBAAsB,CACpC,QAAoE,EAAA;AAEpE,IAAA,MAAM,OAAO,GAAG,wBAAwB,CAAC,YAAY,CAAC;AACtD,IAAA,MAAM,KAAK,GAAG,wBAAwB,CAAC,UAAU,CAAC;AAClD,IAAA,MAAM,SAAS,GAAG,OAAO,QAAQ,KAAK,UAAU,GAAG,QAAQ,GAAG,MAAuB,QAAQ;AAC7F,IAAA,MAAM,CAAC,CAAC,SAAS,KAAI;AACnB,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE;AAC5B,QAAA,MAAM,eAAe,GAAG,KAAK,EAAE;AAC/B,QAAA,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO;QACxB,MAAM,OAAO,GAAoB,6BAA6B,CAAC;YAC7D,SAAS;YACT,QAAQ,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC;AACpD,SAAA,CAAC;;;QAGF,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AAC7C,IAAA,CAAC,CAAC;AACJ;;AC3CA;;;;;;AAMG;AAEH;;ACRA;;AAEG;;;;"}
1
+ {"version":3,"file":"embedpdf-angular-actions.mjs","sources":["../../actions/src/actions.ts","../../actions/src/public_api.ts","../../actions/src/embedpdf-angular-actions.ts"],"sourcesContent":["/**\n * The actions plugin's service and features. The plugin runs a PDF's actions and has no DOM:\n * opening a website, printing and showing a script's alert are the app's job, through a UI\n * adapter. `withActionsUi()` installs the browser's defaults (a website opens in a new tab after\n * `sanitizeExternalUri`, printing opens the print dialog), and you can replace any of them.\n * Without it, an action that needs the adapter (a website, a print) is reported with a\n * `no-adapter` diagnostic instead of running.\n *\n * The defaults themselves (which effects may show for which start, the URI check, the browser\n * fallbacks) are `@embedpdf/web`'s `createDefaultActionsUiAdapter`, written once for every\n * framework; this file is the Angular glue.\n */\nimport { DestroyRef, inject, Injectable, untracked } from '@angular/core';\nimport {\n CapabilityBinding,\n devWarn,\n injectKernelHost,\n pluginService,\n type EmbedPdfFeature,\n} from '@embedpdf/angular/runtime';\nimport {\n actionsPlugin,\n ActionsToken,\n type ActionsCapability,\n type ActionsConfig,\n type ActionUiAdapter,\n} from '@embedpdf/plugin-actions';\nimport { StageToken, type StageCapability } from '@embedpdf/plugin-stage/contract';\nimport { createDefaultActionsUiAdapter } from '@embedpdf/web';\n\n/**\n * The actions of the document in scope (`[epdfDocumentScope]`), else the active one:\n * `executeNamed()`, `execute()`, `dispatch()`, `getActionTree()`, the checks (`canExecuteNamed()`,\n * `isScriptingEnabled()`, …), the settings (`settings()`, `updateSettings()`, …, which work with\n * no document) and the streams (`executed$`, `diagnosticReported$`, `scriptFailed$`, …). With\n * no document every other call refuses with `not-ready`.\n */\n@Injectable({ providedIn: 'root' })\nexport class EpdfActions extends pluginService({\n name: 'EpdfActions',\n feature: 'withActions()',\n token: ActionsToken,\n methods: [\n 'executeNamed',\n 'execute',\n 'dispatch',\n 'getActionTree',\n 'isScriptingEnabled',\n 'setUiAdapter',\n 'setSubmitHandler',\n 'registerExecutor',\n 'runDocumentVerb',\n 'prepareClose',\n 'canExecute',\n 'canExecuteNamed',\n 'canDispatch',\n ],\n events: [\n 'onExecuted',\n 'onDiagnosticReported',\n 'onScriptFailed',\n 'onScriptDiagnosticReported',\n 'onOpenSequenceCompleted',\n ],\n}) {}\n\n/** The actions plugin, for `provideEmbedPdf()`: `withActions({ javascript: { enabled: true } })`. */\nexport function withActions(config?: ActionsConfig): EmbedPdfFeature {\n return { plugins: [actionsPlugin(config)], services: [EpdfActions] };\n}\n\n/** Your own handlers for any of the adapter's effects: `openUri`, `print`, `alert`, `gotoPage`. */\nexport type ActionsUiHandlers = Partial<ActionUiAdapter>;\n\n/**\n * The UI adapter of the active document's actions: the browser's defaults, with `handlers` in\n * place of any of them. `handlers` may be a function, which runs once in an injection context,\n * so your handlers can use your services:\n *\n * withActionsUi(() => {\n * const toasts = inject(Toasts);\n * return { alert: (message) => toasts.show(message) };\n * })\n *\n * It follows the active document, and the Stage showing it for page moves, and is installed in\n * the same change that makes a document ready, before anything inside it can run an action.\n * Installing it is also the first sign of a person that lets a document's opening actions run\n * (`openSequence: 'auto'`), so a document opened in a background tab waits until it's shown.\n * The `doc.print` permission is checked by the plugin, before any handler runs.\n */\nexport function withActionsUi(\n handlers?: ActionsUiHandlers | (() => ActionsUiHandlers),\n): EmbedPdfFeature {\n return { plugins: [], setup: () => installActionsUi(handlers) };\n}\n\n/** Keep the adapter installed on the active document's actions while the viewer lives. */\nfunction installActionsUi(handlers?: ActionsUiHandlers | (() => ActionsUiHandlers)): void {\n const host = injectKernelHost('withActionsUi()');\n if (!host.provides(ActionsToken)) {\n devWarn(\n 'actions-ui-without-actions',\n 'withActionsUi() has no actions plugin to give its handlers to. Add withActions() to provideEmbedPdf().',\n );\n return;\n }\n const overrides = typeof handlers === 'function' ? handlers() : handlers;\n const activeDocument = () => null;\n const actions = new CapabilityBinding(host, () => ActionsToken, activeDocument).capability;\n const stage = new CapabilityBinding(host, () => StageToken, activeDocument).capability;\n\n let installed: {\n actions: ActionsCapability;\n stage: StageCapability | null;\n remove: () => void;\n } | null = null;\n // On every change of the kernel, not in an effect: the adapter is there in the same change\n // that makes a document ready, so an action run as the document's UI appears finds it.\n const follow = () =>\n untracked(() => {\n const current = actions();\n const view = stage();\n if (installed?.actions === current && installed.stage === view) return;\n installed?.remove();\n installed = null;\n if (!current) return;\n const adapter: ActionUiAdapter = createDefaultActionsUiAdapter({\n overrides: () => overrides,\n goToPage: (page) => view?.goToPage(page),\n });\n installed = { actions: current, stage: view, remove: current.setUiAdapter(adapter) };\n });\n const stop = host.subscribe(follow);\n follow();\n inject(DestroyRef).onDestroy(() => {\n stop();\n installed?.remove();\n });\n}\n","/**\n * @embedpdf/angular/actions: what a PDF may do by itself (links, buttons, form calculations,\n * scripts), and how your app shows what its actions ask for.\n *\n * withActions(config) the plugin, for provideEmbedPdf()\n * withActionsUi(handlers) the browser's defaults for websites, printing and alerts, or yours\n * inject(EpdfActions) executeNamed(), execute(), the checks, the settings, the streams\n */\n\n// The plugin's types and helpers, so app code has one import for the feature.\nexport * from '@embedpdf/plugin-actions';\nexport * from './actions';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, input, booleanAttribute, signal, viewChild, computed, inject, Injector, effect, afterNextRender, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { outsideStageError } from '@embedpdf/angular/runtime';
4
+ import { projectAnchoredTarget, observeAnchoredFit, sameAnchoredFit } from '@embedpdf/web';
5
+
6
+ /**
7
+ * Anchored UI: one element for everything that floats next to something on a page, such as a
8
+ * menu over a selection, a badge under a stamp or a card over a hovered note.
9
+ *
10
+ * <epdf-stage>
11
+ * <epdf-anchored [anchor]="{ page: hit.page, bounds: hit.bounds }" placement="bottom">…
12
+ *
13
+ * The page surface (a Stage, a standalone page view) provides an `EpdfProjectorBinding`: how
14
+ * a box on a page lands in the surface, and a `revision` signal that changes whenever that may
15
+ * have changed. The placement math is `@embedpdf/web`'s, shared by every framework.
16
+ *
17
+ * It renders in the surface's own box, so it moves in the same change detection pass as the
18
+ * pages and never trails a scroll by a frame, as an overlay outside the Stage would.
19
+ * On a page that isn't shown it renders nothing and doesn't follow the camera at all.
20
+ */
21
+ /** Provided by page surfaces (`<epdf-stage>`, a standalone page view), not by app code. */
22
+ const EPDF_PROJECTOR = new InjectionToken('EPDF_PROJECTOR');
23
+ /**
24
+ * Its content, placed next to a box on a page. Once the content's size is measured it flips to
25
+ * the other side when the chosen one has no room, and stays inside the view, unless `pinned`.
26
+ * A pointer press inside it doesn't reach the page under it, so a click in a menu is never a
27
+ * click outside the selection.
28
+ */
29
+ class EpdfAnchored {
30
+ /**
31
+ * The box on a page to sit next to, in page coordinates: `{ page, bounds }`, with `avoid`
32
+ * points to keep clear of. Null, or no `bounds` (a search match without a box), hides it.
33
+ */
34
+ anchor = input(null, ...(ngDevMode ? [{ debugName: "anchor" }] : []));
35
+ /** A side of the box, centred, or lined up with that side's start or end (`'top-end'`). */
36
+ placement = input('top', ...(ngDevMode ? [{ debugName: "placement" }] : []));
37
+ /** Screen pixels between the box and the content, and between the content and the view's edge. */
38
+ gap = input(8, ...(ngDevMode ? [{ debugName: "gap" }] : []));
39
+ /**
40
+ * Stay where `placement` puts it: never flip, never move to stay in view, and scroll away with
41
+ * the box. For badges and status; menus leave it off.
42
+ */
43
+ pinned = input(false, ...(ngDevMode ? [{ debugName: "pinned", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
44
+ binding;
45
+ /** The content's size and the view's, measured once it's shown and again when either resizes. */
46
+ fit = signal(null, ...(ngDevMode ? [{ debugName: "fit" }] : []));
47
+ /** Moves the browser made that no state announces (a page view scrolled with the document). */
48
+ moved = signal(0, ...(ngDevMode ? [{ debugName: "moved" }] : []));
49
+ box = viewChild('box', ...(ngDevMode ? [{ debugName: "box" }] : []));
50
+ space = computed(() => this.binding.projector()?.space ?? 'overlay', ...(ngDevMode ? [{ debugName: "space" }] : []));
51
+ /** Whether its page is on screen: until it is, nothing below reads the camera. */
52
+ onShownPage = computed(() => {
53
+ const anchor = this.anchor();
54
+ if (!anchor?.bounds)
55
+ return false;
56
+ const shown = this.binding.shownPages();
57
+ return !shown || shown.has(anchor.page.objectNumber);
58
+ }, ...(ngDevMode ? [{ debugName: "onShownPage" }] : []));
59
+ position = computed(() => {
60
+ if (!this.onShownPage())
61
+ return null;
62
+ this.binding.revision();
63
+ this.moved();
64
+ const projector = this.binding.projector();
65
+ const anchor = this.anchor();
66
+ if (!projector || !anchor?.bounds)
67
+ return null;
68
+ return projectAnchoredTarget(projector, { ...anchor, bounds: anchor.bounds }, { placement: this.placement(), gap: this.gap(), pinned: this.pinned() }, this.fit());
69
+ }, ...(ngDevMode ? [{ debugName: "position" }] : []));
70
+ constructor() {
71
+ const binding = inject(EPDF_PROJECTOR, { optional: true });
72
+ if (!binding)
73
+ throw outsideStageError('<epdf-anchored>');
74
+ this.binding = binding;
75
+ const injector = inject(Injector);
76
+ if (binding.subscribe) {
77
+ const subscribe = binding.subscribe;
78
+ effect((onCleanup) => {
79
+ if (!this.onShownPage())
80
+ return;
81
+ onCleanup(subscribe(() => this.moved.update((count) => count + 1)));
82
+ });
83
+ }
84
+ // A surface in client space measures the DOM, which isn't there on the first pass: one
85
+ // more pass once it is.
86
+ effect(() => {
87
+ if (this.space() !== 'client' || !this.anchor())
88
+ return;
89
+ afterNextRender(() => this.moved.update((count) => count + 1), { injector });
90
+ });
91
+ effect((onCleanup) => {
92
+ const element = this.box()?.nativeElement;
93
+ if (!element)
94
+ return;
95
+ const space = this.space();
96
+ onCleanup(observeAnchoredFit(element, space, (next) => {
97
+ if (!sameAnchoredFit(untracked(this.fit), next))
98
+ this.fit.set(next);
99
+ }));
100
+ });
101
+ }
102
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfAnchored, deps: [], target: i0.ɵɵFactoryTarget.Component });
103
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfAnchored, isStandalone: true, selector: "epdf-anchored", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, pinned: { classPropertyName: "pinned", publicName: "pinned", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: contents" }, viewQueries: [{ propertyName: "box", first: true, predicate: ["box"], descendants: true, isSignal: true }], ngImport: i0, template: `
104
+ @if (position(); as position) {
105
+ <div
106
+ #box
107
+ style="width: max-content; pointer-events: auto"
108
+ [style.position]="space() === 'client' ? 'fixed' : 'absolute'"
109
+ [style.left.px]="position.left"
110
+ [style.top.px]="position.top"
111
+ [style.transform]="position.transform"
112
+ (pointerdown)="$event.stopPropagation()"
113
+ >
114
+ <ng-content />
115
+ </div>
116
+ }
117
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
118
+ }
119
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfAnchored, decorators: [{
120
+ type: Component,
121
+ args: [{
122
+ selector: 'epdf-anchored',
123
+ changeDetection: ChangeDetectionStrategy.OnPush,
124
+ host: { style: 'display: contents' },
125
+ template: `
126
+ @if (position(); as position) {
127
+ <div
128
+ #box
129
+ style="width: max-content; pointer-events: auto"
130
+ [style.position]="space() === 'client' ? 'fixed' : 'absolute'"
131
+ [style.left.px]="position.left"
132
+ [style.top.px]="position.top"
133
+ [style.transform]="position.transform"
134
+ (pointerdown)="$event.stopPropagation()"
135
+ >
136
+ <ng-content />
137
+ </div>
138
+ }
139
+ `,
140
+ }]
141
+ }], ctorParameters: () => [] });
142
+
143
+ /**
144
+ * Generated bundle index. Do not edit.
145
+ */
146
+
147
+ export { EPDF_PROJECTOR, EpdfAnchored };
148
+ //# sourceMappingURL=embedpdf-angular-anchored.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedpdf-angular-anchored.mjs","sources":["../../anchored/src/anchored.ts","../../anchored/src/embedpdf-angular-anchored.ts"],"sourcesContent":["/**\n * Anchored UI: one element for everything that floats next to something on a page, such as a\n * menu over a selection, a badge under a stamp or a card over a hovered note.\n *\n * <epdf-stage>\n * <epdf-anchored [anchor]=\"{ page: hit.page, bounds: hit.bounds }\" placement=\"bottom\">…\n *\n * The page surface (a Stage, a standalone page view) provides an `EpdfProjectorBinding`: how\n * a box on a page lands in the surface, and a `revision` signal that changes whenever that may\n * have changed. The placement math is `@embedpdf/web`'s, shared by every framework.\n *\n * It renders in the surface's own box, so it moves in the same change detection pass as the\n * pages and never trails a scroll by a frame, as an overlay outside the Stage would.\n * On a page that isn't shown it renders nothing and doesn't follow the camera at all.\n */\nimport {\n afterNextRender,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n InjectionToken,\n Injector,\n input,\n signal,\n untracked,\n viewChild,\n type Signal,\n} from '@angular/core';\nimport { outsideStageError } from '@embedpdf/angular/runtime';\nimport {\n observeAnchoredFit,\n projectAnchoredTarget,\n sameAnchoredFit,\n type AnchoredFit,\n type AnchoredPlacement,\n type AnchoredRect,\n type AnchorTarget,\n type ViewProjector,\n} from '@embedpdf/web';\n\n/** What a page surface gives the anchored UI inside it. */\nexport interface EpdfProjectorBinding {\n /** How a box on a page lands in the surface; null while there is nothing to project onto. */\n readonly projector: Signal<ViewProjector | null>;\n /**\n * Changes whenever the projection may have (the Stage's camera): anchored UI reads it, so it\n * moves in the same pass as the pages.\n */\n readonly revision: Signal<unknown>;\n /** The pages on screen, by object number; null when the surface shows a single page. */\n readonly shownPages: Signal<ReadonlySet<number> | null>;\n /**\n * For a surface that moves with the browser rather than with state (a page view scrolled with\n * the document): call `callback` after such a move. A Stage has none.\n */\n readonly subscribe?: (callback: () => void) => () => void;\n}\n\n/** Provided by page surfaces (`<epdf-stage>`, a standalone page view), not by app code. */\nexport const EPDF_PROJECTOR = new InjectionToken<EpdfProjectorBinding>('EPDF_PROJECTOR');\n\n/** Where anchored UI sits: next to a box on a page, and points it keeps clear of. */\nexport type EpdfAnchor = Omit<AnchorTarget, 'bounds'> & { bounds?: AnchoredRect | null };\n\n/**\n * Its content, placed next to a box on a page. Once the content's size is measured it flips to\n * the other side when the chosen one has no room, and stays inside the view, unless `pinned`.\n * A pointer press inside it doesn't reach the page under it, so a click in a menu is never a\n * click outside the selection.\n */\n@Component({\n selector: 'epdf-anchored',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { style: 'display: contents' },\n template: `\n @if (position(); as position) {\n <div\n #box\n style=\"width: max-content; pointer-events: auto\"\n [style.position]=\"space() === 'client' ? 'fixed' : 'absolute'\"\n [style.left.px]=\"position.left\"\n [style.top.px]=\"position.top\"\n [style.transform]=\"position.transform\"\n (pointerdown)=\"$event.stopPropagation()\"\n >\n <ng-content />\n </div>\n }\n `,\n})\nexport class EpdfAnchored {\n /**\n * The box on a page to sit next to, in page coordinates: `{ page, bounds }`, with `avoid`\n * points to keep clear of. Null, or no `bounds` (a search match without a box), hides it.\n */\n readonly anchor = input<EpdfAnchor | null | undefined>(null);\n /** A side of the box, centred, or lined up with that side's start or end (`'top-end'`). */\n readonly placement = input<AnchoredPlacement>('top');\n /** Screen pixels between the box and the content, and between the content and the view's edge. */\n readonly gap = input(8);\n /**\n * Stay where `placement` puts it: never flip, never move to stay in view, and scroll away with\n * the box. For badges and status; menus leave it off.\n */\n readonly pinned = input(false, { transform: booleanAttribute });\n\n private readonly binding: EpdfProjectorBinding;\n /** The content's size and the view's, measured once it's shown and again when either resizes. */\n private readonly fit = signal<AnchoredFit | null>(null);\n /** Moves the browser made that no state announces (a page view scrolled with the document). */\n private readonly moved = signal(0);\n private readonly box = viewChild<ElementRef<HTMLDivElement>>('box');\n\n protected readonly space = computed(() => this.binding.projector()?.space ?? 'overlay');\n\n /** Whether its page is on screen: until it is, nothing below reads the camera. */\n private readonly onShownPage = computed(() => {\n const anchor = this.anchor();\n if (!anchor?.bounds) return false;\n const shown = this.binding.shownPages();\n return !shown || shown.has(anchor.page.objectNumber);\n });\n\n protected readonly position = computed(() => {\n if (!this.onShownPage()) return null;\n this.binding.revision();\n this.moved();\n const projector = this.binding.projector();\n const anchor = this.anchor();\n if (!projector || !anchor?.bounds) return null;\n return projectAnchoredTarget(\n projector,\n { ...anchor, bounds: anchor.bounds },\n { placement: this.placement(), gap: this.gap(), pinned: this.pinned() },\n this.fit(),\n );\n });\n\n constructor() {\n const binding = inject(EPDF_PROJECTOR, { optional: true });\n if (!binding) throw outsideStageError('<epdf-anchored>');\n this.binding = binding;\n const injector = inject(Injector);\n\n if (binding.subscribe) {\n const subscribe = binding.subscribe;\n effect((onCleanup) => {\n if (!this.onShownPage()) return;\n onCleanup(subscribe(() => this.moved.update((count) => count + 1)));\n });\n }\n\n // A surface in client space measures the DOM, which isn't there on the first pass: one\n // more pass once it is.\n effect(() => {\n if (this.space() !== 'client' || !this.anchor()) return;\n afterNextRender(() => this.moved.update((count) => count + 1), { injector });\n });\n\n effect((onCleanup) => {\n const element = this.box()?.nativeElement;\n if (!element) return;\n const space = this.space();\n onCleanup(\n observeAnchoredFit(element, space, (next) => {\n if (!sameAnchoredFit(untracked(this.fit), next)) this.fit.set(next);\n }),\n );\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}