@paged-media/plugin-api 0.2.27-canary.1 → 0.2.29-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/host.d.ts CHANGED
@@ -1,8 +1,10 @@
1
- import type { CollectionName, DocumentMeta, ElementGeometryItem, ElementId, HitFilter, HitResult, Mutation, PageId, ElementProperties, PathAnchorsResult, SceneTreeNode, SelectionMode } from "./wire";
1
+ import type { CollectionName, DocumentMeta, ElementGeometryItem, ElementId, HitFilter, HitResult, Mutation, PageId, ElementProperties, PathAnchorsResult, PathAnchorTriple, SceneTreeNode, SelectionMode } from "./wire";
2
2
  import type { CommandContribution, ExporterContribution, ImporterContribution, KeybindingContribution, OverlayContribution, PagedEditor, PanelContribution, ToolContribution, ToolPreviewShape } from "./editor";
3
3
  import type { SceneLayer } from "./wire";
4
4
  import type { AssetSurface } from "./assets";
5
+ import type { BindingProvider, BindingProviderHandle } from "./binding-provider";
5
6
  import type { ClipboardSurface } from "./clipboard";
7
+ import type { MutationInput } from "./mutations";
6
8
  import type { PluginManifest } from "./manifest";
7
9
  import type { SchemaPanelContribution } from "./panel-schema";
8
10
  import type { WidgetSurface } from "./widgets";
@@ -74,7 +76,25 @@ export interface EditContextContribution {
74
76
  * names. Empty = no restriction (all tools stay available). */
75
77
  toolIds?: string[];
76
78
  /** Panel ids the cockpit emphasizes / raises on enter (the panel-set
77
- * swap). The shell opens/raises these; it does not hide others. */
79
+ * swap).
80
+ *
81
+ * These are the context's OWN panels. There is deliberately no sibling
82
+ * field naming the SHARED host panels a context serves (ADR-023), and
83
+ * the reason is worth stating because the gap looks like an omission:
84
+ * a plugin naming `paged.layers` here would be host-panel IDENTITY in
85
+ * plugin code — the exact coupling binding providers removed from the
86
+ * value lane — and it could drift from `provides`, which is the only
87
+ * declaration that decides who actually answers. The host infers
88
+ * "serves" from `BindingProviderScope` instead (DESIGN.md §18.12).
89
+ *
90
+ * WHAT THE HOST GUARANTEES, corrected 2026-08-05: it OPENS these. It
91
+ * raises them too, EXCEPT when raising would displace a panel the
92
+ * entering context's own binding providers serve — because in a dock
93
+ * that shows one panel at a time, "raise mine" IS "hide the shared
94
+ * one", and hiding it at the instant it retargets defeats the whole
95
+ * point. (This comment used to promise "it does not hide others"
96
+ * unconditionally; that was never true in such a dock.) A withheld
97
+ * raise still opens the tab, so the surface is reachable either way. */
78
98
  panelIds?: string[];
79
99
  /** Called when the context becomes active (after the stack push + the
80
100
  * scope narrowing). The element entered on is passed so the hook can
@@ -252,6 +272,35 @@ export interface ContributionSurface {
252
272
  * `contributes.exporters[]`.
253
273
  */
254
274
  exporter(contribution: ExporterContribution): Disposable;
