@docx-editor.dev/core 0.0.1 → 2.0.1

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.
Files changed (116) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +92 -2
  3. package/THIRD_PARTY_NOTICES.md +18 -0
  4. package/dist/automation.cjs +1 -0
  5. package/dist/automation.d.cts +74 -0
  6. package/dist/automation.d.ts +74 -0
  7. package/dist/automation.js +1 -0
  8. package/dist/binding.cjs +1 -0
  9. package/dist/binding.d.cts +82 -0
  10. package/dist/binding.d.ts +82 -0
  11. package/dist/binding.js +1 -0
  12. package/dist/chunk-5KSCB2OZ.js +2 -0
  13. package/dist/chunk-BSC44TIT.js +5 -0
  14. package/dist/chunk-DLPL5DAG.js +12 -0
  15. package/dist/chunk-DOEIARE2.js +2 -0
  16. package/dist/chunk-G7YCQBZE.cjs +5 -0
  17. package/dist/chunk-HBL3NNH5.cjs +1 -0
  18. package/dist/chunk-HR7K3W6A.js +1 -0
  19. package/dist/chunk-HVAV2JSN.js +11 -0
  20. package/dist/chunk-MYH2HCUJ.cjs +2 -0
  21. package/dist/chunk-NSM75TN6.cjs +1 -0
  22. package/dist/chunk-PHDVKUWZ.js +1 -0
  23. package/dist/chunk-QADYGK5L.cjs +12 -0
  24. package/dist/chunk-REZUWYB7.js +1 -0
  25. package/dist/chunk-RNBHTPW7.js +10 -0
  26. package/dist/chunk-UD2ZXLFI.cjs +2 -0
  27. package/dist/chunk-VYAVK2AH.cjs +1 -0
  28. package/dist/chunk-YUK46A2N.cjs +11 -0
  29. package/dist/chunk-ZMIX42J7.cjs +10 -0
  30. package/dist/chunk-ZT55Q52J.cjs +2 -0
  31. package/dist/chunk-ZUALAR4M.js +2 -0
  32. package/dist/content-control-walk-CbTqHq6O.d.cts +56 -0
  33. package/dist/content-control-walk-D06GijR0.d.ts +56 -0
  34. package/dist/contracts/document.cjs +1 -0
  35. package/dist/contracts/document.d.cts +246 -0
  36. package/dist/contracts/document.d.ts +246 -0
  37. package/dist/contracts/document.js +0 -0
  38. package/dist/contracts/editor.cjs +1 -0
  39. package/dist/contracts/editor.d.cts +11 -0
  40. package/dist/contracts/editor.d.ts +11 -0
  41. package/dist/contracts/editor.js +1 -0
  42. package/dist/contracts/interaction.cjs +1 -0
  43. package/dist/contracts/interaction.d.cts +11 -0
  44. package/dist/contracts/interaction.d.ts +11 -0
  45. package/dist/contracts/interaction.js +0 -0
  46. package/dist/contracts/modules.cjs +1 -0
  47. package/dist/contracts/modules.d.cts +90 -0
  48. package/dist/contracts/modules.d.ts +90 -0
  49. package/dist/contracts/modules.js +1 -0
  50. package/dist/contracts/types.cjs +1 -0
  51. package/dist/contracts/types.d.cts +452 -0
  52. package/dist/contracts/types.d.ts +452 -0
  53. package/dist/contracts/types.js +0 -0
  54. package/dist/editor-C1QRi1v4.d.ts +1671 -0
  55. package/dist/editor-D0UDock4.d.cts +1671 -0
  56. package/dist/editor.cjs +1 -0
  57. package/dist/editor.css +6017 -0
  58. package/dist/editor.d.cts +271 -0
  59. package/dist/editor.d.ts +271 -0
  60. package/dist/editor.js +1 -0
  61. package/dist/hf-references-BdjsQNE_.d.ts +71 -0
  62. package/dist/hf-references-CgEOSw3i.d.cts +71 -0
  63. package/dist/image-resources-BJnTpG66.d.ts +195 -0
  64. package/dist/image-resources-BpIydXtU.d.cts +195 -0
  65. package/dist/index-CrW-IxXt.d.cts +2515 -0
  66. package/dist/index-ZqBEOrTV.d.ts +2515 -0
  67. package/dist/index.cjs +1 -0
  68. package/dist/index.d.cts +22 -0
  69. package/dist/index.d.ts +22 -0
  70. package/dist/index.js +1 -0
  71. package/dist/layout.cjs +1 -0
  72. package/dist/layout.d.cts +2316 -0
  73. package/dist/layout.d.ts +2316 -0
  74. package/dist/layout.js +1 -0
  75. package/dist/note-nodes-BPSTVDMr.d.ts +108 -0
  76. package/dist/note-nodes-C6DLxlvj.d.cts +108 -0
  77. package/dist/note-properties-CnmCoory.d.cts +68 -0
  78. package/dist/note-properties-m75hmP70.d.ts +68 -0
  79. package/dist/ooxml-package-CR5us6ad.d.cts +354 -0
  80. package/dist/ooxml-package-D_CtywTW.d.ts +354 -0
  81. package/dist/ooxml-tree-BU0e3DVi.d.cts +871 -0
  82. package/dist/ooxml-tree-BU0e3DVi.d.ts +871 -0
  83. package/dist/output.cjs +1 -0
  84. package/dist/output.d.cts +158 -0
  85. package/dist/output.d.ts +158 -0
  86. package/dist/output.js +1 -0
  87. package/dist/protocol-CAkOBBGE.d.cts +1201 -0
  88. package/dist/protocol-oxHaWmt4.d.ts +1201 -0
  89. package/dist/review-support-DS8DYX2a.d.cts +326 -0
  90. package/dist/review-support-Fy3vdIea.d.ts +326 -0
  91. package/dist/revision-projection-72ZApe-g.d.cts +84 -0
  92. package/dist/revision-projection-DHAkcD_L.d.ts +84 -0
  93. package/dist/semantic-cell-selection-CZjBSmMv.d.cts +427 -0
  94. package/dist/semantic-cell-selection-DsmHmLJ5.d.ts +427 -0
  95. package/dist/semantic-interaction-D-Dszxrn.d.cts +187 -0
  96. package/dist/semantic-interaction-Dy-dCUFF.d.ts +187 -0
  97. package/dist/semantic-paint-drawings-BA3z9jl8.d.ts +21 -0
  98. package/dist/semantic-paint-drawings-D7qbB0E2.d.cts +21 -0
  99. package/dist/semantic-records-CJR3BvHs.d.cts +1432 -0
  100. package/dist/semantic-records-CWodBJsz.d.ts +1432 -0
  101. package/dist/shaped-measurer-B-TAtcQU.d.ts +613 -0
  102. package/dist/shaped-measurer-CNIT_svZ.d.cts +613 -0
  103. package/dist/store.cjs +1 -0
  104. package/dist/store.d.cts +2247 -0
  105. package/dist/store.d.ts +2247 -0
  106. package/dist/store.js +1 -0
  107. package/dist/tree-op-types-CwOeiaQ8.d.ts +1431 -0
  108. package/dist/tree-op-types-DMiW6nIH.d.cts +1431 -0
  109. package/dist/tree-package-store-D5S7VKs5.d.ts +708 -0
  110. package/dist/tree-package-store-DOKMtCnh.d.cts +708 -0
  111. package/dist/tree-session-CiUel5Kz.d.ts +542 -0
  112. package/dist/tree-session-D47_imvR.d.cts +542 -0
  113. package/dist/tree-store-CxifqwIv.d.cts +273 -0
  114. package/dist/tree-store-hLtjeyWE.d.ts +273 -0
  115. package/package.json +105 -8
  116. package/index.js +0 -1
