@beyondwork/docx-react-component 1.0.129 → 1.0.131

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 (102) hide show
  1. package/dist/api/public-types.cjs +595 -18
  2. package/dist/api/public-types.d.cts +1 -1
  3. package/dist/api/public-types.d.ts +1 -1
  4. package/dist/api/public-types.js +4 -4
  5. package/dist/api/v3.cjs +1164 -332
  6. package/dist/api/v3.d.cts +2 -2
  7. package/dist/api/v3.d.ts +2 -2
  8. package/dist/api/v3.js +13 -13
  9. package/dist/{chunk-OTQIW2TC.js → chunk-35RHOE6I.js} +105 -4
  10. package/dist/{chunk-PGKUJZXV.js → chunk-3YCQM2RV.js} +6 -6
  11. package/dist/{chunk-JJGVE5J7.js → chunk-4YCWECLZ.js} +1 -1
  12. package/dist/{chunk-EZKJXIPH.js → chunk-6TBLDBCL.js} +1 -1
  13. package/dist/{chunk-SKPTKQHF.js → chunk-7G5GR3VV.js} +122 -23
  14. package/dist/{chunk-HUIHBBAQ.js → chunk-A3GSNB4G.js} +17 -6
  15. package/dist/{chunk-5DGKFNQT.js → chunk-A66ZVUAT.js} +150 -1
  16. package/dist/{chunk-YIYM4ZAP.js → chunk-CI2TD3T4.js} +1 -1
  17. package/dist/{chunk-63FYIGCT.js → chunk-DGA7M77X.js} +2 -2
  18. package/dist/{chunk-EB6M3GE6.js → chunk-FM4K4XFJ.js} +100 -97
  19. package/dist/{chunk-VNLDQJ47.js → chunk-HYHCRMR7.js} +1 -1
  20. package/dist/{chunk-DJU2W4E4.js → chunk-KNHMXKC6.js} +2 -2
  21. package/dist/{chunk-Q3QYGKFE.js → chunk-M7YRJX6V.js} +10 -21
  22. package/dist/{chunk-KFCQYZXR.js → chunk-OVLZQ6FZ.js} +61 -0
  23. package/dist/{chunk-W34X3KBR.js → chunk-PHMWH23E.js} +1 -1
  24. package/dist/{chunk-DDN2AIGE.js → chunk-Q7Y57KOK.js} +2 -2
  25. package/dist/{chunk-LJH64PV3.js → chunk-QXKQPUOM.js} +3 -3
  26. package/dist/{chunk-CX42VC67.js → chunk-SYQWQ6FE.js} +1 -1
  27. package/dist/{chunk-5DSHUYSY.js → chunk-T5YYFDZB.js} +1 -1
  28. package/dist/{chunk-RMRTQGW3.js → chunk-THVM6EP5.js} +371 -13
  29. package/dist/{chunk-XMHSGPLN.js → chunk-VRKK2CSZ.js} +111 -90
  30. package/dist/{chunk-OL2UEHRP.js → chunk-WUDSNHWF.js} +1 -1
  31. package/dist/{chunk-XQCAMKIQ.js → chunk-WZDKNF37.js} +250 -106
  32. package/dist/{chunk-PRAZBHNF.js → chunk-YLL7MF5C.js} +15 -15
  33. package/dist/{chunk-YZDZ4FGR.js → chunk-ZVC23LKV.js} +1 -1
  34. package/dist/compare.cjs +100 -97
  35. package/dist/compare.js +3 -3
  36. package/dist/core/commands/formatting-commands.d.cts +1 -1
  37. package/dist/core/commands/formatting-commands.d.ts +1 -1
  38. package/dist/core/commands/image-commands.cjs +16 -5
  39. package/dist/core/commands/image-commands.d.cts +1 -1
  40. package/dist/core/commands/image-commands.d.ts +1 -1
  41. package/dist/core/commands/image-commands.js +5 -5
  42. package/dist/core/commands/section-layout-commands.d.cts +1 -1
  43. package/dist/core/commands/section-layout-commands.d.ts +1 -1
  44. package/dist/core/commands/style-commands.d.cts +1 -1
  45. package/dist/core/commands/style-commands.d.ts +1 -1
  46. package/dist/core/commands/table-structure-commands.cjs +16 -5
  47. package/dist/core/commands/table-structure-commands.d.cts +1 -1
  48. package/dist/core/commands/table-structure-commands.d.ts +1 -1
  49. package/dist/core/commands/table-structure-commands.js +4 -4
  50. package/dist/core/commands/text-commands.cjs +16 -5
  51. package/dist/core/commands/text-commands.d.cts +1 -1
  52. package/dist/core/commands/text-commands.d.ts +1 -1
  53. package/dist/core/commands/text-commands.js +5 -5
  54. package/dist/core/selection/mapping.d.cts +1 -1
  55. package/dist/core/selection/mapping.d.ts +1 -1
  56. package/dist/core/state/editor-state.d.cts +1 -1
  57. package/dist/core/state/editor-state.d.ts +1 -1
  58. package/dist/index.cjs +1280 -347
  59. package/dist/index.d.cts +4 -4
  60. package/dist/index.d.ts +4 -4
  61. package/dist/index.js +24 -24
  62. package/dist/io/docx-session.cjs +216 -203
  63. package/dist/io/docx-session.d.cts +3 -3
  64. package/dist/io/docx-session.d.ts +3 -3
  65. package/dist/io/docx-session.js +6 -6
  66. package/dist/legal.cjs +9 -20
  67. package/dist/legal.js +3 -3
  68. package/dist/{loader-4qsw4eIU.d.ts → loader-B-aL5HGD.d.ts} +2 -2
  69. package/dist/{loader-B8TKhmQi.d.cts → loader-DiY_ZgKl.d.cts} +2 -2
  70. package/dist/{measurement-backend-canvas-Q3MJMEYX.js → measurement-backend-canvas-F7ZYDACK.js} +1 -1
  71. package/dist/{public-types-p9b8rfy8.d.ts → public-types-DyqnxxO9.d.ts} +124 -1
  72. package/dist/{public-types-B5CRoR6f.d.cts → public-types-gvubspUI.d.cts} +124 -1
  73. package/dist/public-types.cjs +595 -18
  74. package/dist/public-types.d.cts +1 -1
  75. package/dist/public-types.d.ts +1 -1
  76. package/dist/public-types.js +4 -4
  77. package/dist/runtime/collab.d.cts +2 -2
  78. package/dist/runtime/collab.d.ts +2 -2
  79. package/dist/runtime/document-runtime.cjs +908 -129
  80. package/dist/runtime/document-runtime.d.cts +1 -1
  81. package/dist/runtime/document-runtime.d.ts +1 -1
  82. package/dist/runtime/document-runtime.js +17 -17
  83. package/dist/{session-BnGIjaex.d.cts → session-BUN6B-Vj.d.cts} +2 -2
  84. package/dist/{session-vEYKf-w3.d.ts → session-CDB0hohT.d.ts} +2 -2
  85. package/dist/session.cjs +216 -203
  86. package/dist/session.d.cts +4 -4
  87. package/dist/session.d.ts +4 -4
  88. package/dist/session.js +7 -7
  89. package/dist/tailwind.cjs +595 -18
  90. package/dist/tailwind.d.cts +1 -1
  91. package/dist/tailwind.d.ts +1 -1
  92. package/dist/tailwind.js +8 -8
  93. package/dist/{types-BLuvZ6cQ.d.cts → types-C4bz3kDU.d.cts} +1 -1
  94. package/dist/{types-Dutlyj0T.d.ts → types-VWH6CRvG.d.ts} +1 -1
  95. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +2 -2
  96. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +2 -2
  97. package/dist/ui-tailwind/editor-surface/search-plugin.js +5 -5
  98. package/dist/ui-tailwind.cjs +595 -18
  99. package/dist/ui-tailwind.d.cts +2 -2
  100. package/dist/ui-tailwind.d.ts +2 -2
  101. package/dist/ui-tailwind.js +8 -8
  102. package/package.json +4 -1