275
+ /**
276
+ * Register a BINDING PROVIDER (ADR-023 phase A): while the edit
277
+ * context named by `contextType` is ACTIVE, this bundle resolves what
278
+ * the HOST's own panels bind to — typed property paths, named
279
+ * collections, and first refusal on structural mutation ops. Reads and
280
+ * writes both.
281
+ *
282
+ * This is the inversion the panel-duplication problem needs: instead
283
+ * of minting a fourth Layers panel, a bundle serves the ONE host
284
+ * Layers panel's values for its own content type. See
285
+ * binding-provider.ts for the three-lane shape, the core-vocabulary-
286
+ * only rule, and the precedence model.
287
+ *
288
+ * LIFETIME is borrowed, not declared: `contextType` must be a type
289
+ * this SAME bundle already registered through `contribute.editContext`
290
+ * (an unregistered type is refused loudly — a provider that can never
291
+ * activate is a bug, not a stance). The capability gate is that
292
+ * context's: `contributes.editContexts[]` must declare the type. There
293
+ * is deliberately NO separate manifest field and NO separate
294
+ * capability — the authority a provider exercises is the authority the
295
+ * active context already holds over the selection it owns.
296
+ *
297
+ * Always present. When the host wires no shared registry, the door
298
+ * warns + returns an inert handle (nothing will ever consult the
299
+ * provider) and `supports("bindings.provider@1")` is false; probe
300
+ * `supports("contribute.bindingProvider@1")` for whether this SDK has
301
+ * the door at all.
302
+ */
303
+ bindingProvider(contextType: string, provider: BindingProvider): BindingProviderHandle;
255
304
  /**
256
305
  * Open a SCENE-LAYER surface (C-1): submit vector content that renders
257
306
  * INSIDE a frame, in frame-content coordinates — core applies the
@@ -516,8 +565,75 @@ export interface RunContent {
516
565
  position?: string | null;
517
566
  tracking?: number | null;
518
567
  }
568
+ /**
569
+ * B-22 (protocol v57) — one FACE of a planar arrangement: a connected
570
+ * region of the plane whose containment signature is constant. This is
571
+ * the level BELOW element hit-testing (the areas overlapping paths
572
+ * divide the plane into), which is what a Shape Builder needs.
573
+ *
574
+ * Coordinates are in the RAW path space `pathAnchors` reports —
575
+ * per-element `itemTransform`s are NOT composed in (the arrangement runs
576
+ * on the anchors as stored, exactly like `pathfinderBoolean`). A consumer
577
+ * that draws faces on canvas maps them with the frontmost input's
578
+ * `itemTransform`, the same chain the anchor overlays use.
579
+ */
580
+ export interface PlanarFace {
581
+ /** Stable id (`"<signature>#<component>"`, e.g. `"0-1#0"`) — stable
582
+ * across calls with the same inputs, which is what lets a hovered
583
+ * face's id ride straight into the `pathfinderFaces` mutation
584
+ * (mutations.ts) without a second query. */
585
+ id: string;
586
+ /** Indices into the REQUEST's `elementIds` whose interior contains
587
+ * this face. */
588
+ signature: number[];
589
+ /** The face outline (closed). A face with holes carries them as extra
590
+ * contours. */
591
+ anchors: PathAnchorTriple[];
592
+ /** Per-contour boundaries into `anchors`. */
593
+ subpathStarts: number[];
594
+ /** Unsigned area, outer contour minus holes. */
595
+ area: number;
596
+ /** A point strictly inside the face — what a hover highlight or a fill
597
+ * drop keys off without re-deriving containment. */
598
+ inside: [number, number];
599
+ }
600
+ /**
601
+ * B-22 (protocol v57) — the full result of `document.planarRegions`.
602
+ *
603
+ * Deliberately NOT a bare face array: the door has THREE distinct
604
+ * answers a caller must be able to tell apart, and flattening them
605
+ * would make a refusal indistinguishable from "these paths divide into
606
+ * nothing":
607
+ * · `found: true` — `faces` is the answer (possibly empty: a point
608
+ * query outside every input);
609
+ * · `found: false` + `reason` — a REFUSAL (no document, an id that
610
+ * doesn't resolve, more inputs than the kernel's cap, or a host
611
+ * whose engine predates the door). Never a truncated answer;
612
+ * surface the engine's own words, never an empty face list.
613
+ * · `complete: false` — the listed faces are all REAL but do not tile
614
+ * the union of the inputs (the enumeration missed a sliver). Always
615
+ * `true` for a point query — one face is not a tiling claim.
616
+ */
617
+ export interface PlanarRegionsResult {
618
+ /** `false` ⇒ the query could not be answered at all; read `reason`. */
619
+ found: boolean;
620
+ /** Populated when `found`. With a `point` in the request this holds at
621
+ * most one face (empty ⇒ the point is outside every input). */
622
+ faces: PlanarFace[];
623
+ /** How many inputs the arrangement was built from. */
624
+ inputCount: number;
625
+ /** `true` when the resolved faces tile the union of the inputs. */
626
+ complete: boolean;
627
+ /** Why `found` is false — the engine's own words (or the host
628
+ * adapter's, when no v57 engine answered). Absent on success. */
629
+ reason?: string | null;
630
+ }
519
631
  export interface DocumentSurface {
520
- mutate(mutation: Mutation): Promise<MutationOutcome>;
632
+ /** The single write door. Accepts `MutationInput` — the vendored
633
+ * `Mutation` union PLUS the protocol-ahead ops the vendored wire
634
+ * hasn't absorbed yet (see mutations.ts). Widening an accepted
635
+ * input is additive: every `Mutation` still passes. */
636
+ mutate(mutation: MutationInput): Promise<MutationOutcome>;
521
637
  undo(): Promise<void>;
522
638
  redo(): Promise<void>;
523
639
  collection<T>(name: CollectionName): Promise<readonly T[]>;
@@ -528,6 +644,33 @@ export interface DocumentSurface {
528
644
  * against). Retires the v0 `host.editor.client.send` escape hatch
529
645
  * the draw fill panel used. `null` for an unknown element. */
530
646
  elementProperties(id: ElementId): Promise<ElementProperties | null>;
647
+ /**
648
+ * B-22 (protocol v57) — the planar-REGION read door: resolve the faces
649
+ * of the arrangement `elementIds` forms (the areas the overlapping
650
+ * paths divide the plane into, one level below element hit-testing).
651
+ * Retires the v0 `host.editor.client.send({ kind:
652
+ * "requestPlanarRegions" })` escape hatch paged.draw's Shape Builder
653
+ * used.
654
+ *
655
+ * With `point` (in the RAW path space `pathAnchors` reports) it answers
656
+ * ONLY the face under it — the hover query, which costs N
657
+ * point-in-path tests plus one region materialisation instead of a
658
+ * full enumeration. Without it, every face comes back.
659
+ *
660
+ * Pure READ, gated on `capabilities.document.read`. Face ids are stable
661
+ * for the same inputs, so a hovered id is handed straight to the
662
+ * `pathfinderFaces` mutation.
663
+ *
664
+ * Answers the FULL {@link PlanarRegionsResult} — never a bare face
665
+ * array — because a refusal (`found: false` + `reason`: input cap,
666
+ * unresolvable id, or a host whose engine predates the door) must not
667
+ * be readable as "no regions". A host that cannot answer comes back as
668
+ * a refusal with a reason — never a throw (the undeclared-capability
669
+ * gate is the one exception, as on every read door). Probe
670
+ * `supports("document.planarRegions@1")` for whether the facade
671
+ * forwards at all.
672
+ */
673
+ planarRegions(elementIds: ElementId[], point?: [number, number]): Promise<PlanarRegionsResult>;
531
674
  /** D-01 (protocol v43) — enumerate every plugin-tagged placeholder
532
675
  * field in the document, in story order. Offsets are FRESH-READ
533
676
  * addresses (a placeholder is its own tagged run; re-enumerate
@@ -538,6 +681,31 @@ export interface DocumentSurface {
538
681
  hitTest(pageId: PageId, point: [number, number], filter?: HitFilter): Promise<HitResult | null>;
539
682
  elementGeometry(ids: ElementId[]): Promise<ElementGeometryItem[]>;
540
683
  tree(): Promise<SceneTreeNode[]>;
684
+ /**
685
+ * C-16 — the per-element PARENTAGE read: the nearest ANCESTOR that is
686
+ * itself an addressable element (a group today — the only element kind
687
+ * that nests page items), or `null` when there is none.
688
+ *
689
+ * Consumer: paged.draw's `selectParentGroup` command
690
+ * (`draw-bundle/src/commands/select-parent-group.ts`), which climbs to
691
+ * the containing group on every press. Without this door it re-read
692
+ * `tree()` and walked the WHOLE scene tree per invocation — O(document)
693
+ * per keystroke, a cost its own module header records as a named gap.
694
+ *
695
+ * `null` is the honest answer for three distinct cases a caller need
696
+ * not distinguish: the element is top-level (its container is a Page /
697
+ * Spread, which is NOT an element and never a selection target), the
698
+ * id does not resolve, or the address is a story/table range (those
699
+ * never nest in groups).
700
+ *
701
+ * A pure READ gated on `capabilities.document.read`, resolved from the
702
+ * SAME scene-tree the `tree()` door reads — no extra engine query and
703
+ * no wire op of its own (DESIGN.md §4.3d). The host keeps a parent
704
+ * index and rebuilds it when the document changes, so the O(document)
705
+ * walk happens once per EDIT rather than once per press. It is
706
+ * therefore exactly as fresh as `tree()` and no fresher.
707
+ */
708
+ parentOf(id: ElementId): Promise<ElementId | null>;
541
709
  /** Read a text frame's thread topology (protocol v38, C-2/S-05) — the
542
710
  * ordered chain of frames a story flows through, tail-overflow flagged.
543
711
  * Empty when the story has no frame or no document is loaded. A
@@ -629,6 +797,23 @@ export interface TextMetrics {
629
797
  /** Face descender at `sizePt` (negative below the baseline). */
630
798
  descender: number;
631
799
  }