@@ -0,0 +1,2515 @@
1
+ import { i as FontSource, j as FontSourceSubstitution, F as FontConfiguration, k as FontFaceRequest, I as ImageContext, D as DocumentSource, E as EditorFontError, l as Editor, a as EditorCommand, C as CanResult, S as SelectedImageState, V as ViewScope, T as TableBorderEdgeTarget, m as TableBorderSpec, n as TableBorderStyle, o as TableContext } from './editor-C1QRi1v4.js';
2
+ import { T as TreeDocOp, d as DrawingPositionInput, k as DrawingTransform, m as DrawingTreeDocOp } from './tree-op-types-CwOeiaQ8.js';
3
+ import { b as ImageDecodePort, S as SupportedImageMime } from './image-resources-BJnTpG66.js';
4
+ import { R as RevisionDisplayMode } from './revision-projection-DHAkcD_L.js';
5
+ import './review-support-Fy3vdIea.js';
6
+ import { a as SemanticSelection, N as NavigationCommand, S as SemanticPosition } from './semantic-interaction-Dy-dCUFF.js';
7
+ import { R as ResolvedFootnoteProperties, a as ResolvedEndnoteProperties, A as AuthoredNoteProperties } from './note-properties-m75hmP70.js';
8
+ import { S as SemanticLayout, C as ContentControlBoundaryRecord, T as TextMeasurer } from './semantic-records-CWodBJsz.js';
9
+ import { S as StoryScope, A as ApplyImagePropertiesInput, b as ImageIntentResult, I as InsertImageInput, B as BookmarkIndex } from './tree-package-store-D5S7VKs5.js';
10
+ import { C as CellSelection, S as SectionProperties } from './semantic-cell-selection-DsmHmLJ5.js';
11
+ import { EditorModule, ReviewModuleContribution } from './contracts/modules.js';
12
+ import { Unsubscribe, ExecResult, IndentFormatting } from './contracts/types.js';
13
+ import { D as DrawingPaintStrings } from './semantic-paint-drawings-BA3z9jl8.js';
14
+ import { T as TreeDocxSession, a as TreeApplyResult } from './tree-session-CiUel5Kz.js';
15
+ import './contracts/document.js';
16
+
17
+ /**
18
+ * A partial font configuration one origin contributes: sources, substitutions, or both.
19
+ * `loadDefaultFonts()` (the substitute package) and `loadFonts()` (the fetch helper) both
20
+ * return this shape, so every origin composes through `composeFontConfiguration` the
21
+ * same way.
22
+ */
23
+ interface FontConfigurationFragment {
24
+ readonly sources?: readonly FontSource[];
25
+ readonly substitutions?: readonly FontSourceSubstitution[];
26
+ }
27
+ /**
28
+ * The base of a composition: everything a `FontConfiguration` carries, all optional.
29
+ * Omitted fields take the documented defaults (`epoch` 0, `maxFontBytes` at the engine
30
+ * hard maximum, `defaultFont` Calibri at 11pt — Word's own default face and size).
31
+ */
32
+ interface FontConfigurationBase extends FontConfigurationFragment {
33
+ /**
34
+ * Identity of this configuration's byte set. The engine uses it to tell one resolved
35
+ * font set from another; leave it unset and the editor supplies the load sequence.
36
+ */
37
+ readonly epoch?: number;
38
+ /** Per-face byte ceiling. Defaults to the engine hard maximum; lower it to tighten intake. */
39
+ readonly maxFontBytes?: number;
40
+ /** The face used when a run names no font. Defaults to Word's own: Calibri at 11pt. */
41
+ readonly defaultFont?: FontConfiguration['defaultFont'];
42
+ /** BCP-47 tag passed to the shaper for language-sensitive shaping. */
43
+ readonly language?: string;
44
+ }
45
+ /**
46
+ * What the document turned out to need, handed to an on-demand resolver.
47
+ *
48
+ * The families are the ones the file actually names — already name-validated and capped,
49
+ * so a resolver may treat them as a list to look up, never as URLs or paths to build.
50
+ */
51
+ interface FontResolutionRequest {
52
+ /**
53
+ * Families declared anywhere in the document (body, headers/footers, styles), deduped,
54
+ * sorted, and capped at {@link MAX_RESOLVER_FAMILIES}.
55
+ */
56
+ readonly families: readonly string[];
57
+ /** The face a run naming no font resolves to, so a resolver can cover it too. */
58
+ readonly defaultFamily: string;
59
+ }
60
+ /**
61
+ * Resolve fonts once the document's needs are known, instead of ahead of them.
62
+ *
63
+ * Called once per load, AFTER the file is parsed and mounted, with the families it
64
+ * declares; whatever it returns composes exactly like a statically supplied fragment.
65
+ * Returning nothing is a valid answer — it means "I cover none of this", and the
66
+ * document stays on the fixed measurer.
67
+ *
68
+ * A resolver that fetches makes opening a document perform network requests. That is a
69
+ * real change in posture and it must stay the APP's decision: the engine never supplies
70
+ * one, and the families here are file-derived, so a resolver must look them up in a set
71
+ * it shipped rather than interpolate them into a URL.
72
+ */
73
+ type FontResolver = (request: FontResolutionRequest) => FontConfiguration | FontConfigurationFragment | undefined | Promise<FontConfiguration | FontConfigurationFragment | undefined>;
74
+ /**
75
+ * Ceiling on the families one document can put in front of a resolver.
76
+ *
77
+ * A resolver typically turns each family into up to four faces, and the resource snapshot
78
+ * refuses more than `HARD_MAX_FONT_SOURCES` (256) sources — so 64 families is the point
79
+ * past which a document could no longer be served anyway. Capping here means a file
80
+ * declaring thousands of distinct `w:rFonts` cannot fan a resolver out across thousands
81
+ * of lookups (or fetches) before that limit is ever reached.
82
+ */
83
+ declare const MAX_RESOLVER_FAMILIES = 64;
84
+ /** Word's document default when nothing else says otherwise: Calibri at 11pt. */
85
+ declare const WORD_DEFAULT_FONT: FontConfiguration['defaultFont'];
86
+ /**
87
+ * Merge a base and any number of fragments into one frozen `FontConfiguration`.
88
+ *
89
+ * A bare fragment IS a valid base, so the single-origin case is one argument:
90
+ * `composeFontConfiguration(await loadDefaultFonts())`. Pass extra fragments to layer
91
+ * origins, and set `epoch`/`maxFontBytes`/`defaultFont` on the base only when you need
92
+ * something other than the documented defaults.
93
+ *
94
+ * - Sources dedupe first-wins by (family, weight, style), in argument order — base
95
+ * before fragments, earlier fragments before later ones.
96
+ * - Substitutions dedupe first-wins by their `from` face, in the same order, and every
97
+ * substitution whose `from` face has a direct source anywhere in the composition is
98
+ * dropped: a real face always beats a stand-in.
99
+ * - The result is frozen (arrays included); the byte arrays themselves are the callers'
100
+ * and are not copied here — the resource snapshot takes its own defensive copy.
101
+ */
102
+ declare function composeFontConfiguration(base: FontConfigurationBase, ...fragments: readonly FontConfigurationFragment[]): FontConfiguration;
103
+
104
+ /**
105
+ * A Word-faithful blank document, freshly zipped per call (the caller may hand the
106
+ * bytes to a loader that takes ownership). Calibri 11pt and Word's Normal paragraph
107
+ * spacing are authored in `w:docDefaults`, US Letter geometry in the section — a New
108
+ * document behaves like Word's, and saving it produces a file Word opens identically.
109
+ *
110
+ * @public
111
+ */
112
+ declare function blankDocumentBytes(): Uint8Array;
113
+
114
+ /** One URL to fetch and the face it claims to be. */
115
+ interface FontUrlSource {
116
+ readonly url: string;
117
+ readonly family: string;
118
+ readonly weight: number;
119
+ readonly style: 'normal' | 'italic';
120
+ /**
121
+ * Expected `sha256:` content hash. When present, mismatching bytes are REFUSED —
122
+ * pin this for any URL not under the app's sole control.
123
+ */
124
+ readonly hash?: string;
125
+ readonly faceIndex?: number;
126
+ }
127
+ /**
128
+ * What to fetch, and under what limits.
129
+ *
130
+ * Only `sources` is required. Each carries its own expected hash, so bytes are trusted by
131
+ * CONTENT rather than by origin — a swapped asset fails admission even from a trusted host.
132
+ */
133
+ interface LoadFontsRequest {
134
+ readonly sources: readonly FontUrlSource[];
135
+ /** Cache API bucket name; default `docx-editor-fonts`. */
136
+ readonly cacheName?: string;
137
+ /** Injectable for tests and CSP-constrained hosts; defaults to global `fetch`. */
138
+ readonly fetcher?: typeof fetch;
139
+ /** Per-font byte ceiling; defaults to the engine hard maximum. */
140
+ readonly maxFontBytes?: number;
141
+ }
142
+ /**
143
+ * Why one font did not load.
144
+ *
145
+ * Distinguished rather than collapsed to "failed" because the responses differ: `networkError`
146
+ * and `httpError` are worth retrying, while `hashMismatch` and `malformed` mean the bytes were
147
+ * not what the source claimed and retrying will fetch the same wrong thing.
148
+ */
149
+ type FontLoadFailureReason = 'networkError' | 'httpError' | 'hashMismatch' | 'overLimit' | 'emptyResponse'
150
+ /** The declared face itself is unusable (empty family, out-of-range weight); nothing was fetched. */
151
+ | 'invalidRequest'
152
+ /** The bytes are not a font at all — most often an HTML error page served with 200. */
153
+ | 'malformed';
154
+ /**
155
+ * One face that did not load, with whatever evidence the failure produced.
156
+ *
157
+ * Non-fatal: {@link LoadFontsResult} still carries every source that succeeded, and the affected
158
+ * family falls back to the engine's fixed measurement.
159
+ */
160
+ interface FontLoadFailure {
161
+ readonly url: string;
162
+ readonly request: FontFaceRequest;
163
+ readonly reason: FontLoadFailureReason;
164
+ /** HTTP status for `httpError`; hashes for `hashMismatch`. */
165
+ readonly status?: number;
166
+ readonly expectedHash?: string;
167
+ readonly actualHash?: string;
168
+ readonly diagnostic?: string;
169
+ }
170
+ /**
171
+ * What one `loadFonts` call produced: the faces that arrived, plus the ones that did not.
172
+ *
173
+ * A `FontConfigurationFragment`, so it composes straight into `composeFontConfiguration`
174
+ * alongside other font sources. Partial success is the normal case — compose it even with
175
+ * failures present.
176
+ */
177
+ interface LoadFontsResult extends FontConfigurationFragment {
178
+ readonly sources: readonly FontSource[];
179
+ readonly failures: readonly FontLoadFailure[];
180
+ }
181
+ /**
182
+ * Fetch app-specified font URLs into verified, cache-backed `FontSource`s.
183
+ *
184
+ * Fetches ONLY the URLs listed — never a default host or engine-chosen CDN — and never
185
+ * rejects for a per-source failure: the result carries every admitted source and a
186
+ * typed entry for every drop. Compose the result with `composeFontConfiguration`.
187
+ */
188
+ declare function loadFonts(request: LoadFontsRequest): Promise<LoadFontsResult>;
189
+ /**
190
+ * Turn font bytes you ALREADY hold into a `FontSource` — a file input, IndexedDB, a
191
+ * bundler import. `loadFonts` covers URLs; this covers everything else, so no caller has
192
+ * to hand-assemble the record or reach for a hashing helper.
193
+ *
194
+ * Returns a typed failure instead of throwing when the descriptor or the bytes are
195
+ * unusable, matching `loadFonts`: one bad face degrades itself, never its neighbours.
196
+ */
197
+ declare function createFontSource(bytes: Uint8Array, request: FontFaceRequest & {
198
+ readonly faceIndex?: number;
199
+ }, options?: {
200
+ readonly id?: string;
201
+ readonly maxFontBytes?: number;
202
+ }): {
203
+ readonly source: FontSource;
204
+ } | {
205
+ readonly failure: FontLoadFailure;
206
+ };
207
+
208
+ /**
209
+ * HOW a control reaches the engine — never WHETHER it is enabled.
210
+ *
211
+ * Enabled state has exactly one source: `toolbarCommandState(editor, slot)`, which
212
+ * asks `Editor.can`. The registry is static data and cannot know what the engine
213
+ * will honour at this selection, in this document, at this moment.
214
+ *
215
+ * There used to be a fourth member, `parityOnly`, meaning "visible but permanently
216
+ * disabled". It was a second, static answer to the question `Editor.can` already
217
+ * answers, and it went stale the moment the engine wired underline, strike, the four
218
+ * alignments, the list commands and the four value slots: the registry still said
219
+ * parity-only, React ignored it and ran them, and Vue believed it and rendered twelve
220
+ * WORKING commands permanently disabled. A slot the engine has not wired needs no
221
+ * registry flag — `commandForSlot` answers null and `toolbarCommandState` disables the
222
+ * control with the engine's own words ("not wired to an editor command").
223
+ */
224
+ type ChromeControlState =
225
+ /**
226
+ * Dispatched as one fixed engine command: enabled when
227
+ * `Editor.can(commandForSlot(slot))` succeeds, a click runs
228
+ * `runToolbarCommand(editor, slot)`. The command is resolved from the SLOT id
229
+ * through `commandForSlot` in toolbar-commands.ts — the one command table both
230
+ * adapters share. A slot with no row there is simply not wired YET, and says so
231
+ * through the engine rather than through this descriptor.
232
+ */
233
+ {
234
+ readonly kind: 'command';
235
+ }
236
+ /**
237
+ * Dispatched with a PICKED value: `commandForSlotValue(slot, value)`. Enabled when
238
+ * the engine would honour a well-formed value right now (`toolbarCommandState`
239
+ * probes for exactly that), so the control needs chrome that produces a value — a
240
+ * font list, a size, a colour — before a click means anything.
241
+ *
242
+ * A distinct kind because 'command' cannot describe it: there is no fixed command
243
+ * to hand `Editor.can`, and a bare click has nothing to send.
244
+ */
245
+ | {
246
+ readonly kind: 'value';
247
+ }
248
+ /** `Editor.save()` — not a command (see `runSave`). */
249
+ | {
250
+ readonly kind: 'save';
251
+ }
252
+ /**
253
+ * `Editor.load()` — not a command either, and the exact twin of `save`: bytes cross the
254
+ * boundary between the host and the engine, and only the host can produce them. The
255
+ * control needs chrome that reads a file (a picker, a drop target) before a click means
256
+ * anything, so a bare click has nothing to send — the same reason `save` is not `command`.
257
+ *
258
+ * A fourth kind rather than reusing `save` because the two dispatch in opposite
259
+ * directions, and an adapter branching on `kind` must be able to tell them apart. Unlike
260
+ * the deleted `parityOnly`, this one IS named by a control (`file.open`).
261
+ */
262
+ | {
263
+ readonly kind: 'load';
264
+ };
265
+ /**
266
+ * The SHAPE a control renders as (task M6V.1).
267
+ *
268
+ * The legacy toolbar is not a row of uniform icon buttons: it mixes labelled dropdowns
269
+ * (`Normal`, `Arial`, alignment, line spacing), numeric steppers with visible values
270
+ * (zoom `- 100% +`, size `- 26 +`), split colour controls (a glyph over a colour swatch
271
+ * with its own caret), and a mode pill (`Editing`). Rendering all of them as icon
272
+ * buttons is the difference an owner review called out as "not visual parity" — the
273
+ * regions were all present and it still did not look like the product.
274
+ */
275
+ /** `modePill` is deliberately absent. It was declared, never used by any control, and
276
+ * rendered nothing — the editing-mode control below is a `dropdown`, which is what the
277
+ * legacy product shows. A shape no descriptor names is a branch adapters must implement
278
+ * and can never exercise. */
279
+ type ChromeControlShape = 'icon' | 'stepper' | 'dropdown' | 'colorSplit';
280
+ /**
281
+ * One toolbar control as the registry describes it — what it renders as, never whether it is
282
+ * enabled.
283
+ *
284
+ * Enabled state has exactly ONE source, `toolbarCommandState`, which asks the engine. A
285
+ * descriptor carrying its own disabled flag would be a second answer that goes stale the moment
286
+ * the engine wires the slot.
287
+ *
288
+ * @public
289
+ */
290
+ interface ChromeControl<Id extends string = string> {
291
+ /** Stable control id, unique WITHIN its group. Public API; renames are breaking. */
292
+ readonly id: Id;
293
+ /** How it renders. Defaults to `icon`. */
294
+ readonly shape?: ChromeControlShape;
295
+ /** Displayed value for a stepper or dropdown (an i18n key, or a literal for numbers). */
296
+ readonly valueText?: string;
297
+ /** Swatch colour for a `colorSplit` control. */
298
+ readonly swatch?: string;
299
+ /** i18n key for the accessible name and tooltip. Never hardcoded English. */
300
+ readonly labelKey: string;
301
+ /** Material Symbols path data, or null for a non-icon control (a picker). */
302
+ readonly paths: readonly string[] | null;
303
+ /** For pickers: the i18n key of the placeholder value shown. */
304
+ readonly valueKey?: string;
305
+ readonly state: ChromeControlState;
306
+ }
307
+ /**
308
+ * One toolbar group: the taxonomy both adapters derive their default arrangement FROM.
309
+ *
310
+ * Never hand-list controls in an adapter — a default toolbar is built by walking `CHROME_GROUPS`,
311
+ * so a slot added here appears in React and Vue without either being edited.
312
+ *
313
+ * @public
314
+ */
315
+ interface ChromeGroup<Id extends string = string, ControlId extends string = string> {
316
+ /** Stable group id. Public API; renames are breaking. */
317
+ readonly id: Id;
318
+ readonly labelKey: string;
319
+ /**
320
+ * Not part of the DEFAULT toolbar arrangement. The chrome spec shows these
321
+ * controls only in a context the engine does not model yet (an image or table
322
+ * selection), or not at all (save belongs in the host's File menu, never in the
323
+ * bar). Their slots stay public for composition — a host can still place
324
+ * `image.insert` or `file.save` explicitly — but the default chrome is the
325
+ * registry's default bar, which ends at the editing-mode picker.
326
+ */
327
+ readonly contextual?: true;
328
+ readonly controls: readonly ChromeControl<ControlId>[];
329
+ }
330
+ /**
331
+ * The complete chrome, in bar order. Literal-typed (`as const`) so the slot-id
332
+ * vocabulary below is derived from the data and cannot drift from it.
333
+ *
334
+ * Taxonomy taste: ids are short, lowercaseCamel, and never repeat their group's name
335
+ * (`alignment.left`, not `alignment.alignLeft`; `font.family`, not `font.fontFamily`).
336
+ */
337
+ declare const CHROME_GROUPS: readonly [{
338
+ readonly id: "history";
339
+ readonly labelKey: "formattingBar.groups.history";
340
+ readonly controls: readonly [{
341
+ readonly id: "undo";
342
+ readonly labelKey: "formattingBar.undoShortcut";
343
+ readonly paths: readonly string[];
344
+ readonly state: {
345
+ readonly kind: "command";
346
+ };
347
+ }, {
348
+ readonly id: "redo";
349
+ readonly labelKey: "formattingBar.redoShortcut";
350
+ readonly paths: readonly string[];
351
+ readonly state: {
352
+ readonly kind: "command";
353
+ };
354
+ }];
355
+ }, {
356
+ readonly id: "zoom";
357
+ readonly labelKey: "formattingBar.groups.zoom";
358
+ readonly controls: readonly [{
359
+ readonly id: "level";
360
+ readonly shape: "stepper";
361
+ readonly valueText: "100%";
362
+ readonly labelKey: "formattingBar.groups.zoom";
363
+ readonly paths: null;
364
+ readonly valueKey: "zoom.zoomLevel";
365
+ readonly state: {
366
+ readonly kind: "command";
367
+ };
368
+ }];
369
+ }, {
370
+ readonly id: "styles";
371
+ readonly labelKey: "formattingBar.groups.styles";
372
+ readonly controls: readonly [{
373
+ readonly id: "style";
374
+ readonly shape: "dropdown";
375
+ readonly labelKey: "styles.selectAriaLabel";
376
+ readonly paths: null;
377
+ readonly valueKey: "styles.normalText";
378
+ readonly state: {
379
+ readonly kind: "value";
380
+ };
381
+ }];
382
+ }, {
383
+ readonly id: "font";
384
+ readonly labelKey: "formattingBar.groups.font";
385
+ readonly controls: readonly [{
386
+ readonly id: "family";
387
+ readonly shape: "dropdown";
388
+ readonly labelKey: "font.selectAriaLabel";
389
+ readonly paths: null;
390
+ readonly valueKey: "font.sansSerif";
391
+ readonly state: {
392
+ readonly kind: "value";
393
+ };
394
+ }, {
395
+ readonly id: "size";
396
+ readonly shape: "stepper";
397
+ readonly valueText: "11";
398
+ readonly labelKey: "fontSize.listLabel";
399
+ readonly paths: null;
400
+ readonly valueKey: "fontSize.label";
401
+ readonly state: {
402
+ readonly kind: "value";
403
+ };
404
+ }];
405
+ }, {
406
+ readonly id: "text";
407
+ readonly labelKey: "formattingBar.groups.textFormatting";
408
+ readonly controls: readonly [{
409
+ readonly id: "bold";
410
+ readonly labelKey: "formattingBar.boldShortcut";
411
+ readonly paths: readonly string[];
412
+ readonly state: {
413
+ readonly kind: "command";
414
+ };
415
+ }, {
416
+ readonly id: "italic";
417
+ readonly labelKey: "formattingBar.italicShortcut";
418
+ readonly paths: readonly string[];
419
+ readonly state: {
420
+ readonly kind: "command";
421
+ };
422
+ }, {
423
+ readonly id: "underline";
424
+ readonly labelKey: "formattingBar.underlineShortcut";
425
+ readonly paths: readonly string[];
426
+ readonly state: {
427
+ readonly kind: "command";
428
+ };
429
+ }, {
430
+ readonly id: "strike";
431
+ readonly labelKey: "formattingBar.strikethrough";
432
+ readonly paths: readonly string[];
433
+ readonly state: {
434
+ readonly kind: "command";
435
+ };
436
+ }, {
437
+ readonly id: "color";
438
+ readonly shape: "colorSplit";
439
+ readonly swatch: "#ff0000";
440
+ readonly labelKey: "formattingBar.fontColor";
441
+ readonly paths: readonly string[];
442
+ readonly state: {
443
+ readonly kind: "value";
444
+ };
445
+ }, {
446
+ readonly id: "highlight";
447
+ readonly shape: "colorSplit";
448
+ readonly swatch: "#ffff00";
449
+ readonly labelKey: "formattingBar.highlightColor";
450
+ readonly paths: readonly string[];
451
+ readonly state: {
452
+ readonly kind: "value";
453
+ };
454
+ }, {
455
+ readonly id: "link";
456
+ readonly labelKey: "formattingBar.insertLinkShortcut";
457
+ readonly paths: readonly string[];
458
+ readonly state: {
459
+ readonly kind: "command";
460
+ };
461
+ }];
462
+ }, {
463
+ readonly id: "script";
464
+ readonly labelKey: "formattingBar.groups.script";
465
+ readonly controls: readonly [{
466
+ readonly id: "super";
467
+ readonly labelKey: "formattingBar.superscript";
468
+ readonly paths: readonly string[];
469
+ readonly state: {
470
+ readonly kind: "command";
471
+ };
472
+ }, {
473
+ readonly id: "sub";
474
+ readonly labelKey: "formattingBar.subscript";
475
+ readonly paths: readonly string[];
476
+ readonly state: {
477
+ readonly kind: "command";
478
+ };
479
+ }];
480
+ }, {
481
+ readonly id: "alignment";
482
+ readonly labelKey: "formattingBar.groups.alignment";
483
+ readonly controls: readonly [{
484
+ readonly id: "left";
485
+ readonly labelKey: "alignment.alignLeft";
486
+ readonly paths: readonly string[];
487
+ readonly state: {
488
+ readonly kind: "command";
489
+ };
490
+ }, {
491
+ readonly id: "center";
492
+ readonly labelKey: "alignment.center";
493
+ readonly paths: readonly string[];
494
+ readonly state: {
495
+ readonly kind: "command";
496
+ };
497
+ }, {
498
+ readonly id: "right";
499
+ readonly labelKey: "alignment.alignRight";
500
+ readonly paths: readonly string[];
501
+ readonly state: {
502
+ readonly kind: "command";
503
+ };
504
+ }, {
505
+ readonly id: "justify";
506
+ readonly labelKey: "alignment.justify";
507
+ readonly paths: readonly string[];
508
+ readonly state: {
509
+ readonly kind: "command";
510
+ };
511
+ }];
512
+ }, {
513
+ readonly id: "list";
514
+ readonly labelKey: "formattingBar.groups.listFormatting";
515
+ readonly controls: readonly [{
516
+ readonly id: "bullet";
517
+ readonly labelKey: "lists.bulletList";
518
+ readonly paths: readonly string[];
519
+ readonly state: {
520
+ readonly kind: "command";
521
+ };
522
+ }, {
523
+ readonly id: "numbered";
524
+ readonly labelKey: "lists.numberedList";
525
+ readonly paths: readonly string[];
526
+ readonly state: {
527
+ readonly kind: "command";
528
+ };
529
+ }, {
530
+ readonly id: "outdent";
531
+ readonly labelKey: "lists.decreaseIndent";
532
+ readonly paths: readonly string[];
533
+ readonly state: {
534
+ readonly kind: "command";
535
+ };
536
+ }, {
537
+ readonly id: "indent";
538
+ readonly labelKey: "lists.increaseIndent";
539
+ readonly paths: readonly string[];
540
+ readonly state: {
541
+ readonly kind: "command";
542
+ };
543
+ }, {
544
+ readonly id: "lineSpacing";
545
+ readonly shape: "dropdown";
546
+ readonly labelKey: "lineSpacing.label";
547
+ readonly paths: readonly string[];
548
+ readonly state: {
549
+ readonly kind: "command";
550
+ };
551
+ }];
552
+ }, {
553
+ readonly id: "format";
554
+ readonly labelKey: "formattingBar.clearFormatting";
555
+ readonly controls: readonly [{
556
+ readonly id: "clear";
557
+ readonly labelKey: "formattingBar.clearFormatting";
558
+ readonly paths: readonly string[];
559
+ readonly state: {
560
+ readonly kind: "command";
561
+ };
562
+ }];
563
+ }, {
564
+ readonly id: "review";
565
+ readonly labelKey: "formattingBar.commentsAndChanges";
566
+ readonly controls: readonly [{
567
+ readonly id: "comments";
568
+ readonly shape: "icon";
569
+ readonly labelKey: "formattingBar.commentsAndChanges";
570
+ readonly paths: readonly string[];
571
+ readonly state: {
572
+ readonly kind: "command";
573
+ };
574
+ }, {
575
+ readonly id: "editingMode";
576
+ readonly shape: "dropdown";
577
+ readonly labelKey: "editingMode.label";
578
+ readonly valueKey: "editingMode.editing";
579
+ readonly paths: readonly string[];
580
+ readonly state: {
581
+ readonly kind: "command";
582
+ };
583
+ }];
584
+ }, {
585
+ readonly id: "contentControl";
586
+ readonly labelKey: "contentControl.group";
587
+ readonly contextual: true;
588
+ readonly controls: readonly [{
589
+ readonly id: "showAll";
590
+ readonly labelKey: "contentControl.showAll";
591
+ readonly paths: readonly string[];
592
+ readonly state: {
593
+ readonly kind: "command";
594
+ };
595
+ }, {
596
+ readonly id: "formFill";
597
+ readonly labelKey: "contentControl.formFill";
598
+ readonly paths: readonly string[];
599
+ readonly state: {
600
+ readonly kind: "command";
601
+ };
602
+ }, {
603
+ readonly id: "inspector";
604
+ readonly labelKey: "contentControl.inspector";
605
+ readonly paths: readonly string[];
606
+ readonly state: {
607
+ readonly kind: "command";
608
+ };
609
+ }, {
610
+ readonly id: "remove";
611
+ readonly labelKey: "contentControl.remove";
612
+ readonly paths: readonly string[];
613
+ readonly state: {
614
+ readonly kind: "command";
615
+ };
616
+ }];
617
+ }, {
618
+ readonly id: "image";
619
+ readonly labelKey: "formattingBar.groups.image";
620
+ readonly contextual: true;
621
+ readonly controls: readonly [{
622
+ readonly id: "insert";
623
+ readonly labelKey: "toolbar.image";
624
+ readonly paths: readonly string[];
625
+ readonly state: {
626
+ readonly kind: "command";
627
+ };
628
+ }, {
629
+ readonly id: "properties";
630
+ readonly labelKey: "formattingBar.imagePropertiesShortcut";
631
+ readonly paths: readonly string[];
632
+ readonly state: {
633
+ readonly kind: "command";
634
+ };
635
+ }, {
636
+ readonly id: "wrap";
637
+ readonly shape: "dropdown";
638
+ readonly labelKey: "formattingBar.imageWrap";
639
+ readonly paths: readonly string[];
640
+ readonly valueKey: "imageWrap.inline";
641
+ readonly state: {
642
+ readonly kind: "value";
643
+ };
644
+ }, {
645
+ readonly id: "altText";
646
+ readonly shape: "dropdown";
647
+ readonly labelKey: "formattingBar.altText";
648
+ readonly paths: null;
649
+ readonly valueKey: "imageProperties.altText";
650
+ readonly state: {
651
+ readonly kind: "value";
652
+ };
653
+ }];
654
+ }, {
655
+ readonly id: "table";
656
+ readonly labelKey: "formattingBar.groups.table";
657
+ readonly contextual: true;
658
+ readonly controls: readonly [{
659
+ readonly id: "insert";
660
+ readonly labelKey: "toolbar.table";
661
+ readonly paths: readonly string[];
662
+ readonly state: {
663
+ readonly kind: "command";
664
+ };
665
+ }, {
666
+ readonly id: "borderTarget";
667
+ readonly shape: "dropdown";
668
+ readonly labelKey: "table.borders.tooltip";
669
+ readonly paths: readonly string[];
670
+ readonly state: {
671
+ readonly kind: "value";
672
+ };
673
+ }, {
674
+ readonly id: "borderColor";
675
+ readonly shape: "colorSplit";
676
+ readonly swatch: "#000000";
677
+ readonly labelKey: "table.borderColor";
678
+ readonly paths: readonly string[];
679
+ readonly state: {
680
+ readonly kind: "value";
681
+ };
682
+ }, {
683
+ readonly id: "borderStyle";
684
+ readonly shape: "dropdown";
685
+ readonly labelKey: "table.borders.styleAriaLabel";
686
+ readonly paths: readonly string[];
687
+ readonly state: {
688
+ readonly kind: "value";
689
+ };
690
+ }, {
691
+ readonly id: "borderWidth";
692
+ readonly shape: "dropdown";
693
+ readonly labelKey: "table.borderWidth";
694
+ readonly paths: readonly string[];
695
+ readonly state: {
696
+ readonly kind: "value";
697
+ };
698
+ }, {
699
+ readonly id: "cellFill";
700
+ readonly shape: "colorSplit";
701
+ readonly swatch: "#ffffff";
702
+ readonly labelKey: "table.cellFillColor";
703
+ readonly paths: readonly string[];
704
+ readonly state: {
705
+ readonly kind: "value";
706
+ };
707
+ }];
708
+ }, {
709
+ readonly id: "file";
710
+ readonly labelKey: "toolbar.file";
711
+ readonly contextual: true;
712
+ readonly controls: readonly [{
713
+ readonly id: "open";
714
+ readonly labelKey: "toolbar.open";
715
+ readonly paths: readonly string[];
716
+ readonly state: {
717
+ readonly kind: "load";
718
+ };
719
+ }, {
720
+ readonly id: "save";
721
+ readonly labelKey: "toolbar.saveShortcut";
722
+ readonly paths: readonly string[];
723
+ readonly state: {
724
+ readonly kind: "save";
725
+ };
726
+ }, {
727
+ readonly id: "pageSetup";
728
+ readonly labelKey: "toolbar.pageSetup";
729
+ readonly paths: readonly string[];
730
+ readonly state: {
731
+ readonly kind: "command";
732
+ };
733
+ }];
734
+ }, {
735
+ readonly id: "insert";
736
+ readonly labelKey: "toolbar.insert";
737
+ readonly contextual: true;
738
+ readonly controls: readonly [{
739
+ readonly id: "footnote";
740
+ readonly labelKey: "toolbar.insertFootnote";
741
+ readonly paths: readonly string[];
742
+ readonly state: {
743
+ readonly kind: "command";
744
+ };
745
+ }, {
746
+ readonly id: "endnote";
747
+ readonly labelKey: "toolbar.insertEndnote";
748
+ readonly paths: readonly string[];
749
+ readonly state: {
750
+ readonly kind: "command";
751
+ };
752
+ }, {
753
+ readonly id: "pageNumber";
754
+ readonly labelKey: "headerFooter.insertPageNumber";
755
+ readonly paths: readonly string[];
756
+ readonly state: {
757
+ readonly kind: "command";
758
+ };
759
+ }, {
760
+ readonly id: "totalPages";
761
+ readonly labelKey: "headerFooter.insertTotalPages";
762
+ readonly paths: readonly string[];
763
+ readonly state: {
764
+ readonly kind: "command";
765
+ };
766
+ }, {
767
+ readonly id: "sectionPages";
768
+ readonly labelKey: "headerFooter.insertSectionPages";
769
+ readonly paths: readonly string[];
770
+ readonly state: {
771
+ readonly kind: "command";
772
+ };
773
+ }, {
774
+ readonly id: "pageXofY";
775
+ readonly labelKey: "headerFooter.insertPageXofY";
776
+ readonly paths: readonly string[];
777
+ readonly state: {
778
+ readonly kind: "command";
779
+ };
780
+ }, {
781
+ readonly id: "pageBreak";
782
+ readonly labelKey: "toolbar.pageBreak";
783
+ readonly paths: readonly string[];
784
+ readonly state: {
785
+ readonly kind: "command";
786
+ };
787
+ }, {
788
+ readonly id: "sectionBreakNextPage";
789
+ readonly labelKey: "toolbar.sectionBreakNextPage";
790
+ readonly paths: readonly string[];
791
+ readonly state: {
792
+ readonly kind: "command";
793
+ };
794
+ }, {
795
+ readonly id: "sectionBreakContinuous";
796
+ readonly labelKey: "toolbar.sectionBreakContinuous";
797
+ readonly paths: readonly string[];
798
+ readonly state: {
799
+ readonly kind: "command";
800
+ };
801
+ }, {
802
+ readonly id: "toc";
803
+ readonly labelKey: "toolbar.tableOfContents";
804
+ readonly paths: readonly string[];
805
+ readonly state: {
806
+ readonly kind: "command";
807
+ };
808
+ }];
809
+ }];
810
+ /**
811
+ * Every group id in the chrome, as a literal union. Stable public API; renaming a group
812
+ * id is a breaking change.
813
+ *
814
+ * @public
815
+ */
816
+ type ChromeGroupId = 'history' | 'zoom' | 'styles' | 'font' | 'text' | 'script' | 'alignment' | 'list' | 'format' | 'review' | 'contentControl' | 'image' | 'table' | 'file' | 'insert';
817
+ /**
818
+ * The public slot vocabulary: `${groupId}.${controlId}` for every control that actually
819
+ * exists — `text.bold`, `font.family`, `alignment.left`. THE stable contract a host
820
+ * composes against and `commandForSlot` resolves; renaming a slot is a breaking change.
821
+ *
822
+ * @public
823
+ */
824
+ type ChromeSlotId = 'history.undo' | 'history.redo' | 'zoom.level' | 'styles.style' | 'font.family' | 'font.size' | 'text.bold' | 'text.italic' | 'text.underline' | 'text.strike' | 'text.color' | 'text.highlight' | 'text.link' | 'script.super' | 'script.sub' | 'alignment.left' | 'alignment.center' | 'alignment.right' | 'alignment.justify' | 'list.bullet' | 'list.numbered' | 'list.outdent' | 'list.indent' | 'list.lineSpacing' | 'format.clear' | 'review.comments' | 'review.editingMode' | 'contentControl.showAll' | 'contentControl.formFill' | 'contentControl.inspector' | 'contentControl.remove' | 'image.insert' | 'image.properties' | 'image.wrap' | 'image.altText' | 'table.insert' | 'table.borderTarget' | 'table.borderColor' | 'table.borderStyle' | 'table.borderWidth' | 'table.cellFill' | 'file.open' | 'file.save' | 'file.pageSetup' | 'insert.footnote' | 'insert.endnote' | 'insert.pageNumber' | 'insert.totalPages' | 'insert.sectionPages' | 'insert.pageXofY' | 'insert.pageBreak' | 'insert.sectionBreakNextPage' | 'insert.sectionBreakContinuous' | 'insert.toc';
825
+ /**
826
+ * Every control id in the chrome, as a literal union. Unique WITHIN a group, not
827
+ * globally (`image.insert` / `table.insert`) — key consumers on {@link ChromeSlotId}.
828
+ *
829
+ * @public
830
+ */
831
+ type ChromeControlId = ChromeSlotId extends `${string}.${infer C}` ? C : never;
832
+ /**
833
+ * The slot id of one control within its group. Only meaningful for entries of
834
+ * `CHROME_GROUPS` — the cast is sound because every group/control pair in the registry
835
+ * is, by construction, a member of the `ChromeSlotId` union.
836
+ *
837
+ * @public
838
+ */
839
+ declare function chromeSlotId(group: {
840
+ readonly id: string;
841
+ }, control: {
842
+ readonly id: string;
843
+ }): ChromeSlotId;
844
+ /**
845
+ * The groups of the DEFAULT toolbar arrangement, in bar order: every group that is
846
+ * not `contextual`. This is the registry's default bar — undo/redo through the
847
+ * editing-mode picker — and what both adapters render when the host composes
848
+ * nothing. Contextual slots (`image.*`, `table.insert`, `file.save`) remain
849
+ * available for explicit composition.
850
+ *
851
+ * @public
852
+ */
853
+ declare function defaultChromeGroups(): readonly ChromeGroup[];
854
+ /**
855
+ * The formatting-bar groups for one editor snapshot: the default bar, plus the
856
+ * contextual `image` group when a drawing is selected. Insertion without a selection is
857
+ * not in the packaged chrome at all — a host that wants it places `image.insert` itself,
858
+ * through `DocxEditor.Toolbar.ImageInsert` or `DocxEditor.Menu.ImageInsert`.
859
+ *
860
+ * @public
861
+ */
862
+ declare function formattingBarChromeGroups(image: ImageContext | null): readonly ChromeGroup[];
863
+ /**
864
+ * A row that runs one chrome slot.
865
+ *
866
+ * @public
867
+ */
868
+ interface ChromeMenuItemEntry {
869
+ readonly kind: 'item';
870
+ readonly slot: ChromeSlotId;
871
+ /**
872
+ * Plain-label override for this row.
873
+ *
874
+ * A slot's own `labelKey` is a TOOLTIP key, and several of them fold the shortcut into
875
+ * the text (`formattingBar.boldShortcut` is "Bold (Ctrl+B)"). A menu puts the shortcut
876
+ * in its own right-hand column, so the row needs the bare noun. Both keys already exist
877
+ * in the catalogue — this points at the plain one rather than minting a duplicate.
878
+ */
879
+ readonly labelKey?: string;
880
+ /** i18n key of the shortcut shown right-aligned on the row (`toolbar.saveShortcut`). */
881
+ readonly shortcutKey?: string;
882
+ /**
883
+ * The row opens a size PICKER instead of firing on click — Word's insert-table grid.
884
+ * The slot still owns the label, the icon and the enabled state; only the dispatch
885
+ * differs, and the picked size is what the host sends.
886
+ */
887
+ readonly picker?: 'tableGrid';
888
+ }
889
+ /**
890
+ * A row that opens a nested panel of rows (Insert › Break).
891
+ *
892
+ * It carries its own label and icon rather than a slot, because a submenu PARENT has no
893
+ * command: clicking it opens the panel. Giving it a slot would mint a public id for a
894
+ * control that can never be enabled, and `toolbarCommandState` would have to invent an
895
+ * answer about it.
896
+ *
897
+ * @public
898
+ */
899
+ interface ChromeMenuSubmenuEntry {
900
+ readonly kind: 'submenu';
901
+ readonly labelKey: string;
902
+ readonly paths: readonly string[] | null;
903
+ readonly items: readonly ChromeMenuEntry[];
904
+ }
905
+ /** A horizontal rule between groups of rows. @public */
906
+ interface ChromeMenuSeparatorEntry {
907
+ readonly kind: 'separator';
908
+ }
909
+ /** One row of a chrome menu. @public */
910
+ type ChromeMenuEntry = ChromeMenuItemEntry | ChromeMenuSubmenuEntry | ChromeMenuSeparatorEntry;
911
+ /**
912
+ * Every menu id, as a literal union. Stable public API; renaming one is a breaking change,
913
+ * exactly like a group or slot id.
914
+ *
915
+ * @public
916
+ */
917
+ type ChromeMenuId = 'file' | 'format' | 'insert' | 'help';
918
+ /** One menu of the menu bar. @public */
919
+ interface ChromeMenu {
920
+ readonly id: ChromeMenuId;
921
+ readonly labelKey: string;
922
+ readonly entries: readonly ChromeMenuEntry[];
923
+ }
924
+ /**
925
+ * The menu bar the chrome shows above the toolbar, in bar order: File, Format, Insert,
926
+ * Help.
927
+ *
928
+ * @public
929
+ */
930
+ declare const CHROME_MENUS: readonly ChromeMenu[];
931
+ /**
932
+ * Every slot a menu places, in menu order, submenus flattened. What a parity test asserts
933
+ * against, and what a host enumerates to know which capabilities the menu bar reaches.
934
+ *
935
+ * @public
936
+ */
937
+ declare function chromeMenuSlots(): readonly ChromeSlotId[];
938
+ /** Total controls, so a parity test can assert none were dropped. */
939
+ declare function chromeControlCount(): number;
940
+ /**
941
+ * i18n key for the tooltip on a control an ADAPTER renders but cannot drive yet — a
942
+ * value slot in a toolbar that has grown no picker for it, say. It is never the reason
943
+ * a control is disabled: when the ENGINE refuses, the tooltip is the engine's own
944
+ * `disabledReason`, never an adapter paraphrase.
945
+ */
946
+ declare const CHROME_UNAVAILABLE_KEY = "formattingBar.unavailableInPreview";
947
+
948
+ type TableCommandSelectionPolicy = {
949
+ readonly kind: 'preserveSelection';
950
+ } | {
951
+ readonly kind: 'adoptCommittedCaret';
952
+ };
953
+ type TableCommandPlan = {
954
+ readonly ok: true;
955
+ readonly ops: readonly TreeDocOp[];
956
+ readonly selection: TableCommandSelectionPolicy;
957
+ } | {
958
+ readonly ok: false;
959
+ readonly code: 'unsupported' | 'invalidArgs' | 'locked';
960
+ readonly reason: string;
961
+ };
962
+
963
+ type NotePropertiesSlice = {
964
+ readonly resolved: ResolvedFootnoteProperties | ResolvedEndnoteProperties;
965
+ readonly documentAuthored?: AuthoredNoteProperties;
966
+ readonly sectionAuthored?: AuthoredNoteProperties;
967
+ };
968
+ type NotePropertiesStateSnapshot = {
969
+ readonly sectionIndex: number;
970
+ readonly footnote: NotePropertiesSlice;
971
+ readonly endnote: NotePropertiesSlice;
972
+ };
973
+
974
+ /**
975
+ * A hyperlink as the surface reports it: its identity, where it sits, and the SANITIZED
976
+ * target. `href: null` is an inert link — a refused scheme or a dangling relationship — which
977
+ * a UI shows and offers to edit but must never offer to open.
978
+ */
979
+ interface SurfaceHyperlink {
980
+ /** Canonical node id of the `w:hyperlink`. */
981
+ readonly id: string;
982
+ readonly paragraphId: string;
983
+ /** UTF-16 range of the link's display text within its paragraph. */
984
+ readonly start: number;
985
+ readonly end: number;
986
+ readonly text: string;
987
+ readonly kind: 'external' | 'internal' | 'unresolved';
988
+ /** Sanitized projection: an absolute URL, `#anchor`, or null when inert. */
989
+ readonly href: string | null;
990
+ /** The authored target, for an editor to seed its input with. */
991
+ readonly authored: string;
992
+ readonly anchor?: string;
993
+ readonly tooltip?: string;
994
+ }
995
+ /**
996
+ * Reading and writing hyperlinks on the surface.
997
+ *
998
+ * Every href that leaves here has already been through `sanitizeHref` — targets come from files,
999
+ * so `javascript:`, `data:` and `vbscript:` are dropped at the parse boundary rather than trusted
1000
+ * to be filtered by whoever renders them.
1001
+ */
1002
+ interface HyperlinkOps {
1003
+ /** Every link in the paragraph the caret is in. */
1004
+ linksInCaretParagraph(): SurfaceHyperlink[];
1005
+ /** The link the caret sits in, or null. */
1006
+ linkAtCaret(): SurfaceHyperlink | null;
1007
+ /** The link with this node id, or null. */
1008
+ linkById(linkId: string): SurfaceHyperlink | null;
1009
+ /**
1010
+ * Apply a link to the selection, or retarget the one the caret is already in.
1011
+ *
1012
+ * `text` replaces the display text when supplied. Returns whether anything committed;
1013
+ * a refusal leaves the document exactly as it was.
1014
+ */
1015
+ applyHyperlink(input: {
1016
+ readonly url?: string;
1017
+ readonly anchor?: string;
1018
+ readonly text?: string;
1019
+ readonly tooltip?: string;
1020
+ }): boolean;
1021
+ /** Take the link off the one at the caret (or a named one). Returns whether it committed. */
1022
+ removeHyperlink(linkId?: string): boolean;
1023
+ }
1024
+
1025
+ /** A click on a painted link, after native navigation was refused. */
1026
+ interface HyperlinkActivation {
1027
+ readonly link: SurfaceHyperlink;
1028
+ /** The clicked line fragment's viewport rect, so a popover can be placed under it. */
1029
+ readonly rect: {
1030
+ readonly left: number;
1031
+ readonly top: number;
1032
+ readonly bottom: number;
1033
+ readonly right: number;
1034
+ };
1035
+ }
1036
+ /**
1037
+ * Moving the caret and the viewport: to a position, to a bookmark, or out to an external target.
1038
+ *
1039
+ * {@link SurfaceNavigation.openExternal} is THE external-activation call site, and it refuses
1040
+ * anything but an already-sanitized href. Routing an authored target through some other path is
1041
+ * how a document gets to choose where a click goes.
1042
+ */
1043
+ interface SurfaceNavigation {
1044
+ /** Snap to a semantic position using layout geometry, then place the caret there. */
1045
+ goToPosition(position: {
1046
+ paragraphId: string;
1047
+ offset: number;
1048
+ }): boolean;
1049
+ /**
1050
+ * Scroll a bookmark into view and place the caret at it. Answers false for a name no
1051
+ * bookmark declares — an inert click, which is what Word does with a dangling anchor.
1052
+ */
1053
+ goToBookmark(name: string): boolean;
1054
+ /**
1055
+ * THE external-activation call site. Refuses anything but a sanitized projection, so a
1056
+ * caller cannot route an authored target through it by mistake.
1057
+ */
1058
+ openExternal(href: string | null): boolean;
1059
+ destroy(): void;
1060
+ }
1061
+
1062
+ /**
1063
+ * Everything {@link createDocxEditor} accepts. Every field is optional.
1064
+ *
1065
+ * `container` is the one that changes the shape of the whole lifecycle: omitting it produces an
1066
+ * instance that does no DOM work until `attach(el)`, which is what lets a provider own the editor
1067
+ * before any component has rendered a mount point.
1068
+ *
1069
+ * @public
1070
+ */
1071
+ interface DocxEditorConfig {
1072
+ /**
1073
+ * The element the paginated surface mounts into. The surface owns this subtree.
1074
+ *
1075
+ * Optional: an instance created WITHOUT a container stashes its document bytes and does
1076
+ * no DOM work until `attach(el)` — the provider-first shape, where the editor exists
1077
+ * before any component has rendered a mount point. With a container, the document mounts
1078
+ * immediately at construction, exactly as before.
1079
+ */
1080
+ container?: HTMLElement;
1081
+ /**
1082
+ * A document to load at construction. Bytes only in practice: a `DocumentHandle` cannot
1083
+ * be re-opened (the handle is identity, not content), so passing one emits a typed
1084
+ * `error` event rather than silently loading nothing.
1085
+ */
1086
+ document?: DocumentSource;
1087
+ /**
1088
+ * Font bytes for Word-accurate (HarfBuzz-shaped) line wrap and pagination. Omitted,
1089
+ * layout falls back to a fixed-width estimate; fonts embedded in the document are
1090
+ * wired in automatically either way. For Word's default faces (Calibri, Times New
1091
+ * Roman, …) pass `await loadDefaultFonts()` from `@docx-editor.dev/fonts` — a bare
1092
+ * fragment (`{ sources, substitutions }`) is accepted and composed with defaults, or
1093
+ * merge several origins yourself with `composeFontConfiguration`. Sampled per load;
1094
+ * failures degrade to the fixed measurer and report through `onFontError`.
1095
+ *
1096
+ * Pass a {@link FontResolver} instead to resolve ON DEMAND: the function is called once
1097
+ * per load, after the document is parsed, with the families it actually declares, and
1098
+ * only what it returns is loaded. A document naming nothing the resolver covers costs
1099
+ * nothing. Note that a fetching resolver makes opening a document perform network
1100
+ * requests — the engine never supplies one, so that stays your call.
1101
+ */
1102
+ fonts?: FontConfiguration | FontConfigurationFragment | FontResolver;
1103
+ author?: string;
1104
+ locale?: string;
1105
+ /** Localized drawing refusal labels; defaults to English when omitted. */
1106
+ translate?: (key: string, params?: Record<string, string | number>) => string;
1107
+ /**
1108
+ * Capability modules to register — the seam `@docx-editor.dev/pro` plugs in
1109
+ * through. Omitted, the editor runs the free tier: lossless round-trip,
1110
+ * final-state revision rendering, review chrome disabled with the engine's
1111
+ * reason. See {@link EditorModule}.
1112
+ */
1113
+ modules?: readonly EditorModule[];
1114
+ /** `'view'` refuses every mutating command through the facade; default `'edit'`. */
1115
+ mode?: 'edit' | 'view';
1116
+ /** Override raster decode for insert/replace image commands; defaults to browser/headless. */
1117
+ imageDecodePort?: ImageDecodePort;
1118
+ zoom?: number;
1119
+ onFontError?: (error: EditorFontError) => void;
1120
+ /** Localized labels for table insertion furniture on the painted surface. */
1121
+ tableInteractionLabel?: (key: 'table.insertRowBelow' | 'table.insertColumnRight') => string;
1122
+ }
1123
+ /**
1124
+ * Which measurer the current document's layout runs on, and whether shaped resolution is
1125
+ * still in flight. Returned by {@link DocxEditorInstance.fontMeasurement}.
1126
+ */
1127
+ interface FontMeasurementState {
1128
+ /** `fixed` estimates advance widths; `shaped` measures real font bytes with HarfBuzz. */
1129
+ readonly measurer: 'fixed' | 'shaped';
1130
+ /** True while font resolution for the current document is still running. */
1131
+ readonly resolving: boolean;
1132
+ /** The shaped measurer's identity (admitted face hashes); absent while fixed. */
1133
+ readonly producer?: string;
1134
+ }
1135
+ /**
1136
+ * The host chrome that answers the engine's hyperlink gestures.
1137
+ *
1138
+ * A CLICK on an external link and Ctrl/Cmd+K both mean "the user wants the link UI", and the
1139
+ * engine deliberately does not know what that looks like. Registered rather than passed at
1140
+ * construction because the chrome mounts after the editor does, and it survives a document
1141
+ * reload — the surface is rebuilt, the handlers are not.
1142
+ */
1143
+ interface HyperlinkChromeHandlers {
1144
+ /** A plain click on an external or inert link: show the popover at `activation.rect`. */
1145
+ readonly onPopover?: (activation: HyperlinkActivation) => void;
1146
+ /** Ctrl/Cmd+K: open insert-or-edit for the selection. */
1147
+ readonly onRequest?: () => void;
1148
+ }
1149
+ /**
1150
+ * The concrete facade type: the full `Editor` contract plus the instance-only surface.
1151
+ *
1152
+ * `surface`, `stateVersion`, `attach` and `detach` live HERE rather than on `Editor`:
1153
+ * they are what a store binding and a mounting host need, not what document commands
1154
+ * need. Production adapters program against `Editor` for everything else.
1155
+ */
1156
+ interface DocxEditorInstance extends Editor {
1157
+ /** Bumps on mount, detach, destroy, and document reload — guards async image intents. */
1158
+ readonly mountGeneration: number;
1159
+ /**
1160
+ * The underlying paginated surface for harnesses and tests that need capabilities the
1161
+ * contract does not carry yet (select-all, node-id addressed selection).
1162
+ */
1163
+ readonly surface: PaginatedSurface | null;
1164
+ /**
1165
+ * Wire the host's hyperlink chrome to the engine's gestures — a click on an external
1166
+ * link, and Ctrl/Cmd+K. Returns an unsubscribe that restores whatever was registered
1167
+ * before, so a popover component can register in an effect and clean up in its teardown.
1168
+ *
1169
+ * Instance-only, like `surface`: it is what a MOUNTING host needs, not what a document
1170
+ * command needs.
1171
+ */
1172
+ setHyperlinkChrome(handlers: HyperlinkChromeHandlers): Unsubscribe;
1173
+ /**
1174
+ * Monotonic version of the observable editor state. Bumps whenever anything
1175
+ * `snapshot()` reports could have moved — a committed change, a selection move, zoom,
1176
+ * load success or failure, attach/detach, destroy. An external store (React's
1177
+ * `useSyncExternalStore`) uses it as a cheap "did anything change" signal; `snapshot()`
1178
+ * itself is cached per version and returns a stable reference between bumps.
1179
+ */
1180
+ stateVersion(): number;
1181
+ /**
1182
+ * Which measurer the current document's layout runs on, and whether shaped
1183
+ * resolution is still in flight — the honest "are wrap points Word-accurate yet?"
1184
+ * readout a host shows instead of guessing. `fixed` with `resolving: false` is the
1185
+ * steady state for a document with no usable font source (the documented zero-config
1186
+ * fallback); `shaped` means HarfBuzz measurement over real font bytes. Changes bump
1187
+ * `stateVersion()`.
1188
+ */
1189
+ fontMeasurement(): FontMeasurementState;
1190
+ /**
1191
+ * Mount into `el`. If the instance holds pending document bytes (created without a
1192
+ * container, or previously detached), they mount now — under the shaped measurer when
1193
+ * fonts have resolved in the meantime. Attaching while already mounted elsewhere moves
1194
+ * the live content via `session.save()`.
1195
+ *
1196
+ * HONEST COSTS: a mount from bytes is a fresh session — the undo stack and the caret do
1197
+ * not survive re-attach (the same cost as the async font remount). After `destroy()`
1198
+ * this is a no-op that emits a typed `error` event: a destroyed instance never remounts.
1199
+ */
1200
+ attach(el: HTMLElement): void;
1201
+ /**
1202
+ * Tear down the painted surface, stashing the CURRENT document bytes
1203
+ * (`session.save()`) so a later `attach` restores the content — but not the undo stack
1204
+ * or the caret. No-op when already detached or destroyed.
1205
+ */
1206
+ detach(): void;
1207
+ }
1208
+
1209
+ type ImageMutationPreconditions = Readonly<{
1210
+ mountGeneration: number;
1211
+ packageRevision: number;
1212
+ drawingNodeId: string | null;
1213
+ selectionParagraphId: string;
1214
+ selectionOffset: number;
1215
+ }>;
1216
+ /**
1217
+ * The selected image and what may be done to it, or null when nothing image-like is selected.
1218
+ *
1219
+ * Null covers more than "no selection": a placeholder graphic, a drawing the file marks hidden,
1220
+ * and one whose `select` lock is set all read as no selection, because chrome that offered
1221
+ * resize handles on them would promise an edit the store is about to refuse.
1222
+ */
1223
+ declare function selectedImageStateOf(surface: PaginatedSurface | null): SelectedImageState | null;
1224
+ /**
1225
+ * Snapshot the state an image mutation was planned against: mount generation, package revision,
1226
+ * and the selection anchor.
1227
+ *
1228
+ * Taken at the START of a drag so the commit can be checked against it with
1229
+ * {@link isStaleImageInteractionCommit}. A pointer gesture spans many frames, and a document that
1230
+ * moved underneath it must not have the gesture's final coordinates applied to it.
1231
+ */
1232
+ declare function captureImageMutationPreconditions(editor: Pick<DocxEditorInstance, 'surface' | 'mountGeneration'>): ImageMutationPreconditions | null;
1233
+ /**
1234
+ * Whether an insert or replace would be accepted — the `can` half of the can-before-exec pair.
1235
+ *
1236
+ * Refuses in suggesting mode, since an image property edit has no tracked-change representation.
1237
+ */
1238
+ declare function canExecuteImageCommand(command: Extract<EditorCommand, {
1239
+ type: 'insertImage' | 'replaceImage';
1240
+ }>, surface: PaginatedSurface | null): CanResult;
1241
+ /**
1242
+ * Run an insert or replace, re-checking the same gates {@link canExecuteImageCommand} applies.
1243
+ *
1244
+ * Async because image bytes must be decoded to derive their natural extent before the drawing can
1245
+ * be projected — the one editor command that cannot complete synchronously.
1246
+ */
1247
+ declare function executeImageCommand(editor: DocxEditorInstance, command: Extract<EditorCommand, {
1248
+ type: 'insertImage' | 'replaceImage';
1249
+ }>): Promise<ExecResult>;
1250
+ /** Which of the eight resize handles a drag started from, by compass direction. */
1251
+ type ImageResizeHandle = 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw';
1252
+ /** How far one arrow-key press nudges a selected image, in points. */
1253
+ declare const IMAGE_OVERLAY_NUDGE_PT = 1;
1254
+ /** How far Shift+arrow nudges a selected image, in points. */
1255
+ declare const IMAGE_OVERLAY_NUDGE_SHIFT_PT = 10;
1256
+ /** EMUs per point. DrawingML stores extents in EMU; layout works in points. */
1257
+ declare const EMU_PER_POINT = 12700;
1258
+ /**
1259
+ * One in-flight image drag: where it started, and everything needed to decide at commit time
1260
+ * whether the document still describes what the gesture was planned against.
1261
+ *
1262
+ * Both revisions are captured because they move independently — layout can re-flow without the
1263
+ * package changing, and vice versa.
1264
+ */
1265
+ interface ImageInteractionSession {
1266
+ readonly drawingNodeId: string;
1267
+ readonly startBounds: {
1268
+ readonly x: number;
1269
+ readonly y: number;
1270
+ readonly width: number;
1271
+ readonly height: number;
1272
+ };
1273
+ readonly startWidthEmu: number;
1274
+ readonly startHeightEmu: number;
1275
+ readonly startPosition: DrawingPositionInput | null;
1276
+ readonly anchorFrameOrigin: {
1277
+ readonly x: number;
1278
+ readonly y: number;
1279
+ } | null;
1280
+ readonly transform: DrawingTransform;
1281
+ readonly mode: 'move' | 'resize';
1282
+ readonly handle: ImageResizeHandle | null;
1283
+ readonly preconditions: ImageMutationPreconditions;
1284
+ readonly layoutRevision: number;
1285
+ readonly packageRevision: number;
1286
+ readonly kind: 'inline' | 'anchored';
1287
+ }
1288
+ /**
1289
+ * How an image drag scrolls the page when it reaches the viewport edge.
1290
+ *
1291
+ * Returns the delta ACTUALLY applied rather than the one requested, because a drag at the end of
1292
+ * the document cannot scroll further and the overlay must not move the image by a distance the
1293
+ * page did not travel.
1294
+ */
1295
+ interface ImageOverlayScrollPort {
1296
+ /** Scroll by a preview delta and return the actual applied document-space delta in points. */
1297
+ scrollBy(deltaY: number): number;
1298
+ }
1299
+ /**
1300
+ * The painted geometry of the selected drawing, plus what the overlay is allowed to do to it.
1301
+ *
1302
+ * Carries BOTH the painted rect (points, for hit-testing and handle placement) and the stored
1303
+ * extent (EMU, for writing back), so the overlay never has to convert between the two spaces to
1304
+ * decide what it is looking at.
1305
+ */
1306
+ interface SelectedDrawingOverlayTarget {
1307
+ readonly id: string;
1308
+ readonly pageIndex: number;
1309
+ readonly x: number;
1310
+ readonly y: number;
1311
+ readonly width: number;
1312
+ readonly height: number;
1313
+ readonly kind: 'inline' | 'anchored';
1314
+ readonly widthEmu: number;
1315
+ readonly heightEmu: number;
1316
+ readonly position: DrawingPositionInput | null;
1317
+ readonly anchorFrameOrigin: {
1318
+ readonly x: number;
1319
+ readonly y: number;
1320
+ } | null;
1321
+ readonly transform: DrawingTransform;
1322
+ readonly canResize: boolean;
1323
+ readonly canMove: boolean;
1324
+ /** Hard lock from `noChangeAspect` — never overridden by Shift. */
1325
+ readonly aspectLocked: boolean;
1326
+ }
1327
+ /**
1328
+ * The painted geometry of the selected drawing, for the resize/move overlay — or null.
1329
+ *
1330
+ * Stricter than {@link selectedImageStateOf}: it also requires a COLLAPSED selection, because a
1331
+ * range that merely contains a drawing is a text selection, and drawing handles over it would
1332
+ * claim an object the user did not single out.
1333
+ */
1334
+ declare function selectedDrawingOverlayTargetOf(surface: PaginatedSurface | null): SelectedDrawingOverlayTarget | null;
1335
+ /** Points to EMU, rounded — EMUs are integral in the file. */
1336
+ declare function pointsToEmu(points: number): number;
1337
+ /** EMU to points. Unrounded, so overlay geometry keeps sub-point precision during a drag. */
1338
+ declare function emuToOverlayPoints(emu: number): number;
1339
+ /**
1340
+ * The extent a resize drag produces, in EMU.
1341
+ *
1342
+ * Computed from the drag's START extent rather than the previous frame's, so a drag that reverses
1343
+ * direction lands exactly where it began instead of accumulating rounding error.
1344
+ *
1345
+ * `preserveAspect` behaves the way Word's handles do: a corner handle scales by whichever axis
1346
+ * moved further, while an edge handle drives the other axis from the original ratio. Both axes
1347
+ * are floored at one point, so a drag past the opposite edge cannot invert the image.
1348
+ */
1349
+ declare function computeResizedImageExtentEmu(startWidthEmu: number, startHeightEmu: number, handle: ImageResizeHandle, deltaWidthPt: number, deltaHeightPt: number, preserveAspect: boolean): {
1350
+ readonly cx: number;
1351
+ readonly cy: number;
1352
+ };
1353
+ /**
1354
+ * The position a move drag produces, preserving the anchoring the file already used.
1355
+ *
1356
+ * A `frame`-mode position keeps its `relativeToH`/`relativeToV` bases and only shifts the offsets
1357
+ * it actually had — writing an offset the file omitted would re-anchor the drawing to a different
1358
+ * reference and move it somewhere the drag never pointed.
1359
+ */
1360
+ declare function computeMovedImagePosition(start: DrawingPositionInput, deltaXPt: number, deltaYPt: number): DrawingPositionInput;
1361
+ /**
1362
+ * Whether a drag's commit should be refused because the document moved under it — the refusal to
1363
+ * return, or null when the commit is still valid.
1364
+ *
1365
+ * Checks the mount generation and both revisions captured by
1366
+ * {@link captureImageMutationPreconditions}. A gesture spans many frames, so this is the one
1367
+ * place that decides its coordinates still describe the document they were measured against.
1368
+ */
1369
+ declare function isStaleImageInteractionCommit(editor: Pick<DocxEditorInstance, 'surface' | 'mountGeneration'>, session: ImageInteractionSession): ExecResult | null;
1370
+
1371
+ /** Points → CSS pixels at the given zoom (matches `mountPaginatedSurface` / semantic paint). */
1372
+ declare function surfacePaintScale(zoom: number): number;
1373
+ /** Layout points to CSS pixels. Pair with {@link surfacePaintScale} for the current zoom. */
1374
+ declare function layoutPointsToCssPixels(points: number, paintScale: number): number;
1375
+ /** CSS pixels back to layout points — what a pointer event's coordinates must go through. */
1376
+ declare function cssPixelsToLayoutPoints(pixels: number, paintScale: number): number;
1377
+ /**
1378
+ * What an overlay needs to place itself over the painted pages: the zoom scale, and where each
1379
+ * page sits horizontally.
1380
+ *
1381
+ * Per-page X offsets rather than one origin, because pages are centred independently and a
1382
+ * narrower page in a mixed-size document does not start where its neighbours do.
1383
+ */
1384
+ interface SurfaceOverlayCoordinates {
1385
+ readonly paintScale: number;
1386
+ readonly pageOffsetX: ReadonlyMap<number, number>;
1387
+ }
1388
+ /** A rectangle in one page's content frame, in layout points. */
1389
+ interface OverlayFrameRect {
1390
+ readonly pageIndex: number;
1391
+ readonly x: number;
1392
+ readonly y: number;
1393
+ readonly width: number;
1394
+ readonly height: number;
1395
+ }
1396
+ /** Page-content frame → sheet-space CSS pixels (matches `paintSelectionOverlay`). */
1397
+ declare function overlayFrameToSheetCssPixels(layout: SemanticLayout, frame: OverlayFrameRect, coordinates: SurfaceOverlayCoordinates): {
1398
+ readonly left: number;
1399
+ readonly top: number;
1400
+ readonly width: number;
1401
+ readonly height: number;
1402
+ };
1403
+ /** Whether this handle drag should preserve aspect ratio. */
1404
+ declare function resizePreservesAspect(handle: ImageResizeHandle, aspectLocked: boolean, shiftKey: boolean): boolean;
1405
+ /**
1406
+ * Where an anchored drawing's positioning frame begins, in layout points.
1407
+ *
1408
+ * An anchored drawing's offsets are relative to a base the file names (page, margin, column, …),
1409
+ * so a move drag needs that base's origin to turn a pointer delta into a stored offset.
1410
+ */
1411
+ interface AnchorFrameOrigin {
1412
+ readonly x: number;
1413
+ readonly y: number;
1414
+ }
1415
+ /**
1416
+ * One resize frame: the extent to store, and the box to draw while the pointer is still down.
1417
+ *
1418
+ * Both, because they are different spaces — the extent is EMU for the file, the preview is
1419
+ * points for the overlay — and computing them separately would let the handle drift from the
1420
+ * rectangle it is dragging.
1421
+ */
1422
+ interface ImageResizeResult {
1423
+ readonly widthEmu: number;
1424
+ readonly heightEmu: number;
1425
+ readonly previewBounds: {
1426
+ readonly x: number;
1427
+ readonly y: number;
1428
+ readonly width: number;
1429
+ readonly height: number;
1430
+ };
1431
+ readonly position: DrawingPositionInput | null;
1432
+ }
1433
+ /**
1434
+ * Resolve one resize frame from the pointer's current position.
1435
+ *
1436
+ * Handles rotation and flips by mapping the SCREEN-space handle back to the drawing's local axes
1437
+ * first: dragging the visually-right handle of a 90°-rotated image must change its stored height,
1438
+ * and a flipped image's handles move in the opposite direction from where they appear.
1439
+ */
1440
+ declare function computeImageResizeResult(options: {
1441
+ readonly handle: ImageResizeHandle;
1442
+ readonly startWidthEmu: number;
1443
+ readonly startHeightEmu: number;
1444
+ readonly startBounds: {
1445
+ readonly x: number;
1446
+ readonly y: number;
1447
+ readonly width: number;
1448
+ readonly height: number;
1449
+ };
1450
+ readonly startPosition: DrawingPositionInput | null;
1451
+ readonly anchorFrameOrigin?: AnchorFrameOrigin | null;
1452
+ readonly deltaXPt: number;
1453
+ readonly deltaYPt: number;
1454
+ readonly transform: DrawingTransform;
1455
+ readonly preserveAspect: boolean;
1456
+ readonly kind: 'inline' | 'anchored';
1457
+ }): ImageResizeResult;
1458
+ /**
1459
+ * An {@link ImageOverlayScrollPort} over a real scroll container.
1460
+ *
1461
+ * Reports the delta the element ACTUALLY scrolled, converted back to points — at the end of the
1462
+ * document that is less than asked for, and the overlay must not move the image further than the
1463
+ * page travelled.
1464
+ */
1465
+ declare function createImageOverlayScrollPort(scroller: HTMLElement, paintScale: number): {
1466
+ scrollBy(deltaYPoints: number): number;
1467
+ };
1468
+ /**
1469
+ * The committed result of a whole drag, recomputed from the release coordinates.
1470
+ *
1471
+ * Recomputed rather than accumulated from the per-frame previews, so rounding applied once per
1472
+ * frame cannot add up into a final extent that differs from where the pointer actually stopped.
1473
+ */
1474
+ interface FinalizedImageOverlayInteraction extends ImageResizeResult {
1475
+ readonly position: DrawingPositionInput | null;
1476
+ }
1477
+ /** Recompute the committed overlay result from release pointer coordinates. */
1478
+ declare function finalizeImageOverlayInteraction(options: {
1479
+ readonly session: ImageInteractionSession;
1480
+ readonly deltaXPt: number;
1481
+ readonly deltaYPt: number;
1482
+ readonly accumulatedScrollPt: number;
1483
+ readonly aspectLocked: boolean;
1484
+ readonly shiftKey: boolean;
1485
+ readonly anchorFrameOrigin: AnchorFrameOrigin | null;
1486
+ }): FinalizedImageOverlayInteraction;
1487
+
1488
+ /**
1489
+ * How an edit is written.
1490
+ *
1491
+ * `'suggest'` is the one that changes what the ops MEAN: the same keystroke becomes a `w:ins`
1492
+ * and the same Backspace becomes a `w:del` over the words it would have removed. `'view'`
1493
+ * refuses edits outright.
1494
+ */
1495
+ type SurfaceEditingMode = 'edit' | 'suggest' | 'view';
1496
+ /**
1497
+ * Content-control interaction lane on the paginated surface.
1498
+ *
1499
+ * Chrome toggles are surface state; value / remove commit through tree ops.
1500
+ */
1501
+ interface ContentControlOps {
1502
+ /** Toggle show-all boundary chrome. No layout reflow. */
1503
+ setShowAll(show: boolean): void;
1504
+ /** Toggle form-fill Tab navigation mode. */
1505
+ setFormFill(active: boolean): void;
1506
+ /** Whether show-all chrome is on. */
1507
+ showAll(): boolean;
1508
+ /** Whether form-fill navigation is on. */
1509
+ formFill(): boolean;
1510
+ /** Innermost control at the caret from layout boundary records. */
1511
+ atCaret(): ContentControlBoundaryRecord | null;
1512
+ /**
1513
+ * Move to the next or previous editable control (tabIndex, then document order).
1514
+ *
1515
+ * Skips content-locked and bound controls. Selects the control's content for replacement.
1516
+ * Returns whether navigation landed somewhere.
1517
+ */
1518
+ navigate(direction: 'next' | 'previous'): boolean;
1519
+ /**
1520
+ * Set a control's value through `setContentControlValue`. Honours lock / bound refusals.
1521
+ * Returns whether the op committed.
1522
+ */
1523
+ setValue(controlId: string, value: string): boolean;
1524
+ /**
1525
+ * Unwrap a control keeping its content (`removeContentControl`). Defaults to the control
1526
+ * at the caret. Returns whether the op committed.
1527
+ */
1528
+ remove(controlId?: string): boolean;
1529
+ /**
1530
+ * Engine reason a widget or remove action is disabled, or null when allowed.
1531
+ *
1532
+ * `edit` covers content / value changes; `remove` covers unwrap.
1533
+ */
1534
+ disabledReason(controlId: string, action: 'edit' | 'remove'): string | null;
1535
+ }
1536
+ /**
1537
+ * How a paginated surface opens. Every field is optional.
1538
+ *
1539
+ * `measurer` is the injection seam that keeps layout DOM-free — supply one to lay a document out
1540
+ * on a server, or leave it off in a browser to get the canvas measurer.
1541
+ */
1542
+ interface PaginatedSurfaceOptions {
1543
+ readonly measurer?: TextMeasurer;
1544
+ /** Ambient author for tracked edits. Required before suggesting can write anything. */
1545
+ readonly author?: string;
1546
+ /** Opening mode; changeable at runtime with `setEditingMode`. */
1547
+ readonly editingMode?: SurfaceEditingMode;
1548
+ /**
1549
+ * Identifies the measurer for cache invalidation.
1550
+ *
1551
+ * Fonts resolve asynchronously, so a host that swaps its measurer must change this or the
1552
+ * cached pre-font layout is served for the rest of the session.
1553
+ */
1554
+ readonly producer?: string;
1555
+ /**
1556
+ * Maps a document-declared font family to the alias its registered bytes live under, so
1557
+ * painted runs can use embedded glyphs without the file's family name entering the
1558
+ * page-global CSS font namespace.
1559
+ */
1560
+ readonly fontAlias?: (family: string) => string | undefined;
1561
+ /** Points to CSS pixels. */
1562
+ readonly scale?: number;
1563
+ /**
1564
+ * How revisions project into layout and paint. Omitted keeps the layout default
1565
+ * (`all-markup`). The editor facade passes `proposed` when no review module is
1566
+ * registered — the free tier's final-state rendering; the machinery below this
1567
+ * option is shared either way.
1568
+ */
1569
+ readonly revisionDisplayMode?: RevisionDisplayMode;
1570
+ /**
1571
+ * The review module's derivation hooks for this surface's session. Absent,
1572
+ * `session.reviewItems()` is the typed empty queue and every review affordance
1573
+ * built on it stays inert.
1574
+ */
1575
+ readonly reviewModel?: ReviewModuleContribution;
1576
+ /**
1577
+ * The family a run with no authored font is reported as by `formatting()` AND painted
1578
+ * in — the face the measurer falls back to. Absent, such a run reports
1579
+ * `fontFamily: null` and paints in whatever font the page inherits, which the measurer
1580
+ * did not measure: visible glyphs drift from wrap points and caret geometry.
1581
+ */
1582
+ readonly defaultFontFamily?: string;
1583
+ /**
1584
+ * Who resolves a pointer to a caret.
1585
+ *
1586
+ * `'engine'` (the default) answers from the layout records, which is what makes a click in
1587
+ * a margin, an indent or a cell's padding land where it was aimed. `'native'` binds no
1588
+ * pointer handlers and leaves the browser's own caret placement in charge.
1589
+ */
1590
+ readonly pointer?: 'engine' | 'native';
1591
+ readonly onChange?: (state: PaginatedSurfaceState) => void;
1592
+ /**
1593
+ * A plain click on an external (or inert) hyperlink, for a host to open its popover with.
1594
+ *
1595
+ * Absent means such a click does nothing. That is deliberate: a host with no popover
1596
+ * mounted must not have clicks silently opening tabs, and the popover is the only path to
1597
+ * activation (see the navigation module's single `window.open` gate).
1598
+ */
1599
+ readonly onHyperlinkPopover?: (activation: HyperlinkActivation) => void;
1600
+ /**
1601
+ * Ctrl/Cmd+K — Word's Insert Hyperlink. The engine reports the request; the host's chrome
1602
+ * decides what a link dialog looks like. A host that passes nothing leaves the key alone
1603
+ * rather than doing something surprising with it.
1604
+ */
1605
+ readonly onRequestHyperlink?: () => void;
1606
+ /**
1607
+ * Localized accessible names for core-owned table insertion furniture.
1608
+ * Defaults to English from `@docx-editor.dev/i18n` when omitted.
1609
+ */
1610
+ readonly tableInteractionLabel?: (key: 'table.insertRowBelow' | 'table.insertColumnRight') => string;
1611
+ /** Localized drawing refusal labels; defaults to English when omitted. */
1612
+ readonly drawingStrings?: DrawingPaintStrings;
1613
+ /** Override raster decode for package image intents; defaults to browser/headless. */
1614
+ readonly imageDecodePort?: ImageDecodePort;
1615
+ /**
1616
+ * Localized name for a generated TOC, written as the control's `w:alias` on insert.
1617
+ *
1618
+ * The update ACTIONS are not here: they are rows in the host's context menu, which owns
1619
+ * its own labels. The engine paints no menu of its own.
1620
+ */
1621
+ readonly tocLabels?: {
1622
+ readonly title: string;
1623
+ };
1624
+ }
1625
+ /**
1626
+ * What the selection is currently formatted as.
1627
+ *
1628
+ * A value is present only when EVERY span in the selection agrees on it: a selection running
1629
+ * from 11pt into 14pt has no font size, and a toolbar should show a blank rather than pick
1630
+ * one of the two and imply the whole selection is that.
1631
+ */
1632
+ interface SurfaceFormatting {
1633
+ readonly bold: boolean;
1634
+ readonly italic: boolean;
1635
+ readonly underline: boolean;
1636
+ readonly strikethrough: boolean;
1637
+ readonly superscript: boolean;
1638
+ readonly subscript: boolean;
1639
+ readonly fontFamily: string | null;
1640
+ /** Half-points, the unit OOXML stores and the picker expects. */
1641
+ readonly fontSizeHalfPoints: number | null;
1642
+ readonly color: string | null;
1643
+ readonly highlight: string | null;
1644
+ readonly alignment: 'left' | 'center' | 'right' | 'both' | null;
1645
+ readonly styleId: string | null;
1646
+ /**
1647
+ * `w:spacing`'s line rule and its value: LINES for `multiple`, points for the other two
1648
+ * (`w:line` is 240ths of a line under `auto` and twentieths of a point otherwise).
1649
+ * Null when the selection's paragraphs disagree, or state no line spacing at all.
1650
+ */
1651
+ readonly lineSpacing: {
1652
+ readonly rule: 'multiple' | 'exact' | 'atLeast';
1653
+ readonly value: number;
1654
+ } | null;
1655
+ /** `w:spacing/@w:before` and `@w:after` in points, null when the selection disagrees. */
1656
+ readonly spaceBeforePt: number | null;
1657
+ readonly spaceAfterPt: number | null;
1658
+ /**
1659
+ * Effective indent at the selection, in twips, or null with no selection or inside a
1660
+ * table.
1661
+ *
1662
+ * The one field here that does NOT go null on disagreement: the values are the FIRST
1663
+ * touched paragraph's and `mixed` reports the disagreement per field, because a ruler
1664
+ * must draw its handles somewhere and Word draws them at the first selected paragraph.
1665
+ */
1666
+ readonly indent: IndentFormatting | null;
1667
+ }
1668
+ /**
1669
+ * Where the last pass spent its time, and how much work it actually did.
1670
+ *
1671
+ * The durations are the surface's own three phases — layout, paint, selection sync — timed
1672
+ * separately because they fail separately: a full relayout, a full repaint and a forced
1673
+ * reflow each have a different fix. The counters come free from machinery that already
1674
+ * exists: the layout session says how much was re-placed versus reused, and the scheduler
1675
+ * says how often work was thrown away as stale. `placed` equal to `total` on every
1676
+ * keystroke is the one-glance sign that incremental layout is not engaging.
1677
+ */
1678
+ interface PaginatedSurfacePerf {
1679
+ /** Time the last layout pass took, in milliseconds. */
1680
+ readonly layoutMs: number;
1681
+ /** Time the last paint took — building and swapping the page DOM. */
1682
+ readonly paintMs: number;
1683
+ /** Time the last selection sync took — writing the model selection into the browser. */
1684
+ readonly selectionMs: number;
1685
+ /** Paragraphs the last pass re-placed, against the number in the document. */
1686
+ readonly placed: number;
1687
+ readonly total: number;
1688
+ /** Pages carried over from the previous layout without being rebuilt. */
1689
+ readonly reusedPages: number;
1690
+ /** Passes that could not resume and laid the document out from the top. */
1691
+ readonly fullPasses: number;
1692
+ /** Layouts discarded because the model had already moved on. */
1693
+ readonly staleDiscards: number;
1694
+ /** Cooperative runs abandoned mid-flight for a newer revision. */
1695
+ readonly cancelledRuns: number;
1696
+ }
1697
+ /** How a reveal places its target in the viewport. */
1698
+ interface RevealOptions {
1699
+ /**
1700
+ * `'start'` puts the target near the top (a heading the user jumped to), `'center'`
1701
+ * centres it, `'nearest'` scrolls only when it is out of view. Default `'start'`.
1702
+ */
1703
+ readonly block?: 'start' | 'center' | 'nearest';
1704
+ /** Padding above the target, in CSS pixels. Default 24. */
1705
+ readonly offsetPx?: number;
1706
+ readonly behavior?: ScrollBehavior;
1707
+ }
1708
+ /**
1709
+ * Everything observable about the surface right now, as one immutable value.
1710
+ *
1711
+ * `revision` is the change token: it moves whenever anything else here does, which is what lets
1712
+ * `snapshot()` hand back the same reference until state actually changes.
1713
+ */
1714
+ interface PaginatedSurfaceState {
1715
+ readonly revision: number;
1716
+ readonly pageCount: number;
1717
+ readonly selection: SemanticSelection;
1718
+ /**
1719
+ * The rectangle of table cells a drag across cells selected, or null.
1720
+ *
1721
+ * `selection` always holds the equivalent TEXT range, so a reader that does not care about
1722
+ * rectangles needs no branch. This is for the ones that do — the highlight, and table
1723
+ * commands that act on cells rather than characters.
1724
+ */
1725
+ readonly cellSelection: CellSelection | null;
1726
+ readonly canUndo: boolean;
1727
+ readonly canRedo: boolean;
1728
+ readonly lastRejection: string | null;
1729
+ /**
1730
+ * The typing format armed at the caret (Word's stored marks), or null.
1731
+ *
1732
+ * NOT document state — nothing is written until the next characters are typed — but it
1733
+ * IS observable state: `formatting()` reports it, so a host that reflects the toolbar
1734
+ * has to learn when it moves. Reference-stable while unchanged, so a host can compare
1735
+ * it to decide whether to re-derive. See `toggleRunProperty` for the lane itself.
1736
+ */
1737
+ readonly pendingFormat: readonly {
1738
+ readonly localName: string;
1739
+ }[] | null;
1740
+ /**
1741
+ * Content-control chrome and form-fill mode.
1742
+ *
1743
+ * Surface-owned (not document bytes). Updates report through the same `onChange` path as
1744
+ * selection moves — hosts must not maintain a parallel channel.
1745
+ */
1746
+ readonly contentControls: ContentControlSurfaceState;
1747
+ /**
1748
+ * The TOC the last right-click landed on, or null.
1749
+ *
1750
+ * A right-click deliberately does not move the caret, and a TOC refuses the caret
1751
+ * entirely, so `selection` can never say which table of contents the user is pointing at.
1752
+ * This is how a host's context menu learns it. Surface chrome, not document state.
1753
+ */
1754
+ readonly contextTocId: string | null;
1755
+ /** Timing and reuse counters for the last pass. Diagnostics, not document state. */
1756
+ readonly perf: PaginatedSurfacePerf;
1757
+ }
1758
+ /**
1759
+ * Observable content-control interaction state on the paginated surface.
1760
+ *
1761
+ * Boundary furniture visibility and form-fill navigation are surface chrome, not model
1762
+ * bytes — toggling them never reflows layout records.
1763
+ */
1764
+ interface ContentControlSurfaceState {
1765
+ /** Show boundary chrome for every control. */
1766
+ readonly showAll: boolean;
1767
+ /** Tab / Shift+Tab navigate between editable controls. */
1768
+ readonly formFill: boolean;
1769
+ /** Innermost control containing the caret, or null. */
1770
+ readonly activeControlId: string | null;
1771
+ }
1772
+ /**
1773
+ * The mounted, painted, editable document — the layer `createDocxEditor` builds its contract on.
1774
+ *
1775
+ * The painted pages ARE the editable surface: they are `contenteditable`, but the DOM is a
1776
+ * picture. Browser mutations are prevented and re-expressed as tree ops, and selection maps only
1777
+ * through `data-paragraph-id`/`data-start`, never through DOM node identity.
1778
+ *
1779
+ * Every write goes through the guarded mutation path on this object. Reaching past it into
1780
+ * `session` to apply ops directly bypasses the layout invalidation and the caret bookkeeping.
1781
+ */
1782
+ interface PaginatedSurface {
1783
+ readonly session: TreeDocxSession;
1784
+ storyScope(): StoryScope;
1785
+ imageDecodePort(): ImageDecodePort;
1786
+ applyDrawingOps(ops: readonly DrawingTreeDocOp[]): ReturnType<TreeDocxSession['applyTreeOps']>;
1787
+ applyImageProperties(input: ApplyImagePropertiesInput): ImageIntentResult;
1788
+ deleteImage(drawingNodeId: string): ImageIntentResult;
1789
+ insertImage(input: Omit<InsertImageInput, 'decodePort'>): Promise<ImageIntentResult>;
1790
+ replaceImage(drawingNodeId: string, bytes: Uint8Array, mime: SupportedImageMime, options: {
1791
+ readonly expectedPackageRevision: number;
1792
+ readonly commitGuard?: () => boolean;
1793
+ }): Promise<ImageIntentResult>;
1794
+ layout(): SemanticLayout;
1795
+ state(): PaginatedSurfaceState;
1796
+ /** One-based page at the caret, or at the centre of the mounted viewport. */
1797
+ currentPage(mode?: 'viewport' | 'caret'): number;
1798
+ type(text: string): void;
1799
+ /**
1800
+ * Insert text whose newlines are PARAGRAPH BOUNDARIES, in one commit.
1801
+ *
1802
+ * `type` writes its argument into run text verbatim, so a newline reaching it is a
1803
+ * control character the store refuses — which vetoes the whole transaction and makes
1804
+ * the insert do nothing at all. This is the lane for text that arrives from outside the
1805
+ * editor (a paste, a drop), where line breaks are structure rather than characters.
1806
+ *
1807
+ * Plain text only, by construction: no markup is parsed and no DOM is built from the
1808
+ * payload, whatever its origin.
1809
+ */
1810
+ insertPlainText(text: string): void;
1811
+ deleteBackward(): void;
1812
+ /** Delete forward — the Delete key, and `deleteContentForward` from an IME. */
1813
+ deleteForward(): void;
1814
+ /** Delete to the previous word boundary — Alt/Ctrl+Backspace. */
1815
+ deleteWordBackward(): void;
1816
+ /** Delete to the next word boundary — Alt/Ctrl+Delete. */
1817
+ deleteWordForward(): void;
1818
+ splitParagraph(): void;
1819
+ /** A tab character as a `w:tab` element, not a literal tab in the run text. */
1820
+ insertTab(): void;
1821
+ /** A `w:br` — Shift+Enter, a line break inside the same paragraph. */
1822
+ insertLineBreak(): void;
1823
+ /** A `w:br w:type="page"` — Ctrl+Enter, a hard page break inside the paragraph. */
1824
+ insertPageBreak(): void;
1825
+ /**
1826
+ * Word's Increase/Decrease Indent, over every paragraph the selection touches.
1827
+ *
1828
+ * A NUMBERED or BULLETED paragraph changes LEVEL: `w:numPr/w:ilvl` moves by one, which
1829
+ * re-resolves its marker from `numbering.xml` — so a bullet becomes a hollow circle, a
1830
+ * `1.` becomes an `a.`, exactly as Word demotes a list item. A level the definition
1831
+ * does not declare is DECLARED on the way, with Word's default format for that depth
1832
+ * (its stock bullets and number formats cycle every three levels) — a definition that
1833
+ * stops at `ilvl 0` never blocks the press. Everything else moves its `w:ind/@left` by
1834
+ * one default tab stop, never past the margin.
1835
+ *
1836
+ * Answers whether anything changed, so a caller can fall back (Tab inserting a tab
1837
+ * where there is no list to demote).
1838
+ */
1839
+ adjustIndent(direction: 'increase' | 'decrease'): boolean;
1840
+ /**
1841
+ * Set indent to exact values on every paragraph the selection touches — what a ruler
1842
+ * drag and an indent spinner both need, where {@link adjustIndent} only steps.
1843
+ *
1844
+ * Twips. Omitting a field leaves it as authored; `null` CLEARS it, so the paragraph
1845
+ * falls back to its style — distinct from zero, which blocks the cascade.
1846
+ *
1847
+ * `firstLine` is ONE SIGNED offset, negative for a hanging indent; the two OOXML
1848
+ * spellings are written for it, the unused one as an explicit zero. Answers whether
1849
+ * anything was committed.
1850
+ */
1851
+ setIndent(update: {
1852
+ readonly left?: number | null;
1853
+ readonly right?: number | null;
1854
+ readonly firstLine?: number | null;
1855
+ }): boolean;
1856
+ /**
1857
+ * Whether Increase/Decrease Indent would do anything right now.
1858
+ *
1859
+ * A list item at level 0 cannot outdent and one at level 8 cannot indent — `w:ilvl`
1860
+ * has nine levels and Word greys the control out at the ends. A missing level
1861
+ * DEFINITION never disables it: `adjustIndent` declares the level as it goes. The one
1862
+ * residue: a `w:numStyleLink` definition missing the level refuses the declaration
1863
+ * (its levels belong to the linked style), so there the press is a safe no-op rather
1864
+ * than a greyed control.
1865
+ */
1866
+ canAdjustIndent(direction: 'increase' | 'decrease'): boolean;
1867
+ /**
1868
+ * Enter on an empty list item: outdent a level, or leave the list at level 0.
1869
+ *
1870
+ * Answers false when the caret is not on an empty list item, so the caller falls
1871
+ * through to an ordinary paragraph split.
1872
+ */
1873
+ exitListOnEmptyItem(): boolean;
1874
+ /** Whether the paragraph at the caret is a list item, for Tab's Word-like fallback. */
1875
+ isListParagraph(): boolean;
1876
+ /**
1877
+ * Word's Bullets and Numbering buttons.
1878
+ *
1879
+ * Turns every paragraph the selection touches into a list of `kind`, or takes them all
1880
+ * out when they are already one. The definition is created in `numbering.xml` on first
1881
+ * use — a document that has never carried a list has no numbering part at all.
1882
+ */
1883
+ toggleList(kind: 'bullet' | 'ordered'): boolean;
1884
+ /** Whether every paragraph the selection touches is already a list of `kind`. */
1885
+ isListActive(kind: 'bullet' | 'ordered'): boolean;
1886
+ /** Select the whole document. */
1887
+ selectAll(): void;
1888
+ /**
1889
+ * Turn the browser's editing affordance on the pages layer on or off.
1890
+ *
1891
+ * The facade's `mode` gates COMMANDS, which stops `exec` but not the keyboard: the pages
1892
+ * layer is `contentEditable` and binds `beforeinput` itself, so a document the facade
1893
+ * called read-only still accepted typing straight into it. Read-only has to reach the
1894
+ * surface to be true.
1895
+ */
1896
+ setEditable(editable: boolean): void;
1897
+ /**
1898
+ * Scroll a page, or the page a paragraph sits on, into view. Returns whether it
1899
+ * scrolled — false when the target is not laid out, or the surface is not inside a
1900
+ * scroll container, so a caller can tell "no such target" from "done".
1901
+ *
1902
+ * The geometry comes from the LAYOUT, never from the DOM: a page that has not been
1903
+ * materialized yet has no element to measure, and that is exactly the page a reveal is
1904
+ * usually asked for. `revealParagraph` scrolls to the paragraph's own line rather than
1905
+ * the top of its page, so a heading deep in a page lands in view.
1906
+ */
1907
+ revealPage(pageIndex: number, options?: RevealOptions): boolean;
1908
+ revealParagraph(paragraphId: string, options?: RevealOptions): boolean;
1909
+ /** Set the selection directly, for a host driving the surface programmatically. */
1910
+ setSelection(next: SemanticSelection): void;
1911
+ /**
1912
+ * Select a rectangle of table cells, or clear one with null.
1913
+ *
1914
+ * The equivalent text range is installed alongside it, so `state().selection` stays valid
1915
+ * for every reader that does not know rectangles exist.
1916
+ */
1917
+ setCellSelection(next: CellSelection | null): void;
1918
+ /**
1919
+ * Toggle a run property over the selection, e.g. `b`, `i`, `u`.
1920
+ *
1921
+ * AT A COLLAPSED CARET this ARMS the property instead of writing it — Word's stored
1922
+ * marks. Nothing reaches the document until the next characters are typed there, and
1923
+ * those take the armed format; the armed state shows in `formatting()` and in
1924
+ * `state().pendingFormat` immediately, so a toolbar reflects the press. It survives the
1925
+ * caret-preserving edits (Backspace, Delete, Enter) and IME composition, and is
1926
+ * discarded when the caret moves elsewhere or the document is undone. A property the
1927
+ * store cannot author is refused at arm time rather than left to poison the keystroke.
1928
+ */
1929
+ toggleRunProperty(localName: string, attributes?: Record<string, string>): void;
1930
+ /**
1931
+ * SET a run property over the selection, rather than toggling it.
1932
+ *
1933
+ * Font family, size and colour are values, not switches: picking Arial twice must leave
1934
+ * the text in Arial, which a toggle would not. Arms at a collapsed caret on the same
1935
+ * terms as `toggleRunProperty`.
1936
+ */
1937
+ setRunProperty(localName: string, attributes?: Record<string, string>): void;
1938
+ /**
1939
+ * Set a property on every paragraph the selection touches — alignment, style, spacing.
1940
+ *
1941
+ * `mergeAttributes` keeps the attributes the call does not name, for the properties that
1942
+ * carry several independent settings in one element: `w:spacing` holds the line rule and
1943
+ * the space before and after, so a line-spacing pick must not delete the space-before. A
1944
+ * null-valued attribute removes just that one.
1945
+ */
1946
+ setParagraphProperty(localName: string, attributes?: Record<string, string | null>, options?: {
1947
+ readonly mergeAttributes?: boolean;
1948
+ }): void;
1949
+ /**
1950
+ * Word's Clear All Formatting: direct run properties off the selected text, and every
1951
+ * paragraph the selection touches back to the default style with its direct paragraph
1952
+ * properties and mark dropped.
1953
+ *
1954
+ * Only what the document states DIRECTLY — formatting inherited from a style survives, so
1955
+ * the text falls back to its style rather than to nothing. Properties an op cannot name
1956
+ * (`w:rStyle`, `w:lang`, `w:sectPr`, `w:pBdr`) are preserved for the same reason every
1957
+ * other write preserves them.
1958
+ */
1959
+ clearFormatting(): void;
1960
+ /**
1961
+ * Formatting as it stands at the selection, for a toolbar to reflect.
1962
+ *
1963
+ * With a typing format armed at the caret this reports what the NEXT characters typed
1964
+ * will look like, not what the document holds — which is the answer a toolbar wants and
1965
+ * the one Word gives.
1966
+ */
1967
+ formatting(): SurfaceFormatting;
1968
+ /**
1969
+ * The section the document declares: page size, margins, columns, orientation.
1970
+ *
1971
+ * What a ruler is made of, and what pagination is measured against.
1972
+ */
1973
+ sectionProperties(): SectionProperties;
1974
+ /**
1975
+ * The section GOVERNING one paragraph — what a ruler or dialog reflects when the
1976
+ * caret sits in a multi-section document. Falls back to the body-level section for
1977
+ * an unknown id.
1978
+ */
1979
+ sectionPropertiesAt(paragraphId: string): SectionProperties;
1980
+ /**
1981
+ * Write section page-setup fields — size, orientation, margins — as ONE undoable
1982
+ * transaction. Twips throughout; omitted fields are left as authored. With
1983
+ * `anchorParagraphId` only that paragraph's governing section is written (Word's
1984
+ * "Apply to: This section"); without it, every section. Returns whether the write
1985
+ * committed (a hostile value is refused by the op layer).
1986
+ */
1987
+ setSectionProperties(update: {
1988
+ readonly pageWidthTwips?: number;
1989
+ readonly pageHeightTwips?: number;
1990
+ readonly orientation?: 'portrait' | 'landscape';
1991
+ readonly marginTopTwips?: number;
1992
+ readonly marginRightTwips?: number;
1993
+ readonly marginBottomTwips?: number;
1994
+ readonly marginLeftTwips?: number;
1995
+ readonly anchorParagraphId?: string;
1996
+ }): boolean;
1997
+ /**
1998
+ * Insert a next-page section break at the caret: the paragraph splits, and the head
1999
+ * ends a new section cloning the governing section's page setup — Word's Layout >
2000
+ * Breaks > Next Page. One undoable step. Returns whether the break committed.
2001
+ */
2002
+ insertSectionBreak(): boolean;
2003
+ /** The layout session, so a host or a test can see how much work a pass actually did. */
2004
+ layoutSession(): {
2005
+ readonly stats: {
2006
+ readonly placed: number;
2007
+ readonly total: number;
2008
+ readonly reusedPages: number;
2009
+ };
2010
+ };
2011
+ /**
2012
+ * The hyperlink lane: what link the caret is in, and the insert / retarget / unlink verbs.
2013
+ *
2014
+ * Every verb is one `transact`, so it is one undo step. Targets going IN are host-supplied
2015
+ * and pass the package's own URL allowlist; targets coming OUT are the sanitized
2016
+ * projection, so a caller cannot accidentally hand a refused scheme to a sink.
2017
+ */
2018
+ readonly hyperlinks: HyperlinkOps;
2019
+ /**
2020
+ * Content-control chrome, form-fill navigation, and value / remove verbs.
2021
+ *
2022
+ * Value and remove commit through `session.applyTreeOps` — the same write path as typing.
2023
+ * Show-all and form-fill are surface chrome and never reflow layout.
2024
+ */
2025
+ readonly contentControls: ContentControlOps;
2026
+ /** Whether a `rows`×`cols` table can be inserted at the caret. */
2027
+ canInsertTable(rows: number, cols: number): boolean;
2028
+ /**
2029
+ * Insert an empty `rows`×`cols` table at the caret, columns evenly dividing the content
2030
+ * width of the caret's section, and leave the caret in the first cell.
2031
+ */
2032
+ insertTable(rows: number, cols: number): boolean;
2033
+ /** Whether the addressed (or caret-local) body TOC can be refreshed. */
2034
+ canRefreshToc(tocId?: string): boolean;
2035
+ /** Whether a generated body TOC can be inserted before the caret paragraph. */
2036
+ canInsertToc(): boolean;
2037
+ /** Insert and populate a generated body TOC before the caret paragraph. */
2038
+ insertToc(): boolean;
2039
+ /** Refresh cached TOC entries and/or page numbers through the two-pass layout pipeline. */
2040
+ refreshToc(tocId?: string, mode?: 'entire' | 'pageNumbers'): boolean;
2041
+ /** Whether a body paragraph belongs to a detected TOC boundary or cached result. */
2042
+ isInsideToc(paragraphId: string): boolean;
2043
+ /**
2044
+ * Bookmark jumps and the ONE external-activation gate. A host's popover "open" action
2045
+ * calls `openExternal`; nothing else in the engine may call `window.open`.
2046
+ */
2047
+ readonly navigation: SurfaceNavigation;
2048
+ /**
2049
+ * Pin the current selection so it stays VISIBLY selected while focus is elsewhere.
2050
+ *
2051
+ * A document has one selection: the moment a panel focuses an input of its own the browser
2052
+ * takes the highlight off the text, which is when the user most needs to see what the panel
2053
+ * is about to act on. This draws the range on the engine's own overlay instead, so it
2054
+ * survives the focus move. The MODEL selection is untouched — the op the panel finally runs
2055
+ * addresses the same characters it always would.
2056
+ *
2057
+ * The pin releases itself when the caret leaves the range (either edge counts as inside),
2058
+ * which is what lets a host close its panel on "the user clicked somewhere else" without
2059
+ * every adapter reimplementing that comparison.
2060
+ */
2061
+ retainSelection(): void;
2062
+ /** Drop the pin and stop drawing it, whether or not the caret ever left. */
2063
+ releaseSelection(): void;
2064
+ /** The pinned range, or null once it was released or escaped. */
2065
+ retainedSelection(): SemanticSelection | null;
2066
+ /**
2067
+ * How edits are written right now.
2068
+ *
2069
+ * Lives on the SURFACE, not on the store. The store's write vocabulary stays explicit —
2070
+ * an op says whether it is tracked — and the surface is the one thing that knows a
2071
+ * keystroke happened, so it is the right place to decide what that keystroke becomes.
2072
+ */
2073
+ editingMode(): SurfaceEditingMode;
2074
+ setEditingMode(mode: SurfaceEditingMode): void;
2075
+ /**
2076
+ * Commit ops that came from automation, through the gate a keystroke goes through.
2077
+ *
2078
+ * The narrow entry an automation host writes with, and the reason it needs one: reaching
2079
+ * `session.applyTreeOps` past this skips the editing-mode gate entirely — a document open for
2080
+ * viewing accepts a scripted edit, and a suggesting document records one as a permanent
2081
+ * change with no proposal and no author. Here, viewing refuses, suggesting attributes, the
2082
+ * refusal reason is reported like any other, and the pages repaint from the commit.
2083
+ *
2084
+ * The ops address the story the CALLER named, whatever story the reader is in: the caller
2085
+ * identified its target before calling, so following the caret into a header would write
2086
+ * somewhere else entirely. They default to the body rather than to the reader's story.
2087
+ *
2088
+ * They arrive as a BUILDER, given a way to mint the relationship an external hyperlink names.
2089
+ * That mint changes the package outside the transaction and outside the undo stack, so it must not
2090
+ * happen until the mode has allowed the write — a link minted while the batch was still being
2091
+ * planned left its target in a read-only document's `.rels`. A builder answering null means the
2092
+ * target is one this engine will not author, and the write is refused having changed nothing.
2093
+ */
2094
+ applyAutomationOps(staged: (relate: (url: string) => string | null) => readonly TreeDocOp[] | null, scope?: StoryScope): TreeApplyResult;
2095
+ /**
2096
+ * Commit review ops — accept, reject, a new comment — through the SAME path a keystroke
2097
+ * takes: layout, paint, and a caret clamped to what the document now holds.
2098
+ *
2099
+ * Applying them straight to the session skipped all three. Rejecting an insertion left the
2100
+ * pages painting text the tree no longer had, every card anchored where it used to be, and
2101
+ * the caret past the end of the paragraph — after which every keystroke was refused with
2102
+ * `offset-out-of-range` until the user happened to click somewhere else.
2103
+ */
2104
+ commitReviewOps(run: () => {
2105
+ readonly committed: boolean;
2106
+ readonly reason?: unknown;
2107
+ }): void;
2108
+ /**
2109
+ * The layout as last PUBLISHED, without forcing pending work.
2110
+ *
2111
+ * `layout()` flushes first, which is right for a caller that is about to act on geometry
2112
+ * and wrong for one that merely decorates it. The review rail read through `layout()` and
2113
+ * so forced a synchronous full pass on every keystroke — eleven seconds per read on a
2114
+ * 2432-block document. A card whose anchor is one frame stale is invisible; the paint that
2115
+ * follows the flush republishes it.
2116
+ */
2117
+ publishedLayout(): SemanticLayout;
2118
+ /**
2119
+ * Paint-scale coordinate context for overlay chrome.
2120
+ *
2121
+ * Internal seam — not part of the public editor contract. Image overlay uses the same
2122
+ * `zoom * 96/72` scale and per-page horizontal offsets the painter applied.
2123
+ */
2124
+ overlayCoordinates(): SurfaceOverlayCoordinates;
2125
+ /**
2126
+ * The comment or tracked change the caret is in, as the painted bands report it.
2127
+ *
2128
+ * ONE source for "which item is open". The band under the text and the card beside it are
2129
+ * two views of the same answer, and deriving it twice let them disagree — the card closed
2130
+ * while the text stayed highlighted.
2131
+ */
2132
+ activeReviewKey(): string | null;
2133
+ /**
2134
+ * Close the open item until the caret next moves.
2135
+ *
2136
+ * What a click on the canvas means. The caret does not move when someone clicks the grey
2137
+ * around the page, so nothing else would ever put the item away.
2138
+ */
2139
+ dismissActiveReview(): void;
2140
+ /**
2141
+ * `bookmarkName -> position` over the current revision, for resolving an internal link.
2142
+ * First in document order wins a duplicate name, matching Word.
2143
+ */
2144
+ bookmarks(): BookmarkIndex;
2145
+ /** The selected text, for copy and cut. */
2146
+ selectedText(): string;
2147
+ /** Remove the selection, if any. Returns whether anything was deleted. */
2148
+ deleteSelection(): boolean;
2149
+ navigate(command: NavigationCommand, extend?: boolean): void;
2150
+ /** Reverse the last history entry and put the caret back where it was made. */
2151
+ undo(): void;
2152
+ redo(): void;
2153
+ /**
2154
+ * Refresh table insertion furniture labels without remounting or relayout.
2155
+ *
2156
+ * @public
2157
+ */
2158
+ refreshTableInteractionLabels(): void;
2159
+ focus(): void;
2160
+ /**
2161
+ * Refresh table insertion furniture labels without remounting the surface.
2162
+ *
2163
+ * @public
2164
+ */
2165
+ setTableInteractionLabel(resolver: (key: 'table.insertRowBelow' | 'table.insertColumnRight') => string): void;
2166
+ destroy(): void;
2167
+ /** Active editing view — body, or an open header/footer story by rId. */
2168
+ activeScope(): ViewScope;
2169
+ /** Activate a view scope. Returns false when a header/footer rId cannot be opened. */
2170
+ setActiveScope(scope: ViewScope): boolean;
2171
+ /**
2172
+ * Open a header/footer story for editing on the painted surface.
2173
+ * Refuses dangling / unknown relationship ids.
2174
+ */
2175
+ enterHeaderFooter(args: {
2176
+ readonly rId: string;
2177
+ readonly pageIndex?: number;
2178
+ readonly sectionIndex?: number;
2179
+ readonly kind?: 'header' | 'footer';
2180
+ readonly variant?: 'default' | 'first' | 'even';
2181
+ readonly position?: SemanticPosition;
2182
+ }): boolean;
2183
+ /** Leave furniture editing and restore the prior body selection. */
2184
+ exitHeaderFooter(): void;
2185
+ /** Chrome read-model for the open furniture scope, or null when editing the body. */
2186
+ headerFooterState(): {
2187
+ readonly editing: 'header' | 'footer' | null;
2188
+ readonly sectionIndex: number;
2189
+ readonly variant?: 'default' | 'first' | 'even';
2190
+ readonly rId?: string;
2191
+ readonly partName?: string;
2192
+ readonly inherited?: boolean;
2193
+ readonly titlePage?: boolean;
2194
+ readonly evenAndOddHeaders?: boolean;
2195
+ readonly headerDistanceTwips?: number;
2196
+ readonly footerDistanceTwips?: number;
2197
+ } | null;
2198
+ /**
2199
+ * Commit one package-level furniture lifecycle op (create/delete/link/unlink/options).
2200
+ * Flushes layout so the next enter/rebind sees the new resolution.
2201
+ */
2202
+ applyHeaderFooterLifecycle(op: {
2203
+ readonly op: 'createHeaderFooter' | 'deleteHeaderFooter' | 'linkToPrevious' | 'unlinkFromPrevious' | 'setSectionFurnitureOptions';
2204
+ readonly sectionIndex?: number;
2205
+ readonly kind?: 'header' | 'footer';
2206
+ readonly variant?: 'default' | 'first' | 'even';
2207
+ readonly titlePage?: boolean;
2208
+ readonly evenAndOddHeaders?: boolean;
2209
+ readonly headerDistanceTwips?: number;
2210
+ readonly footerDistanceTwips?: number;
2211
+ }): {
2212
+ readonly ok: true;
2213
+ } | {
2214
+ readonly ok: false;
2215
+ readonly reason: string;
2216
+ };
2217
+ /** Insert an allowlisted page field at the caret in the open HF story. */
2218
+ insertPageField(field: 'PAGE' | 'NUMPAGES' | 'SECTIONPAGES' | 'PAGE_X_OF_Y'): boolean;
2219
+ /** Insert a footnote/endnote at the body caret. */
2220
+ insertNote(noteKind: 'footnote' | 'endnote'): boolean;
2221
+ deleteNote(noteKind: 'footnote' | 'endnote', noteId: number): boolean;
2222
+ convertNote(fromKind: 'footnote' | 'endnote', noteId: number): boolean;
2223
+ convertAllNotes(fromKind: 'footnote' | 'endnote'): boolean;
2224
+ setNoteProperties(args: {
2225
+ readonly scope: 'document' | 'section';
2226
+ readonly sectionIndex?: number;
2227
+ readonly footnote?: {
2228
+ readonly numFmt?: string;
2229
+ readonly numRestart?: string;
2230
+ readonly position?: string;
2231
+ readonly numStart?: number;
2232
+ };
2233
+ readonly endnote?: {
2234
+ readonly numFmt?: string;
2235
+ readonly numRestart?: string;
2236
+ readonly position?: string;
2237
+ readonly numStart?: number;
2238
+ };
2239
+ }): boolean;
2240
+ enterNote(scopeId: string, position?: {
2241
+ paragraphId: string;
2242
+ offset: number;
2243
+ }): boolean;
2244
+ exitNote(): void;
2245
+ /** Resolved/authored note properties for the caret section — chrome read-model. */
2246
+ notePropertiesState(): NotePropertiesStateSnapshot | null;
2247
+ /** Plain-text preview for hover chrome — never returns markup. */
2248
+ notePreviewText(scopeId: string): string | null;
2249
+ /** Commit one table-command plan as a single store transaction. */
2250
+ applyTableCommandPlan(plan: TableCommandPlan): ExecResult;
2251
+ }
2252
+ /**
2253
+ * What opening a document produced: a mounted surface, or a refusal.
2254
+ *
2255
+ * A result rather than a throw, because every refusal here comes from FILE input — a package the
2256
+ * bounded reader rejected, a part that exceeded a limit — and a malformed upload should surface
2257
+ * as a message the host can show rather than an exception it has to catch.
2258
+ */
2259
+ type OpenPaginatedResult = {
2260
+ readonly ok: true;
2261
+ readonly surface: PaginatedSurface;
2262
+ } | {
2263
+ readonly ok: false;
2264
+ readonly reason: string;
2265
+ readonly detail?: string;
2266
+ };
2267
+
2268
+ /** Material Symbol name → SVG path data (viewBox "0 -960 960 960"). */
2269
+ declare const GENERATED_ICON_PATHS: Readonly<Record<string, readonly string[]>>;
2270
+
2271
+ /** Border target picker value — concrete scopes plus clear (`none`). */
2272
+ type TableBorderTargetValue = TableBorderEdgeTarget | 'none';
2273
+ /** UI draft: last active edge scope and the complete border spec to reapply. */
2274
+ interface TableChromeDraft {
2275
+ readonly activeTarget: TableBorderEdgeTarget;
2276
+ readonly spec: TableBorderSpec;
2277
+ }
2278
+ /** The draft a table's border chrome starts from: a 1pt black single border on every edge. */
2279
+ declare const DEFAULT_TABLE_CHROME_DRAFT: TableChromeDraft;
2280
+ /**
2281
+ * The chrome slots that only exist while the caret is inside a table.
2282
+ *
2283
+ * A subset of `ChromeSlotId`, so these names are public API on the same terms — renaming one is
2284
+ * a breaking change.
2285
+ */
2286
+ type TableChromeSlotId = 'table.borderTarget' | 'table.borderColor' | 'table.borderStyle' | 'table.borderWidth' | 'table.cellFill';
2287
+ /** Every {@link TableChromeSlotId}, for iteration and for {@link isTableChromeSlot}. */
2288
+ declare const TABLE_CHROME_SLOT_IDS: readonly TableChromeSlotId[];
2289
+ /** Furniture insertion controls (Task 8) share this label seam. */
2290
+ type TableInteractionLabelKey = 'table.insertRowBelow' | 'table.insertColumnRight';
2291
+ /**
2292
+ * The English label for a furniture-insertion control.
2293
+ *
2294
+ * The FALLBACK, for a host that has not wired its own translator — adapters pass their
2295
+ * `useTranslation` result instead so the control follows the app's locale.
2296
+ */
2297
+ declare function defaultTableLabel(key: TableInteractionLabelKey): string;
2298
+ /** Whether a chrome slot is one of the table-only ones. Narrows the type. */
2299
+ declare function isTableChromeSlot(slot: ChromeSlotId): slot is TableChromeSlotId;
2300
+ /** Contextual table chrome is visible when the engine reports table context. */
2301
+ declare function tableChromeVisible(table: TableContext | null | undefined): boolean;
2302
+ /** One entry in the border-target picker: which edges it addresses, its icon, and its label key. */
2303
+ interface TableBorderTargetOption {
2304
+ readonly value: TableBorderTargetValue;
2305
+ readonly icon: keyof typeof GENERATED_ICON_PATHS;
2306
+ /** i18n key, never a literal string — both adapters translate it themselves. */
2307
+ readonly labelKey: string;
2308
+ }
2309
+ /** The border-target picker's entries, in Word's own order, ending with clear. */
2310
+ declare const TABLE_BORDER_TARGET_OPTIONS: readonly TableBorderTargetOption[];
2311
+ /** One entry in the border-style picker, with the CSS class that draws its preview line. */
2312
+ interface TableBorderStyleOption {
2313
+ readonly value: TableBorderStyle;
2314
+ readonly labelKey: string;
2315
+ /** Class on the preview swatch. Defined in the core stylesheet, which both adapters import. */
2316
+ readonly previewClass: string;
2317
+ }
2318
+ /** The border-style picker's entries. */
2319
+ declare const TABLE_BORDER_STYLE_OPTIONS: readonly TableBorderStyleOption[];
2320
+ /** One entry in the border-width picker. */
2321
+ interface TableBorderWidthOption {
2322
+ /** `w:sz` — eighths of a point, as OOXML stores border widths. */
2323
+ readonly size: number;
2324
+ readonly labelKey: string;
2325
+ /** Points, for drawing the preview swatch. `size / 8`. */
2326
+ readonly previewThickness: number;
2327
+ }
2328
+ /** Widths in eighths of a point — Word-like presets. */
2329
+ declare const TABLE_BORDER_WIDTH_OPTIONS: readonly TableBorderWidthOption[];
2330
+ /**
2331
+ * What one table-chrome selection produced: the command to run, and the draft to remember.
2332
+ *
2333
+ * Both halves matter. A border picker is stateful — choosing "dashed" and then "outside" must
2334
+ * apply a dashed outside border — so each pick returns the COMPLETE spec to execute alongside the
2335
+ * draft the next pick builds on.
2336
+ */
2337
+ interface TableChromePick {
2338
+ readonly command: EditorCommand;
2339
+ readonly nextDraft: TableChromeDraft;
2340
+ }
2341
+ /**
2342
+ * Build the engine command for one table chrome pick and the draft state after it.
2343
+ * Target picks apply the current complete spec; `none` clears the active target only.
2344
+ */
2345
+ declare function applyTableChromePick(draft: TableChromeDraft, slot: TableChromeSlotId, value: unknown): TableChromePick | null;
2346
+ /** Probe command for `Editor.can` — uses the draft's active target and a well-formed value. */
2347
+ declare function probeTableChromeCommand(slot: TableChromeSlotId, draft?: TableChromeDraft): EditorCommand | null;
2348
+ /**
2349
+ * The i18n key naming one border target, for a trigger that shows the active scope.
2350
+ *
2351
+ * Falls back to the "all" key rather than throwing: a label is chrome, and a missing one should
2352
+ * not take the toolbar down.
2353
+ */
2354
+ declare function tableChromeLabelKeyForTarget(target: TableBorderEdgeTarget): string;
2355
+ /** Material Symbol paths for one table chrome icon name. */
2356
+ declare function tableChromeIconPaths(name: keyof typeof GENERATED_ICON_PATHS): readonly string[];
2357
+
2358
+ /**
2359
+ * The probe a slot uses to ask "would the engine honour this right now?" when its real
2360
+ * command needs an argument the slot itself cannot supply.
2361
+ *
2362
+ * `text.link` is the case: whether this selection could become a link is the engine's
2363
+ * question, but WHICH link is a URL field's. Chrome that owns a link UI (React's
2364
+ * `ToolbarLink`) asks with this and dispatches through that UI.
2365
+ *
2366
+ * DELIBERATELY NOT in `SLOT_COMMANDS`. Enabled state has one source, and putting the probe
2367
+ * there would enable the control in EVERY adapter — including Vue, which has grown no link
2368
+ * UI, where the result is an enabled button whose click can only be refused. A dead button
2369
+ * is the worse lie: `file.save` was a disabled control for a capability that works, and this
2370
+ * would be an enabled control for one that is not reachable. Vue's slot therefore keeps
2371
+ * reporting the honest "not wired to an editor command" until its popover lands.
2372
+ *
2373
+ * @public
2374
+ */
2375
+ declare function chromeProbeForSlot(slotId: ChromeSlotId): EditorCommand | null;
2376
+ /**
2377
+ * The public editor command behind one chrome slot, or `null` when the slot is
2378
+ * not wired to a command yet (parity-only chrome, or save — which is not a
2379
+ * command). The single source of command truth for both adapters.
2380
+ *
2381
+ * @public
2382
+ */
2383
+ declare function commandForSlot(slotId: ChromeSlotId): EditorCommand | null;
2384
+
2385
+ /**
2386
+ * Build an engine command for one table chrome slot using the caller's draft state.
2387
+ *
2388
+ * @public
2389
+ */
2390
+ declare function commandForTableChromeSlotValue(slotId: TableChromeSlotId, value: unknown, draft: TableChromeDraft): EditorCommand | null;
2391
+ /**
2392
+ * The engine command for a VALUE-TYPED slot carrying the picked value, or `null` for a
2393
+ * slot that does not take a value.
2394
+ *
2395
+ * Two families: the run-property pickers (`font.family`, `font.size`, `text.color`,
2396
+ * `text.highlight`) resolve to `setMarkAttr`, and `styles.style` resolves to
2397
+ * `setParagraphStyle` — a paragraph styleId, not a mark. Either way the value is
2398
+ * validated by the engine's own gate (`can` refuses a malformed one with `invalidArgs`;
2399
+ * a styleId the document does not define is refused at `exec`), so a host can pass user
2400
+ * input through unmodified.
2401
+ *
2402
+ * @public
2403
+ */
2404
+ declare function commandForSlotValue(slotId: ChromeSlotId, value: unknown): EditorCommand | null;
2405
+ /**
2406
+ * Whether one control is enabled, and the engine's reason when it is not.
2407
+ *
2408
+ * @public
2409
+ */
2410
+ interface ToolbarCommandState {
2411
+ readonly id: ChromeSlotId;
2412
+ readonly enabled: boolean;
2413
+ /** The engine's reason when disabled — surfaced as a tooltip, never invented. */
2414
+ readonly disabledReason: string | null;
2415
+ /**
2416
+ * What the control currently SHOWS, for the slots whose answer is a value rather than a
2417
+ * pressed state — the editing-mode pill, and image wrap when it lands.
2418
+ *
2419
+ * `active` cannot express this: "the mode is Suggesting" is not a boolean about one
2420
+ * command, and a parallel channel for it would be a second place a control could read its
2421
+ * own state from. Absent for every slot whose state really is just pressed-or-not.
2422
+ */
2423
+ readonly value?: string;
2424
+ /** Whether the command is currently APPLIED at the selection, from `Editor.isActive` —
2425
+ * derived in the engine for marks and alignment, honest-false elsewhere. */
2426
+ readonly active: boolean;
2427
+ }
2428
+ /**
2429
+ * Enabled state for one table chrome slot using explicit draft state.
2430
+ *
2431
+ * @public
2432
+ */
2433
+ declare function tableChromeToolbarState(editor: Editor | null, slot: TableChromeSlotId, draft?: TableChromeDraft): ToolbarCommandState;
2434
+ /**
2435
+ * Ask the engine whether one control should be enabled.
2436
+ *
2437
+ * @public
2438
+ */
2439
+ declare function toolbarCommandState(editor: Editor | null, id: ChromeSlotId): ToolbarCommandState;
2440
+ /**
2441
+ * Enabled state for several controls in one pass.
2442
+ *
2443
+ * @public
2444
+ */
2445
+ declare function toolbarCommandStates(editor: Editor | null, ids: readonly ChromeSlotId[]): readonly ToolbarCommandState[];
2446
+ /**
2447
+ * Result of {@link runTableChromeCommand}: engine outcome plus post-pick draft on success.
2448
+ *
2449
+ * @public
2450
+ */
2451
+ interface RunTableChromeCommandResult {
2452
+ readonly result: ExecResult;
2453
+ readonly nextDraft: TableChromeDraft | null;
2454
+ }
2455
+ /**
2456
+ * Run one table chrome pick with explicit draft state; returns the post-pick draft on success.
2457
+ *
2458
+ * @public
2459
+ */
2460
+ declare function runTableChromeCommand(editor: Editor | null, slot: TableChromeSlotId, value: unknown, draft: TableChromeDraft): RunTableChromeCommandResult;
2461
+ /**
2462
+ * Run a toolbar control: `can` first, then `exec` only if it said yes. Returns
2463
+ * the engine's refusal untouched when it said no, so a caller cannot mistake a
2464
+ * declined command for a no-op.
2465
+ *
2466
+ * @public
2467
+ */
2468
+ declare function runToolbarCommand(editor: Editor | null, id: ChromeSlotId,
2469
+ /** The chosen value, for a slot whose command carries one (the editing-mode pill). */
2470
+ value?: unknown): ExecResult;
2471
+ /**
2472
+ * Save goes straight to `Editor.save()` — it is not a command.
2473
+ *
2474
+ * @public
2475
+ */
2476
+ declare function runSave(editor: Editor | null): Promise<ArrayBuffer>;
2477
+ /**
2478
+ * Enabled state for a table command when the caller holds the paginated surface.
2479
+ *
2480
+ * Uses the same planner-backed `tableCommandState` as `Editor.can`/`gateTableCommand`.
2481
+ * Chrome slot mapping for table controls is Task 9 — this helper is the shared
2482
+ * can-before-exec seam for arbitrary table commands.
2483
+ *
2484
+ * @public
2485
+ */
2486
+ declare function tableCommandToolbarState(surface: PaginatedSurface | null, command: EditorCommand): Pick<ToolbarCommandState, 'enabled' | 'disabledReason'>;
2487
+ /**
2488
+ * Run a table command: planner-backed `can` first, then `exec` only when allowed.
2489
+ *
2490
+ * @public
2491
+ */
2492
+ declare function runTableCommand(editor: Editor | null, command: EditorCommand): ExecResult;
2493
+
2494
+ /**
2495
+ * Build an editor: the full `Editor` contract over a paginated surface.
2496
+ *
2497
+ * Construction is separate from mounting. Pass a `container` and the document mounts
2498
+ * immediately; omit it and nothing touches the DOM until `attach(el)` — the provider-first
2499
+ * shape. `detach()` remounts from the saved bytes, which resets undo and the caret.
2500
+ *
2501
+ * `snapshot()` is version-cached: the same reference until state actually moves, with
2502
+ * reference-stable sub-objects, so it is safe as a `useSyncExternalStore` source.
2503
+ *
2504
+ * @example
2505
+ * ```ts
2506
+ * const editor = createDocxEditor({ document: bytes, modules: [reviewModule()] });
2507
+ * editor.attach(element);
2508
+ * editor.on('change', () => setDirty(true));
2509
+ * ```
2510
+ *
2511
+ * @public
2512
+ */
2513
+ declare function createDocxEditor(config: DocxEditorConfig): DocxEditorInstance;
2514
+
2515
+ export { TABLE_BORDER_STYLE_OPTIONS as $, type AnchorFrameOrigin as A, type HyperlinkOps as B, CHROME_GROUPS as C, type DocxEditorInstance as D, EMU_PER_POINT as E, type FinalizedImageOverlayInteraction as F, IMAGE_OVERLAY_NUDGE_SHIFT_PT as G, type HyperlinkActivation as H, IMAGE_OVERLAY_NUDGE_PT as I, type ImageInteractionSession as J, type ImageOverlayScrollPort as K, type ImageResizeHandle as L, type ImageResizeResult as M, type LoadFontsRequest as N, type OpenPaginatedResult as O, type PaginatedSurfaceOptions as P, type LoadFontsResult as Q, MAX_RESOLVER_FAMILIES as R, type OverlayFrameRect as S, type TableCommandPlan as T, type PaginatedSurfaceState as U, type RunTableChromeCommandResult as V, type SelectedDrawingOverlayTarget as W, type SurfaceFormatting as X, type SurfaceHyperlink as Y, type SurfaceNavigation as Z, type SurfaceOverlayCoordinates as _, type PaginatedSurface as a, TABLE_BORDER_TARGET_OPTIONS as a0, TABLE_BORDER_WIDTH_OPTIONS as a1, TABLE_CHROME_SLOT_IDS as a2, type TableBorderStyleOption as a3, type TableBorderTargetOption as a4, type TableBorderTargetValue as a5, type TableBorderWidthOption as a6, type TableChromeDraft as a7, type TableChromePick as a8, type TableChromeSlotId as a9, finalizeImageOverlayInteraction as aA, formattingBarChromeGroups as aB, isStaleImageInteractionCommit as aC, isTableChromeSlot as aD, layoutPointsToCssPixels as aE, loadFonts as aF, overlayFrameToSheetCssPixels as aG, pointsToEmu as aH, probeTableChromeCommand as aI, resizePreservesAspect as aJ, runSave as aK, runTableChromeCommand as aL, runTableCommand as aM, runToolbarCommand as aN, selectedDrawingOverlayTargetOf as aO, selectedImageStateOf as aP, surfacePaintScale as aQ, tableChromeIconPaths as aR, tableChromeLabelKeyForTarget as aS, tableChromeToolbarState as aT, tableChromeVisible as aU, tableCommandToolbarState as aV, toolbarCommandState as aW, toolbarCommandStates as aX, type TableInteractionLabelKey as aa, type ToolbarCommandState as ab, WORD_DEFAULT_FONT as ac, applyTableChromePick as ad, blankDocumentBytes as ae, canExecuteImageCommand as af, captureImageMutationPreconditions as ag, chromeControlCount as ah, chromeMenuSlots as ai, chromeProbeForSlot as aj, chromeSlotId as ak, commandForSlot as al, commandForSlotValue as am, commandForTableChromeSlotValue as an, composeFontConfiguration as ao, computeImageResizeResult as ap, computeMovedImagePosition as aq, computeResizedImageExtentEmu as ar, createDocxEditor as as, createFontSource as at, createImageOverlayScrollPort as au, cssPixelsToLayoutPoints as av, defaultChromeGroups as aw, defaultTableLabel as ax, emuToOverlayPoints as ay, executeImageCommand as az, CHROME_MENUS as b, CHROME_UNAVAILABLE_KEY as c, type ChromeControl as d, type ChromeControlId as e, type ChromeControlState as f, type ChromeGroup as g, type ChromeGroupId as h, type ChromeMenu as i, type ChromeMenuEntry as j, type ChromeMenuId as k, type ChromeMenuItemEntry as l, type ChromeMenuSeparatorEntry as m, type ChromeMenuSubmenuEntry as n, type ChromeSlotId as o, DEFAULT_TABLE_CHROME_DRAFT as p, type DocxEditorConfig as q, type FontConfigurationBase as r, type FontConfigurationFragment as s, type FontLoadFailure as t, type FontLoadFailureReason as u, type FontMeasurementState as v, type FontResolutionRequest as w, type FontResolver as x, type FontUrlSource as y, type HyperlinkChromeHandlers as z };