docxodus 9.9.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +182 -4
  2. package/dist/canonical.d.ts +7 -0
  3. package/dist/canonical.d.ts.map +1 -0
  4. package/dist/canonical.js +56 -0
  5. package/dist/canonical.js.map +1 -0
  6. package/dist/docxodus.worker.d.ts +2 -1
  7. package/dist/docxodus.worker.d.ts.map +1 -1
  8. package/dist/docxodus.worker.js +250 -41
  9. package/dist/docxodus.worker.js.map +1 -1
  10. package/dist/editor.bundle.js +1980 -423
  11. package/dist/editor.d.ts +22 -2
  12. package/dist/editor.d.ts.map +1 -1
  13. package/dist/editor.js +94 -9
  14. package/dist/editor.js.map +1 -1
  15. package/dist/embed.bundle.js +2968 -597
  16. package/dist/embed.d.ts +1 -1
  17. package/dist/embed.iife.js +2969 -598
  18. package/dist/embed.js +1 -1
  19. package/dist/export-assets.json +277 -0
  20. package/dist/export-browser.bundle.js +7989 -0
  21. package/dist/export-browser.d.ts +266 -0
  22. package/dist/export-browser.d.ts.map +1 -0
  23. package/dist/export-browser.js +2915 -0
  24. package/dist/export-browser.js.map +1 -0
  25. package/dist/export-resource-limits-v1.json +57 -0
  26. package/dist/font-contract.d.ts +150 -0
  27. package/dist/font-contract.d.ts.map +1 -0
  28. package/dist/font-contract.js +59 -0
  29. package/dist/font-contract.js.map +1 -0
  30. package/dist/font-runtime.d.ts +33 -0
  31. package/dist/font-runtime.d.ts.map +1 -0
  32. package/dist/font-runtime.js +1100 -0
  33. package/dist/font-runtime.js.map +1 -0
  34. package/dist/index.d.ts +83 -65
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +194 -149
  37. package/dist/index.js.map +1 -1
  38. package/dist/page-geometry.d.ts +8 -1
  39. package/dist/page-geometry.d.ts.map +1 -1
  40. package/dist/page-geometry.js +37 -11
  41. package/dist/page-geometry.js.map +1 -1
  42. package/dist/pagination.bundle.js +1827 -410
  43. package/dist/pagination.d.ts +249 -15
  44. package/dist/pagination.d.ts.map +1 -1
  45. package/dist/pagination.js +1942 -453
  46. package/dist/pagination.js.map +1 -1
  47. package/dist/react.d.ts +13 -6
  48. package/dist/react.d.ts.map +1 -1
  49. package/dist/react.js +38 -5
  50. package/dist/react.js.map +1 -1
  51. package/dist/render-report-v2.schema.json +1581 -0
  52. package/dist/ribbon-chrome.d.ts +2 -2
  53. package/dist/ribbon-chrome.d.ts.map +1 -1
  54. package/dist/ribbon-chrome.js +72 -4
  55. package/dist/ribbon-chrome.js.map +1 -1
  56. package/dist/ribbon.js +119 -0
  57. package/dist/ribbon.js.map +1 -1
  58. package/dist/session.bundle.js +747 -29
  59. package/dist/session.d.ts +159 -19
  60. package/dist/session.d.ts.map +1 -1
  61. package/dist/session.js +593 -25
  62. package/dist/session.js.map +1 -1
  63. package/dist/types.d.ts +1287 -238
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/types.js +9 -75
  66. package/dist/types.js.map +1 -1
  67. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
  68. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
  69. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
  70. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -0
  71. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  72. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  73. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  74. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  75. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  76. package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
  77. package/dist/wasm/_framework/System.Collections.Immutable.wasm +0 -0
  78. package/dist/wasm/_framework/System.Collections.Immutable.wasm.br +0 -0
  79. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  80. package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
  81. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm +0 -0
  82. package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm.br +0 -0
  83. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  84. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  85. package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
  86. package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
  87. package/dist/wasm/_framework/System.IO.Pipelines.wasm +0 -0
  88. package/dist/wasm/_framework/System.IO.Pipelines.wasm.br +0 -0
  89. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  90. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  91. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  92. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  93. package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
  94. package/dist/wasm/_framework/System.Private.Uri.wasm.br +0 -0
  95. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  96. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  97. package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
  98. package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
  99. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  100. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
  101. package/dist/wasm/_framework/System.Runtime.wasm +0 -0
  102. package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
  103. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  104. package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
  105. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm +0 -0
  106. package/dist/wasm/_framework/System.Text.Encodings.Web.wasm.br +0 -0
  107. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  108. package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
  109. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  110. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  111. package/dist/wasm/_framework/dotnet.boot.js +34 -40
  112. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  113. package/dist/wasm/_framework/dotnet.native.js +39 -3
  114. package/dist/wasm/_framework/dotnet.native.js.br +0 -0
  115. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  116. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  117. package/dist/worker-proxy.bundle.js +198 -32
  118. package/dist/worker-proxy.d.ts +37 -4
  119. package/dist/worker-proxy.d.ts.map +1 -1
  120. package/dist/worker-proxy.js +187 -35
  121. package/dist/worker-proxy.js.map +1 -1
  122. package/package.json +22 -6
  123. package/dist/wasm/_framework/System.Diagnostics.Process.wasm +0 -0
  124. package/dist/wasm/_framework/System.Diagnostics.Process.wasm.br +0 -0