800
+ /**
801
+ * C-9 — the user's text caret: the story + insertion offset a
802
+ * text-inserting plugin should target (paged.data first-insert
803
+ * placement — without this door a freshly-placed variable field lands
804
+ * at story start, offset 0).
805
+ *
806
+ * `offset` is a story-local content offset in the SAME convention the
807
+ * engine's text mutations consume (`insertText.offset`,
808
+ * `deleteRange.start/end` — the `ContentSelection` addressing: run
809
+ * bytes plus one synthetic `\n` per inter-paragraph boundary), so the
810
+ * value can be passed straight to `host.document.mutate`.
811
+ */
812
+ export interface TextCaret {
813
+ storyId: string;
814
+ /** Insertion offset (see the offset-convention note above). */
815
+ offset: number;
816
+ }
632
817
  /**
633
818
  * Text measurement against the loaded document's fonts (S-13). A read
634
819
  * door — no capability gate (like {@link ViewportSurface}); it wraps the
@@ -640,12 +825,51 @@ export interface TextMetrics {
640
825
  */
641
826
  export interface TextSurface {
642
827
  measureString(family: string, style: string | null, text: string, sizePt: number): Promise<TextMetrics>;
828
+ /**
829
+ * C-9 — read the user's text caret (the active `ContentSelection`),
830
+ * or `null` when no text caret is active OR the host injects no
831
+ * caret reader (probe `supports("text.caret@1")` to tell the two
832
+ * apart). For a collapsed selection this is the caret offset; for a
833
+ * RANGE selection it answers the range START (where a replace
834
+ * inserts). Honest v1 gap: a caret inside a TABLE CELL
835
+ * (cell-qualified selection) answers `null` — cell-local offsets
836
+ * would be misread as story-local body offsets.
837
+ */
838
+ caret(): TextCaret | null;
643
839
  }
