@paged-media/plugin-api 0.2.27-canary.1 → 0.2.29-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/dist/wire.d.ts +427 -40
- 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;
|