@paged-media/plugin-sdk 0.2.37-canary.0 → 0.2.39-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 +46 -7
- package/dist/index.js +398 -33
- 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, 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, 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
|
/**
|
|
@@ -333,7 +333,19 @@ type BundleTrust = "first-party";
|
|
|
333
333
|
* omits `saveFile` still injects a valid backend and
|
|
334
334
|
* `supports("shell.saveFile@1")` reports the truth.
|
|
335
335
|
*/
|
|
336
|
-
type ShellBackend = Omit<ShellSurface, "saveFile"> & Partial<Pick<ShellSurface, "saveFile">>;
|
|
336
|
+
type ShellBackend = Omit<ShellSurface, "saveFile" | "enterEditContext"> & Partial<Pick<ShellSurface, "saveFile" | "enterEditContext">>;
|
|
337
|
+
/** The host's will-save registry (protocol 66 batch): the editor awaits
|
|
338
|
+
* every registered listener before it serialises the document. The SDK
|
|
339
|
+
* wraps each bundle listener so it is awaited and its errors contained. */
|
|
340
|
+
interface WillSaveBackend {
|
|
341
|
+
register(pluginId: string, listener: (e: WillSaveEvent) => Promise<void>): Disposable;
|
|
342
|
+
}
|
|
343
|
+
/** The host's tool-options store, read side: the values its tool-options
|
|
344
|
+
* UI holds per tool id, plus a change subscription. */
|
|
345
|
+
interface ToolSettingsBackend {
|
|
346
|
+
get(toolId: string): Record<string, ToolSettingValue>;
|
|
347
|
+
subscribe(toolId: string, listener: () => void): () => void;
|
|
348
|
+
}
|
|
337
349
|
interface CreateBundleHostOptions {
|
|
338
350
|
/**
|
|
339
351
|
* The host's trust assertion for this bundle (`plugin-trust-line.md`).
|
|
@@ -385,7 +397,14 @@ interface CreateBundleHostOptions {
|
|
|
385
397
|
* the editor's UI package and is injected here. When absent,
|
|
386
398
|
* `host.widgets` is the plain-textarea fallback and
|
|
387
399
|
* `supports("widgets.codeEditor@1")` answers false. */
|
|
388
|
-
widgets?: WidgetSurface
|
|
400
|
+
widgets?: Partial<WidgetSurface>;
|
|
401
|
+
/** The host's will-save registry. When absent, `document.onWillSave`
|
|
402
|
+
* holds listeners that never run and `supports("document.onWillSave@1")`
|
|
403
|
+
* is false. */
|
|
404
|
+
willSave?: WillSaveBackend;
|
|
405
|
+
/** The host's tool-options store. When absent, `host.tools.settings`
|
|
406
|
+
* answers `{}` and `supports("tools.settings@1")` is false. */
|
|
407
|
+
toolSettings?: ToolSettingsBackend;
|
|
389
408
|
/** Host-provided SCHEMA-PANEL renderer (W3.1): the editor's
|
|
390
409
|
* `SchemaPanelRenderer` that walks a `PanelSchema` through the
|
|
391
410
|
* catalog + subscribes to the bundle's bindings. When absent,
|
|
@@ -560,8 +579,20 @@ interface RecordedContribution {
|
|
|
560
579
|
id: string;
|
|
561
580
|
value: ToolContribution | PanelContribution | SchemaPanelContribution | CommandContribution | KeybindingContribution | OverlayContribution | EditContextContribution | ObjectTypeContribution | ImporterContribution | ExporterContribution;
|
|
562
581
|
}
|
|
563
|
-
interface HarnessOptions extends LoadHeadlessEngineOptions, Pick<CreateBundleHostOptions, "console" | "storage" | "capabilityMode" | "assetSource" | "blobStore" | "clipboard" | "secrets" | "bindingProviders"> {
|
|
582
|
+
interface HarnessOptions extends LoadHeadlessEngineOptions, Pick<CreateBundleHostOptions, "console" | "storage" | "capabilityMode" | "assetSource" | "blobStore" | "clipboard" | "secrets" | "bindingProviders" | "willSave" | "toolSettings"> {
|
|
564
583
|
}
|
|
584
|
+
/** A headless will-save registry: listeners register per plugin and
|
|
585
|
+
* `fire()` awaits them all, the way the editor's save path does — so a
|
|
586
|
+
* bundle's commit-before-save is assertable without an editor. */
|
|
587
|
+
declare function inMemoryWillSave(): WillSaveBackend & {
|
|
588
|
+
fire(): Promise<void>;
|
|
589
|
+
count(): number;
|
|
590
|
+
};
|
|
591
|
+
/** A headless tool-options store: what the editor's tool-options UI would
|
|
592
|
+
* hold, settable from a test with `set`. */
|
|
593
|
+
declare function inMemoryToolSettings(): ToolSettingsBackend & {
|
|
594
|
+
set(toolId: string, key: string, value: ToolSettingValue): void;
|
|
595
|
+
};
|
|
565
596
|
/** What `createHeadlessHost` resolves to: a real engine-backed host plus
|
|
566
597
|
* the conformance affordances (load an IDML, read the contribution log,
|
|
567
598
|
* load a bundle, dispose honestly). */
|
|
@@ -615,6 +646,13 @@ interface HeadlessHost {
|
|
|
615
646
|
* that a path it does not declare comes back as a typed refusal to
|
|
616
647
|
* fall through to core). */
|
|
617
648
|
readonly bindingProviders: BindingProviderBackend;
|
|
649
|
+
/** The will-save registry this host injected: `fire()` runs every
|
|
650
|
+
* bundle's `document.onWillSave` listener and awaits them, as a save
|
|
651
|
+
* would. */
|
|
652
|
+
readonly willSave: ReturnType<typeof inMemoryWillSave>;
|
|
653
|
+
/** The tool-options store this host injected: `set()` stands in for the
|
|
654
|
+
* editor's tool-options UI, and `host.tools` reads it back. */
|
|
655
|
+
readonly toolSettings: ReturnType<typeof inMemoryToolSettings>;
|
|
618
656
|
/** Load an IDML package into the headless document. Resolves to the
|
|
619
657
|
* loaded page ids (or throws on a parse failure). */
|
|
620
658
|
load(idml: Uint8Array): Promise<string[]>;
|
|
@@ -664,8 +702,9 @@ declare const API_VERSION = "0.2.0";
|
|
|
664
702
|
*/
|
|
665
703
|
declare function satisfiesApiVersion(range: string, version?: string): boolean;
|
|
666
704
|
|
|
667
|
-
/** The default widget catalog: a textarea CodeEditor
|
|
668
|
-
*
|
|
705
|
+
/** The default widget catalog: a textarea CodeEditor and a native colour
|
|
706
|
+
* input. Each member is replaced by the one the host app injects through
|
|
707
|
+
* `createBundleHost({ widgets })`; members it omits keep the fallback. */
|
|
669
708
|
declare const FALLBACK_WIDGETS: WidgetSurface;
|
|
670
709
|
|
|
671
710
|
/** One recorded `getFontFace` call. */
|
|
@@ -882,4 +921,4 @@ declare function contributeObjectType(host: BundleHost, contribution: ObjectType
|
|
|
882
921
|
*/
|
|
883
922
|
declare function contributeBindingProvider(host: BundleHost, contextType: string, provider: BindingProvider): BindingProviderHandle;
|
|
884
923
|
|
|
885
|
-
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, WASM_BUDGETS, WORKER_BUDGETS, type WorkerBackend, beginPageDrag, commitAndSelect, contributeBindingProvider, contributeEditContext, contributeObjectType, contributePanel, contributeSchemaPanel, contributeTool, createBindingProviderRegistry, createBundleHost, createDataProviderRegistry, createHeadlessHost, createRecordableAssetSource, defineBundle, endLocalFor, loadBundle, loadBundleWasm, loadHeadlessEngine, makeSchemaPanelComponent, protocolFromVersion, pxToPt, readVendoredWireVersion, resolveCanvasWasm, resolveGate, satisfiesApiVersion, toDisposable };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -93,8 +93,31 @@ function TextareaCodeEditor(props) {
|
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
+
function NativeColorPicker(props) {
|
|
97
|
+
const createElement2 = requireCreateElement(
|
|
98
|
+
"plugin-sdk: host.widgets.ColorPicker fell back to <input type=color>, but React is not installed. Install react, or inject a widget catalog via createBundleHost({ widgets })."
|
|
99
|
+
);
|
|
100
|
+
return createElement2("input", {
|
|
101
|
+
type: "color",
|
|
102
|
+
value: props.value,
|
|
103
|
+
disabled: props.disabled,
|
|
104
|
+
"aria-label": props.ariaLabel,
|
|
105
|
+
"data-color-picker-fallback": "native",
|
|
106
|
+
onChange: (e) => props.onChange(e.currentTarget.value),
|
|
107
|
+
onBlur: (e) => props.onCommit?.(e.currentTarget.value),
|
|
108
|
+
style: {
|
|
109
|
+
width: 40,
|
|
110
|
+
height: 24,
|
|
111
|
+
padding: 0,
|
|
112
|
+
border: "1px solid var(--pg-border)",
|
|
113
|
+
borderRadius: "var(--radius-sm, 4px)",
|
|
114
|
+
background: "var(--pg-bg)"
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
96
118
|
var FALLBACK_WIDGETS = {
|
|
97
|
-
CodeEditor: TextareaCodeEditor
|
|
119
|
+
CodeEditor: TextareaCodeEditor,
|
|
120
|
+
ColorPicker: NativeColorPicker
|
|
98
121
|
};
|
|
99
122
|
|
|
100
123
|
// src/schema-panel.tsx
|
|
@@ -139,6 +162,34 @@ function makeSchemaPanelComponent(contribution, bindings, renderer) {
|
|
|
139
162
|
}
|
|
140
163
|
|
|
141
164
|
// src/host-impl.ts
|
|
165
|
+
function isBytesSlot(m) {
|
|
166
|
+
if (m.op !== "replaceImageBytes") return false;
|
|
167
|
+
const b = m.args.bytes;
|
|
168
|
+
return Array.isArray(b) && b.length === 0;
|
|
169
|
+
}
|
|
170
|
+
function hasBytesSlot(m) {
|
|
171
|
+
if (isBytesSlot(m)) return true;
|
|
172
|
+
if (m.op === "batch") return m.args.ops.some((op) => hasBytesSlot(op));
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
function spliceBytesSlot(m, bytes) {
|
|
176
|
+
let filled = false;
|
|
177
|
+
const visit = (node) => {
|
|
178
|
+
if (filled) return node;
|
|
179
|
+
if (isBytesSlot(node)) {
|
|
180
|
+
filled = true;
|
|
181
|
+
return { ...node, args: { ...node.args, bytes } };
|
|
182
|
+
}
|
|
183
|
+
if (node.op === "batch") {
|
|
184
|
+
return {
|
|
185
|
+
...node,
|
|
186
|
+
args: { ...node.args, ops: node.args.ops.map((op) => visit(op)) }
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
return node;
|
|
190
|
+
};
|
|
191
|
+
return visit(m);
|
|
192
|
+
}
|
|
142
193
|
var HOST_FEATURES = [
|
|
143
194
|
"contribute.tool@1",
|
|
144
195
|
"contribute.panel@1",
|
|
@@ -200,6 +251,11 @@ var HOST_FEATURES = [
|
|
|
200
251
|
// v51) is the writer, so it is live whenever a bundle runs (the worker
|
|
201
252
|
// handshake guarantees a v51-protocol worker).
|
|
202
253
|
"storage.parts@1",
|
|
254
|
+
// Protocol 66 — `host.parts.delete`. Static for the same reason as @1:
|
|
255
|
+
// the pinned engine (the worker handshake refuses an older one) answers
|
|
256
|
+
// `deletePagedPart`, and the binary lane is an optimisation the SDK
|
|
257
|
+
// falls back from, not a different contract.
|
|
258
|
+
"storage.parts@2",
|
|
203
259
|
// B-22 (protocol v57) — the planar-region read door. STATIC, and the
|
|
204
260
|
// flag means exactly one thing: this SDK's document surface IMPLEMENTS
|
|
205
261
|
// `planarRegions` and forwards it (retiring the `host.editor.client`
|
|
@@ -210,6 +266,10 @@ var HOST_FEATURES = [
|
|
|
210
266
|
// honesty split as the protocol-ahead mutations (mutations.ts): the
|
|
211
267
|
// engine-level gate is the worker handshake, not `supports()`.
|
|
212
268
|
"document.planarRegions@1",
|
|
269
|
+
// v67 (RFI C-68) — the engine's point snapper. Static like the region
|
|
270
|
+
// door: the flag says this SDK forwards `snapPoint`; an engine older
|
|
271
|
+
// than v67 answers the point unsnapped, never a throw.
|
|
272
|
+
"document.snapPoint@1",
|
|
213
273
|
// DOC-03 structured whole-story read (canvas-wasm v54). Static for the same
|
|
214
274
|
// reason as storage.parts@1: the pinned engine is the server, and the worker
|
|
215
275
|
// handshake refuses a protocol older than the pin — so a running bundle is
|
|
@@ -464,6 +524,50 @@ function createBindingProviderRegistry() {
|
|
|
464
524
|
}
|
|
465
525
|
};
|
|
466
526
|
}
|
|
527
|
+
function checkImage(image) {
|
|
528
|
+
const { width, height, rgba } = image;
|
|
529
|
+
if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`contribute.sceneLayer().submitImage: ${rgba.length} bytes for a ${width}x${height} RGBA8 image (want width*height*4)`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function imageLayer(image) {
|
|
536
|
+
const [x, y, w, h2] = image.dest;
|
|
537
|
+
return {
|
|
538
|
+
items: [
|
|
539
|
+
{
|
|
540
|
+
kind: "image",
|
|
541
|
+
rgba: Array.from(image.rgba),
|
|
542
|
+
width: image.width,
|
|
543
|
+
height: image.height,
|
|
544
|
+
x,
|
|
545
|
+
y,
|
|
546
|
+
w,
|
|
547
|
+
h: h2
|
|
548
|
+
}
|
|
549
|
+
]
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function checkTiles(image, tiles) {
|
|
553
|
+
for (const t of tiles) {
|
|
554
|
+
if (t.width <= 0 || t.height <= 0 || t.x < 0 || t.y < 0 || t.x + t.width > image.width || t.y + t.height > image.height || t.rgba.length !== t.width * t.height * 4) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
`contribute.sceneLayer().submitImageTiles: tile ${t.x},${t.y} ${t.width}x${t.height} (${t.rgba.length} bytes) does not fit the ${image.width}x${image.height} image`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function patchImage(image, tiles) {
|
|
562
|
+
const stride = image.width * 4;
|
|
563
|
+
for (const t of tiles) {
|
|
564
|
+
const row = t.width * 4;
|
|
565
|
+
for (let j = 0; j < t.height; j++) {
|
|
566
|
+
const dst = (t.y + j) * stride + t.x * 4;
|
|
567
|
+
image.rgba.set(t.rgba.subarray(j * row, j * row + row), dst);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
467
571
|
function createBundleHost(getEditor, manifest, options) {
|
|
468
572
|
const store = new DisposableStore();
|
|
469
573
|
const sink = options?.console ?? console;
|
|
@@ -892,6 +996,8 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
892
996
|
'capabilities.rendering must include "sceneLayer"'
|
|
893
997
|
);
|
|
894
998
|
const submitted = /* @__PURE__ */ new Set();
|
|
999
|
+
const imaged = /* @__PURE__ */ new Map();
|
|
1000
|
+
const retained = /* @__PURE__ */ new Map();
|
|
895
1001
|
const channel = () => getEditor().sceneLayers;
|
|
896
1002
|
const surface = {
|
|
897
1003
|
async submit(elementId, layer) {
|
|
@@ -907,14 +1013,83 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
907
1013
|
},
|
|
908
1014
|
async clear(elementId) {
|
|
909
1015
|
submitted.delete(elementId);
|
|
1016
|
+
imaged.delete(elementId);
|
|
1017
|
+
retained.delete(elementId);
|
|
910
1018
|
await channel()?.clear(elementId);
|
|
911
1019
|
},
|
|
1020
|
+
// Protocol 66 — the binary scene-image lane, with the SDK owning
|
|
1021
|
+
// the fallback so a bundle calls one method on every host.
|
|
1022
|
+
async submitImage(elementId, image, options2) {
|
|
1023
|
+
const ch = channel();
|
|
1024
|
+
if (!ch) {
|
|
1025
|
+
log.warn(
|
|
1026
|
+
`contribute.sceneLayer().submitImage("${elementId}") ignored \u2014 the host wired no scene channel (probe supports("rendering.sceneLayer@1"))`
|
|
1027
|
+
);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
checkImage(image);
|
|
1031
|
+
submitted.add(elementId);
|
|
1032
|
+
imaged.set(elementId, { width: image.width, height: image.height });
|
|
1033
|
+
if (ch.submitImage && ch.submitImageTiles) {
|
|
1034
|
+
retained.delete(elementId);
|
|
1035
|
+
await ch.submitImage(
|
|
1036
|
+
elementId,
|
|
1037
|
+
image,
|
|
1038
|
+
manifest.id,
|
|
1039
|
+
options2?.transfer ?? false
|
|
1040
|
+
);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const copy = {
|
|
1044
|
+
rgba: new Uint8Array(image.rgba),
|
|
1045
|
+
width: image.width,
|
|
1046
|
+
height: image.height,
|
|
1047
|
+
dest: [...image.dest]
|
|
1048
|
+
};
|
|
1049
|
+
retained.set(elementId, copy);
|
|
1050
|
+
await ch.submit(elementId, imageLayer(copy), manifest.id);
|
|
1051
|
+
},
|
|
1052
|
+
async submitImageTiles(elementId, tiles, options2) {
|
|
1053
|
+
const ch = channel();
|
|
1054
|
+
if (!ch) {
|
|
1055
|
+
log.warn(
|
|
1056
|
+
`contribute.sceneLayer().submitImageTiles("${elementId}") ignored \u2014 the host wired no scene channel`
|
|
1057
|
+
);
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const size = imaged.get(elementId);
|
|
1061
|
+
if (!size) {
|
|
1062
|
+
throw new Error(
|
|
1063
|
+
`contribute.sceneLayer().submitImageTiles("${elementId}"): no image was submitted for this element \u2014 call submitImage first`
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
checkTiles(size, tiles);
|
|
1067
|
+
if (ch.submitImage && ch.submitImageTiles) {
|
|
1068
|
+
await ch.submitImageTiles(
|
|
1069
|
+
elementId,
|
|
1070
|
+
tiles,
|
|
1071
|
+
manifest.id,
|
|
1072
|
+
options2?.transfer ?? false
|
|
1073
|
+
);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const image = retained.get(elementId);
|
|
1077
|
+
if (!image) {
|
|
1078
|
+
throw new Error(
|
|
1079
|
+
`contribute.sceneLayer().submitImageTiles("${elementId}"): the retained image is gone (the binary lane went away mid-session)`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
patchImage(image, tiles);
|
|
1083
|
+
await ch.submit(elementId, imageLayer(image), manifest.id);
|
|
1084
|
+
},
|
|
912
1085
|
dispose() {
|
|
913
1086
|
const ch = channel();
|
|
914
1087
|
if (ch) {
|
|
915
1088
|
for (const id of submitted) void ch.clear(id);
|
|
916
1089
|
}
|
|
917
1090
|
submitted.clear();
|
|
1091
|
+
imaged.clear();
|
|
1092
|
+
retained.clear();
|
|
918
1093
|
}
|
|
919
1094
|
};
|
|
920
1095
|
return store.add(surface);
|
|
@@ -965,33 +1140,66 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
965
1140
|
walk(roots, null);
|
|
966
1141
|
return index;
|
|
967
1142
|
};
|
|
1143
|
+
const gateRawWrite = (mutation, door) => {
|
|
1144
|
+
const denied = denyWrite(hasDoc("write"), door, "capabilities.document.write");
|
|
1145
|
+
if (denied !== null) return { applied: false, error: denied };
|
|
1146
|
+
const foreign = foreignMetadataKey(mutation);
|
|
1147
|
+
if (foreign !== null) {
|
|
1148
|
+
const error = `setPluginMetadata key "${foreign}" is outside this plugin's namespace ("${metadataKey(manifest)}")`;
|
|
1149
|
+
log.warn(error);
|
|
1150
|
+
return { applied: false, error };
|
|
1151
|
+
}
|
|
1152
|
+
return null;
|
|
1153
|
+
};
|
|
1154
|
+
const mutateOutcome = (reply) => {
|
|
1155
|
+
if (reply.kind === "mutationApplied") {
|
|
1156
|
+
const minted = reply.payload.minted;
|
|
1157
|
+
return {
|
|
1158
|
+
applied: true,
|
|
1159
|
+
createdId: reply.payload.createdId ?? null,
|
|
1160
|
+
pageIds: reply.payload.pageIds,
|
|
1161
|
+
...minted !== void 0 ? { minted } : {}
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
return {
|
|
1165
|
+
applied: false,
|
|
1166
|
+
error: reply.kind === "mutationFailed" ? reply.payload : reply
|
|
1167
|
+
};
|
|
1168
|
+
};
|
|
968
1169
|
const document = {
|
|
969
1170
|
async mutate(mutation) {
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
"document.mutate",
|
|
973
|
-
"capabilities.document.write"
|
|
974
|
-
);
|
|
975
|
-
if (denied !== null) return { applied: false, error: denied };
|
|
976
|
-
const foreign = foreignMetadataKey(mutation);
|
|
977
|
-
if (foreign !== null) {
|
|
978
|
-
const error = `setPluginMetadata key "${foreign}" is outside this plugin's namespace ("${metadataKey(manifest)}")`;
|
|
979
|
-
log.warn(error);
|
|
980
|
-
return { applied: false, error };
|
|
981
|
-
}
|
|
1171
|
+
const gated = gateRawWrite(mutation, "document.mutate");
|
|
1172
|
+
if (gated !== null) return gated;
|
|
982
1173
|
try {
|
|
983
1174
|
const reply = await getEditor().client.mutate(mutation);
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1175
|
+
return mutateOutcome(reply);
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
return { applied: false, error };
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
async mutateWithBytes(mutation, bytes, options2) {
|
|
1181
|
+
const gated = gateRawWrite(mutation, "document.mutateWithBytes");
|
|
1182
|
+
if (gated !== null) return gated;
|
|
1183
|
+
if (!hasBytesSlot(mutation)) {
|
|
991
1184
|
return {
|
|
992
1185
|
applied: false,
|
|
993
|
-
error:
|
|
1186
|
+
error: "mutateWithBytes: no replaceImageBytes with an empty `bytes` slot to fill"
|
|
994
1187
|
};
|
|
1188
|
+
}
|
|
1189
|
+
try {
|
|
1190
|
+
const editor = getEditor();
|
|
1191
|
+
if (typeof editor.mutateWithBytes === "function") {
|
|
1192
|
+
const reply2 = await editor.mutateWithBytes(
|
|
1193
|
+
mutation,
|
|
1194
|
+
bytes,
|
|
1195
|
+
options2?.transfer === true
|
|
1196
|
+
);
|
|
1197
|
+
return mutateOutcome(reply2);
|
|
1198
|
+
}
|
|
1199
|
+
const reply = await editor.client.mutate(
|
|
1200
|
+
spliceBytesSlot(mutation, Array.from(bytes))
|
|
1201
|
+
);
|
|
1202
|
+
return mutateOutcome(reply);
|
|
995
1203
|
} catch (error) {
|
|
996
1204
|
return { applied: false, error };
|
|
997
1205
|
}
|
|
@@ -1092,6 +1300,24 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1092
1300
|
return refusal(`the region read door failed: ${String(error)}`);
|
|
1093
1301
|
}
|
|
1094
1302
|
},
|
|
1303
|
+
async snapPoint(query) {
|
|
1304
|
+
requireDocRead("document.snapPoint");
|
|
1305
|
+
const unsnapped = () => ({
|
|
1306
|
+
point: [query.point[0], query.point[1]],
|
|
1307
|
+
snapped: false,
|
|
1308
|
+
lines: [],
|
|
1309
|
+
tolerancePt: 0
|
|
1310
|
+
});
|
|
1311
|
+
try {
|
|
1312
|
+
const reply = await getEditor().client.send({
|
|
1313
|
+
kind: "requestSnapPoint",
|
|
1314
|
+
payload: { query }
|
|
1315
|
+
});
|
|
1316
|
+
return reply.kind === "snapPoint" ? reply.payload.result : unsnapped();
|
|
1317
|
+
} catch {
|
|
1318
|
+
return unsnapped();
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1095
1321
|
async tree() {
|
|
1096
1322
|
requireDocRead("document.tree");
|
|
1097
1323
|
const reply = await getEditor().client.send({
|
|
@@ -1185,6 +1411,24 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1185
1411
|
}
|
|
1186
1412
|
});
|
|
1187
1413
|
return store.add(toDisposable(off));
|
|
1414
|
+
},
|
|
1415
|
+
// Protocol 66 batch — the save hook. The host awaits every
|
|
1416
|
+
// registered listener before it serialises the document; the wrapper
|
|
1417
|
+
// makes a bundle's failure a logged event, never a failed save.
|
|
1418
|
+
onWillSave(listener) {
|
|
1419
|
+
const backend = options?.willSave;
|
|
1420
|
+
if (!backend) {
|
|
1421
|
+
return store.add(toDisposable(() => {
|
|
1422
|
+
}));
|
|
1423
|
+
}
|
|
1424
|
+
const wrapped = async (e) => {
|
|
1425
|
+
try {
|
|
1426
|
+
await listener(e);
|
|
1427
|
+
} catch (err) {
|
|
1428
|
+
log.error(`document.onWillSave listener failed: ${String(err)}`);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
return store.add(backend.register(manifest.id, wrapped));
|
|
1188
1432
|
}
|
|
1189
1433
|
};
|
|
1190
1434
|
const selection = {
|
|
@@ -1474,9 +1718,44 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1474
1718
|
`shell.saveFile("${saveOptions.suggestedName}") ignored \u2014 the host app wired no file saver (probe with supports("shell.saveFile@1"))`
|
|
1475
1719
|
);
|
|
1476
1720
|
return false;
|
|
1721
|
+
},
|
|
1722
|
+
// Programmatic entry into one of THIS bundle's edit contexts — the
|
|
1723
|
+
// same stack push a double-click performs. A foreign or unregistered
|
|
1724
|
+
// type is a bug in the bundle, so it throws; a host that cannot enter
|
|
1725
|
+
// answers false.
|
|
1726
|
+
async enterEditContext(type, elementId) {
|
|
1727
|
+
if (!registeredContexts.has(type)) {
|
|
1728
|
+
throw new Error(
|
|
1729
|
+
`plugin-api: shell.enterEditContext("${type}") \u2014 ${manifest.id} has registered no edit context of that type. A bundle enters only its own contexts: call contribute.editContext({ type: "${type}", \u2026 }) first.`
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
if (!shellBackend?.enterEditContext) {
|
|
1733
|
+
log.warn(
|
|
1734
|
+
`shell.enterEditContext("${type}") ignored \u2014 the host app wired no context entry (probe with supports("shell.enterEditContext@1"))`
|
|
1735
|
+
);
|
|
1736
|
+
return false;
|
|
1737
|
+
}
|
|
1738
|
+
return shellBackend.enterEditContext(type, elementId);
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
const toolSettingsBackend = options?.toolSettings;
|
|
1742
|
+
const tools = {
|
|
1743
|
+
settings(toolId) {
|
|
1744
|
+
assertNamespaced(toolId, "tools.settings tool");
|
|
1745
|
+
return { ...toolSettingsBackend?.get(toolId) ?? {} };
|
|
1746
|
+
},
|
|
1747
|
+
onDidChangeSettings(toolId, listener) {
|
|
1748
|
+
assertNamespaced(toolId, "tools.onDidChangeSettings tool");
|
|
1749
|
+
if (!toolSettingsBackend) return store.add(toDisposable(() => {
|
|
1750
|
+
}));
|
|
1751
|
+
const off = toolSettingsBackend.subscribe(
|
|
1752
|
+
toolId,
|
|
1753
|
+
() => listener({ ...toolSettingsBackend.get(toolId) })
|
|
1754
|
+
);
|
|
1755
|
+
return store.add(toDisposable(off));
|
|
1477
1756
|
}
|
|
1478
1757
|
};
|
|
1479
|
-
const widgets = options?.widgets
|
|
1758
|
+
const widgets = { ...FALLBACK_WIDGETS, ...options?.widgets };
|
|
1480
1759
|
const assetSource = options?.assetSource;
|
|
1481
1760
|
const assets = {
|
|
1482
1761
|
async getFontFace(family, style) {
|
|
@@ -1673,10 +1952,20 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1673
1952
|
};
|
|
1674
1953
|
const parts = {
|
|
1675
1954
|
async write(path, bytes) {
|
|
1955
|
+
const full = partsFull(path);
|
|
1956
|
+
const binary = getEditor().parts;
|
|
1957
|
+
if (binary) {
|
|
1958
|
+
try {
|
|
1959
|
+
await binary.write(full, bytes, manifest.id);
|
|
1960
|
+
} catch (err) {
|
|
1961
|
+
throw new Error(`host.parts.write("${path}"): ${String(err)}`);
|
|
1962
|
+
}
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1676
1965
|
const reply = await getEditor().client.send({
|
|
1677
1966
|
kind: "writePagedPart",
|
|
1678
1967
|
payload: {
|
|
1679
|
-
path:
|
|
1968
|
+
path: full,
|
|
1680
1969
|
bytes: Array.from(bytes),
|
|
1681
1970
|
// C-34 — DECLARE WHO IS WRITING, so the engine
|
|
1682
1971
|
// confines this to `paged/<id>/` instead of trusting that we
|
|
@@ -1692,14 +1981,6 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1692
1981
|
// probe needed — there is no degraded path to choose between.
|
|
1693
1982
|
caller: manifest.id
|
|
1694
1983
|
}
|
|
1695
|
-
// THE CAST, named rather than left to be discovered. `wire.d.ts`
|
|
1696
|
-
// is vendored from the PUBLISHED `@paged-media/canvas-wasm`
|
|
1697
|
-
// (0.61.0), whose `writePagedPart` has no `caller` — the field
|
|
1698
|
-
// exists in core today and reaches these types only when the
|
|
1699
|
-
// next canvas-wasm publishes and `sync-wire` runs. Same escape-hatch
|
|
1700
|
-
// shape the repo already uses elsewhere: a cast pointing at a
|
|
1701
|
-
// contract that EXISTS AND IS COMMITTED, not at a hope. It
|
|
1702
|
-
// deletes itself at the repin.
|
|
1703
1984
|
});
|
|
1704
1985
|
if (reply.kind !== "pagedPartWritten") {
|
|
1705
1986
|
const err = reply.kind === "pagedPartFailed" ? reply.payload.error : `unexpected ${reply.kind}`;
|
|
@@ -1707,9 +1988,12 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1707
1988
|
}
|
|
1708
1989
|
},
|
|
1709
1990
|
async read(path) {
|
|
1991
|
+
const full = partsFull(path);
|
|
1992
|
+
const binary = getEditor().parts;
|
|
1993
|
+
if (binary) return binary.read(full);
|
|
1710
1994
|
const reply = await getEditor().client.send({
|
|
1711
1995
|
kind: "readPagedPart",
|
|
1712
|
-
payload: { path:
|
|
1996
|
+
payload: { path: full }
|
|
1713
1997
|
});
|
|
1714
1998
|
if (reply.kind === "pagedPartRead") {
|
|
1715
1999
|
return reply.payload.found ? Uint8Array.from(reply.payload.bytes) : null;
|
|
@@ -1725,6 +2009,27 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1725
2009
|
return reply.payload.paths.map(
|
|
1726
2010
|
(p) => p.startsWith(partsBase) ? p.slice(partsBase.length) : p
|
|
1727
2011
|
);
|
|
2012
|
+
},
|
|
2013
|
+
// Protocol 66 — `deletePagedPart`. The engine tombstones a part the
|
|
2014
|
+
// loaded file carries, so it leaves the next save rather than
|
|
2015
|
+
// reappearing from the source container.
|
|
2016
|
+
async delete(path) {
|
|
2017
|
+
const full = partsFull(path);
|
|
2018
|
+
const binary = getEditor().parts;
|
|
2019
|
+
if (binary) {
|
|
2020
|
+
try {
|
|
2021
|
+
return await binary.delete(full, manifest.id);
|
|
2022
|
+
} catch (err2) {
|
|
2023
|
+
throw new Error(`host.parts.delete("${path}"): ${String(err2)}`);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
const reply = await getEditor().client.send({
|
|
2027
|
+
kind: "deletePagedPart",
|
|
2028
|
+
payload: { path: full, caller: manifest.id }
|
|
2029
|
+
});
|
|
2030
|
+
if (reply.kind === "pagedPartDeleted") return reply.payload.existed;
|
|
2031
|
+
const err = reply.kind === "pagedPartFailed" ? reply.payload.error : `unexpected ${reply.kind}`;
|
|
2032
|
+
throw new Error(`host.parts.delete("${path}"): ${err}`);
|
|
1728
2033
|
}
|
|
1729
2034
|
};
|
|
1730
2035
|
const nativeDocBackend = options?.nativeDocument;
|
|
@@ -1972,9 +2277,30 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
1972
2277
|
featureSet.add("shell.saveFile@1");
|
|
1973
2278
|
}
|
|
1974
2279
|
}
|
|
1975
|
-
if (options?.widgets) {
|
|
2280
|
+
if (options?.widgets?.CodeEditor) {
|
|
1976
2281
|
featureSet.add("widgets.codeEditor@1");
|
|
1977
2282
|
}
|
|
2283
|
+
if (options?.widgets?.ColorPicker) {
|
|
2284
|
+
featureSet.add("widgets.colorPicker@1");
|
|
2285
|
+
}
|
|
2286
|
+
if (options?.willSave) {
|
|
2287
|
+
featureSet.add("document.onWillSave@1");
|
|
2288
|
+
}
|
|
2289
|
+
if (typeof options?.shell?.enterEditContext === "function") {
|
|
2290
|
+
featureSet.add("shell.enterEditContext@1");
|
|
2291
|
+
}
|
|
2292
|
+
if (options?.toolSettings) {
|
|
2293
|
+
featureSet.add("tools.settings@1");
|
|
2294
|
+
}
|
|
2295
|
+
{
|
|
2296
|
+
const sl = getEditor().sceneLayers;
|
|
2297
|
+
if (typeof sl?.submitImage === "function" && typeof sl?.submitImageTiles === "function") {
|
|
2298
|
+
featureSet.add("rendering.sceneLayer.binary@1");
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
if (typeof getEditor().mutateWithBytes === "function") {
|
|
2302
|
+
featureSet.add("document.mutateBinary@1");
|
|
2303
|
+
}
|
|
1978
2304
|
if (options?.schemaPanelRenderer) {
|
|
1979
2305
|
featureSet.add("schemaPanel.renderer@1");
|
|
1980
2306
|
}
|
|
@@ -2040,6 +2366,7 @@ function createBundleHost(getEditor, manifest, options) {
|
|
|
2040
2366
|
workers,
|
|
2041
2367
|
secrets,
|
|
2042
2368
|
clipboard,
|
|
2369
|
+
tools,
|
|
2043
2370
|
supports: (feature) => featureSet.has(feature),
|
|
2044
2371
|
get editor() {
|
|
2045
2372
|
return getEditor();
|
|
@@ -2186,6 +2513,36 @@ async function loadHeadlessEngine(options = {}) {
|
|
|
2186
2513
|
}
|
|
2187
2514
|
|
|
2188
2515
|
// src/harness.ts
|
|
2516
|
+
function inMemoryWillSave() {
|
|
2517
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2518
|
+
return {
|
|
2519
|
+
register(_pluginId, listener) {
|
|
2520
|
+
listeners.add(listener);
|
|
2521
|
+
return { dispose: () => void listeners.delete(listener) };
|
|
2522
|
+
},
|
|
2523
|
+
async fire() {
|
|
2524
|
+
await Promise.all([...listeners].map((l) => l({ format: "paged" })));
|
|
2525
|
+
},
|
|
2526
|
+
count: () => listeners.size
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
function inMemoryToolSettings() {
|
|
2530
|
+
const values = /* @__PURE__ */ new Map();
|
|
2531
|
+
const subs = /* @__PURE__ */ new Map();
|
|
2532
|
+
return {
|
|
2533
|
+
get: (toolId) => ({ ...values.get(toolId) ?? {} }),
|
|
2534
|
+
subscribe(toolId, listener) {
|
|
2535
|
+
const set = subs.get(toolId) ?? /* @__PURE__ */ new Set();
|
|
2536
|
+
set.add(listener);
|
|
2537
|
+
subs.set(toolId, set);
|
|
2538
|
+
return () => void set.delete(listener);
|
|
2539
|
+
},
|
|
2540
|
+
set(toolId, key, value) {
|
|
2541
|
+
values.set(toolId, { ...values.get(toolId) ?? {}, [key]: value });
|
|
2542
|
+
for (const l of subs.get(toolId) ?? []) l();
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2189
2546
|
function inMemoryBlobStore() {
|
|
2190
2547
|
const byPlugin = /* @__PURE__ */ new Map();
|
|
2191
2548
|
const dir = (id) => {
|
|
@@ -2437,6 +2794,8 @@ async function createHeadlessHost(options = {}) {
|
|
|
2437
2794
|
const clipboard = options.clipboard ?? inMemoryClipboard();
|
|
2438
2795
|
const secrets = options.secrets ?? inMemorySecretStore();
|
|
2439
2796
|
const bindingProviders = options.bindingProviders ?? createBindingProviderRegistry();
|
|
2797
|
+
const willSave = inMemoryWillSave();
|
|
2798
|
+
const toolSettings = inMemoryToolSettings();
|
|
2440
2799
|
const buildHost = (manifest, mode) => createBundleHost(() => editor, manifest, {
|
|
2441
2800
|
console: options.console,
|
|
2442
2801
|
storage: options.storage,
|
|
@@ -2444,6 +2803,8 @@ async function createHeadlessHost(options = {}) {
|
|
|
2444
2803
|
clipboard,
|
|
2445
2804
|
secrets,
|
|
2446
2805
|
bindingProviders,
|
|
2806
|
+
willSave: options.willSave ?? willSave,
|
|
2807
|
+
toolSettings: options.toolSettings ?? toolSettings,
|
|
2447
2808
|
capabilityMode: mode,
|
|
2448
2809
|
// W-06 — a recordable fake asset source the conformance harness
|
|
2449
2810
|
// can pass so a bundle's `@font-face` byte path is exercisable
|
|
@@ -2564,6 +2925,8 @@ async function createHeadlessHost(options = {}) {
|
|
|
2564
2925
|
return lastPreviews;
|
|
2565
2926
|
},
|
|
2566
2927
|
bindingProviders,
|
|
2928
|
+
willSave,
|
|
2929
|
+
toolSettings,
|
|
2567
2930
|
async load(idml) {
|
|
2568
2931
|
const raw = worker.loadDocumentDirect(seqCounter++, idml);
|
|
2569
2932
|
const reply = JSON.parse(raw);
|
|
@@ -2951,6 +3314,8 @@ export {
|
|
|
2951
3314
|
createRecordableAssetSource,
|
|
2952
3315
|
defineBundle,
|
|
2953
3316
|
endLocalFor,
|
|
3317
|
+
inMemoryToolSettings,
|
|
3318
|
+
inMemoryWillSave,
|
|
2954
3319
|
loadBundle,
|
|
2955
3320
|
loadBundleWasm,
|
|
2956
3321
|
loadHeadlessEngine,
|
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.39-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.39-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.67.0",
|
|
26
26
|
"@types/node": "20.19.39",
|
|
27
27
|
"@types/react": "^18.3.31",
|
|
28
28
|
"react": "^18.3.1",
|