@paged-media/plugin-sdk 0.2.39-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 +70 -2
- package/dist/index.js +173 -20
- package/package.json +3 -3
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) => {
|
|
@@ -1006,10 +1012,12 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1006
1012
|
log.warn(
|
|
1007
1013
|
`contribute.sceneLayer().submit("${elementId}") ignored \u2014 the host wired no scene channel (probe supports("rendering.sceneLayer@1"))`
|
|
1008
1014
|
);
|
|
1009
|
-
return;
|
|
1015
|
+
return { fontFallbacks: [] };
|
|
1010
1016
|
}
|
|
1011
1017
|
submitted.add(elementId);
|
|
1012
|
-
await ch.submit(elementId, layer, manifest.id);
|
|
1018
|
+
const reply = await ch.submit(elementId, layer, manifest.id);
|
|
1019
|
+
const fontFallbacks = reply && Array.isArray(reply.fontFallbacks) ? [...reply.fontFallbacks] : [];
|
|
1020
|
+
return { fontFallbacks };
|
|
1013
1021
|
},
|
|
1014
1022
|
async clear(elementId) {
|
|
1015
1023
|
submitted.delete(elementId);
|
|
@@ -1096,7 +1104,7 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1096
1104
|
}
|
|
1097
1105
|
};
|
|
1098
1106
|
const foreignMetadataKey = (m) => {
|
|
1099
|
-
if (m.op === "setPluginMetadata") {
|
|
1107
|
+
if (m.op === "setPluginMetadata" || m.op === "setDocumentMetadata") {
|
|
1100
1108
|
return m.args.key === metadataKey(manifest) ? null : m.args.key;
|
|
1101
1109
|
}
|
|
1102
1110
|
if (m.op === "batch") {
|
|
@@ -1145,7 +1153,7 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1145
1153
|
if (denied !== null) return { applied: false, error: denied };
|
|
1146
1154
|
const foreign = foreignMetadataKey(mutation);
|
|
1147
1155
|
if (foreign !== null) {
|
|
1148
|
-
const error = `
|
|
1156
|
+
const error = `metadata key "${foreign}" is outside this plugin's namespace ("${metadataKey(manifest)}")`;
|
|
1149
1157
|
log.warn(error);
|
|
1150
1158
|
return { applied: false, error };
|
|
1151
1159
|
}
|
|
@@ -1382,6 +1390,28 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1382
1390
|
}
|
|
1383
1391
|
});
|
|
1384
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
|
+
},
|
|
1385
1415
|
async frameChain(storyId) {
|
|
1386
1416
|
requireDocRead("document.frameChain");
|
|
1387
1417
|
const reply = await getEditor().client.send({
|
|
@@ -1485,6 +1515,25 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1485
1515
|
// "no reader").
|
|
1486
1516
|
caret() {
|
|
1487
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
|
+
);
|
|
1488
1537
|
}
|
|
1489
1538
|
};
|
|
1490
1539
|
const overlay = {
|
|
@@ -2056,6 +2105,42 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
2056
2105
|
return nativeDocBackend.open(bytes);
|
|
2057
2106
|
}
|
|
2058
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
|
+
};
|
|
2059
2144
|
const clipboardBackend = options?.clipboard;
|
|
2060
2145
|
const clipboardGate = (door) => {
|
|
2061
2146
|
const grant = clipboardGrant();
|
|
@@ -2261,8 +2346,12 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
2261
2346
|
if (getEditor().text) {
|
|
2262
2347
|
featureSet.add("text.measure@1");
|
|
2263
2348
|
}
|
|
2349
|
+
if (getEditor().text?.measureMany) {
|
|
2350
|
+
featureSet.add("text.measureStrings@1");
|
|
2351
|
+
}
|
|
2264
2352
|
if (getEditor().sceneLayers) {
|
|
2265
2353
|
featureSet.add("rendering.sceneLayer@1");
|
|
2354
|
+
featureSet.add("rendering.sceneLayer.faces@1");
|
|
2266
2355
|
}
|
|
2267
2356
|
if (typeof getEditor().overlaySignals?.setToolPreviews === "function") {
|
|
2268
2357
|
featureSet.add("overlay.multiPreview@1");
|
|
@@ -2332,6 +2421,10 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
2332
2421
|
featureSet.add("document.readNative@1");
|
|
2333
2422
|
featureSet.add("document.openNative@1");
|
|
2334
2423
|
}
|
|
2424
|
+
if (options?.documents) {
|
|
2425
|
+
featureSet.add("documents.exportPaged@1");
|
|
2426
|
+
featureSet.add("documents.open@1");
|
|
2427
|
+
}
|
|
2335
2428
|
if (options?.workers) {
|
|
2336
2429
|
featureSet.add("workers@1");
|
|
2337
2430
|
}
|
|
@@ -2355,6 +2448,7 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
2355
2448
|
blob,
|
|
2356
2449
|
parts,
|
|
2357
2450
|
nativeDocument,
|
|
2451
|
+
documents,
|
|
2358
2452
|
network,
|
|
2359
2453
|
dataProviders,
|
|
2360
2454
|
diagnostics,
|
|
@@ -2609,7 +2703,7 @@ function inMemorySecretStore() {
|
|
|
2609
2703
|
};
|
|
2610
2704
|
}
|
|
2611
2705
|
var seqCounter = 1;
|
|
2612
|
-
function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews) {
|
|
2706
|
+
function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews, engineShaper = false) {
|
|
2613
2707
|
const protocol = worker.protocolVersion;
|
|
2614
2708
|
const listeners = /* @__PURE__ */ new Set();
|
|
2615
2709
|
const fanOut = (reply) => {
|
|
@@ -2769,7 +2863,23 @@ function makeEngineEditor(worker, recorder, onToolPreview, onToolPreviews) {
|
|
|
2769
2863
|
},
|
|
2770
2864
|
contentSelection: { contentSelection: null }
|
|
2771
2865
|
};
|
|
2772
|
-
|
|
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 };
|
|
2773
2883
|
}
|
|
2774
2884
|
async function createHeadlessHost(options = {}) {
|
|
2775
2885
|
const engine = await loadHeadlessEngine(options);
|
|
@@ -2777,7 +2887,7 @@ async function createHeadlessHost(options = {}) {
|
|
|
2777
2887
|
const contributions = [];
|
|
2778
2888
|
let lastPreview = null;
|
|
2779
2889
|
let lastPreviews = null;
|
|
2780
|
-
const editor = makeEngineEditor(
|
|
2890
|
+
const { editor, fanOut } = makeEngineEditor(
|
|
2781
2891
|
worker,
|
|
2782
2892
|
contributions,
|
|
2783
2893
|
(value) => {
|
|
@@ -2785,8 +2895,54 @@ async function createHeadlessHost(options = {}) {
|
|
|
2785
2895
|
},
|
|
2786
2896
|
(value) => {
|
|
2787
2897
|
lastPreviews = value;
|
|
2788
|
-
}
|
|
2898
|
+
},
|
|
2899
|
+
options.engineShaper === true
|
|
2789
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
|
+
};
|
|
2790
2946
|
let active = null;
|
|
2791
2947
|
let currentHost = null;
|
|
2792
2948
|
let disposed = false;
|
|
@@ -2811,6 +2967,7 @@ async function createHeadlessHost(options = {}) {
|
|
|
2811
2967
|
// headlessly (the editor's real adapter currently serves null;
|
|
2812
2968
|
// DESIGN.md §13.4). Absent → the no-bytes door.
|
|
2813
2969
|
assetSource: options.assetSource,
|
|
2970
|
+
documents: documentsBackend,
|
|
2814
2971
|
// Record the SCHEMA verbatim at registration — the panel registry
|
|
2815
2972
|
// only ever sees the synthesized React panel, so the conformance
|
|
2816
2973
|
// log gets the schema through this adapter seam (no host renderer
|
|
@@ -2872,7 +3029,8 @@ async function createHeadlessHost(options = {}) {
|
|
|
2872
3029
|
rendering: ["overlay", "hitTest", "sceneLayer"],
|
|
2873
3030
|
keybindings: true,
|
|
2874
3031
|
storage: { blob: true },
|
|
2875
|
-
secrets: { sources: true }
|
|
3032
|
+
secrets: { sources: true },
|
|
3033
|
+
documents: { export: true, open: true }
|
|
2876
3034
|
},
|
|
2877
3035
|
// Broad contribution declarations so the neutral DRIVER host (which
|
|
2878
3036
|
// registers arbitrary contributions directly in 'warn' mode) never
|
|
@@ -2928,17 +3086,12 @@ async function createHeadlessHost(options = {}) {
|
|
|
2928
3086
|
willSave,
|
|
2929
3087
|
toolSettings,
|
|
2930
3088
|
async load(idml) {
|
|
2931
|
-
const
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
}
|
|
2938
|
-
return reply.payload.pageIds;
|
|
2939
|
-
}
|
|
2940
|
-
const errKind = reply.kind === "loadFailed" ? reply.payload.error.kind : reply.kind;
|
|
2941
|
-
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;
|
|
2942
3095
|
},
|
|
2943
3096
|
loadBundle(bundle) {
|
|
2944
3097
|
if (active) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paged-media/plugin-sdk",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
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.
|
|
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",
|