package/dist/api/v3.cjs CHANGED
@@ -21,6 +21,43 @@ var __copyProps = (to, from, except, desc) => {
21
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
22
 
23
23
  // src/runtime/layout/resolved-formatting-state.ts
24
+ function createLayoutReadyFormattingSnapshot(formatting, runs = /* @__PURE__ */ new Map()) {
25
+ const snapshot = {
26
+ version: 1,
27
+ paragraph: {
28
+ spacingBefore: formatting.spacingBefore,
29
+ spacingAfter: formatting.spacingAfter,
30
+ lineHeight: formatting.lineHeight,
31
+ lineRule: formatting.lineRule,
32
+ indentLeft: formatting.indentLeft,
33
+ indentRight: formatting.indentRight,
34
+ firstLineIndent: formatting.firstLineIndent,
35
+ hangingIndent: formatting.hangingIndent,
36
+ fontSizeHalfPoints: formatting.fontSizeHalfPoints,
37
+ averageCharWidthTwips: formatting.averageCharWidthTwips,
38
+ tabStops: formatting.tabStops.map((tab) => ({ ...tab })),
39
+ defaultTabInterval: formatting.defaultTabInterval,
40
+ keepNext: formatting.keepNext,
41
+ keepLines: formatting.keepLines,
42
+ pageBreakBefore: formatting.pageBreakBefore,
43
+ widowControl: formatting.widowControl,
44
+ contextualSpacing: formatting.contextualSpacing,
45
+ ...formatting.numberingMarkerBox ? { numberingMarkerBox: { ...formatting.numberingMarkerBox } } : {}
46
+ },
47
+ runs: Array.from(runs.entries()).map(([runId, run]) => ({
48
+ runId,
49
+ ...run.fontFamily ? { fontFamily: run.fontFamily } : {},
50
+ ...typeof run.fontSizeHalfPoints === "number" ? { fontSizeHalfPoints: run.fontSizeHalfPoints } : {},
51
+ bold: Boolean(run.bold),
52
+ italic: Boolean(run.italic),
53
+ verticalAlign: run.verticalAlign ?? "baseline"
54
+ })).sort((a, b) => a.runId.localeCompare(b.runId))
55
+ };
56
+ return {
57
+ ...snapshot,
58
+ hash: hashStable(snapshot)
59
+ };
60
+ }
24
61
  function resolveBlockFormatting(block, defaultTabInterval = 720, themeFonts) {
25
62
  if (block.kind !== "paragraph") {
26
63
  return null;
@@ -263,6 +300,29 @@ function buildRunFormattingMap(block) {
263
300
  }
264
301
  return runs;
265
302
  }
303
+ function hashStable(value) {
304
+ return fnv1a2(stableStringify3(value)).toString(36);
305
+ }
306
+ function stableStringify3(value) {
307
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
308
+ if (Array.isArray(value)) {
309
+ return `[${value.map((item) => stableStringify3(item)).join(",")}]`;
310
+ }
311
+ if (value instanceof Map) {
312
+ return stableStringify3(Array.from(value.entries()));
313
+ }
314
+ const object = value;
315
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
316
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify3(object[key])}`).join(",")}}`;
317
+ }
318
+ function fnv1a2(input) {
319
+ let hash = 2166136261;
320
+ for (let i = 0; i < input.length; i += 1) {
321
+ hash ^= input.charCodeAt(i);
322
+ hash = Math.imul(hash, 16777619);
323
+ }
324
+ return hash >>> 0;
325
+ }
266
326
  var FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_SIZE_HALF_POINTS, DEFAULT_LINE_HEIGHT_FACTOR, TWIPS_PER_POINT;
267
327
  var init_resolved_formatting_state = __esm({
268
328
  "src/runtime/layout/resolved-formatting-state.ts"() {
@@ -3591,26 +3651,35 @@ function buildParagraphPropertiesXml(paragraph) {
3591
3651
  if (frameXml) children.push(frameXml);
3592
3652
  }
3593
3653
  pushOnOffParagraphProperty(children, "widowControl", paragraph.widowControl);
3594
- if (paragraph.outlineLevel !== void 0) {
3595
- children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
3596
- }
3597
3654
  if (paragraph.numbering) {
3598
3655
  children.push(serializeParagraphNumberingProperties(paragraph.numbering));
3599
3656
  }
3657
+ pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
3658
+ if (paragraph.borders) {
3659
+ const bordersXml = serializeParagraphBorders(paragraph.borders);
3660
+ if (bordersXml) children.push(bordersXml);
3661
+ }
3662
+ if (paragraph.shading) {
3663
+ const shadingXml = serializeParagraphShading(paragraph.shading);
3664
+ if (shadingXml) children.push(shadingXml);
3665
+ }
3666
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
3667
+ const tabsXml = paragraph.tabStops.map((tab) => {
3668
+ const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
3669
+ return `<w:tab w:val="${escapeXmlAttribute2(tab.align)}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
3670
+ }).join("");
3671
+ children.push(`<w:tabs>${tabsXml}</w:tabs>`);
3672
+ }
3673
+ pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
3600
3674
  if (paragraph.spacing) {
3601
3675
  const s = paragraph.spacing;
3602
3676
  const attrs = [];
3603
3677
  if (s.before !== void 0) attrs.push(`w:before="${twip(s.before)}"`);
3604
3678
  if (s.after !== void 0) attrs.push(`w:after="${twip(s.after)}"`);
3605
3679
  if (s.line !== void 0) attrs.push(`w:line="${twip(s.line)}"`);
3606
- if (s.lineRule !== void 0) attrs.push(`w:lineRule="${s.lineRule}"`);
3680
+ if (s.lineRule !== void 0) attrs.push(`w:lineRule="${escapeXmlAttribute2(s.lineRule)}"`);
3607
3681
  if (attrs.length > 0) children.push(`<w:spacing ${attrs.join(" ")}/>`);
3608
3682
  }
3609
- if (paragraph.contextualSpacing === true) {
3610
- children.push("<w:contextualSpacing/>");
3611
- } else if (paragraph.contextualSpacing === false) {
3612
- children.push(`<w:contextualSpacing w:val="false"/>`);
3613
- }
3614
3683
  if (paragraph.indentation) {
3615
3684
  const ind = paragraph.indentation;
3616
3685
  const attrs = [];
@@ -3620,33 +3689,20 @@ function buildParagraphPropertiesXml(paragraph) {
3620
3689
  if (ind.hanging !== void 0) attrs.push(`w:hanging="${twip(ind.hanging)}"`);
3621
3690
  if (attrs.length > 0) children.push(`<w:ind ${attrs.join(" ")}/>`);
3622
3691
  }
3623
- if (paragraph.alignment) {
3624
- children.push(`<w:jc w:val="${paragraph.alignment}"/>`);
3692
+ if (paragraph.contextualSpacing === true) {
3693
+ children.push("<w:contextualSpacing/>");
3694
+ } else if (paragraph.contextualSpacing === false) {
3695
+ children.push(`<w:contextualSpacing w:val="false"/>`);
3625
3696
  }
3626
- if (paragraph.borders) {
3627
- const bordersXml = serializeParagraphBorders(paragraph.borders);
3628
- if (bordersXml) {
3629
- children.push(bordersXml);
3630
- }
3697
+ if (paragraph.alignment) {
3698
+ children.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
3631
3699
  }
3632
- if (paragraph.shading) {
3633
- const shadingXml = serializeParagraphShading(paragraph.shading);
3634
- if (shadingXml) {
3635
- children.push(shadingXml);
3636
- }
3700
+ if (paragraph.outlineLevel !== void 0) {
3701
+ children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
3637
3702
  }
3638
- pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
3639
- pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
3640
3703
  if (paragraph.cnfStyle) {
3641
3704
  children.push(`<w:cnfStyle w:val="${escapeXmlAttribute2(paragraph.cnfStyle)}"/>`);
3642
3705
  }
3643
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
3644
- const tabsXml = paragraph.tabStops.map((tab) => {
3645
- const leaderAttr = tab.leader ? ` w:leader="${tab.leader}"` : "";
3646
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
3647
- }).join("");
3648
- children.push(`<w:tabs>${tabsXml}</w:tabs>`);
3649
- }
3650
3706
  if (children.length === 0) return "";
3651
3707
  return `<w:pPr>${children.join("")}</w:pPr>`;
3652
3708
  }
@@ -4211,79 +4267,86 @@ function serializeRunProperties(marks) {
4211
4267
  if (!marks || marks.length === 0) {
4212
4268
  return "";
4213
4269
  }
4270
+ const orderedTypes = [
4271
+ "fontFamily",
4272
+ "bold",
4273
+ "italic",
4274
+ "allCaps",
4275
+ "smallCaps",
4276
+ "strikethrough",
4277
+ "doubleStrikethrough",
4278
+ "shadow",
4279
+ "emboss",
4280
+ "imprint",
4281
+ "vanish",
4282
+ "textColor",
4283
+ "charSpacing",
4284
+ "kerning",
4285
+ "position",
4286
+ "fontSize",
4287
+ "highlight",
4288
+ "underline",
4289
+ "backgroundColor",
4290
+ "lang",
4291
+ "textFill"
4292
+ ];
4214
4293
  const markParts = [];
4215
- for (const mark of marks) {
4216
- switch (mark.type) {
4217
- case "bold":
4218
- markParts.push("<w:b/>");
4219
- break;
4220
- case "italic":
4221
- markParts.push("<w:i/>");
4222
- break;
4223
- case "underline":
4224
- markParts.push(`<w:u w:val="single"/>`);
4225
- break;
4226
- case "strikethrough":
4227
- markParts.push("<w:strike/>");
4228
- break;
4229
- case "doubleStrikethrough":
4230
- markParts.push("<w:dstrike/>");
4231
- break;
4232
- case "vanish":
4233
- markParts.push("<w:vanish/>");
4234
- break;
4235
- case "lang":
4236
- markParts.push(`<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`);
4237
- break;
4238
- case "highlight":
4239
- markParts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
4240
- break;
4241
- case "backgroundColor":
4242
- markParts.push(
4243
- `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`
4244
- );
4245
- break;
4246
- case "charSpacing":
4247
- markParts.push(`<w:spacing w:val="${mark.val}"/>`);
4248
- break;
4249
- case "kerning":
4250
- markParts.push(`<w:kern w:val="${mark.val}"/>`);
4251
- break;
4252
- case "emboss":
4253
- markParts.push("<w:emboss/>");
4254
- break;
4255
- case "imprint":
4256
- markParts.push("<w:imprint/>");
4257
- break;
4258
- case "shadow":
4259
- markParts.push("<w:shadow/>");
4260
- break;
4261
- case "position":
4262
- markParts.push(`<w:position w:val="${mark.val}"/>`);
4263
- break;
4264
- case "textFill":
4265
- markParts.push(mark.xml);
4266
- break;
4267
- case "fontFamily":
4268
- markParts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
4269
- break;
4270
- case "fontSize":
4271
- markParts.push(`<w:sz w:val="${mark.val}"/>`);
4272
- break;
4273
- case "textColor":
4274
- markParts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
4275
- break;
4276
- case "smallCaps":
4277
- markParts.push("<w:smallCaps/>");
4278
- break;
4279
- case "allCaps":
4280
- markParts.push("<w:caps/>");
4281
- break;
4294
+ for (const type of orderedTypes) {
4295
+ for (const mark of marks) {
4296
+ if (mark.type !== type) continue;
4297
+ const xml = serializeRunPropertyMark(mark);
4298
+ if (xml) markParts.push(xml);
4282
4299
  }
4283
4300
  }
4284
4301
  const children = markParts.join("");
4285
4302
  return children.length > 0 ? `<w:rPr>${children}</w:rPr>` : "";
4286
4303
  }
4304
+ function serializeRunPropertyMark(mark) {
4305
+ switch (mark.type) {
4306
+ case "bold":
4307
+ return "<w:b/>";
4308
+ case "italic":
4309
+ return "<w:i/>";
4310
+ case "underline":
4311
+ return `<w:u w:val="single"/>`;
4312
+ case "strikethrough":
4313
+ return "<w:strike/>";
4314
+ case "doubleStrikethrough":
4315
+ return "<w:dstrike/>";
4316
+ case "vanish":
4317
+ return "<w:vanish/>";
4318
+ case "lang":
4319
+ return `<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`;
4320
+ case "highlight":
4321
+ return `<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`;
4322
+ case "backgroundColor":
4323
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`;
4324
+ case "charSpacing":
4325
+ return `<w:spacing w:val="${mark.val}"/>`;
4326
+ case "kerning":
4327
+ return `<w:kern w:val="${mark.val}"/>`;
4328
+ case "emboss":
4329
+ return "<w:emboss/>";
4330
+ case "imprint":
4331
+ return "<w:imprint/>";
4332
+ case "shadow":
4333
+ return "<w:shadow/>";
4334
+ case "position":
4335
+ return `<w:position w:val="${mark.val}"/>`;
4336
+ case "textFill":
4337
+ return mark.xml;
4338
+ case "fontFamily":
4339
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
4340
+ case "fontSize":
4341
+ return `<w:sz w:val="${mark.val}"/>`;
4342
+ case "textColor":
4343
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
4344
+ case "smallCaps":
4345
+ return "<w:smallCaps/>";
4346
+ case "allCaps":
4347
+ return "<w:caps/>";
4348
+ }
4349
+ }
4287
4350
  function requiresPreservedSpace(text) {
4288
4351
  return /^\s/.test(text) || /\s$/.test(text) || text.includes(" ");
4289
4352
  }
@@ -7391,6 +7454,13 @@ function buildParagraphPropertiesXml2(paragraph) {
7391
7454
  const frameXml = buildFrameXml(paragraph.frameProperties);
7392
7455
  if (frameXml) parts.push(frameXml);
7393
7456
  }
7457
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
7458
+ const tabsXml = paragraph.tabStops.map((tab) => {
7459
+ const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
7460
+ return `<w:tab w:val="${escapeXmlAttribute2(tab.align)}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
7461
+ }).join("");
7462
+ parts.push(`<w:tabs>${tabsXml}</w:tabs>`);
7463
+ }
7394
7464
  if (paragraph.spacing) {
7395
7465
  const s = paragraph.spacing;
7396
7466
  const attrs = [];
@@ -7412,13 +7482,6 @@ function buildParagraphPropertiesXml2(paragraph) {
7412
7482
  if (paragraph.alignment) {
7413
7483
  parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
7414
7484
  }
7415
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
7416
- const tabsXml = paragraph.tabStops.map((tab) => {
7417
- const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
7418
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
7419
- }).join("");
7420
- parts.push(`<w:tabs>${tabsXml}</w:tabs>`);
7421
- }
7422
7485
  return parts.length > 0 ? `<w:pPr>${parts.join("")}</w:pPr>` : "";
7423
7486
  }
7424
7487
  function serializeInlineNode2(node) {
@@ -7487,51 +7550,60 @@ function buildRunPropertiesXml2(marks) {
7487
7550
  if (!marks || marks.length === 0) {
7488
7551
  return "";
7489
7552
  }
7553
+ const orderedTypes = [
7554
+ "fontFamily",
7555
+ "bold",
7556
+ "italic",
7557
+ "allCaps",
7558
+ "smallCaps",
7559
+ "strikethrough",
7560
+ "doubleStrikethrough",
7561
+ "textColor",
7562
+ "fontSize",
7563
+ "highlight",
7564
+ "underline",
7565
+ "backgroundColor"
7566
+ ];
7490
7567
  const parts = [];
7491
- for (const mark of marks) {
7492
- switch (mark.type) {
7493
- case "bold":
7494
- parts.push("<w:b/>");
7495
- break;
7496
- case "italic":
7497
- parts.push("<w:i/>");
7498
- break;
7499
- case "underline":
7500
- parts.push('<w:u w:val="single"/>');
7501
- break;
7502
- case "strikethrough":
7503
- parts.push("<w:strike/>");
7504
- break;
7505
- case "doubleStrikethrough":
7506
- parts.push("<w:dstrike/>");
7507
- break;
7508
- case "fontFamily":
7509
- parts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
7510
- break;
7511
- case "fontSize":
7512
- parts.push(`<w:sz w:val="${twip(mark.val)}"/>`);
7513
- break;
7514
- case "textColor":
7515
- parts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
7516
- break;
7517
- case "highlight":
7518
- parts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
7519
- break;
7520
- case "backgroundColor":
7521
- parts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`);
7522
- break;
7523
- case "smallCaps":
7524
- parts.push("<w:smallCaps/>");
7525
- break;
7526
- case "allCaps":
7527
- parts.push("<w:caps/>");
7528
- break;
7529
- default:
7530
- break;
7568
+ for (const type of orderedTypes) {
7569
+ for (const mark of marks) {
7570
+ if (mark.type !== type) continue;
7571
+ const xml = serializeRunPropertyMark2(mark);
7572
+ if (xml) parts.push(xml);
7531
7573
  }
7532
7574
  }
7533
7575
  return parts.length > 0 ? `<w:rPr>${parts.join("")}</w:rPr>` : "";
7534
7576
  }
7577
+ function serializeRunPropertyMark2(mark) {
7578
+ switch (mark.type) {
7579
+ case "bold":
7580
+ return "<w:b/>";
7581
+ case "italic":
7582
+ return "<w:i/>";
7583
+ case "underline":
7584
+ return `<w:u w:val="single"/>`;
7585
+ case "strikethrough":
7586
+ return "<w:strike/>";
7587
+ case "doubleStrikethrough":
7588
+ return "<w:dstrike/>";
7589
+ case "fontFamily":
7590
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
7591
+ case "fontSize":
7592
+ return `<w:sz w:val="${twip(mark.val)}"/>`;
7593
+ case "textColor":
7594
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
7595
+ case "highlight":
7596
+ return `<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`;
7597
+ case "backgroundColor":
7598
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`;
7599
+ case "smallCaps":
7600
+ return "<w:smallCaps/>";
7601
+ case "allCaps":
7602
+ return "<w:caps/>";
7603
+ default:
7604
+ return "";
7605
+ }
7606
+ }
7535
7607
  function requiresPreservedSpace3(text) {
7536
7608
  return text.length > 0 && (text[0] === " " || text[text.length - 1] === " " || text.includes(" "));
7537
7609
  }
@@ -7705,9 +7777,6 @@ function buildParagraphPropertiesXml3(paragraph) {
7705
7777
  const frameXml = buildFrameXml(paragraph.frameProperties);
7706
7778
  if (frameXml) parts.push(frameXml);
7707
7779
  }
7708
- if (paragraph.alignment) {
7709
- parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
7710
- }
7711
7780
  if (paragraph.spacing) {
7712
7781
  const attrs = [];
7713
7782
  if (paragraph.spacing.before !== void 0) attrs.push(`w:before="${twip(paragraph.spacing.before)}"`);
@@ -7728,6 +7797,9 @@ function buildParagraphPropertiesXml3(paragraph) {
7728
7797
  parts.push(`<w:ind ${attrs.join(" ")}/>`);
7729
7798
  }
7730
7799
  }
7800
+ if (paragraph.alignment) {
7801
+ parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
7802
+ }
7731
7803
  return parts.length > 0 ? `<w:pPr>${parts.join("")}</w:pPr>` : "";
7732
7804
  }
7733
7805
  function serializeInlineNode3(node) {
@@ -7788,47 +7860,59 @@ function buildRunPropertiesXml3(marks) {
7788
7860
  if (!marks || marks.length === 0) {
7789
7861
  return "";
7790
7862
  }
7791
- const parts = [];
7863
+ const orderedTypes = [
7864
+ "fontFamily",
7865
+ "bold",
7866
+ "italic",
7867
+ "allCaps",
7868
+ "smallCaps",
7869
+ "strikethrough",
7870
+ "doubleStrikethrough",
7871
+ "textColor",
7872
+ "fontSize",
7873
+ "underline"
7874
+ ];
7875
+ const supportedTypes = new Set(orderedTypes);
7792
7876
  for (const mark of marks) {
7793
- switch (mark.type) {
7794
- case "bold":
7795
- parts.push("<w:b/>");
7796
- break;
7797
- case "italic":
7798
- parts.push("<w:i/>");
7799
- break;
7800
- case "underline":
7801
- parts.push('<w:u w:val="single"/>');
7802
- break;
7803
- case "strikethrough":
7804
- parts.push("<w:strike/>");
7805
- break;
7806
- case "doubleStrikethrough":
7807
- parts.push("<w:dstrike/>");
7808
- break;
7809
- case "fontFamily":
7810
- parts.push(
7811
- `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`
7812
- );
7813
- break;
7814
- case "fontSize":
7815
- parts.push(`<w:sz w:val="${twip(mark.val)}"/>`);
7816
- break;
7817
- case "textColor":
7818
- parts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
7819
- break;
7820
- case "smallCaps":
7821
- parts.push("<w:smallCaps/>");
7822
- break;
7823
- case "allCaps":
7824
- parts.push("<w:caps/>");
7825
- break;
7826
- default:
7827
- throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
7877
+ if (!supportedTypes.has(mark.type)) {
7878
+ throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
7879
+ }
7880
+ }
7881
+ const parts = [];
7882
+ for (const type of orderedTypes) {
7883
+ for (const mark of marks) {
7884
+ if (mark.type !== type) continue;
7885
+ parts.push(serializeRunPropertyMark3(mark));
7828
7886
  }
7829
7887
  }
7830
7888
  return parts.length > 0 ? `<w:rPr>${parts.join("")}</w:rPr>` : "";
7831
7889
  }
7890
+ function serializeRunPropertyMark3(mark) {
7891
+ switch (mark.type) {
7892
+ case "bold":
7893
+ return "<w:b/>";
7894
+ case "italic":
7895
+ return "<w:i/>";
7896
+ case "underline":
7897
+ return `<w:u w:val="single"/>`;
7898
+ case "strikethrough":
7899
+ return "<w:strike/>";
7900
+ case "doubleStrikethrough":
7901
+ return "<w:dstrike/>";
7902
+ case "fontFamily":
7903
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
7904
+ case "fontSize":
7905
+ return `<w:sz w:val="${twip(mark.val)}"/>`;
7906
+ case "textColor":
7907
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
7908
+ case "smallCaps":
7909
+ return "<w:smallCaps/>";
7910
+ case "allCaps":
7911
+ return "<w:caps/>";
7912
+ default:
7913
+ throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
7914
+ }
7915
+ }
7832
7916
  function compareNoteIds(left, right) {
7833
7917
  return Number.parseInt(left.noteId, 10) - Number.parseInt(right.noteId, 10);
7834
7918
  }
@@ -12816,6 +12900,7 @@ function collectCanonicalLayoutInputs(doc) {
12816
12900
  const blockContexts = collectStoryBlockContexts(doc);
12817
12901
  return {
12818
12902
  stories: collectCanonicalStoryIdentities(doc),
12903
+ layoutIdentities: collectLayoutInputIdentities(doc, blockContexts),
12819
12904
  fieldRegions: collectCanonicalFieldRegionIdentities(doc),
12820
12905
  numbering: collectCanonicalNumberingLayoutInputs(doc, blockContexts),
12821
12906
  tables: collectCanonicalTableLayoutInputs(blockContexts),
@@ -12824,6 +12909,116 @@ function collectCanonicalLayoutInputs(doc) {
12824
12909
  editableTargets: collectEditableTargetRefs(doc)
12825
12910
  };
12826
12911
  }
12912
+ function collectLayoutInputIdentities(doc, contexts = collectStoryBlockContexts(doc)) {
12913
+ const identities = [];
12914
+ const editableTargets = collectEditableTargetRefs(doc);
12915
+ const editableByStoryBlock = new Map(
12916
+ editableTargets.map((target) => [`${target.storyKey}:${target.blockPath}`, target])
12917
+ );
12918
+ for (const context of contexts) {
12919
+ walkBlocks(context.blocks, context.storyKey, context.basePath, {
12920
+ paragraph(paragraph, blockPath) {
12921
+ const styleNumbering = paragraph.numbering === void 0 && paragraph.styleId !== void 0 ? resolveParagraphStyleNumbering(doc, paragraph.styleId) : void 0;
12922
+ const numbering = paragraph.numbering ?? styleNumbering;
12923
+ const editableTargetRef = editableByStoryBlock.get(`${context.storyKey}:${blockPath}`);
12924
+ identities.push(
12925
+ createLayoutInputIdentity({
12926
+ storyKey: context.storyKey,
12927
+ blockPath,
12928
+ block: paragraph,
12929
+ ...editableTargetRef !== void 0 ? { editableTargetRef } : {},
12930
+ ...numbering !== void 0 ? {
12931
+ list: {
12932
+ numberingInstanceId: numbering.numberingInstanceId,
12933
+ level: numbering.level ?? 0,
12934
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
12935
+ }
12936
+ } : {}
12937
+ })
12938
+ );
12939
+ },
12940
+ table(table, blockPath) {
12941
+ identities.push(
12942
+ createLayoutInputIdentity({
12943
+ storyKey: context.storyKey,
12944
+ blockPath,
12945
+ block: table,
12946
+ table: { tableBlockPath: blockPath }
12947
+ })
12948
+ );
12949
+ },
12950
+ inline(inline, blockPath, inlinePath) {
12951
+ const objectId = objectIdForInline(inline);
12952
+ if (!objectId) return;
12953
+ const sourceRef = sourceRefForInline(inline);
12954
+ identities.push({
12955
+ storyKey: context.storyKey,
12956
+ blockPath,
12957
+ blockId: `${context.storyKey}:${inlinePath}`,
12958
+ ...sourceRef !== void 0 ? { sourceRef } : {},
12959
+ object: {
12960
+ objectId,
12961
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
12962
+ }
12963
+ });
12964
+ }
12965
+ });
12966
+ }
12967
+ return identities;
12968
+ }
12969
+ function createLayoutInputIdentity(input) {
12970
+ const sourceRef = sourceRefForBlock(input.block);
12971
+ const blockId = input.block.type === "paragraph" && input.block.wordExtensionIds?.paraId ? `para:${input.block.wordExtensionIds.paraId}` : sourceRef?.sourceId !== void 0 ? `source:${sourceRef.sourceId}` : `${input.storyKey}:${input.blockPath}`;
12972
+ return {
12973
+ storyKey: input.storyKey,
12974
+ blockPath: input.blockPath,
12975
+ blockId,
12976
+ ...sourceRef !== void 0 ? { sourceRef } : {},
12977
+ ...input.editableTargetRef !== void 0 ? { editableTargetRef: input.editableTargetRef } : {},
12978
+ ...input.table !== void 0 ? { table: input.table } : {},
12979
+ ...input.list !== void 0 ? { list: input.list } : {}
12980
+ };
12981
+ }
12982
+ function sourceRefForBlock(block) {
12983
+ return "sourceRef" in block ? block.sourceRef : void 0;
12984
+ }
12985
+ function sourceRefForInline(inline) {
12986
+ return "sourceRef" in inline ? inline.sourceRef : void 0;
12987
+ }
12988
+ function objectIdForInline(inline) {
12989
+ switch (inline.type) {
12990
+ case "image":
12991
+ return inline.mediaId;
12992
+ case "drawing_frame":
12993
+ return objectIdForDrawingContent(inline.content) ?? inline.sourceRef?.sourceId;
12994
+ case "shape":
12995
+ case "vml_shape":
12996
+ case "wordart":
12997
+ return inline.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
12998
+ case "chart_preview":
12999
+ case "smartart_preview":
13000
+ return inline.previewMediaId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
13001
+ case "ole_embed":
13002
+ return inline.relationshipId ?? inline.id ?? inline.sourceRef?.sourceId;
13003
+ case "opaque_inline":
13004
+ return inline.fragmentId;
13005
+ default:
13006
+ return void 0;
13007
+ }
13008
+ }
13009
+ function objectIdForDrawingContent(content) {
13010
+ switch (content.type) {
13011
+ case "picture":
13012
+ return content.mediaId ?? content.packagePartName ?? content.blipRef;
13013
+ case "chart_preview":
13014
+ case "smartart_preview":
13015
+ return content.previewMediaId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
13016
+ case "shape":
13017
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
13018
+ case "opaque":
13019
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
13020
+ }
13021
+ }
12827
13022
  function collectEditableTargetRefs(doc, cache) {
12828
13023
  const targets = [];
12829
13024
  for (const context of collectStoryBlockContexts(doc)) {
@@ -14923,6 +15118,16 @@ function collectCanonicalNumberingLayoutInputs(doc, contexts = collectStoryBlock
14923
15118
  const instance = doc.numbering.instances[numbering.numberingInstanceId];
14924
15119
  const abstractDefinition = instance?.abstractNumberingId === void 0 ? void 0 : doc.numbering.abstractDefinitions[instance.abstractNumberingId];
14925
15120
  inputs.push({
15121
+ identity: createLayoutInputIdentity({
15122
+ storyKey: context.storyKey,
15123
+ blockPath,
15124
+ block: paragraph,
15125
+ list: {
15126
+ numberingInstanceId: numbering.numberingInstanceId,
15127
+ level: numbering.level ?? 0,
15128
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
15129
+ }
15130
+ }),
14926
15131
  numberingKey: `${context.storyKey}:${blockPath}:numbering`,
14927
15132
  storyKey: context.storyKey,
14928
15133
  blockPath,
@@ -15224,6 +15429,12 @@ function projectTableLayoutInput(table, storyKey, blockPath) {
15224
15429
  (row, rowIndex) => projectTableRowLayoutInput(row, tableKey, rowIndex)
15225
15430
  );
15226
15431
  return {
15432
+ identity: createLayoutInputIdentity({
15433
+ storyKey,
15434
+ blockPath,
15435
+ block: table,
15436
+ table: { tableBlockPath: blockPath }
15437
+ }),
15227
15438
  tableKey,
15228
15439
  storyKey,
15229
15440
  blockPath,
@@ -15311,6 +15522,16 @@ function projectDrawingFrameAnchor(node, storyKey, blockPath, inlinePath) {
15311
15522
  const objectKey = `${storyKey}:${inlinePath}`;
15312
15523
  const textBoxBody = projectTextBoxBodyLayoutInput(content, objectKey, sourceRef, `${inlinePath}/txbx`);
15313
15524
  return {
15525
+ identity: {
15526
+ storyKey,
15527
+ blockPath,
15528
+ blockId: objectKey,
15529
+ ...sourceRef !== void 0 ? { sourceRef } : {},
15530
+ object: {
15531
+ objectId: objectKey,
15532
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
15533
+ }
15534
+ },
15314
15535
  objectKey,
15315
15536
  storyKey,
15316
15537
  blockPath,
@@ -15432,8 +15653,19 @@ function projectTextBoxRunLayoutInput(inline, paragraphKey, inlineIndex) {
15432
15653
  }
15433
15654
  function projectLegacyImageAnchor(doc, node, storyKey, blockPath, inlinePath) {
15434
15655
  const media = doc.media.items[node.mediaId];
15656
+ const objectKey = `${storyKey}:${inlinePath}`;
15435
15657
  return {
15436
- objectKey: `${storyKey}:${inlinePath}`,
15658
+ identity: {
15659
+ storyKey,
15660
+ blockPath,
15661
+ blockId: objectKey,
15662
+ ...node.sourceRef !== void 0 ? { sourceRef: node.sourceRef } : {},
15663
+ object: {
15664
+ objectId: objectKey,
15665
+ ...node.sourceRef !== void 0 ? { anchorSourceRef: node.sourceRef } : {}
15666
+ }
15667
+ },
15668
+ objectKey,
15437
15669
  storyKey,
15438
15670
  blockPath,
15439
15671
  inlinePath,
@@ -18409,8 +18641,7 @@ function resolveContentType(path, contentTypes) {
18409
18641
  // src/io/ooxml/parse-bookmark-references.ts
18410
18642
  var HYPERLINK_ANCHOR_RE = /<(?:\w+:)?hyperlink\b[^>]*\bw:anchor\s*=\s*"([^"]*)"/gi;
18411
18643
  var INSTR_TEXT_RE = /<(?:\w+:)?instrText\b[^>]*>([\s\S]*?)<\/(?:\w+:)?instrText>/gi;
18412
- var FLD_SIMPLE_INSTR_DOUBLE_RE = /<(?:\w+:)?fldSimple\b[^>]*?\b(?:w:)?instr\s*=\s*"([^"]*)"/gi;
18413
- var FLD_SIMPLE_INSTR_SINGLE_RE = /<(?:\w+:)?fldSimple\b[^>]*?\b(?:w:)?instr\s*=\s*'([^']*)'/gi;
18644
+ var FLD_SIMPLE_INSTR_RE = /<(?:\w+:)?fldSimple\b[^>]*\bw:instr\s*=\s*(["'])([\s\S]*?)\1/gi;
18414
18645
  var TOC_FIELD_RE = /\bTOC\b/;
18415
18646
  var REFLIKE_FIELD_RE = /\b(?:HYPERLINK|REF|PAGEREF|NOTEREF)\s+([A-Za-z0-9_:.\-]+)/g;
18416
18647
  var DATA_BINDING_RE = /<(?:\w+:)?dataBinding\b/i;
@@ -18429,21 +18660,14 @@ function scanBookmarkReferences(documentXml, callerAllowlist = []) {
18429
18660
  }
18430
18661
  INSTR_TEXT_RE.lastIndex = 0;
18431
18662
  while ((m = INSTR_TEXT_RE.exec(documentXml)) !== null) {
18432
- const instrText = m[1] ?? "";
18433
- scanFieldInstruction(instrText, retained, () => {
18434
- retainAllToc = true;
18663
+ retainInstructionReferences(m[1] ?? "", retained, (value) => {
18664
+ retainAllToc = retainAllToc || value;
18435
18665
  });
18436
18666
  }
18437
- FLD_SIMPLE_INSTR_DOUBLE_RE.lastIndex = 0;
18438
- while ((m = FLD_SIMPLE_INSTR_DOUBLE_RE.exec(documentXml)) !== null) {
18439
- scanFieldInstruction(decodeXmlAttribute(m[1] ?? ""), retained, () => {
18440
- retainAllToc = true;
18441
- });
18442
- }
18443
- FLD_SIMPLE_INSTR_SINGLE_RE.lastIndex = 0;
18444
- while ((m = FLD_SIMPLE_INSTR_SINGLE_RE.exec(documentXml)) !== null) {
18445
- scanFieldInstruction(decodeXmlAttribute(m[1] ?? ""), retained, () => {
18446
- retainAllToc = true;
18667
+ FLD_SIMPLE_INSTR_RE.lastIndex = 0;
18668
+ while ((m = FLD_SIMPLE_INSTR_RE.exec(documentXml)) !== null) {
18669
+ retainInstructionReferences(m[2] ?? "", retained, (value) => {
18670
+ retainAllToc = retainAllToc || value;
18447
18671
  });
18448
18672
  }
18449
18673
  retainRevisionBoundedBookmarks(documentXml, retained);
@@ -18453,17 +18677,14 @@ function scanBookmarkReferences(documentXml, callerAllowlist = []) {
18453
18677
  retainAll
18454
18678
  };
18455
18679
  }
18456
- function scanFieldInstruction(instrText, retained, retainToc) {
18457
- if (TOC_FIELD_RE.test(instrText)) retainToc();
18680
+ function retainInstructionReferences(instrText, retained, setRetainAllToc) {
18681
+ if (TOC_FIELD_RE.test(instrText)) setRetainAllToc(true);
18458
18682
  REFLIKE_FIELD_RE.lastIndex = 0;
18459
18683
  let r;
18460
18684
  while ((r = REFLIKE_FIELD_RE.exec(instrText)) !== null) {
18461
18685
  if (r[1]) retained.add(r[1]);
18462
18686
  }
18463
18687
  }
18464
- function decodeXmlAttribute(value) {
18465
- return value.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
18466
- }
18467
18688
  function retainRevisionBoundedBookmarks(documentXml, retained) {
18468
18689
  const starts = /* @__PURE__ */ new Map();
18469
18690
  BOOKMARK_START_RE.lastIndex = 0;
@@ -32621,7 +32842,7 @@ async function computeStructuralHash(blocks) {
32621
32842
  }
32622
32843
 
32623
32844
  // src/runtime/layout/layout-engine-version.ts
32624
- var LAYOUT_ENGINE_VERSION = 89;
32845
+ var LAYOUT_ENGINE_VERSION = 93;
32625
32846
  var LAYCACHE_SCHEMA_VERSION = 12;
32626
32847
 
32627
32848
  // src/runtime/prerender/customxml-cache.ts
@@ -34343,6 +34564,19 @@ function paragraphHasBookmarkId(paragraph, bookmarkId) {
34343
34564
  function commandActionHandleForAddress(commandFamily, address) {
34344
34565
  return address ? `scope-command:${commandFamily}:${address.addressKey}` : void 0;
34345
34566
  }
34567
+ function layoutIdentityIndexes(document2) {
34568
+ const byTargetKey = /* @__PURE__ */ new Map();
34569
+ const byStoryBlockPath = /* @__PURE__ */ new Map();
34570
+ for (const identity of collectLayoutInputIdentities(document2)) {
34571
+ byStoryBlockPath.set(`${identity.storyKey}:${identity.blockPath}`, identity);
34572
+ const targetKey = identity.editableTargetRef?.targetKey;
34573
+ if (targetKey) byTargetKey.set(targetKey, identity);
34574
+ }
34575
+ return { byTargetKey, byStoryBlockPath };
34576
+ }
34577
+ function layoutIdentityForTarget(target, indexes) {
34578
+ return indexes.byTargetKey.get(target.targetKey) ?? indexes.byStoryBlockPath.get(`${target.storyKey}:${target.blockPath}`);
34579
+ }
34346
34580
  function withCommandAction(evidence, target, canonicalAddress = target.canonicalAddress) {
34347
34581
  if (evidence.status !== "supported" || !canonicalAddress) return evidence;
34348
34582
  return {
@@ -34354,6 +34588,24 @@ function withCommandAction(evidence, target, canonicalAddress = target.canonical
34354
34588
  function commandAddressForTarget(target, scopeKind) {
34355
34589
  return scopeKind === "list-item" && target.listAddress !== void 0 ? target.listAddress : target.canonicalAddress;
34356
34590
  }
34591
+ function freezeCommandIntents(intents) {
34592
+ return Object.freeze([...new Set(intents)]);
34593
+ }
34594
+ function relatedListActionHandlesForTarget(target, primaryAddress) {
34595
+ if (target.commandFamily !== "text-leaf" || target.listAddress === void 0 || !LIST_TEXT_TARGET_KINDS2.has(target.kind) || target.listAddress.addressKey === primaryAddress?.addressKey) {
34596
+ return void 0;
34597
+ }
34598
+ const actionHandle = commandActionHandleForAddress(target.commandFamily, target.listAddress);
34599
+ if (!actionHandle) return void 0;
34600
+ return Object.freeze([
34601
+ {
34602
+ actionHandle,
34603
+ canonicalAddress: target.listAddress,
34604
+ intents: freezeCommandIntents(["list-text-edit", "list-structure-action"]),
34605
+ reason: "l07:list-address-target-supported"
34606
+ }
34607
+ ]);
34608
+ }
34357
34609
  function runtimeTextCommandEvidence(target, workflowBlockers) {
34358
34610
  const shapeIssues = validateEditableTargetRef(target);
34359
34611
  if (shapeIssues.length > 0) {
@@ -34453,13 +34705,16 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand, scopeKind
34453
34705
  };
34454
34706
  }
34455
34707
  if (target.commandFamily === "text-leaf") {
34708
+ const canonicalAddress = commandAddressForTarget(target, scopeKind);
34709
+ const relatedActionHandles = relatedListActionHandlesForTarget(target, canonicalAddress);
34456
34710
  return withCommandAction({
34457
34711
  status: textCommand.status,
34458
34712
  commandFamily: target.commandFamily,
34459
34713
  intents: commandIntentsForTarget(target, scopeKind),
34460
34714
  reason: textCommand.reason,
34715
+ ...relatedActionHandles !== void 0 ? { relatedActionHandles } : {},
34461
34716
  ...textCommand.blockers ? { blockers: textCommand.blockers } : {}
34462
- }, target, commandAddressForTarget(target, scopeKind));
34717
+ }, target, canonicalAddress);
34463
34718
  }
34464
34719
  if (target.commandFamily === "comment-revision") {
34465
34720
  const isOpen = target.review?.status === "open";
@@ -34784,19 +35039,21 @@ function commandIntentsForTarget(target, scopeKind) {
34784
35039
  if (target.kind === "hyperlink-text") {
34785
35040
  return Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]);
34786
35041
  }
34787
- if (target.table?.operationScope === "text") {
34788
- return Object.freeze([
34789
- "text-leaf-edit",
34790
- "table-text-paste",
34791
- "table-text-drop",
34792
- "table-structured-fragment-paste",
34793
- "table-structured-fragment-drop"
34794
- ]);
34795
- }
34796
- if (scopeKind === "list-item" && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
34797
- return Object.freeze(["text-leaf-edit", "list-text-edit", "list-structure-action"]);
35042
+ {
35043
+ const intents = ["text-leaf-edit"];
35044
+ if (target.table?.operationScope === "text") {
35045
+ intents.push(
35046
+ "table-text-paste",
35047
+ "table-text-drop",
35048
+ "table-structured-fragment-paste",
35049
+ "table-structured-fragment-drop"
35050
+ );
35051
+ }
35052
+ if ((scopeKind === "list-item" || target.listAddress !== void 0) && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
35053
+ intents.push("list-text-edit", "list-structure-action");
35054
+ }
35055
+ return freezeCommandIntents(intents);
34798
35056
  }
34799
- return Object.freeze(["text-leaf-edit"]);
34800
35057
  case "field":
34801
35058
  return Object.freeze(
34802
35059
  target.field?.fieldFamily === "TOC" ? ["toc-refresh", "field-update"] : ["field-update"]
@@ -34943,7 +35200,7 @@ function readbackForTarget(document2, target, runtimeTextCommand) {
34943
35200
  source: "canonical-text-leaf"
34944
35201
  };
34945
35202
  }
34946
- function projectTarget(document2, target, relation, workflowBlockers, scopeKind) {
35203
+ function projectTarget(document2, target, relation, workflowBlockers, scopeKind, layoutIdentity) {
34947
35204
  const runtimeTextCommand = runtimeTextCommandEvidence(target, workflowBlockers);
34948
35205
  const readback = readbackForTarget(document2, target, runtimeTextCommand);
34949
35206
  return {
@@ -34964,6 +35221,7 @@ function projectTarget(document2, target, relation, workflowBlockers, scopeKind)
34964
35221
  ...target.table ? { table: target.table } : {},
34965
35222
  ...target.editableOwner ? { editableOwner: target.editableOwner } : {},
34966
35223
  ...target.canonicalAddress ? { canonicalAddress: target.canonicalAddress } : {},
35224
+ ...layoutIdentity ? { layoutIdentity } : {},
34967
35225
  staleCheck: target.staleCheck,
34968
35226
  posture: target.posture,
34969
35227
  ...workflowBlockers.length > 0 ? { workflowBlockers: Object.freeze([...workflowBlockers]) } : {},
@@ -34979,7 +35237,7 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
34979
35237
  facts.push(projectWorkflowBlockerFact(fact));
34980
35238
  factsByTargetKey.set(fact.targetKey, facts);
34981
35239
  }
34982
- const entries = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
35240
+ const relatedTargets = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
34983
35241
  const relation = relationForTarget(target, scope, entry);
34984
35242
  if (!relation) return null;
34985
35243
  const workflowBlockers = Object.freeze(
@@ -34987,8 +35245,19 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
34987
35245
  (left, right) => left.source.localeCompare(right.source) || left.blocker.localeCompare(right.blocker) || left.refusalId.localeCompare(right.refusalId)
34988
35246
  )
34989
35247
  );
34990
- return projectTarget(document2, target, relation, workflowBlockers, scope.kind);
34991
- }).filter((target) => target !== null).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
35248
+ return { target, relation, workflowBlockers };
35249
+ }).filter((target) => target !== null);
35250
+ const layoutIndexes = relatedTargets.length > 0 ? layoutIdentityIndexes(document2) : void 0;
35251
+ const entries = relatedTargets.map(
35252
+ ({ target, relation, workflowBlockers }) => projectTarget(
35253
+ document2,
35254
+ target,
35255
+ relation,
35256
+ workflowBlockers,
35257
+ scope.kind,
35258
+ layoutIndexes ? layoutIdentityForTarget(target, layoutIndexes) : void 0
35259
+ )
35260
+ ).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
34992
35261
  const blockers = /* @__PURE__ */ new Set();
34993
35262
  let supportedTextTargetCount = 0;
34994
35263
  for (const target of entries) {
@@ -40795,15 +41064,65 @@ function projectEntry2(entry) {
40795
41064
  ...entry.posture.preserveOnly ? { preserveOnly: true } : {}
40796
41065
  };
40797
41066
  }
41067
+ function familyKindFor(entry) {
41068
+ return entry.objectKind ?? "unknown";
41069
+ }
41070
+ function familyStatusFor(entries) {
41071
+ if (entries.some((entry) => entry.runtimeCommand.status === "supported")) {
41072
+ return "supported";
41073
+ }
41074
+ if (entries.length > 0 && entries.every(
41075
+ (entry) => entry.preserveOnly === true || entry.commandFamily === "preserve-only-refusal"
41076
+ )) {
41077
+ return "preserve-only";
41078
+ }
41079
+ return "blocked";
41080
+ }
41081
+ function summarizeFamily(objectKind, entries) {
41082
+ const sortedEntries = [...entries].sort(
41083
+ (left, right) => left.targetKey.localeCompare(right.targetKey)
41084
+ );
41085
+ const preserveOnly = sortedEntries.some((entry) => entry.preserveOnly === true);
41086
+ return {
41087
+ objectKind,
41088
+ status: familyStatusFor(sortedEntries),
41089
+ count: sortedEntries.length,
41090
+ targetKeys: unique2(sortedEntries.map((entry) => entry.targetKey)),
41091
+ relations: unique2(sortedEntries.map((entry) => entry.relation)),
41092
+ commandFamilies: unique2(sortedEntries.map((entry) => entry.commandFamily)),
41093
+ runtimeIntents: unique2(
41094
+ sortedEntries.flatMap((entry) => [...entry.runtimeCommand.intents])
41095
+ ),
41096
+ blockers: unique2(sortedEntries.flatMap((entry) => [...entry.blockers])),
41097
+ ...preserveOnly ? { preserveOnly: true } : {}
41098
+ };
41099
+ }
41100
+ function summarizeFamilies(entries) {
41101
+ const byFamily = /* @__PURE__ */ new Map();
41102
+ for (const entry of entries) {
41103
+ const objectKind = familyKindFor(entry);
41104
+ const bucket = byFamily.get(objectKind);
41105
+ if (bucket) {
41106
+ bucket.push(entry);
41107
+ continue;
41108
+ }
41109
+ byFamily.set(objectKind, [entry]);
41110
+ }
41111
+ return Object.freeze(
41112
+ [...byFamily.entries()].map(([objectKind, familyEntries]) => summarizeFamily(objectKind, familyEntries)).sort((left, right) => left.objectKind.localeCompare(right.objectKind))
41113
+ );
41114
+ }
40798
41115
  function deriveScopeObjectEvidence(editableTargets) {
40799
41116
  const entries = Object.freeze(
40800
41117
  [...editableTargets?.entries ?? []].filter(isObjectTarget).map(projectEntry2).sort((left, right) => left.targetKey.localeCompare(right.targetKey))
40801
41118
  );
40802
41119
  const blockers = unique2(entries.flatMap((entry) => [...entry.blockers]));
41120
+ const families = summarizeFamilies(entries);
40803
41121
  return {
40804
41122
  status: entries.length > 0 ? "present" : "none",
40805
41123
  count: entries.length,
40806
41124
  blockers,
41125
+ families,
40807
41126
  entries
40808
41127
  };
40809
41128
  }
@@ -46803,6 +47122,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
46803
47122
  ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
46804
47123
  });
46805
47124
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
47125
+ const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
46806
47126
  const activeStoryBlockPathBase = getActiveStoryBlockPathBase(document2, activeStory);
46807
47127
  chartModelStore.beginBuildPass(document2);
46808
47128
  const unsupportedNumberingFormatsSeen = options.emitFormattingTelemetry ? /* @__PURE__ */ new Set() : null;
@@ -46826,6 +47146,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
46826
47146
  activeStory.kind !== "main",
46827
47147
  !isInViewport,
46828
47148
  editableTargetsByBlockPath,
47149
+ layoutIdentitiesByBlockPath,
46829
47150
  `${activeStoryBlockPathBase}/block[${index}]`
46830
47151
  );
46831
47152
  if (isInViewport) {
@@ -46892,7 +47213,7 @@ function isIndexInAnyRange(index, ranges) {
46892
47213
  }
46893
47214
  return false;
46894
47215
  }
46895
- function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath) {
47216
+ function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, blockPath) {
46896
47217
  if (block.type === "opaque_block") {
46897
47218
  const fragment = getOpaqueFragment(document2.preservation, block.fragmentId);
46898
47219
  const descriptor = fragment ? describeOpaqueFragment(fragment) : null;
@@ -46929,6 +47250,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
46929
47250
  promoteSecondaryStoryTextBoxes,
46930
47251
  cullBuild,
46931
47252
  editableTargetsByBlockPath,
47253
+ layoutIdentitiesByBlockPath,
46932
47254
  blockPath
46933
47255
  );
46934
47256
  }
@@ -46969,6 +47291,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
46969
47291
  promoteSecondaryStoryTextBoxes,
46970
47292
  cullBuild,
46971
47293
  editableTargetsByBlockPath,
47294
+ layoutIdentitiesByBlockPath,
46972
47295
  blockPath
46973
47296
  );
46974
47297
  }
@@ -47051,10 +47374,11 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
47051
47374
  cullBuild,
47052
47375
  editableTargetsByBlockPath,
47053
47376
  blockPath,
47054
- blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0
47377
+ blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0,
47378
+ blockPath !== void 0 ? layoutIdentitiesByBlockPath?.get(blockPath) : void 0
47055
47379
  );
47056
47380
  }
47057
- function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, tablePath) {
47381
+ function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, tablePath) {
47058
47382
  const lockedFragmentIds = [];
47059
47383
  let innerCursor = cursor;
47060
47384
  if (cullBuild) {
@@ -47071,6 +47395,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
47071
47395
  promoteSecondaryStoryTextBoxes,
47072
47396
  true,
47073
47397
  editableTargetsByBlockPath,
47398
+ layoutIdentitiesByBlockPath,
47074
47399
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
47075
47400
  );
47076
47401
  lockedFragmentIds.push(...result.lockedFragmentIds);
@@ -47086,6 +47411,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
47086
47411
  to: innerCursor,
47087
47412
  styleId: table.styleId,
47088
47413
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
47414
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
47089
47415
  gridColumns: table.gridColumns,
47090
47416
  rows: []
47091
47417
  },
@@ -47123,6 +47449,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
47123
47449
  promoteSecondaryStoryTextBoxes,
47124
47450
  cullBuild,
47125
47451
  editableTargetsByBlockPath,
47452
+ layoutIdentitiesByBlockPath,
47126
47453
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
47127
47454
  );
47128
47455
  cellContent.push(result.block);
@@ -47211,6 +47538,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
47211
47538
  to: innerCursor,
47212
47539
  styleId: table.styleId,
47213
47540
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
47541
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
47214
47542
  gridColumns: table.gridColumns,
47215
47543
  ...gridColumnsRelative ? { gridColumnsRelative } : {},
47216
47544
  ...resolvedTable.table?.alignment ? { alignment: resolvedTable.table.alignment } : {},
@@ -47398,7 +47726,7 @@ function resolveCellBorderStyles(borders, tableBorders, position) {
47398
47726
  if (left) result.borderLeft = left;
47399
47727
  return result;
47400
47728
  }
47401
- function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, sdtPath) {
47729
+ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, sdtPath) {
47402
47730
  const children = [];
47403
47731
  const lockedFragmentIds = [];
47404
47732
  let innerCursor = cursor;
@@ -47415,6 +47743,7 @@ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formatting
47415
47743
  promoteSecondaryStoryTextBoxes,
47416
47744
  cullBuild,
47417
47745
  editableTargetsByBlockPath,
47746
+ layoutIdentitiesByBlockPath,
47418
47747
  sdtPath !== void 0 ? `${sdtPath}/block[${childIndex}]` : void 0
47419
47748
  );
47420
47749
  children.push(result.block);
@@ -47457,7 +47786,7 @@ function getRecursableSdtBlockedReasonCode(block) {
47457
47786
  ].filter(Boolean).join(" ").toLowerCase();
47458
47787
  return searchText.includes("table of contents") || /\btoc\b/u.test(searchText) ? "workflow_preserve_only" : null;
47459
47788
  }
47460
- function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget) {
47789
+ function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget, layoutIdentity) {
47461
47790
  const themeResolver = formattingContext.theme;
47462
47791
  const effectiveNumbering = formattingContext.resolveEffectiveParagraphNumbering(paragraph);
47463
47792
  let resolvedNumbering = null;
@@ -47480,6 +47809,7 @@ function createParagraphBlock(paragraphIndex, paragraph, document2, start, forma
47480
47809
  from: start,
47481
47810
  to: start,
47482
47811
  ...editableTarget !== void 0 ? { editableTarget } : {},
47812
+ ...layoutIdentity !== void 0 ? { layoutIdentity } : {},
47483
47813
  ...paragraph.styleId ? { styleId: paragraph.styleId } : {},
47484
47814
  ...effectiveNumbering ? { numbering: effectiveNumbering } : {},
47485
47815
  ...resolvedNumbering ? {
@@ -54146,9 +54476,327 @@ function createEmpiricalProvider() {
54146
54476
  return createEmpiricalBackend();
54147
54477
  }
54148
54478
 
54479
+ // src/runtime/layout/persistent-layout-measurement-cache.ts
54480
+ init_resolved_formatting_state();
54481
+ var DEFAULT_MAX_ENTRIES = 1e4;
54482
+ var DEFAULT_MAX_ESTIMATED_BYTES = 50 * 1024 * 1024;
54483
+ function createPersistentLayoutMeasurementCache(options = {}) {
54484
+ const maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_MAX_ENTRIES);
54485
+ const maxEstimatedBytes = Math.max(
54486
+ 1024,
54487
+ options.maxEstimatedBytes ?? DEFAULT_MAX_ESTIMATED_BYTES
54488
+ );
54489
+ const entries = /* @__PURE__ */ new Map();
54490
+ const counters = {
54491
+ hits: 0,
54492
+ misses: 0,
54493
+ sets: 0,
54494
+ evictions: 0,
54495
+ clears: 0,
54496
+ estimatedBytes: 0,
54497
+ invalidatedByReason: {}
54498
+ };
54499
+ function evictOldest() {
54500
+ const oldest = entries.keys().next();
54501
+ if (oldest.done) return;
54502
+ const entry = entries.get(oldest.value);
54503
+ if (entry) {
54504
+ counters.estimatedBytes -= entry.estimatedBytes;
54505
+ }
54506
+ entries.delete(oldest.value);
54507
+ counters.evictions += 1;
54508
+ }
54509
+ return {
54510
+ get(key) {
54511
+ const cacheKey = serializeCacheKey(key);
54512
+ const entry = entries.get(cacheKey);
54513
+ if (!entry) {
54514
+ counters.misses += 1;
54515
+ return void 0;
54516
+ }
54517
+ entries.delete(cacheKey);
54518
+ entries.set(cacheKey, entry);
54519
+ counters.hits += 1;
54520
+ return entry.value;
54521
+ },
54522
+ set(key, value) {
54523
+ const cacheKey = serializeCacheKey(key);
54524
+ const previous = entries.get(cacheKey);
54525
+ if (previous) {
54526
+ counters.estimatedBytes -= previous.estimatedBytes;
54527
+ entries.delete(cacheKey);
54528
+ }
54529
+ const estimatedBytes = estimateBytes(key, value);
54530
+ entries.set(cacheKey, { key, value, estimatedBytes });
54531
+ counters.estimatedBytes += estimatedBytes;
54532
+ counters.sets += 1;
54533
+ while (entries.size > maxEntries || counters.estimatedBytes > maxEstimatedBytes) {
54534
+ evictOldest();
54535
+ }
54536
+ },
54537
+ invalidate(reason, predicate) {
54538
+ if (!predicate) {
54539
+ const removed2 = entries.size;
54540
+ counters.estimatedBytes = 0;
54541
+ entries.clear();
54542
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed2;
54543
+ return;
54544
+ }
54545
+ let removed = 0;
54546
+ for (const [cacheKey, entry] of entries) {
54547
+ if (!predicate(entry.key)) continue;
54548
+ counters.estimatedBytes -= entry.estimatedBytes;
54549
+ entries.delete(cacheKey);
54550
+ removed += 1;
54551
+ }
54552
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
54553
+ },
54554
+ clear(reason) {
54555
+ const removed = entries.size;
54556
+ counters.estimatedBytes = 0;
54557
+ entries.clear();
54558
+ counters.clears += 1;
54559
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
54560
+ },
54561
+ stats() {
54562
+ return {
54563
+ hits: counters.hits,
54564
+ misses: counters.misses,
54565
+ sets: counters.sets,
54566
+ evictions: counters.evictions,
54567
+ clears: counters.clears,
54568
+ size: entries.size,
54569
+ estimatedBytes: Math.max(0, counters.estimatedBytes),
54570
+ invalidatedByReason: { ...counters.invalidatedByReason }
54571
+ };
54572
+ }
54573
+ };
54574
+ }
54575
+ function createCachedLayoutMeasurementProvider(delegate, options = {}) {
54576
+ const cache = options.cache ?? createPersistentLayoutMeasurementCache(options);
54577
+ function emitCounter(type, key) {
54578
+ const telemetryBus = options.telemetryBus;
54579
+ if (!telemetryBus?.isEnabled("layout")) return;
54580
+ telemetryBus.emitLazy("layout", () => ({
54581
+ type,
54582
+ payload: key ? {
54583
+ measurementKind: key.measurementKind,
54584
+ blockId: key.blockId,
54585
+ backendVersion: key.backendVersion
54586
+ } : void 0
54587
+ }));
54588
+ }
54589
+ return {
54590
+ get fidelity() {
54591
+ return delegate.fidelity;
54592
+ },
54593
+ measurementCache: cache,
54594
+ measurementCacheStats() {
54595
+ return cache.stats();
54596
+ },
54597
+ whenReady() {
54598
+ return delegate.whenReady();
54599
+ },
54600
+ measureLineFragments(input) {
54601
+ const key = buildLineFragmentsKey(input, delegate, options);
54602
+ const cached = cache.get(key);
54603
+ if (cached) {
54604
+ emitCounter("pageRender.measurement.hit", key);
54605
+ return cloneMeasuredLineFragments(cached);
54606
+ }
54607
+ emitCounter("pageRender.measurement.miss", key);
54608
+ const measured = delegate.measureLineFragments(input);
54609
+ cache.set(key, cloneMeasuredLineFragments(measured));
54610
+ return measured;
54611
+ },
54612
+ measureInlineObject(input) {
54613
+ const key = buildInlineObjectKey(input, delegate, options);
54614
+ const cached = cache.get(key);
54615
+ if (cached) {
54616
+ emitCounter("pageRender.measurement.hit", key);
54617
+ return { ...cached };
54618
+ }
54619
+ emitCounter("pageRender.measurement.miss", key);
54620
+ const measured = delegate.measureInlineObject(input);
54621
+ cache.set(key, { ...measured });
54622
+ return measured;
54623
+ },
54624
+ measureTableBlock(input) {
54625
+ const key = buildTableBlockKey(input, delegate, options);
54626
+ const cached = cache.get(key);
54627
+ if (cached) {
54628
+ emitCounter("pageRender.measurement.hit", key);
54629
+ return cloneMeasuredTableBlock(cached);
54630
+ }
54631
+ emitCounter("pageRender.measurement.miss", key);
54632
+ const measured = delegate.measureTableBlock(input);
54633
+ cache.set(key, cloneMeasuredTableBlock(measured));
54634
+ return measured;
54635
+ },
54636
+ invalidateCache() {
54637
+ delegate.invalidateCache();
54638
+ cache.clear("provider.invalidateCache");
54639
+ emitCounter("pageRender.measurement.invalidated.provider.invalidateCache");
54640
+ }
54641
+ };
54642
+ }
54643
+ function isCachedLayoutMeasurementProvider(provider) {
54644
+ return typeof provider.measurementCacheStats === "function";
54645
+ }
54646
+ function buildLineFragmentsKey(input, delegate, options) {
54647
+ const block = input.block;
54648
+ const identity = measurementIdentityForBlock(block);
54649
+ return {
54650
+ measurementKind: "line-fragments",
54651
+ blockId: identity.blockId,
54652
+ blockPath: identity.blockPath,
54653
+ contentHash: hashStable2({
54654
+ kind: block.kind,
54655
+ identity: identity.identityHash,
54656
+ segments: block.segments,
54657
+ numbering: block.numbering,
54658
+ numberingPrefix: block.numberingPrefix,
54659
+ numberingSuffix: block.numberingSuffix,
54660
+ resolvedNumbering: block.resolvedNumbering
54661
+ }),
54662
+ formattingHash: createLayoutReadyFormattingSnapshot(
54663
+ input.formatting,
54664
+ input.runs
54665
+ ).hash,
54666
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
54667
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
54668
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
54669
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
54670
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
54671
+ };
54672
+ }
54673
+ function buildInlineObjectKey(input, delegate, options) {
54674
+ return {
54675
+ measurementKind: "inline-object",
54676
+ blockId: "inline-object",
54677
+ blockPath: "inline-object",
54678
+ contentHash: hashStable2({ display: input.display }),
54679
+ formattingHash: "format:none",
54680
+ constraintsHash: hashStable2({
54681
+ widthTwips: input.widthTwips,
54682
+ heightTwips: input.heightTwips
54683
+ }),
54684
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
54685
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
54686
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
54687
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
54688
+ };
54689
+ }
54690
+ function buildTableBlockKey(input, delegate, options) {
54691
+ const block = input.block;
54692
+ const identity = measurementIdentityForBlock(block);
54693
+ return {
54694
+ measurementKind: "table-block",
54695
+ blockId: identity.blockId,
54696
+ blockPath: identity.blockPath,
54697
+ contentHash: hashStable2({
54698
+ kind: block.kind,
54699
+ identity: identity.identityHash,
54700
+ rows: block.rows,
54701
+ gridColumns: block.gridColumns,
54702
+ gridColumnsRelative: block.gridColumnsRelative,
54703
+ tableResolved: block.tableResolved,
54704
+ tblLook: block.tblLook
54705
+ }),
54706
+ formattingHash: hashStable2({
54707
+ styleId: block.styleId,
54708
+ alignment: block.alignment
54709
+ }),
54710
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
54711
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
54712
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
54713
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
54714
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
54715
+ };
54716
+ }
54717
+ function measurementIdentityForBlock(block) {
54718
+ const identity = block.layoutIdentity;
54719
+ if (!identity) {
54720
+ return {
54721
+ blockId: block.blockId,
54722
+ blockPath: blockPathForBlock(block),
54723
+ identityHash: "identity:legacy"
54724
+ };
54725
+ }
54726
+ return {
54727
+ blockId: identity.blockId,
54728
+ blockPath: identity.blockPath,
54729
+ identityHash: hashStable2({
54730
+ storyKey: identity.storyKey,
54731
+ blockId: identity.blockId,
54732
+ blockPath: identity.blockPath,
54733
+ sourceRef: identity.sourceRef,
54734
+ table: identity.table,
54735
+ list: identity.list,
54736
+ field: identity.field,
54737
+ object: identity.object
54738
+ })
54739
+ };
54740
+ }
54741
+ function blockPathForBlock(block) {
54742
+ return `${block.from}:${block.to}`;
54743
+ }
54744
+ function resolveOptionValue(option, fallback) {
54745
+ if (typeof option === "function") return option();
54746
+ return option ?? fallback;
54747
+ }
54748
+ function serializeCacheKey(key) {
54749
+ return stableStringify4(key);
54750
+ }
54751
+ function estimateBytes(key, value) {
54752
+ return stableStringify4(key).length + stableStringify4(value).length;
54753
+ }
54754
+ function cloneMeasuredLineFragments(value) {
54755
+ return {
54756
+ lineCount: value.lineCount,
54757
+ maxLineWidth: value.maxLineWidth,
54758
+ lineHeights: [...value.lineHeights]
54759
+ };
54760
+ }
54761
+ function cloneMeasuredTableBlock(value) {
54762
+ return {
54763
+ totalHeightTwips: value.totalHeightTwips,
54764
+ rowHeights: [...value.rowHeights]
54765
+ };
54766
+ }
54767
+ function hashStable2(value) {
54768
+ return fnv1a3(stableStringify4(value)).toString(36);
54769
+ }
54770
+ function stableStringify4(value) {
54771
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
54772
+ if (Array.isArray(value)) {
54773
+ return `[${value.map((item) => stableStringify4(item)).join(",")}]`;
54774
+ }
54775
+ if (value instanceof Map) {
54776
+ return stableStringify4(Array.from(value.entries()));
54777
+ }
54778
+ const object = value;
54779
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
54780
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify4(object[key])}`).join(",")}}`;
54781
+ }
54782
+ function fnv1a3(input) {
54783
+ let hash = 2166136261;
54784
+ for (let i = 0; i < input.length; i += 1) {
54785
+ hash ^= input.charCodeAt(i);
54786
+ hash = Math.imul(hash, 16777619);
54787
+ }
54788
+ return hash >>> 0;
54789
+ }
54790
+
54149
54791
  // src/runtime/layout/layout-engine-instance.ts
54150
54792
  var FULL_VIEWPORT_WINDOW_KEY = "full";
54151
54793
  var DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER = 1;
54794
+ function indexLayoutIdentitiesByBlockPath(identities) {
54795
+ return new Map(identities.map((identity) => [identity.blockPath, identity]));
54796
+ }
54797
+ function indexEditableTargetsByBlockPath2(targets) {
54798
+ return new Map(targets.map((target) => [target.blockPath, target]));
54799
+ }
54152
54800
  function normalizeViewportPageWindow(window2) {
54153
54801
  if (!window2) return void 0;
54154
54802
  const buffer = Number.isFinite(window2.bufferPages) ? Math.max(0, Math.floor(window2.bufferPages ?? 0)) : DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER;
@@ -54344,8 +54992,22 @@ function recordFullRebuildReason(reasonKind) {
54344
54992
  probe.invalidationCounts[totalKey] = (probe.invalidationCounts[totalKey] ?? 0) + 1;
54345
54993
  probe.invalidationCounts[byReasonKey] = (probe.invalidationCounts[byReasonKey] ?? 0) + 1;
54346
54994
  }
54995
+ function wrapMeasurementProvider(provider, measurementCache, telemetryBus) {
54996
+ if (!measurementCache || isCachedLayoutMeasurementProvider(provider)) {
54997
+ return provider;
54998
+ }
54999
+ const cacheOptions = measurementCache === true ? {} : measurementCache;
55000
+ return createCachedLayoutMeasurementProvider(provider, {
55001
+ ...cacheOptions,
55002
+ ...telemetryBus ? { telemetryBus } : {}
55003
+ });
55004
+ }
54347
55005
  function createLayoutEngine(options = {}) {
54348
- let measurementProvider = options.measurementProvider ?? createEmpiricalMeasurementProvider();
55006
+ let measurementProvider = wrapMeasurementProvider(
55007
+ options.measurementProvider ?? createEmpiricalMeasurementProvider(),
55008
+ options.measurementCache,
55009
+ options.telemetryBus
55010
+ );
54349
55011
  const autoUpgradeToCanvas = options.autoUpgradeToCanvasBackend !== false;
54350
55012
  const telemetryBus = options.telemetryBus;
54351
55013
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
@@ -54445,13 +55107,20 @@ function createLayoutEngine(options = {}) {
54445
55107
  const pageCountBeforeRecompute = previousPageCount;
54446
55108
  const document2 = input.document;
54447
55109
  const viewportWindow = normalizeViewportPageWindow(input.viewportPageWindow);
55110
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
55111
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
55112
+ layoutInputs.layoutIdentities
55113
+ );
55114
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
55115
+ layoutInputs.editableTargets
55116
+ );
54448
55117
  const mainSurface = createEditorSurfaceSnapshot(
54449
55118
  document2,
54450
55119
  createSelectionSnapshot(0, 0),
54451
- MAIN_STORY_TARGET
55120
+ MAIN_STORY_TARGET,
55121
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
54452
55122
  );
54453
55123
  const sections = buildResolvedSections(document2);
54454
- const layoutInputs = collectCanonicalLayoutInputs(document2);
54455
55124
  const fieldRegions = layoutInputs.fieldRegions;
54456
55125
  const pageStack = buildPageStackWithSplits(
54457
55126
  document2,
@@ -54563,10 +55232,18 @@ function createLayoutEngine(options = {}) {
54563
55232
  const firstDirty = range.firstPageIndex;
54564
55233
  if (firstDirty < 0 || firstDirty >= priorGraph.pages.length) return null;
54565
55234
  const document2 = input.document;
55235
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
55236
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
55237
+ layoutInputs.layoutIdentities
55238
+ );
55239
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
55240
+ layoutInputs.editableTargets
55241
+ );
54566
55242
  const mainSurface = createEditorSurfaceSnapshot(
54567
55243
  document2,
54568
55244
  createSelectionSnapshot(0, 0),
54569
- MAIN_STORY_TARGET
55245
+ MAIN_STORY_TARGET,
55246
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
54570
55247
  );
54571
55248
  const sections = buildResolvedSections(document2);
54572
55249
  const dirtyPage = priorGraph.pages[firstDirty];
@@ -54595,7 +55272,6 @@ function createLayoutEngine(options = {}) {
54595
55272
  const freshSnapshotsToRebuild = freshSnapshots.slice(0, convergenceIndex);
54596
55273
  const convergedTailStart = convergenceIndex < freshSnapshots.length ? firstDirty + convergenceIndex : void 0;
54597
55274
  const freshStories = resolvePageStories(freshSnapshotsToRebuild);
54598
- const layoutInputs = collectCanonicalLayoutInputs(document2);
54599
55275
  const fieldRegions = layoutInputs.fieldRegions;
54600
55276
  const freshBodyFragmentsByPageIndex = projectSurfaceBlocksToPageFragments(
54601
55277
  mainSurface,
@@ -54727,7 +55403,11 @@ function createLayoutEngine(options = {}) {
54727
55403
  try {
54728
55404
  const mod = await Promise.resolve().then(() => (init_measurement_backend_canvas(), measurement_backend_canvas_exports));
54729
55405
  const canvasProvider = mod.createCanvasBackend();
54730
- measurementProvider = canvasProvider;
55406
+ measurementProvider = wrapMeasurementProvider(
55407
+ canvasProvider,
55408
+ options.measurementCache,
55409
+ telemetryBus
55410
+ );
54731
55411
  cachedKey = null;
54732
55412
  cachedGraph = null;
54733
55413
  cachedFormatting = null;
@@ -54812,7 +55492,11 @@ function createLayoutEngine(options = {}) {
54812
55492
  },
54813
55493
  swapMeasurementProvider(provider) {
54814
55494
  const previousFidelity = measurementProvider.fidelity;
54815
- measurementProvider = provider;
55495
+ measurementProvider = wrapMeasurementProvider(
55496
+ provider,
55497
+ options.measurementCache,
55498
+ telemetryBus
55499
+ );
54816
55500
  if (previousFidelity !== provider.fidelity) {
54817
55501
  cachedKey = null;
54818
55502
  cachedGraph = null;
@@ -54841,6 +55525,10 @@ function createLayoutEngine(options = {}) {
54841
55525
  cachedFormatting = null;
54842
55526
  cachedMapper = null;
54843
55527
  },
55528
+ getMeasurementCacheStats() {
55529
+ if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
55530
+ return measurementProvider.measurementCacheStats();
55531
+ },
54844
55532
  /**
54845
55533
  * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
54846
55534
  * Populates both `cachedGraph` and `cachedKey` (keyed on the provided
@@ -58865,7 +59553,7 @@ var listOperationMetadata = actionMethodMetadata(
58865
59553
  "listOperation",
58866
59554
  "mutate",
58867
59555
  "actions-list-operation",
58868
- "Apply list toggle, indent, outdent, restart, or continue-numbering commands to paragraph-like scope handles.",
59556
+ "Apply list toggle, indent, outdent, restart, or continue-numbering commands through opaque list action handles or command-safe list scope targets.",
58869
59557
  {
58870
59558
  uiVisible: true,
58871
59559
  expectsUxResponse: "inline-change",
@@ -60600,25 +61288,16 @@ function checkPlanStepCapability(runtime, step, before) {
60600
61288
  return null;
60601
61289
  }
60602
61290
  if (step.kind === "listOperation") {
60603
- const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
61291
+ const resolved = resolveListOperationTarget(runtime, step.target);
60604
61292
  if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
60605
- const scope = resolved.target.scope;
60606
- if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
60607
- return blocker(
60608
- `actions:list-operation:target-kind-unsupported:${scope.kind}`,
60609
- "unsupported",
60610
- "List operations require a paragraph-like scope handle.",
60611
- "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
60612
- );
60613
- }
60614
- if (paragraphIndexFromHandle(scope.handle) === null) {
60615
- return blockerWithOwner(
60616
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
60617
- "blocked",
60618
- "The list operation target did not resolve to a current paragraph index.",
60619
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
60620
- "L08 semantic scope compiler"
60621
- );
61293
+ const command = runtimeListCommandForOperation(step.operation);
61294
+ const preview = createListsFamily(runtime).previewCommand({
61295
+ ...resolved.input,
61296
+ command,
61297
+ ...step.operation.kind === "restart-numbering" && step.operation.startAt !== void 0 ? { startAt: step.operation.startAt } : {}
61298
+ });
61299
+ if (preview.blockers.length > 0) {
61300
+ return listBlockerDetails("actions:list-operation:preflight", preview.blockers)[0] ?? null;
60622
61301
  }
60623
61302
  return null;
60624
61303
  }
@@ -60775,70 +61454,253 @@ function applyModeledCommand(runtime, targetInput, kind, commandInput, reference
60775
61454
  };
60776
61455
  }
60777
61456
  function applyListOperation(runtime, input) {
60778
- const resolvedScope = resolveScopeExactTarget(runtime, input.target, "listOperation");
60779
- if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
61457
+ const resolvedTarget = resolveListOperationTarget(runtime, input.target);
61458
+ if (!resolvedTarget.ok) return blockedApplyFromResolution(resolvedTarget);
61459
+ const command = runtimeListCommandForOperation(input.operation);
61460
+ const result = createListsFamily(runtime).applyCommand({
61461
+ ...resolvedTarget.input,
61462
+ command,
61463
+ ...input.operation.kind === "restart-numbering" && input.operation.startAt !== void 0 ? { startAt: input.operation.startAt } : {}
61464
+ });
61465
+ if (!result.applied) {
61466
+ const details = result.blockers.length > 0 ? listBlockerDetails("actions:list-operation", result.blockers) : [
61467
+ blockerWithOwner(
61468
+ `actions:list-operation:runtime-noop:${resolvedTarget.before?.addressKey ?? "unknown"}`,
61469
+ "blocked",
61470
+ "The runtime list command produced no document change for the selected list target.",
61471
+ "Refresh the list action handle and retry; route persistent failures with before/after readback to L07 runtime list commands.",
61472
+ "L07 runtime list commands"
61473
+ )
61474
+ ];
61475
+ return {
61476
+ status: details.some((detail) => detail.category === "unsupported") ? "unsupported" : "blocked",
61477
+ applied: false,
61478
+ changed: false,
61479
+ target: summarizeListTarget(resolvedTarget, result.before ?? resolvedTarget.before),
61480
+ blockers: Object.freeze(details.map((detail) => detail.code)),
61481
+ blockerDetails: Object.freeze(details),
61482
+ listReadback: listOperationReadback(result, resolvedTarget.before)
61483
+ };
61484
+ }
61485
+ const compiledAfter = resolvedTarget.scopeHandle ? createScopeCompilerService(runtime).compileScopeById(resolvedTarget.scopeHandle.scopeId) : null;
61486
+ const target = compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeListTarget(resolvedTarget, result.after ?? result.before ?? resolvedTarget.before);
61487
+ return {
61488
+ status: "applied",
61489
+ applied: true,
61490
+ changed: result.applied,
61491
+ target,
61492
+ commandReference: {
61493
+ command: commandReferenceForListCommand(result.command),
61494
+ actorId: input.actorId ?? "v3-ai-api",
61495
+ origin: input.origin ?? "agent",
61496
+ emittedAtUtc: currentAuditTimestamp(runtime)
61497
+ },
61498
+ listReadback: listOperationReadback(result, resolvedTarget.before)
61499
+ };
61500
+ }
61501
+ function resolveListOperationTarget(runtime, targetInput) {
61502
+ const lists = createListsFamily(runtime);
61503
+ if ("actionHandle" in targetInput) {
61504
+ const addressKey = listAddressKeyFromActionHandle(targetInput.actionHandle);
61505
+ if (!addressKey) {
61506
+ const detail = blockerWithOwner(
61507
+ `actions:list-operation:action-handle-malformed:${targetInput.actionHandle}`,
61508
+ "unsupported",
61509
+ "List operations require an opaque list action handle from runtime.lists or list editable-target evidence.",
61510
+ "Call runtime.lists.list or refresh the scope bundle and retry with a list-action:* or scope-command:text-leaf:* list handle.",
61511
+ "L07 runtime list commands and L08 semantic scopes"
61512
+ );
61513
+ return {
61514
+ ok: false,
61515
+ blockers: Object.freeze([detail.code]),
61516
+ blockerDetails: Object.freeze([detail])
61517
+ };
61518
+ }
61519
+ const before2 = lists.get({ addressKey });
61520
+ if (!before2) {
61521
+ const detail = blockerWithOwner(
61522
+ `actions:list-operation:action-handle-not-found:${targetInput.actionHandle}`,
61523
+ "unresolved-target",
61524
+ "No current list target matches the supplied opaque action handle.",
61525
+ "Refresh runtime.lists.list or the scope bundle, then retry with the current list action handle.",
61526
+ "L07 runtime list commands"
61527
+ );
61528
+ return {
61529
+ ok: false,
61530
+ blockers: Object.freeze([detail.code]),
61531
+ blockerDetails: Object.freeze([detail])
61532
+ };
61533
+ }
61534
+ return { ok: true, input: { addressKey }, before: before2 };
61535
+ }
61536
+ const resolvedScope = resolveScopeExactTarget(runtime, targetInput, "listOperation");
61537
+ if (!resolvedScope.ok) return resolvedScope;
60780
61538
  const scope = resolvedScope.target.scope;
60781
61539
  if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
60782
- return blockedApply(
61540
+ const detail = blocker(
60783
61541
  `actions:list-operation:target-kind-unsupported:${scope.kind}`,
60784
61542
  "unsupported",
60785
- "List operations require a paragraph-like scope handle.",
60786
- "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
61543
+ "List operations require a list item or command-safe paragraph-like scope target.",
61544
+ "Use ai.actions.locateAll, ai.listScopes, or runtime.lists.list to select a current list item target."
60787
61545
  );
61546
+ return {
61547
+ ok: false,
61548
+ blockers: Object.freeze([detail.code]),
61549
+ blockerDetails: Object.freeze([detail])
61550
+ };
60788
61551
  }
60789
- const paragraphIndex = paragraphIndexFromHandle(scope.handle);
60790
- if (paragraphIndex === null) {
60791
- return blockedApply(
60792
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
61552
+ const bundle = createScopeCompilerService(runtime).compileBundleById(
61553
+ scope.handle.scopeId,
61554
+ currentAuditTimestamp(runtime)
61555
+ );
61556
+ const entries = bundle?.evidence.editableTargets?.entries ?? [];
61557
+ const listEntry = entries.find(
61558
+ (entry) => entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("list-structure-action") && entry.runtimeCommand.canonicalAddress?.operationScope === "list-text" && entry.runtimeCommand.canonicalAddress.addressKind === "list-item-text"
61559
+ );
61560
+ if (!listEntry) {
61561
+ const detail = blockerWithOwner(
61562
+ `actions:list-operation:list-target-missing:${scope.handle.scopeId}`,
60793
61563
  "blocked",
60794
- "The list operation target did not resolve to a current paragraph index.",
60795
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
60796
- [
60797
- blockerWithOwner(
60798
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
60799
- "blocked",
60800
- "The list operation target did not resolve to a current paragraph index.",
60801
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
60802
- "L08 semantic scope compiler"
60803
- )
60804
- ]
61564
+ "The selected scope does not expose a command-safe list structure target.",
61565
+ "Refresh the scope bundle and route missing list editable-target evidence to L08/L07; do not derive a paragraph index from the scope handle.",
61566
+ "L08 semantic scopes and L07 runtime list commands"
60805
61567
  );
61568
+ return {
61569
+ ok: false,
61570
+ blockers: Object.freeze([detail.code]),
61571
+ blockerDetails: Object.freeze([detail])
61572
+ };
60806
61573
  }
60807
- const command = listCommandForOperation(input.operation, paragraphIndex, actionOrigin(runtime, input));
60808
- const before = runtime.getCanonicalDocument();
60809
- runtime.dispatch(command);
60810
- const changed = runtime.getCanonicalDocument() !== before;
60811
- if (!changed) {
60812
- return blockedApply(
60813
- `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
61574
+ const currentTarget = collectEditableTargetRefs(runtime.getCanonicalDocument()).find(
61575
+ (target) => target.targetKey === listEntry.targetKey || target.listAddress?.addressKey === listEntry.runtimeCommand.canonicalAddress?.addressKey
61576
+ );
61577
+ if (!currentTarget) {
61578
+ const detail = blockerWithOwner(
61579
+ `actions:list-operation:list-target-not-current:${scope.handle.scopeId}`,
61580
+ "unresolved-target",
61581
+ "The list editable target evidence is stale in the current document.",
61582
+ "Refresh the scope bundle and retry with the current opaque list target.",
61583
+ "L08 semantic scopes and L07 runtime list commands"
61584
+ );
61585
+ return {
61586
+ ok: false,
61587
+ blockers: Object.freeze([detail.code]),
61588
+ blockerDetails: Object.freeze([detail])
61589
+ };
61590
+ }
61591
+ const before = lists.get({ editableTarget: currentTarget });
61592
+ if (!before) {
61593
+ const detail = blockerWithOwner(
61594
+ `actions:list-operation:list-readback-missing:${scope.handle.scopeId}`,
60814
61595
  "blocked",
60815
- "The list runtime command produced no document change for the selected scope.",
60816
- "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
60817
- [
60818
- blockerWithOwner(
60819
- `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
60820
- "blocked",
60821
- "The list runtime command produced no document change for the selected scope.",
60822
- "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
60823
- "L03 numbering/list semantics and L07 runtime list commands"
60824
- )
60825
- ]
61596
+ "The list target was present in scope evidence but runtime.lists could not produce readback.",
61597
+ "Route the target with editable-target evidence to L07 runtime list readback.",
61598
+ "L07 runtime list commands"
60826
61599
  );
61600
+ return {
61601
+ ok: false,
61602
+ blockers: Object.freeze([detail.code]),
61603
+ blockerDetails: Object.freeze([detail])
61604
+ };
60827
61605
  }
60828
- const compiledAfter = createScopeCompilerService(runtime).compileScopeById(scope.handle.scopeId);
60829
61606
  return {
60830
- status: "applied",
60831
- applied: true,
60832
- changed: true,
60833
- target: compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeTarget(resolvedScope.target),
60834
- commandReference: {
60835
- command: command.type,
60836
- actorId: input.actorId ?? "v3-ai-api",
60837
- origin: input.origin ?? "agent",
60838
- emittedAtUtc: command.origin?.timestamp ?? currentAuditTimestamp(runtime)
60839
- }
61607
+ ok: true,
61608
+ input: { editableTarget: currentTarget },
61609
+ before,
61610
+ scopeHandle: scope.handle
61611
+ };
61612
+ }
61613
+ function listAddressKeyFromActionHandle(actionHandle) {
61614
+ const listPrefix = "list-action:";
61615
+ if (actionHandle.startsWith(listPrefix)) return actionHandle.slice(listPrefix.length) || null;
61616
+ const scopeCommandPrefix = "scope-command:text-leaf:";
61617
+ if (actionHandle.startsWith(scopeCommandPrefix)) {
61618
+ return actionHandle.slice(scopeCommandPrefix.length) || null;
61619
+ }
61620
+ return null;
61621
+ }
61622
+ function runtimeListCommandForOperation(operation) {
61623
+ switch (operation.kind) {
61624
+ case "toggle":
61625
+ return operation.listKind === "numbered" ? "toggle-numbered" : "toggle-bulleted";
61626
+ case "indent":
61627
+ return "indent";
61628
+ case "outdent":
61629
+ return "outdent";
61630
+ case "restart-numbering":
61631
+ return "restart-numbering";
61632
+ case "continue-numbering":
61633
+ return "continue-numbering";
61634
+ }
61635
+ }
61636
+ function commandReferenceForListCommand(command) {
61637
+ switch (command) {
61638
+ case "toggle-numbered":
61639
+ case "toggle-bulleted":
61640
+ return "list.toggle";
61641
+ case "indent":
61642
+ return "list.indent";
61643
+ case "outdent":
61644
+ return "list.outdent";
61645
+ case "restart-numbering":
61646
+ return "list.restart-numbering";
61647
+ case "continue-numbering":
61648
+ return "list.continue-numbering";
61649
+ default:
61650
+ return "list.toggle";
61651
+ }
61652
+ }
61653
+ function listOperationReadback(result, fallbackBefore) {
61654
+ const before = sanitizeListReadback(result.before ?? fallbackBefore);
61655
+ const after = sanitizeListReadback(result.after);
61656
+ return {
61657
+ ...before ? { before } : {},
61658
+ ...after ? { after } : {}
60840
61659
  };
60841
61660
  }
61661
+ function sanitizeListReadback(readback) {
61662
+ if (!readback) return void 0;
61663
+ return {
61664
+ actionHandle: readback.actionHandle,
61665
+ kind: readback.kind,
61666
+ storyKey: readback.storyKey,
61667
+ addressKey: readback.addressKey,
61668
+ numberingInstanceId: readback.numberingInstanceId,
61669
+ ...readback.abstractNumberingId ? { abstractNumberingId: readback.abstractNumberingId } : {},
61670
+ level: readback.level,
61671
+ ...readback.listKind ? { listKind: readback.listKind } : {},
61672
+ editability: readback.editability,
61673
+ blockers: readback.blockers,
61674
+ supportedCommands: readback.supportedCommands,
61675
+ unsupportedCommands: readback.unsupportedCommands,
61676
+ staleDiscriminators: readback.staleDiscriminators
61677
+ };
61678
+ }
61679
+ function summarizeListTarget(target, readback) {
61680
+ const current = readback ?? target.before;
61681
+ return {
61682
+ kind: "list-item",
61683
+ ...target.scopeHandle ? { handle: target.scopeHandle } : {},
61684
+ ...current?.actionHandle ? { actionHandle: current.actionHandle } : {},
61685
+ canRewriteText: current?.editability === "editable" && (current.blockers.length ?? 0) === 0,
61686
+ canInsertAdjacentText: false,
61687
+ canFlag: Boolean(target.scopeHandle),
61688
+ canMark: Boolean(target.scopeHandle)
61689
+ };
61690
+ }
61691
+ function listBlockerDetails(prefix, blockers) {
61692
+ return Object.freeze(
61693
+ blockers.map(
61694
+ (entry) => blockerWithOwner(
61695
+ `${prefix}:${entry.code}:${entry.addressKey ?? entry.targetKey ?? "unknown"}`,
61696
+ entry.code === "list-command-unsupported" ? "unsupported" : "blocked",
61697
+ entry.message,
61698
+ "Refresh the opaque list target/readback and route persistent blockers to the owning runtime list layer.",
61699
+ entry.ownerLayer === "L07" ? "L07 runtime list commands" : entry.ownerLayer
61700
+ )
61701
+ )
61702
+ );
61703
+ }
60842
61704
  function projectTableApplyResult(result) {
60843
61705
  return {
60844
61706
  status: result.applied ? "applied" : "blocked",
@@ -60966,36 +61828,6 @@ function tableSelectionStepHasDescriptor(operation) {
60966
61828
  operation && "selectionDescriptor" in operation && operation.selectionDescriptor
60967
61829
  );
60968
61830
  }
60969
- function listCommandForOperation(operation, paragraphIndex, origin) {
60970
- switch (operation.kind) {
60971
- case "toggle":
60972
- return {
60973
- type: "list.toggle",
60974
- kind: operation.listKind,
60975
- paragraphIndexes: [paragraphIndex],
60976
- origin
60977
- };
60978
- case "indent":
60979
- return { type: "list.indent", paragraphIndexes: [paragraphIndex], origin };
60980
- case "outdent":
60981
- return { type: "list.outdent", paragraphIndexes: [paragraphIndex], origin };
60982
- case "restart-numbering":
60983
- return {
60984
- type: "list.restart-numbering",
60985
- paragraphIndex,
60986
- ...operation.startAt !== void 0 ? { startAt: operation.startAt } : {},
60987
- origin
60988
- };
60989
- case "continue-numbering":
60990
- return { type: "list.continue-numbering", paragraphIndex, origin };
60991
- }
60992
- }
60993
- function paragraphIndexFromHandle(handle) {
60994
- const raw = handle.semanticPath[handle.semanticPath.length - 1];
60995
- if (raw === void 0) return null;
60996
- const index = Number(raw);
60997
- return Number.isSafeInteger(index) && index >= 0 ? index : null;
60998
- }
60999
61831
  function actionOrigin(runtime, input) {
61000
61832
  return { source: "api", timestamp: currentAuditTimestamp(runtime) };
61001
61833
  }