@@ -47,7 +47,46 @@ var DocxodusSession = (() => {
47
47
  };
48
48
 
49
49
  // src/session.ts
50
- var DocxSession = class {
50
+ function mutationBatchChangeSet(before, after, key) {
51
+ const beforeGroups = /* @__PURE__ */ new Map();
52
+ const afterGroups = /* @__PURE__ */ new Map();
53
+ const group = (items, target) => {
54
+ items.forEach((item, index) => {
55
+ const identity = key(item);
56
+ const indices = target.get(identity) ?? [];
57
+ indices.push(index);
58
+ target.set(identity, indices);
59
+ });
60
+ };
61
+ group(before, beforeGroups);
62
+ group(after, afterGroups);
63
+ const beforeMatched = before.map(() => false);
64
+ const afterMatched = after.map(() => false);
65
+ const modified = after.map(() => false);
66
+ for (const [identity, afterIndices] of afterGroups) {
67
+ const beforeIndices = beforeGroups.get(identity) ?? [];
68
+ for (const afterIndex of afterIndices) {
69
+ const beforeIndex = beforeIndices.find((index) => !beforeMatched[index] && JSON.stringify(before[index]) === JSON.stringify(after[afterIndex]));
70
+ if (beforeIndex === void 0) continue;
71
+ beforeMatched[beforeIndex] = true;
72
+ afterMatched[afterIndex] = true;
73
+ }
74
+ const remainingBefore = beforeIndices.filter((index) => !beforeMatched[index]);
75
+ const remainingAfter = afterIndices.filter((index) => !afterMatched[index]);
76
+ const modifiedCount = Math.min(remainingBefore.length, remainingAfter.length);
77
+ for (let index = 0; index < modifiedCount; index++) {
78
+ beforeMatched[remainingBefore[index]] = true;
79
+ afterMatched[remainingAfter[index]] = true;
80
+ modified[remainingAfter[index]] = true;
81
+ }
82
+ }
83
+ return {
84
+ added: after.filter((_, index) => !afterMatched[index]),
85
+ removed: before.filter((_, index) => !beforeMatched[index]),
86
+ modified: after.filter((_, index) => modified[index])
87
+ };
88
+ }
89
+ var DocxSession = class _DocxSession {
51
90
  /** @internal */
52
91
  constructor(handle, wasm) {
53
92
  // ─── Raw escape hatch ────────────────────────────────────────────────
@@ -63,6 +102,336 @@ var DocxodusSession = (() => {
63
102
  project() {
64
103
  return JSON.parse(this.wasm.Project(this.handle));
65
104
  }
105
+ /** Monotonic document version (0 at open; +1 per committed mutation/undo/redo). */
106
+ getVersion() {
107
+ return JSON.parse(this.wasm.GetVersion(this.handle)).version;
108
+ }
109
+ /**
110
+ * Verification manifest for the current logical checkpoint. Unsaved edits are included and
111
+ * the read does not mutate package bytes, caches, history, or the document version.
112
+ */
113
+ getPackageManifest() {
114
+ return JSON.parse(this.wasm.GetPackageManifest(this.handle));
115
+ }
116
+ /** Register a browser-materialized PageMap without changing the document version. */
117
+ registerPageMap(pageMap, expectedRendererFingerprint) {
118
+ return JSON.parse(this.wasm.RegisterPageMap(
119
+ this.handle,
120
+ JSON.stringify(pageMap),
121
+ expectedRendererFingerprint ?? ""
122
+ ));
123
+ }
124
+ getPageMapStatus(request) {
125
+ return JSON.parse(this.wasm.GetPageMapStatus(
126
+ this.handle,
127
+ request ? JSON.stringify(request) : ""
128
+ ));
129
+ }
130
+ getPageCitation(anchorId, request) {
131
+ return JSON.parse(this.wasm.GetPageCitation(
132
+ this.handle,
133
+ anchorId,
134
+ JSON.stringify(request)
135
+ ));
136
+ }
137
+ /** Evaluate optimistic guards without mutating or advancing the version. */
138
+ checkPreconditions(preconditions) {
139
+ return JSON.parse(
140
+ this.wasm.CheckPreconditions(this.handle, JSON.stringify(preconditions))
141
+ );
142
+ }
143
+ /**
144
+ * Guard any synchronous mutation. WASM calls are synchronous and single-threaded, so the
145
+ * check and callback form one uninterrupted client-side operation. Prefer a method's native
146
+ * `preconditions` option where it has one (notably replaceTextRange's match-count guard).
147
+ */
148
+ runWithPreconditions(preconditions, mutation) {
149
+ const checked = this.checkPreconditions(preconditions);
150
+ return checked.success ? mutation() : checked;
151
+ }
152
+ /**
153
+ * Execute synchronous mutations atomically by default. Atomic success is one undo/version
154
+ * unit; any failed or thrown step restores the exact package and history checkpoint.
155
+ */
156
+ executeBatch(steps, mode = "atomic") {
157
+ if (mode !== "atomic" && mode !== "best_effort") {
158
+ throw new RangeError(`unknown mutation batch mode: ${String(mode)}`);
159
+ }
160
+ const baseVersion = this.getVersion();
161
+ const observationWarnings = [];
162
+ const inspect = (label, read, fallback) => {
163
+ try {
164
+ return read();
165
+ } catch (error) {
166
+ observationWarnings.push(`${label} unavailable: ${error instanceof Error ? error.message : String(error)}`);
167
+ return fallback;
168
+ }
169
+ };
170
+ const beforeRevisions = inspect("Revision delta inspection", () => this.listRevisions(), []);
171
+ const beforeComments = inspect("Comment delta inspection", () => this.listComments(), []);
172
+ const beforeAnnotations = inspect("Annotation delta inspection", () => this.listAnnotations(), []);
173
+ const complete = (result) => {
174
+ try {
175
+ const revisionChanges = mutationBatchChangeSet(
176
+ beforeRevisions,
177
+ inspect("Revision delta inspection", () => this.listRevisions(), beforeRevisions),
178
+ (revision) => revision.id
179
+ );
180
+ const commentChanges = mutationBatchChangeSet(
181
+ beforeComments,
182
+ inspect("Comment delta inspection", () => this.listComments(), beforeComments),
183
+ (comment) => comment.anchorId
184
+ );
185
+ const annotationChanges = mutationBatchChangeSet(
186
+ beforeAnnotations,
187
+ inspect("Annotation delta inspection", () => this.listAnnotations(), beforeAnnotations),
188
+ (annotation) => annotation.id ?? ""
189
+ );
190
+ const resultVersion = inspect(
191
+ "Result version inspection",
192
+ () => this.getVersion(),
193
+ baseVersion
194
+ );
195
+ const warnings = [...observationWarnings];
196
+ if ([...revisionChanges.added, ...revisionChanges.modified].some((revision) => revision.date !== void 0 && revision.date !== null)) {
197
+ warnings.push("Tracked-revision date attributes may use the execution clock; compare revision ids, authors, types, text, and anchors across separate executions.");
198
+ }
199
+ if ([...commentChanges.added, ...commentChanges.modified].some((comment) => comment.date !== void 0 && comment.date !== null)) {
200
+ warnings.push("Comment date attributes may be generated from the execution clock; supply dates explicitly when byte-identical replay is required.");
201
+ }
202
+ if (annotationChanges.added.some((annotation) => annotation.created !== void 0 && annotation.created !== null)) {
203
+ warnings.push("Auto-generated annotation ids or creation timestamps are execution metadata; supply id and created explicitly when byte-identical replay is required.");
204
+ }
205
+ if (result.steps.some((step) => step.results.some((edit) => edit.created.length > 0))) {
206
+ warnings.push("Created anchors and related OOXML ids may be generated independently on replay; preview/apply equivalence is semantic and packageHash or anchor ids may differ.");
207
+ }
208
+ if (mode === "best_effort" && !result.success) {
209
+ warnings.push("Best-effort execution retains every successful step despite later failures.");
210
+ }
211
+ let packageHash = null;
212
+ if (!this.wasm.GetPackageContentHash) {
213
+ warnings.push("This WASM bundle predates package equivalence hashes; packageHash is unavailable.");
214
+ } else {
215
+ try {
216
+ packageHash = this.wasm.GetPackageContentHash(this.handle);
217
+ } catch (error) {
218
+ warnings.push(`Package equivalence hash unavailable: ${error instanceof Error ? error.message : String(error)}`);
219
+ }
220
+ }
221
+ return {
222
+ ...result,
223
+ preview: false,
224
+ baseVersion,
225
+ resultVersion,
226
+ packageHash,
227
+ revisionChanges,
228
+ commentChanges,
229
+ annotationChanges,
230
+ warnings,
231
+ html: null
232
+ };
233
+ } catch (error) {
234
+ return {
235
+ ...result,
236
+ preview: false,
237
+ baseVersion,
238
+ resultVersion: inspect("Result version inspection", () => this.getVersion(), baseVersion),
239
+ packageHash: null,
240
+ revisionChanges: { added: [], removed: [], modified: [] },
241
+ commentChanges: { added: [], removed: [], modified: [] },
242
+ annotationChanges: { added: [], removed: [], modified: [] },
243
+ warnings: [
244
+ ...observationWarnings,
245
+ `Batch receipt enrichment unavailable: ${error instanceof Error ? error.message : String(error)}`
246
+ ],
247
+ html: null
248
+ };
249
+ }
250
+ };
251
+ const internalFailure = (value) => ({
252
+ success: false,
253
+ error: { code: "internal_error", message: value instanceof Error ? value.message : String(value) },
254
+ created: [],
255
+ removed: [],
256
+ modified: []
257
+ });
258
+ const run = (step) => {
259
+ try {
260
+ const value = step.mutation();
261
+ const results = Array.isArray(value) ? value : [value];
262
+ if (results.some((result) => result === null || typeof result !== "object" || typeof result.success !== "boolean")) {
263
+ return [internalFailure("batch mutation returned invalid edit results")];
264
+ }
265
+ return results;
266
+ } catch (error) {
267
+ return [internalFailure(error)];
268
+ }
269
+ };
270
+ const failureOf = (step, rolledBack) => ({
271
+ index: step.index,
272
+ tool: step.tool,
273
+ action: step.action,
274
+ error: step.results.find((result) => !result.success)?.error ?? { code: "internal_error", message: "batch step failed without an error" },
275
+ rolledBack
276
+ });
277
+ const preflightOne = (step) => {
278
+ try {
279
+ return step.preflight?.();
280
+ } catch (error) {
281
+ return internalFailure(error).error;
282
+ }
283
+ };
284
+ if (mode === "atomic") {
285
+ const preflight = steps.map(preflightOne);
286
+ const failedPreflight = preflight.findIndex((error) => error !== void 0);
287
+ if (failedPreflight >= 0) {
288
+ const source = steps[failedPreflight];
289
+ const failed2 = {
290
+ index: failedPreflight,
291
+ tool: source.tool,
292
+ action: source.action,
293
+ success: false,
294
+ rolledBack: true,
295
+ results: [{ success: false, error: preflight[failedPreflight], created: [], removed: [], modified: [] }]
296
+ };
297
+ return complete({
298
+ mode,
299
+ status: "failed",
300
+ success: false,
301
+ rolledBack: true,
302
+ steps: [failed2],
303
+ failure: failureOf(failed2, true)
304
+ });
305
+ }
306
+ const transaction = this.wasm.BeginTransaction(this.handle);
307
+ const completed2 = [];
308
+ try {
309
+ for (let index = 0; index < steps.length; index++) {
310
+ const source = steps[index];
311
+ const results = run(source);
312
+ const step = {
313
+ index,
314
+ tool: source.tool,
315
+ action: source.action,
316
+ success: results.every((result) => result.success),
317
+ rolledBack: false,
318
+ results
319
+ };
320
+ completed2.push(step);
321
+ if (!step.success) {
322
+ this.wasm.RollbackTransaction(transaction);
323
+ const rolledBack = completed2.map((value) => ({ ...value, rolledBack: true }));
324
+ const failed2 = rolledBack[rolledBack.length - 1];
325
+ return complete({
326
+ mode,
327
+ status: "failed",
328
+ success: false,
329
+ rolledBack: true,
330
+ steps: rolledBack,
331
+ failure: failureOf(failed2, true)
332
+ });
333
+ }
334
+ }
335
+ this.wasm.CommitTransaction(transaction);
336
+ return complete({ mode, status: "ok", success: true, rolledBack: false, steps: completed2 });
337
+ } catch (error) {
338
+ try {
339
+ this.wasm.RollbackTransaction(transaction);
340
+ } catch {
341
+ }
342
+ throw error;
343
+ }
344
+ }
345
+ const completed = steps.map((source, index) => {
346
+ const preflight = preflightOne(source);
347
+ const results = preflight ? [{ success: false, error: preflight, created: [], removed: [], modified: [] }] : run(source);
348
+ return {
349
+ index,
350
+ tool: source.tool,
351
+ action: source.action,
352
+ success: results.every((result) => result.success),
353
+ rolledBack: false,
354
+ results
355
+ };
356
+ });
357
+ const failed = completed.find((step) => !step.success);
358
+ return complete({
359
+ mode,
360
+ status: failed ? completed.some((step) => step.success) ? "partial" : "failed" : "ok",
361
+ success: failed === void 0,
362
+ rolledBack: false,
363
+ steps: completed,
364
+ failure: failed ? failureOf(failed, false) : void 0
365
+ });
366
+ }
367
+ /**
368
+ * Execute the same callback batch algorithm against a complete isolated package clone.
369
+ * Callbacks receive the shadow session explicitly; mutate that argument. The live session's
370
+ * package, caches, version, configuration, and undo/redo history are never execution targets.
371
+ */
372
+ previewBatch(steps, mode = "atomic", options) {
373
+ if (mode !== "atomic" && mode !== "best_effort") {
374
+ throw new RangeError(`unknown mutation batch mode: ${String(mode)}`);
375
+ }
376
+ const htmlMode = options?.html ?? "none";
377
+ if (htmlMode !== "none" && htmlMode !== "scoped" && htmlMode !== "full") {
378
+ throw new RangeError(`unknown preview HTML mode: ${String(htmlMode)}`);
379
+ }
380
+ if (!this.wasm.OpenPreviewSession) {
381
+ throw new Error("This WASM bundle does not support isolated mutation previews.");
382
+ }
383
+ const shadow = new _DocxSession(this.wasm.OpenPreviewSession(this.handle), this.wasm);
384
+ try {
385
+ const result = shadow.executeBatch(
386
+ steps.map((step) => ({
387
+ tool: step.tool,
388
+ action: step.action,
389
+ mutation: () => step.mutation(shadow),
390
+ preflight: step.preflight ? () => step.preflight(shadow) : void 0
391
+ })),
392
+ mode
393
+ );
394
+ const warnings = [...result.warnings];
395
+ let html = null;
396
+ const unwrapRendered = (rendered) => {
397
+ if (rendered.trimStart().startsWith("{")) {
398
+ const envelope = JSON.parse(rendered);
399
+ if (envelope.error) {
400
+ warnings.push(`Preview HTML could not be generated: ${envelope.error}`);
401
+ return null;
402
+ }
403
+ }
404
+ return rendered;
405
+ };
406
+ const legacyProfileWarning = "This WASM bundle predates the shared preview HTML profile; preview HTML omits comments, annotations and headers/footers and may differ from other surfaces.";
407
+ try {
408
+ if (htmlMode === "scoped") {
409
+ if (!options?.htmlAnchorId) {
410
+ warnings.push("Scoped HTML was requested without htmlAnchorId; no HTML was generated.");
411
+ } else if (this.wasm.RenderPreviewBlockHtml) {
412
+ html = unwrapRendered(
413
+ this.wasm.RenderPreviewBlockHtml(shadow.handle, options.htmlAnchorId)
414
+ );
415
+ } else {
416
+ warnings.push(legacyProfileWarning);
417
+ html = shadow.renderBlock(options.htmlAnchorId);
418
+ }
419
+ } else if (htmlMode === "full") {
420
+ if (this.wasm.RenderPreviewHtml) {
421
+ html = unwrapRendered(this.wasm.RenderPreviewHtml(shadow.handle));
422
+ } else {
423
+ warnings.push(legacyProfileWarning);
424
+ html = unwrapRendered(this.wasm.RenderHtmlForReview ? this.wasm.RenderHtmlForReview(shadow.handle, "docx-", false, false, 1, true) : this.wasm.RenderHtml(shadow.handle, "docx-", false, false, 1));
425
+ }
426
+ }
427
+ } catch (error) {
428
+ warnings.push(`Preview HTML could not be generated: ${error instanceof Error ? error.message : String(error)}`);
429
+ }
430
+ return { ...result, preview: true, warnings, html };
431
+ } finally {
432
+ shadow.close();
433
+ }
434
+ }
66
435
  /**
67
436
  * Project a slice of the document keyed off an anchor — useful for showing
68
437
  * one section to an LLM at a time without paying the cost of projecting the
@@ -78,9 +447,9 @@ var DocxodusSession = (() => {
78
447
  *
79
448
  * @see docs/architecture/docx_mutation_api.md
80
449
  */
81
- projectAnchor(anchorId, depth = 2 /* SubtreeAndFollowingSiblings */) {
450
+ projectAnchor(anchorId, depth = 2 /* SubtreeAndFollowingSiblings */, citation) {
82
451
  return JSON.parse(
83
- this.wasm.ProjectAnchor(this.handle, anchorId, depth)
452
+ citation ? this.wasm.ProjectAnchorWithCitations(this.handle, anchorId, depth, JSON.stringify(citation)) : this.wasm.ProjectAnchor(this.handle, anchorId, depth)
84
453
  );
85
454
  }
86
455
  /**
@@ -104,11 +473,21 @@ var DocxodusSession = (() => {
104
473
  return html;
105
474
  }
106
475
  // ─── Tier A: text CRUD ───────────────────────────────────────────────
107
- replaceText(anchorId, markdown) {
108
- return JSON.parse(this.wasm.ReplaceText(this.handle, anchorId, markdown));
109
- }
110
- deleteBlock(anchorId) {
111
- return JSON.parse(this.wasm.DeleteBlock(this.handle, anchorId));
476
+ replaceText(anchorId, markdown, preconditions) {
477
+ const apply = () => JSON.parse(
478
+ this.wasm.ReplaceText(this.handle, anchorId, markdown)
479
+ );
480
+ return preconditions ? this.runWithPreconditions(
481
+ { ...preconditions, anchorId: preconditions.anchorId ?? anchorId },
482
+ apply
483
+ ) : apply();
484
+ }
485
+ deleteBlock(anchorId, preconditions) {
486
+ const apply = () => JSON.parse(this.wasm.DeleteBlock(this.handle, anchorId));
487
+ return preconditions ? this.runWithPreconditions(
488
+ { ...preconditions, anchorId: preconditions.anchorId ?? anchorId },
489
+ apply
490
+ ) : apply();
112
491
  }
113
492
  /** Reorder one top-level paragraph/heading/list/table block relative to another. */
114
493
  moveBlock(sourceAnchorId, targetAnchorId, position) {
@@ -163,7 +542,7 @@ var DocxodusSession = (() => {
163
542
  }
164
543
  /**
165
544
  * Insert a `rows`×`cols` table before/after the block. `options` controls borders, row-major
166
- * cell markdown, and cell alignment. The returned `EditResult.created` lists the cell-paragraph
545
+ * cell markdown, and cell alignment. The returned `EditResult.created` lists canonical `tc`
167
546
  * anchors (row-major), so each cell can then be addressed to fill/format.
168
547
  */
169
548
  insertTable(anchorId, position, rows, cols, options) {
@@ -172,10 +551,26 @@ var DocxodusSession = (() => {
172
551
  this.wasm.InsertTable(this.handle, anchorId, position, rows, cols, optionsJson)
173
552
  );
174
553
  }
554
+ /** Resolve a canonical `tbl` anchor to explicit table/row/column/cell identities. */
555
+ getTableMetadata(tableAnchorId) {
556
+ return JSON.parse(this.wasm.GetTableMetadata(this.handle, tableAnchorId));
557
+ }
558
+ /** Resolve a canonical `tc` anchor to its zero-based table-grid coordinate and spans. */
559
+ resolveTableCellAnchor(cellAnchorId) {
560
+ return JSON.parse(
561
+ this.wasm.ResolveTableCellAnchor(this.handle, cellAnchorId)
562
+ );
563
+ }
564
+ /** Resolve a zero-based table-grid coordinate to the physical `tc` covering it. */
565
+ resolveTableCellCoordinate(tableAnchorId, rowIndex, columnIndex) {
566
+ return JSON.parse(
567
+ this.wasm.ResolveTableCellCoordinate(this.handle, tableAnchorId, rowIndex, columnIndex)
568
+ );
569
+ }
175
570
  /**
176
- * Table row/column editing, addressed by a cell-paragraph anchor (e.g. one returned from
177
- * {@link insertTable}'s `created`). Insert clones the reference row/column's widths and starts
178
- * empty (`created` lists the new cell-paragraph anchors); delete of the last row/column removes
571
+ * Table row/column editing, addressed by the canonical `tc` anchor returned from
572
+ * {@link insertTable}'s `created` or table metadata. Insert clones the reference row/column's
573
+ * widths and starts empty (`created` lists new `tc` anchors); delete of the last row/column removes
179
574
  * the whole table. All four are grid-aware: inserting across a merge extends it, deleting
180
575
  * through one narrows it, and deleting a vertical merge's lead row promotes the next row to
181
576
  * carry it — the grid is never left ragged.
@@ -215,7 +610,7 @@ var DocxodusSession = (() => {
215
610
  return JSON.parse(this.wasm.UnmergeCells(this.handle, cellAnchorId));
216
611
  }
217
612
  /**
218
- * Table styling, addressed by a cell-paragraph anchor — the post-insert counterpart of
613
+ * Table styling, addressed by a canonical `tc` anchor — the post-insert counterpart of
219
614
  * {@link insertTable}'s options (issue #315 Stage A). `setColumnWidths` retunes `w:tblGrid` +
220
615
  * every row's cell width (one positive twip value per column) and pins the table to fixed
221
616
  * layout, exactly as inserting with explicit `columnWidths` would.
@@ -255,6 +650,19 @@ var DocxodusSession = (() => {
255
650
  this.wasm.SetRepeatHeaderRow(this.handle, cellAnchorId, repeat)
256
651
  );
257
652
  }
653
+ /** Apply row layout options to the row containing the canonical cell anchor. */
654
+ setTableRowOptions(cellAnchorId, options) {
655
+ return JSON.parse(
656
+ this.wasm.SetTableRowOptions(
657
+ this.handle,
658
+ cellAnchorId,
659
+ options.repeatHeader ?? null,
660
+ options.allowBreakAcrossPages ?? null,
661
+ options.heightTwips ?? null,
662
+ options.heightRule ?? "atLeast"
663
+ )
664
+ );
665
+ }
258
666
  // ─── Headers / footers / page numbers ────────────────────────────────
259
667
  /**
260
668
  * Set the running header story for the section that owns `anchorId` (any body block in that
@@ -305,6 +713,58 @@ var DocxodusSession = (() => {
305
713
  this.wasm.SetPageNumbering(this.handle, anchorId, JSON.stringify(op))
306
714
  );
307
715
  }
716
+ /**
717
+ * Insert a **table of contents** before or after `anchorId` (issue #607). The field is written
718
+ * dirty and the document asks for a field update on open, so Word paginates and fills the table
719
+ * itself — nothing here ships a cached result that is stale the moment anything above it moves.
720
+ *
721
+ * The table is wrapped in the `w:sdt` content control Word puts around one, which is what gives
722
+ * it the *Update Table* control in Word's UI. Word's `TOCHeading` and `TOC1` styles are
723
+ * find-or-created; a document that already defines them keeps its own.
724
+ *
725
+ * Body anchors only, and refused under tracked-change recording: a generated table is regenerated
726
+ * wholesale on every field update, so there is no reversible way to redline it.
727
+ */
728
+ insertTableOfContents(anchorId, pos = "before", options) {
729
+ return JSON.parse(
730
+ this.wasm.InsertTableOfContents(
731
+ this.handle,
732
+ anchorId,
733
+ pos,
734
+ options ? JSON.stringify(options) : ""
735
+ )
736
+ );
737
+ }
738
+ /**
739
+ * Insert a **table of figures** — the captions carrying `options.captionLabel` and their page
740
+ * numbers. Same field mechanics as {@link insertTableOfContents}; Word writes this one as a bare
741
+ * paragraph rather than inside a content control, so this does too.
742
+ */
743
+ insertTableOfFigures(anchorId, pos = "before", options) {
744
+ return JSON.parse(
745
+ this.wasm.InsertTableOfFigures(
746
+ this.handle,
747
+ anchorId,
748
+ pos,
749
+ options ? JSON.stringify(options) : ""
750
+ )
751
+ );
752
+ }
753
+ /**
754
+ * Insert a **table of authorities** — the cases, statutes or other authorities marked in the
755
+ * document, grouped by `options.category`. The table lists entries the document has MARKED with
756
+ * `TA` fields; a document with no marked citations produces a table that is correct and empty.
757
+ */
758
+ insertTableOfAuthorities(anchorId, pos = "before", options) {
759
+ return JSON.parse(
760
+ this.wasm.InsertTableOfAuthorities(
761
+ this.handle,
762
+ anchorId,
763
+ pos,
764
+ options ? JSON.stringify(options) : ""
765
+ )
766
+ );
767
+ }
308
768
  /**
309
769
  * Remove the section's page-numbering start/format: it reverts to continuing the previous
310
770
  * section's numbering in Word's default `1, 2, 3`. Chapter-numbering attributes
@@ -349,6 +809,25 @@ var DocxodusSession = (() => {
349
809
  this.wasm.InsertFootnote(this.handle, anchorId, characterOffset, markdown)
350
810
  );
351
811
  }
812
+ /**
813
+ * Insert a Word-faithful internal cross-reference — a `REF` field targeting an existing
814
+ * bookmark — at a character offset (issue #545). The field carries a cached result run
815
+ * (the bookmarked text, or the target's auto-number under `referenceNumber`), so
816
+ * renderers that do not recompute fields show a faithful snapshot and Word updates it
817
+ * like a hand-authored cross-reference. A missing or incoherent bookmark fails with
818
+ * `missing_bookmark_target`.
819
+ */
820
+ insertCrossReference(anchorId, characterOffset, bookmarkName, options) {
821
+ return JSON.parse(
822
+ this.wasm.InsertCrossReference(
823
+ this.handle,
824
+ anchorId,
825
+ characterOffset,
826
+ bookmarkName,
827
+ options ? JSON.stringify(options) : ""
828
+ )
829
+ );
830
+ }
352
831
  /** Create an endnote — see {@link insertFootnote}; writes the endnotes part and a
353
832
  * `w:endnoteReference`, and the created definition anchor has kind `en`. */
354
833
  insertEndnote(anchorId, characterOffset, markdown) {
@@ -440,6 +919,170 @@ var DocxodusSession = (() => {
440
919
  listComments() {
441
920
  return JSON.parse(this.wasm.ListComments(this.handle));
442
921
  }
922
+ listHyperlinks(scopes = 63 /* All */) {
923
+ return JSON.parse(this.wasm.ListHyperlinks(this.handle, scopes));
924
+ }
925
+ addHyperlink(anchorId, span, kind, target) {
926
+ return JSON.parse(this.wasm.AddHyperlink(
927
+ this.handle,
928
+ anchorId,
929
+ span.start,
930
+ span.length,
931
+ kind,
932
+ target
933
+ ));
934
+ }
935
+ updateHyperlink(hyperlinkId, kind, target) {
936
+ return JSON.parse(this.wasm.UpdateHyperlink(this.handle, hyperlinkId, kind, target));
937
+ }
938
+ removeHyperlink(hyperlinkId) {
939
+ return JSON.parse(this.wasm.RemoveHyperlink(this.handle, hyperlinkId));
940
+ }
941
+ /** Versioned operational facts for native image inspection/mutation in this runtime. */
942
+ getImageCapabilities() {
943
+ return JSON.parse(this.wasm.GetImageCapabilities());
944
+ }
945
+ listImages(scopes = 63 /* All */) {
946
+ return JSON.parse(this.wasm.ListImages(this.handle, scopes));
947
+ }
948
+ insertImage(anchorId, characterOffset, bytes, options = {}) {
949
+ return JSON.parse(this.wasm.InsertImage(
950
+ this.handle,
951
+ anchorId,
952
+ characterOffset,
953
+ imageBytesToBase64(bytes),
954
+ JSON.stringify(options)
955
+ ));
956
+ }
957
+ replaceImage(imageId, bytes) {
958
+ return JSON.parse(this.wasm.ReplaceImage(
959
+ this.handle,
960
+ imageId,
961
+ imageBytesToBase64(bytes)
962
+ ));
963
+ }
964
+ setImageDimensions(imageId, dimensions) {
965
+ return JSON.parse(this.wasm.SetImageDimensions(
966
+ this.handle,
967
+ imageId,
968
+ JSON.stringify(dimensions)
969
+ ));
970
+ }
971
+ setImageMetadata(imageId, altText, title) {
972
+ return JSON.parse(this.wasm.SetImageMetadata(
973
+ this.handle,
974
+ imageId,
975
+ altText,
976
+ title
977
+ ));
978
+ }
979
+ setImageFloatingLayout(imageId, layout) {
980
+ return JSON.parse(this.wasm.SetImageFloatingLayout(
981
+ this.handle,
982
+ imageId,
983
+ JSON.stringify(layout)
984
+ ));
985
+ }
986
+ removeImage(imageId) {
987
+ return JSON.parse(this.wasm.RemoveImage(this.handle, imageId));
988
+ }
989
+ /** Native Word structured-document tags, in outer-before-inner story order. */
990
+ listContentControls(scopes = 63 /* All */) {
991
+ return JSON.parse(this.wasm.ListContentControls(this.handle, scopes));
992
+ }
993
+ fillContentControlText(anchorId, text, options = {}) {
994
+ return JSON.parse(this.wasm.FillContentControlText(
995
+ this.handle,
996
+ anchorId,
997
+ text,
998
+ JSON.stringify(options)
999
+ ));
1000
+ }
1001
+ fillContentControlRichText(anchorId, markdown, options = {}) {
1002
+ return JSON.parse(this.wasm.FillContentControlRichText(
1003
+ this.handle,
1004
+ anchorId,
1005
+ markdown,
1006
+ JSON.stringify(options)
1007
+ ));
1008
+ }
1009
+ setContentControlChecked(anchorId, isChecked, options = {}) {
1010
+ return JSON.parse(this.wasm.SetContentControlChecked(
1011
+ this.handle,
1012
+ anchorId,
1013
+ isChecked,
1014
+ JSON.stringify(options)
1015
+ ));
1016
+ }
1017
+ setContentControlDate(anchorId, value, displayText, options = {}) {
1018
+ const timestamp = value instanceof Date ? value.toISOString() : value;
1019
+ return JSON.parse(this.wasm.SetContentControlDate(
1020
+ this.handle,
1021
+ anchorId,
1022
+ timestamp,
1023
+ displayText ?? null,
1024
+ JSON.stringify(options)
1025
+ ));
1026
+ }
1027
+ selectContentControlItem(anchorId, value, options = {}) {
1028
+ return JSON.parse(this.wasm.SelectContentControlItem(
1029
+ this.handle,
1030
+ anchorId,
1031
+ value,
1032
+ JSON.stringify(options)
1033
+ ));
1034
+ }
1035
+ fillContentControlPicture(anchorId, bytes, options = {}) {
1036
+ return JSON.parse(this.wasm.FillContentControlPicture(
1037
+ this.handle,
1038
+ anchorId,
1039
+ imageBytesToBase64(bytes),
1040
+ JSON.stringify(options)
1041
+ ));
1042
+ }
1043
+ addRepeatingSectionItem(sectionAnchorId, afterItemAnchorId, options = {}) {
1044
+ return JSON.parse(this.wasm.AddRepeatingSectionItem(
1045
+ this.handle,
1046
+ sectionAnchorId,
1047
+ afterItemAnchorId ?? "",
1048
+ JSON.stringify(options)
1049
+ ));
1050
+ }
1051
+ removeRepeatingSectionItem(itemAnchorId) {
1052
+ return JSON.parse(this.wasm.RemoveRepeatingSectionItem(
1053
+ this.handle,
1054
+ itemAnchorId
1055
+ ));
1056
+ }
1057
+ listBookmarks(scopes = 63 /* All */) {
1058
+ return JSON.parse(this.wasm.ListBookmarks(this.handle, scopes));
1059
+ }
1060
+ addBookmark(name, range) {
1061
+ return JSON.parse(this.wasm.AddBookmark(
1062
+ this.handle,
1063
+ name,
1064
+ range.startAnchorId,
1065
+ range.startOffset,
1066
+ range.endAnchorId,
1067
+ range.endOffset
1068
+ ));
1069
+ }
1070
+ renameBookmark(name, newName) {
1071
+ return JSON.parse(this.wasm.RenameBookmark(this.handle, name, newName));
1072
+ }
1073
+ moveBookmark(name, range) {
1074
+ return JSON.parse(this.wasm.MoveBookmark(
1075
+ this.handle,
1076
+ name,
1077
+ range.startAnchorId,
1078
+ range.startOffset,
1079
+ range.endAnchorId,
1080
+ range.endOffset
1081
+ ));
1082
+ }
1083
+ removeBookmark(name) {
1084
+ return JSON.parse(this.wasm.RemoveBookmark(this.handle, name));
1085
+ }
443
1086
  // ─── Tracked revisions (issue #318) ──────────────────────────────────
444
1087
  /** Markup-native tracked-revision listing, in document order across body, headers,
445
1088
  * footers, footnotes, and endnotes. Ids are stable while the underlying markup
@@ -460,6 +1103,22 @@ var DocxodusSession = (() => {
460
1103
  rejectRevision(revisionId) {
461
1104
  return JSON.parse(this.wasm.RejectRevision(this.handle, revisionId));
462
1105
  }
1106
+ /**
1107
+ * Accept every live revision as one undoable session mutation.
1108
+ *
1109
+ * Fails closed: an unsupported, malformed, or ambiguous registry entry aborts the whole
1110
+ * operation (`revisionUnsupported`/`revisionMalformed`/`revisionAmbiguous`) and nothing is
1111
+ * mutated. There is no force mode — call {@link listRevisions} and read each entry's
1112
+ * `diagnostic` to see what blocks it.
1113
+ */
1114
+ acceptAllRevisions() {
1115
+ return JSON.parse(this.wasm.AcceptAllRevisions(this.handle));
1116
+ }
1117
+ /** Reject every live revision as one undoable session mutation. Fails closed exactly like
1118
+ * {@link acceptAllRevisions}. */
1119
+ rejectAllRevisions() {
1120
+ return JSON.parse(this.wasm.RejectAllRevisions(this.handle));
1121
+ }
463
1122
  // ─── Tier C: formatting ──────────────────────────────────────────────
464
1123
  applyFormat(anchorId, span, op) {
465
1124
  const spanJson = span ? JSON.stringify(span) : "";
@@ -565,6 +1224,10 @@ var DocxodusSession = (() => {
565
1224
  * replaces it with `replace`, preserving the surrounding run formatting that
566
1225
  * the match didn't touch. Returns one `EditResult` per attempted match.
567
1226
  *
1227
+ * A `find` that matches nothing fails with a single `text_not_found` result
1228
+ * naming the anchor and the needle — never an empty array. Pass
1229
+ * `expectedMatchCount: 0` to assert absence as a successful no-op instead.
1230
+ *
568
1231
  * Run-formatting contract: the replacement text inherits the formatting of
569
1232
  * the FIRST run the match spanned. Middle/trailing runs keep their `w:rPr`
570
1233
  * but lose the slice of text the match consumed.
@@ -741,10 +1404,16 @@ var DocxodusSession = (() => {
741
1404
  *
742
1405
  * @see docs/architecture/docx_mutation_api.md#findplaceholders
743
1406
  */
744
- findPlaceholders(kinds = PlaceholderKinds.All, scope = 1, contextChars = 80, boundary = ContextBoundary.Char) {
745
- return JSON.parse(
746
- this.wasm.FindPlaceholders(this.handle, kinds, scope, contextChars, boundary)
747
- );
1407
+ findPlaceholders(kinds = PlaceholderKinds.All, scope = 1, contextChars = 80, boundary = ContextBoundary.Char, citation) {
1408
+ const json = citation ? this.wasm.FindPlaceholdersWithCitations(
1409
+ this.handle,
1410
+ kinds,
1411
+ scope,
1412
+ contextChars,
1413
+ boundary,
1414
+ JSON.stringify(citation)
1415
+ ) : this.wasm.FindPlaceholders(this.handle, kinds, scope, contextChars, boundary);
1416
+ return JSON.parse(json);
748
1417
  }
749
1418
  /**
750
1419
  * Returns a snapshot of edit-state introspection signals — placeholder counts,
@@ -767,6 +1436,23 @@ var DocxodusSession = (() => {
767
1436
  }
768
1437
  return raw;
769
1438
  }
1439
+ /**
1440
+ * Return stable semantic changes from the package opened for this session to
1441
+ * its current state. Requires `captureInitialProjection` (enabled by default).
1442
+ */
1443
+ getSemanticChanges() {
1444
+ return JSON.parse(this.wasm.GetSemanticChanges(this.handle));
1445
+ }
1446
+ /**
1447
+ * Run the default deliverable gate on this session's normal clean-save checkpoint.
1448
+ * With initial projection capture enabled (the default), exact opening bytes are the
1449
+ * baseline used for dispositions and semantic/package deltas.
1450
+ */
1451
+ verifyDeliverable() {
1452
+ return JSON.parse(
1453
+ this.wasm.VerifyDeliverable(this.handle)
1454
+ );
1455
+ }
770
1456
  // ─── Annotation-based anchor discovery (#132) ────────────────────────
771
1457
  /**
772
1458
  * Resolves an annotation's range to the block-level markdown anchors covering
@@ -782,8 +1468,9 @@ var DocxodusSession = (() => {
782
1468
  *
783
1469
  * @see docs/architecture/docx_mutation_api.md#findbyannotation
784
1470
  */
785
- findByAnnotation(annotationId) {
786
- return JSON.parse(this.wasm.FindByAnnotation(this.handle, annotationId));
1471
+ findByAnnotation(annotationId, citation) {
1472
+ const json = citation ? this.wasm.FindByAnnotationWithCitations(this.handle, annotationId, JSON.stringify(citation)) : this.wasm.FindByAnnotation(this.handle, annotationId);
1473
+ return JSON.parse(json);
787
1474
  }
788
1475
  /**
789
1476
  * Finds every annotation whose `labelId` matches and resolves each of their
@@ -792,8 +1479,9 @@ var DocxodusSession = (() => {
792
1479
  * annotations on different paragraphs become three entries). Annotations
793
1480
  * whose bookmark resolves to no anchors are omitted from the result.
794
1481
  */
795
- findByLabel(labelId) {
796
- return JSON.parse(this.wasm.FindByLabel(this.handle, labelId));
1482
+ findByLabel(labelId, citation) {
1483
+ const json = citation ? this.wasm.FindByLabelWithCitations(this.handle, labelId, JSON.stringify(citation)) : this.wasm.FindByLabel(this.handle, labelId);
1484
+ return JSON.parse(json);
797
1485
  }
798
1486
  /**
799
1487
  * Resolves any bookmark in the main document part (Docxodus-managed or
@@ -801,8 +1489,9 @@ var DocxodusSession = (() => {
801
1489
  * order. Empty when the bookmark name is unknown. Use this for raw bookmark
802
1490
  * names that didn't come from the annotation system.
803
1491
  */
804
- findByBookmark(bookmarkName) {
805
- return JSON.parse(this.wasm.FindByBookmark(this.handle, bookmarkName));
1492
+ findByBookmark(bookmarkName, citation) {
1493
+ const json = citation ? this.wasm.FindByBookmarkWithCitations(this.handle, bookmarkName, JSON.stringify(citation)) : this.wasm.FindByBookmark(this.handle, bookmarkName);
1494
+ return JSON.parse(json);
806
1495
  }
807
1496
  // ─── Text/kind-based anchor discovery (#171) ─────────────────────────
808
1497
  /**
@@ -847,15 +1536,25 @@ var DocxodusSession = (() => {
847
1536
  );
848
1537
  }
849
1538
  /**
850
- * Return every anchor of the given `kind` (`"p"`, `"h"`, `"li"`, `"tbl"`,
851
- * `"row"`, `"cell"`, …), in document order. Reads the projection's anchor
1539
+ * Return every anchor of the given `kind` — one of `"p"`, `"h"`, `"li"`,
1540
+ * `"tbl"`, `"tr"`, `"tc"`, `"col"`, `"sdt"`, `"sec"`, `"fn"`, `"en"`,
1541
+ * `"cmt"`, `"unk"` — in document order. Matching is an exact string comparison;
1542
+ * in particular the row and cell tokens are `"tr"` and `"tc"`, never
1543
+ * `"row"`/`"cell"`. Unknown strings are not parsed or rejected: they simply
1544
+ * return an empty array. `"img"` and `"drw"` likewise return empty because the
1545
+ * projection never assigns those reserved kinds; address images through the
1546
+ * image surface instead. Reads the projection's anchor
852
1547
  * index directly — no text scan. Pass `scope` (e.g. `"body"`) to restrict to
853
1548
  * a single part; omit it to span all scopes.
854
1549
  */
855
- findByKind(kind, scope) {
856
- return JSON.parse(
857
- this.wasm.FindByKind(this.handle, kind, scope ?? "")
858
- );
1550
+ findByKind(kind, scope, citation) {
1551
+ const json = citation ? this.wasm.FindByKindWithCitations(
1552
+ this.handle,
1553
+ kind,
1554
+ scope ?? "",
1555
+ JSON.stringify(citation)
1556
+ ) : this.wasm.FindByKind(this.handle, kind, scope ?? "");
1557
+ return JSON.parse(json);
859
1558
  }
860
1559
  /**
861
1560
  * Look up a single anchor's preview info — `{ id, kind, scope, textPreview }`.
@@ -909,6 +1608,18 @@ var DocxodusSession = (() => {
909
1608
  const raw = this.wasm.GetSectionInfo(this.handle, anchorId);
910
1609
  return JSON.parse(raw);
911
1610
  }
1611
+ /** Enumerate the document's explicit style catalog with resolved high-signal properties. */
1612
+ listStyles() {
1613
+ return JSON.parse(this.wasm.ListStyles(this.handle));
1614
+ }
1615
+ /** Inspect direct and effective paragraph/run formatting for one paragraph anchor. */
1616
+ getFormatting(anchorId) {
1617
+ return JSON.parse(this.wasm.GetFormatting(this.handle, anchorId));
1618
+ }
1619
+ /** Enumerate text-bearing runs as mutation-compatible anchor/span pairs. */
1620
+ listInlineSpans(anchorId) {
1621
+ return JSON.parse(this.wasm.ListInlineSpans(this.handle, anchorId));
1622
+ }
912
1623
  /**
913
1624
  * Enumerates every annotation persisted in the document. Lets an agent prime
914
1625
  * itself with "here are the labeled regions you can target" before committing
@@ -973,6 +1684,13 @@ var DocxodusSession = (() => {
973
1684
  this.close();
974
1685
  }
975
1686
  };
1687
+ function imageBytesToBase64(bytes) {
1688
+ let binary = "";
1689
+ for (let offset = 0; offset < bytes.length; offset += 32768) {
1690
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
1691
+ }
1692
+ return globalThis.btoa(binary);
1693
+ }
976
1694
  function openDocxSession(bytes, wasmExports, settings) {
977
1695
  const bridge = wasmExports.DocxSessionBridge;
978
1696
  const handle = bridge.OpenSession(bytes, settings ? JSON.stringify(settings) : "");