@paged-media/plugin-sdk 0.2.40-canary.0 → 0.2.41-canary.0

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
@@ -1,4 +1,4 @@
1
- import { PagedBundle, BindingProvider, Disposable, ActiveBindingProvider, BindingPropertyRequest, BindingReadResult, BindingPropertyWrite, BindingWriteResult, BindingCollectionRequest, BindingCollectionResult, MutationInput, FontFaceAsset, BundleHost, ClipboardPayload, ConsentResult, DataProviderRegistration, DataProviderHandle, DataProviderInfo, DataProviderSnapshot, JournalRecord, ShellSurface, WidgetSurface, WillSaveEvent, ToolSettingValue, SchemaPanelRenderer, SchemaPanelContribution, EditContextContribution, ObjectTypeContribution, Diagnostic, TextCaret, PagedEditor, PluginManifest, ToolContribution, PanelContribution, CommandContribution, KeybindingContribution, OverlayContribution, ImporterContribution, ExporterContribution, ToolPreviewShape, BindingsSurface, PanelProps, SchemaGate, WasmArtifact, CanvasPointerEvent, Mutation, MutationOutcome, BindingProviderHandle } from '@paged-media/plugin-api';
1
+ import { PagedBundle, BindingProvider, Disposable, ActiveBindingProvider, BindingPropertyRequest, BindingReadResult, BindingPropertyWrite, BindingWriteResult, BindingCollectionRequest, BindingCollectionResult, MutationInput, FontFaceAsset, BundleHost, ClipboardPayload, ConsentResult, DataProviderRegistration, DataProviderHandle, DataProviderInfo, DataProviderSnapshot, JournalRecord, ShellSurface, WidgetSurface, WillSaveEvent, ToolSettingValue, SchemaPanelRenderer, SchemaPanelContribution, EditContextContribution, ObjectTypeContribution, Diagnostic, TextCaret, OpenDocumentResult, PagedEditor, PluginManifest, ToolContribution, PanelContribution, CommandContribution, KeybindingContribution, OverlayContribution, ImporterContribution, ExporterContribution, ToolPreviewShape, BindingsSurface, PanelProps, SchemaGate, WasmArtifact, CanvasPointerEvent, Mutation, MutationOutcome, BindingProviderHandle } from '@paged-media/plugin-api';
2
2
  import { ComponentType } from 'react';
3
3
 
4
4
  /**
@@ -240,6 +240,27 @@ interface NativeDocumentBackend {
240
240
  listParts(prefix?: string): Promise<string[]>;
241
241
  open(bytes: Uint8Array): Promise<void>;
242
242
  }
243
+ /**
244
+ * D-26 — the backend the editor injects to back `host.documents`. The SDK
245
+ * owns the capability gate (`capabilities.documents.export` / `.open`); the
246
+ * backend owns the IO AND the unsaved-edits decision: `open` must ask the
247
+ * user (host UI) before discarding a dirty active document and answer
248
+ * `{ opened: false, reason: "declined" }` when they keep it. `requester`
249
+ * is filled by the SDK from the calling bundle's manifest (not from the
250
+ * plugin's arguments), so the prompt can name who is asking. No backend →
251
+ * both doors reject and `supports("documents.open@1")` /
252
+ * `supports("documents.exportPaged@1")` are false.
253
+ */
254
+ interface DocumentsBackend {
255
+ exportPaged(): Promise<Uint8Array>;
256
+ open(bytes: Uint8Array, request: {
257
+ name: string | null;
258
+ requester: {
259
+ id: string;
260
+ name: string;
261
+ };
262
+ }): Promise<OpenDocumentResult>;
263
+ }
243
264
  /** The SHARED cross-plugin data-provider registry (paged.data §7.1 / D-09). The
244
265
  * editor creates ONE (`createDataProviderRegistry`) and injects the SAME
245
266
  * instance into every plugin host, so a provider plugin and a consumer plugin
@@ -469,6 +490,12 @@ interface CreateBundleHostOptions {
469
490
  * `supports("document.openNative@1")` answer true. When absent, the reads
470
491
  * answer `null`/`[]` and `open` rejects (the honest no-backend door). */