644
840
  /** The v0 overlay channel: the shared tool-preview signal (polyline /
645
- * rect). Retained plugin scene layers are the P2 channel — reserved,
646
- * not faked. */
841
+ * rect / path — and, with `supports("overlay.text@1")`, the TEXT
842
+ * primitive `ToolPreviewText` for on-canvas readouts). Retained
843
+ * plugin scene layers are the P2 channel — reserved, not faked. */
647
844
  export interface OverlaySurface {
648
845
  setToolPreview(shape: ToolPreviewShape | null): void;
846
+ /**
847
+ * K-9 — publish MANY preview shapes at once (DESIGN.md §4.5a). The
848
+ * single-slot `setToolPreview` is last-write-wins, so a tool could
849
+ * show geometry OR a label, never both: paged.draw's **Measure** trades
850
+ * the frozen line for the frozen readout at pointer-up, and its region
851
+ * **Shape Builder** can highlight one hovered face but not shade the
852
+ * whole collected set. Both trades are named in that bundle's code;
853
+ * this door retires them.
854
+ *
855
+ * ONE SLOT, two writers: this REPLACES whatever the tool-preview slot
856
+ * holds with `shapes` (rendered in array order, first = bottom-most);
857
+ * `null` — or an empty array — clears it, exactly like
858
+ * `setToolPreview(null)`. It is not a second overlay layer (that would
859
+ * resurrect z-ordering and double the teardown paths).
860
+ *
861
+ * Shapes may address DIFFERENT pages: each carries its own `pageId`
862
+ * and the host resolves the page rect per shape.
863
+ *
864
+ * Same `capabilities.rendering` ∋ `"overlay"` gate as
865
+ * `setToolPreview` — one channel, one gate. Probe
866
+ * `supports("overlay.multiPreview@1")`: it is DYNAMIC (unlike the
867
+ * static `overlay.text@1`), true only when the host wired a real
868
+ * multi-shape sink. When it is false the door still works and never
869
+ * throws — it forwards the FIRST shape through the single slot, which
870
+ * is precisely the pre-K-9 behaviour a bundle used to hand-code.
871
+ */
872
+ setToolPreviews(shapes: readonly ToolPreviewShape[] | null): void;
649
873
  }
650
874
  /**
651
875
  * Shell actions the HOST APP injects at `loadBundle` time (the
@@ -653,6 +877,11 @@ export interface OverlaySurface {
653
877
  * function over the editor handle). When the host app provides no
654
878
  * implementation, calls warn and no-op — probe with
655
879
  * `host.supports("shell.openPanel@1")`.
880
+ *
881
+ * The two FILE doors are a pair: `pickFile` reads bytes IN (K-5 / S-11),
882
+ * `saveFile` hands bytes OUT (K-10). Both are byte-level by design —
883
+ * no DOM `File`/`Blob` ever crosses the contract — and both answer an
884
+ * honest "nothing happened" value rather than throwing.
656
885
  */
657
886
  export interface ShellSurface {
658
887
  /** Open a REGISTERED panel as the active dock tab (the
@@ -665,6 +894,42 @@ export interface ShellSurface {
665
894
  * the user cancels OR no picker is wired (the honest no-picker door);
666
895
  * probe `host.supports("shell.pickFile@1")` for the latter. */
667
896
  pickFile(options?: FilePickerOptions): Promise<readonly PickedFile[]>;
897
+ /**
898
+ * K-10 — the WRITE half of the picker door: hand the host bytes to
899
+ * deliver to the user's filesystem under `suggestedName` (DESIGN.md
900
+ * §4.5c). The mirror of `pickFile`: bytes cross, never a DOM `File`
901
+ * or a `Blob`, so a bundle stays isolate-ready.
902
+ *
903
+ * Consumer: **paged.image**, which can compute an adjusted
904
+ * PSD/PNG/JPEG but — with `pickFile` READ-only — could only deliver it
905
+ * through the Export Center's exporter registry, so "Save adjusted
906
+ * copy…" was unofferable from the bundle's own panel.
907
+ *
908
+ * Answers `true` when the host ACCEPTED the bytes and handed them to
909
+ * its save path, `false` when it did not — no saver wired (probe
910
+ * `supports("shell.saveFile@1")` to know that up front), the user
911
+ * declined, or the host refused. Never throws: a refused save is a
912
+ * result, like a refused mutation. Honest ceiling: a host backed by
913
+ * the browser's anchor-download cannot observe a user cancel, so
914
+ * `true` means "delivered to the browser's download path", not "a file
915
+ * exists on disk"; a File System Access backing can answer a real
916
+ * `false`.
917
+ */
918
+ saveFile(options: SaveFileOptions): Promise<boolean>;
919
+ }
920
+ /** What `ShellSurface.saveFile` delivers (K-10) — the inverse of
921
+ * {@link PickedFile}, field-for-field, so bytes picked from disk can be
922
+ * handed straight back after an edit. */
923
+ export interface SaveFileOptions {
924
+ /** File name incl. extension the host proposes to the user (it may
925
+ * sanitize or de-duplicate it — the name is a suggestion, not a
926
+ * path; a bundle can never target a location). */
927
+ suggestedName: string;
928
+ /** The bytes to write. */
929
+ bytes: Uint8Array;
930
+ /** MIME type for the saved file (same field name as `PickedFile`).
931
+ * Absent ⇒ the host uses `application/octet-stream`. */
932
+ mimeType?: string;
668
933
  }
