@paged-media/plugin-api 0.2.42-canary.0 → 0.2.45-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 +80 -0
- package/dist/index.d.ts +6 -5
- package/dist/manifest.d.ts +25 -0
- package/dist/mutations.d.ts +1 -1
- package/dist/object-model.d.ts +556 -0
- package/dist/panel-schema.d.ts +113 -1
- package/dist/widgets.d.ts +56 -1
- package/dist/wire.d.ts +124 -9
- package/package.json +1 -1
- package/src/manifest.schema.json +170 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import type { MutationInput } from "./mutations";
|
|
2
|
+
import type { Disposable, MutationOutcome } from "./host";
|
|
3
|
+
/**
|
|
4
|
+
* One object's address (ADR 131). A SESSION HANDLE, not a durable
|
|
5
|
+
* reference: InDesign renumbers every `Self` on save, so anything stored in
|
|
6
|
+
* a document (a binding target) is a selector over a stable key — a `Name`
|
|
7
|
+
* or an `x-paged:` label — never a raw id.
|
|
8
|
+
*
|
|
9
|
+
* Canonical core forms (what the formatter emits):
|
|
10
|
+
* `doc` · `textFrame:<id>` (and `rectangle:` `oval:` `polygon:`
|
|
11
|
+
* `graphicLine:` `group:`) · `page:<id>` · `page:#<n>` · `spread:<id>` ·
|
|
12
|
+
* `layer:<id>` · `master:<id>` · `story:<id>` ·
|
|
13
|
+
* `storyRange:<id>@<start>..<end>` · `field:<story>@<offset>` ·
|
|
14
|
+
* `table:<id>` · `cell:<table>/<row>,<col>` · `style:<family>/<rest>` ·
|
|
15
|
+
* `swatch:<rest>` · `gradient:<rest>` · `colorGroup:<rest>` ·
|
|
16
|
+
* `numberingList:<rest>` · `guide:<id>` · `condition:<rest>` ·
|
|
17
|
+
* `conditionSet:<rest>` · `section:<id>` · `hyperlink:<id>` ·
|
|
18
|
+
* `textVariable:<id>` (protocol 71) ·
|
|
19
|
+
* `master:<id> > <item address>`
|
|
20
|
+
* Aliases that parse and are never emitted: `frame:<id>`,
|
|
21
|
+
* `story:<id>@a..b`, `textframe`, `rect`, `graphicline`, `storyrange`.
|
|
22
|
+
* Plugin objects: `plugin:<pluginId>/<kind>/<rest>`. Batch handles:
|
|
23
|
+
* `$h:<name>`.
|
|
24
|
+
*/
|
|
25
|
+
export type Address = string;
|
|
26
|
+
/** ADR 131 selector: `step *( (" > " | " ") step )`, a step being an
|
|
27
|
+
* address, a kind or `*` followed by predicates — `[key op value]` with
|
|
28
|
+
* `= != ^= *=`, keys `name` `label` `label.<key>` `kind` `layer` `style`
|
|
29
|
+
* or any property path, and `:nth(n)`. */
|
|
30
|
+
export type Selector = string;
|
|
31
|
+
/** A property path: `frameStrokeWeight`, `name`, `points[3].anchor`,
|
|
32
|
+
* `labels[x-paged:oid]`. The reserved path `"content"` reads/writes an
|
|
33
|
+
* object's content (its `ContentSchema`). */
|
|
34
|
+
export type PropertyPathName = string;
|
|
35
|
+
/** The page-item kinds (core's `ElementId` string-id kinds). */
|
|
36
|
+
export type PageItemKind = "textFrame" | "rectangle" | "oval" | "polygon" | "graphicLine" | "group";
|
|
37
|
+
/** Style families a `style:` address names (= core's `StyleCollection`). */
|
|
38
|
+
export type StyleFamily = "paragraph" | "character" | "object" | "cell" | "table";
|
|
39
|
+
/** A parsed address — what the SDK's `parseAddress` returns and
|
|
40
|
+
* `formatAddress` takes. Plain data. */
|
|
41
|
+
export type ParsedAddress = {
|
|
42
|
+
kind: "doc";
|
|
43
|
+
} | {
|
|
44
|
+
kind: "item";
|
|
45
|
+
itemKind: PageItemKind;
|
|
46
|
+
id: string;
|
|
47
|
+
}
|
|
48
|
+
/** `frame:<id>` — kind-agnostic; the resolver finds the item kind. */
|
|
49
|
+
| {
|
|
50
|
+
kind: "frame";
|
|
51
|
+
id: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "page";
|
|
54
|
+
id: string;
|
|
55
|
+
}
|
|
56
|
+
/** `page:#<n>` — the n-th page, 1-based, document order. */
|
|
57
|
+
| {
|
|
58
|
+
kind: "pageIndex";
|
|
59
|
+
index: number;
|
|
60
|
+
} | {
|
|
61
|
+
kind: "spread";
|
|
62
|
+
id: string;
|
|
63
|
+
} | {
|
|
64
|
+
kind: "layer";
|
|
65
|
+
id: string;
|
|
66
|
+
} | {
|
|
67
|
+
kind: "master";
|
|
68
|
+
id: string;
|
|
69
|
+
} | {
|
|
70
|
+
kind: "story";
|
|
71
|
+
id: string;
|
|
72
|
+
} | {
|
|
73
|
+
kind: "storyRange";
|
|
74
|
+
story: string;
|
|
75
|
+
start: number;
|
|
76
|
+
end: number;
|
|
77
|
+
} | {
|
|
78
|
+
kind: "field";
|
|
79
|
+
story: string;
|
|
80
|
+
offset: number;
|
|
81
|
+
} | {
|
|
82
|
+
kind: "table";
|
|
83
|
+
id: string;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "cell";
|
|
86
|
+
table: string;
|
|
87
|
+
row: number;
|
|
88
|
+
col: number;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "style";
|
|
91
|
+
family: StyleFamily;
|
|
92
|
+
name: string;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "swatch" | "gradient" | "colorGroup" | "numberingList" | "condition" | "conditionSet";
|
|
95
|
+
name: string;
|
|
96
|
+
} | {
|
|
97
|
+
kind: "guide" | "section" | "hyperlink" | "textVariable";
|
|
98
|
+
id: string;
|
|
99
|
+
} | {
|
|
100
|
+
kind: "onMaster";
|
|
101
|
+
master: string;
|
|
102
|
+
item: ParsedAddress;
|
|
103
|
+
} | {
|
|
104
|
+
kind: "plugin";
|
|
105
|
+
plugin: string;
|
|
106
|
+
objectKind: string;
|
|
107
|
+
id: string;
|
|
108
|
+
} | {
|
|
109
|
+
kind: "handle";
|
|
110
|
+
name: string;
|
|
111
|
+
};
|
|
112
|
+
/** The ADR 131 address kind of a parsed address (`ParsedAddress` folds the
|
|
113
|
+
* six item kinds into `item` and the page index into `pageIndex`). */
|
|
114
|
+
export type AddressKind = ParsedAddress["kind"];
|
|
115
|
+
/** ADR 132's value types, mirrored verbatim. Value shapes:
|
|
116
|
+
* bool → boolean · number → number (in `unit`) · length → number (pt;
|
|
117
|
+
* unit strings like "3mm" coerce) · color → a swatch ref (`Color/Red`)
|
|
118
|
+
* · enum → one of `members` · text → string · ref → an address or the
|
|
119
|
+
* target's self id · bounds → [top,left,bottom,right] · transform →
|
|
120
|
+
* [a,b,c,d,tx,ty] · point → [x,y] · bytes → Uint8Array · asset → a
|
|
121
|
+
* `data:<mime>;base64,<payload>` URI or bare base64 (core reads it back
|
|
122
|
+
* as a `data:` URI typed by the bytes' magic; `host.objects` also takes
|
|
123
|
+
* a `Uint8Array` / `ArrayBuffer` and encodes it) · list → array · map →
|
|
124
|
+
* record keyed by string · struct → record. */
|
|
125
|
+
export type ValueType = {
|
|
126
|
+
kind: "bool";
|
|
127
|
+
} | {
|
|
128
|
+
kind: "number";
|
|
129
|
+
integer?: boolean;
|
|
130
|
+
unit?: "pt" | "deg" | "percent" | "em1000" | "ppi";
|
|
131
|
+
} | {
|
|
132
|
+
kind: "length";
|
|
133
|
+
} | {
|
|
134
|
+
kind: "color";
|
|
135
|
+
} | {
|
|
136
|
+
kind: "enum";
|
|
137
|
+
members: readonly string[];
|
|
138
|
+
} | {
|
|
139
|
+
kind: "text";
|
|
140
|
+
multiline?: boolean;
|
|
141
|
+
maxLength?: number;
|
|
142
|
+
} | {
|
|
143
|
+
kind: "ref";
|
|
144
|
+
to: string;
|
|
145
|
+
} | {
|
|
146
|
+
kind: "bounds";
|
|
147
|
+
} | {
|
|
148
|
+
kind: "transform";
|
|
149
|
+
} | {
|
|
150
|
+
kind: "point";
|
|
151
|
+
} | {
|
|
152
|
+
kind: "bytes";
|
|
153
|
+
mime?: readonly string[];
|
|
154
|
+
}
|
|
155
|
+
/** `shape` names the encoding core expects (`data:<mime>;base64,<payload>`). */
|
|
156
|
+
| {
|
|
157
|
+
kind: "asset";
|
|
158
|
+
shape?: string;
|
|
159
|
+
} | {
|
|
160
|
+
kind: "list";
|
|
161
|
+
of: ValueType;
|
|
162
|
+
maxItems?: number;
|
|
163
|
+
} | {
|
|
164
|
+
kind: "map";
|
|
165
|
+
of: ValueType;
|
|
166
|
+
keyPattern?: string;
|
|
167
|
+
}
|
|
168
|
+
/** `shape` names a core wire struct (`SwatchColorSpec`, `TabStopSpec`);
|
|
169
|
+
* `open: true` accepts fields `fields` does not list — core's catalog
|
|
170
|
+
* (protocol 71) names the shape but not its fields, and the engine
|
|
171
|
+
* validates them. */
|
|
172
|
+
| {
|
|
173
|
+
kind: "struct";
|
|
174
|
+
fields: Readonly<Record<string, StructField>>;
|
|
175
|
+
shape?: string;
|
|
176
|
+
open?: true;
|
|
177
|
+
};
|
|
178
|
+
export type ValueTypeKind = ValueType["kind"];
|
|
179
|
+
/**
|
|
180
|
+
* One field of a `struct`: a value type, plus whether a record may omit it.
|
|
181
|
+
* · `optional: true` — the field may be absent; nothing is filled in.
|
|
182
|
+
* · `default` — the field may be absent; `host.objects.invoke` (and a
|
|
183
|
+
* struct-typed `set`) fill the default in before the handler or the
|
|
184
|
+
* plugin sees the value. The default must satisfy the field's type
|
|
185
|
+
* (checked at registration).
|
|
186
|
+
* A field with neither is required.
|
|
187
|
+
*/
|
|
188
|
+
export type StructField = ValueType & {
|
|
189
|
+
optional?: true;
|
|
190
|
+
default?: unknown;
|
|
191
|
+
};
|
|
192
|
+
/** ADR 132's schema ROW (the JSON shape the core catalog publishes, and
|
|
193
|
+
* the one plugins publish). One row per (address kind, path). */
|
|
194
|
+
export interface PropertySchema {
|
|
195
|
+
/** ADR 131 kind (`frame`, `swatch`, …); plugin rows: `plugin:<id>/<kind>`
|
|
196
|
+
* (the registry fills it in when a kind omits it). */
|
|
197
|
+
addressKind?: string;
|
|
198
|
+
/** Item kinds the row applies to; omitted = all. */
|
|
199
|
+
appliesTo?: readonly string[];
|
|
200
|
+
path: PropertyPathName;
|
|
201
|
+
type: ValueType;
|
|
202
|
+
/** `null` clears / inherits. */
|
|
203
|
+
nullable?: boolean;
|
|
204
|
+
range?: {
|
|
205
|
+
min?: number;
|
|
206
|
+
max?: number;
|
|
207
|
+
step?: number;
|
|
208
|
+
};
|
|
209
|
+
default?: unknown;
|
|
210
|
+
/** Default `readWrite`. `derived` is computed from other paths. */
|
|
211
|
+
access?: "readWrite" | "readOnly" | "derived";
|
|
212
|
+
/** Core only: the dedicated op a virtual path resolves to; `clearOp`
|
|
213
|
+
* names the op a `null` write resolves to when clearing is a DIFFERENT
|
|
214
|
+
* op (`nextTextFrame` → `unlinkFrames`, `opacityMask` →
|
|
215
|
+
* `releaseOpacityMask`). */
|
|
216
|
+
virtual?: {
|
|
217
|
+
op: string;
|
|
218
|
+
field?: string;
|
|
219
|
+
clearOp?: string;
|
|
220
|
+
} | null;
|
|
221
|
+
idml?: {
|
|
222
|
+
element: string;
|
|
223
|
+
attribute: string;
|
|
224
|
+
} | null;
|
|
225
|
+
/** Default: `access === "readWrite"`. */
|
|
226
|
+
bindable?: boolean;
|
|
227
|
+
since?: number;
|
|
228
|
+
summary?: string;
|
|
229
|
+
title?: string;
|
|
230
|
+
/** An expensive read (pixels, a whole sheet, story text): `getAll`
|
|
231
|
+
* skips it unless asked (`getAll(address, { content: true })`). The
|
|
232
|
+
* `content` row is always heavy. */
|
|
233
|
+
heavy?: boolean;
|
|
234
|
+
}
|
|
235
|
+
/** What an object's CONTENT is; drives content ops and `get … --content`. */
|
|
236
|
+
export interface ContentSchema {
|
|
237
|
+
kind: "text" | "rows" | "cells" | "pixels" | "vector" | "html" | "blob";
|
|
238
|
+
mime?: readonly string[];
|
|
239
|
+
}
|
|
240
|
+
/** Why a call was refused. Expected failures are results, never throws. */
|
|
241
|
+
export type ObjectErrorCode = "badAddress" | "unknownAddress" | "unknownPath" | "unknownCommand" | "invalidValue" | "readOnly"
|
|
242
|
+
/** In the grammar, but no backend routes it yet (core forms whose write
|
|
243
|
+
* lands with core's `Set`, ADR 132). */
|
|
244
|
+
| "unmapped" | "denied" | "failed";
|
|
245
|
+
/** One read (ADR 323). `refused` is this SDK's addition: an unknown
|
|
246
|
+
* address, a bad path or an unmapped kind answer a typed reason rather
|
|
247
|
+
* than an `absent` that would read as "the path does not apply". */
|
|
248
|
+
export type ObjectValue = {
|
|
249
|
+
kind: "value";
|
|
250
|
+
value: unknown;
|
|
251
|
+
} | {
|
|
252
|
+
kind: "mixed";
|
|
253
|
+
} | {
|
|
254
|
+
kind: "absent";
|
|
255
|
+
} | {
|
|
256
|
+
kind: "refused";
|
|
257
|
+
code: ObjectErrorCode;
|
|
258
|
+
reason: string;
|
|
259
|
+
};
|
|
260
|
+
/** One op of a batch (ADR 323). */
|
|
261
|
+
export type ObjectOp = {
|
|
262
|
+
op: "set";
|
|
263
|
+
address: Address;
|
|
264
|
+
path: PropertyPathName;
|
|
265
|
+
value: unknown;
|
|
266
|
+
} | {
|
|
267
|
+
op: "create";
|
|
268
|
+
kind: string;
|
|
269
|
+
within?: Address;
|
|
270
|
+
props?: Record<string, unknown>;
|
|
271
|
+
handle?: string;
|
|
272
|
+
} | {
|
|
273
|
+
op: "delete";
|
|
274
|
+
address: Address;
|
|
275
|
+
} | {
|
|
276
|
+
op: "invoke";
|
|
277
|
+
command: string;
|
|
278
|
+
args: unknown;
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* An edit of a LIST value (ADR 323 property rows; DESIGN.md §12.8): what a
|
|
282
|
+
* host's list field does when the user adds, removes, reorders or edits
|
|
283
|
+
* one item. It is not a new batch op: `planValueEdit` / `editValue`
|
|
284
|
+
* (plugin-sdk) read the current value and LOWER the edit to plain `set`
|
|
285
|
+
* ops — the whole new list on the list's own path, or, when the kind only
|
|
286
|
+
* types its items (`path[].field` rows, no `path` row), `set path[i].field`
|
|
287
|
+
* — so it lands as ONE batch = ONE undo step, through the same validation
|
|
288
|
+
* as any write, and every planner keeps receiving `set` / `create` /
|
|
289
|
+
* `delete` only.
|
|
290
|
+
* · `add` — insert `item` (absent: the item type's default) at `index`
|
|
291
|
+
* (absent: the end).
|
|
292
|
+
* · `remove` — drop the item at `index`.
|
|
293
|
+
* · `move` — move the item at `from` so it ends up at `to`.
|
|
294
|
+
* · `itemSet` — replace the item at `index`, or with `field` one field of
|
|
295
|
+
* a struct item.
|
|
296
|
+
* Indices are positions in the VALUE (a stack painted bottom → top is
|
|
297
|
+
* still indexed bottom-first, whatever order the UI lists it in).
|
|
298
|
+
*/
|
|
299
|
+
export type ListEdit = {
|
|
300
|
+
edit: "add";
|
|
301
|
+
index?: number;
|
|
302
|
+
item?: unknown;
|
|
303
|
+
} | {
|
|
304
|
+
edit: "remove";
|
|
305
|
+
index: number;
|
|
306
|
+
} | {
|
|
307
|
+
edit: "move";
|
|
308
|
+
from: number;
|
|
309
|
+
to: number;
|
|
310
|
+
} | {
|
|
311
|
+
edit: "itemSet";
|
|
312
|
+
index: number;
|
|
313
|
+
field?: string;
|
|
314
|
+
value: unknown;
|
|
315
|
+
};
|
|
316
|
+
/** An edit of one field of a STRUCT value (a gradient stop, an anchor
|
|
317
|
+
* point): lowered like {@link ListEdit} to a `set` of the whole struct,
|
|
318
|
+
* or `set path.field` when the kind types the field as its own row. */
|
|
319
|
+
export type StructEdit = {
|
|
320
|
+
edit: "fieldSet";
|
|
321
|
+
field: string;
|
|
322
|
+
value: unknown;
|
|
323
|
+
};
|
|
324
|
+
/** One edit of a list or struct value. */
|
|
325
|
+
export type ValueEdit = ListEdit | StructEdit;
|
|
326
|
+
/** A container part change, in the WRITER's own `paged/<plugin-id>/`
|
|
327
|
+
* namespace (relative path; `bytes: null` deletes). */
|
|
328
|
+
export interface PartChange {
|
|
329
|
+
path: string;
|
|
330
|
+
bytes: Uint8Array | null;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* What a plugin write produces; the host commits it as ONE undo step.
|
|
334
|
+
* · `mutations` — core-backed state: folded into the batch.
|
|
335
|
+
* · `state` — plugin-backed state (the label-hash pattern): the parts
|
|
336
|
+
* are written first (content-addressed, never overwritten), then the
|
|
337
|
+
* plugin's label is set on `host` INSIDE the batch. Undo reverts the
|
|
338
|
+
* label; the plugin reloads the state the label points back to.
|
|
339
|
+
*
|
|
340
|
+
* `host` is a page item, a story (range), a page (`page:<id>`,
|
|
341
|
+
* `page:#n` — protocol 70 `setPageMetadata`) or the document (`doc` —
|
|
342
|
+
* `setDocumentMetadata`).
|
|
343
|
+
*
|
|
344
|
+
* Core holds ONE label per plugin per host, under exactly
|
|
345
|
+
* `x-paged:<pluginId>`, whose value is the envelope
|
|
346
|
+
* `{"v": <int >= 1>, "data": {…}}` (≤ 64 KiB). So `labelKey` is either
|
|
347
|
+
* - `x-paged:<pluginId>` — `labelValue` IS that whole envelope
|
|
348
|
+
* (JSON text), and replaces the label; or
|
|
349
|
+
* - `x-paged:<pluginId>.<sub>` — a SUB-KEY: the registry reads the
|
|
350
|
+
* current envelope, sets `data[<sub>]` to `labelValue` (parsed
|
|
351
|
+
* when it is JSON text, else kept as the string) and writes the
|
|
352
|
+
* merged envelope back. Several sub-keys on one host — in one
|
|
353
|
+
* write or across writes of one batch — merge into one label.
|
|
354
|
+
* Any other key is refused before anything applies.
|
|
355
|
+
* · `both` — the two together.
|
|
356
|
+
* · `rejected` — a typed no.
|
|
357
|
+
*
|
|
358
|
+
* `created` — the address of each object the write's `create` ops make,
|
|
359
|
+
* ONE per `create` op the planner received, in the order received (the
|
|
360
|
+
* registry splices them into `ObjectWriteResult.created` at the ops'
|
|
361
|
+
* batch positions). When the engine mints the id, write the placeholder
|
|
362
|
+
* `$h:<handle>` (the `bindCreated` handle of that insert) or `$created`
|
|
363
|
+
* (the batch's `createdId`) inside the address; the registry replaces it
|
|
364
|
+
* with the minted element's id after the commit. Omitted: each of those
|
|
365
|
+
* creates answers `""`. A list of the wrong length is refused (`failed`)
|
|
366
|
+
* before anything applies.
|
|
367
|
+
*/
|
|
368
|
+
export type ObjectWrite = {
|
|
369
|
+
kind: "mutations";
|
|
370
|
+
mutations: readonly MutationInput[];
|
|
371
|
+
created?: readonly Address[];
|
|
372
|
+
} | {
|
|
373
|
+
kind: "state";
|
|
374
|
+
parts: readonly PartChange[];
|
|
375
|
+
host: Address;
|
|
376
|
+
labelKey: string;
|
|
377
|
+
labelValue: string;
|
|
378
|
+
created?: readonly Address[];
|
|
379
|
+
} | {
|
|
380
|
+
kind: "both";
|
|
381
|
+
mutations: readonly MutationInput[];
|
|
382
|
+
parts: readonly PartChange[];
|
|
383
|
+
host: Address;
|
|
384
|
+
labelKey: string;
|
|
385
|
+
labelValue: string;
|
|
386
|
+
created?: readonly Address[];
|
|
387
|
+
} | {
|
|
388
|
+
kind: "rejected";
|
|
389
|
+
reason: string;
|
|
390
|
+
};
|
|
391
|
+
/** What a kind's `list` receives. The registry ALSO post-filters the
|
|
392
|
+
* selector's predicates by reading paths through `get`, so honouring
|
|
393
|
+
* `predicates` in `list` is an optimisation, never a duty. */
|
|
394
|
+
export interface ObjectQuery {
|
|
395
|
+
/** The selector step that selected this kind (`plugin:x/layer[name="a"]`). */
|
|
396
|
+
selector?: Selector;
|
|
397
|
+
kind?: string;
|
|
398
|
+
/** The container the selector scoped the step to (`scope > kind`):
|
|
399
|
+
* a page item or story (a host), or one of this plugin's own
|
|
400
|
+
* addresses (`plugin:x/image/1 > plugin:x/layer`). Container scopes
|
|
401
|
+
* (`doc`, `page:`, `spread:`, `master:`, `layer:`) never reach `list`:
|
|
402
|
+
* the registry lists unscoped and keeps the objects whose `hostOf` lies
|
|
403
|
+
* in the container. */
|
|
404
|
+
within?: Address;
|
|
405
|
+
limit?: number;
|
|
406
|
+
/** The step's parsed predicates (this SDK's addition to ADR 323). */
|
|
407
|
+
predicates?: readonly ObjectPredicate[];
|
|
408
|
+
}
|
|
409
|
+
export interface ObjectPredicate {
|
|
410
|
+
key: string;
|
|
411
|
+
op: "=" | "!=" | "^=" | "*=";
|
|
412
|
+
value: string;
|
|
413
|
+
}
|
|
414
|
+
/** One object KIND a plugin contributes (ADR 323). Headless; live for the
|
|
415
|
+
* whole session; declared under `contributes.objectModel.kinds[]`. */
|
|
416
|
+
export interface ObjectKindContribution {
|
|
417
|
+
/** Plugin-local kind name (`layer`, `cell`). Addresses are
|
|
418
|
+
* `plugin:<manifest.id>/<kind>/<id>`. */
|
|
419
|
+
kind: string;
|
|
420
|
+
title: string;
|
|
421
|
+
/** At least one row; validated at registration. */
|
|
422
|
+
schema: readonly PropertySchema[];
|
|
423
|
+
content?: ContentSchema;
|
|
424
|
+
/** The core element hosting an object (frame / scene layer / page /
|
|
425
|
+
* `doc`): where its labels live (ADR 559), and how the registry scopes
|
|
426
|
+
* `page:x > plugin:…` selectors. May answer asynchronously. */
|
|
427
|
+
hostOf(address: Address): Address | null | Promise<Address | null>;
|
|
428
|
+
list(query: ObjectQuery): readonly Address[] | Promise<readonly Address[]>;
|
|
429
|
+
get(address: Address, path: PropertyPathName): ObjectValue | Promise<ObjectValue>;
|
|
430
|
+
set?(address: Address, path: PropertyPathName, value: unknown): ObjectWrite | Promise<ObjectWrite>;
|
|
431
|
+
/** Preferred over repeated `set`: many ops, one write, one undo step.
|
|
432
|
+
* Receives this kind's `set` / `create` / `delete` ops. Ignored when
|
|
433
|
+
* the contribution has a plugin-level `batch`. */
|
|
434
|
+
batch?(ops: readonly ObjectOp[]): ObjectWrite | Promise<ObjectWrite>;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* A TYPED command (ADR 323): its args and result carry value types, so the
|
|
438
|
+
* CLI, Boa and a generated form can call it without knowing the plugin.
|
|
439
|
+
* `host.objects.invoke` validates `args` first. The untyped
|
|
440
|
+
* `contribute.command` stays for the migration (a shrink-only list).
|
|
441
|
+
*/
|
|
442
|
+
export interface TypedCommandContribution<A = unknown, R = unknown> {
|
|
443
|
+
id: string;
|
|
444
|
+
title: string;
|
|
445
|
+
/** Usually `{ kind: "struct", fields }`. */
|
|
446
|
+
args: ValueType;
|
|
447
|
+
result?: ValueType;
|
|
448
|
+
handler(ctx: {
|
|
449
|
+
objects: ObjectsSurface;
|
|
450
|
+
}, args: A): R | Promise<R>;
|
|
451
|
+
}
|
|
452
|
+
export interface ObjectModelContribution {
|
|
453
|
+
kinds: readonly ObjectKindContribution[];
|
|
454
|
+
commands?: readonly TypedCommandContribution[];
|
|
455
|
+
/**
|
|
456
|
+
* PLUGIN-level planner: receives every `set` / `create` / `delete` op of
|
|
457
|
+
* a batch that targets ANY of this plugin's kinds, in batch order, and
|
|
458
|
+
* answers ONE write. Use it whenever two kinds share state (an image's
|
|
459
|
+
* layers and adjustments live in one stack): planned per kind, each
|
|
460
|
+
* kind would start from the same state and the second write would drop
|
|
461
|
+
* the first one's changes. When present, the kinds' own `batch`/`set`
|
|
462
|
+
* are not used for writes. Without it, ops are planned per kind, and
|
|
463
|
+
* two per-kind `state` writes landing on the same host are refused
|
|
464
|
+
* (`failed`, "conflicting state writes") rather than silently merged.
|
|
465
|
+
*/
|
|
466
|
+
batch?(ops: readonly ObjectOp[]): ObjectWrite | Promise<ObjectWrite>;
|
|
467
|
+
}
|
|
468
|
+
/** What `contribute.objectModel` returns: a `Disposable` (ADR 323) that
|
|
469
|
+
* can also announce changes the engine does not see (wasm-held state). */
|
|
470
|
+
export interface ObjectModelHandle extends Disposable {
|
|
471
|
+
/** No argument = every object of this plugin may have changed. */
|
|
472
|
+
changed(addresses?: readonly Address[]): void;
|
|
473
|
+
}
|
|
474
|
+
export interface ObjectWriteResult {
|
|
475
|
+
applied: boolean;
|
|
476
|
+
reason?: string;
|
|
477
|
+
/** Engine undo steps the write took: a batch is ONE. */
|
|
478
|
+
undoSteps: 0 | 1;
|
|
479
|
+
/** This SDK's additions: the refusal code and the failing op. */
|
|
480
|
+
code?: ObjectErrorCode;
|
|
481
|
+
index?: number;
|
|
482
|
+
/** The engine outcome (minted ids for `create`/`$h:`). */
|
|
483
|
+
outcome?: MutationOutcome;
|
|
484
|
+
/** The address of every object a `create` op made, in op order (this
|
|
485
|
+
* SDK's addition; a core create needs engine protocol 71). */
|
|
486
|
+
created?: readonly Address[];
|
|
487
|
+
/** One entry per typed CORE command (`invoke` of `core.<op>`) the batch
|
|
488
|
+
* ran, in op order: its op index and the addresses of what it created
|
|
489
|
+
* (this SDK's addition, DESIGN.md §21.12). */
|
|
490
|
+
commands?: readonly ObjectCommandResult[];
|
|
491
|
+
}
|
|
492
|
+
/** What one typed core command of a batch made. */
|
|
493
|
+
export interface ObjectCommandResult {
|
|
494
|
+
index: number;
|
|
495
|
+
command: string;
|
|
496
|
+
created: readonly Address[];
|
|
497
|
+
}
|
|
498
|
+
export interface ObjectChange {
|
|
499
|
+
/** Empty = "anything may have changed" (core reports page-level damage
|
|
500
|
+
* today, not addresses). */
|
|
501
|
+
addresses: readonly Address[];
|
|
502
|
+
paths?: readonly string[];
|
|
503
|
+
origin: "core" | string;
|
|
504
|
+
}
|
|
505
|
+
export interface ObjectKindInfo {
|
|
506
|
+
owner: "core" | string;
|
|
507
|
+
/** Qualified: core kinds bare (`frame`), plugin kinds
|
|
508
|
+
* `plugin:<pluginId>/<kind>`. */
|
|
509
|
+
kind: string;
|
|
510
|
+
title: string;
|
|
511
|
+
/** The address form, e.g. `plugin:acme.notes/note/<id>`. */
|
|
512
|
+
addressForm: string;
|
|
513
|
+
}
|
|
514
|
+
export interface ObjectCommandInfo {
|
|
515
|
+
id: string;
|
|
516
|
+
title: string;
|
|
517
|
+
owner: string;
|
|
518
|
+
args: ValueType;
|
|
519
|
+
result?: ValueType;
|
|
520
|
+
}
|
|
521
|
+
/** `host.objects` — the one registry over core and every plugin. */
|
|
522
|
+
export interface ObjectsSurface {
|
|
523
|
+
query(selector: Selector, opts?: {
|
|
524
|
+
limit?: number;
|
|
525
|
+
}): Promise<Address[]>;
|
|
526
|
+
get(address: Address, path: PropertyPathName): Promise<ObjectValue>;
|
|
527
|
+
/** Every schema path of the object, read — except `heavy` rows (and
|
|
528
|
+
* `content`), which are read only with `{ content: true }`. */
|
|
529
|
+
getAll(address: Address, opts?: {
|
|
530
|
+
content?: boolean;
|
|
531
|
+
}): Promise<Record<string, ObjectValue>>;
|
|
532
|
+
set(address: Address, path: PropertyPathName, value: unknown, opts?: {
|
|
533
|
+
label?: string;
|
|
534
|
+
}): Promise<ObjectWriteResult>;
|
|
535
|
+
/** All-or-nothing: validated and planned before anything applies; core
|
|
536
|
+
* ops and plugin plans land as ONE engine batch = one undo step. */
|
|
537
|
+
batch(ops: readonly ObjectOp[], opts?: {
|
|
538
|
+
label?: string;
|
|
539
|
+
}): Promise<ObjectWriteResult>;
|
|
540
|
+
/** The rows of an address's kind, or of a qualified kind name. Empty
|
|
541
|
+
* when the kind is unknown. */
|
|
542
|
+
schema(addressOrKind: Address | string): Promise<readonly PropertySchema[]>;
|
|
543
|
+
kinds(): Promise<readonly ObjectKindInfo[]>;
|
|
544
|
+
/** Typed commands contributed through `contribute.objectModel`. */
|
|
545
|
+
commands(): Promise<readonly ObjectCommandInfo[]>;
|
|
546
|
+
/** Run a typed command. Rejects with an `Error` whose message starts
|
|
547
|
+
* with the refusal code (`unknownCommand: …`, `invalidValue: …`).
|
|
548
|
+
* A CORE command (`core.<op>`, listed by `commands()` with owner
|
|
549
|
+
* `"core"`) runs as ONE batch and answers `{ created, undoSteps }`;
|
|
550
|
+
* it also joins `batch` as an `invoke` op (one undo step with the
|
|
551
|
+
* batch's sets / creates / deletes). */
|
|
552
|
+
invoke(command: string, args: unknown): Promise<unknown>;
|
|
553
|
+
onDidChange(listener: (e: ObjectChange) => void, filter?: {
|
|
554
|
+
selector?: Selector;
|
|
555
|
+
}): Disposable;
|
|
556
|
+
}
|
package/dist/panel-schema.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
import type { BindingsSurface } from "./host";
|
|
3
|
+
import type { Address, PropertyPathName, Selector } from "./object-model";
|
|
3
4
|
import type { PropertyPath, Value } from "./wire";
|
|
4
5
|
export type WidgetValueBinding = {
|
|
5
6
|
kind: "literal";
|
|
@@ -90,7 +91,113 @@ export interface SchemaListSpec {
|
|
|
90
91
|
/** Per-row action buttons. */
|
|
91
92
|
actions?: SchemaListAction[];
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Which object(s) a property row edits.
|
|
96
|
+
* · `"selection"` — the selected objects. With `kind` set, the objects of
|
|
97
|
+
* that kind HOSTED by each selected object (`<selected> > <kind>`):
|
|
98
|
+
* the web frame of a selected text frame, the layers of a selected
|
|
99
|
+
* image frame.
|
|
100
|
+
* · a selector string (ADR 131) — every address it selects, re-resolved
|
|
101
|
+
* when the object model changes. Also the durable "Bind to data…"
|
|
102
|
+
* target, so prefer a name/label selector over a raw session address.
|
|
103
|
+
* · `{ bind }` — an address, or a list of addresses, the plugin publishes
|
|
104
|
+
* through `host.bindings.publish(name, …)` (its active layer, the cell
|
|
105
|
+
* under the cursor). Absent / empty = no target (the field disables).
|
|
106
|
+
* · `{ addresses }` — explicit addresses, as given.
|
|
107
|
+
*/
|
|
108
|
+
export type PropertyFieldAddress = "selection" | Selector | {
|
|
109
|
+
bind: string;
|
|
110
|
+
} | {
|
|
111
|
+
addresses: readonly Address[];
|
|
112
|
+
};
|
|
113
|
+
/** How a property row presents an `enum` (or a text row offering a
|
|
114
|
+
* collection): the host picks from the schema row otherwise. */
|
|
115
|
+
export type PropertyFieldStyle = "select" | "segments" | "collection";
|
|
116
|
+
/** One option override for a `select` / `segments` presentation (labels
|
|
117
|
+
* for enum members; the member list still comes from the schema). */
|
|
118
|
+
export interface PropertyFieldOption {
|
|
119
|
+
value: string;
|
|
120
|
+
label?: string;
|
|
121
|
+
icon?: string;
|
|
122
|
+
}
|
|
123
|
+
/** One sub-field a property row shows of a STRUCT value (or of each
|
|
124
|
+
* struct item of a list): `field` is the struct field's name; the widget
|
|
125
|
+
* comes from that field's value type, like a row's comes from its schema
|
|
126
|
+
* row. A bare string is `{ field }`. */
|
|
127
|
+
export interface PropertySubField {
|
|
128
|
+
field: string;
|
|
129
|
+
label?: string;
|
|
130
|
+
style?: PropertyFieldStyle;
|
|
131
|
+
options?: readonly PropertyFieldOption[];
|
|
132
|
+
/** Leaf chrome passthrough. */
|
|
133
|
+
props?: Record<string, unknown>;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* How a property row presents a LIST value (`{ kind: "list", of }`): an
|
|
137
|
+
* appearance stack's fills, a gradient's stops. Every edit the host
|
|
138
|
+
* offers is a {@link ListEdit} the SDK lowers to `set` ops (one batch, one
|
|
139
|
+
* undo step) — `planValueEdit` / `editValue` in plugin-sdk.
|
|
140
|
+
*/
|
|
141
|
+
export interface PropertyListPresentation {
|
|
142
|
+
/** Struct items: the fields each item shows, in order. Absent = every
|
|
143
|
+
* declared field of the item struct. Scalar items show one widget. */
|
|
144
|
+
fields?: readonly (string | PropertySubField)[];
|
|
145
|
+
/** The item field whose value titles an item (`name`, `stopColor`). */
|
|
146
|
+
itemLabel?: string;
|
|
147
|
+
/** Offer add / remove / reorder. Each defaults to `true` on a
|
|
148
|
+
* read-write row; `add` also stops at the type's `maxItems`. */
|
|
149
|
+
add?: boolean;
|
|
150
|
+
remove?: boolean;
|
|
151
|
+
reorder?: boolean;
|
|
152
|
+
/** List the LAST item first (a stack painted bottom → top reads
|
|
153
|
+
* top-first, like a layers panel). Indices in the edits stay value
|
|
154
|
+
* indices. Default `false`. */
|
|
155
|
+
reversed?: boolean;
|
|
156
|
+
/** The item `add` inserts. Absent = the item type's default
|
|
157
|
+
* (`defaultValueOf`: struct field defaults, the first enum member,
|
|
158
|
+
* 0, "", false, []). Must satisfy the item type. */
|
|
159
|
+
newItem?: unknown;
|
|
160
|
+
}
|
|
161
|
+
/** The property row (the property row). Mutually exclusive with `widget`: a
|
|
162
|
+
* row is EITHER a catalog widget OR an object-model property field. */
|
|
163
|
+
export interface PanelSchemaPropertyRow {
|
|
164
|
+
field: "property";
|
|
165
|
+
/** The object(s) the field edits — see {@link PropertyFieldAddress}. */
|
|
166
|
+
address: PropertyFieldAddress;
|
|
167
|
+
/** The ADR 132 path on the target's kind (`opacity`, `blendMode`,
|
|
168
|
+
* `points[0].anchor`). */
|
|
169
|
+
path: PropertyPathName;
|
|
170
|
+
/** The qualified kind (`plugin:<id>/<kind>`, or a core kind) whose
|
|
171
|
+
* schema row types the field. Required with `address: "selection"`
|
|
172
|
+
* for plugin objects (it scopes the selection to that kind); otherwise
|
|
173
|
+
* it lets the field show its widget before any target resolves. */
|
|
174
|
+
kind?: string;
|
|
175
|
+
/** Row label (sentence case). Absent = the schema row's `title`, then
|
|
176
|
+
* the path. */
|
|
177
|
+
label?: string;
|
|
178
|
+
style?: PropertyFieldStyle;
|
|
179
|
+
options?: readonly PropertyFieldOption[];
|
|
180
|
+
/** Offer "Bind to data…" (default: when the schema row is bindable). */
|
|
181
|
+
bindable?: boolean;
|
|
182
|
+
/** A STRUCT-typed path: the fields to show, in order (absent = every
|
|
183
|
+
* declared field; an `open` struct with no `fields` shows none and
|
|
184
|
+
* falls back to its JSON text). Edits are {@link StructEdit}s. */
|
|
185
|
+
fields?: readonly (string | PropertySubField)[];
|
|
186
|
+
/** A LIST-typed path: how its items present and which edits are
|
|
187
|
+
* offered. Absent = the defaults of {@link PropertyListPresentation}. */
|
|
188
|
+
items?: PropertyListPresentation;
|
|
189
|
+
/** Leaf chrome passthrough (placeholder, prefix, suffix, icon, …). */
|
|
190
|
+
props?: Record<string, unknown>;
|
|
191
|
+
visible?: SchemaGate;
|
|
192
|
+
enabled?: SchemaGate;
|
|
193
|
+
/** Never set on a property row (keeps `row.widget` readable on the
|
|
194
|
+
* union). */
|
|
195
|
+
widget?: undefined;
|
|
196
|
+
value?: undefined;
|
|
197
|
+
list?: undefined;
|
|
198
|
+
}
|
|
199
|
+
/** The catalog-widget row (schema v1 / v1.1). */
|
|
200
|
+
export interface PanelSchemaWidgetRow {
|
|
94
201
|
/** Catalog widget id (a curated primitive-leaf id the host
|
|
95
202
|
* registered). An unknown id renders a visible "unknown widget"
|
|
96
203
|
* placeholder, not a throw. */
|
|
@@ -112,7 +219,12 @@ export interface PanelSchemaRow {
|
|
|
112
219
|
* always enabled (subject to the widget's own no-write-path
|
|
113
220
|
* disable). */
|
|
114
221
|
enabled?: SchemaGate;
|
|
222
|
+
/** Never set on a widget row. */
|
|
223
|
+
field?: undefined;
|
|
115
224
|
}
|
|
225
|
+
/** One panel row: a catalog widget, or an object-model property
|
|
226
|
+
* field. Discriminate with `isPropertyRow` (plugin-sdk) or `row.field`. */
|
|
227
|
+
export type PanelSchemaRow = PanelSchemaWidgetRow | PanelSchemaPropertyRow;
|
|
116
228
|
export interface PanelSchemaSection {
|
|
117
229
|
/** Section title (the kicker / disclosure header). Absent = an
|
|
118
230
|
* untitled flat group. */
|