471
492
  nativeDocument?: NativeDocumentBackend;
493
+ /** D-26 — host-provided DOCUMENTS backend. When present,
494
+ * `host.documents.exportPaged` / `open` go through it (gated on
495
+ * `capabilities.documents.export` / `.open`) and
496
+ * `supports("documents.exportPaged@1")` / `supports("documents.open@1")`
497
+ * answer true. When absent, both doors reject. */
498
+ documents?: DocumentsBackend;
472
499
  /** Host-provided WORKER backend (K-3 / S-07 / I-02). When present,
473
500
  * `host.workers.spawn` resolves a declared bundle-relative module +
474
501
  * constructs a host-owned `Worker` through it (capability-gated,
@@ -521,6 +548,14 @@ interface HeadlessCanvasWorker {
521
548
  handleMessage(input: string): string;
522
549
  loadDocumentDirect(seq: number, bytes: Uint8Array, font?: Uint8Array, cmykIccProfile?: Uint8Array): string;
523
550
  runResolveJson(): string | undefined;
551
+ /** The engine shaper (v38): `{ advance, ascender, descender }` in pt, or
552
+ * null when no document is loaded / the face resolves to nothing.
553
+ * Optional so a structural fake need not carry it. */
554
+ measureText?(family: string, style: string | null | undefined, text: string, sizePt: number): {
555
+ advance: number;
556
+ ascender: number;
557
+ descender: number;
558
+ } | null;
524
559
  free(): void;
525
560
  }
