@paged-media/plugin-api 0.2.27-canary.1 → 0.2.28-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/binding-provider.d.ts +331 -0
- package/dist/contributions.d.ts +1 -1
- package/dist/editor.d.ts +46 -1
- package/dist/host.d.ts +270 -5
- package/dist/index.d.ts +3 -2
- package/dist/mutations.d.ts +274 -0
- package/dist/panel-schema.d.ts +68 -0
- package/package.json +1 -1
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import type { Disposable, MutationOutcome } from "./host";
|
|
2
|
+
import type { MutationInput } from "./mutations";
|
|
3
|
+
import type { CollectionName, ElementId, PropertyPath, Value } from "./wire";
|
|
4
|
+
/**
|
|
5
|
+
* WHAT a property resolution addresses. Three variants, one per shape
|
|
6
|
+
* the proof consumers need — and no more:
|
|
7
|
+
*
|
|
8
|
+
* · `selection` — the host's CURRENT selection in the given scope.
|
|
9
|
+
* The default for a panel binding, and the ONLY variant a
|
|
10
|
+
* `WidgetValueBinding` produces today (its `scope` field maps here
|
|
11
|
+
* 1:1). The provider resolves it IN ITS OWN REALM: it already knows
|
|
12
|
+
* which raster layer / which cell / which DOCX run is selected, so
|
|
13
|
+
* the host never has to name a thing it cannot address.
|
|
14
|
+
* · `element` — a core-addressable target the host names explicitly.
|
|
15
|
+
* `ElementId` ALREADY models both halves the consumers need: the
|
|
16
|
+
* element kinds (`rectangle`, `textFrame`, `group`, …) AND the
|
|
17
|
+
* range kinds (`storyRange` carries `{story_id, start, end}`;
|
|
18
|
+
* `tableCell` carries the cell address). Range-scoped addressing is
|
|
19
|
+
* therefore admitted by core's own type — this contract does not
|
|
20
|
+
* invent a parallel one.
|
|
21
|
+
* · `row` — a row this provider itself handed out through
|
|
22
|
+
* `readCollection`. The id is the PROVIDER's vocabulary, opaque to
|
|
23
|
+
* the host, which is what lets plugin-image key rows by its own
|
|
24
|
+
* stable layer ids while the editor keys them by `selfId`.
|
|
25
|
+
*
|
|
26
|
+
* There is deliberately NO document-scoped target variant: the
|
|
27
|
+
* document-scoped lanes (`readCollection`, `applyMutation`) take no
|
|
28
|
+
* target at all, because a swatch list is not "the value of a path for
|
|
29
|
+
* a thing".
|
|
30
|
+
*/
|
|
31
|
+
export type BindingTarget = {
|
|
32
|
+
kind: "selection";
|
|
33
|
+
/** Which selection surface — mirrors `WidgetValueBinding.scope`.
|
|
34
|
+
* `"element"` is the page-item selection; `"content"` is the text
|
|
35
|
+
* caret / range selection (the Character-panel lane). */
|
|
36
|
+
scope: "element" | "content";
|
|
37
|
+
} | {
|
|
38
|
+
kind: "element";
|
|
39
|
+
id: ElementId;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "row";
|
|
42
|
+
/** The collection the row came from — a provider only ever gets a
|
|
43
|
+
* row target for a collection it declared. */
|
|
44
|
+
collection: CollectionName;
|
|
45
|
+
/** The row's id, in the shape the provider's own rows carry. */
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
/** A property READ: one typed path at one target. */
|
|
49
|
+
export interface BindingPropertyRequest {
|
|
50
|
+
path: PropertyPath;
|
|
51
|
+
target: BindingTarget;
|
|
52
|
+
}
|
|
53
|
+
/** A property WRITE: the read request plus the typed payload. The
|
|
54
|
+
* payload is a core `Value` — see the vocabulary rule in this file's
|
|
55
|
+
* header; a provider that cannot express its own storage as a `Value`
|
|
56
|
+
* declines rather than inventing one. */
|
|
57
|
+
export interface BindingPropertyWrite extends BindingPropertyRequest {
|
|
58
|
+
value: Value;
|
|
59
|
+
}
|
|
60
|
+
/** A collection READ. DOCUMENT-SCOPED: no target, because a swatch list
|
|
61
|
+
* or a layer list belongs to the document, not to a selection. */
|
|
62
|
+
export interface BindingCollectionRequest {
|
|
63
|
+
collection: CollectionName;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A provider's answer to a property READ. FOUR states, and the
|
|
67
|
+
* distinctions are load-bearing — collapsing any two of them produces a
|
|
68
|
+
* user-visible lie:
|
|
69
|
+
*
|
|
70
|
+
* · `value` — a definite value.
|
|
71
|
+
* · `mixed` — the target spans SEVERAL values (a multi-format
|
|
72
|
+
* character range, a multi-row selection). The panel must show
|
|
73
|
+
* "mixed", never pick a winner. This mirrors core's OWN convention
|
|
74
|
+
* exactly: `PropertyEntry.value` is `Value | null`, and the wire's
|
|
75
|
+
* own comment says `None` signals "mixed / indeterminate — a
|
|
76
|
+
* `StoryRange` whose `CharacterRun`s carry conflicting values".
|
|
77
|
+
* · `absent` — this provider OWNS the target, but the path does not
|
|
78
|
+
* apply to it (a raster text layer with no leading concept). The
|
|
79
|
+
* panel blanks / disables the row; it does NOT fall through to
|
|
80
|
+
* core, because core has no opinion about a thing it does not hold.
|
|
81
|
+
* · `decline` — NOT MINE right now. Resolution continues down the
|
|
82
|
+
* active-provider stack and then falls through to core.
|
|
83
|
+
*
|
|
84
|
+
* `absent` vs `decline` is the pair most easily conflated and the most
|
|
85
|
+
* expensive to conflate: fall through on an owned-but-inapplicable path
|
|
86
|
+
* and the panel shows the CORE text frame's leading while the user is
|
|
87
|
+
* editing a raster layer.
|
|
88
|
+
*/
|
|
89
|
+
export type BindingRead = BindingResolved | BindingDecline;
|
|
90
|
+
/** The answers that CLAIM the target (everything but `decline`). */
|
|
91
|
+
export type BindingResolved = {
|
|
92
|
+
kind: "value";
|
|
93
|
+
value: Value;
|
|
94
|
+
} | {
|
|
95
|
+
kind: "mixed";
|
|
96
|
+
} | {
|
|
97
|
+
kind: "absent";
|
|
98
|
+
reason?: string;
|
|
99
|
+
};
|
|
100
|
+
/** "Not mine" — keep looking (the next active provider, then core). */
|
|
101
|
+
export interface BindingDecline {
|
|
102
|
+
kind: "decline";
|
|
103
|
+
reason?: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A provider's answer to a WRITE (property or structural).
|
|
107
|
+
*
|
|
108
|
+
* `applied` carries a {@link MutationOutcome} — the SAME type
|
|
109
|
+
* `host.document.mutate` answers — deliberately, so the host panel has
|
|
110
|
+
* ONE code path for the provider write and the core fall-through write.
|
|
111
|
+
* A provider that simply forwards to `host.document.mutate` returns its
|
|
112
|
+
* outcome unchanged; a provider writing its own storage answers
|
|
113
|
+
* `{ applied: true, createdId: null, pageIds: [] }` — an empty
|
|
114
|
+
* `pageIds` is the honest "no engine page changed", not a placeholder.
|
|
115
|
+
* A refused write (a locked layer) is `{ applied: false, error }`, not
|
|
116
|
+
* a `decline`: the provider owned it and said no.
|
|
117
|
+
*
|
|
118
|
+
* THE UNDO RULE, which the type states and cannot enforce: a provider's
|
|
119
|
+
* write MUST land through a door that participates in undo — either
|
|
120
|
+
* `host.document.mutate` (the document stack) or its active context's
|
|
121
|
+
* OWN op-log when that context owns undo (ADR-012 Tier 1: a context
|
|
122
|
+
* declaring `onUndo`/`onRedo` has Cmd-Z routed to it while active, and
|
|
123
|
+
* commit-exit re-lowers the net change as one atomic document step).
|
|
124
|
+
* Either way the user's Cmd-Z works. A provider that mutates outside
|
|
125
|
+
* both IS the side channel this contract exists to prevent; the
|
|
126
|
+
* contract cannot police a callback running in the plugin's realm, so
|
|
127
|
+
* this is stated as a requirement on the implementer, honestly.
|
|
128
|
+
*/
|
|
129
|
+
export type BindingWrite = {
|
|
130
|
+
kind: "applied";
|
|
131
|
+
outcome: MutationOutcome;
|
|
132
|
+
} | BindingDecline;
|
|
133
|
+
/** A provider's answer to a collection READ. `rows` MUST carry CORE's
|
|
134
|
+
* row shape for that collection (`LayerSummary` for `"layers"`,
|
|
135
|
+
* `SwatchSummary` for `"swatches"`, …) — that is the vocabulary rule
|
|
136
|
+
* applied to collections, and it is what lets one host list render
|
|
137
|
+
* provider rows and core rows with the same renderer. Extra fields are
|
|
138
|
+
* ignored by the host; a field the provider's model has no counterpart
|
|
139
|
+
* for carries the provider's honest default, and the honest way to
|
|
140
|
+
* suppress a control it cannot serve is to leave that path OUT of
|
|
141
|
+
* `provides.paths` (see {@link BindingProviderScope}). */
|
|
142
|
+
export type BindingCollection = {
|
|
143
|
+
kind: "rows";
|
|
144
|
+
rows: readonly unknown[];
|
|
145
|
+
} | BindingDecline;
|
|
146
|
+
/** The op names a provider may intercept — core's own mutation
|
|
147
|
+
* vocabulary, unchanged (`MutationInput` = the vendored `Mutation`
|
|
148
|
+
* union plus the protocol-ahead ops). No new verbs enter here. */
|
|
149
|
+
export type BindingOp = MutationInput["op"];
|
|
150
|
+
/**
|
|
151
|
+
* WHAT a provider declares it serves. The declaration IS the gate: the
|
|
152
|
+
* registry never consults a provider about a path / collection / op it
|
|
153
|
+
* did not declare, so a provider cannot accidentally intercept the
|
|
154
|
+
* whole panel surface, and the host can read the declaration (through
|
|
155
|
+
* `activeProviders()`) to decide what a panel may offer at all.
|
|
156
|
+
*
|
|
157
|
+
* Every member is optional and every member is a CLOSED core union —
|
|
158
|
+
* see the vocabulary rule in this file's header.
|
|
159
|
+
*/
|
|
160
|
+
export interface BindingProviderScope {
|
|
161
|
+
/** Typed property paths this provider READS. (Also the write set unless
|
|
162
|
+
* {@link BindingProviderScope.writablePaths} narrows it.) */
|
|
163
|
+
paths?: readonly PropertyPath[];
|
|
164
|
+
/**
|
|
165
|
+
* The subset of {@link BindingProviderScope.paths} this provider accepts
|
|
166
|
+
* WRITES for. OMITTED means "all of them" — the behaviour before this
|
|
167
|
+
* member existed, so no existing provider changes.
|
|
168
|
+
*
|
|
169
|
+
* WHY IT EXISTS, and why the first two proof consumers did not need it.
|
|
170
|
+
* The property-WRITE lane was the one lane no consumer had exercised:
|
|
171
|
+
* Layers writes STRUCTURALLY (`layerMove` / `layerSetVisible` through
|
|
172
|
+
* `applyMutation`) and Swatches likewise (`editSwatch`), and both of
|
|
173
|
+
* those are DECLARED in `ops` — so a host panel can ask
|
|
174
|
+
* "may this control work?" synchronously and DISABLE it, which is
|
|
175
|
+
* exactly what ADR-023's Swatches slice does. The property lane had no
|
|
176
|
+
* such declaration: `writeProperty` is an optional CALLBACK, and a
|
|
177
|
+
* callback's absence is not visible through `activeProviders()`.
|
|
178
|
+
*
|
|
179
|
+
* The consequence was a contradiction inside this very file. The op
|
|
180
|
+
* lane's rule is "an undeclared op must not reach core — the panel is
|
|
181
|
+
* showing somebody else's rows"; the path lane's rule (below, now
|
|
182
|
+
* corrected) said the opposite: that an absent `writeProperty` lets
|
|
183
|
+
* writes fall through to core. Fall-through there is the WRITE-side
|
|
184
|
+
* form of the `absent` lie — the panel is showing a sheet cell's font
|
|
185
|
+
* size and the commit lands on whatever core text the caret last
|
|
186
|
+
* touched.
|
|
187
|
+
*
|
|
188
|
+
* So a provider that can READ a path but not WRITE it says so HERE, the
|
|
189
|
+
* host reads the declaration, and the control renders read-only instead
|
|
190
|
+
* of being fake-interactive. Forced by paged.sheet as the
|
|
191
|
+
* Character/Paragraph provider: the sheet engine has no cell-style
|
|
192
|
+
* write API at all (all spreadsheet semantics live in Rust, and adding
|
|
193
|
+
* one is a Rust change), so it reads the whole surface and writes none
|
|
194
|
+
* of it — `writablePaths: []`.
|
|
195
|
+
*/
|
|
196
|
+
writablePaths?: readonly PropertyPath[];
|
|
197
|
+
/** Named core collections whose ROWS this provider serves. */
|
|
198
|
+
collections?: readonly CollectionName[];
|
|
199
|
+
/** Mutation ops this provider takes first refusal on — the STRUCTURAL
|
|
200
|
+
* edits a host panel would otherwise send straight to core
|
|
201
|
+
* (`layerMove`, `createSwatch`, …). A `"batch"` declaration takes the
|
|
202
|
+
* WHOLE batch: the registry never decomposes one to route children
|
|
203
|
+
* separately (that would split one undo step across two authorities). */
|
|
204
|
+
ops?: readonly BindingOp[];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The resolver a bundle registers through
|
|
208
|
+
* `host.contribute.bindingProvider(contextType, provider)`.
|
|
209
|
+
*
|
|
210
|
+
* LIFETIME — the whole design rests on this: a provider is consulted
|
|
211
|
+
* ONLY while the edit context named at registration is ACTIVE. It
|
|
212
|
+
* borrows `contributeEditContext`'s activation rather than inventing a
|
|
213
|
+
* parallel notion of "who is active", because the shell's context stack
|
|
214
|
+
* ALREADY is that notion (it owns the stack, the breadcrumb, the
|
|
215
|
+
* tool/panel swap and the write-scope narrowing). Concretely the SDK
|
|
216
|
+
* adapter wraps the context's own `onEnter`/`onExit` hooks, so
|
|
217
|
+
* activation is derived from the shell's stack rather than reported
|
|
218
|
+
* alongside it and cannot drift from it.
|
|
219
|
+
*
|
|
220
|
+
* The consequence is deliberate and worth stating rather than
|
|
221
|
+
* discovering: a provider is never consulted while its context is
|
|
222
|
+
* inactive — INCLUDING for the document-scoped lanes. A plugin's colour
|
|
223
|
+
* vocabulary shows in the host Swatches panel while you are inside that
|
|
224
|
+
* plugin's frame and not after you leave it. That is the retargeting
|
|
225
|
+
* behaviour ADR-023 set out to copy (there is ONE Character panel and it
|
|
226
|
+
* retargets), applied consistently; a plugin that needs its resources
|
|
227
|
+
* visible document-wide keeps its own panel.
|
|
228
|
+
*
|
|
229
|
+
* Every lane is OPTIONAL. Declaring a lane in `provides` without
|
|
230
|
+
* implementing its callback is an authoring bug, not a stance, and the
|
|
231
|
+
* adapter refuses it loudly at registration.
|
|
232
|
+
*/
|
|
233
|
+
export interface BindingProvider {
|
|
234
|
+
/** What this provider answers for. See {@link BindingProviderScope}. */
|
|
235
|
+
provides: BindingProviderScope;
|
|
236
|
+
/** Read one typed path at one target. Required iff `provides.paths`
|
|
237
|
+
* is non-empty. */
|
|
238
|
+
readProperty?(request: BindingPropertyRequest): BindingRead | Promise<BindingRead>;
|
|
239
|
+
/** Write one typed path at one target. OPTIONAL even with declared
|
|
240
|
+
* paths — a read-only provider (a computed readout) is legitimate.
|
|
241
|
+
*
|
|
242
|
+
* CORRECTED 2026-08-05 (the Character/Paragraph consumer). This used
|
|
243
|
+
* to say its absence means "writes fall through to core, which is the
|
|
244
|
+
* honest behaviour". It is not honest: a provider that CLAIMED the
|
|
245
|
+
* read owns the target, and sending the commit to core writes
|
|
246
|
+
* whatever core's selection happens to be — the write-side form of
|
|
247
|
+
* the `absent` lie the whole seam exists to prevent. A read-only
|
|
248
|
+
* provider now DECLARES itself with
|
|
249
|
+
* {@link BindingProviderScope.writablePaths}, and the host renders
|
|
250
|
+
* those controls read-only rather than routing their writes anywhere.
|
|
251
|
+
* A claimed path whose write the registry refuses is DROPPED, never
|
|
252
|
+
* re-aimed at core. */
|
|
253
|
+
writeProperty?(request: BindingPropertyWrite): BindingWrite | Promise<BindingWrite>;
|
|
254
|
+
/** Serve the rows of a declared collection. Required iff
|
|
255
|
+
* `provides.collections` is non-empty. */
|
|
256
|
+
readCollection?(request: BindingCollectionRequest): BindingCollection | Promise<BindingCollection>;
|
|
257
|
+
/** Take first refusal on a declared structural op. Required iff
|
|
258
|
+
* `provides.ops` is non-empty. */
|
|
259
|
+
applyMutation?(mutation: MutationInput): BindingWrite | Promise<BindingWrite>;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* What `host.contribute.bindingProvider` returns. Disposing it removes
|
|
263
|
+
* the provider WITHOUT touching the edit context it borrowed activation
|
|
264
|
+
* from — which is what makes ADR-023 phase D a migration rather than a
|
|
265
|
+
* deletion: a duplicate panel retires behind the seam one at a time,
|
|
266
|
+
* and rolls back by disposing one handle.
|
|
267
|
+
*/
|
|
268
|
+
export interface BindingProviderHandle extends Disposable {
|
|
269
|
+
/**
|
|
270
|
+
* Announce that the values this provider serves have CHANGED, so host
|
|
271
|
+
* panels bound through it re-read.
|
|
272
|
+
*
|
|
273
|
+
* This exists because the host's usual refresh signal —
|
|
274
|
+
* `host.document.onDidChange` — fires on ENGINE mutations, and a
|
|
275
|
+
* provider's state frequently changes without one (a raster layer
|
|
276
|
+
* toggled inside plugin-image's own wasm layer graph moves no engine
|
|
277
|
+
* page). Without this the host panel goes quietly stale, which is the
|
|
278
|
+
* class of lie this platform refuses. Cheap and coarse by design:
|
|
279
|
+
* "re-read", not a per-path diff — panels are small and a diff
|
|
280
|
+
* protocol here would be a second damage-tracking system.
|
|
281
|
+
*/
|
|
282
|
+
invalidate(): void;
|
|
283
|
+
}
|
|
284
|
+
/** The registry's answer to a property read. `resolved: false` ⇒ the
|
|
285
|
+
* host reads core (`document.elementProperties` / its own panel path). */
|
|
286
|
+
export type BindingReadResult = {
|
|
287
|
+
resolved: true;
|
|
288
|
+
provider: string;
|
|
289
|
+
read: BindingResolved;
|
|
290
|
+
} | {
|
|
291
|
+
resolved: false;
|
|
292
|
+
reason: string;
|
|
293
|
+
};
|
|
294
|
+
/** The registry's answer to a write (property or structural).
|
|
295
|
+
* `handled: false` ⇒ the host writes core (`document.mutate`). */
|
|
296
|
+
export type BindingWriteResult = {
|
|
297
|
+
handled: true;
|
|
298
|
+
provider: string;
|
|
299
|
+
outcome: MutationOutcome;
|
|
300
|
+
} | {
|
|
301
|
+
handled: false;
|
|
302
|
+
reason: string;
|
|
303
|
+
};
|
|
304
|
+
/** The registry's answer to a collection read. `resolved: false` ⇒ the
|
|
305
|
+
* host reads core (`document.collection(name)`). */
|
|
306
|
+
export type BindingCollectionResult = {
|
|
307
|
+
resolved: true;
|
|
308
|
+
provider: string;
|
|
309
|
+
rows: readonly unknown[];
|
|
310
|
+
} | {
|
|
311
|
+
resolved: false;
|
|
312
|
+
reason: string;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* One entry of the ACTIVE provider stack, as the host sees it —
|
|
316
|
+
* innermost (most recently entered) FIRST, which is the precedence
|
|
317
|
+
* order. A host panel reads this to decide what it may OFFER at all:
|
|
318
|
+
* a control whose path no active provider declares, over a row an
|
|
319
|
+
* active provider owns, is a control that cannot work.
|
|
320
|
+
*/
|
|
321
|
+
export interface ActiveBindingProvider {
|
|
322
|
+
/** The owning bundle's manifest id. */
|
|
323
|
+
plugin: string;
|
|
324
|
+
/** The edit-context type whose activation this provider borrows. */
|
|
325
|
+
contextType: string;
|
|
326
|
+
/** The element the context was entered on (the shell's scope root),
|
|
327
|
+
* or `null` when the host reported none. */
|
|
328
|
+
elementId: string | null;
|
|
329
|
+
/** What it declares it serves. */
|
|
330
|
+
provides: BindingProviderScope;
|
|
331
|
+
}
|
package/dist/contributions.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { ShellRegistries, ToolRegistry, PanelRegistry, CommandRegistry, KeybindingRegistry, OverlayRegistry, EditContextRegistry, ObjectTypeRegistry, ImporterRegistry, ExporterRegistry, ToolContribution, ToolId, ToolGroupId, ToolSectionId, ToolOptionsSpec, ToolOptionField, CursorSpec, CssCursorToken, GestureHandler, CanvasPointerEvent, OverlayContext, OverlayPrimitive, DeactivateReason, PanelContribution, PanelProps, PanelApi, OverlayContribution, OverlayProps, OverlayPageRect, CommandContribution, KeybindingContribution, ImporterContribution, ImportRequest, ExporterContribution, ExportResult, DockEdge, VisibilityPredicate, PagedEditor, PagedClient, ImageResourceClaim, ResourceTilesNeeded, ToolPreviewShape, ToolPreviewPolyline, ToolPreviewPath, MarqueeRectPageLocal, } from "./editor";
|
|
1
|
+
export type { ShellRegistries, ToolRegistry, PanelRegistry, CommandRegistry, KeybindingRegistry, OverlayRegistry, EditContextRegistry, ObjectTypeRegistry, ImporterRegistry, ExporterRegistry, ToolContribution, ToolId, ToolGroupId, ToolSectionId, ToolOptionsSpec, ToolOptionField, CursorSpec, CssCursorToken, GestureHandler, CanvasPointerEvent, OverlayContext, OverlayPrimitive, DeactivateReason, PanelContribution, PanelProps, PanelApi, OverlayContribution, OverlayProps, OverlayPageRect, CommandContribution, KeybindingContribution, ImporterContribution, ImportRequest, ExporterContribution, ExportResult, DockEdge, VisibilityPredicate, PagedEditor, PagedClient, ImageResourceClaim, ResourceTilesNeeded, ToolPreviewShape, ToolPreviewPolyline, ToolPreviewPath, ToolPreviewText, MarqueeRectPageLocal, } from "./editor";
|
package/dist/editor.d.ts
CHANGED
|
@@ -363,7 +363,43 @@ export interface ToolPreviewPath {
|
|
|
363
363
|
/** Dashed stroke instead of the default solid (preview vocabulary). */
|
|
364
364
|
dashed?: boolean;
|
|
365
365
|
}
|
|
366
|
-
|
|
366
|
+
/**
|
|
367
|
+
* A text tool-preview (the overlay TEXT primitive). Closes the RFI
|
|
368
|
+
* gap "the overlay channel carries shapes only, no text primitive" —
|
|
369
|
+
* consumer: paged.draw's Measure tool readout (an on-canvas
|
|
370
|
+
* length/angle HUD was impossible with shapes alone); future
|
|
371
|
+
* consumers: the Dimension tool, crop HUDs, Ruler markers.
|
|
372
|
+
*
|
|
373
|
+
* `x`/`y` are the text anchor position in page-local pt — the SAME
|
|
374
|
+
* space every other preview variant uses. The host renders the label
|
|
375
|
+
* at a constant SCREEN size (like its page captions), so `size` is a
|
|
376
|
+
* screen-px font size (default host-chosen, ~11), not a document pt
|
|
377
|
+
* size that would scale with zoom. `anchor` is SVG `text-anchor`
|
|
378
|
+
* semantics relative to `x`. `background` opts into a small backing
|
|
379
|
+
* plate behind the label for legibility over page content.
|
|
380
|
+
*
|
|
381
|
+
* PLAIN TEXT ONLY — the host sanitizes (control characters stripped,
|
|
382
|
+
* no markup interpreted). Probe `supports("overlay.text@1")`.
|
|
383
|
+
*/
|
|
384
|
+
export interface ToolPreviewText {
|
|
385
|
+
/** Explicit discriminant — the first in this vocabulary (the older
|
|
386
|
+
* variants discriminate structurally; new primitives get a `kind`). */
|
|
387
|
+
kind: "text";
|
|
388
|
+
pageId: PageId;
|
|
389
|
+
/** Text anchor x, page-local pt. */
|
|
390
|
+
x: number;
|
|
391
|
+
/** Text BASELINE y, page-local pt. */
|
|
392
|
+
y: number;
|
|
393
|
+
/** The label. Plain text; the host sanitizes + may truncate. */
|
|
394
|
+
text: string;
|
|
395
|
+
/** Font size in screen px (constant under zoom). Host default ~11. */
|
|
396
|
+
size?: number;
|
|
397
|
+
/** Horizontal anchoring relative to `x` (SVG text-anchor). Default "start". */
|
|
398
|
+
anchor?: "start" | "middle" | "end";
|
|
399
|
+
/** Render a small backing plate behind the label for legibility. */
|
|
400
|
+
background?: boolean;
|
|
401
|
+
}
|
|
402
|
+
export type ToolPreviewShape = MarqueeRectPageLocal | ToolPreviewPolyline | ToolPreviewPath | ToolPreviewText;
|
|
367
403
|
export interface PagedClient {
|
|
368
404
|
mutate(mutation: Mutation): Promise<WorkerToMain>;
|
|
369
405
|
undo(): Promise<WorkerToMain>;
|
|
@@ -430,6 +466,15 @@ export interface PagedEditor {
|
|
|
430
466
|
};
|
|
431
467
|
overlaySignals: {
|
|
432
468
|
setToolPreview(value: ToolPreviewShape | null): void;
|
|
469
|
+
/** K-9 — the MULTI-shape tool-preview sink. `undefined` when the host
|
|
470
|
+
* build's overlay renderer predates the multi-shape channel (older
|
|
471
|
+
* editor / a headless host that wires only the single slot); the
|
|
472
|
+
* `host.overlay.setToolPreviews` door then degrades to the first
|
|
473
|
+
* shape on `setToolPreview` and
|
|
474
|
+
* `supports("overlay.multiPreview@1")` is false. Same slot as
|
|
475
|
+
* `setToolPreview` — this REPLACES the slot's content with the
|
|
476
|
+
* list; `null` clears it. */
|
|
477
|
+
setToolPreviews?(value: readonly ToolPreviewShape[] | null): void;
|
|
433
478
|
};
|
|
434
479
|
tool: {
|
|
435
480
|
setBaseTool(id: ToolId): void;
|
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).
|
|
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
|
-
|
|
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
|
|
646
|
-
*
|
|
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";
|
package/dist/mutations.d.ts
CHANGED
|
@@ -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;
|
package/dist/panel-schema.d.ts
CHANGED
|
@@ -25,6 +25,71 @@ export interface BindingRef {
|
|
|
25
25
|
* (and a missing name reads as `false` — a visible seam, never a
|
|
26
26
|
* throw). */
|
|
27
27
|
export type SchemaGate = boolean | BindingRef;
|
|
28
|
+
/** Where a `list` row's items come from. Two sources, mirroring the
|
|
29
|
+
* two live-collection lanes the host already has:
|
|
30
|
+
* · `documentCollection` — a named engine collection (the same
|
|
31
|
+
* `host.document.collection(name)` lane the editor's own panels
|
|
32
|
+
* read);
|
|
33
|
+
* · `binding` — an ARRAY the plugin publishes through
|
|
34
|
+
* `host.bindings.publish(name, rows)` (the §12.2 published-
|
|
35
|
+
* bindings door, now carrying rows instead of a boolean). */
|
|
36
|
+
export type WidgetCollectionBinding = {
|
|
37
|
+
kind: "documentCollection";
|
|
38
|
+
collection: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "binding";
|
|
41
|
+
bind: string;
|
|
42
|
+
};
|
|
43
|
+
/** G3 — a schema row ACTION. Either dispatches a registered command
|
|
44
|
+
* with the row id as payload, or the `applyEntity` write kind: apply
|
|
45
|
+
* the row's entity id (style / swatch self-id) to the current
|
|
46
|
+
* selection through the SAME `setElementProperty` mutation channel the
|
|
47
|
+
* scalar widgets commit on. `valueType` picks the wire payload
|
|
48
|
+
* (`text` for applied-style paths, `colorRef` for swatch/gradient
|
|
49
|
+
* paths) — the collection-select leaf's convention. Note the ceiling
|
|
50
|
+
* holds: an action writes ONE typed PropertyPath, it does not compute. */
|
|
51
|
+
export type SchemaRowAction = {
|
|
52
|
+
kind: "command";
|
|
53
|
+
command: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "applyEntity";
|
|
56
|
+
/** Selection surface to write to (defaults to `"element"`). */
|
|
57
|
+
scope?: "element" | "content";
|
|
58
|
+
/** The applied-entity PropertyPath (e.g. `frameFillColor`,
|
|
59
|
+
* `appliedParagraphStyle`). */
|
|
60
|
+
path: PropertyPath;
|
|
61
|
+
/** Wire payload variant; defaults to `"text"`. */
|
|
62
|
+
valueType?: "text" | "colorRef";
|
|
63
|
+
};
|
|
64
|
+
/** One per-row action button on a `list` widget. */
|
|
65
|
+
export interface SchemaListAction {
|
|
66
|
+
/** Button label (sentence case, no emoji — brand content rules). */
|
|
67
|
+
label: string;
|
|
68
|
+
action: SchemaRowAction;
|
|
69
|
+
/** Gate the button on a published binding (absent = enabled;
|
|
70
|
+
* `applyEntity` actions additionally disable while the target
|
|
71
|
+
* selection is empty — the honest no-write-path rule). */
|
|
72
|
+
enabled?: SchemaGate;
|
|
73
|
+
}
|
|
74
|
+
/** The `list` widget spec (widget id `paged.list`). Renders rows from a
|
|
75
|
+
* collection binding; publishes the clicked row's id back through
|
|
76
|
+
* `selectionBinding` so other rows/sections can gate on it — which is
|
|
77
|
+
* how a list participates in §12.2 without a DSL. */
|
|
78
|
+
export interface SchemaListSpec {
|
|
79
|
+
items: WidgetCollectionBinding;
|
|
80
|
+
/** Dot-path into a row object for the primary label ("name"). */
|
|
81
|
+
labelField: string;
|
|
82
|
+
/** Optional secondary line (mono), e.g. "kind". */
|
|
83
|
+
secondaryField?: string;
|
|
84
|
+
/** Dot-path carrying the row's stable id; defaults to "selfId" (the
|
|
85
|
+
* summary-shape convention every document collection uses). */
|
|
86
|
+
idField?: string;
|
|
87
|
+
/** Published binding name that receives the selected row id on click
|
|
88
|
+
* (string). Absent = the list keeps private selection. */
|
|
89
|
+
selectionBinding?: string;
|
|
90
|
+
/** Per-row action buttons. */
|
|
91
|
+
actions?: SchemaListAction[];
|
|
92
|
+
}
|
|
28
93
|
export interface PanelSchemaRow {
|
|
29
94
|
/** Catalog widget id (a curated primitive-leaf id the host
|
|
30
95
|
* registered). An unknown id renders a visible "unknown widget"
|
|
@@ -38,6 +103,9 @@ export interface PanelSchemaRow {
|
|
|
38
103
|
* (`literal | selectionProperty`). Absent = a layout-only / display
|
|
39
104
|
* leaf (label, section). */
|
|
40
105
|
value?: WidgetValueBinding;
|
|
106
|
+
/** ADDITIVE (schema v1.1) — present iff `widget` is the list widget
|
|
107
|
+
* (`paged.list`). Scalar rows ignore it; a v1 schema never sets it. */
|
|
108
|
+
list?: SchemaListSpec;
|
|
41
109
|
/** Show the row only when this gate is truthy. Absent = always. */
|
|
42
110
|
visible?: SchemaGate;
|
|
43
111
|
/** Enable the row's control only when this gate is truthy. Absent =
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paged-media/plugin-api",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28-canary.0",
|
|
4
4
|
"description": "The Paged plugin contract: manifest, bundle lifecycle, the BundleHost surface, and the contribution + engine wire types. Type-only.",
|
|
5
5
|
"license": "MPL-2.0 OR LicenseRef-PMEL",
|
|
6
6
|
"type": "module",
|