@embedpdf/plugin-actions 3.0.0-next.15 → 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.cjs CHANGED
@@ -254,6 +254,10 @@ function createDispatcher(ctx, services, { run }, { triggerEnabled, lifecycleAnn
254
254
  return fields.find((candidate) => target.kind === "objectNumber" ? candidate.ref.kind === "objectNumber" && candidate.ref.objectNumber === target.objectNumber : candidate.name === target.name)?.actions?.[source.event] ?? null;
255
255
  }
256
256
  case "page": return ctx.getPage(source.page)?.actions?.[source.event ?? "open"] ?? null;
257
+ case "outline": {
258
+ const { items } = await ctx.doc.outline.get();
259
+ return items.find((candidate) => candidate.ref.objectNumber === source.item.objectNumber)?.action ?? null;
260
+ }
257
261
  case "document": {
258
262
  const snapshot = await readDocumentActions();
259
263
  if (!snapshot) return null;
@@ -755,8 +759,8 @@ function createTriggers(ctx, { settings, form }) {
755
759
  * The verb-shaped document events (/WC, /WS, /DS, /WP, /DP) and the print
756
760
  * door: one catalog lifecycle tree per event, run inside the current queue
757
761
  * operation and never throwing (a broken script must not cancel the user's
758
- * save or print), and the adapter print wrapped /WP → print → /DP under the
759
- * print latch.
762
+ * save or print), and a print request sent to the kernel's print, or, without
763
+ * a printer, to the adapter, wrapped /WP → print → /DP under the print latch.
760
764
  */
761
765
  function createDocumentEvents(services, { ensureOpenSequenceBeforeDocEvent }, { runAndEmit }) {
762
766
  const { reportDiagnostic } = services.events;
@@ -764,6 +768,7 @@ function createDocumentEvents(services, { ensureOpenSequenceBeforeDocEvent }, {
764
768
  const { enqueue, budget } = services.queue;
765
769
  const ports = services.ports.slots;
766
770
  const { allowsPrint } = services.authority;
771
+ const { documentPrint } = services;
767
772
  const printLatch = services.printLatch;
768
773
  /**
769
774
  * Run one catalog lifecycle tree inside the current queue operation, shared
@@ -803,13 +808,21 @@ function createDocumentEvents(services, { ensureOpenSequenceBeforeDocEvent }, {
803
808
  return foldSteps(step ? [step] : [], diagnostics);
804
809
  };
805
810
  /**
806
- * Both adapter print paths (the Named Print verb, and script `doc.print()`
807
- * effects from the actions plane) go through here: /WP →
808
- * `ports.uiAdapter.print` exactly once → /DP, with the latch reset in
809
- * `finally`. While the latch is held (including `runDocumentVerb('print')`'s
810
- * body) a nested request is suppressed with `reentrant-print`. An adapter
811
- * throw skips /DP (the latch still resets). /DP fires when the adapter call
812
- * returns, since a browser cannot observe dialog completion.
811
+ * Both print paths (the Named Print verb, and script `doc.print()` effects
812
+ * from the actions plane) go through here. While the latch is held
813
+ * (including `runDocumentVerb('print')`'s body) a nested request is
814
+ * suppressed with `reentrant-print`.
815
+ *
816
+ * With the kernel's print, a user's request starts `documents.print()` and
817
+ * returns: the kernel runs /WP and /DP around the dialog through this
818
+ * plugin's `aroundPrint` wrap, a `runDocumentVerb('print')` that waits for
819
+ * the current queue operation, so it is never awaited here. A hover or
820
+ * lifecycle request never opens the dialog.
821
+ *
822
+ * Without a printer: /WP → `ports.uiAdapter.print` exactly once → /DP,
823
+ * with the latch reset in `finally`. An adapter throw skips /DP (the latch
824
+ * still resets). /DP fires when the adapter call returns, since a browser
825
+ * cannot observe dialog completion.
813
826
  */
814
827
  const firePrintThroughAdapter = async (uiContext, diagnose) => {
815
828
  if (printLatch.active) {
@@ -832,6 +845,23 @@ function createDocumentEvents(services, { ensureOpenSequenceBeforeDocEvent }, {
832
845
  detail: "doc.print is not allowed"
833
846
  };
834
847
  }
848
+ if (documentPrint.available()) {
849
+ if (uiContext && uiContext.origin !== "user") {
850
+ diagnose({
851
+ code: "blocked",
852
+ message: `print: a ${uiContext.origin} request never prints`
853
+ });
854
+ return {
855
+ status: "blocked",
856
+ detail: `${uiContext.origin} print request`
857
+ };
858
+ }
859
+ documentPrint.start().catch((error) => diagnose({
860
+ code: "trigger-failed",
861
+ message: `print: ${error instanceof Error ? error.message : String(error)}`
862
+ }));
863
+ return { status: "executed" };
864
+ }
835
865
  if (!ports.uiAdapter) {
836
866
  diagnose({
837
867
  code: "no-adapter",
@@ -1206,7 +1236,7 @@ function createScriptWorld(ctx, { form }) {
1206
1236
  };
1207
1237
  const scriptEventFor = (actionContext, snapshot) => {
1208
1238
  const provenance = actionContext.event;
1209
- const type = provenance.scope === "page" ? "Page" : provenance.scope === "document" ? "Doc" : actionContext.source.kind === "widget" ? "Field" : actionContext.source.kind === "link" ? "Link" : "Annot";
1239
+ const type = provenance.scope === "page" ? "Page" : provenance.scope === "document" ? "Doc" : actionContext.source.kind === "widget" ? "Field" : actionContext.source.kind === "link" ? "Link" : actionContext.source.kind === "outline" ? "Bookmark" : "Annot";
1210
1240
  const name = provenance.scope === "activate" ? "Mouse Up" : provenance.scope === "annotation" ? ANNOTATION_EVENT_NAMES[provenance.name] ?? "Mouse Up" : provenance.scope === "page" ? PAGE_EVENT_NAMES[provenance.name] ?? "Open" : DOC_EVENT_NAMES[provenance.name] ?? "Open";
1211
1241
  const targetRef = actionContext.source.kind === "widget" ? actionContext.source.field : void 0;
1212
1242
  const targetField = targetRef ? snapshot.fields.find((field) => targetRef.kind === "objectNumber" && field.ref.kind === "objectNumber" ? field.ref.objectNumber === targetRef.objectNumber : targetRef.kind === "fqn" && field.name === targetRef.name) : void 0;
@@ -1439,6 +1469,7 @@ function createScriptSurface(services, { performSubmit }) {
1439
1469
  const { reportDiagnostic, scriptDiagnosticReported, scriptFailed } = services.events;
1440
1470
  const ports = services.ports.slots;
1441
1471
  const { allowsPrint } = services.authority;
1472
+ const { documentPrint } = services;
1442
1473
  const printLatch = services.printLatch;
1443
1474
  const surfaceScriptResult = (result) => {
1444
1475
  const uiContext = {
@@ -1479,6 +1510,23 @@ function createScriptSurface(services, { performSubmit }) {
1479
1510
  });
1480
1511
  continue;
1481
1512
  }
1513
+ if (effect.kind === "print" && documentPrint.available()) {
1514
+ if (result.origin !== "user") {
1515
+ scriptDiagnosticReported.emit({
1516
+ code: "ui-effect-suppressed",
1517
+ message: `script print request from a ${result.origin} event suppressed`
1518
+ });
1519
+ continue;
1520
+ }
1521
+ documentPrint.start().catch((error) => reportDiagnostic({
1522
+ code: "trigger-failed",
1523
+ message: `print: ${error instanceof Error ? error.message : String(error)}`
1524
+ }, {
1525
+ action: "javascript",
1526
+ source
1527
+ }));
1528
+ continue;
1529
+ }
1482
1530
  if (!ports.uiAdapter) {
1483
1531
  reportDiagnostic({
1484
1532
  code: "no-adapter",
@@ -1564,6 +1612,24 @@ function createCatalog(ctx) {
1564
1612
  };
1565
1613
  }
1566
1614
  //#endregion
1615
+ //#region src/services/document-print.ts
1616
+ /**
1617
+ * The kernel's print, where the document's own print requests go (the Named Print verb, a
1618
+ * script's `print()`) when the host gave the kernel a printer. The kernel prints the
1619
+ * document's pages, and runs the document's /WP and /DP around the dialog through the wrap
1620
+ * this plugin registers (`ctx.aroundPrint`). Without a printer, requests fall back to the UI
1621
+ * adapter's `print`.
1622
+ */
1623
+ function createDocumentPrint(ctx) {
1624
+ const documents = () => ctx.get(_embedpdf_core.DocumentsToken);
1625
+ return {
1626
+ /** Whether the kernel prints this document: `doc.print`, and the host gave a printer. */
1627
+ available: () => documents().canPrint(),
1628
+ /** Print the document through the kernel. */
1629
+ start: () => documents().print()
1630
+ };
1631
+ }
1632
+ //#endregion
1567
1633
  //#region src/services/events.ts
1568
1634
  function createEvents(ctx) {
1569
1635
  const executed = ctx.events.source();
@@ -1675,6 +1741,7 @@ function createServices(ctx) {
1675
1741
  catalog: createCatalog(ctx),
1676
1742
  form: createForm(ctx, ports.slots),
1677
1743
  authority: createAuthority(ctx),
1744
+ documentPrint: createDocumentPrint(ctx),
1678
1745
  printLatch: createPrintLatch()
1679
1746
  };
1680
1747
  }
@@ -1829,6 +1896,7 @@ function createActionsController(ctx, config = {}) {
1829
1896
  ctx.listen(ctx.doc.events, (event) => triggers.invalidate(event));
1830
1897
  openSequence.arm();
1831
1898
  ctx.aroundDownload((read) => documentEvents.api.runDocumentVerb("save", read));
1899
+ ctx.aroundPrint((print) => documentEvents.api.runDocumentVerb("print", print));
1832
1900
  }
1833
1901
  };
1834
1902
  }