526
561
  interface LoadedEngine {
@@ -580,6 +615,32 @@ interface RecordedContribution {
580
615
  value: ToolContribution | PanelContribution | SchemaPanelContribution | CommandContribution | KeybindingContribution | OverlayContribution | EditContextContribution | ObjectTypeContribution | ImporterContribution | ExporterContribution;
581
616
  }
582
617
  interface HarnessOptions extends LoadHeadlessEngineOptions, Pick<CreateBundleHostOptions, "console" | "storage" | "capabilityMode" | "assetSource" | "blobStore" | "clipboard" | "secrets" | "bindingProviders" | "willSave" | "toolSettings"> {
618
+ /** D-27 — back `host.text` with the ENGINE's shaper
619
+ * (`CanvasWorker.measureText`, the one the editor's worker calls)
620
+ * instead of the estimate, so `measureString` / `measureStrings`
621
+ * answer real widths against the loaded document's fonts and
622
+ * `supports("text.measure@1")` / `("text.measureStrings@1")` are true.
623
+ * Off by default: existing headless suites pinned the estimate. */
624
+ engineShaper?: boolean;
625
+ /** D-26 — the headless stand-in for the editor's keep/discard prompt.
626
+ * `host.documents.open` calls it ONLY when the active document has
627
+ * unsaved edits; resolve `false` to keep them (the door then answers
628
+ * `{ opened: false, reason: "declined" }`). Default: discard. */
629
+ confirmReplace?: (request: {
630
+ name: string | null;
631
+ requester: {
632
+ id: string;
633
+ name: string;
634
+ };
635
+ }) => boolean | Promise<boolean>;
636
+ /** D-28 — the default font every load lays out with, the headless
637
+ * stand-in for the editor's `defaultFontProvider` (the editor passes
638
+ * `/fonts/Inter.ttf`). The engine lays text out, and reports a story
639
+ * `overset`, only when it has a font: without one nothing is laid out and
640
+ * every story reads `overset: false` (not measured). Passed to every
641
+ * load, `load()` and `host.documents.open` alike. Absent: no font, as
642
+ * before. */
643
+ defaultFont?: Uint8Array;
583
644
  }
584
645
  /** A headless will-save registry: listeners register per plugin and
585
646
  * `fire()` awaits them all, the way the editor's save path does — so a
@@ -656,6 +717,13 @@ interface HeadlessHost {
656
717
  /** Load an IDML package into the headless document. Resolves to the
657
718
  * loaded page ids (or throws on a parse failure). */
658
719
  load(idml: Uint8Array): Promise<string[]>;
720
+ /** D-26 — every document a bundle opened through `host.documents.open`,
721
+ * in order (declined opens are not listed). */
722
+ openedDocuments(): ReadonlyArray<{
723
+ name: string | null;
724
+ requester: string;
725
+ pageIds: string[];
726
+ }>;
659
727
  /** Activate a bundle against this host (apiVersion-negotiated). The
660
728
  * returned disposer runs the bundle's teardown; the host's own
661
729
  * facade teardown runs on `dispose()`. */
@@ -921,4 +989,4 @@ declare function contributeObjectType(host: BundleHost, contribution: ObjectType
921
989
  */
922
990
  declare function contributeBindingProvider(host: BundleHost, contextType: string, provider: BindingProvider): BindingProviderHandle;
923
991
 
924
- export { API_VERSION, ASSET_BUDGETS, BLOB_BUDGETS, type BindingProviderBackend, type BlobStore, type BundleAssetProvider, type BundleAssetSource, type BundleHostHandle, type BundleTrust, CANVAS_WASM_PKG, CLICK_DRAG_THRESHOLD_PX, type ClipboardBackend, type ConsentBackend, type CreateBundleHostOptions, type DataProviderBackend, type DiagnosticsSink, DisposableStore, FALLBACK_WIDGETS, HOST_FEATURES, type HarnessOptions, type HeadlessCanvasWorker, type HeadlessHost, type HeadlessHostHandle, type JournalSink, type LoadBundleWasmOptions, type LoadHeadlessEngineOptions, type LoadedBundle, type LoadedBundleWasm, type LoadedEngine, type NativeDocumentBackend, type PageDrag, PluginApiNotImplemented, PluginCapabilityError, type RecordableAssetSource, type RecordedContribution, type RecordedFontFaceRequest, type RegisteredBindingProvider, type SecretStoreBackend, type SeededFace, type ShellBackend, type SpawnedWorker, type StorageBacking, type TextCaretBackend, type ToolSettingsBackend, WASM_BUDGETS, WORKER_BUDGETS, type WillSaveBackend, type WorkerBackend, beginPageDrag, commitAndSelect, contributeBindingProvider, contributeEditContext, contributeObjectType, contributePanel, contributeSchemaPanel, contributeTool, createBindingProviderRegistry, createBundleHost, createDataProviderRegistry, createHeadlessHost, createRecordableAssetSource, defineBundle, endLocalFor, inMemoryToolSettings, inMemoryWillSave, loadBundle, loadBundleWasm, loadHeadlessEngine, makeSchemaPanelComponent, protocolFromVersion, pxToPt, readVendoredWireVersion, resolveCanvasWasm, resolveGate, satisfiesApiVersion, toDisposable };
992
+ export { API_VERSION, ASSET_BUDGETS, BLOB_BUDGETS, type BindingProviderBackend, type BlobStore, type BundleAssetProvider, type BundleAssetSource, type BundleHostHandle, type BundleTrust, CANVAS_WASM_PKG, CLICK_DRAG_THRESHOLD_PX, type ClipboardBackend, type ConsentBackend, type CreateBundleHostOptions, type DataProviderBackend, type DiagnosticsSink, DisposableStore, type DocumentsBackend, FALLBACK_WIDGETS, HOST_FEATURES, type HarnessOptions, type HeadlessCanvasWorker, type HeadlessHost, type HeadlessHostHandle, type JournalSink, type LoadBundleWasmOptions, type LoadHeadlessEngineOptions, type LoadedBundle, type LoadedBundleWasm, type LoadedEngine, type NativeDocumentBackend, type PageDrag, PluginApiNotImplemented, PluginCapabilityError, type RecordableAssetSource, type RecordedContribution, type RecordedFontFaceRequest, type RegisteredBindingProvider, type SecretStoreBackend, type SeededFace, type ShellBackend, type SpawnedWorker, type StorageBacking, type TextCaretBackend, type ToolSettingsBackend, WASM_BUDGETS, WORKER_BUDGETS, type WillSaveBackend, type WorkerBackend, beginPageDrag, commitAndSelect, contributeBindingProvider, contributeEditContext, contributeObjectType, contributePanel, contributeSchemaPanel, contributeTool, createBindingProviderRegistry, createBundleHost, createDataProviderRegistry, createHeadlessHost, createRecordableAssetSource, defineBundle, endLocalFor, inMemoryToolSettings, inMemoryWillSave, loadBundle, loadBundleWasm, loadHeadlessEngine, makeSchemaPanelComponent, protocolFromVersion, pxToPt, readVendoredWireVersion, resolveCanvasWasm, resolveGate, satisfiesApiVersion, toDisposable };
package/dist/index.js CHANGED
@@ -230,6 +230,11 @@ var HOST_FEATURES = [
230
230
  "document.onDidChange@1",
231
231
  "document.getMetadata@1",
232
232
  "document.setMetadata@1",
233
+ // Protocol 69 — the document-scoped label. Static like the snap door:
234
+ // the flag says this SDK forwards the two calls; an engine older than 69
235
+ // answers `null` on read (no `pluginMetadata` in its DocumentMeta) and
236
+ // `applied: false` on write (it does not know the op).
237
+ "document.documentMetadata@1",
233
238
  "selection@1",
234
239
  "viewport@1",
235
240
  "overlay.toolPreview@1",
@@ -628,6 +633,7 @@ function createBundleHost(getEditor, manifest, options) {
628
633
  const hasWorkers = () => typeof caps?.workers?.max === "number";
629
634
  const hasSecrets = () => caps?.secrets?.sources === true;
630
635
  const hasGpu = () => caps?.gpu?.realm === "bundle";
636
+ const hasDocuments = (dir) => caps?.documents?.[dir] === true;
631
637
  const lists = (arr, id) => arr?.includes(id) ?? false;
632
638
  const declaresType = (arr, type) => arr?.some((e) => e.type === type) ?? false;
633
639
  const requireDeclared = (ok, door, missing) => {
@@ -1098,7 +1104,7 @@ function createBundleHost(getEditor, manifest, options) {
1098
1104
  }
1099
1105
  };
1100
1106
  const foreignMetadataKey = (m) => {
1101
- if (m.op === "setPluginMetadata") {
1107
+ if (m.op === "setPluginMetadata" || m.op === "setDocumentMetadata") {
1102
1108
  return m.args.key === metadataKey(manifest) ? null : m.args.key;
1103
1109
  }
1104
1110
  if (m.op === "batch") {
@@ -1147,7 +1153,7 @@ function createBundleHost(getEditor, manifest, options) {
1147
1153
  if (denied !== null) return { applied: false, error: denied };
1148
1154
  const foreign = foreignMetadataKey(mutation);
1149
1155
  if (foreign !== null) {
1150
- const error = `setPluginMetadata key "${foreign}" is outside this plugin's namespace ("${metadataKey(manifest)}")`;
1156
+ const error = `metadata key "${foreign}" is outside this plugin's namespace ("${metadataKey(manifest)}")`;
1151
1157
  log.warn(error);
1152
1158
  return { applied: false, error };
1153
1159
  }
@@ -1384,6 +1390,28 @@ function createBundleHost(getEditor, manifest, options) {
1384
1390
  }
1385
1391
  });
1386
1392
  },
1393
+ async getDocumentMetadata() {
1394
+ requireDocRead("document.getDocumentMetadata");
1395
+ const key = metadataKey(manifest);
1396
+ const meta = await getEditor().client.documentMeta();
1397
+ const entry = meta.pluginMetadata?.find((e) => e.key === key);
1398
+ if (!entry) return null;
1399
+ try {
1400
+ return JSON.parse(entry.value);
1401
+ } catch {
1402
+ return null;
1403
+ }
1404
+ },
1405
+ async setDocumentMetadata(envelope) {
1406
+ return this.mutate({
1407
+ op: "setDocumentMetadata",
1408
+ args: {
1409
+ key: metadataKey(manifest),
1410
+ value: envelope === null ? null : JSON.stringify(envelope),
1411
+ caller: manifest.id
1412
+ }
1413
+ });
1414
+ },
1387
1415
  async frameChain(storyId) {
1388
1416
  requireDocRead("document.frameChain");
1389
1417
  const reply = await getEditor().client.send({
@@ -1487,6 +1515,25 @@ function createBundleHost(getEditor, manifest, options) {
1487
1515
  // "no reader").
1488
1516
  caret() {
1489
1517
  return options?.textCaret?.read() ?? null;
1518
+ },
1519
+ // D-27 — the batched measure. One round-trip when the editor wires
1520
+ // `measureMany`; otherwise the same answers one call at a time (or
1521
+ // the estimate), so a bundle never needs two code paths.
1522
+ async measureStrings(family, style, texts, sizePt) {
1523
+ if (texts.length === 0) return [];
1524
+ const editorText = getEditor().text;
1525
+ if (editorText?.measureMany) {
1526
+ const out = await editorText.measureMany(family, style, texts, sizePt);
1527
+ if (out.length !== texts.length) {
1528
+ throw new Error(
1529
+ `host.text.measureStrings: the host answered ${out.length} metrics for ${texts.length} strings`
1530
+ );
1531
+ }
1532
+ return out;
1533
+ }
1534
+ return Promise.all(
1535
+ texts.map((t) => text.measureString(family, style, t, sizePt))
1536
+ );
1490
1537
  }
1491
1538
  };
1492
1539
  const overlay = {
@@ -2058,6 +2105,42 @@ function createBundleHost(getEditor, manifest, options) {
2058
2105
  return nativeDocBackend.open(bytes);
2059
2106
  }
2060
2107
  };
2108
+ const documentsBackend = options?.documents;
2109
+ const documents = {
2110
+ async exportPaged() {
2111
+ requireDeclared(
2112
+ hasDocuments("export"),
2113
+ "documents.exportPaged",
2114
+ "capabilities.documents.export must be declared"
2115
+ );
2116
+ if (!documentsBackend) {
2117
+ throw new Error(
2118
+ 'host.documents.exportPaged: the host wired no documents backend (supports("documents.exportPaged@1") is false)'
2119
+ );
2120
+ }
2121
+ return documentsBackend.exportPaged();
2122
+ },
2123
+ async open(bytes, opts) {
2124
+ requireDeclared(
2125
+ hasDocuments("open"),
2126
+ "documents.open",
2127
+ "capabilities.documents.open must be declared"
2128
+ );
2129
+ if (!documentsBackend) {
2130
+ throw new Error(
2131
+ 'host.documents.open: the host wired no documents backend (supports("documents.open@1") is false)'
2132
+ );
2133
+ }
2134
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength === 0) {
2135
+ throw new Error("host.documents.open: bytes must be a non-empty Uint8Array");
2136
+ }
2137
+ const name = typeof opts?.name === "string" && opts.name.trim() !== "" ? opts.name.trim() : null;
2138
+ return documentsBackend.open(bytes.slice(), {
2139
+ name,
2140
+ requester: { id: manifest.id, name: manifest.name }
2141
+ });
2142
+ }
2143
+ };
2061
2144
  const clipboardBackend = options?.clipboard;
2062
2145
  const clipboardGate = (door) => {
2063
2146
  const grant = clipboardGrant();
@@ -2263,6 +2346,9 @@ function createBundleHost(getEditor, manifest, options) {
2263
2346
  if (getEditor().text) {
2264
2347
  featureSet.add("text.measure@1");
2265
2348
  }
2349
+ if (getEditor().text?.measureMany) {
2350
+ featureSet.add("text.measureStrings@1");
2351
+ }
2266
2352
  if (getEditor().sceneLayers) {
2267
2353
  featureSet.add("rendering.sceneLayer@1");
2268
2354
  featureSet.add("rendering.sceneLayer.faces@1");
@@ -2335,6 +2421,10 @@ function createBundleHost(getEditor, manifest, options) {
2335
2421
  featureSet.add("document.readNative@1");
2336
2422
  featureSet.add("document.openNative@1");
2337
2423
  }
2424
+ if (options?.documents) {
2425
+ featureSet.add("documents.exportPaged@1");
2426
+ featureSet.add("documents.open@1");
2427
+ }
2338
2428
  if (options?.workers) {
2339
2429
  featureSet.add("workers@1");
2340
2430
  }
@@ -2358,6 +2448,7 @@ function createBundleHost(getEditor, manifest, options) {
2358
2448
  blob,
2359
2449
  parts,
2360
2450
  nativeDocument,
2451
+ documents,
2361
2452
  network,
2362
2453
  dataProviders,
2363
2454
  diagnostics,
@@ -2612,7 +2703,7 @@ function inMemorySecretStore() {
2612
2703
  };
2613
2704
  }
2614
2705
  var seqCounter = 1;
2615
- function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews) {
2706
+ function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews, engineShaper = false) {
2616
2707
  const protocol = worker.protocolVersion;
2617
2708
  const listeners = /* @__PURE__ */ new Set();
2618
2709
  const fanOut = (reply) => {
@@ -2772,7 +2863,23 @@ function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews) {
2772
2863
  },
2773
2864
  contentSelection: { contentSelection: null }
2774
2865
  };
2775
- return editor;
2866
+ const measureText = worker.measureText?.bind(worker);
2867
+ if (engineShaper && measureText) {
2868
+ const zero = { advance: 0, ascender: 0, descender: 0 };
2869
+ const one = (family, style, t, sizePt) => {
2870
+ const m = measureText(family, style, t, sizePt);
2871
+ return m ? { advance: m.advance, ascender: m.ascender, descender: m.descender } : zero;
2872
+ };
2873
+ editor.text = {
2874
+ async measure(family, style, t, sizePt) {
2875
+ return one(family, style, t, sizePt);
2876
+ },
2877
+ async measureMany(family, style, texts, sizePt) {
2878
+ return texts.map((t) => one(family, style, t, sizePt));
2879
+ }
2880
+ };
2881
+ }
2882
+ return { editor, fanOut };
2776
2883
  }
2777
2884
  async function createHeadlessHost(options = {}) {
2778
2885
  const engine = await loadHeadlessEngine(options);
@@ -2780,7 +2887,7 @@ async function createHeadlessHost(options = {}) {
2780
2887
  const contributions = [];
2781
2888
  let lastPreview = null;
2782
2889
  let lastPreviews = null;
2783
- const editor = makeEngineEditor(
2890
+ const { editor, fanOut } = makeEngineEditor(
2784
2891
  worker,
2785
2892
  contributions,
2786
2893
  (value) => {
@@ -2788,8 +2895,54 @@ async function createHeadlessHost(options = {}) {
2788
2895
  },
2789
2896
  (value) => {
2790
2897
  lastPreviews = value;
2791
- }
2898
+ },
2899
+ options.engineShaper === true
2792
2900
  );
2901
+ const loadDirect = (bytes) => {
2902
+ const reply = JSON.parse(
2903
+ options.defaultFont ? worker.loadDocumentDirect(seqCounter++, bytes, options.defaultFont) : worker.loadDocumentDirect(seqCounter++, bytes)
2904
+ );
2905
+ if (reply.kind === "documentLoaded") {
2906
+ try {
2907
+ worker.runResolveJson();
2908
+ } catch {
2909
+ }
2910
+ }
2911
+ return reply;
2912
+ };
2913
+ const loadFailure = (reply) => {
2914
+ const errKind = reply.kind === "loadFailed" ? reply.payload.error.kind : reply.kind;
2915
+ return new Error(`headless load failed (${reply.kind}: ${errKind})`);
2916
+ };
2917
+ const opened = [];
2918
+ const documentsBackend = {
2919
+ async exportPaged() {
2920
+ const reply = await editor.client.send({ kind: "exportPaged", payload: {} });
2921
+ if (reply.kind === "pagedExported") return Uint8Array.from(reply.payload.bytes);
2922
+ if (reply.kind === "pagedPartFailed") throw new Error(reply.payload.error);
2923
+ throw new Error(`unexpected reply: ${reply.kind}`);
2924
+ },
2925
+ async open(bytes, request) {
2926
+ let dirty = false;
2927
+ try {
2928
+ dirty = (await editor.client.documentMeta()).dirty;
2929
+ } catch {
2930
+ dirty = false;
2931
+ }
2932
+ if (dirty && options.confirmReplace && !await options.confirmReplace(request)) {
2933
+ return { opened: false, reason: "declined" };
2934
+ }
2935
+ const reply = loadDirect(bytes);
2936
+ if (reply.kind !== "documentLoaded") throw loadFailure(reply);
2937
+ fanOut(reply);
2938
+ opened.push({
2939
+ name: request.name,
2940
+ requester: request.requester.id,
2941
+ pageIds: reply.payload.pageIds
2942
+ });
2943
+ return { opened: true, pageIds: reply.payload.pageIds };
2944
+ }
2945
+ };
2793
2946
  let active = null;
2794
2947
  let currentHost = null;
2795
2948
  let disposed = false;
@@ -2814,6 +2967,7 @@ async function createHeadlessHost(options = {}) {
2814
2967
  // headlessly (the editor's real adapter currently serves null;
2815
2968
  // DESIGN.md §13.4). Absent → the no-bytes door.
2816
2969
  assetSource: options.assetSource,
2970
+ documents: documentsBackend,
2817
2971
  // Record the SCHEMA verbatim at registration — the panel registry
2818
2972
  // only ever sees the synthesized React panel, so the conformance
2819
2973
  // log gets the schema through this adapter seam (no host renderer
@@ -2875,7 +3029,8 @@ async function createHeadlessHost(options = {}) {
2875
3029
  rendering: ["overlay", "hitTest", "sceneLayer"],
2876
3030
  keybindings: true,
2877
3031
  storage: { blob: true },
2878
- secrets: { sources: true }
3032
+ secrets: { sources: true },
3033
+ documents: { export: true, open: true }
2879
3034
  },
2880
3035
  // Broad contribution declarations so the neutral DRIVER host (which
2881
3036
  // registers arbitrary contributions directly in 'warn' mode) never
@@ -2931,17 +3086,12 @@ async function createHeadlessHost(options = {}) {
2931
3086
  willSave,
2932
3087
  toolSettings,
2933
3088
  async load(idml) {
2934
- const raw = worker.loadDocumentDirect(seqCounter++, idml);
2935
- const reply = JSON.parse(raw);
2936
- if (reply.kind === "documentLoaded") {
2937
- try {
2938
- worker.runResolveJson();
2939
- } catch {
2940
- }
2941
- return reply.payload.pageIds;
2942
- }
2943
- const errKind = reply.kind === "loadFailed" ? reply.payload.error.kind : reply.kind;
2944
- throw new Error(`headless load failed (${reply.kind}: ${errKind})`);
3089
+ const reply = loadDirect(idml);
3090
+ if (reply.kind === "documentLoaded") return reply.payload.pageIds;
3091
+ throw loadFailure(reply);
3092
+ },
3093
+ openedDocuments() {
3094
+ return opened;
2945
3095
  },
2946
3096
  loadBundle(bundle) {
2947
3097
  if (active) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paged-media/plugin-sdk",
3
- "version": "0.2.40-canary.0",
3
+ "version": "0.2.41-canary.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  }
12
12
  },
13
13
  "dependencies": {
14
- "@paged-media/plugin-api": "0.2.40-canary.0"
14
+ "@paged-media/plugin-api": "0.2.41-canary.0"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "react": "^18.3.0"
@@ -22,7 +22,7 @@
22
22
  }
23
23
  },
24
24
  "devDependencies": {
25
- "@paged-media/canvas-wasm": "0.68.0",
25
+ "@paged-media/canvas-wasm": "0.69.0",
26
26
  "@types/node": "20.19.39",
27
27
  "@types/react": "^18.3.31",
28
28
  "react": "^18.3.1",