669
934
  /** Filter + multiplicity for `ShellSurface.pickFile` (K-5 / S-11). */
670
935
  export interface FilePickerOptions {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export type { PluginId, PluginManifest, PluginCapabilities, PluginContributions, NetworkCapability, DataProvidersCapability, StorageCapability, WasmArtifact, WasmPurpose, WorkersCapability, SecretsCapability, GpuCapability, } from "./manifest";
2
2
  export type { BundleHandle, PagedBundle } from "./bundle";
3
- export type { BundleHost, ContributionSurface, SceneLayerSurface, ImagesSurface, ImageResourceClaimOptions, TileBytes, WorkersSurface, BundleWorker, SpawnWorkerOptions, SecretsSurface, SecretMaterial, DocumentSurface, SelectionSurface, ViewportSurface, TextSurface, TextMetrics, FrameChainLink, StoryContent, ParagraphContent, RunContent, OverlaySurface, ShellSurface, FilePickerOptions, PickedFile, StorageSurface, BlobSurface, BlobUsage, PartsSurface, NativeDocumentSurface, NetworkSurface, ConsentResult, DataProvidersSurface, DataProviderRegistration, DataProviderHandle, DataProviderInfo, DataProviderSnapshot, ProviderSchema, ProviderField, ProviderRecordSet, DiagnosticsSurface, BindingsSurface, Diagnostic, DocumentChangeEvent, MutationOutcome, Disposable, PluginLogger, EditContextContribution, ObjectTypeContribution, EditContextCandidate, EnteredEditContext, ContentPointerEvent, EditContextDescriptor, ObjectTypeDescriptor, PluginMetadataEnvelope, ObjectTypeBaker, BakeContext, } from "./host";
4
- export type { PanelSchema, PanelSchemaSection, PanelSchemaRow, SchemaPanelContribution, SchemaPanelRenderer, SchemaPanelRendererProps, WidgetValueBinding, BindingRef, SchemaGate, } from "./panel-schema";
3
+ export type { BundleHost, ContributionSurface, SceneLayerSurface, ImagesSurface, ImageResourceClaimOptions, TileBytes, WorkersSurface, BundleWorker, SpawnWorkerOptions, SecretsSurface, SecretMaterial, DocumentSurface, SelectionSurface, ViewportSurface, TextSurface, TextMetrics, TextCaret, FrameChainLink, PlanarFace, PlanarRegionsResult, StoryContent, ParagraphContent, RunContent, OverlaySurface, ShellSurface, FilePickerOptions, PickedFile, SaveFileOptions, StorageSurface, BlobSurface, BlobUsage, PartsSurface, NativeDocumentSurface, NetworkSurface, ConsentResult, DataProvidersSurface, DataProviderRegistration, DataProviderHandle, DataProviderInfo, DataProviderSnapshot, ProviderSchema, ProviderField, ProviderRecordSet, DiagnosticsSurface, BindingsSurface, Diagnostic, DocumentChangeEvent, MutationOutcome, Disposable, PluginLogger, EditContextContribution, ObjectTypeContribution, EditContextCandidate, EnteredEditContext, ContentPointerEvent, EditContextDescriptor, ObjectTypeDescriptor, PluginMetadataEnvelope, ObjectTypeBaker, BakeContext, } from "./host";
4
+ export type { PanelSchema, PanelSchemaSection, PanelSchemaRow, SchemaPanelContribution, SchemaPanelRenderer, SchemaPanelRendererProps, WidgetValueBinding, BindingRef, SchemaGate, WidgetCollectionBinding, SchemaRowAction, SchemaListAction, SchemaListSpec, } from "./panel-schema";
5
5
  export type { WidgetSurface, CodeEditorProps, CodeEditorDiagnostic, CodeEditorLanguage, } from "./widgets";
6
+ export type { BindingTarget, BindingPropertyRequest, BindingPropertyWrite, BindingCollectionRequest, BindingRead, BindingResolved, BindingDecline, BindingWrite, BindingCollection, BindingOp, BindingProvider, BindingProviderScope, BindingProviderHandle, BindingReadResult, BindingWriteResult, BindingCollectionResult, ActiveBindingProvider, } from "./binding-provider";
6
7
  export type { AssetSurface, AssetKind, FontFaceAsset, FontFaceFormat, } from "./assets";
7
8
  export type { ProviderTileWire } from "./wire";
8
9
  export type { ClipboardSurface, ClipboardPayload, TabularClipboard, } from "./clipboard";
@@ -1 +1,275 @@
1
+ import type { ElementId, Mutation } from "./wire";
1
2
  export type { ElementId, PageId, NodeId, NodeSpec, Mutation, Operation, PropertyPath, Value, PathAnchorSpec, PathAnchorTriple, PathAnchorsResult, PathPointAddress, PathPointRole, PathfinderKind, HitFilter, HitResult, CollectionName, DocumentMeta, ElementGeometryItem, SceneTreeNode, SelectionMode, ContentSelection, MainToWorker, MainToWorkerKind, WorkerToMain, SceneLayer, SceneItem, ScenePathSeg, ScenePaint, GestureType, GestureHandle, GestureModifiers, SwatchSpec, GradientSpec, GradientStopSpec, SwatchSummary, GradientSummary, LayerSummary, } from "./wire";
3
+ /** v56 (Wave B) — close an OPEN subpath of a path element: the inverse
4
+ * gesture of `pathOpenAt`'s scissors cut. `subpath` picks the contour
5
+ * by index; omit for the default (the single/last open subpath).
6
+ * Coincident endpoints — the duplicated pair a cut leaves — merge back
7
+ * into one anchor; endpoints apart gain the implicit closing edge. */
8
+ export type ClosePathMutation = {
9
+ op: "closePath";
10
+ args: {
11
+ elementId: ElementId;
12
+ subpath?: number | null;
13
+ };
14
+ };
15
+ /** v56 (Wave B) — weld two OPEN single-contour path elements into one
16
+ * (InDesign's Join): connect the NEAREST endpoints of `elementId` and
17
+ * `otherId`, then delete `otherId`. Both endpoint pairs coincident
18
+ * closes the result into a ring. Non-path / closed / multi-contour
19
+ * inputs are rejected with an honest error (no-op). One undo step
20
+ * restores BOTH elements. */
21
+ export type JoinPathsMutation = {
22
+ op: "joinPaths";
23
+ args: {
24
+ elementId: ElementId;
25
+ otherId: ElementId;
26
+ };
27
+ };
28
+ /** v56 (B-18) — InDesign paste-into: nest an existing TOP-LEVEL page
29
+ * item inside a container Rectangle / Oval / Polygon. The child keeps
30
+ * its document-space geometry (nothing moves on canvas) and renders
31
+ * clipped by the container's outline. Grouped / already-nested
32
+ * children and non-container hosts are rejected with an honest error.
33
+ * One undo pops the child back to its exact stacking slot. */
34
+ export type PasteIntoMutation = {
35
+ op: "pasteInto";
36
+ args: {
37
+ containerId: ElementId;
38
+ childId: ElementId;
39
+ };
40
+ };
41
+ /** v56 (B-18) — the inverse gesture: pop a pasted-in child back to top
42
+ * level (stacks on top), world transform preserved. One undo re-nests
43
+ * it at the same child index. */
44
+ export type ReleaseFromMutation = {
45
+ op: "releaseFrom";
46
+ args: {
47
+ childId: ElementId;
48
+ };
49
+ };
50
+ /** v57 (B-22) — every face of the arrangement becomes its own object,
51
+ * keeping the attributes of the topmost input covering it. */
52
+ export type PathfinderDivideMutation = {
53
+ op: "pathfinderDivide";
54
+ args: {
55
+ elementIds: ElementId[];
56
+ };
57
+ };
58
+ /** v57 (B-22) — each input is clipped to the part nothing above it
59
+ * covers, and loses its stroke. Inputs entirely hidden are deleted. */
60
+ export type PathfinderTrimMutation = {
61
+ op: "pathfinderTrim";
62
+ args: {
63
+ elementIds: ElementId[];
64
+ };
65
+ };
66
+ /** v57 (B-22) — Trim, then coalesce: inputs that share a fill colour
67
+ * merge into one object. */
68
+ export type PathfinderMergeMutation = {
69
+ op: "pathfinderMerge";
70
+ args: {
71
+ elementIds: ElementId[];
72
+ };
73
+ };
74
+ /** v57 (B-22) — keep only what falls inside the TOPMOST input, coloured
75
+ * by the objects beneath it; the topmost input is consumed as the
76
+ * cookie cutter and disappears. */
77
+ export type PathfinderCropMutation = {
78
+ op: "pathfinderCrop";
79
+ args: {
80
+ elementIds: ElementId[];
81
+ };
82
+ };
83
+ /** v57 (B-22) — fills become strokes: every arrangement edge (split at
84
+ * each crossing) becomes an open `GraphicLine` stroked with the fill of
85
+ * the input that contributed it. All inputs are consumed. */
86
+ export type PathfinderOutlineMutation = {
87
+ op: "pathfinderOutline";
88
+ args: {
89
+ elementIds: ElementId[];
90
+ };
91
+ };
92
+ /** v57 (B-22) — the BACKMOST object minus every object in front of it. */
93
+ export type PathfinderMinusBackMutation = {
94
+ op: "pathfinderMinusBack";
95
+ args: {
96
+ elementIds: ElementId[];
97
+ };
98
+ };
99
+ /** v57 (B-22) — whether `pathfinderFaces` KEEPS the named faces or
100
+ * REMOVES them (Shape Builder's drag vs alt-drag). */
101
+ export type FaceSelectMode = "keep" | "remove";
102
+ /** v57 (B-22) — Shape Builder's click/drag output: unite the named faces
103
+ * of the arrangement into one result element. `faces` carries the
104
+ * STABLE ids `host.document.planarRegions` reported for the SAME
105
+ * `elementIds` (ids are only stable per input set); `mode` picks
106
+ * whether they are the faces kept or the faces removed. An unknown id
107
+ * is REFUSED, not ignored — a stale face set fails loudly. */
108
+ export type PathfinderFacesMutation = {
109
+ op: "pathfinderFaces";
110
+ args: {
111
+ elementIds: ElementId[];
112
+ faces: string[];
113
+ mode: FaceSelectMode;
114
+ };
115
+ };
116
+ /** v58 (C-28) — how the mask artwork's coverage is read. `luminosity`
117
+ * is Illustrator's default and PDF's `/S /Luminosity`; `alpha` reads
118
+ * the artwork's alpha channel instead. Unpainted area means HIDDEN for
119
+ * both (PDF's black backdrop). Omitted = `luminosity`. */
120
+ export type OpacityMaskType = "luminosity" | "alpha";
121
+ /** v58 (C-28) — **Make Opacity Mask**: `maskId`'s artwork becomes a soft
122
+ * mask over `targetId`. The mask item leaves the page's z-order (its
123
+ * slot is captured) and one undo pops it back exactly.
124
+ *
125
+ * RENDERS EVERYWHERE. An earlier revision of this comment warned that
126
+ * the mask was honoured by the CPU rasterizer and PDF export but NOT by
127
+ * the Vello/WebGPU backend the editor canvas uses, so a plugin must not
128
+ * imply WYSIWYG. **That is no longer true, and it was never as
129
+ * fundamental as it read**: the claim rested on the belief that Vello
130
+ * lacked a coverage-buffer API, when the pinned version has
131
+ * `Scene::push_luminance_mask_layer` (an unrelated 0.3.0 entry in
132
+ * `Cargo.lock`, belonging to a spike crate, was misread as the resolved
133
+ * version). Alpha masks need no mask layer at all — `Compose::DestIn`
134
+ * IS `dst · src.a`.
135
+ *
136
+ * Vello output is now byte-identical to the CPU rasterizer across
137
+ * luminosity, alpha and both inverted forms. Safe to build WYSIWYG UI
138
+ * over. */
139
+ export type ApplyOpacityMaskMutation = {
140
+ op: "applyOpacityMask";
141
+ args: {
142
+ targetId: ElementId;
143
+ maskId: ElementId;
144
+ maskType?: OpacityMaskType | null;
145
+ invert?: boolean | null;
146
+ };
147
+ };
148
+ /** v58 (C-28) — drop the mask relation; the artwork returns to top level
149
+ * with its geometry untouched. One undo re-applies the mask with its
150
+ * original mode/invert at the same z slot. */
151
+ export type ReleaseOpacityMaskMutation = {
152
+ op: "releaseOpacityMask";
153
+ args: {
154
+ targetId: ElementId;
155
+ };
156
+ };
157
+ /** v58 (C-29) — **Type on a Path**: flow an existing story along an
158
+ * existing path element. The engine could already RENDER type on a path
159
+ * loaded from an IDML; until v58 nothing could create one.
160
+ *
161
+ * Hosts are Rectangle / GraphicLine / Polygon. A TextFrame is refused
162
+ * WITH a reason: its glyphs are emitted by the story pass, so accepting
163
+ * it would produce a visible lie. `pathTypeAlignment` (Baseline /
164
+ * Center honoured), `flipPathEffect` and the `startBracket`/`endBracket`
165
+ * range are all live. `PathEffect` is deliberately NOT exposed — only
166
+ * `RainbowPathEffect` actually renders. */
167
+ export type AttachTextToPathMutation = {
168
+ op: "attachTextToPath";
169
+ args: {
170
+ elementId: ElementId;
171
+ storyId: string;
172
+ pathTypeAlignment?: string | null;
173
+ flipPathEffect?: string | null;
174
+ startBracket?: number | null;
175
+ endBracket?: number | null;
176
+ };
177
+ };
178
+ /** v58 (C-29) — unlink the text from the path; the exact inverse. */
179
+ export type DetachTextFromPathMutation = {
180
+ op: "detachTextFromPath";
181
+ args: {
182
+ elementId: ElementId;
183
+ };
184
+ };
185
+ /** v59 (Arrange) — where `reorderElement` puts its target inside the
186
+ * sibling list it already belongs to. `0` is the BACKMOST slot (painted
187
+ * first). Prefer the four verbs: they are evaluated against the order
188
+ * the engine holds at apply time, so a concurrent insert or delete
189
+ * cannot make them restack the wrong item. `{ index }` is absolute and
190
+ * therefore CAN go stale — an out-of-range index is refused with an
191
+ * honest error, never clamped. */
192
+ export type ZOrderTarget = "front" | "back" | "forward" | "backward" | {
193
+ index: number;
194
+ };
195
+ /** v59 (Arrange) — **the z-order door**: restack an element within the
196
+ * sibling list it already belongs to. Nothing on the wire could change
197
+ * stacking before v59, which is why so many bundles carry "inserted
198
+ * items land on top of the z-order" as an accepted limit — that
199
+ * sentence is retired by this op.
200
+ *
201
+ * The sibling list is DERIVED from where the element already is: a
202
+ * top-level item restacks in the spread's z table, a group member
203
+ * inside its group, a pasted-in child inside its container. There is no
204
+ * parent argument, so a reorder can never move an element between those
205
+ * scopes — for that use `createGroup` / `dissolveGroup` /
206
+ * `pasteInto` / `releaseFrom`. An opacity-mask artwork is painted from
207
+ * no list at all and is refused. One undo restores the previous order
208
+ * exactly (the engine's inverse carries the slot the item came from).
209
+ *
210
+ * CREATE-THEN-ARRANGE in one undo step — the Live Paint shape (fill a
211
+ * face, then put the fill UNDER the strokes bounding it) — is a batch
212
+ * of `[insert…, bindCreated { handle }, reorderElement
213
+ * { elementId: "$h:<handle>", to: "back" }]`. The `bindCreated` is
214
+ * required: the bare `$created` sentinel is understood by
215
+ * `setPluginMetadata` / `setElementProperty` only, while the generic
216
+ * handle resolver — the one that rewrites any id position of any op —
217
+ * is armed by a `bindCreated` child being present.
218
+ *
219
+ * HONEST LIMIT 1 — Arrange is WITHIN a layer. The renderer sorts by
220
+ * `ItemLayer` before it paints, so bring-to-front cannot lift an
221
+ * element above one on a higher layer. That is InDesign's model:
222
+ * crossing layers is a property write, not an Arrange.
223
+ *
224
+ * HONEST LIMIT 2 — the new order shows on canvas and survives a
225
+ * `.paged` save, but NOT an `.idml` export: the IDML writer re-emits
226
+ * existing source elements byte-for-byte and only places new ones, so a
227
+ * reorder reverts on an export/reopen round trip. Do not present
228
+ * Arrange as durable across IDML interchange yet. */
229
+ export type ReorderElementMutation = {
230
+ op: "reorderElement";
231
+ args: {
232
+ elementId: ElementId;
233
+ to: ZOrderTarget;
234
+ };
235
+ };
236
+ /** The protocol-ahead ops, as one union — the delta between the
237
+ * vendored `Mutation` (protocol 51) and core's protocol 59. Empties
238
+ * itself on the next `sync-wire.mjs` run. */
239
+ /** v57 (C-15) — name the id a creating sibling is about to mint, so a
240
+ * LATER child of the same batch can address it as `"$h:<handle>"`.
241
+ *
242
+ * This is what collapses a two-batch flow into ONE undo step. Before it,
243
+ * a bundle that inserted geometry and then painted or grouped it had to
244
+ * issue two batches, because a batch op could not reference an id minted
245
+ * in the same batch — paged.draw's appearance bake, pattern bake and
246
+ * re-plan, compound-path release, symbol place, image trace and
247
+ * live-paint fill all pay two undo steps for exactly that reason.
248
+ *
249
+ * Three rules, each of which was measured against the engine rather than
250
+ * assumed, because getting any of them wrong fails in a confusing way:
251
+ *
252
+ * 1. **Order matters.** The bind must come AFTER the creating child.
253
+ * Placed before, the batch is refused BY NAME (an honest error, not a
254
+ * silent no-op).
255
+ * 2. **It is its own op, not a field.** Passing a handle inside a
256
+ * creating op's own `args` is SILENTLY IGNORED — which is what made
257
+ * an early probe read as "the engine doesn't support this".
258
+ * 3. **Scope is the declaring batch**, visible to its own later children
259
+ * including nested batches, never outward. A `$h:` appearing in TEXT
260
+ * content is content and is never rewritten.
261
+ *
262
+ * Generalises the v34 `$created` sentinel — which only two mutation
263
+ * kinds understood — to any number of live names addressable from any
264
+ * mutation kind. */
265
+ export type BindCreatedMutation = {
266
+ op: "bindCreated";
267
+ args: {
268
+ handle: string;
269
+ };
270
+ };
271
+ export type PendingMutation = BindCreatedMutation | ClosePathMutation | JoinPathsMutation | PasteIntoMutation | ReleaseFromMutation | PathfinderDivideMutation | PathfinderTrimMutation | PathfinderMergeMutation | PathfinderCropMutation | PathfinderOutlineMutation | PathfinderMinusBackMutation | PathfinderFacesMutation | ApplyOpacityMaskMutation | ReleaseOpacityMaskMutation | AttachTextToPathMutation | DetachTextFromPathMutation | ReorderElementMutation;
272
+ /** What `host.document.mutate` ACCEPTS: the vendored op union plus the
273
+ * protocol-ahead ops. Widening an accepted-input type is additive —
274
+ * every existing `Mutation` call site still compiles. */
275
+ export type MutationInput = Mutation | PendingMutation;