@beyondwork/docx-react-component 1.0.128 → 1.0.130

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 (108) hide show
  1. package/dist/api/public-types.cjs +809 -78
  2. package/dist/api/public-types.d.cts +2 -2
  3. package/dist/api/public-types.d.ts +2 -2
  4. package/dist/api/public-types.js +4 -4
  5. package/dist/api/v3.cjs +2059 -402
  6. package/dist/api/v3.d.cts +3 -3
  7. package/dist/api/v3.d.ts +3 -3
  8. package/dist/api/v3.js +13 -13
  9. package/dist/{canonical-document-CXCFCbAz.d.cts → canonical-document-BMtONpgf.d.cts} +6 -0
  10. package/dist/{canonical-document-CXCFCbAz.d.ts → canonical-document-BMtONpgf.d.ts} +6 -0
  11. package/dist/{chunk-MWSBGJQO.js → chunk-35RHOE6I.js} +105 -4
  12. package/dist/{chunk-2QL5DAKF.js → chunk-3YCQM2RV.js} +6 -6
  13. package/dist/{chunk-ESJ2MES5.js → chunk-4YCWECLZ.js} +1 -1
  14. package/dist/{chunk-4EENH4FG.js → chunk-6TBLDBCL.js} +1 -1
  15. package/dist/{chunk-XRACP43Q.js → chunk-7G5GR3VV.js} +469 -32
  16. package/dist/{chunk-CXSYRB37.js → chunk-A3GSNB4G.js} +183 -55
  17. package/dist/{chunk-5DGKFNQT.js → chunk-A66ZVUAT.js} +150 -1
  18. package/dist/{chunk-YIYM4ZAP.js → chunk-CI2TD3T4.js} +1 -1
  19. package/dist/{chunk-TQDQU2E3.js → chunk-DGA7M77X.js} +2 -2
  20. package/dist/{chunk-EB6M3GE6.js → chunk-FM4K4XFJ.js} +100 -97
  21. package/dist/{chunk-D5HYZQTG.js → chunk-HYHCRMR7.js} +1 -1
  22. package/dist/{chunk-6F5QW44A.js → chunk-KNHMXKC6.js} +2 -2
  23. package/dist/{chunk-4YJVRIUB.js → chunk-M7YRJX6V.js} +61 -29
  24. package/dist/{chunk-KFCQYZXR.js → chunk-OVLZQ6FZ.js} +61 -0
  25. package/dist/{chunk-BYSRJ4FE.js → chunk-PHMWH23E.js} +1 -1
  26. package/dist/{chunk-ZDOAUP3V.js → chunk-Q7Y57KOK.js} +2 -2
  27. package/dist/{chunk-LZVBNDGU.js → chunk-QXKQPUOM.js} +3 -3
  28. package/dist/{chunk-CX42VC67.js → chunk-SYQWQ6FE.js} +1 -1
  29. package/dist/{chunk-KV435YXO.js → chunk-T5YYFDZB.js} +1 -1
  30. package/dist/{chunk-YHZHPXDB.js → chunk-THVM6EP5.js} +419 -24
  31. package/dist/{chunk-V6XVZFFH.js → chunk-VRKK2CSZ.js} +111 -90
  32. package/dist/{chunk-OL2UEHRP.js → chunk-WUDSNHWF.js} +1 -1
  33. package/dist/{chunk-LCYYR57Q.js → chunk-WZDKNF37.js} +666 -107
  34. package/dist/{chunk-6EROGFUF.js → chunk-YLL7MF5C.js} +444 -131
  35. package/dist/{chunk-YD2JE54B.js → chunk-ZVC23LKV.js} +1 -1
  36. package/dist/compare.cjs +100 -97
  37. package/dist/compare.d.cts +1 -1
  38. package/dist/compare.d.ts +1 -1
  39. package/dist/compare.js +3 -3
  40. package/dist/core/commands/formatting-commands.d.cts +2 -2
  41. package/dist/core/commands/formatting-commands.d.ts +2 -2
  42. package/dist/core/commands/image-commands.cjs +182 -54
  43. package/dist/core/commands/image-commands.d.cts +2 -2
  44. package/dist/core/commands/image-commands.d.ts +2 -2
  45. package/dist/core/commands/image-commands.js +5 -5
  46. package/dist/core/commands/section-layout-commands.d.cts +2 -2
  47. package/dist/core/commands/section-layout-commands.d.ts +2 -2
  48. package/dist/core/commands/style-commands.d.cts +2 -2
  49. package/dist/core/commands/style-commands.d.ts +2 -2
  50. package/dist/core/commands/table-structure-commands.cjs +182 -54
  51. package/dist/core/commands/table-structure-commands.d.cts +2 -2
  52. package/dist/core/commands/table-structure-commands.d.ts +2 -2
  53. package/dist/core/commands/table-structure-commands.js +4 -4
  54. package/dist/core/commands/text-commands.cjs +182 -54
  55. package/dist/core/commands/text-commands.d.cts +2 -2
  56. package/dist/core/commands/text-commands.d.ts +2 -2
  57. package/dist/core/commands/text-commands.js +5 -5
  58. package/dist/core/selection/mapping.d.cts +2 -2
  59. package/dist/core/selection/mapping.d.ts +2 -2
  60. package/dist/core/state/editor-state.d.cts +2 -2
  61. package/dist/core/state/editor-state.d.ts +2 -2
  62. package/dist/index.cjs +2765 -557
  63. package/dist/index.d.cts +5 -5
  64. package/dist/index.d.ts +5 -5
  65. package/dist/index.js +52 -28
  66. package/dist/io/docx-session.cjs +267 -211
  67. package/dist/io/docx-session.d.cts +4 -4
  68. package/dist/io/docx-session.d.ts +4 -4
  69. package/dist/io/docx-session.js +6 -6
  70. package/dist/legal.cjs +9 -20
  71. package/dist/legal.d.cts +1 -1
  72. package/dist/legal.d.ts +1 -1
  73. package/dist/legal.js +3 -3
  74. package/dist/{loader-19ct2Be0.d.ts → loader-B-aL5HGD.d.ts} +3 -3
  75. package/dist/{loader-CoXQ2wGd.d.cts → loader-DiY_ZgKl.d.cts} +3 -3
  76. package/dist/{measurement-backend-canvas-Q3MJMEYX.js → measurement-backend-canvas-F7ZYDACK.js} +1 -1
  77. package/dist/{public-types-B-CskQen.d.cts → public-types-DyqnxxO9.d.ts} +252 -2
  78. package/dist/{public-types-7KZsNGE2.d.ts → public-types-gvubspUI.d.cts} +252 -2
  79. package/dist/public-types.cjs +809 -78
  80. package/dist/public-types.d.cts +2 -2
  81. package/dist/public-types.d.ts +2 -2
  82. package/dist/public-types.js +4 -4
  83. package/dist/runtime/collab.d.cts +3 -3
  84. package/dist/runtime/collab.d.ts +3 -3
  85. package/dist/runtime/document-runtime.cjs +1887 -313
  86. package/dist/runtime/document-runtime.d.cts +2 -2
  87. package/dist/runtime/document-runtime.d.ts +2 -2
  88. package/dist/runtime/document-runtime.js +17 -17
  89. package/dist/{session-B5015J4v.d.cts → session-BUN6B-Vj.d.cts} +3 -3
  90. package/dist/{session-C2i8-d6v.d.ts → session-CDB0hohT.d.ts} +3 -3
  91. package/dist/session.cjs +267 -211
  92. package/dist/session.d.cts +5 -5
  93. package/dist/session.d.ts +5 -5
  94. package/dist/session.js +7 -7
  95. package/dist/tailwind.cjs +809 -78
  96. package/dist/tailwind.d.cts +2 -2
  97. package/dist/tailwind.d.ts +2 -2
  98. package/dist/tailwind.js +8 -8
  99. package/dist/{types-DNhN0WeN.d.cts → types-C4bz3kDU.d.cts} +2 -2
  100. package/dist/{types-yvrQuGX9.d.ts → types-VWH6CRvG.d.ts} +2 -2
  101. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
  102. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
  103. package/dist/ui-tailwind/editor-surface/search-plugin.js +5 -5
  104. package/dist/ui-tailwind.cjs +809 -78
  105. package/dist/ui-tailwind.d.cts +3 -3
  106. package/dist/ui-tailwind.d.ts +3 -3
  107. package/dist/ui-tailwind.js +8 -8
  108. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -31,6 +31,43 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/runtime/layout/resolved-formatting-state.ts
34
+ function createLayoutReadyFormattingSnapshot(formatting, runs = /* @__PURE__ */ new Map()) {
35
+ const snapshot = {
36
+ version: 1,
37
+ paragraph: {
38
+ spacingBefore: formatting.spacingBefore,
39
+ spacingAfter: formatting.spacingAfter,
40
+ lineHeight: formatting.lineHeight,
41
+ lineRule: formatting.lineRule,
42
+ indentLeft: formatting.indentLeft,
43
+ indentRight: formatting.indentRight,
44
+ firstLineIndent: formatting.firstLineIndent,
45
+ hangingIndent: formatting.hangingIndent,
46
+ fontSizeHalfPoints: formatting.fontSizeHalfPoints,
47
+ averageCharWidthTwips: formatting.averageCharWidthTwips,
48
+ tabStops: formatting.tabStops.map((tab) => ({ ...tab })),
49
+ defaultTabInterval: formatting.defaultTabInterval,
50
+ keepNext: formatting.keepNext,
51
+ keepLines: formatting.keepLines,
52
+ pageBreakBefore: formatting.pageBreakBefore,
53
+ widowControl: formatting.widowControl,
54
+ contextualSpacing: formatting.contextualSpacing,
55
+ ...formatting.numberingMarkerBox ? { numberingMarkerBox: { ...formatting.numberingMarkerBox } } : {}
56
+ },
57
+ runs: Array.from(runs.entries()).map(([runId, run]) => ({
58
+ runId,
59
+ ...run.fontFamily ? { fontFamily: run.fontFamily } : {},
60
+ ...typeof run.fontSizeHalfPoints === "number" ? { fontSizeHalfPoints: run.fontSizeHalfPoints } : {},
61
+ bold: Boolean(run.bold),
62
+ italic: Boolean(run.italic),
63
+ verticalAlign: run.verticalAlign ?? "baseline"
64
+ })).sort((a, b) => a.runId.localeCompare(b.runId))
65
+ };
66
+ return {
67
+ ...snapshot,
68
+ hash: hashStable(snapshot)
69
+ };
70
+ }
34
71
  function resolveBlockFormatting(block, defaultTabInterval = 720, themeFonts) {
35
72
  if (block.kind !== "paragraph") {
36
73
  return null;
@@ -273,6 +310,29 @@ function buildRunFormattingMap(block) {
273
310
  }
274
311
  return runs;
275
312
  }
313
+ function hashStable(value) {
314
+ return fnv1a(stableStringify(value)).toString(36);
315
+ }
316
+ function stableStringify(value) {
317
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
318
+ if (Array.isArray(value)) {
319
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
320
+ }
321
+ if (value instanceof Map) {
322
+ return stableStringify(Array.from(value.entries()));
323
+ }
324
+ const object = value;
325
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
326
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`).join(",")}}`;
327
+ }
328
+ function fnv1a(input) {
329
+ let hash = 2166136261;
330
+ for (let i = 0; i < input.length; i += 1) {
331
+ hash ^= input.charCodeAt(i);
332
+ hash = Math.imul(hash, 16777619);
333
+ }
334
+ return hash >>> 0;
335
+ }
276
336
  var FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_SIZE_HALF_POINTS, DEFAULT_LINE_HEIGHT_FACTOR, TWIPS_PER_POINT;
277
337
  var init_resolved_formatting_state = __esm({
278
338
  "src/runtime/layout/resolved-formatting-state.ts"() {
@@ -2877,7 +2937,7 @@ function toStableJsonValue(value) {
2877
2937
  }
2878
2938
  return result;
2879
2939
  }
2880
- function stableStringify(value) {
2940
+ function stableStringify2(value) {
2881
2941
  return JSON.stringify(toStableJsonValue(value));
2882
2942
  }
2883
2943
 
@@ -3041,7 +3101,7 @@ function migrateReviewRecordAnchor(record) {
3041
3101
  return record;
3042
3102
  }
3043
3103
  function createCanonicalDocumentSignature(document2) {
3044
- return stableStringify(document2);
3104
+ return stableStringify2(document2);
3045
3105
  }
3046
3106
  function assertCanonicalDocument(value) {
3047
3107
  const issues = validateCanonicalDocument(value);
@@ -6296,6 +6356,7 @@ function collectCanonicalLayoutInputs(doc) {
6296
6356
  const blockContexts = collectStoryBlockContexts(doc);
6297
6357
  return {
6298
6358
  stories: collectCanonicalStoryIdentities(doc),
6359
+ layoutIdentities: collectLayoutInputIdentities(doc, blockContexts),
6299
6360
  fieldRegions: collectCanonicalFieldRegionIdentities(doc),
6300
6361
  numbering: collectCanonicalNumberingLayoutInputs(doc, blockContexts),
6301
6362
  tables: collectCanonicalTableLayoutInputs(blockContexts),
@@ -6304,6 +6365,116 @@ function collectCanonicalLayoutInputs(doc) {
6304
6365
  editableTargets: collectEditableTargetRefs(doc)
6305
6366
  };
6306
6367
  }
6368
+ function collectLayoutInputIdentities(doc, contexts = collectStoryBlockContexts(doc)) {
6369
+ const identities = [];
6370
+ const editableTargets = collectEditableTargetRefs(doc);
6371
+ const editableByStoryBlock = new Map(
6372
+ editableTargets.map((target) => [`${target.storyKey}:${target.blockPath}`, target])
6373
+ );
6374
+ for (const context of contexts) {
6375
+ walkBlocks(context.blocks, context.storyKey, context.basePath, {
6376
+ paragraph(paragraph, blockPath) {
6377
+ const styleNumbering = paragraph.numbering === void 0 && paragraph.styleId !== void 0 ? resolveParagraphStyleNumbering(doc, paragraph.styleId) : void 0;
6378
+ const numbering = paragraph.numbering ?? styleNumbering;
6379
+ const editableTargetRef = editableByStoryBlock.get(`${context.storyKey}:${blockPath}`);
6380
+ identities.push(
6381
+ createLayoutInputIdentity({
6382
+ storyKey: context.storyKey,
6383
+ blockPath,
6384
+ block: paragraph,
6385
+ ...editableTargetRef !== void 0 ? { editableTargetRef } : {},
6386
+ ...numbering !== void 0 ? {
6387
+ list: {
6388
+ numberingInstanceId: numbering.numberingInstanceId,
6389
+ level: numbering.level ?? 0,
6390
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
6391
+ }
6392
+ } : {}
6393
+ })
6394
+ );
6395
+ },
6396
+ table(table, blockPath) {
6397
+ identities.push(
6398
+ createLayoutInputIdentity({
6399
+ storyKey: context.storyKey,
6400
+ blockPath,
6401
+ block: table,
6402
+ table: { tableBlockPath: blockPath }
6403
+ })
6404
+ );
6405
+ },
6406
+ inline(inline, blockPath, inlinePath) {
6407
+ const objectId = objectIdForInline(inline);
6408
+ if (!objectId) return;
6409
+ const sourceRef = sourceRefForInline(inline);
6410
+ identities.push({
6411
+ storyKey: context.storyKey,
6412
+ blockPath,
6413
+ blockId: `${context.storyKey}:${inlinePath}`,
6414
+ ...sourceRef !== void 0 ? { sourceRef } : {},
6415
+ object: {
6416
+ objectId,
6417
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
6418
+ }
6419
+ });
6420
+ }
6421
+ });
6422
+ }
6423
+ return identities;
6424
+ }
6425
+ function createLayoutInputIdentity(input) {
6426
+ const sourceRef = sourceRefForBlock(input.block);
6427
+ 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}`;
6428
+ return {
6429
+ storyKey: input.storyKey,
6430
+ blockPath: input.blockPath,
6431
+ blockId,
6432
+ ...sourceRef !== void 0 ? { sourceRef } : {},
6433
+ ...input.editableTargetRef !== void 0 ? { editableTargetRef: input.editableTargetRef } : {},
6434
+ ...input.table !== void 0 ? { table: input.table } : {},
6435
+ ...input.list !== void 0 ? { list: input.list } : {}
6436
+ };
6437
+ }
6438
+ function sourceRefForBlock(block) {
6439
+ return "sourceRef" in block ? block.sourceRef : void 0;
6440
+ }
6441
+ function sourceRefForInline(inline) {
6442
+ return "sourceRef" in inline ? inline.sourceRef : void 0;
6443
+ }
6444
+ function objectIdForInline(inline) {
6445
+ switch (inline.type) {
6446
+ case "image":
6447
+ return inline.mediaId;
6448
+ case "drawing_frame":
6449
+ return objectIdForDrawingContent(inline.content) ?? inline.sourceRef?.sourceId;
6450
+ case "shape":
6451
+ case "vml_shape":
6452
+ case "wordart":
6453
+ return inline.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
6454
+ case "chart_preview":
6455
+ case "smartart_preview":
6456
+ return inline.previewMediaId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
6457
+ case "ole_embed":
6458
+ return inline.relationshipId ?? inline.id ?? inline.sourceRef?.sourceId;
6459
+ case "opaque_inline":
6460
+ return inline.fragmentId;
6461
+ default:
6462
+ return void 0;
6463
+ }
6464
+ }
6465
+ function objectIdForDrawingContent(content) {
6466
+ switch (content.type) {
6467
+ case "picture":
6468
+ return content.mediaId ?? content.packagePartName ?? content.blipRef;
6469
+ case "chart_preview":
6470
+ case "smartart_preview":
6471
+ return content.previewMediaId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
6472
+ case "shape":
6473
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
6474
+ case "opaque":
6475
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
6476
+ }
6477
+ }
6307
6478
  function collectEditableTargetRefs(doc, cache) {
6308
6479
  const targets = [];
6309
6480
  for (const context of collectStoryBlockContexts(doc)) {
@@ -8452,6 +8623,16 @@ function collectCanonicalNumberingLayoutInputs(doc, contexts = collectStoryBlock
8452
8623
  const instance = doc.numbering.instances[numbering.numberingInstanceId];
8453
8624
  const abstractDefinition = instance?.abstractNumberingId === void 0 ? void 0 : doc.numbering.abstractDefinitions[instance.abstractNumberingId];
8454
8625
  inputs.push({
8626
+ identity: createLayoutInputIdentity({
8627
+ storyKey: context.storyKey,
8628
+ blockPath,
8629
+ block: paragraph,
8630
+ list: {
8631
+ numberingInstanceId: numbering.numberingInstanceId,
8632
+ level: numbering.level ?? 0,
8633
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
8634
+ }
8635
+ }),
8455
8636
  numberingKey: `${context.storyKey}:${blockPath}:numbering`,
8456
8637
  storyKey: context.storyKey,
8457
8638
  blockPath,
@@ -8753,6 +8934,12 @@ function projectTableLayoutInput(table, storyKey2, blockPath) {
8753
8934
  (row2, rowIndex) => projectTableRowLayoutInput(row2, tableKey, rowIndex)
8754
8935
  );
8755
8936
  return {
8937
+ identity: createLayoutInputIdentity({
8938
+ storyKey: storyKey2,
8939
+ blockPath,
8940
+ block: table,
8941
+ table: { tableBlockPath: blockPath }
8942
+ }),
8756
8943
  tableKey,
8757
8944
  storyKey: storyKey2,
8758
8945
  blockPath,
@@ -8840,6 +9027,16 @@ function projectDrawingFrameAnchor(node, storyKey2, blockPath, inlinePath) {
8840
9027
  const objectKey = `${storyKey2}:${inlinePath}`;
8841
9028
  const textBoxBody = projectTextBoxBodyLayoutInput(content, objectKey, sourceRef, `${inlinePath}/txbx`);
8842
9029
  return {
9030
+ identity: {
9031
+ storyKey: storyKey2,
9032
+ blockPath,
9033
+ blockId: objectKey,
9034
+ ...sourceRef !== void 0 ? { sourceRef } : {},
9035
+ object: {
9036
+ objectId: objectKey,
9037
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
9038
+ }
9039
+ },
8843
9040
  objectKey,
8844
9041
  storyKey: storyKey2,
8845
9042
  blockPath,
@@ -8961,8 +9158,19 @@ function projectTextBoxRunLayoutInput(inline, paragraphKey, inlineIndex) {
8961
9158
  }
8962
9159
  function projectLegacyImageAnchor(doc, node, storyKey2, blockPath, inlinePath) {
8963
9160
  const media = doc.media.items[node.mediaId];
9161
+ const objectKey = `${storyKey2}:${inlinePath}`;
8964
9162
  return {
8965
- objectKey: `${storyKey2}:${inlinePath}`,
9163
+ identity: {
9164
+ storyKey: storyKey2,
9165
+ blockPath,
9166
+ blockId: objectKey,
9167
+ ...node.sourceRef !== void 0 ? { sourceRef: node.sourceRef } : {},
9168
+ object: {
9169
+ objectId: objectKey,
9170
+ ...node.sourceRef !== void 0 ? { anchorSourceRef: node.sourceRef } : {}
9171
+ }
9172
+ },
9173
+ objectKey,
8966
9174
  storyKey: storyKey2,
8967
9175
  blockPath,
8968
9176
  inlinePath,
@@ -10120,12 +10328,13 @@ function getLevelStartAt(level, levelDefinitions) {
10120
10328
  return levelDefinitions.get(level)?.startAt ?? DEFAULT_NUMBERING_START_AT;
10121
10329
  }
10122
10330
  function getNumberingFormatPosture(format, value) {
10123
- if (!isSupportedNumberingFormat(format)) {
10331
+ const registryEntry = getNumberingFormatRegistryEntry(format);
10332
+ if (!registryEntry || registryEntry.renderSupport === "approximated") {
10124
10333
  return {
10125
10334
  status: "approximated",
10126
10335
  requestedFormat: format,
10127
10336
  renderedFormat: "decimal",
10128
- reason: "unsupported-numbering-format-decimal-fallback"
10337
+ reason: registryEntry?.fallbackReason ?? "unsupported-numbering-format-decimal-fallback"
10129
10338
  };
10130
10339
  }
10131
10340
  if (value !== void 0 && ((format === "upperRoman" || format === "lowerRoman") && (value <= 0 || value >= 4e3) || (format === "cardinalText" || format === "ordinalText") && (!Number.isInteger(value) || value < 1 || value > 999) || (format === "upperLetter" || format === "lowerLetter" || format === "chicago") && value < 1)) {
@@ -10156,56 +10365,148 @@ function renderLevelText(text, counters, levelDefinitions) {
10156
10365
  });
10157
10366
  return rendered.trim().length > 0 ? rendered : null;
10158
10367
  }
10159
- var SUPPORTED_NUMBERING_FORMATS = /* @__PURE__ */ new Set([
10160
- "decimal",
10161
- "decimalZero",
10162
- "upperLetter",
10163
- "lowerLetter",
10164
- "upperRoman",
10165
- "lowerRoman",
10166
- "hex",
10167
- "ordinal",
10168
- "cardinalText",
10169
- "ordinalText",
10170
- "chicago",
10171
- "bullet",
10172
- "none"
10173
- ]);
10174
10368
  function isSupportedNumberingFormat(format) {
10175
- return SUPPORTED_NUMBERING_FORMATS.has(format);
10369
+ return getNumberingFormatRegistryEntry(format)?.renderSupport === "supported";
10370
+ }
10371
+ function getNumberingFormatRegistryEntry(format) {
10372
+ return NUMBERING_FORMAT_REGISTRY.get(format);
10176
10373
  }
10177
10374
  function formatCounter(value, format) {
10178
- switch (format) {
10179
- case "decimal":
10180
- return String(value);
10181
- case "decimalZero":
10182
- return String(value).padStart(2, "0");
10183
- case "upperLetter":
10184
- return toAlphabetic2(value).toUpperCase();
10185
- case "lowerLetter":
10186
- return toAlphabetic2(value).toLowerCase();
10187
- case "upperRoman":
10188
- return toRoman2(value).toUpperCase();
10189
- case "lowerRoman":
10190
- return toRoman2(value).toLowerCase();
10191
- case "hex":
10192
- return value >= 0 ? value.toString(16).toUpperCase() : String(value);
10193
- case "ordinal":
10194
- return toOrdinal2(value);
10195
- case "cardinalText":
10196
- return toCardinalText2(value);
10197
- case "ordinalText":
10198
- return toOrdinalText2(value);
10199
- case "chicago":
10200
- return toChicago2(value);
10201
- case "bullet":
10202
- return "";
10203
- case "none":
10204
- return "";
10205
- default:
10206
- return String(value);
10207
- }
10375
+ return getNumberingFormatRegistryEntry(format)?.render(value) ?? String(value);
10208
10376
  }
10377
+ var exactNumberingFormatEntries = [
10378
+ {
10379
+ format: "decimal",
10380
+ renderSupport: "supported",
10381
+ renderedFormat: "decimal",
10382
+ supportsMutation: true,
10383
+ render: (value) => String(value)
10384
+ },
10385
+ {
10386
+ format: "decimalZero",
10387
+ renderSupport: "supported",
10388
+ renderedFormat: "decimalZero",
10389
+ supportsMutation: true,
10390
+ render: (value) => String(value).padStart(2, "0")
10391
+ },
10392
+ {
10393
+ format: "upperLetter",
10394
+ renderSupport: "supported",
10395
+ renderedFormat: "upperLetter",
10396
+ supportsMutation: true,
10397
+ render: (value) => toAlphabetic2(value).toUpperCase()
10398
+ },
10399
+ {
10400
+ format: "lowerLetter",
10401
+ renderSupport: "supported",
10402
+ renderedFormat: "lowerLetter",
10403
+ supportsMutation: true,
10404
+ render: (value) => toAlphabetic2(value).toLowerCase()
10405
+ },
10406
+ {
10407
+ format: "upperRoman",
10408
+ renderSupport: "supported",
10409
+ renderedFormat: "upperRoman",
10410
+ supportsMutation: true,
10411
+ render: (value) => toRoman2(value).toUpperCase()
10412
+ },
10413
+ {
10414
+ format: "lowerRoman",
10415
+ renderSupport: "supported",
10416
+ renderedFormat: "lowerRoman",
10417
+ supportsMutation: true,
10418
+ render: (value) => toRoman2(value).toLowerCase()
10419
+ },
10420
+ {
10421
+ format: "hex",
10422
+ renderSupport: "supported",
10423
+ renderedFormat: "hex",
10424
+ supportsMutation: true,
10425
+ render: (value) => value >= 0 ? value.toString(16).toUpperCase() : String(value)
10426
+ },
10427
+ {
10428
+ format: "ordinal",
10429
+ renderSupport: "supported",
10430
+ renderedFormat: "ordinal",
10431
+ supportsMutation: true,
10432
+ render: toOrdinal2
10433
+ },
10434
+ {
10435
+ format: "cardinalText",
10436
+ renderSupport: "supported",
10437
+ renderedFormat: "cardinalText",
10438
+ supportsMutation: true,
10439
+ render: toCardinalText2
10440
+ },
10441
+ {
10442
+ format: "ordinalText",
10443
+ renderSupport: "supported",
10444
+ renderedFormat: "ordinalText",
10445
+ supportsMutation: true,
10446
+ render: toOrdinalText2
10447
+ },
10448
+ {
10449
+ format: "chicago",
10450
+ renderSupport: "supported",
10451
+ renderedFormat: "chicago",
10452
+ supportsMutation: true,
10453
+ render: toChicago2
10454
+ },
10455
+ {
10456
+ format: "bullet",
10457
+ renderSupport: "supported",
10458
+ renderedFormat: "bullet",
10459
+ supportsMutation: true,
10460
+ render: () => ""
10461
+ },
10462
+ {
10463
+ format: "none",
10464
+ renderSupport: "supported",
10465
+ renderedFormat: "none",
10466
+ supportsMutation: true,
10467
+ render: () => ""
10468
+ }
10469
+ ];
10470
+ var approximatedDecimalFormats = [
10471
+ "decimalEnclosedCircle",
10472
+ "decimalEnclosedFullstop",
10473
+ "decimalEnclosedParen",
10474
+ "decimalFullWidth",
10475
+ "decimalHalfWidth",
10476
+ "aiueo",
10477
+ "iroha",
10478
+ "ganada",
10479
+ "chosung",
10480
+ "russianLower",
10481
+ "russianUpper",
10482
+ "hebrew1",
10483
+ "hebrew2",
10484
+ "arabicAlpha",
10485
+ "arabicAbjad",
10486
+ "thaiLetters",
10487
+ "thaiNumbers",
10488
+ "hindiLetters",
10489
+ "hindiNumbers",
10490
+ "ideographDigital",
10491
+ "ideographTraditional",
10492
+ "chineseCounting",
10493
+ "japaneseCounting",
10494
+ "japaneseLegal"
10495
+ ];
10496
+ var approximatedNumberingFormatEntries = approximatedDecimalFormats.map((format) => ({
10497
+ format,
10498
+ renderSupport: "approximated",
10499
+ renderedFormat: "decimal",
10500
+ supportsMutation: false,
10501
+ fallbackReason: "unsupported-numbering-format-decimal-fallback",
10502
+ render: (value) => String(value)
10503
+ }));
10504
+ var NUMBERING_FORMAT_REGISTRY = new Map(
10505
+ [...exactNumberingFormatEntries, ...approximatedNumberingFormatEntries].map((entry) => [
10506
+ entry.format,
10507
+ entry
10508
+ ])
10509
+ );
10209
10510
  function toOrdinal2(value) {
10210
10511
  if (value <= 0) return String(value);
10211
10512
  const lastTwo = value % 100;
@@ -10859,12 +11160,36 @@ var DEFAULT_HYPERLINK_COLOR_HEX = "0563C1";
10859
11160
  function resolveHyperlinkRunFormatting(input, catalog, resolver) {
10860
11161
  const augmentedInput = input.characterStyleId === void 0 ? { ...input, characterStyleId: HYPERLINK_CHARACTER_STYLE_ID } : input;
10861
11162
  const cascade = resolveEffectiveRunFormatting(augmentedInput, catalog);
10862
- const resolvedColor = resolveHyperlinkColorHex(cascade, resolver);
11163
+ const resolvedColor = resolveHyperlinkColorHex(
11164
+ stripInheritedColorHexForImplicitHyperlink(input, catalog, cascade),
11165
+ resolver
11166
+ );
10863
11167
  if (resolvedColor && resolvedColor !== cascade.colorHex) {
10864
11168
  return { ...cascade, colorHex: resolvedColor };
10865
11169
  }
10866
11170
  return cascade;
10867
11171
  }
11172
+ function hasDirectNonAutoColor(input) {
11173
+ return Boolean(input.direct?.colorHex && input.direct.colorHex !== "auto");
11174
+ }
11175
+ function characterStyleDeclaresColorHex(styleId, catalog) {
11176
+ if (!catalog) return false;
11177
+ const chain = resolveCharacterStyleChain(styleId, catalog);
11178
+ return chain.some((chainStyleId) => {
11179
+ const colorHex = catalog.characters[chainStyleId]?.runProperties?.colorHex;
11180
+ return Boolean(colorHex && colorHex !== "auto");
11181
+ });
11182
+ }
11183
+ function stripInheritedColorHexForImplicitHyperlink(originalInput, catalog, cascade) {
11184
+ if (originalInput.characterStyleId !== void 0 || hasDirectNonAutoColor(originalInput) || !cascade.colorHex || cascade.colorHex === "auto" || characterStyleDeclaresColorHex(HYPERLINK_CHARACTER_STYLE_ID, catalog)) {
11185
+ return cascade;
11186
+ }
11187
+ const inheritedCascade = resolveEffectiveRunFormatting(originalInput, catalog);
11188
+ if (inheritedCascade.colorHex !== cascade.colorHex) {
11189
+ return cascade;
11190
+ }
11191
+ return { ...cascade, colorHex: void 0 };
11192
+ }
10868
11193
  function resolveHyperlinkColorHex(cascade, resolver) {
10869
11194
  if (cascade.colorHex && cascade.colorHex !== "auto") {
10870
11195
  return cascade.colorHex;
@@ -11096,7 +11421,7 @@ function buildEffectiveLayoutFormatting(input) {
11096
11421
  };
11097
11422
  }
11098
11423
  function createStructuralHash(value) {
11099
- const stable = stableStringify2(value);
11424
+ const stable = stableStringify3(value);
11100
11425
  let hash = 2166136261;
11101
11426
  for (let i = 0; i < stable.length; i += 1) {
11102
11427
  hash ^= stable.charCodeAt(i);
@@ -11104,16 +11429,16 @@ function createStructuralHash(value) {
11104
11429
  }
11105
11430
  return `fnv1a32:${(hash >>> 0).toString(16).padStart(8, "0")}`;
11106
11431
  }
11107
- function stableStringify2(value) {
11432
+ function stableStringify3(value) {
11108
11433
  if (value === null || typeof value !== "object") {
11109
11434
  return JSON.stringify(value);
11110
11435
  }
11111
11436
  if (Array.isArray(value)) {
11112
- return `[${value.map((entry) => stableStringify2(entry)).join(",")}]`;
11437
+ return `[${value.map((entry) => stableStringify3(entry)).join(",")}]`;
11113
11438
  }
11114
11439
  const record = value;
11115
11440
  const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
11116
- return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify2(record[key])}`).join(",")}}`;
11441
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify3(record[key])}`).join(",")}}`;
11117
11442
  }
11118
11443
 
11119
11444
  // src/runtime/formatting/formatting-context.ts
@@ -11704,6 +12029,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
11704
12029
  ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
11705
12030
  });
11706
12031
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
12032
+ const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
11707
12033
  const activeStoryBlockPathBase = getActiveStoryBlockPathBase(document2, activeStory);
11708
12034
  chartModelStore.beginBuildPass(document2);
11709
12035
  const unsupportedNumberingFormatsSeen = options.emitFormattingTelemetry ? /* @__PURE__ */ new Set() : null;
@@ -11727,6 +12053,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
11727
12053
  activeStory.kind !== "main",
11728
12054
  !isInViewport,
11729
12055
  editableTargetsByBlockPath,
12056
+ layoutIdentitiesByBlockPath,
11730
12057
  `${activeStoryBlockPathBase}/block[${index}]`
11731
12058
  );
11732
12059
  if (isInViewport) {
@@ -11793,7 +12120,7 @@ function isIndexInAnyRange(index, ranges) {
11793
12120
  }
11794
12121
  return false;
11795
12122
  }
11796
- function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath) {
12123
+ function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, blockPath) {
11797
12124
  if (block.type === "opaque_block") {
11798
12125
  const fragment = getOpaqueFragment(document2.preservation, block.fragmentId);
11799
12126
  const descriptor = fragment ? describeOpaqueFragment(fragment) : null;
@@ -11830,6 +12157,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11830
12157
  promoteSecondaryStoryTextBoxes,
11831
12158
  cullBuild,
11832
12159
  editableTargetsByBlockPath,
12160
+ layoutIdentitiesByBlockPath,
11833
12161
  blockPath
11834
12162
  );
11835
12163
  }
@@ -11870,6 +12198,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11870
12198
  promoteSecondaryStoryTextBoxes,
11871
12199
  cullBuild,
11872
12200
  editableTargetsByBlockPath,
12201
+ layoutIdentitiesByBlockPath,
11873
12202
  blockPath
11874
12203
  );
11875
12204
  }
@@ -11952,10 +12281,11 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11952
12281
  cullBuild,
11953
12282
  editableTargetsByBlockPath,
11954
12283
  blockPath,
11955
- blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0
12284
+ blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0,
12285
+ blockPath !== void 0 ? layoutIdentitiesByBlockPath?.get(blockPath) : void 0
11956
12286
  );
11957
12287
  }
11958
- function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, tablePath) {
12288
+ function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, tablePath) {
11959
12289
  const lockedFragmentIds = [];
11960
12290
  let innerCursor = cursor;
11961
12291
  if (cullBuild) {
@@ -11972,6 +12302,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11972
12302
  promoteSecondaryStoryTextBoxes,
11973
12303
  true,
11974
12304
  editableTargetsByBlockPath,
12305
+ layoutIdentitiesByBlockPath,
11975
12306
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
11976
12307
  );
11977
12308
  lockedFragmentIds.push(...result.lockedFragmentIds);
@@ -11987,6 +12318,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11987
12318
  to: innerCursor,
11988
12319
  styleId: table.styleId,
11989
12320
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
12321
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
11990
12322
  gridColumns: table.gridColumns,
11991
12323
  rows: []
11992
12324
  },
@@ -12024,6 +12356,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
12024
12356
  promoteSecondaryStoryTextBoxes,
12025
12357
  cullBuild,
12026
12358
  editableTargetsByBlockPath,
12359
+ layoutIdentitiesByBlockPath,
12027
12360
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
12028
12361
  );
12029
12362
  cellContent.push(result.block);
@@ -12112,6 +12445,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
12112
12445
  to: innerCursor,
12113
12446
  styleId: table.styleId,
12114
12447
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
12448
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
12115
12449
  gridColumns: table.gridColumns,
12116
12450
  ...gridColumnsRelative ? { gridColumnsRelative } : {},
12117
12451
  ...resolvedTable.table?.alignment ? { alignment: resolvedTable.table.alignment } : {},
@@ -12299,7 +12633,7 @@ function resolveCellBorderStyles(borders, tableBorders, position) {
12299
12633
  if (left) result.borderLeft = left;
12300
12634
  return result;
12301
12635
  }
12302
- function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, sdtPath) {
12636
+ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, sdtPath) {
12303
12637
  const children = [];
12304
12638
  const lockedFragmentIds = [];
12305
12639
  let innerCursor = cursor;
@@ -12316,6 +12650,7 @@ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formatting
12316
12650
  promoteSecondaryStoryTextBoxes,
12317
12651
  cullBuild,
12318
12652
  editableTargetsByBlockPath,
12653
+ layoutIdentitiesByBlockPath,
12319
12654
  sdtPath !== void 0 ? `${sdtPath}/block[${childIndex}]` : void 0
12320
12655
  );
12321
12656
  children.push(result.block);
@@ -12358,7 +12693,7 @@ function getRecursableSdtBlockedReasonCode(block) {
12358
12693
  ].filter(Boolean).join(" ").toLowerCase();
12359
12694
  return searchText.includes("table of contents") || /\btoc\b/u.test(searchText) ? "workflow_preserve_only" : null;
12360
12695
  }
12361
- function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget) {
12696
+ function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget, layoutIdentity) {
12362
12697
  const themeResolver = formattingContext.theme;
12363
12698
  const effectiveNumbering = formattingContext.resolveEffectiveParagraphNumbering(paragraph);
12364
12699
  let resolvedNumbering = null;
@@ -12381,6 +12716,7 @@ function createParagraphBlock(paragraphIndex, paragraph, document2, start, forma
12381
12716
  from: start,
12382
12717
  to: start,
12383
12718
  ...editableTarget !== void 0 ? { editableTarget } : {},
12719
+ ...layoutIdentity !== void 0 ? { layoutIdentity } : {},
12384
12720
  ...paragraph.styleId ? { styleId: paragraph.styleId } : {},
12385
12721
  ...effectiveNumbering ? { numbering: effectiveNumbering } : {},
12386
12722
  ...resolvedNumbering ? {
@@ -16572,6 +16908,14 @@ function paginateSectionBlocksWithSplits(section, blocks, layout, footnotes, mea
16572
16908
  pushPage(block.from);
16573
16909
  continue;
16574
16910
  }
16911
+ if (isStandalonePageBreakParagraph(block)) {
16912
+ if (columnHeight > 0) {
16913
+ pushPage(nextBoundary);
16914
+ } else {
16915
+ pageStart = Math.max(pageStart, Math.min(nextBoundary, section.end));
16916
+ }
16917
+ break;
16918
+ }
16575
16919
  const effectiveNoteHeight = estimateFootnoteReservation(
16576
16920
  block,
16577
16921
  footnotes,
@@ -16746,6 +17090,21 @@ function hasPageBreak(block) {
16746
17090
  }
16747
17091
  return nestedBlocks(block).some(hasPageBreak);
16748
17092
  }
17093
+ function isStandalonePageBreakParagraph(block) {
17094
+ if (block.kind !== "paragraph") return false;
17095
+ let sawPageBreak = false;
17096
+ for (const segment of block.segments) {
17097
+ if (segment.kind === "opaque_inline" && segment.label === "Page break") {
17098
+ sawPageBreak = true;
17099
+ continue;
17100
+ }
17101
+ if (segment.kind === "text" && segment.text.trim().length === 0) {
17102
+ continue;
17103
+ }
17104
+ return false;
17105
+ }
17106
+ return sawPageBreak;
17107
+ }
16749
17108
  function nestedBlocks(block) {
16750
17109
  if (block.kind === "sdt_block") {
16751
17110
  return block.children;
@@ -18230,6 +18589,9 @@ function projectSurfaceBlocksToPageFragments(surface, pages, splits, columnByBlo
18230
18589
  for (let blockIndex = 0; blockIndex < surface.blocks.length; blockIndex += 1) {
18231
18590
  const block = surface.blocks[blockIndex];
18232
18591
  const blockPath = `main/block[${blockIndex}]`;
18592
+ if (isStandalonePageBreakParagraph2(block)) {
18593
+ continue;
18594
+ }
18233
18595
  if (block.kind === "table") {
18234
18596
  const tableSliceList = splits?.tablesByBlockId.get(block.blockId);
18235
18597
  if (tableSliceList && tableSliceList.length > 1) {
@@ -18315,6 +18677,23 @@ function projectSurfaceBlocksToPageFragments(surface, pages, splits, columnByBlo
18315
18677
  }
18316
18678
  return byPage;
18317
18679
  }
18680
+ function isStandalonePageBreakParagraph2(block) {
18681
+ if (block.kind !== "paragraph") {
18682
+ return false;
18683
+ }
18684
+ let sawPageBreak = false;
18685
+ for (const segment of block.segments) {
18686
+ if (segment.kind === "opaque_inline" && segment.label === "Page break") {
18687
+ sawPageBreak = true;
18688
+ continue;
18689
+ }
18690
+ if (segment.kind === "text" && segment.text.trim().length === 0) {
18691
+ continue;
18692
+ }
18693
+ return false;
18694
+ }
18695
+ return sawPageBreak;
18696
+ }
18318
18697
  function projectLineBoxesForPageFragments(pages, fragmentsByPageIndex, fragmentMeasurementsByPageIndex, surface) {
18319
18698
  const byPage = /* @__PURE__ */ new Map();
18320
18699
  const blocksById = surface ? new Map(surface.blocks.map((block) => [block.blockId, block])) : /* @__PURE__ */ new Map();
@@ -18827,13 +19206,11 @@ function buildBookmarkRanges(targets) {
18827
19206
  }
18828
19207
  var EMPTY_NUMBERING_INPUT_INDEX = {
18829
19208
  byNumberingKey: /* @__PURE__ */ new Map(),
18830
- byBlockPath: /* @__PURE__ */ new Map(),
18831
- byParagraphIndex: /* @__PURE__ */ new Map()
19209
+ byBlockPath: /* @__PURE__ */ new Map()
18832
19210
  };
18833
19211
  function buildNumberingInputIndex(numberingInputs) {
18834
19212
  const byNumberingKey = /* @__PURE__ */ new Map();
18835
19213
  const byBlockPath = /* @__PURE__ */ new Map();
18836
- const byParagraph = /* @__PURE__ */ new Map();
18837
19214
  for (const input of numberingInputs) {
18838
19215
  if (!byNumberingKey.has(input.numberingKey)) {
18839
19216
  byNumberingKey.set(input.numberingKey, input);
@@ -18841,11 +19218,8 @@ function buildNumberingInputIndex(numberingInputs) {
18841
19218
  if (!byBlockPath.has(input.blockPath)) {
18842
19219
  byBlockPath.set(input.blockPath, input);
18843
19220
  }
18844
- if (!byParagraph.has(input.paragraphIndex)) {
18845
- byParagraph.set(input.paragraphIndex, input);
18846
- }
18847
19221
  }
18848
- return { byNumberingKey, byBlockPath, byParagraphIndex: byParagraph };
19222
+ return { byNumberingKey, byBlockPath };
18849
19223
  }
18850
19224
  function collectBookmarkRangeLayoutFacts(fragmentId, blockPath, bookmarkRanges) {
18851
19225
  if (!blockPath || !bookmarkRanges || bookmarkRanges.length === 0) return [];
@@ -18965,8 +19339,7 @@ function collectNumberingLayoutFactsForBlock(block, blockPath, numberingIndex, n
18965
19339
  const canonical = lookupNumberingInput(
18966
19340
  numberingIndex,
18967
19341
  numberingKey,
18968
- context.path,
18969
- paragraphIndex
19342
+ context.path
18970
19343
  );
18971
19344
  const target = findNumberingTarget(numberingTargets, context.path);
18972
19345
  const numberingLayoutId = numberingKey ?? [
@@ -19006,7 +19379,7 @@ function findNumberingTarget(numberingTargets, blockPath) {
19006
19379
  if (blockPath === void 0) return void 0;
19007
19380
  return numberingTargets.find((target) => target.blockPath === blockPath);
19008
19381
  }
19009
- function lookupNumberingInput(numberingIndex, numberingKey, blockPath, paragraphIndex) {
19382
+ function lookupNumberingInput(numberingIndex, numberingKey, blockPath) {
19010
19383
  if (!numberingIndex) return void 0;
19011
19384
  if (numberingKey !== void 0) {
19012
19385
  const byKey = numberingIndex.byNumberingKey.get(numberingKey);
@@ -19016,7 +19389,7 @@ function lookupNumberingInput(numberingIndex, numberingKey, blockPath, paragraph
19016
19389
  const byPath = numberingIndex.byBlockPath.get(blockPath);
19017
19390
  if (byPath !== void 0) return byPath;
19018
19391
  }
19019
- return paragraphIndex !== void 0 ? numberingIndex.byParagraphIndex.get(paragraphIndex) : void 0;
19392
+ return void 0;
19020
19393
  }
19021
19394
  function collectNumberingUnavailableReasons(numbering, canonical) {
19022
19395
  const reasons = [];
@@ -19434,13 +19807,331 @@ async function createCanvasProvider(fontLoader) {
19434
19807
  }
19435
19808
  }
19436
19809
 
19810
+ // src/runtime/layout/persistent-layout-measurement-cache.ts
19811
+ init_resolved_formatting_state();
19812
+ var DEFAULT_MAX_ENTRIES = 1e4;
19813
+ var DEFAULT_MAX_ESTIMATED_BYTES = 50 * 1024 * 1024;
19814
+ function createPersistentLayoutMeasurementCache(options = {}) {
19815
+ const maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_MAX_ENTRIES);
19816
+ const maxEstimatedBytes = Math.max(
19817
+ 1024,
19818
+ options.maxEstimatedBytes ?? DEFAULT_MAX_ESTIMATED_BYTES
19819
+ );
19820
+ const entries = /* @__PURE__ */ new Map();
19821
+ const counters = {
19822
+ hits: 0,
19823
+ misses: 0,
19824
+ sets: 0,
19825
+ evictions: 0,
19826
+ clears: 0,
19827
+ estimatedBytes: 0,
19828
+ invalidatedByReason: {}
19829
+ };
19830
+ function evictOldest() {
19831
+ const oldest = entries.keys().next();
19832
+ if (oldest.done) return;
19833
+ const entry = entries.get(oldest.value);
19834
+ if (entry) {
19835
+ counters.estimatedBytes -= entry.estimatedBytes;
19836
+ }
19837
+ entries.delete(oldest.value);
19838
+ counters.evictions += 1;
19839
+ }
19840
+ return {
19841
+ get(key) {
19842
+ const cacheKey2 = serializeCacheKey(key);
19843
+ const entry = entries.get(cacheKey2);
19844
+ if (!entry) {
19845
+ counters.misses += 1;
19846
+ return void 0;
19847
+ }
19848
+ entries.delete(cacheKey2);
19849
+ entries.set(cacheKey2, entry);
19850
+ counters.hits += 1;
19851
+ return entry.value;
19852
+ },
19853
+ set(key, value) {
19854
+ const cacheKey2 = serializeCacheKey(key);
19855
+ const previous = entries.get(cacheKey2);
19856
+ if (previous) {
19857
+ counters.estimatedBytes -= previous.estimatedBytes;
19858
+ entries.delete(cacheKey2);
19859
+ }
19860
+ const estimatedBytes = estimateBytes(key, value);
19861
+ entries.set(cacheKey2, { key, value, estimatedBytes });
19862
+ counters.estimatedBytes += estimatedBytes;
19863
+ counters.sets += 1;
19864
+ while (entries.size > maxEntries || counters.estimatedBytes > maxEstimatedBytes) {
19865
+ evictOldest();
19866
+ }
19867
+ },
19868
+ invalidate(reason, predicate) {
19869
+ if (!predicate) {
19870
+ const removed2 = entries.size;
19871
+ counters.estimatedBytes = 0;
19872
+ entries.clear();
19873
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed2;
19874
+ return;
19875
+ }
19876
+ let removed = 0;
19877
+ for (const [cacheKey2, entry] of entries) {
19878
+ if (!predicate(entry.key)) continue;
19879
+ counters.estimatedBytes -= entry.estimatedBytes;
19880
+ entries.delete(cacheKey2);
19881
+ removed += 1;
19882
+ }
19883
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
19884
+ },
19885
+ clear(reason) {
19886
+ const removed = entries.size;
19887
+ counters.estimatedBytes = 0;
19888
+ entries.clear();
19889
+ counters.clears += 1;
19890
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
19891
+ },
19892
+ stats() {
19893
+ return {
19894
+ hits: counters.hits,
19895
+ misses: counters.misses,
19896
+ sets: counters.sets,
19897
+ evictions: counters.evictions,
19898
+ clears: counters.clears,
19899
+ size: entries.size,
19900
+ estimatedBytes: Math.max(0, counters.estimatedBytes),
19901
+ invalidatedByReason: { ...counters.invalidatedByReason }
19902
+ };
19903
+ }
19904
+ };
19905
+ }
19906
+ function createCachedLayoutMeasurementProvider(delegate, options = {}) {
19907
+ const cache = options.cache ?? createPersistentLayoutMeasurementCache(options);
19908
+ function emitCounter(type, key) {
19909
+ const telemetryBus = options.telemetryBus;
19910
+ if (!telemetryBus?.isEnabled("layout")) return;
19911
+ telemetryBus.emitLazy("layout", () => ({
19912
+ type,
19913
+ payload: key ? {
19914
+ measurementKind: key.measurementKind,
19915
+ blockId: key.blockId,
19916
+ backendVersion: key.backendVersion
19917
+ } : void 0
19918
+ }));
19919
+ }
19920
+ return {
19921
+ get fidelity() {
19922
+ return delegate.fidelity;
19923
+ },
19924
+ measurementCache: cache,
19925
+ measurementCacheStats() {
19926
+ return cache.stats();
19927
+ },
19928
+ whenReady() {
19929
+ return delegate.whenReady();
19930
+ },
19931
+ measureLineFragments(input) {
19932
+ const key = buildLineFragmentsKey(input, delegate, options);
19933
+ const cached = cache.get(key);
19934
+ if (cached) {
19935
+ emitCounter("pageRender.measurement.hit", key);
19936
+ return cloneMeasuredLineFragments(cached);
19937
+ }
19938
+ emitCounter("pageRender.measurement.miss", key);
19939
+ const measured = delegate.measureLineFragments(input);
19940
+ cache.set(key, cloneMeasuredLineFragments(measured));
19941
+ return measured;
19942
+ },
19943
+ measureInlineObject(input) {
19944
+ const key = buildInlineObjectKey(input, delegate, options);
19945
+ const cached = cache.get(key);
19946
+ if (cached) {
19947
+ emitCounter("pageRender.measurement.hit", key);
19948
+ return { ...cached };
19949
+ }
19950
+ emitCounter("pageRender.measurement.miss", key);
19951
+ const measured = delegate.measureInlineObject(input);
19952
+ cache.set(key, { ...measured });
19953
+ return measured;
19954
+ },
19955
+ measureTableBlock(input) {
19956
+ const key = buildTableBlockKey(input, delegate, options);
19957
+ const cached = cache.get(key);
19958
+ if (cached) {
19959
+ emitCounter("pageRender.measurement.hit", key);
19960
+ return cloneMeasuredTableBlock(cached);
19961
+ }
19962
+ emitCounter("pageRender.measurement.miss", key);
19963
+ const measured = delegate.measureTableBlock(input);
19964
+ cache.set(key, cloneMeasuredTableBlock(measured));
19965
+ return measured;
19966
+ },
19967
+ invalidateCache() {
19968
+ delegate.invalidateCache();
19969
+ cache.clear("provider.invalidateCache");
19970
+ emitCounter("pageRender.measurement.invalidated.provider.invalidateCache");
19971
+ }
19972
+ };
19973
+ }
19974
+ function isCachedLayoutMeasurementProvider(provider) {
19975
+ return typeof provider.measurementCacheStats === "function";
19976
+ }
19977
+ function buildLineFragmentsKey(input, delegate, options) {
19978
+ const block = input.block;
19979
+ const identity = measurementIdentityForBlock(block);
19980
+ return {
19981
+ measurementKind: "line-fragments",
19982
+ blockId: identity.blockId,
19983
+ blockPath: identity.blockPath,
19984
+ contentHash: hashStable2({
19985
+ kind: block.kind,
19986
+ identity: identity.identityHash,
19987
+ segments: block.segments,
19988
+ numbering: block.numbering,
19989
+ numberingPrefix: block.numberingPrefix,
19990
+ numberingSuffix: block.numberingSuffix,
19991
+ resolvedNumbering: block.resolvedNumbering
19992
+ }),
19993
+ formattingHash: createLayoutReadyFormattingSnapshot(
19994
+ input.formatting,
19995
+ input.runs
19996
+ ).hash,
19997
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
19998
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
19999
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
20000
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
20001
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
20002
+ };
20003
+ }
20004
+ function buildInlineObjectKey(input, delegate, options) {
20005
+ return {
20006
+ measurementKind: "inline-object",
20007
+ blockId: "inline-object",
20008
+ blockPath: "inline-object",
20009
+ contentHash: hashStable2({ display: input.display }),
20010
+ formattingHash: "format:none",
20011
+ constraintsHash: hashStable2({
20012
+ widthTwips: input.widthTwips,
20013
+ heightTwips: input.heightTwips
20014
+ }),
20015
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
20016
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
20017
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
20018
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
20019
+ };
20020
+ }
20021
+ function buildTableBlockKey(input, delegate, options) {
20022
+ const block = input.block;
20023
+ const identity = measurementIdentityForBlock(block);
20024
+ return {
20025
+ measurementKind: "table-block",
20026
+ blockId: identity.blockId,
20027
+ blockPath: identity.blockPath,
20028
+ contentHash: hashStable2({
20029
+ kind: block.kind,
20030
+ identity: identity.identityHash,
20031
+ rows: block.rows,
20032
+ gridColumns: block.gridColumns,
20033
+ gridColumnsRelative: block.gridColumnsRelative,
20034
+ tableResolved: block.tableResolved,
20035
+ tblLook: block.tblLook
20036
+ }),
20037
+ formattingHash: hashStable2({
20038
+ styleId: block.styleId,
20039
+ alignment: block.alignment
20040
+ }),
20041
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
20042
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
20043
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
20044
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
20045
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
20046
+ };
20047
+ }
20048
+ function measurementIdentityForBlock(block) {
20049
+ const identity = block.layoutIdentity;
20050
+ if (!identity) {
20051
+ return {
20052
+ blockId: block.blockId,
20053
+ blockPath: blockPathForBlock(block),
20054
+ identityHash: "identity:legacy"
20055
+ };
20056
+ }
20057
+ return {
20058
+ blockId: identity.blockId,
20059
+ blockPath: identity.blockPath,
20060
+ identityHash: hashStable2({
20061
+ storyKey: identity.storyKey,
20062
+ blockId: identity.blockId,
20063
+ blockPath: identity.blockPath,
20064
+ sourceRef: identity.sourceRef,
20065
+ table: identity.table,
20066
+ list: identity.list,
20067
+ field: identity.field,
20068
+ object: identity.object
20069
+ })
20070
+ };
20071
+ }
20072
+ function blockPathForBlock(block) {
20073
+ return `${block.from}:${block.to}`;
20074
+ }
20075
+ function resolveOptionValue(option, fallback) {
20076
+ if (typeof option === "function") return option();
20077
+ return option ?? fallback;
20078
+ }
20079
+ function serializeCacheKey(key) {
20080
+ return stableStringify4(key);
20081
+ }
20082
+ function estimateBytes(key, value) {
20083
+ return stableStringify4(key).length + stableStringify4(value).length;
20084
+ }
20085
+ function cloneMeasuredLineFragments(value) {
20086
+ return {
20087
+ lineCount: value.lineCount,
20088
+ maxLineWidth: value.maxLineWidth,
20089
+ lineHeights: [...value.lineHeights]
20090
+ };
20091
+ }
20092
+ function cloneMeasuredTableBlock(value) {
20093
+ return {
20094
+ totalHeightTwips: value.totalHeightTwips,
20095
+ rowHeights: [...value.rowHeights]
20096
+ };
20097
+ }
20098
+ function hashStable2(value) {
20099
+ return fnv1a2(stableStringify4(value)).toString(36);
20100
+ }
20101
+ function stableStringify4(value) {
20102
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
20103
+ if (Array.isArray(value)) {
20104
+ return `[${value.map((item) => stableStringify4(item)).join(",")}]`;
20105
+ }
20106
+ if (value instanceof Map) {
20107
+ return stableStringify4(Array.from(value.entries()));
20108
+ }
20109
+ const object = value;
20110
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
20111
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify4(object[key])}`).join(",")}}`;
20112
+ }
20113
+ function fnv1a2(input) {
20114
+ let hash = 2166136261;
20115
+ for (let i = 0; i < input.length; i += 1) {
20116
+ hash ^= input.charCodeAt(i);
20117
+ hash = Math.imul(hash, 16777619);
20118
+ }
20119
+ return hash >>> 0;
20120
+ }
20121
+
19437
20122
  // src/runtime/layout/layout-engine-version.ts
19438
- var LAYOUT_ENGINE_VERSION = 88;
20123
+ var LAYOUT_ENGINE_VERSION = 93;
19439
20124
  var LAYCACHE_SCHEMA_VERSION = 12;
19440
20125
 
19441
20126
  // src/runtime/layout/layout-engine-instance.ts
19442
20127
  var FULL_VIEWPORT_WINDOW_KEY = "full";
19443
20128
  var DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER = 1;
20129
+ function indexLayoutIdentitiesByBlockPath(identities) {
20130
+ return new Map(identities.map((identity) => [identity.blockPath, identity]));
20131
+ }
20132
+ function indexEditableTargetsByBlockPath2(targets) {
20133
+ return new Map(targets.map((target) => [target.blockPath, target]));
20134
+ }
19444
20135
  function normalizeViewportPageWindow(window2) {
19445
20136
  if (!window2) return void 0;
19446
20137
  const buffer = Number.isFinite(window2.bufferPages) ? Math.max(0, Math.floor(window2.bufferPages ?? 0)) : DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER;
@@ -19636,8 +20327,22 @@ function recordFullRebuildReason(reasonKind) {
19636
20327
  probe.invalidationCounts[totalKey] = (probe.invalidationCounts[totalKey] ?? 0) + 1;
19637
20328
  probe.invalidationCounts[byReasonKey] = (probe.invalidationCounts[byReasonKey] ?? 0) + 1;
19638
20329
  }
20330
+ function wrapMeasurementProvider(provider, measurementCache, telemetryBus) {
20331
+ if (!measurementCache || isCachedLayoutMeasurementProvider(provider)) {
20332
+ return provider;
20333
+ }
20334
+ const cacheOptions = measurementCache === true ? {} : measurementCache;
20335
+ return createCachedLayoutMeasurementProvider(provider, {
20336
+ ...cacheOptions,
20337
+ ...telemetryBus ? { telemetryBus } : {}
20338
+ });
20339
+ }
19639
20340
  function createLayoutEngine(options = {}) {
19640
- let measurementProvider = options.measurementProvider ?? createEmpiricalMeasurementProvider();
20341
+ let measurementProvider = wrapMeasurementProvider(
20342
+ options.measurementProvider ?? createEmpiricalMeasurementProvider(),
20343
+ options.measurementCache,
20344
+ options.telemetryBus
20345
+ );
19641
20346
  const autoUpgradeToCanvas = options.autoUpgradeToCanvasBackend !== false;
19642
20347
  const telemetryBus = options.telemetryBus;
19643
20348
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
@@ -19737,13 +20442,20 @@ function createLayoutEngine(options = {}) {
19737
20442
  const pageCountBeforeRecompute = previousPageCount;
19738
20443
  const document2 = input.document;
19739
20444
  const viewportWindow = normalizeViewportPageWindow(input.viewportPageWindow);
20445
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
20446
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
20447
+ layoutInputs.layoutIdentities
20448
+ );
20449
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
20450
+ layoutInputs.editableTargets
20451
+ );
19740
20452
  const mainSurface = createEditorSurfaceSnapshot(
19741
20453
  document2,
19742
20454
  createSelectionSnapshot(0, 0),
19743
- MAIN_STORY_TARGET
20455
+ MAIN_STORY_TARGET,
20456
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
19744
20457
  );
19745
20458
  const sections = buildResolvedSections(document2);
19746
- const layoutInputs = collectCanonicalLayoutInputs(document2);
19747
20459
  const fieldRegions = layoutInputs.fieldRegions;
19748
20460
  const pageStack = buildPageStackWithSplits(
19749
20461
  document2,
@@ -19855,10 +20567,18 @@ function createLayoutEngine(options = {}) {
19855
20567
  const firstDirty = range.firstPageIndex;
19856
20568
  if (firstDirty < 0 || firstDirty >= priorGraph.pages.length) return null;
19857
20569
  const document2 = input.document;
20570
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
20571
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
20572
+ layoutInputs.layoutIdentities
20573
+ );
20574
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
20575
+ layoutInputs.editableTargets
20576
+ );
19858
20577
  const mainSurface = createEditorSurfaceSnapshot(
19859
20578
  document2,
19860
20579
  createSelectionSnapshot(0, 0),
19861
- MAIN_STORY_TARGET
20580
+ MAIN_STORY_TARGET,
20581
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
19862
20582
  );
19863
20583
  const sections = buildResolvedSections(document2);
19864
20584
  const dirtyPage = priorGraph.pages[firstDirty];
@@ -19887,7 +20607,6 @@ function createLayoutEngine(options = {}) {
19887
20607
  const freshSnapshotsToRebuild = freshSnapshots.slice(0, convergenceIndex);
19888
20608
  const convergedTailStart = convergenceIndex < freshSnapshots.length ? firstDirty + convergenceIndex : void 0;
19889
20609
  const freshStories = resolvePageStories(freshSnapshotsToRebuild);
19890
- const layoutInputs = collectCanonicalLayoutInputs(document2);
19891
20610
  const fieldRegions = layoutInputs.fieldRegions;
19892
20611
  const freshBodyFragmentsByPageIndex = projectSurfaceBlocksToPageFragments(
19893
20612
  mainSurface,
@@ -20019,7 +20738,11 @@ function createLayoutEngine(options = {}) {
20019
20738
  try {
20020
20739
  const mod = await Promise.resolve().then(() => (init_measurement_backend_canvas(), measurement_backend_canvas_exports));
20021
20740
  const canvasProvider = mod.createCanvasBackend();
20022
- measurementProvider = canvasProvider;
20741
+ measurementProvider = wrapMeasurementProvider(
20742
+ canvasProvider,
20743
+ options.measurementCache,
20744
+ telemetryBus
20745
+ );
20023
20746
  cachedKey = null;
20024
20747
  cachedGraph = null;
20025
20748
  cachedFormatting = null;
@@ -20104,7 +20827,11 @@ function createLayoutEngine(options = {}) {
20104
20827
  },
20105
20828
  swapMeasurementProvider(provider) {
20106
20829
  const previousFidelity = measurementProvider.fidelity;
20107
- measurementProvider = provider;
20830
+ measurementProvider = wrapMeasurementProvider(
20831
+ provider,
20832
+ options.measurementCache,
20833
+ telemetryBus
20834
+ );
20108
20835
  if (previousFidelity !== provider.fidelity) {
20109
20836
  cachedKey = null;
20110
20837
  cachedGraph = null;
@@ -20133,6 +20860,10 @@ function createLayoutEngine(options = {}) {
20133
20860
  cachedFormatting = null;
20134
20861
  cachedMapper = null;
20135
20862
  },
20863
+ getMeasurementCacheStats() {
20864
+ if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
20865
+ return measurementProvider.measurementCacheStats();
20866
+ },
20136
20867
  /**
20137
20868
  * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
20138
20869
  * Populates both `cachedGraph` and `cachedKey` (keyed on the provided
@@ -22000,6 +22731,133 @@ function clamp(value, min, max) {
22000
22731
  return Math.min(max, Math.max(min, value));
22001
22732
  }
22002
22733
 
22734
+ // src/io/ooxml/numbering-catalog-mutation.ts
22735
+ function cloneNumberingCatalog(catalog) {
22736
+ return structuredClone(catalog);
22737
+ }
22738
+ function getListKind(catalog, numberingInstanceId) {
22739
+ const instance = catalog.instances[numberingInstanceId];
22740
+ const definition = instance ? catalog.abstractDefinitions[instance.abstractNumberingId] : void 0;
22741
+ const levelZero = definition?.levels.find((level) => level.level === 0);
22742
+ if (!levelZero) return void 0;
22743
+ return levelZero.format === "bullet" ? "bulleted" : "numbered";
22744
+ }
22745
+ function ensureDefaultListInstance(catalog, kind) {
22746
+ const existing = Object.values(catalog.instances).find(
22747
+ (instance) => getListKind(catalog, instance.numberingInstanceId) === kind
22748
+ );
22749
+ if (existing) return existing.numberingInstanceId;
22750
+ const abstractNumberingId = allocateAbstractDefinition(catalog, {
22751
+ levels: defaultLevels(kind),
22752
+ multiLevelType: "hybridMultilevel"
22753
+ });
22754
+ return allocateNumberingInstance(catalog, abstractNumberingId);
22755
+ }
22756
+ function allocateNumberingInstance(catalog, abstractNumberingId, overrides = []) {
22757
+ const numberingInstanceId = nextCanonicalNumericId(
22758
+ "num:",
22759
+ Object.keys(catalog.instances)
22760
+ );
22761
+ catalog.instances[numberingInstanceId] = {
22762
+ numberingInstanceId,
22763
+ abstractNumberingId,
22764
+ overrides: cloneOverrides(overrides)
22765
+ };
22766
+ return numberingInstanceId;
22767
+ }
22768
+ function allocateAbstractDefinition(catalog, input) {
22769
+ const abstractNumberingId = input.abstractNumberingId && !catalog.abstractDefinitions[input.abstractNumberingId] ? input.abstractNumberingId : nextCanonicalNumericId("abstract-num:", Object.keys(catalog.abstractDefinitions));
22770
+ catalog.abstractDefinitions[abstractNumberingId] = {
22771
+ abstractNumberingId,
22772
+ levels: structuredClone(input.levels),
22773
+ ...input.nsid ? { nsid: input.nsid } : { nsid: freshLongHex(catalog, abstractNumberingId, "nsid") },
22774
+ ...input.multiLevelType ? { multiLevelType: input.multiLevelType } : {},
22775
+ ...input.tplc ? { tplc: input.tplc } : { tplc: freshLongHex(catalog, abstractNumberingId, "tmpl") },
22776
+ ...input.styleLink ? { styleLink: input.styleLink } : {},
22777
+ ...input.numStyleLink ? { numStyleLink: input.numStyleLink } : {}
22778
+ };
22779
+ return abstractNumberingId;
22780
+ }
22781
+ function setStartOverride(catalog, numberingInstanceId, level, startAt) {
22782
+ const instance = catalog.instances[numberingInstanceId];
22783
+ if (!instance) return;
22784
+ catalog.instances[numberingInstanceId] = {
22785
+ ...instance,
22786
+ overrides: mergeOverride(instance.overrides, { level, startAt })
22787
+ };
22788
+ }
22789
+ function mergeOverride(overrides, nextOverride) {
22790
+ return [
22791
+ ...overrides.filter((override) => override.level !== nextOverride.level),
22792
+ structuredClone(nextOverride)
22793
+ ].sort((left, right) => left.level - right.level);
22794
+ }
22795
+ function cloneOverrides(overrides) {
22796
+ return overrides.map((override) => structuredClone(override));
22797
+ }
22798
+ function defaultLevels(kind) {
22799
+ if (kind === "bulleted") {
22800
+ return [
22801
+ { level: 0, format: "bullet", text: "\u2022" },
22802
+ { level: 1, format: "bullet", text: "o" },
22803
+ { level: 2, format: "bullet", text: "\u25A0" },
22804
+ { level: 3, format: "bullet", text: "\u2022" },
22805
+ { level: 4, format: "bullet", text: "o" },
22806
+ { level: 5, format: "bullet", text: "\u25A0" },
22807
+ { level: 6, format: "bullet", text: "\u2022" },
22808
+ { level: 7, format: "bullet", text: "o" },
22809
+ { level: 8, format: "bullet", text: "\u25A0" }
22810
+ ];
22811
+ }
22812
+ return [
22813
+ { level: 0, format: "decimal", text: "%1." },
22814
+ { level: 1, format: "decimal", text: "%1.%2." },
22815
+ { level: 2, format: "lowerLetter", text: "(%3)" },
22816
+ { level: 3, format: "lowerRoman", text: "(%4)" },
22817
+ { level: 4, format: "decimal", text: "%5." },
22818
+ { level: 5, format: "lowerLetter", text: "(%6)" },
22819
+ { level: 6, format: "lowerRoman", text: "(%7)" },
22820
+ { level: 7, format: "decimal", text: "%8." },
22821
+ { level: 8, format: "lowerLetter", text: "(%9)" }
22822
+ ];
22823
+ }
22824
+ function nextCanonicalNumericId(prefix, ids) {
22825
+ return `${prefix}${nextNumericString(ids.map((id) => stripPrefix(id, prefix)))}`;
22826
+ }
22827
+ function nextNumericString(ids) {
22828
+ const used = new Set(ids);
22829
+ let next = 1;
22830
+ for (const id of ids) {
22831
+ const parsed = Number.parseInt(id, 10);
22832
+ if (Number.isFinite(parsed) && parsed >= next) next = parsed + 1;
22833
+ }
22834
+ while (used.has(String(next))) next += 1;
22835
+ return String(next);
22836
+ }
22837
+ function stripPrefix(value, prefix) {
22838
+ return value.startsWith(prefix) ? value.slice(prefix.length) : value;
22839
+ }
22840
+ function freshLongHex(catalog, abstractNumberingId, salt) {
22841
+ const existing = new Set(
22842
+ Object.values(catalog.abstractDefinitions).map((definition) => salt === "nsid" ? definition.nsid : definition.tplc).filter((value) => Boolean(value))
22843
+ );
22844
+ let attempt = 0;
22845
+ while (attempt < 1e3) {
22846
+ const candidate = hashLongHex(`${salt}:${abstractNumberingId}:${attempt}`);
22847
+ if (!existing.has(candidate)) return candidate;
22848
+ attempt += 1;
22849
+ }
22850
+ return hashLongHex(`${salt}:${abstractNumberingId}:${Date.now()}`);
22851
+ }
22852
+ function hashLongHex(input) {
22853
+ let hash = 2166136261;
22854
+ for (let index = 0; index < input.length; index += 1) {
22855
+ hash ^= input.charCodeAt(index);
22856
+ hash = Math.imul(hash, 16777619) >>> 0;
22857
+ }
22858
+ return hash.toString(16).toUpperCase().padStart(8, "0").slice(-8);
22859
+ }
22860
+
22003
22861
  // src/core/commands/list-commands.ts
22004
22862
  function toggleNumberedList(document2, paragraphIndexes, context, options = {}) {
22005
22863
  return toggleListKind(document2, paragraphIndexes, "numbered", context, options);
@@ -22151,7 +23009,7 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
22151
23009
  affectedParagraphIndexes: []
22152
23010
  };
22153
23011
  }
22154
- const catalog = ensureNumberingCatalog(working.numbering);
23012
+ const catalog = cloneNumberingCatalog(working.numbering);
22155
23013
  const existingInstance = catalog.instances[target.numbering.numberingInstanceId];
22156
23014
  if (!existingInstance) {
22157
23015
  return {
@@ -22159,15 +23017,12 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
22159
23017
  affectedParagraphIndexes: []
22160
23018
  };
22161
23019
  }
22162
- const numberingInstanceId = createNumberingInstanceId(catalog);
22163
- catalog.instances[numberingInstanceId] = {
22164
- numberingInstanceId,
22165
- abstractNumberingId: existingInstance.abstractNumberingId,
22166
- overrides: mergeOverride(existingInstance.overrides, {
22167
- level: target.numbering.level,
22168
- startAt
22169
- })
22170
- };
23020
+ const numberingInstanceId = allocateNumberingInstance(
23021
+ catalog,
23022
+ existingInstance.abstractNumberingId,
23023
+ existingInstance.overrides
23024
+ );
23025
+ setStartOverride(catalog, numberingInstanceId, target.numbering.level, startAt);
22171
23026
  const affectedParagraphIndexes = [];
22172
23027
  for (let index = resolvedParagraphIndex; index < paragraphs.length; index += 1) {
22173
23028
  const paragraph = paragraphs[index];
@@ -22221,7 +23076,7 @@ function continueNumbering(document2, paragraphIndex, context, options = {}) {
22221
23076
  affectedParagraphIndexes: []
22222
23077
  };
22223
23078
  }
22224
- const catalog = ensureNumberingCatalog(working.numbering);
23079
+ const catalog = cloneNumberingCatalog(working.numbering);
22225
23080
  const currentInstanceId = target.numbering.numberingInstanceId;
22226
23081
  const currentKind = getListKind(catalog, currentInstanceId);
22227
23082
  if (!currentKind) {
@@ -22288,7 +23143,7 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
22288
23143
  affectedParagraphIndexes: []
22289
23144
  };
22290
23145
  }
22291
- const catalog = ensureNumberingCatalog(working.numbering);
23146
+ const catalog = cloneNumberingCatalog(working.numbering);
22292
23147
  const allAlreadyKind = normalizedIndexes.every((index) => {
22293
23148
  const paragraph = paragraphs[index];
22294
23149
  return paragraph.numbering ? getListKind(catalog, paragraph.numbering.numberingInstanceId) === kind : false;
@@ -22303,7 +23158,7 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
22303
23158
  affectedParagraphIndexes: normalizedIndexes
22304
23159
  };
22305
23160
  }
22306
- const numberingInstanceId = findAdjacentCompatibleInstance(paragraphs, catalog, normalizedIndexes[0], kind) ?? ensureDefaultInstance(catalog, kind);
23161
+ const numberingInstanceId = findAdjacentCompatibleInstance(paragraphs, catalog, normalizedIndexes[0], kind) ?? ensureDefaultListInstance(catalog, kind);
22307
23162
  for (const index of normalizedIndexes) {
22308
23163
  const paragraph = paragraphs[index];
22309
23164
  paragraph.numbering = {
@@ -22468,23 +23323,6 @@ function sortJson(value) {
22468
23323
  }
22469
23324
  return value;
22470
23325
  }
22471
- function ensureDefaultInstance(catalog, kind) {
22472
- const existing = Object.values(catalog.instances).find(
22473
- (instance) => getListKind(catalog, instance.numberingInstanceId) === kind
22474
- );
22475
- if (existing) {
22476
- return existing.numberingInstanceId;
22477
- }
22478
- const abstractNumberingId = createAbstractNumberingId(catalog);
22479
- catalog.abstractDefinitions[abstractNumberingId] = kind === "bulleted" ? createDefaultBulletedDefinition(abstractNumberingId) : createDefaultNumberedDefinition(abstractNumberingId);
22480
- const numberingInstanceId = createNumberingInstanceId(catalog);
22481
- catalog.instances[numberingInstanceId] = {
22482
- numberingInstanceId,
22483
- abstractNumberingId,
22484
- overrides: []
22485
- };
22486
- return numberingInstanceId;
22487
- }
22488
23326
  function findAdjacentCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
22489
23327
  const previous = paragraphs[fromIndex - 1];
22490
23328
  if (previous?.numbering) {
@@ -22507,69 +23345,6 @@ function findPreviousCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
22507
23345
  }
22508
23346
  return void 0;
22509
23347
  }
22510
- function getListKind(catalog, numberingInstanceId) {
22511
- const instance = catalog.instances[numberingInstanceId];
22512
- const definition = instance ? catalog.abstractDefinitions[instance.abstractNumberingId] : void 0;
22513
- const levelZero = definition?.levels.find((level) => level.level === 0);
22514
- if (!levelZero) {
22515
- return void 0;
22516
- }
22517
- return levelZero.format === "bullet" ? "bulleted" : "numbered";
22518
- }
22519
- function mergeOverride(overrides, nextOverride) {
22520
- const filtered = overrides.filter((override) => override.level !== nextOverride.level);
22521
- filtered.push(nextOverride);
22522
- return filtered.sort((left, right) => left.level - right.level);
22523
- }
22524
- function createDefaultNumberedDefinition(abstractNumberingId) {
22525
- return {
22526
- abstractNumberingId,
22527
- levels: [
22528
- { level: 0, format: "decimal", text: "%1." },
22529
- { level: 1, format: "decimal", text: "%1.%2." },
22530
- { level: 2, format: "lowerLetter", text: "(%3)" },
22531
- { level: 3, format: "lowerRoman", text: "(%4)" },
22532
- { level: 4, format: "decimal", text: "%5." },
22533
- { level: 5, format: "lowerLetter", text: "(%6)" },
22534
- { level: 6, format: "lowerRoman", text: "(%7)" },
22535
- { level: 7, format: "decimal", text: "%8." },
22536
- { level: 8, format: "lowerLetter", text: "(%9)" }
22537
- ]
22538
- };
22539
- }
22540
- function createDefaultBulletedDefinition(abstractNumberingId) {
22541
- return {
22542
- abstractNumberingId,
22543
- levels: [
22544
- { level: 0, format: "bullet", text: "\u2022" },
22545
- { level: 1, format: "bullet", text: "o" },
22546
- { level: 2, format: "bullet", text: "\u25A0" },
22547
- { level: 3, format: "bullet", text: "\u2022" },
22548
- { level: 4, format: "bullet", text: "o" },
22549
- { level: 5, format: "bullet", text: "\u25A0" },
22550
- { level: 6, format: "bullet", text: "\u2022" },
22551
- { level: 7, format: "bullet", text: "o" },
22552
- { level: 8, format: "bullet", text: "\u25A0" }
22553
- ]
22554
- };
22555
- }
22556
- function createAbstractNumberingId(catalog) {
22557
- let index = 1;
22558
- while (catalog.abstractDefinitions[`abstract-num:generated-${index}`]) {
22559
- index += 1;
22560
- }
22561
- return `abstract-num:generated-${index}`;
22562
- }
22563
- function createNumberingInstanceId(catalog) {
22564
- let index = 1;
22565
- while (catalog.instances[`num:generated-${index}`]) {
22566
- index += 1;
22567
- }
22568
- return `num:generated-${index}`;
22569
- }
22570
- function ensureNumberingCatalog(value) {
22571
- return structuredClone(value);
22572
- }
22573
23348
  function cloneEnvelope(document2, timestamp) {
22574
23349
  return {
22575
23350
  ...structuredClone(document2),
@@ -33916,7 +34691,7 @@ function applyFragmentInsert(document2, selection, fragment, context) {
33916
34691
  );
33917
34692
  workingDocument = collapseResult.document;
33918
34693
  workingSelection = collapseResult.selection;
33919
- if (context.textTarget?.kind === "table-paragraph") {
34694
+ if (context.textTarget?.kind === "table-paragraph" || context.textTarget?.kind === "text-leaf") {
33920
34695
  workingContext = {
33921
34696
  ...context,
33922
34697
  textTarget: {
@@ -33938,11 +34713,11 @@ function applyFragmentInsert(document2, selection, fragment, context) {
33938
34713
  selection
33939
34714
  };
33940
34715
  }
33941
- const targetedTablePath = workingContext.textTarget?.kind === "table-paragraph" ? workingContext.textTarget.blockPath : void 0;
33942
- if (targetedTablePath) {
34716
+ const targetedBlockPath = workingContext.textTarget?.kind === "table-paragraph" || workingContext.textTarget?.kind === "text-leaf" ? workingContext.textTarget.blockPath : void 0;
34717
+ if (targetedBlockPath) {
33943
34718
  const targeted = insertFragmentBlocksAfterPath(
33944
34719
  splitResult.document,
33945
- targetedTablePath,
34720
+ targetedBlockPath,
33946
34721
  fragment.blocks
33947
34722
  );
33948
34723
  if (targeted) {
@@ -41572,6 +42347,76 @@ function diffRenderFrames(prev, next) {
41572
42347
  }
41573
42348
  return { addedPages, removedPages, unchangedPages, changedPages };
41574
42349
  }
42350
+ function createPagePatchPlan(prev, next, diff = diffRenderFrames(prev, next), options = {}) {
42351
+ const prevByIndex = indexPagesByIndex(prev?.pages ?? []);
42352
+ const nextByIndex = indexPagesByIndex(next.pages);
42353
+ const addedPages = diff.addedPages.map(
42354
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
42355
+ );
42356
+ const removedPages = diff.removedPages.map(
42357
+ (pageIndex) => pageIdForIndex(prevByIndex, pageIndex)
42358
+ );
42359
+ const unchangedPages = diff.unchangedPages.map(
42360
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
42361
+ );
42362
+ const changedPages = diff.changedPages.map(
42363
+ (entry) => createPagePatchEntry(entry, pageIdForIndex(nextByIndex, entry.pageIndex), options)
42364
+ );
42365
+ return {
42366
+ layoutRevision: options.layoutRevision ?? next.revision,
42367
+ geometryRevision: options.geometryRevision ?? next.revision,
42368
+ addedPages,
42369
+ removedPages,
42370
+ unchangedPages,
42371
+ changedPages,
42372
+ mountChanges: resolveMountChanges({
42373
+ addedPages,
42374
+ removedPages,
42375
+ mountedPageIds: options.mountedPageIds,
42376
+ requestedMountPageIds: options.requestedMountPageIds
42377
+ })
42378
+ };
42379
+ }
42380
+ function createPagePatchEntry(entry, pageId, options) {
42381
+ const regionChanges = entry.regions.map((region) => region.kind);
42382
+ const fragmentChanges = uniqueStrings(
42383
+ entry.regions.flatMap((region) => region.changedBlockIds)
42384
+ );
42385
+ const overlayLaneChanges = !entry.pageFrameChanged && entry.regions.length === 0 ? ["chrome-reservations"] : [];
42386
+ return {
42387
+ pageId,
42388
+ pageIndex: entry.pageIndex,
42389
+ frameChanged: entry.pageFrameChanged === true,
42390
+ regionChanges,
42391
+ fragmentChanges,
42392
+ lineBoxChanges: fragmentChanges,
42393
+ overlayLaneChanges,
42394
+ reason: options.reason ?? (overlayLaneChanges.length > 0 ? "overlay" : "layout")
42395
+ };
42396
+ }
42397
+ function resolveMountChanges(input) {
42398
+ if (!input.mountedPageIds || !input.requestedMountPageIds) {
42399
+ return {
42400
+ mountPageIds: input.addedPages,
42401
+ unmountPageIds: input.removedPages
42402
+ };
42403
+ }
42404
+ const mounted = new Set(input.mountedPageIds);
42405
+ const requested = new Set(input.requestedMountPageIds);
42406
+ return {
42407
+ mountPageIds: input.requestedMountPageIds.filter((pageId) => !mounted.has(pageId)),
42408
+ unmountPageIds: input.mountedPageIds.filter((pageId) => !requested.has(pageId))
42409
+ };
42410
+ }
42411
+ function indexPagesByIndex(pages) {
42412
+ return new Map(pages.map((page) => [page.page.pageIndex, page]));
42413
+ }
42414
+ function pageIdForIndex(pages, pageIndex) {
42415
+ return pages.get(pageIndex)?.page.pageId ?? `page:${pageIndex}`;
42416
+ }
42417
+ function uniqueStrings(values) {
42418
+ return [...new Set(values)];
42419
+ }
41575
42420
  function diffPage(prev, next, prevIndex, nextIndex) {
41576
42421
  const changed = [];
41577
42422
  const bodyChanges = diffRegion(prev.regions.body, next.regions.body, prevIndex, nextIndex);
@@ -41792,10 +42637,12 @@ function createRenderKernel(input) {
41792
42637
  emit2({ kind: "frame_built", revision: frame.revision, reason: "full" });
41793
42638
  const diffT0 = typeof performance !== "undefined" ? performance.now() : 0;
41794
42639
  const diff = diffRenderFrames(lastEmittedFrame, frame);
42640
+ const patchPlan = createPagePatchPlan(lastEmittedFrame, frame, diff);
42641
+ recordPagePatchPlanTelemetry(patchPlan);
41795
42642
  if (diffT0 > 0) {
41796
42643
  recordPerfSample("render.frame_diff", performance.now() - diffT0);
41797
42644
  }
41798
- emit2({ kind: "frame_diff", revision: frame.revision, diff });
42645
+ emit2({ kind: "frame_diff", revision: frame.revision, diff, patchPlan });
41799
42646
  lastEmittedFrame = frame;
41800
42647
  }
41801
42648
  return frame;
@@ -41818,6 +42665,35 @@ function createRenderKernel(input) {
41818
42665
  }
41819
42666
  };
41820
42667
  }
42668
+ function recordPagePatchPlanTelemetry(plan) {
42669
+ incrementInvalidationCounter(
42670
+ "pageRender.patch.addedPages",
42671
+ plan.addedPages.length
42672
+ );
42673
+ incrementInvalidationCounter(
42674
+ "pageRender.patch.removedPages",
42675
+ plan.removedPages.length
42676
+ );
42677
+ incrementInvalidationCounter(
42678
+ "pageRender.patch.unchangedPages",
42679
+ plan.unchangedPages.length
42680
+ );
42681
+ incrementInvalidationCounter(
42682
+ "pageRender.patch.changedFragments",
42683
+ plan.changedPages.reduce(
42684
+ (total, page) => total + page.fragmentChanges.length,
42685
+ 0
42686
+ )
42687
+ );
42688
+ incrementInvalidationCounter(
42689
+ "pageRender.patch.mountedPages",
42690
+ plan.mountChanges.mountPageIds.length
42691
+ );
42692
+ incrementInvalidationCounter(
42693
+ "pageRender.patch.unmountedPages",
42694
+ plan.mountChanges.unmountPageIds.length
42695
+ );
42696
+ }
41821
42697
  function buildPage(page, topPx, zoom, activeStory, facet) {
41822
42698
  const layout = page.layout;
41823
42699
  const widthPx = layout.pageWidth * zoom.pxPerTwip;
@@ -43194,7 +44070,7 @@ function emptyCompatibilityReport() {
43194
44070
  }
43195
44071
 
43196
44072
  // src/runtime/debug/runtime-debug-facet.ts
43197
- function createRuntimeDebugFacet(getRuntime, bus) {
44073
+ function createRuntimeDebugFacet(getRuntime, bus, options = {}) {
43198
44074
  return {
43199
44075
  bus,
43200
44076
  getSnapshot(query) {
@@ -43212,10 +44088,321 @@ function createRuntimeDebugFacet(getRuntime, bus) {
43212
44088
  },
43213
44089
  getChannels() {
43214
44090
  return bus.getChannels();
44091
+ },
44092
+ getHotEditTraces() {
44093
+ return options.getHotEditTraces?.() ?? [];
43215
44094
  }
43216
44095
  };
43217
44096
  }
43218
44097
 
44098
+ // src/runtime/hot-edit/hot-edit-trace.ts
44099
+ var HOT_EDIT_TRACE_LIMIT = 64;
44100
+ var HotEditTraceRecorder = class {
44101
+ traces = [];
44102
+ sequence = 0;
44103
+ begin(input) {
44104
+ this.sequence += 1;
44105
+ return {
44106
+ sequence: this.sequence,
44107
+ commandType: input.commandType,
44108
+ startedAtUtc: input.nowUtc,
44109
+ startedAtMs: nowMs(),
44110
+ startCounters: input.counters.snapshot()
44111
+ };
44112
+ }
44113
+ complete(pending, input) {
44114
+ const endCounters = input.counters.snapshot();
44115
+ const refreshAllCount = counterDelta(pending.startCounters, endCounters, "refresh.all");
44116
+ const patchHitCount = counterDelta(pending.startCounters, endCounters, "surface.localText.patchHit");
44117
+ const patchBudgetFallbackCount = counterDelta(
44118
+ pending.startCounters,
44119
+ endCounters,
44120
+ "surface.localText.patchBudgetFallback"
44121
+ );
44122
+ const boundedEditableTargetBuildCount = counterDelta(
44123
+ pending.startCounters,
44124
+ endCounters,
44125
+ "runtime.editableTargets.boundedBuilds"
44126
+ );
44127
+ const tier = refineTier(input.tier, {
44128
+ refreshAllCount,
44129
+ patchHitCount,
44130
+ patchBudgetFallbackCount,
44131
+ boundedEditableTargetBuildCount
44132
+ });
44133
+ const trace = {
44134
+ sequence: pending.sequence,
44135
+ startedAtUtc: pending.startedAtUtc,
44136
+ commandType: pending.commandType,
44137
+ tier,
44138
+ patchMissReason: input.patchMissReason,
44139
+ totalDurationUs: Math.max(0, Math.round((nowMs() - pending.startedAtMs) * 1e3)),
44140
+ refreshAllCount,
44141
+ patchHitCount,
44142
+ patchBudgetFallbackCount,
44143
+ boundedEditableTargetBuildCount
44144
+ };
44145
+ this.traces.push(trace);
44146
+ if (this.traces.length > HOT_EDIT_TRACE_LIMIT) {
44147
+ this.traces.splice(0, this.traces.length - HOT_EDIT_TRACE_LIMIT);
44148
+ }
44149
+ input.counters.increment(`hotEdit.tier.${trace.tier}`);
44150
+ if (trace.patchMissReason !== "none") {
44151
+ input.counters.increment(`hotEdit.patchMiss.${trace.patchMissReason}`);
44152
+ }
44153
+ return trace;
44154
+ }
44155
+ getTraces() {
44156
+ return Object.freeze([...this.traces]);
44157
+ }
44158
+ clear() {
44159
+ this.traces.splice(0, this.traces.length);
44160
+ }
44161
+ };
44162
+ function refineTier(requested, counters) {
44163
+ if (requested === "blocked") return requested;
44164
+ if (counters.refreshAllCount > 0) return "full-refresh";
44165
+ if (counters.patchBudgetFallbackCount > 0 || counters.boundedEditableTargetBuildCount > 0) {
44166
+ return "bounded-projection";
44167
+ }
44168
+ if (counters.patchHitCount > 0) return "patch";
44169
+ return requested;
44170
+ }
44171
+ function counterDelta(start, end, key) {
44172
+ return (end[key] ?? 0) - (start[key] ?? 0);
44173
+ }
44174
+ function nowMs() {
44175
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
44176
+ }
44177
+
44178
+ // src/runtime/hot-edit/surface-local-patcher.ts
44179
+ function patchSurfaceTextSegment(surface, patch) {
44180
+ const delta = patch.insertedText.length - Math.max(0, patch.to - patch.from);
44181
+ let patched = false;
44182
+ const blocks = surface.blocks.map((block) => {
44183
+ const result = patchBlock(block, patch, delta);
44184
+ if (result.patched) patched = true;
44185
+ return result.block;
44186
+ });
44187
+ if (!patched) {
44188
+ return { status: "miss", reason: "segment-not-found" };
44189
+ }
44190
+ return {
44191
+ status: "patched",
44192
+ surface: {
44193
+ ...surface,
44194
+ storySize: surface.storySize + delta,
44195
+ plainText: surface.plainText.slice(0, patch.from) + patch.insertedText + surface.plainText.slice(patch.to),
44196
+ blocks
44197
+ }
44198
+ };
44199
+ }
44200
+ function patchBlock(block, patch, delta) {
44201
+ if (patch.to < block.from) return { block, patched: false };
44202
+ if (patch.from > block.to) {
44203
+ return { block: shiftBlock(block, delta), patched: false };
44204
+ }
44205
+ switch (block.kind) {
44206
+ case "paragraph":
44207
+ return patchParagraph(block, patch, delta);
44208
+ case "sdt_block": {
44209
+ let patched = false;
44210
+ const children = block.children.map((child) => {
44211
+ const result = patchBlock(child, patch, delta);
44212
+ patched ||= result.patched;
44213
+ return result.block;
44214
+ });
44215
+ return {
44216
+ block: patched ? { ...block, to: block.to + delta, children } : block,
44217
+ patched
44218
+ };
44219
+ }
44220
+ case "table": {
44221
+ let patched = false;
44222
+ const rows = block.rows.map((row2) => ({
44223
+ ...row2,
44224
+ cells: row2.cells.map((cell) => ({
44225
+ ...cell,
44226
+ content: cell.content.map((child) => {
44227
+ const result = patchBlock(child, patch, delta);
44228
+ patched ||= result.patched;
44229
+ return result.block;
44230
+ })
44231
+ }))
44232
+ }));
44233
+ return {
44234
+ block: patched ? { ...block, to: block.to + delta, rows } : block,
44235
+ patched
44236
+ };
44237
+ }
44238
+ case "opaque_block":
44239
+ return { block, patched: false };
44240
+ }
44241
+ }
44242
+ function patchParagraph(block, patch, delta) {
44243
+ if (patch.from < block.from || patch.to > block.to) {
44244
+ return { block, patched: false };
44245
+ }
44246
+ const segmentIndex = block.segments.findIndex(
44247
+ (segment2) => segment2.kind === "text" && patch.from >= segment2.from && patch.to <= segment2.to
44248
+ );
44249
+ if (segmentIndex < 0) return { block, patched: false };
44250
+ const segment = block.segments[segmentIndex];
44251
+ if (segment.kind !== "text") return { block, patched: false };
44252
+ const localFrom = patch.from - segment.from;
44253
+ const localTo = patch.to - segment.from;
44254
+ const segments = block.segments.map((candidate, index) => {
44255
+ if (index < segmentIndex) return candidate;
44256
+ if (index === segmentIndex) {
44257
+ return {
44258
+ ...segment,
44259
+ text: segment.text.slice(0, localFrom) + patch.insertedText + segment.text.slice(localTo),
44260
+ to: segment.to + delta
44261
+ };
44262
+ }
44263
+ return shiftInlineSegment(candidate, delta);
44264
+ });
44265
+ return {
44266
+ block: {
44267
+ ...block,
44268
+ to: block.to + delta,
44269
+ segments
44270
+ },
44271
+ patched: true
44272
+ };
44273
+ }
44274
+ function shiftBlock(block, delta) {
44275
+ if (delta === 0) return block;
44276
+ switch (block.kind) {
44277
+ case "paragraph":
44278
+ return {
44279
+ ...block,
44280
+ from: block.from + delta,
44281
+ to: block.to + delta,
44282
+ segments: block.segments.map((segment) => shiftInlineSegment(segment, delta))
44283
+ };
44284
+ case "sdt_block":
44285
+ return {
44286
+ ...block,
44287
+ from: block.from + delta,
44288
+ to: block.to + delta,
44289
+ children: block.children.map((child) => shiftBlock(child, delta))
44290
+ };
44291
+ case "table":
44292
+ return {
44293
+ ...block,
44294
+ from: block.from + delta,
44295
+ to: block.to + delta,
44296
+ rows: block.rows.map((row2) => ({
44297
+ ...row2,
44298
+ cells: row2.cells.map((cell) => ({
44299
+ ...cell,
44300
+ content: cell.content.map((child) => shiftBlock(child, delta))
44301
+ }))
44302
+ }))
44303
+ };
44304
+ case "opaque_block":
44305
+ return {
44306
+ ...block,
44307
+ from: block.from + delta,
44308
+ to: block.to + delta
44309
+ };
44310
+ }
44311
+ }
44312
+ function shiftInlineSegment(segment, delta) {
44313
+ if (delta === 0) return segment;
44314
+ return {
44315
+ ...segment,
44316
+ from: segment.from + delta,
44317
+ to: segment.to + delta
44318
+ };
44319
+ }
44320
+
44321
+ // src/runtime/hot-edit/hot-edit-profiler.ts
44322
+ var HEAVY_REVIEW_ITEM_THRESHOLD = 24;
44323
+ var DENSE_BLOCK_NODE_THRESHOLD = 256;
44324
+ function deriveHotEditDocumentProfile(document2) {
44325
+ const blocks = document2.content.children ?? [];
44326
+ let tableBlockCount = 0;
44327
+ let maxNestedSurfaceNodesPerBlock = 0;
44328
+ for (const block of blocks) {
44329
+ if (block.type === "table") tableBlockCount += 1;
44330
+ maxNestedSurfaceNodesPerBlock = Math.max(
44331
+ maxNestedSurfaceNodesPerBlock,
44332
+ countBlockNodes(block)
44333
+ );
44334
+ }
44335
+ const commentCount = Object.keys(document2.review.comments ?? {}).length;
44336
+ const revisionCount = Object.keys(document2.review.revisions ?? {}).length;
44337
+ const secondaryStoryBlockCount = countSecondaryStoryBlocks(document2);
44338
+ return {
44339
+ blockCount: blocks.length,
44340
+ tableBlockCount,
44341
+ maxNestedSurfaceNodesPerBlock,
44342
+ commentCount,
44343
+ revisionCount,
44344
+ secondaryStoryBlockCount,
44345
+ hasHeavyReview: commentCount + revisionCount >= HEAVY_REVIEW_ITEM_THRESHOLD,
44346
+ hasDenseTables: tableBlockCount > 0 && maxNestedSurfaceNodesPerBlock > DENSE_BLOCK_NODE_THRESHOLD
44347
+ };
44348
+ }
44349
+ function countBlockNodes(block) {
44350
+ const value = block;
44351
+ let total = 1;
44352
+ if (Array.isArray(value.children)) {
44353
+ total += value.children.length;
44354
+ for (const child of value.children) {
44355
+ if (isBlockLike(child)) total += countBlockNodes(child);
44356
+ }
44357
+ }
44358
+ if (Array.isArray(value.rows)) {
44359
+ for (const row2 of value.rows) {
44360
+ total += 1;
44361
+ for (const cell of row2.cells ?? []) {
44362
+ total += 1;
44363
+ for (const child of cell.children ?? []) {
44364
+ total += countBlockNodes(child);
44365
+ }
44366
+ }
44367
+ }
44368
+ }
44369
+ return total;
44370
+ }
44371
+ function isBlockLike(value) {
44372
+ return Boolean(value && typeof value === "object" && "type" in value);
44373
+ }
44374
+ function countSecondaryStoryBlocks(document2) {
44375
+ const subParts = document2.subParts;
44376
+ if (!subParts) return 0;
44377
+ let count = 0;
44378
+ for (const value of Object.values(subParts)) {
44379
+ count += countBlocksInUnknownStory(value);
44380
+ }
44381
+ return count;
44382
+ }
44383
+ function countBlocksInUnknownStory(value) {
44384
+ if (!value || typeof value !== "object") return 0;
44385
+ const candidate = value;
44386
+ if (Array.isArray(candidate.children)) return candidate.children.length;
44387
+ if (Array.isArray(candidate.content?.children)) return candidate.content.children.length;
44388
+ return Object.values(value).reduce((total, entry) => total + countBlocksInUnknownStory(entry), 0);
44389
+ }
44390
+
44391
+ // src/runtime/hot-edit/hot-edit-policy.ts
44392
+ var DEFAULT_MAX_SHIFTED_SURFACE_NODES = 5e3;
44393
+ var DEFAULT_MAX_SHIFTED_NODES_PER_BLOCK = 256;
44394
+ var DEFAULT_SELECTION_CORRIDOR_BLOCK_RADIUS = 8;
44395
+ var HEAVY_DOCUMENT_SELECTION_CORRIDOR_BLOCK_RADIUS = 12;
44396
+ function resolveHotEditPolicy(profile) {
44397
+ const complexGeometry = profile.hasDenseTables || profile.secondaryStoryBlockCount > 0;
44398
+ return {
44399
+ maxShiftedSurfaceNodes: DEFAULT_MAX_SHIFTED_SURFACE_NODES,
44400
+ maxShiftedNodesPerBlock: DEFAULT_MAX_SHIFTED_NODES_PER_BLOCK,
44401
+ selectionCorridorBlockRadius: complexGeometry ? HEAVY_DOCUMENT_SELECTION_CORRIDOR_BLOCK_RADIUS : DEFAULT_SELECTION_CORRIDOR_BLOCK_RADIUS,
44402
+ preferBoundedProjectionForDenseTails: profile.hasDenseTables
44403
+ };
44404
+ }
44405
+
43219
44406
  // src/io/export/serialize-ffdata.ts
43220
44407
  function regenerateFFDataRawXml(node) {
43221
44408
  const parts = ["<w:ffData>"];
@@ -44638,26 +45825,35 @@ function buildParagraphPropertiesXml(paragraph) {
44638
45825
  if (frameXml) children.push(frameXml);
44639
45826
  }
44640
45827
  pushOnOffParagraphProperty(children, "widowControl", paragraph.widowControl);
44641
- if (paragraph.outlineLevel !== void 0) {
44642
- children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
44643
- }
44644
45828
  if (paragraph.numbering) {
44645
45829
  children.push(serializeParagraphNumberingProperties(paragraph.numbering));
44646
45830
  }
45831
+ pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
45832
+ if (paragraph.borders) {
45833
+ const bordersXml = serializeParagraphBorders(paragraph.borders);
45834
+ if (bordersXml) children.push(bordersXml);
45835
+ }
45836
+ if (paragraph.shading) {
45837
+ const shadingXml = serializeParagraphShading(paragraph.shading);
45838
+ if (shadingXml) children.push(shadingXml);
45839
+ }
45840
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
45841
+ const tabsXml = paragraph.tabStops.map((tab) => {
45842
+ const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
45843
+ return `<w:tab w:val="${escapeXmlAttribute2(tab.align)}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
45844
+ }).join("");
45845
+ children.push(`<w:tabs>${tabsXml}</w:tabs>`);
45846
+ }
45847
+ pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
44647
45848
  if (paragraph.spacing) {
44648
45849
  const s = paragraph.spacing;
44649
45850
  const attrs = [];
44650
45851
  if (s.before !== void 0) attrs.push(`w:before="${twip(s.before)}"`);
44651
45852
  if (s.after !== void 0) attrs.push(`w:after="${twip(s.after)}"`);
44652
45853
  if (s.line !== void 0) attrs.push(`w:line="${twip(s.line)}"`);
44653
- if (s.lineRule !== void 0) attrs.push(`w:lineRule="${s.lineRule}"`);
45854
+ if (s.lineRule !== void 0) attrs.push(`w:lineRule="${escapeXmlAttribute2(s.lineRule)}"`);
44654
45855
  if (attrs.length > 0) children.push(`<w:spacing ${attrs.join(" ")}/>`);
44655
45856
  }
44656
- if (paragraph.contextualSpacing === true) {
44657
- children.push("<w:contextualSpacing/>");
44658
- } else if (paragraph.contextualSpacing === false) {
44659
- children.push(`<w:contextualSpacing w:val="false"/>`);
44660
- }
44661
45857
  if (paragraph.indentation) {
44662
45858
  const ind = paragraph.indentation;
44663
45859
  const attrs = [];
@@ -44667,33 +45863,20 @@ function buildParagraphPropertiesXml(paragraph) {
44667
45863
  if (ind.hanging !== void 0) attrs.push(`w:hanging="${twip(ind.hanging)}"`);
44668
45864
  if (attrs.length > 0) children.push(`<w:ind ${attrs.join(" ")}/>`);
44669
45865
  }
44670
- if (paragraph.alignment) {
44671
- children.push(`<w:jc w:val="${paragraph.alignment}"/>`);
45866
+ if (paragraph.contextualSpacing === true) {
45867
+ children.push("<w:contextualSpacing/>");
45868
+ } else if (paragraph.contextualSpacing === false) {
45869
+ children.push(`<w:contextualSpacing w:val="false"/>`);
44672
45870
  }
44673
- if (paragraph.borders) {
44674
- const bordersXml = serializeParagraphBorders(paragraph.borders);
44675
- if (bordersXml) {
44676
- children.push(bordersXml);
44677
- }
45871
+ if (paragraph.alignment) {
45872
+ children.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
44678
45873
  }
44679
- if (paragraph.shading) {
44680
- const shadingXml = serializeParagraphShading(paragraph.shading);
44681
- if (shadingXml) {
44682
- children.push(shadingXml);
44683
- }
45874
+ if (paragraph.outlineLevel !== void 0) {
45875
+ children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
44684
45876
  }
44685
- pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
44686
- pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
44687
45877
  if (paragraph.cnfStyle) {
44688
45878
  children.push(`<w:cnfStyle w:val="${escapeXmlAttribute2(paragraph.cnfStyle)}"/>`);
44689
45879
  }
44690
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
44691
- const tabsXml = paragraph.tabStops.map((tab) => {
44692
- const leaderAttr = tab.leader ? ` w:leader="${tab.leader}"` : "";
44693
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
44694
- }).join("");
44695
- children.push(`<w:tabs>${tabsXml}</w:tabs>`);
44696
- }
44697
45880
  if (children.length === 0) return "";
44698
45881
  return `<w:pPr>${children.join("")}</w:pPr>`;
44699
45882
  }
@@ -45258,79 +46441,86 @@ function serializeRunProperties(marks) {
45258
46441
  if (!marks || marks.length === 0) {
45259
46442
  return "";
45260
46443
  }
46444
+ const orderedTypes = [
46445
+ "fontFamily",
46446
+ "bold",
46447
+ "italic",
46448
+ "allCaps",
46449
+ "smallCaps",
46450
+ "strikethrough",
46451
+ "doubleStrikethrough",
46452
+ "shadow",
46453
+ "emboss",
46454
+ "imprint",
46455
+ "vanish",
46456
+ "textColor",
46457
+ "charSpacing",
46458
+ "kerning",
46459
+ "position",
46460
+ "fontSize",
46461
+ "highlight",
46462
+ "underline",
46463
+ "backgroundColor",
46464
+ "lang",
46465
+ "textFill"
46466
+ ];
45261
46467
  const markParts = [];
45262
- for (const mark of marks) {
45263
- switch (mark.type) {
45264
- case "bold":
45265
- markParts.push("<w:b/>");
45266
- break;
45267
- case "italic":
45268
- markParts.push("<w:i/>");
45269
- break;
45270
- case "underline":
45271
- markParts.push(`<w:u w:val="single"/>`);
45272
- break;
45273
- case "strikethrough":
45274
- markParts.push("<w:strike/>");
45275
- break;
45276
- case "doubleStrikethrough":
45277
- markParts.push("<w:dstrike/>");
45278
- break;
45279
- case "vanish":
45280
- markParts.push("<w:vanish/>");
45281
- break;
45282
- case "lang":
45283
- markParts.push(`<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`);
45284
- break;
45285
- case "highlight":
45286
- markParts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
45287
- break;
45288
- case "backgroundColor":
45289
- markParts.push(
45290
- `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`
45291
- );
45292
- break;
45293
- case "charSpacing":
45294
- markParts.push(`<w:spacing w:val="${mark.val}"/>`);
45295
- break;
45296
- case "kerning":
45297
- markParts.push(`<w:kern w:val="${mark.val}"/>`);
45298
- break;
45299
- case "emboss":
45300
- markParts.push("<w:emboss/>");
45301
- break;
45302
- case "imprint":
45303
- markParts.push("<w:imprint/>");
45304
- break;
45305
- case "shadow":
45306
- markParts.push("<w:shadow/>");
45307
- break;
45308
- case "position":
45309
- markParts.push(`<w:position w:val="${mark.val}"/>`);
45310
- break;
45311
- case "textFill":
45312
- markParts.push(mark.xml);
45313
- break;
45314
- case "fontFamily":
45315
- markParts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
45316
- break;
45317
- case "fontSize":
45318
- markParts.push(`<w:sz w:val="${mark.val}"/>`);
45319
- break;
45320
- case "textColor":
45321
- markParts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
45322
- break;
45323
- case "smallCaps":
45324
- markParts.push("<w:smallCaps/>");
45325
- break;
45326
- case "allCaps":
45327
- markParts.push("<w:caps/>");
45328
- break;
46468
+ for (const type of orderedTypes) {
46469
+ for (const mark of marks) {
46470
+ if (mark.type !== type) continue;
46471
+ const xml = serializeRunPropertyMark(mark);
46472
+ if (xml) markParts.push(xml);
45329
46473
  }
45330
46474
  }
45331
46475
  const children = markParts.join("");
45332
46476
  return children.length > 0 ? `<w:rPr>${children}</w:rPr>` : "";
45333
46477
  }
46478
+ function serializeRunPropertyMark(mark) {
46479
+ switch (mark.type) {
46480
+ case "bold":
46481
+ return "<w:b/>";
46482
+ case "italic":
46483
+ return "<w:i/>";
46484
+ case "underline":
46485
+ return `<w:u w:val="single"/>`;
46486
+ case "strikethrough":
46487
+ return "<w:strike/>";
46488
+ case "doubleStrikethrough":
46489
+ return "<w:dstrike/>";
46490
+ case "vanish":
46491
+ return "<w:vanish/>";
46492
+ case "lang":
46493
+ return `<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`;
46494
+ case "highlight":
46495
+ return `<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`;
46496
+ case "backgroundColor":
46497
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`;
46498
+ case "charSpacing":
46499
+ return `<w:spacing w:val="${mark.val}"/>`;
46500
+ case "kerning":
46501
+ return `<w:kern w:val="${mark.val}"/>`;
46502
+ case "emboss":
46503
+ return "<w:emboss/>";
46504
+ case "imprint":
46505
+ return "<w:imprint/>";
46506
+ case "shadow":
46507
+ return "<w:shadow/>";
46508
+ case "position":
46509
+ return `<w:position w:val="${mark.val}"/>`;
46510
+ case "textFill":
46511
+ return mark.xml;
46512
+ case "fontFamily":
46513
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
46514
+ case "fontSize":
46515
+ return `<w:sz w:val="${mark.val}"/>`;
46516
+ case "textColor":
46517
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
46518
+ case "smallCaps":
46519
+ return "<w:smallCaps/>";
46520
+ case "allCaps":
46521
+ return "<w:caps/>";
46522
+ }
46523
+ }
45334
46524
  function requiresPreservedSpace(text) {
45335
46525
  return /^\s/.test(text) || /\s$/.test(text) || text.includes(" ");
45336
46526
  }
@@ -46755,6 +47945,12 @@ function resolveScopeRange(entry, handle, positionMap) {
46755
47945
  if (inlineRange) return inlineRange;
46756
47946
  return positionMap.blocks.get(entry.blockIndex) ?? null;
46757
47947
  }
47948
+ case "image": {
47949
+ const key = `${entry.blockIndex}:${entry.inlineIndex}`;
47950
+ const inlineRange = positionMap.inlines.get(key);
47951
+ if (inlineRange) return inlineRange;
47952
+ return positionMap.blocks.get(entry.blockIndex) ?? null;
47953
+ }
46758
47954
  case "comment-thread":
46759
47955
  return anchorToRange(entry.thread.anchor);
46760
47956
  case "revision":
@@ -46893,6 +48089,11 @@ function deriveReplaceability(kind, provenance) {
46893
48089
  level: "preserve-only",
46894
48090
  reason: "field-result-is-computed-preserve-only"
46895
48091
  };
48092
+ case "image":
48093
+ return {
48094
+ level: "preserve-only",
48095
+ reason: "image-object-command-required"
48096
+ };
46896
48097
  case "comment-thread":
46897
48098
  return {
46898
48099
  level: "preserve-only",
@@ -47597,6 +48798,55 @@ function compileHeadingScope(entry, options = {}) {
47597
48798
  };
47598
48799
  }
47599
48800
 
48801
+ // src/runtime/scopes/scope-kinds/image.ts
48802
+ function imageAltText(image) {
48803
+ if (image.type === "image") return image.altText ?? "";
48804
+ return image.anchor.docPr?.descr ?? image.anchor.docPr?.name ?? "";
48805
+ }
48806
+ function imageMediaId(image) {
48807
+ if (image.type === "image") return image.mediaId;
48808
+ return image.content.type === "picture" ? image.content.mediaId : void 0;
48809
+ }
48810
+ function imageDisplay(image) {
48811
+ if (image.type === "image") return image.display;
48812
+ return image.anchor.display;
48813
+ }
48814
+ function imageSourceKind(image) {
48815
+ return image.type === "image" ? "legacy-image" : "drawing-picture";
48816
+ }
48817
+ function compileImageScope(entry) {
48818
+ const { handle, image } = entry;
48819
+ const altText = imageAltText(image);
48820
+ const mediaId = imageMediaId(image);
48821
+ const display = imageDisplay(image);
48822
+ const sourceKind = imageSourceKind(image);
48823
+ return {
48824
+ handle,
48825
+ kind: "image",
48826
+ classifications: entry.classifications,
48827
+ content: {
48828
+ text: altText,
48829
+ excerpt: buildExcerpt(altText),
48830
+ authority: "structural-summary"
48831
+ },
48832
+ formatting: {},
48833
+ layout: display ? { flowKind: display } : {},
48834
+ geometry: {},
48835
+ workflow: { scopeIds: [], effectiveMode: "edit" },
48836
+ replaceability: deriveReplaceability("image", handle.provenance),
48837
+ audit: {
48838
+ source: "runtime",
48839
+ derivedFrom: [
48840
+ "canonical",
48841
+ `image-source:${sourceKind}`,
48842
+ ...mediaId ? [`media-id:${mediaId}`] : []
48843
+ ],
48844
+ confidence: "medium"
48845
+ },
48846
+ partial: true
48847
+ };
48848
+ }
48849
+
47600
48850
  // src/runtime/scopes/scope-kinds/list-item.ts
47601
48851
  function compileListItemScope(entry, options = {}) {
47602
48852
  const { handle, paragraph } = entry;
@@ -48185,6 +49435,8 @@ function compileScope(entry, optionsOrCatalog) {
48185
49435
  });
48186
49436
  case "field":
48187
49437
  return compileFieldScope(entry);
49438
+ case "image":
49439
+ return compileImageScope(entry);
48188
49440
  case "comment-thread":
48189
49441
  return compileCommentThreadScope(entry);
48190
49442
  case "revision":
@@ -48373,6 +49625,44 @@ function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentSco
48373
49625
  }
48374
49626
  return out;
48375
49627
  }
49628
+ function isImageInline(child) {
49629
+ return child.type === "image" || child.type === "drawing_frame" && child.content.type === "picture";
49630
+ }
49631
+ function enumerateImagesInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
49632
+ const out = [];
49633
+ for (let i = 0; i < paragraph.children.length; i += 1) {
49634
+ const child = paragraph.children[i];
49635
+ if (!isImageInline(child)) continue;
49636
+ const semanticPath = [
49637
+ "body",
49638
+ "paragraph",
49639
+ String(blockIndex),
49640
+ "image",
49641
+ String(i)
49642
+ ];
49643
+ const scopeId = `image:${blockIndex}:${i}`;
49644
+ const handle = {
49645
+ scopeId,
49646
+ documentId,
49647
+ storyTarget: MAIN_STORY2,
49648
+ semanticPath,
49649
+ parentScopeId,
49650
+ stableRef: { kind: "semantic-path", value: semanticPath.join("/") },
49651
+ provenance: "derived",
49652
+ rangePrecision: "canonical"
49653
+ };
49654
+ out.push({
49655
+ kind: "image",
49656
+ handle,
49657
+ image: child,
49658
+ paragraph,
49659
+ blockIndex,
49660
+ inlineIndex: i,
49661
+ classifications: Object.freeze([])
49662
+ });
49663
+ }
49664
+ return out;
49665
+ }
48376
49666
  function enumerateCommentThreads(document2, documentId) {
48377
49667
  const review = document2.review;
48378
49668
  const comments = review?.comments;
@@ -48503,6 +49793,8 @@ function enumerateScopes(document2, inputs = {}) {
48503
49793
  });
48504
49794
  const fields = enumerateFieldsInParagraph(block, index, documentId, scopeId);
48505
49795
  for (const field of fields) results.push(field);
49796
+ const images = enumerateImagesInParagraph(block, index, documentId, scopeId);
49797
+ for (const image of images) results.push(image);
48506
49798
  continue;
48507
49799
  }
48508
49800
  if (block.type === "table") {
@@ -48892,6 +50184,15 @@ var LIST_TEXT_TARGET_KINDS = /* @__PURE__ */ new Set([
48892
50184
  "sdt-paragraph-text",
48893
50185
  "secondary-story-paragraph-text"
48894
50186
  ]);
50187
+ var OBJECT_COMMAND_INTENTS = /* @__PURE__ */ new Set([
50188
+ "image-layout",
50189
+ "image-frame",
50190
+ "chart-edit",
50191
+ "custom-xml-update",
50192
+ "embedded-content-update",
50193
+ "opaque-content-preserve",
50194
+ "object-edit"
50195
+ ]);
48895
50196
  function freezeList(values) {
48896
50197
  return Object.freeze([...values]);
48897
50198
  }
@@ -49436,6 +50737,35 @@ function listStructureCapability(scope, context) {
49436
50737
  ]
49437
50738
  );
49438
50739
  }
50740
+ function objectEditCapability(context) {
50741
+ const objectTargets = (context?.editableTargets?.entries ?? []).filter(
50742
+ (entry) => entry.kind === "object-anchor" || entry.kind === "custom-xml-content" || entry.kind === "opaque-content" || entry.commandFamily === "object" || entry.commandFamily === "preserve-only-refusal" || entry.runtimeCommand.intents.some((intent) => OBJECT_COMMAND_INTENTS.has(intent))
50743
+ );
50744
+ const supportedTargets = objectTargets.filter(
50745
+ (entry) => entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.some((intent) => OBJECT_COMMAND_INTENTS.has(intent))
50746
+ );
50747
+ if (supportedTargets.length > 0) {
50748
+ return supportedCommand(
50749
+ "compile-supported:object-edit:editable-target",
50750
+ supportedTargets
50751
+ );
50752
+ }
50753
+ if (objectTargets.length > 0) {
50754
+ const blockers = commandTargetBlockers(objectTargets);
50755
+ return blocked(
50756
+ "compile-blocked:object-edit:target-ref-blocked",
50757
+ blockers.length > 0 ? blockers : ["compile-blocked:object-edit:target-ref-blocked"]
50758
+ );
50759
+ }
50760
+ return unsupported(
50761
+ "compile-unsupported:object-edit:no-target-family",
50762
+ [
50763
+ "compile-unsupported:object-edit:no-target-family",
50764
+ "capability:object-edit:l02-object-target-required",
50765
+ "capability:object-edit:l07-command-support-required"
50766
+ ]
50767
+ );
50768
+ }
49439
50769
  function deriveScopeCapabilities(scope, context = {}) {
49440
50770
  return {
49441
50771
  canReplaceText: replaceTextCapability(scope, context),
@@ -49452,7 +50782,8 @@ function deriveScopeCapabilities(scope, context = {}) {
49452
50782
  canEditTableStructure: tableStructureCapability(scope, context),
49453
50783
  canUseTableContinuationEvidence: tableContinuationEvidenceCapability(scope, context),
49454
50784
  canEditListText: listTextCapability(scope, context),
49455
- canEditListStructure: listStructureCapability(scope, context)
50785
+ canEditListStructure: listStructureCapability(scope, context),
50786
+ canEditObject: objectEditCapability(context)
49456
50787
  };
49457
50788
  }
49458
50789
 
@@ -49724,6 +51055,11 @@ function tokensForScope(entry) {
49724
51055
  { kind: "row", index: entry.rowIndex },
49725
51056
  { kind: "cell", index: entry.cellIndex }
49726
51057
  ]);
51058
+ case "image":
51059
+ return Object.freeze([
51060
+ { kind: "block", index: entry.blockIndex },
51061
+ { kind: "inline", index: entry.inlineIndex }
51062
+ ]);
49727
51063
  default:
49728
51064
  return null;
49729
51065
  }
@@ -49807,6 +51143,19 @@ function paragraphHasBookmarkId(paragraph, bookmarkId) {
49807
51143
  function commandActionHandleForAddress(commandFamily, address) {
49808
51144
  return address ? `scope-command:${commandFamily}:${address.addressKey}` : void 0;
49809
51145
  }
51146
+ function layoutIdentityIndexes(document2) {
51147
+ const byTargetKey = /* @__PURE__ */ new Map();
51148
+ const byStoryBlockPath = /* @__PURE__ */ new Map();
51149
+ for (const identity of collectLayoutInputIdentities(document2)) {
51150
+ byStoryBlockPath.set(`${identity.storyKey}:${identity.blockPath}`, identity);
51151
+ const targetKey = identity.editableTargetRef?.targetKey;
51152
+ if (targetKey) byTargetKey.set(targetKey, identity);
51153
+ }
51154
+ return { byTargetKey, byStoryBlockPath };
51155
+ }
51156
+ function layoutIdentityForTarget(target, indexes) {
51157
+ return indexes.byTargetKey.get(target.targetKey) ?? indexes.byStoryBlockPath.get(`${target.storyKey}:${target.blockPath}`);
51158
+ }
49810
51159
  function withCommandAction(evidence, target, canonicalAddress = target.canonicalAddress) {
49811
51160
  if (evidence.status !== "supported" || !canonicalAddress) return evidence;
49812
51161
  return {
@@ -49818,6 +51167,24 @@ function withCommandAction(evidence, target, canonicalAddress = target.canonical
49818
51167
  function commandAddressForTarget(target, scopeKind) {
49819
51168
  return scopeKind === "list-item" && target.listAddress !== void 0 ? target.listAddress : target.canonicalAddress;
49820
51169
  }
51170
+ function freezeCommandIntents(intents) {
51171
+ return Object.freeze([...new Set(intents)]);
51172
+ }
51173
+ function relatedListActionHandlesForTarget(target, primaryAddress) {
51174
+ if (target.commandFamily !== "text-leaf" || target.listAddress === void 0 || !LIST_TEXT_TARGET_KINDS2.has(target.kind) || target.listAddress.addressKey === primaryAddress?.addressKey) {
51175
+ return void 0;
51176
+ }
51177
+ const actionHandle = commandActionHandleForAddress(target.commandFamily, target.listAddress);
51178
+ if (!actionHandle) return void 0;
51179
+ return Object.freeze([
51180
+ {
51181
+ actionHandle,
51182
+ canonicalAddress: target.listAddress,
51183
+ intents: freezeCommandIntents(["list-text-edit", "list-structure-action"]),
51184
+ reason: "l07:list-address-target-supported"
51185
+ }
51186
+ ]);
51187
+ }
49821
51188
  function runtimeTextCommandEvidence(target, workflowBlockers) {
49822
51189
  const shapeIssues = validateEditableTargetRef(target);
49823
51190
  if (shapeIssues.length > 0) {
@@ -49917,13 +51284,16 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand, scopeKind
49917
51284
  };
49918
51285
  }
49919
51286
  if (target.commandFamily === "text-leaf") {
51287
+ const canonicalAddress = commandAddressForTarget(target, scopeKind);
51288
+ const relatedActionHandles = relatedListActionHandlesForTarget(target, canonicalAddress);
49920
51289
  return withCommandAction({
49921
51290
  status: textCommand.status,
49922
51291
  commandFamily: target.commandFamily,
49923
51292
  intents: commandIntentsForTarget(target, scopeKind),
49924
51293
  reason: textCommand.reason,
51294
+ ...relatedActionHandles !== void 0 ? { relatedActionHandles } : {},
49925
51295
  ...textCommand.blockers ? { blockers: textCommand.blockers } : {}
49926
- }, target, commandAddressForTarget(target, scopeKind));
51296
+ }, target, canonicalAddress);
49927
51297
  }
49928
51298
  if (target.commandFamily === "comment-revision") {
49929
51299
  const isOpen = target.review?.status === "open";
@@ -50248,19 +51618,21 @@ function commandIntentsForTarget(target, scopeKind) {
50248
51618
  if (target.kind === "hyperlink-text") {
50249
51619
  return Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]);
50250
51620
  }
50251
- if (target.table?.operationScope === "text") {
50252
- return Object.freeze([
50253
- "text-leaf-edit",
50254
- "table-text-paste",
50255
- "table-text-drop",
50256
- "table-structured-fragment-paste",
50257
- "table-structured-fragment-drop"
50258
- ]);
50259
- }
50260
- if (scopeKind === "list-item" && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
50261
- return Object.freeze(["text-leaf-edit", "list-text-edit", "list-structure-action"]);
51621
+ {
51622
+ const intents = ["text-leaf-edit"];
51623
+ if (target.table?.operationScope === "text") {
51624
+ intents.push(
51625
+ "table-text-paste",
51626
+ "table-text-drop",
51627
+ "table-structured-fragment-paste",
51628
+ "table-structured-fragment-drop"
51629
+ );
51630
+ }
51631
+ if ((scopeKind === "list-item" || target.listAddress !== void 0) && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
51632
+ intents.push("list-text-edit", "list-structure-action");
51633
+ }
51634
+ return freezeCommandIntents(intents);
50262
51635
  }
50263
- return Object.freeze(["text-leaf-edit"]);
50264
51636
  case "field":
50265
51637
  return Object.freeze(
50266
51638
  target.field?.fieldFamily === "TOC" ? ["toc-refresh", "field-update"] : ["field-update"]
@@ -50407,7 +51779,7 @@ function readbackForTarget(document2, target, runtimeTextCommand) {
50407
51779
  source: "canonical-text-leaf"
50408
51780
  };
50409
51781
  }
50410
- function projectTarget(document2, target, relation, workflowBlockers, scopeKind) {
51782
+ function projectTarget(document2, target, relation, workflowBlockers, scopeKind, layoutIdentity) {
50411
51783
  const runtimeTextCommand = runtimeTextCommandEvidence(target, workflowBlockers);
50412
51784
  const readback = readbackForTarget(document2, target, runtimeTextCommand);
50413
51785
  return {
@@ -50428,6 +51800,7 @@ function projectTarget(document2, target, relation, workflowBlockers, scopeKind)
50428
51800
  ...target.table ? { table: target.table } : {},
50429
51801
  ...target.editableOwner ? { editableOwner: target.editableOwner } : {},
50430
51802
  ...target.canonicalAddress ? { canonicalAddress: target.canonicalAddress } : {},
51803
+ ...layoutIdentity ? { layoutIdentity } : {},
50431
51804
  staleCheck: target.staleCheck,
50432
51805
  posture: target.posture,
50433
51806
  ...workflowBlockers.length > 0 ? { workflowBlockers: Object.freeze([...workflowBlockers]) } : {},
@@ -50443,7 +51816,7 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
50443
51816
  facts.push(projectWorkflowBlockerFact(fact));
50444
51817
  factsByTargetKey.set(fact.targetKey, facts);
50445
51818
  }
50446
- const entries = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
51819
+ const relatedTargets = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
50447
51820
  const relation = relationForTarget(target, scope, entry);
50448
51821
  if (!relation) return null;
50449
51822
  const workflowBlockers = Object.freeze(
@@ -50451,8 +51824,19 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
50451
51824
  (left, right) => left.source.localeCompare(right.source) || left.blocker.localeCompare(right.blocker) || left.refusalId.localeCompare(right.refusalId)
50452
51825
  )
50453
51826
  );
50454
- return projectTarget(document2, target, relation, workflowBlockers, scope.kind);
50455
- }).filter((target) => target !== null).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
51827
+ return { target, relation, workflowBlockers };
51828
+ }).filter((target) => target !== null);
51829
+ const layoutIndexes = relatedTargets.length > 0 ? layoutIdentityIndexes(document2) : void 0;
51830
+ const entries = relatedTargets.map(
51831
+ ({ target, relation, workflowBlockers }) => projectTarget(
51832
+ document2,
51833
+ target,
51834
+ relation,
51835
+ workflowBlockers,
51836
+ scope.kind,
51837
+ layoutIndexes ? layoutIdentityForTarget(target, layoutIndexes) : void 0
51838
+ )
51839
+ ).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
50456
51840
  const blockers = /* @__PURE__ */ new Set();
50457
51841
  let supportedTextTargetCount = 0;
50458
51842
  for (const target of entries) {
@@ -50471,6 +51855,114 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
50471
51855
  };
50472
51856
  }
50473
51857
 
51858
+ // src/runtime/scopes/object-evidence.ts
51859
+ var OBJECT_INTENTS = /* @__PURE__ */ new Set([
51860
+ "image-layout",
51861
+ "image-frame",
51862
+ "chart-edit",
51863
+ "custom-xml-update",
51864
+ "embedded-content-update",
51865
+ "opaque-content-preserve",
51866
+ "object-edit",
51867
+ "preserve-only-refusal"
51868
+ ]);
51869
+ function unique2(values) {
51870
+ return Object.freeze([...new Set(values.filter((value) => value.length > 0))]);
51871
+ }
51872
+ function isObjectTarget(entry) {
51873
+ return entry.object !== void 0 || entry.kind === "object-anchor" || entry.kind === "custom-xml-content" || entry.kind === "opaque-content" || entry.commandFamily === "object" || entry.runtimeCommand.intents.some((intent) => OBJECT_INTENTS.has(intent));
51874
+ }
51875
+ function blockersFor(entry) {
51876
+ return unique2([
51877
+ ...entry.posture.blockers,
51878
+ ...entry.runtimeCommand.blockers ?? [],
51879
+ ...entry.runtimeTextCommand.blockers ?? [],
51880
+ ...(entry.workflowBlockers ?? []).flatMap((blocker2) => [
51881
+ blocker2.blocker,
51882
+ blocker2.refusalId
51883
+ ]),
51884
+ entry.runtimeCommand.status === "blocked" ? entry.runtimeCommand.reason : ""
51885
+ ]);
51886
+ }
51887
+ function projectEntry2(entry) {
51888
+ const blockers = blockersFor(entry);
51889
+ return {
51890
+ targetKey: entry.targetKey,
51891
+ kind: entry.kind,
51892
+ ...entry.object?.objectKind ? { objectKind: entry.object.objectKind } : {},
51893
+ relation: entry.relation,
51894
+ commandFamily: entry.commandFamily,
51895
+ editability: entry.editability,
51896
+ ...entry.sourceRef ? { sourceRef: entry.sourceRef } : {},
51897
+ ...entry.object ? { object: entry.object } : {},
51898
+ runtimeCommand: entry.runtimeCommand,
51899
+ blockers,
51900
+ ...entry.posture.preserveOnly ? { preserveOnly: true } : {}
51901
+ };
51902
+ }
51903
+ function familyKindFor(entry) {
51904
+ return entry.objectKind ?? "unknown";
51905
+ }
51906
+ function familyStatusFor(entries) {
51907
+ if (entries.some((entry) => entry.runtimeCommand.status === "supported")) {
51908
+ return "supported";
51909
+ }
51910
+ if (entries.length > 0 && entries.every(
51911
+ (entry) => entry.preserveOnly === true || entry.commandFamily === "preserve-only-refusal"
51912
+ )) {
51913
+ return "preserve-only";
51914
+ }
51915
+ return "blocked";
51916
+ }
51917
+ function summarizeFamily(objectKind, entries) {
51918
+ const sortedEntries = [...entries].sort(
51919
+ (left, right) => left.targetKey.localeCompare(right.targetKey)
51920
+ );
51921
+ const preserveOnly = sortedEntries.some((entry) => entry.preserveOnly === true);
51922
+ return {
51923
+ objectKind,
51924
+ status: familyStatusFor(sortedEntries),
51925
+ count: sortedEntries.length,
51926
+ targetKeys: unique2(sortedEntries.map((entry) => entry.targetKey)),
51927
+ relations: unique2(sortedEntries.map((entry) => entry.relation)),
51928
+ commandFamilies: unique2(sortedEntries.map((entry) => entry.commandFamily)),
51929
+ runtimeIntents: unique2(
51930
+ sortedEntries.flatMap((entry) => [...entry.runtimeCommand.intents])
51931
+ ),
51932
+ blockers: unique2(sortedEntries.flatMap((entry) => [...entry.blockers])),
51933
+ ...preserveOnly ? { preserveOnly: true } : {}
51934
+ };
51935
+ }
51936
+ function summarizeFamilies(entries) {
51937
+ const byFamily = /* @__PURE__ */ new Map();
51938
+ for (const entry of entries) {
51939
+ const objectKind = familyKindFor(entry);
51940
+ const bucket = byFamily.get(objectKind);
51941
+ if (bucket) {
51942
+ bucket.push(entry);
51943
+ continue;
51944
+ }
51945
+ byFamily.set(objectKind, [entry]);
51946
+ }
51947
+ return Object.freeze(
51948
+ [...byFamily.entries()].map(([objectKind, familyEntries]) => summarizeFamily(objectKind, familyEntries)).sort((left, right) => left.objectKind.localeCompare(right.objectKind))
51949
+ );
51950
+ }
51951
+ function deriveScopeObjectEvidence(editableTargets) {
51952
+ const entries = Object.freeze(
51953
+ [...editableTargets?.entries ?? []].filter(isObjectTarget).map(projectEntry2).sort((left, right) => left.targetKey.localeCompare(right.targetKey))
51954
+ );
51955
+ const blockers = unique2(entries.flatMap((entry) => [...entry.blockers]));
51956
+ const families = summarizeFamilies(entries);
51957
+ return {
51958
+ status: entries.length > 0 ? "present" : "none",
51959
+ count: entries.length,
51960
+ blockers,
51961
+ families,
51962
+ entries
51963
+ };
51964
+ }
51965
+
50474
51966
  // src/runtime/scopes/table-evidence.ts
50475
51967
  function isTableFamilyEntry(entry) {
50476
51968
  return entry?.kind === "table" || entry?.kind === "table-row" || entry?.kind === "table-cell";
@@ -51040,7 +52532,7 @@ function deriveScopeGeometryEvidence(scopeId, provider, context) {
51040
52532
  function freezeList2(values) {
51041
52533
  return values ? Object.freeze([...values]) : void 0;
51042
52534
  }
51043
- function unique2(values) {
52535
+ function unique3(values) {
51044
52536
  return Object.freeze([...new Set(values)]);
51045
52537
  }
51046
52538
  function parseTableFamilyScopeId2(scopeId) {
@@ -51070,7 +52562,7 @@ function candidateTableBlockIds(blockIndex, mapped) {
51070
52562
  const mappedId = mapped?.get(blockIndex);
51071
52563
  if (mappedId) ids.push(mappedId);
51072
52564
  ids.push(`table-${blockIndex}`, `table:${blockIndex}`, `block-${blockIndex}`);
51073
- return unique2(ids);
52565
+ return unique3(ids);
51074
52566
  }
51075
52567
  function rowInFragment(fragment, rowIndex) {
51076
52568
  const range = fragment.tableRowRange;
@@ -51107,19 +52599,19 @@ function projectTableFramePage(fragment) {
51107
52599
  ...fragment.tableRowRange ? { rowRange: { ...fragment.tableRowRange } } : {},
51108
52600
  ...fragment.continuation?.continuesFromPreviousPage !== void 0 ? { continuesFromPreviousPage: fragment.continuation.continuesFromPreviousPage } : {},
51109
52601
  ...fragment.continuation?.continuesToNextPage !== void 0 ? { continuesToNextPage: fragment.continuation.continuesToNextPage } : {},
51110
- ...repeated ? { repeatedHeaderRowIndexes: unique2(repeated) } : {},
52602
+ ...repeated ? { repeatedHeaderRowIndexes: unique3(repeated) } : {},
51111
52603
  ...splitRowCarry ? { splitRowCarry: Object.freeze(splitRowCarry.map((item) => ({ ...item }))) } : {},
51112
52604
  ...carry ? { verticalMergeCarry: Object.freeze(carry.map((item) => ({ ...item }))) } : {}
51113
52605
  };
51114
52606
  }
51115
52607
  function projectTableFrame(blockId, parsed, fragments) {
51116
- const pageIds = unique2(fragments.map((fragment) => fragment.pageId));
51117
- const pageSliceIds = unique2(fragments.map((fragment) => fragment.fragmentId));
51118
- const layoutObjectIds = unique2(
52608
+ const pageIds = unique3(fragments.map((fragment) => fragment.pageId));
52609
+ const pageSliceIds = unique3(fragments.map((fragment) => fragment.fragmentId));
52610
+ const layoutObjectIds = unique3(
51119
52611
  fragments.map((fragment) => fragment.layoutObject?.objectId).filter((objectId) => typeof objectId === "string" && objectId.length > 0)
51120
52612
  );
51121
52613
  const rowRangesByPage = Object.freeze(fragments.map(projectTableFramePage));
51122
- const repeatedHeaderRowIndexes = unique2(
52614
+ const repeatedHeaderRowIndexes = unique3(
51123
52615
  fragments.flatMap((fragment) => fragment.continuation?.repeatedHeaderRowIndexes ?? [])
51124
52616
  );
51125
52617
  const verticalMergeCarry = Object.freeze(
@@ -51144,7 +52636,7 @@ function projectTableFrame(blockId, parsed, fragments) {
51144
52636
  };
51145
52637
  }
51146
52638
  function continuationFromTableFrame(fragments) {
51147
- const pageIds = unique2(fragments.map((fragment) => fragment.pageId));
52639
+ const pageIds = unique3(fragments.map((fragment) => fragment.pageId));
51148
52640
  return {
51149
52641
  ...pageIds.length > 0 ? { pageIds } : {},
51150
52642
  pageCount: pageIds.length,
@@ -51442,6 +52934,7 @@ function composeEvidence(inputs) {
51442
52934
  ...editableTargets ? { editableTargets } : {},
51443
52935
  layout
51444
52936
  });
52937
+ const objects = deriveScopeObjectEvidence(editableTargets);
51445
52938
  return {
51446
52939
  formattingSummary: formattingSummaryOf(scope),
51447
52940
  reviewItemIds,
@@ -51452,6 +52945,7 @@ function composeEvidence(inputs) {
51452
52945
  ...adjacentGeometry ? { adjacentGeometry } : {},
51453
52946
  visualization: deriveScopeVisualization(scope),
51454
52947
  ...editableTargets ? { editableTargets } : {},
52948
+ objects,
51455
52949
  ...table ? { table } : {},
51456
52950
  contentControls,
51457
52951
  capabilities,
@@ -58679,7 +60173,7 @@ function shouldYield(scheduler, lastYieldAt) {
58679
60173
  const deadline = scheduler.frameDeadlineMs ?? DEFAULT_FRAME_DEADLINE_MS;
58680
60174
  return now3 - lastYieldAt >= deadline;
58681
60175
  }
58682
- function nowMs() {
60176
+ function nowMs2() {
58683
60177
  return typeof performance !== "undefined" ? performance.now() : Date.now();
58684
60178
  }
58685
60179
  function createLoadScheduler(options = {}) {
@@ -59247,8 +60741,7 @@ async function collectEditorStateForSerialize(args) {
59247
60741
  // src/io/ooxml/parse-bookmark-references.ts
59248
60742
  var HYPERLINK_ANCHOR_RE = /<(?:\w+:)?hyperlink\b[^>]*\bw:anchor\s*=\s*"([^"]*)"/gi;
59249
60743
  var INSTR_TEXT_RE = /<(?:\w+:)?instrText\b[^>]*>([\s\S]*?)<\/(?:\w+:)?instrText>/gi;
59250
- var FLD_SIMPLE_INSTR_DOUBLE_RE = /<(?:\w+:)?fldSimple\b[^>]*?\b(?:w:)?instr\s*=\s*"([^"]*)"/gi;
59251
- var FLD_SIMPLE_INSTR_SINGLE_RE = /<(?:\w+:)?fldSimple\b[^>]*?\b(?:w:)?instr\s*=\s*'([^']*)'/gi;
60744
+ var FLD_SIMPLE_INSTR_RE = /<(?:\w+:)?fldSimple\b[^>]*\bw:instr\s*=\s*(["'])([\s\S]*?)\1/gi;
59252
60745
  var TOC_FIELD_RE = /\bTOC\b/;
59253
60746
  var REFLIKE_FIELD_RE = /\b(?:HYPERLINK|REF|PAGEREF|NOTEREF)\s+([A-Za-z0-9_:.\-]+)/g;
59254
60747
  var DATA_BINDING_RE = /<(?:\w+:)?dataBinding\b/i;
@@ -59267,21 +60760,14 @@ function scanBookmarkReferences(documentXml, callerAllowlist = []) {
59267
60760
  }
59268
60761
  INSTR_TEXT_RE.lastIndex = 0;
59269
60762
  while ((m = INSTR_TEXT_RE.exec(documentXml)) !== null) {
59270
- const instrText = m[1] ?? "";
59271
- scanFieldInstruction(instrText, retained, () => {
59272
- retainAllToc = true;
59273
- });
59274
- }
59275
- FLD_SIMPLE_INSTR_DOUBLE_RE.lastIndex = 0;
59276
- while ((m = FLD_SIMPLE_INSTR_DOUBLE_RE.exec(documentXml)) !== null) {
59277
- scanFieldInstruction(decodeXmlAttribute(m[1] ?? ""), retained, () => {
59278
- retainAllToc = true;
60763
+ retainInstructionReferences(m[1] ?? "", retained, (value) => {
60764
+ retainAllToc = retainAllToc || value;
59279
60765
  });
59280
60766
  }
59281
- FLD_SIMPLE_INSTR_SINGLE_RE.lastIndex = 0;
59282
- while ((m = FLD_SIMPLE_INSTR_SINGLE_RE.exec(documentXml)) !== null) {
59283
- scanFieldInstruction(decodeXmlAttribute(m[1] ?? ""), retained, () => {
59284
- retainAllToc = true;
60767
+ FLD_SIMPLE_INSTR_RE.lastIndex = 0;
60768
+ while ((m = FLD_SIMPLE_INSTR_RE.exec(documentXml)) !== null) {
60769
+ retainInstructionReferences(m[2] ?? "", retained, (value) => {
60770
+ retainAllToc = retainAllToc || value;
59285
60771
  });
59286
60772
  }
59287
60773
  retainRevisionBoundedBookmarks(documentXml, retained);
@@ -59291,17 +60777,14 @@ function scanBookmarkReferences(documentXml, callerAllowlist = []) {
59291
60777
  retainAll
59292
60778
  };
59293
60779
  }
59294
- function scanFieldInstruction(instrText, retained, retainToc) {
59295
- if (TOC_FIELD_RE.test(instrText)) retainToc();
60780
+ function retainInstructionReferences(instrText, retained, setRetainAllToc) {
60781
+ if (TOC_FIELD_RE.test(instrText)) setRetainAllToc(true);
59296
60782
  REFLIKE_FIELD_RE.lastIndex = 0;
59297
60783
  let r;
59298
60784
  while ((r = REFLIKE_FIELD_RE.exec(instrText)) !== null) {
59299
60785
  if (r[1]) retained.add(r[1]);
59300
60786
  }
59301
60787
  }
59302
- function decodeXmlAttribute(value) {
59303
- return value.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
59304
- }
59305
60788
  function retainRevisionBoundedBookmarks(documentXml, retained) {
59306
60789
  const starts = /* @__PURE__ */ new Map();
59307
60790
  BOOKMARK_START_RE.lastIndex = 0;
@@ -59690,7 +61173,7 @@ function parseNumberingXml(xml, context) {
59690
61173
  abstractDefinitions[abstractNumberingId] = {
59691
61174
  abstractNumberingId,
59692
61175
  ...context?.partPath !== void 0 ? { sourceRef: createNumberingSourceRef(context.partPath, "abstractNum", rawId, childXmlPath) } : {},
59693
- levels: readLevels(child),
61176
+ levels: readLevels(child, context, childXmlPath, rawId),
59694
61177
  ...nsid ? { nsid } : {},
59695
61178
  ...multiLevelType ? { multiLevelType } : {},
59696
61179
  ...tplc ? { tplc } : {},
@@ -59711,7 +61194,7 @@ function parseNumberingXml(xml, context) {
59711
61194
  numberingInstanceId,
59712
61195
  ...context?.partPath !== void 0 ? { sourceRef: createNumberingSourceRef(context.partPath, "num", rawId, childXmlPath) } : {},
59713
61196
  abstractNumberingId: toCanonicalAbstractNumberingId(rawAbstractId),
59714
- overrides: readOverrides(child)
61197
+ overrides: readOverrides(child, context, childXmlPath, rawId)
59715
61198
  };
59716
61199
  break;
59717
61200
  }
@@ -59732,6 +61215,16 @@ function createNumberingSourceRef(partPath, element, rawId, xmlPath) {
59732
61215
  ...xmlPath !== void 0 ? { xmlPath } : {}
59733
61216
  };
59734
61217
  }
61218
+ function createNumberingChildSourceRef(partPath, element, sourceIdSuffix, xmlPath) {
61219
+ if (partPath === void 0) return void 0;
61220
+ return {
61221
+ sourceId: `part:${partPath}#${sourceIdSuffix}`,
61222
+ partPath,
61223
+ storyKind: "numbering",
61224
+ element,
61225
+ ...xmlPath !== void 0 ? { xmlPath } : {}
61226
+ };
61227
+ }
59735
61228
  function readNumPicBullet(node, numPicBulletId, context, xmlPath) {
59736
61229
  let widthEmu;
59737
61230
  let heightEmu;
@@ -59808,18 +61301,29 @@ function toCanonicalAbstractNumberingId(value) {
59808
61301
  function toCanonicalNumberingInstanceId(value) {
59809
61302
  return value.startsWith("num:") ? value : `num:${value}`;
59810
61303
  }
59811
- function readLevels(abstractNode) {
61304
+ function readLevels(abstractNode, context, abstractXmlPath, rawAbstractId) {
59812
61305
  const levels = [];
61306
+ let levelOrdinal = 0;
59813
61307
  for (const child of abstractNode.children) {
59814
61308
  if (child.type !== "element" || localName(child.name) !== "lvl") {
59815
61309
  continue;
59816
61310
  }
61311
+ levelOrdinal += 1;
59817
61312
  const rawLevel = readStringAttr(child, "w:ilvl");
59818
61313
  const level = rawLevel === void 0 ? void 0 : parseInteger(rawLevel);
59819
61314
  if (level === void 0) {
59820
61315
  continue;
59821
61316
  }
59822
- const definition = readLevelDefinition(child, level);
61317
+ const definition = readLevelDefinition(
61318
+ child,
61319
+ level,
61320
+ createNumberingChildSourceRef(
61321
+ context?.partPath,
61322
+ "lvl",
61323
+ `abstractNum:${rawAbstractId}:lvl:${level}`,
61324
+ `${abstractXmlPath}/lvl[${levelOrdinal}]`
61325
+ )
61326
+ );
59823
61327
  if (!definition) {
59824
61328
  continue;
59825
61329
  }
@@ -59827,12 +61331,14 @@ function readLevels(abstractNode) {
59827
61331
  }
59828
61332
  return levels.sort((left, right) => left.level - right.level);
59829
61333
  }
59830
- function readOverrides(numNode) {
61334
+ function readOverrides(numNode, context, numXmlPath, rawNumId) {
59831
61335
  const overrides = [];
61336
+ let overrideOrdinal = 0;
59832
61337
  for (const child of numNode.children) {
59833
61338
  if (child.type !== "element" || localName(child.name) !== "lvlOverride") {
59834
61339
  continue;
59835
61340
  }
61341
+ overrideOrdinal += 1;
59836
61342
  const rawLevel = readStringAttr(child, "w:ilvl");
59837
61343
  const level = rawLevel === void 0 ? void 0 : parseInteger(rawLevel);
59838
61344
  if (level === void 0) {
@@ -59842,8 +61348,26 @@ function readOverrides(numNode) {
59842
61348
  const rawStart = startOverrideNode ? readStringAttr(startOverrideNode, "w:val") : void 0;
59843
61349
  const startAt = rawStart === void 0 ? void 0 : parseInteger(rawStart);
59844
61350
  const levelDefinitionNode = findChildElementOptional(child, "lvl");
59845
- const levelDefinition = levelDefinitionNode ? readLevelOverrideDefinition(levelDefinitionNode, level) : void 0;
61351
+ const overrideXmlPath = `${numXmlPath}/lvlOverride[${overrideOrdinal}]`;
61352
+ const levelDefinition = levelDefinitionNode ? readLevelOverrideDefinition(
61353
+ levelDefinitionNode,
61354
+ level,
61355
+ createNumberingChildSourceRef(
61356
+ context?.partPath,
61357
+ "lvl",
61358
+ `num:${rawNumId}:lvlOverride:${level}:lvl`,
61359
+ `${overrideXmlPath}/lvl[1]`
61360
+ )
61361
+ ) : void 0;
59846
61362
  overrides.push({
61363
+ ...context?.partPath !== void 0 ? {
61364
+ sourceRef: createNumberingChildSourceRef(
61365
+ context.partPath,
61366
+ "lvlOverride",
61367
+ `num:${rawNumId}:lvlOverride:${level}`,
61368
+ overrideXmlPath
61369
+ )
61370
+ } : {},
59847
61371
  level,
59848
61372
  ...startAt !== void 0 ? { startAt } : {},
59849
61373
  ...levelDefinition ? { levelDefinition } : {}
@@ -59851,7 +61375,7 @@ function readOverrides(numNode) {
59851
61375
  }
59852
61376
  return overrides.sort((left, right) => left.level - right.level);
59853
61377
  }
59854
- function readLevelDefinition(levelNode, fallbackLevel) {
61378
+ function readLevelDefinition(levelNode, fallbackLevel, sourceRef) {
59855
61379
  const rawLevel = readStringAttr(levelNode, "w:ilvl");
59856
61380
  const level = rawLevel === void 0 ? fallbackLevel : parseInteger(rawLevel);
59857
61381
  if (level === void 0) {
@@ -59881,6 +61405,7 @@ function readLevelDefinition(levelNode, fallbackLevel) {
59881
61405
  const lvlPicBulletNode = findChildElementOptional(levelNode, "lvlPicBulletId");
59882
61406
  const picBulletId = lvlPicBulletNode ? readStringAttr(lvlPicBulletNode, "w:val") : void 0;
59883
61407
  return {
61408
+ ...sourceRef ? { sourceRef } : {},
59884
61409
  level,
59885
61410
  format,
59886
61411
  text,
@@ -59894,7 +61419,7 @@ function readLevelDefinition(levelNode, fallbackLevel) {
59894
61419
  ...picBulletId !== void 0 ? { picBulletId } : {}
59895
61420
  };
59896
61421
  }
59897
- function readLevelOverrideDefinition(levelNode, fallbackLevel) {
61422
+ function readLevelOverrideDefinition(levelNode, fallbackLevel, sourceRef) {
59898
61423
  const rawLevel = readStringAttr(levelNode, "w:ilvl");
59899
61424
  const level = rawLevel === void 0 ? fallbackLevel : parseInteger(rawLevel);
59900
61425
  if (level === void 0) {
@@ -59928,6 +61453,7 @@ function readLevelOverrideDefinition(levelNode, fallbackLevel) {
59928
61453
  return void 0;
59929
61454
  }
59930
61455
  return {
61456
+ ...sourceRef ? { sourceRef } : {},
59931
61457
  level,
59932
61458
  ...startAt !== void 0 ? { startAt } : {},
59933
61459
  ...format !== void 0 ? { format } : {},
@@ -66121,7 +67647,7 @@ async function normalizeParsedTextDocumentAsync(document2, packagePartName = "/w
66121
67647
  };
66122
67648
  const children = [];
66123
67649
  let previousParagraph = false;
66124
- let lastYieldAt = nowMs();
67650
+ let lastYieldAt = nowMs2();
66125
67651
  for (let i = 0; i < document2.blocks.length; i += 1) {
66126
67652
  const block = document2.blocks[i];
66127
67653
  const normalizedBlocks = normalizeBlocks(block, state, packagePartName);
@@ -66134,7 +67660,7 @@ async function normalizeParsedTextDocumentAsync(document2, packagePartName = "/w
66134
67660
  }
66135
67661
  if (i > 0 && i % NORMALIZE_YIELD_STRIDE === 0 && shouldYield(scheduler, lastYieldAt)) {
66136
67662
  await scheduler.yield();
66137
- lastYieldAt = nowMs();
67663
+ lastYieldAt = nowMs2();
66138
67664
  }
66139
67665
  }
66140
67666
  const content = { type: "doc", children };
@@ -67505,8 +69031,6 @@ function sortJson2(value) {
67505
69031
 
67506
69032
  // src/runtime/document-runtime.ts
67507
69033
  var CANONICAL_BLOCK_REFS_SYMBOL2 = /* @__PURE__ */ Symbol.for("wre.canonical-block-refs");
67508
- var LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES = 5e3;
67509
- var LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK = 256;
67510
69034
  function getLocalTextPatchMetadata(mapping) {
67511
69035
  const metadata = mapping.metadata?.localTextPatch;
67512
69036
  if (!metadata || typeof metadata !== "object") {
@@ -67613,6 +69137,17 @@ function createDocumentRuntime(options) {
67613
69137
  const loadScheduler = options.loadScheduler ?? createLoadScheduler({ backendOverride: "sync" });
67614
69138
  let effectiveMarkupModeProvider;
67615
69139
  const perfCounters = new PerfCounters();
69140
+ const hotEditTraces = new HotEditTraceRecorder();
69141
+ let cachedHotEditPolicy = null;
69142
+ function getHotEditPolicy() {
69143
+ if (cachedHotEditPolicy?.document === state.document) {
69144
+ return cachedHotEditPolicy.policy;
69145
+ }
69146
+ const profile = deriveHotEditDocumentProfile(state.document);
69147
+ const policy = resolveHotEditPolicy(profile);
69148
+ cachedHotEditPolicy = { document: state.document, profile, policy };
69149
+ return policy;
69150
+ }
67616
69151
  let analyticsEmitScheduled = false;
67617
69152
  let analyticsEmitScheduleMode = "none";
67618
69153
  let deferNextContextAnalyticsEmit = false;
@@ -67684,7 +69219,6 @@ function createDocumentRuntime(options) {
67684
69219
  let viewportBlockRanges = null;
67685
69220
  let viewportRangesKey = serializeViewportRanges(viewportBlockRanges);
67686
69221
  let viewportBlocksPerPageEstimate = null;
67687
- const EDITING_CORRIDOR_BLOCK_RADIUS = 8;
67688
69222
  const getRuntimeForLayoutFacet = () => {
67689
69223
  if (!runtimeRef) {
67690
69224
  throw new Error("Document runtime viewport methods are not initialized");
@@ -68018,8 +69552,8 @@ function createDocumentRuntime(options) {
68018
69552
  return viewportBlockRanges;
68019
69553
  }
68020
69554
  const corridor = {
68021
- start: Math.max(0, caretBlockIndex - EDITING_CORRIDOR_BLOCK_RADIUS),
68022
- end: Math.min(previousSurface.blocks.length, caretBlockIndex + EDITING_CORRIDOR_BLOCK_RADIUS + 1)
69555
+ start: Math.max(0, caretBlockIndex - getHotEditPolicy().selectionCorridorBlockRadius),
69556
+ end: Math.min(previousSurface.blocks.length, caretBlockIndex + getHotEditPolicy().selectionCorridorBlockRadius + 1)
68023
69557
  };
68024
69558
  return normalizeViewportRanges([...viewportBlockRanges ?? [], corridor]);
68025
69559
  }
@@ -68100,8 +69634,12 @@ function createDocumentRuntime(options) {
68100
69634
  (block2) => block2.kind === "paragraph" && editFrom >= block2.from && editTo <= block2.to
68101
69635
  );
68102
69636
  if (blockIndex < 0) {
68103
- perfCounters.increment("surface.localText.patchMiss");
68104
- return null;
69637
+ return tryPatchNestedLocalTextSurface(
69638
+ previousSurface,
69639
+ editFrom,
69640
+ editTo,
69641
+ patch.insertedText
69642
+ );
68105
69643
  }
68106
69644
  const block = previousSurface.blocks[blockIndex];
68107
69645
  if (block.kind !== "paragraph") {
@@ -68176,6 +69714,34 @@ function createDocumentRuntime(options) {
68176
69714
  perfCounters.increment("surface.localText.total.us", Math.round((performance.now() - tTotal0) * 1e3));
68177
69715
  }
68178
69716
  }
69717
+ function tryPatchNestedLocalTextSurface(previousSurface, editFrom, editTo, insertedText) {
69718
+ const shiftBudget = estimateLocalTextPatchShiftBudget(previousSurface.blocks, 0);
69719
+ perfCounters.increment("surface.localText.shiftedBlocks", shiftBudget.shiftedBlocks);
69720
+ perfCounters.increment("surface.localText.shiftedNodes", shiftBudget.shiftedNodes);
69721
+ if (!shiftBudget.withinBudget) {
69722
+ perfCounters.increment("surface.localText.patchBudgetFallback");
69723
+ return null;
69724
+ }
69725
+ const patched = patchSurfaceTextSegment(previousSurface, {
69726
+ from: editFrom,
69727
+ to: editTo,
69728
+ insertedText
69729
+ });
69730
+ if (patched.status !== "patched") {
69731
+ perfCounters.increment("surface.localText.patchMiss");
69732
+ return null;
69733
+ }
69734
+ const refs = getSurfaceCanonicalBlockRefs(previousSurface);
69735
+ if (refs) {
69736
+ const nextRefs = state.document.content.children.map(
69737
+ (block, index) => block ?? refs[index] ?? null
69738
+ );
69739
+ attachSurfaceCanonicalBlockRefs(patched.surface, nextRefs);
69740
+ }
69741
+ perfCounters.increment("surface.localText.patchHit");
69742
+ perfCounters.increment("surface.localText.patchDelta", insertedText.length - (editTo - editFrom));
69743
+ return patched.surface;
69744
+ }
68179
69745
  function shiftSurfaceInlineSegment(segment, delta) {
68180
69746
  if (delta === 0) return segment;
68181
69747
  return {
@@ -68223,25 +69789,26 @@ function createDocumentRuntime(options) {
68223
69789
  }
68224
69790
  }
68225
69791
  function estimateLocalTextPatchShiftBudget(blocks, startIndex) {
69792
+ const policy = getHotEditPolicy();
68226
69793
  const shiftedBlocks = Math.max(0, blocks.length - startIndex);
68227
69794
  let shiftedNodes = 0;
68228
69795
  for (let index = startIndex; index < blocks.length; index += 1) {
68229
69796
  const shiftedBlockNodes = countSurfaceShiftNodesUpTo(
68230
69797
  blocks[index],
68231
69798
  Math.min(
68232
- LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK,
68233
- LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES - shiftedNodes
69799
+ policy.maxShiftedNodesPerBlock,
69800
+ policy.maxShiftedSurfaceNodes - shiftedNodes
68234
69801
  ) + 1
68235
69802
  );
68236
69803
  shiftedNodes += shiftedBlockNodes;
68237
- if (shiftedBlockNodes > LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK) {
69804
+ if (shiftedBlockNodes > policy.maxShiftedNodesPerBlock) {
68238
69805
  return {
68239
69806
  shiftedBlocks,
68240
69807
  shiftedNodes,
68241
69808
  withinBudget: false
68242
69809
  };
68243
69810
  }
68244
- if (shiftedNodes > LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES) {
69811
+ if (shiftedNodes > policy.maxShiftedSurfaceNodes) {
68245
69812
  return {
68246
69813
  shiftedBlocks,
68247
69814
  shiftedNodes,
@@ -69205,7 +70772,9 @@ function createDocumentRuntime(options) {
69205
70772
  });
69206
70773
  const r5ScratchReplayState = { ...state };
69207
70774
  const r5ScratchReplaySnapshot = { ...cachedRenderSnapshot };
69208
- const debugFacet = createRuntimeDebugFacet(() => runtime, telemetryBus);
70775
+ const debugFacet = createRuntimeDebugFacet(() => runtime, telemetryBus, {
70776
+ getHotEditTraces: () => hotEditTraces.getTraces()
70777
+ });
69209
70778
  function prepareTableStructureCommandForExecution(command, document2, storyTarget) {
69210
70779
  const resolution = resolveEditableTableStructureTarget({
69211
70780
  document: document2,
@@ -69302,13 +70871,12 @@ function createDocumentRuntime(options) {
69302
70871
  if (!command.editableTarget) {
69303
70872
  return { kind: "accepted", selection };
69304
70873
  }
69305
- const preserveCallerSelection = command.editableTarget.listAddress?.operationScope === "list-text";
69306
70874
  const resolution = resolveEditableTextTarget({
69307
70875
  document: document2,
69308
70876
  surface,
69309
70877
  target: command.editableTarget,
69310
70878
  activeStoryKey: canonicalEditableTargetStoryKey(storyTarget),
69311
- ...preserveCallerSelection ? { selection } : {},
70879
+ selection,
69312
70880
  editableTargetCache: editableTargetBlockCache
69313
70881
  });
69314
70882
  if (resolution.kind === "rejected") {
@@ -69326,7 +70894,7 @@ function createDocumentRuntime(options) {
69326
70894
  }
69327
70895
  return {
69328
70896
  kind: "accepted",
69329
- selection: preserveCallerSelection ? selection : createSelectionSnapshot(resolution.range.from, resolution.range.to),
70897
+ selection,
69330
70898
  ...resolution.textTarget ? { textTarget: resolution.textTarget } : {}
69331
70899
  };
69332
70900
  }
@@ -70502,12 +72070,23 @@ function createDocumentRuntime(options) {
70502
72070
  return actionDepth > 0;
70503
72071
  },
70504
72072
  applyActiveStoryTextCommand(command) {
72073
+ const pendingHotEditTrace = hotEditTraces.begin({
72074
+ commandType: command.type,
72075
+ nowUtc: clock(),
72076
+ counters: perfCounters
72077
+ });
70505
72078
  try {
70506
- return applyTextCommandInActiveStory(command);
72079
+ const ack = applyTextCommandInActiveStory(command);
72080
+ hotEditTraces.complete(pendingHotEditTrace, {
72081
+ tier: classifyHotEditTier(ack),
72082
+ patchMissReason: classifyHotEditPatchMissReason(ack),
72083
+ counters: perfCounters
72084
+ });
72085
+ return ack;
70507
72086
  } catch (error) {
70508
72087
  const runtimeError = toRuntimeError(error);
70509
72088
  emitError(runtimeError);
70510
- return {
72089
+ const ack = {
70511
72090
  kind: "rejected",
70512
72091
  refreshClass: "blocked",
70513
72092
  opId: command.origin?.opId,
@@ -70519,6 +72098,12 @@ function createDocumentRuntime(options) {
70519
72098
  }
70520
72099
  ]
70521
72100
  };
72101
+ hotEditTraces.complete(pendingHotEditTrace, {
72102
+ tier: "blocked",
72103
+ patchMissReason: "participant-blocked",
72104
+ counters: perfCounters
72105
+ });
72106
+ return ack;
70522
72107
  }
70523
72108
  },
70524
72109
  addComment(params) {
@@ -72294,6 +73879,27 @@ function createDocumentRuntime(options) {
72294
73879
  newRevisionToken: state.revisionToken
72295
73880
  }));
72296
73881
  }
73882
+ function classifyHotEditTier(ack) {
73883
+ if (ack.kind === "rejected" || ack.refreshClass === "blocked") {
73884
+ return "blocked";
73885
+ }
73886
+ if (ack.refreshClass === "full-projection" || ack.kind === "structural-divergence") {
73887
+ return "full-refresh";
73888
+ }
73889
+ if (ack.refreshClass === "local-text-equivalent" || ack.refreshClass === "selection-only" || ack.refreshClass === "surface-only") {
73890
+ return "patch";
73891
+ }
73892
+ return "full-refresh";
73893
+ }
73894
+ function classifyHotEditPatchMissReason(ack) {
73895
+ if (ack.kind === "rejected" || ack.refreshClass === "blocked") {
73896
+ return "participant-blocked";
73897
+ }
73898
+ if (ack.refreshClass === "full-projection" || ack.kind === "structural-divergence") {
73899
+ return "effect-disqualified";
73900
+ }
73901
+ return "none";
73902
+ }
72297
73903
  function classifyAck(params) {
72298
73904
  const { opId, priorState, transaction, newRevisionToken } = params;
72299
73905
  const meta = transaction.mapping.metadata ?? {};
@@ -75285,11 +76891,11 @@ function computeBaseDocFingerprint(envelope) {
75285
76891
  fieldRegistry: envelope.fieldRegistry,
75286
76892
  fontTable: envelope.fontTable
75287
76893
  };
75288
- const serialized = stableStringify3(subset);
76894
+ const serialized = stableStringify5(subset);
75289
76895
  const bytes = new TextEncoder().encode(serialized);
75290
76896
  return sha256Hex3(bytes);
75291
76897
  }
75292
- function stableStringify3(value) {
76898
+ function stableStringify5(value) {
75293
76899
  if (value === null) return "null";
75294
76900
  if (value === void 0) return "null";
75295
76901
  const t = typeof value;
@@ -75299,7 +76905,7 @@ function stableStringify3(value) {
75299
76905
  if (Array.isArray(value)) {
75300
76906
  const parts = [];
75301
76907
  for (const item of value) {
75302
- parts.push(item === void 0 ? "null" : stableStringify3(item));
76908
+ parts.push(item === void 0 ? "null" : stableStringify5(item));
75303
76909
  }
75304
76910
  return "[" + parts.join(",") + "]";
75305
76911
  }
@@ -75314,7 +76920,7 @@ function stableStringify3(value) {
75314
76920
  keys.sort();
75315
76921
  const parts = [];
75316
76922
  for (const key of keys) {
75317
- parts.push(JSON.stringify(key) + ":" + stableStringify3(obj[key]));
76923
+ parts.push(JSON.stringify(key) + ":" + stableStringify5(obj[key]));
75318
76924
  }
75319
76925
  return "{" + parts.join(",") + "}";
75320
76926
  }
@@ -80646,6 +82252,13 @@ function buildParagraphPropertiesXml2(paragraph) {
80646
82252
  const frameXml = buildFrameXml(paragraph.frameProperties);
80647
82253
  if (frameXml) parts.push(frameXml);
80648
82254
  }
82255
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
82256
+ const tabsXml = paragraph.tabStops.map((tab) => {
82257
+ const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
82258
+ return `<w:tab w:val="${escapeXmlAttribute2(tab.align)}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
82259
+ }).join("");
82260
+ parts.push(`<w:tabs>${tabsXml}</w:tabs>`);
82261
+ }
80649
82262
  if (paragraph.spacing) {
80650
82263
  const s = paragraph.spacing;
80651
82264
  const attrs = [];
@@ -80667,13 +82280,6 @@ function buildParagraphPropertiesXml2(paragraph) {
80667
82280
  if (paragraph.alignment) {
80668
82281
  parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
80669
82282
  }
80670
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
80671
- const tabsXml = paragraph.tabStops.map((tab) => {
80672
- const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
80673
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
80674
- }).join("");
80675
- parts.push(`<w:tabs>${tabsXml}</w:tabs>`);
80676
- }
80677
82283
  return parts.length > 0 ? `<w:pPr>${parts.join("")}</w:pPr>` : "";
80678
82284
  }
80679
82285
  function serializeInlineNode2(node) {
@@ -80742,51 +82348,60 @@ function buildRunPropertiesXml2(marks) {
80742
82348
  if (!marks || marks.length === 0) {
80743
82349
  return "";
80744
82350
  }
82351
+ const orderedTypes = [
82352
+ "fontFamily",
82353
+ "bold",
82354
+ "italic",
82355
+ "allCaps",
82356
+ "smallCaps",
82357
+ "strikethrough",
82358
+ "doubleStrikethrough",
82359
+ "textColor",
82360
+ "fontSize",
82361
+ "highlight",
82362
+ "underline",
82363
+ "backgroundColor"
82364
+ ];
80745
82365
  const parts = [];
80746
- for (const mark of marks) {
80747
- switch (mark.type) {
80748
- case "bold":
80749
- parts.push("<w:b/>");
80750
- break;
80751
- case "italic":
80752
- parts.push("<w:i/>");
80753
- break;
80754
- case "underline":
80755
- parts.push('<w:u w:val="single"/>');
80756
- break;
80757
- case "strikethrough":
80758
- parts.push("<w:strike/>");
80759
- break;
80760
- case "doubleStrikethrough":
80761
- parts.push("<w:dstrike/>");
80762
- break;
80763
- case "fontFamily":
80764
- parts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
80765
- break;
80766
- case "fontSize":
80767
- parts.push(`<w:sz w:val="${twip(mark.val)}"/>`);
80768
- break;
80769
- case "textColor":
80770
- parts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
80771
- break;
80772
- case "highlight":
80773
- parts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
80774
- break;
80775
- case "backgroundColor":
80776
- parts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`);
80777
- break;
80778
- case "smallCaps":
80779
- parts.push("<w:smallCaps/>");
80780
- break;
80781
- case "allCaps":
80782
- parts.push("<w:caps/>");
80783
- break;
80784
- default:
80785
- break;
82366
+ for (const type of orderedTypes) {
82367
+ for (const mark of marks) {
82368
+ if (mark.type !== type) continue;
82369
+ const xml = serializeRunPropertyMark2(mark);
82370
+ if (xml) parts.push(xml);
80786
82371
  }
80787
82372
  }
80788
82373
  return parts.length > 0 ? `<w:rPr>${parts.join("")}</w:rPr>` : "";
80789
82374
  }
82375
+ function serializeRunPropertyMark2(mark) {
82376
+ switch (mark.type) {
82377
+ case "bold":
82378
+ return "<w:b/>";
82379
+ case "italic":
82380
+ return "<w:i/>";
82381
+ case "underline":
82382
+ return `<w:u w:val="single"/>`;
82383
+ case "strikethrough":
82384
+ return "<w:strike/>";
82385
+ case "doubleStrikethrough":
82386
+ return "<w:dstrike/>";
82387
+ case "fontFamily":
82388
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
82389
+ case "fontSize":
82390
+ return `<w:sz w:val="${twip(mark.val)}"/>`;
82391
+ case "textColor":
82392
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
82393
+ case "highlight":
82394
+ return `<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`;
82395
+ case "backgroundColor":
82396
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`;
82397
+ case "smallCaps":
82398
+ return "<w:smallCaps/>";
82399
+ case "allCaps":
82400
+ return "<w:caps/>";
82401
+ default:
82402
+ return "";
82403
+ }
82404
+ }
80790
82405
  function requiresPreservedSpace3(text) {
80791
82406
  return text.length > 0 && (text[0] === " " || text[text.length - 1] === " " || text.includes(" "));
80792
82407
  }
@@ -80960,9 +82575,6 @@ function buildParagraphPropertiesXml3(paragraph) {
80960
82575
  const frameXml = buildFrameXml(paragraph.frameProperties);
80961
82576
  if (frameXml) parts.push(frameXml);
80962
82577
  }
80963
- if (paragraph.alignment) {
80964
- parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
80965
- }
80966
82578
  if (paragraph.spacing) {
80967
82579
  const attrs = [];
80968
82580
  if (paragraph.spacing.before !== void 0) attrs.push(`w:before="${twip(paragraph.spacing.before)}"`);
@@ -80983,6 +82595,9 @@ function buildParagraphPropertiesXml3(paragraph) {
80983
82595
  parts.push(`<w:ind ${attrs.join(" ")}/>`);
80984
82596
  }
80985
82597
  }
82598
+ if (paragraph.alignment) {
82599
+ parts.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
82600
+ }
80986
82601
  return parts.length > 0 ? `<w:pPr>${parts.join("")}</w:pPr>` : "";
80987
82602
  }
80988
82603
  function serializeInlineNode3(node) {
@@ -81043,47 +82658,59 @@ function buildRunPropertiesXml3(marks) {
81043
82658
  if (!marks || marks.length === 0) {
81044
82659
  return "";
81045
82660
  }
81046
- const parts = [];
82661
+ const orderedTypes = [
82662
+ "fontFamily",
82663
+ "bold",
82664
+ "italic",
82665
+ "allCaps",
82666
+ "smallCaps",
82667
+ "strikethrough",
82668
+ "doubleStrikethrough",
82669
+ "textColor",
82670
+ "fontSize",
82671
+ "underline"
82672
+ ];
82673
+ const supportedTypes = new Set(orderedTypes);
81047
82674
  for (const mark of marks) {
81048
- switch (mark.type) {
81049
- case "bold":
81050
- parts.push("<w:b/>");
81051
- break;
81052
- case "italic":
81053
- parts.push("<w:i/>");
81054
- break;
81055
- case "underline":
81056
- parts.push('<w:u w:val="single"/>');
81057
- break;
81058
- case "strikethrough":
81059
- parts.push("<w:strike/>");
81060
- break;
81061
- case "doubleStrikethrough":
81062
- parts.push("<w:dstrike/>");
81063
- break;
81064
- case "fontFamily":
81065
- parts.push(
81066
- `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`
81067
- );
81068
- break;
81069
- case "fontSize":
81070
- parts.push(`<w:sz w:val="${twip(mark.val)}"/>`);
81071
- break;
81072
- case "textColor":
81073
- parts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
81074
- break;
81075
- case "smallCaps":
81076
- parts.push("<w:smallCaps/>");
81077
- break;
81078
- case "allCaps":
81079
- parts.push("<w:caps/>");
81080
- break;
81081
- default:
81082
- throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
82675
+ if (!supportedTypes.has(mark.type)) {
82676
+ throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
82677
+ }
82678
+ }
82679
+ const parts = [];
82680
+ for (const type of orderedTypes) {
82681
+ for (const mark of marks) {
82682
+ if (mark.type !== type) continue;
82683
+ parts.push(serializeRunPropertyMark3(mark));
81083
82684
  }
81084
82685
  }
81085
82686
  return parts.length > 0 ? `<w:rPr>${parts.join("")}</w:rPr>` : "";
81086
82687
  }
82688
+ function serializeRunPropertyMark3(mark) {
82689
+ switch (mark.type) {
82690
+ case "bold":
82691
+ return "<w:b/>";
82692
+ case "italic":
82693
+ return "<w:i/>";
82694
+ case "underline":
82695
+ return `<w:u w:val="single"/>`;
82696
+ case "strikethrough":
82697
+ return "<w:strike/>";
82698
+ case "doubleStrikethrough":
82699
+ return "<w:dstrike/>";
82700
+ case "fontFamily":
82701
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
82702
+ case "fontSize":
82703
+ return `<w:sz w:val="${twip(mark.val)}"/>`;
82704
+ case "textColor":
82705
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
82706
+ case "smallCaps":
82707
+ return "<w:smallCaps/>";
82708
+ case "allCaps":
82709
+ return "<w:caps/>";
82710
+ default:
82711
+ throw new Error(`Cannot safely serialize ${mark.type} marks in note sub-parts.`);
82712
+ }
82713
+ }
81087
82714
  function compareNoteIds2(left, right) {
81088
82715
  return Number.parseInt(left.noteId, 10) - Number.parseInt(right.noteId, 10);
81089
82716
  }
@@ -88683,11 +90310,11 @@ function extractFilename2(target) {
88683
90310
  }
88684
90311
  function mintManifestId(partPath, relationshipId, size) {
88685
90312
  const material = `${partPath}${relationshipId}${size}`;
88686
- const a = fnv1a(material, 2166136261);
88687
- const b = fnv1a(material, 461845907);
90313
+ const a = fnv1a3(material, 2166136261);
90314
+ const b = fnv1a3(material, 461845907);
88688
90315
  return `emb:${toHex8(a)}${toHex8(b)}`;
88689
90316
  }
88690
- function fnv1a(input, seed) {
90317
+ function fnv1a3(input, seed) {
88691
90318
  let hash = seed >>> 0;
88692
90319
  for (let i = 0; i < input.length; i++) {
88693
90320
  hash ^= input.charCodeAt(i);
@@ -91398,6 +93025,9 @@ function createLoadingDebugFacet() {
91398
93025
  },
91399
93026
  getChannels() {
91400
93027
  return bus.getChannels();
93028
+ },
93029
+ getHotEditTraces() {
93030
+ return [];
91401
93031
  }
91402
93032
  };
91403
93033
  }
@@ -116085,6 +117715,16 @@ function resolveSelectionEditableTarget(anchorTarget, headTarget) {
116085
117715
  if (!anchorTarget || !headTarget) return void 0;
116086
117716
  return anchorTarget.targetKey === headTarget.targetKey ? anchorTarget : void 0;
116087
117717
  }
117718
+ function resolveCurrentEditableTarget(state, positionMap) {
117719
+ if (!positionMap) return void 0;
117720
+ if (state.selection instanceof import_prosemirror_state2.NodeSelection) {
117721
+ return positionMap.pmToRuntimeWithContext(state.selection.from).editableTarget;
117722
+ }
117723
+ const { anchor, head } = state.selection;
117724
+ const anchorContext = positionMap.pmToRuntimeWithContext(anchor);
117725
+ const headContext = positionMap.pmToRuntimeWithContext(head);
117726
+ return resolveSelectionEditableTarget(anchorContext.editableTarget, headContext.editableTarget);
117727
+ }
116088
117728
  function createCommandBridgePlugins(callbacks, options) {
116089
117729
  let isComposing = false;
116090
117730
  let pendingCompositionText = null;
@@ -116263,11 +117903,13 @@ function createCommandBridgePlugins(callbacks, options) {
116263
117903
  if (isComposing) return true;
116264
117904
  const dragEvent = event;
116265
117905
  resolveDropSelectionFromCoordinates(_view, dragEvent);
116266
- if (!isPmEditTargetEditable(_view.state, callbacks.getPositionMap())) {
117906
+ const positionMap = callbacks.getPositionMap();
117907
+ if (!isPmEditTargetEditable(_view.state, positionMap)) {
116267
117908
  callbacks.onBlockedInput?.("drop", NON_EDITABLE_INPUT_MESSAGE);
116268
117909
  dragSourceRange = null;
116269
117910
  return true;
116270
117911
  }
117912
+ const editableTarget = resolveCurrentEditableTarget(_view.state, positionMap);
116271
117913
  const dt = dragEvent.dataTransfer;
116272
117914
  if (!dt) {
116273
117915
  callbacks.onBlockedInput?.("drop", "Drop data was not available.");
@@ -116284,6 +117926,7 @@ function createCommandBridgePlugins(callbacks, options) {
116284
117926
  callbacks.onDropFragment({
116285
117927
  fragment: parsed.fragment,
116286
117928
  effect,
117929
+ ...editableTarget ? { editableTarget } : {},
116287
117930
  ...sourceRange ? { sourceRange } : {}
116288
117931
  });
116289
117932
  dragSourceRange = null;
@@ -116414,7 +118057,7 @@ var import_prosemirror_view3 = require("prosemirror-view");
116414
118057
 
116415
118058
  // src/ui-tailwind/editor-surface/pm-decorations.ts
116416
118059
  var import_prosemirror_view2 = require("prosemirror-view");
116417
- function nowMs2() {
118060
+ function nowMs3() {
116418
118061
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
116419
118062
  }
116420
118063
  function sanitizeRevisionAuthorColor(raw) {
@@ -116688,7 +118331,7 @@ function buildDecorations(doc, positionMap, commentModel, revisionModel, markupD
116688
118331
  const activeScopeIds = new Set(activeWorkflowScopeIds ?? []);
116689
118332
  const effectiveWorkflowScopes = workflowScopes ?? [];
116690
118333
  const lockedPmRanges = collectLockedPmRanges(workflowLockedZones, activeStory, positionMap);
116691
- const commentsStartMs = nowMs2();
118334
+ const commentsStartMs = nowMs3();
116692
118335
  let commentCount = 0;
116693
118336
  if (commentModel) {
116694
118337
  for (const thread of commentModel.threads) {
@@ -116712,11 +118355,11 @@ function buildDecorations(doc, positionMap, commentModel, revisionModel, markupD
116712
118355
  }
116713
118356
  }
116714
118357
  }
116715
- recordPerfSample("pm.decorations.comments", nowMs2() - commentsStartMs);
118358
+ recordPerfSample("pm.decorations.comments", nowMs3() - commentsStartMs);
116716
118359
  if (commentCount > 0) {
116717
118360
  incrementInvalidationCounter("pm.decorations.comments.count", commentCount);
116718
118361
  }
116719
- const revisionsStartMs = nowMs2();
118362
+ const revisionsStartMs = nowMs3();
116720
118363
  let revisionCount = 0;
116721
118364
  if (revisionModel) {
116722
118365
  for (const rev of revisionModel.revisions) {
@@ -116825,11 +118468,11 @@ function buildDecorations(doc, positionMap, commentModel, revisionModel, markupD
116825
118468
  revisionCount += 1;
116826
118469
  }
116827
118470
  }
116828
- recordPerfSample("pm.decorations.revisions", nowMs2() - revisionsStartMs);
118471
+ recordPerfSample("pm.decorations.revisions", nowMs3() - revisionsStartMs);
116829
118472
  if (revisionCount > 0) {
116830
118473
  incrementInvalidationCounter("pm.decorations.revisions.count", revisionCount);
116831
118474
  }
116832
- const workflowStartMs = nowMs2();
118475
+ const workflowStartMs = nowMs3();
116833
118476
  const workflowDecorationsBefore = decorations.length;
116834
118477
  if (effectiveWorkflowScopes.length > 0) {
116835
118478
  for (const scope of effectiveWorkflowScopes) {
@@ -116947,7 +118590,7 @@ function buildDecorations(doc, positionMap, commentModel, revisionModel, markupD
116947
118590
  }, railRangeCache);
116948
118591
  }
116949
118592
  }
116950
- recordPerfSample("pm.decorations.workflow", nowMs2() - workflowStartMs);
118593
+ recordPerfSample("pm.decorations.workflow", nowMs3() - workflowStartMs);
116951
118594
  const workflowDecorationCount = decorations.length - workflowDecorationsBefore;
116952
118595
  if (workflowDecorationCount > 0) {
116953
118596
  incrementInvalidationCounter(
@@ -117024,11 +118667,11 @@ function applyRuntimeDecorationInputs(view, inputs) {
117024
118667
  const tr = view.state.tr.setMeta(runtimeDecorationPluginKey, {
117025
118668
  inputs
117026
118669
  });
117027
- const startedAt = nowMs3();
118670
+ const startedAt = nowMs4();
117028
118671
  view.dispatch(tr);
117029
- recordPerfSample("pm.decorations.apply", nowMs3() - startedAt);
118672
+ recordPerfSample("pm.decorations.apply", nowMs4() - startedAt);
117030
118673
  }
117031
- function nowMs3() {
118674
+ function nowMs4() {
117032
118675
  return typeof performance !== "undefined" ? performance.now() : Date.now();
117033
118676
  }
117034
118677
 
@@ -122818,6 +124461,7 @@ var LAYER_DEBUG_PANES = [
122818
124461
  snippets: [
122819
124462
  { label: "Selection context", expression: "ref?.getRuntimeContextAnalytics?.()" },
122820
124463
  { label: "Document context", expression: "ref?.getRuntimeContextAnalytics?.({ scopeKind: 'document' })" },
124464
+ { label: "Hot edit trace", expression: "runtime?.debug?.getHotEditTraces?.().at(-1)" },
122821
124465
  { label: "View state", expression: "ref?.getViewState?.()" }
122822
124466
  ],
122823
124467
  routed: [
@@ -122874,6 +124518,7 @@ var LAYER_DEBUG_PANES = [
122874
124518
  snippets: [
122875
124519
  { label: "Navigation", expression: "ref?.getDocumentNavigationSnapshot?.()" },
122876
124520
  { label: "Review surface", expression: "({ comments: ref?.getCommentSidebarSnapshot?.(), changes: ref?.getTrackedChangesSnapshot?.() })" },
124521
+ { label: "Hot edit traces", expression: "runtime?.debug?.getHotEditTraces?.()" },
122877
124522
  { label: "Compatibility", expression: "ref?.getCompatibilityReport?.()" }
122878
124523
  ],
122879
124524
  routed: [
@@ -124299,6 +125944,10 @@ function extractNLHaystack(entry) {
124299
125944
  }
124300
125945
  return out.slice(0, 200).toLowerCase();
124301
125946
  }
125947
+ case "image": {
125948
+ const text = entry.image.type === "image" ? entry.image.altText ?? "" : entry.image.anchor.docPr?.descr ?? entry.image.anchor.docPr?.name ?? "";
125949
+ return text.slice(0, 200).toLowerCase();
125950
+ }
124302
125951
  case "table":
124303
125952
  case "table-row":
124304
125953
  case "table-cell":
@@ -126134,6 +127783,391 @@ function createViewportFamily2(runtime) {
126134
127783
  };
126135
127784
  }
126136
127785
 
127786
+ // src/api/v3/runtime/lists.ts
127787
+ var SUPPORTED_COMMANDS = [
127788
+ "toggle-numbered",
127789
+ "toggle-bulleted",
127790
+ "indent",
127791
+ "outdent",
127792
+ "restart-numbering",
127793
+ "continue-numbering"
127794
+ ];
127795
+ var UNSUPPORTED_COMMANDS = [
127796
+ "create",
127797
+ "attach",
127798
+ "detach",
127799
+ "join",
127800
+ "separate",
127801
+ "split",
127802
+ "set-value",
127803
+ "apply-template",
127804
+ "capture-template",
127805
+ "apply-preset",
127806
+ "set-level-numbering",
127807
+ "set-level-bullet",
127808
+ "set-level-picture-bullet",
127809
+ "set-level-alignment",
127810
+ "set-level-indents",
127811
+ "set-level-trailing-character",
127812
+ "set-level-marker-font",
127813
+ "set-level-text",
127814
+ "set-level-start",
127815
+ "set-level-layout",
127816
+ "convert-to-text",
127817
+ "paste-fragment",
127818
+ "drop-fragment"
127819
+ ];
127820
+ var applyCommandMetadata = {
127821
+ name: "runtime.lists.applyCommand",
127822
+ status: "live-with-adapter",
127823
+ sourceLayer: "runtime-core",
127824
+ liveEvidence: {
127825
+ runnerTest: "test/api/v3/runtime/lists.test.ts",
127826
+ commit: "refactor-07-runtime-lists-namespace"
127827
+ },
127828
+ uxIntent: {
127829
+ uiVisible: true,
127830
+ expectsUxResponse: "surface-refresh",
127831
+ expectedDelta: "list structure changes through the L07 list command surface"
127832
+ },
127833
+ agentMetadata: { readOrMutate: "mutate", boundedScope: "scope", auditCategory: "list-command" },
127834
+ stateClass: "A-canonical",
127835
+ persistsTo: "canonical",
127836
+ broadcastsVia: "crdt",
127837
+ rwdReference: "\xA7Runtime API \xA7 runtime.lists.applyCommand. Dispatches only proven L07 list commands through opaque list targets; future commands return owner-routed blockers until command/export/readback support lands."
127838
+ };
127839
+ function createListsFamily(runtime) {
127840
+ return {
127841
+ list(input = {}) {
127842
+ const document2 = runtime.getCanonicalDocument();
127843
+ const rows = currentListTargets(document2).filter((entry) => input.storyKey === void 0 || entry.target.storyKey === input.storyKey).map((entry) => toReadback(document2, entry.target, entry.paragraph));
127844
+ return input.limit === void 0 ? rows : rows.slice(0, Math.max(0, input.limit));
127845
+ },
127846
+ get(input) {
127847
+ const resolved = resolveCurrentListTarget(runtime.getCanonicalDocument(), input);
127848
+ return resolved.kind === "resolved" ? toReadback(runtime.getCanonicalDocument(), resolved.target, resolved.paragraph) : null;
127849
+ },
127850
+ previewCommand(input) {
127851
+ return previewListCommand(runtime.getCanonicalDocument(), input);
127852
+ },
127853
+ applyCommand(input) {
127854
+ const preview = previewListCommand(runtime.getCanonicalDocument(), input);
127855
+ if (!preview.supported || !preview.target) {
127856
+ return {
127857
+ applied: false,
127858
+ command: input.command,
127859
+ before: preview.target,
127860
+ blockers: preview.blockers
127861
+ };
127862
+ }
127863
+ const resolved = resolveCurrentListTarget(runtime.getCanonicalDocument(), {
127864
+ addressKey: preview.target.addressKey
127865
+ });
127866
+ if (resolved.kind !== "resolved") {
127867
+ return {
127868
+ applied: false,
127869
+ command: input.command,
127870
+ before: preview.target,
127871
+ blockers: [resolved.blocker]
127872
+ };
127873
+ }
127874
+ const command = editorCommandForListCommand(
127875
+ input.command,
127876
+ resolved.target,
127877
+ runtime.now(),
127878
+ input.startAt
127879
+ );
127880
+ if (!command) {
127881
+ return {
127882
+ applied: false,
127883
+ command: input.command,
127884
+ before: preview.target,
127885
+ blockers: [
127886
+ unsupportedCommandBlocker(input.command, {
127887
+ targetKey: preview.target.targetKey,
127888
+ addressKey: preview.target.addressKey
127889
+ })
127890
+ ]
127891
+ };
127892
+ }
127893
+ const beforeDocument = runtime.getCanonicalDocument();
127894
+ runtime.dispatch(command);
127895
+ const afterDocument = runtime.getCanonicalDocument();
127896
+ const after = resolveCurrentListTarget(afterDocument, { targetKey: preview.target.targetKey });
127897
+ emitUxResponse(runtime, {
127898
+ apiFn: applyCommandMetadata.name,
127899
+ intent: applyCommandMetadata.uxIntent.expectedDelta ?? "",
127900
+ mockOrLive: "live",
127901
+ uiVisible: true,
127902
+ expectedDelta: applyCommandMetadata.uxIntent.expectedDelta
127903
+ });
127904
+ return {
127905
+ applied: beforeDocument !== afterDocument,
127906
+ command: input.command,
127907
+ before: preview.target,
127908
+ ...after.kind === "resolved" ? { after: toReadback(afterDocument, after.target, after.paragraph) } : {},
127909
+ blockers: []
127910
+ };
127911
+ }
127912
+ };
127913
+ }
127914
+ function previewListCommand(document2, input) {
127915
+ const resolved = resolveCurrentListTarget(document2, input);
127916
+ if (resolved.kind !== "resolved") {
127917
+ return {
127918
+ command: input.command,
127919
+ supported: false,
127920
+ affectedTargets: [],
127921
+ blockers: [resolved.blocker]
127922
+ };
127923
+ }
127924
+ const target = toReadback(document2, resolved.target, resolved.paragraph);
127925
+ const targetRef = { targetKey: target.targetKey, addressKey: target.addressKey };
127926
+ if (!SUPPORTED_COMMANDS.includes(input.command)) {
127927
+ return {
127928
+ command: input.command,
127929
+ supported: false,
127930
+ target,
127931
+ affectedTargets: [target],
127932
+ blockers: [unsupportedCommandBlocker(input.command, targetRef)]
127933
+ };
127934
+ }
127935
+ const canContinuePrevious = canContinuePreviousSequence(document2, resolved.paragraphIndex);
127936
+ const canJoin = canJoinPreviousSequence(document2, resolved.paragraphIndex);
127937
+ const blockers = [];
127938
+ if (input.command === "continue-numbering" && !canContinuePrevious) {
127939
+ blockers.push({
127940
+ code: "list-continue-target-missing",
127941
+ ownerLayer: "L07",
127942
+ message: "No previous compatible list sequence is available for continue-numbering.",
127943
+ ...targetRef
127944
+ });
127945
+ }
127946
+ return {
127947
+ command: input.command,
127948
+ supported: blockers.length === 0,
127949
+ target,
127950
+ affectedTargets: [target],
127951
+ blockers,
127952
+ canJoin,
127953
+ canContinuePrevious
127954
+ };
127955
+ }
127956
+ function editorCommandForListCommand(command, editableTarget, timestamp, startAt) {
127957
+ const origin = { source: "api", timestamp };
127958
+ switch (command) {
127959
+ case "toggle-numbered":
127960
+ return { type: "list.toggle", kind: "numbered", editableTargets: [editableTarget], origin };
127961
+ case "toggle-bulleted":
127962
+ return { type: "list.toggle", kind: "bulleted", editableTargets: [editableTarget], origin };
127963
+ case "indent":
127964
+ return { type: "list.indent", editableTargets: [editableTarget], origin };
127965
+ case "outdent":
127966
+ return { type: "list.outdent", editableTargets: [editableTarget], origin };
127967
+ case "restart-numbering":
127968
+ return {
127969
+ type: "list.restart-numbering",
127970
+ editableTarget,
127971
+ ...startAt !== void 0 ? { startAt } : {},
127972
+ origin
127973
+ };
127974
+ case "continue-numbering":
127975
+ return { type: "list.continue-numbering", editableTarget, origin };
127976
+ default:
127977
+ return void 0;
127978
+ }
127979
+ }
127980
+ function resolveCurrentListTarget(document2, input) {
127981
+ if (input.editableTarget) {
127982
+ const shapeIssues = validateEditableTargetRef(input.editableTarget);
127983
+ if (shapeIssues.length > 0) {
127984
+ return {
127985
+ kind: "blocked",
127986
+ blocker: {
127987
+ code: "list-target-malformed",
127988
+ ownerLayer: "L07",
127989
+ message: `List target is malformed: ${shapeIssues[0]?.path ?? "$"}.`,
127990
+ targetKey: input.editableTarget.targetKey,
127991
+ addressKey: input.editableTarget.listAddress?.addressKey
127992
+ }
127993
+ };
127994
+ }
127995
+ }
127996
+ const requestedTargetKey = input.editableTarget?.targetKey ?? input.targetKey;
127997
+ const requestedAddressKey = input.editableTarget?.listAddress?.addressKey ?? input.addressKey;
127998
+ if (!requestedTargetKey && !requestedAddressKey) {
127999
+ return {
128000
+ kind: "blocked",
128001
+ blocker: {
128002
+ code: "list-target-required",
128003
+ ownerLayer: "L07",
128004
+ message: "runtime.lists requires a targetKey, addressKey, or editableTarget."
128005
+ }
128006
+ };
128007
+ }
128008
+ const currentTargets = currentListTargets(document2);
128009
+ const resolved = currentTargets.find(
128010
+ ({ target }) => requestedTargetKey !== void 0 && target.targetKey === requestedTargetKey || requestedAddressKey !== void 0 && target.listAddress?.addressKey === requestedAddressKey
128011
+ );
128012
+ if (!resolved) {
128013
+ return {
128014
+ kind: "blocked",
128015
+ blocker: {
128016
+ code: "list-target-not-found",
128017
+ ownerLayer: "L07",
128018
+ message: "List target no longer resolves in the current canonical document.",
128019
+ ...requestedTargetKey !== void 0 ? { targetKey: requestedTargetKey } : {},
128020
+ ...requestedAddressKey !== void 0 ? { addressKey: requestedAddressKey } : {}
128021
+ }
128022
+ };
128023
+ }
128024
+ if (input.editableTarget && !sameTargetStaleHash(input.editableTarget, resolved.target)) {
128025
+ return {
128026
+ kind: "blocked",
128027
+ blocker: {
128028
+ code: "list-target-stale",
128029
+ ownerLayer: "L07",
128030
+ message: "List target resolved by identity but stale discriminators changed.",
128031
+ targetKey: input.editableTarget.targetKey,
128032
+ addressKey: input.editableTarget.listAddress?.addressKey
128033
+ }
128034
+ };
128035
+ }
128036
+ if (resolved.target.editability !== "editable" || resolved.target.posture.blockers.length > 0) {
128037
+ return {
128038
+ kind: "blocked",
128039
+ blocker: {
128040
+ code: "list-target-non-editable",
128041
+ ownerLayer: "L07",
128042
+ message: resolved.target.posture.blockers.length > 0 ? `List target is not editable: ${resolved.target.posture.blockers.join(", ")}.` : "List target is not editable.",
128043
+ targetKey: resolved.target.targetKey,
128044
+ addressKey: resolved.target.listAddress?.addressKey
128045
+ }
128046
+ };
128047
+ }
128048
+ return { kind: "resolved", ...resolved };
128049
+ }
128050
+ function currentListTargets(document2) {
128051
+ const paragraphs = collectParagraphEntries(document2.content.children, "main");
128052
+ const targets = collectEditableTargetRefs(document2).filter(isListTextTarget);
128053
+ const byBlockPath = /* @__PURE__ */ new Map();
128054
+ for (const target of targets) byBlockPath.set(target.blockPath, target);
128055
+ const out = [];
128056
+ for (let paragraphIndex = 0; paragraphIndex < paragraphs.length; paragraphIndex += 1) {
128057
+ const entry = paragraphs[paragraphIndex];
128058
+ if (!entry?.paragraph.numbering) continue;
128059
+ const target = byBlockPath.get(entry.blockPath);
128060
+ if (!target) continue;
128061
+ out.push({ target, paragraph: entry.paragraph, paragraphIndex });
128062
+ }
128063
+ return out;
128064
+ }
128065
+ function collectParagraphEntries(blocks, basePath) {
128066
+ const out = [];
128067
+ collectParagraphEntriesInto(blocks, basePath, out);
128068
+ return out;
128069
+ }
128070
+ function collectParagraphEntriesInto(blocks, basePath, out) {
128071
+ for (let blockIndex = 0; blockIndex < blocks.length; blockIndex += 1) {
128072
+ const block = blocks[blockIndex];
128073
+ if (!block) continue;
128074
+ const blockPath = `${basePath}/block[${blockIndex}]`;
128075
+ switch (block.type) {
128076
+ case "paragraph":
128077
+ out.push({ paragraph: block, blockPath });
128078
+ break;
128079
+ case "table":
128080
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
128081
+ const row2 = block.rows[rowIndex];
128082
+ if (!row2) continue;
128083
+ for (let cellIndex = 0; cellIndex < row2.cells.length; cellIndex += 1) {
128084
+ const cell = row2.cells[cellIndex];
128085
+ if (!cell) continue;
128086
+ collectParagraphEntriesInto(
128087
+ cell.children,
128088
+ `${blockPath}/row[${rowIndex}]/cell[${cellIndex}]`,
128089
+ out
128090
+ );
128091
+ }
128092
+ }
128093
+ break;
128094
+ case "sdt":
128095
+ collectParagraphEntriesInto(block.children, blockPath, out);
128096
+ break;
128097
+ case "custom_xml":
128098
+ break;
128099
+ default:
128100
+ break;
128101
+ }
128102
+ }
128103
+ }
128104
+ function isListTextTarget(target) {
128105
+ return target.commandFamily === "text-leaf" && target.listAddress?.operationScope === "list-text" && target.listAddress.addressKind === "list-item-text";
128106
+ }
128107
+ function sameTargetStaleHash(left, right) {
128108
+ return left.targetKey === right.targetKey && left.listAddress?.addressKey === right.listAddress?.addressKey && left.listAddress?.resolver?.staleHash === right.listAddress?.resolver?.staleHash && left.staleCheck.paragraphTextHash === right.staleCheck.paragraphTextHash && left.staleCheck.paragraphTextLength === right.staleCheck.paragraphTextLength && left.staleCheck.inlineCount === right.staleCheck.inlineCount;
128109
+ }
128110
+ function toReadback(document2, target, paragraph) {
128111
+ const numbering = paragraph.numbering;
128112
+ const instance = document2.numbering.instances[numbering.numberingInstanceId];
128113
+ const listKind = instance ? getListKind(document2.numbering, numbering.numberingInstanceId) : void 0;
128114
+ return {
128115
+ targetKey: target.targetKey,
128116
+ actionHandle: `list-action:${target.listAddress.addressKey}`,
128117
+ kind: target.kind,
128118
+ storyKey: target.storyKey,
128119
+ blockPath: target.blockPath,
128120
+ leafPath: target.leafPath,
128121
+ addressKey: target.listAddress.addressKey,
128122
+ numberingInstanceId: numbering.numberingInstanceId,
128123
+ ...instance?.abstractNumberingId ? { abstractNumberingId: instance.abstractNumberingId } : {},
128124
+ level: numbering.level,
128125
+ ...listKind ? { listKind } : {},
128126
+ editability: target.editability,
128127
+ blockers: target.posture.blockers,
128128
+ supportedCommands: SUPPORTED_COMMANDS,
128129
+ unsupportedCommands: UNSUPPORTED_COMMANDS,
128130
+ staleDiscriminators: {
128131
+ paragraphTextHash: target.staleCheck.paragraphTextHash,
128132
+ paragraphTextLength: target.staleCheck.paragraphTextLength,
128133
+ inlineCount: target.staleCheck.inlineCount,
128134
+ listAddressStaleHash: target.listAddress?.resolver?.staleHash
128135
+ }
128136
+ };
128137
+ }
128138
+ function canContinuePreviousSequence(document2, paragraphIndex) {
128139
+ const paragraphs = collectParagraphEntries(document2.content.children, "main");
128140
+ const current = paragraphs[paragraphIndex]?.paragraph;
128141
+ if (!current?.numbering) return false;
128142
+ const currentKind = getListKind(document2.numbering, current.numbering.numberingInstanceId);
128143
+ if (!currentKind) return false;
128144
+ for (let index = paragraphIndex - 1; index >= 0; index -= 1) {
128145
+ const previous = paragraphs[index]?.paragraph;
128146
+ if (!previous?.numbering) continue;
128147
+ const previousKind = getListKind(document2.numbering, previous.numbering.numberingInstanceId);
128148
+ return previousKind === currentKind && previous.numbering.numberingInstanceId !== current.numbering.numberingInstanceId;
128149
+ }
128150
+ return false;
128151
+ }
128152
+ function canJoinPreviousSequence(document2, paragraphIndex) {
128153
+ const paragraphs = collectParagraphEntries(document2.content.children, "main");
128154
+ const current = paragraphs[paragraphIndex]?.paragraph;
128155
+ const previous = paragraphs[paragraphIndex - 1]?.paragraph;
128156
+ if (!current?.numbering || !previous?.numbering) return false;
128157
+ const currentKind = getListKind(document2.numbering, current.numbering.numberingInstanceId);
128158
+ const previousKind = getListKind(document2.numbering, previous.numbering.numberingInstanceId);
128159
+ return Boolean(currentKind) && currentKind === previousKind && current.numbering.numberingInstanceId !== previous.numbering.numberingInstanceId;
128160
+ }
128161
+ function unsupportedCommandBlocker(command, target) {
128162
+ return {
128163
+ code: "list-command-unsupported",
128164
+ ownerLayer: "L07",
128165
+ message: `runtime.lists.${command} is reserved but not implemented by the L07 command surface yet.`,
128166
+ ...target.targetKey !== void 0 ? { targetKey: target.targetKey } : {},
128167
+ ...target.addressKey !== void 0 ? { addressKey: target.addressKey } : {}
128168
+ };
128169
+ }
128170
+
126137
128171
  // src/api/v3/ai/_pe2-evidence.ts
126138
128172
  function copyCoverage(coverage) {
126139
128173
  return {
@@ -127861,6 +129895,17 @@ function createTableActionFamily(runtime) {
127861
129895
  operationScope: target.table?.operationScope
127862
129896
  });
127863
129897
  }
129898
+ if (fragmentContent && fragmentContent.blocks.length === 0) {
129899
+ return blockedResult(input, proposalId, {
129900
+ code: `table-action-structured-fragment-empty:${input.operation.kind}`,
129901
+ category: "unsupported-operation",
129902
+ message: "Structured table text actions require a canonical document fragment with at least one block.",
129903
+ nextStep: 'Retry with operation.content.kind="structured" and a CanonicalDocumentFragment whose blocks array contains the paragraph or table content to paste/drop.',
129904
+ actionHandle: input.actionHandle,
129905
+ operation: input.operation.kind,
129906
+ operationScope: target.table?.operationScope
129907
+ });
129908
+ }
127864
129909
  const resolution2 = resolveEditableTextTarget({
127865
129910
  document: runtime.getCanonicalDocument(),
127866
129911
  surface: runtime.getRenderSnapshot().surface?.blocks ?? [],
@@ -127883,6 +129928,7 @@ function createTableActionFamily(runtime) {
127883
129928
  runtime.dispatch({
127884
129929
  type: "fragment.insert",
127885
129930
  fragment: fragmentContent,
129931
+ selection: createSelectionSnapshot(resolution2.range.to, resolution2.range.to),
127886
129932
  editableTarget: target,
127887
129933
  origin: { source: "api", timestamp: nowUtc }
127888
129934
  });
@@ -127895,6 +129941,17 @@ function createTableActionFamily(runtime) {
127895
129941
  });
127896
129942
  }
127897
129943
  const changed2 = runtime.getCanonicalDocument() !== before2;
129944
+ if (!changed2) {
129945
+ return blockedResult(input, proposalId, {
129946
+ code: `table-action-noop:${input.operation.kind}:${input.actionHandle}`,
129947
+ category: "runtime-noop",
129948
+ message: "The runtime accepted the table text target but the operation produced no document change.",
129949
+ nextStep: "Refresh the table action list and verify the target is still editable, the payload is non-empty when required, and structured fragments are dispatched through a command-safe table text action.",
129950
+ actionHandle: input.actionHandle,
129951
+ operation: input.operation.kind,
129952
+ operationScope: target.table?.operationScope
129953
+ });
129954
+ }
127898
129955
  const afterReadback = tableTextReadback(readEditableTargetText(runtime.getCanonicalDocument(), target));
127899
129956
  return {
127900
129957
  proposalId,
@@ -128502,7 +130559,7 @@ var listOperationMetadata = actionMethodMetadata(
128502
130559
  "listOperation",
128503
130560
  "mutate",
128504
130561
  "actions-list-operation",
128505
- "Apply list toggle, indent, outdent, restart, or continue-numbering commands to paragraph-like scope handles.",
130562
+ "Apply list toggle, indent, outdent, restart, or continue-numbering commands through opaque list action handles or command-safe list scope targets.",
128506
130563
  {
128507
130564
  uiVisible: true,
128508
130565
  expectsUxResponse: "inline-change",
@@ -130237,25 +132294,16 @@ function checkPlanStepCapability(runtime, step, before) {
130237
132294
  return null;
130238
132295
  }
130239
132296
  if (step.kind === "listOperation") {
130240
- const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
132297
+ const resolved = resolveListOperationTarget(runtime, step.target);
130241
132298
  if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
130242
- const scope = resolved.target.scope;
130243
- if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
130244
- return blocker(
130245
- `actions:list-operation:target-kind-unsupported:${scope.kind}`,
130246
- "unsupported",
130247
- "List operations require a paragraph-like scope handle.",
130248
- "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
130249
- );
130250
- }
130251
- if (paragraphIndexFromHandle(scope.handle) === null) {
130252
- return blockerWithOwner(
130253
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
130254
- "blocked",
130255
- "The list operation target did not resolve to a current paragraph index.",
130256
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
130257
- "L08 semantic scope compiler"
130258
- );
132299
+ const command = runtimeListCommandForOperation(step.operation);
132300
+ const preview = createListsFamily(runtime).previewCommand({
132301
+ ...resolved.input,
132302
+ command,
132303
+ ...step.operation.kind === "restart-numbering" && step.operation.startAt !== void 0 ? { startAt: step.operation.startAt } : {}
132304
+ });
132305
+ if (preview.blockers.length > 0) {
132306
+ return listBlockerDetails("actions:list-operation:preflight", preview.blockers)[0] ?? null;
130259
132307
  }
130260
132308
  return null;
130261
132309
  }
@@ -130412,70 +132460,253 @@ function applyModeledCommand(runtime, targetInput, kind, commandInput, reference
130412
132460
  };
130413
132461
  }
130414
132462
  function applyListOperation(runtime, input) {
130415
- const resolvedScope = resolveScopeExactTarget(runtime, input.target, "listOperation");
130416
- if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
132463
+ const resolvedTarget = resolveListOperationTarget(runtime, input.target);
132464
+ if (!resolvedTarget.ok) return blockedApplyFromResolution(resolvedTarget);
132465
+ const command = runtimeListCommandForOperation(input.operation);
132466
+ const result = createListsFamily(runtime).applyCommand({
132467
+ ...resolvedTarget.input,
132468
+ command,
132469
+ ...input.operation.kind === "restart-numbering" && input.operation.startAt !== void 0 ? { startAt: input.operation.startAt } : {}
132470
+ });
132471
+ if (!result.applied) {
132472
+ const details = result.blockers.length > 0 ? listBlockerDetails("actions:list-operation", result.blockers) : [
132473
+ blockerWithOwner(
132474
+ `actions:list-operation:runtime-noop:${resolvedTarget.before?.addressKey ?? "unknown"}`,
132475
+ "blocked",
132476
+ "The runtime list command produced no document change for the selected list target.",
132477
+ "Refresh the list action handle and retry; route persistent failures with before/after readback to L07 runtime list commands.",
132478
+ "L07 runtime list commands"
132479
+ )
132480
+ ];
132481
+ return {
132482
+ status: details.some((detail) => detail.category === "unsupported") ? "unsupported" : "blocked",
132483
+ applied: false,
132484
+ changed: false,
132485
+ target: summarizeListTarget(resolvedTarget, result.before ?? resolvedTarget.before),
132486
+ blockers: Object.freeze(details.map((detail) => detail.code)),
132487
+ blockerDetails: Object.freeze(details),
132488
+ listReadback: listOperationReadback(result, resolvedTarget.before)
132489
+ };
132490
+ }
132491
+ const compiledAfter = resolvedTarget.scopeHandle ? createScopeCompilerService(runtime).compileScopeById(resolvedTarget.scopeHandle.scopeId) : null;
132492
+ const target = compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeListTarget(resolvedTarget, result.after ?? result.before ?? resolvedTarget.before);
132493
+ return {
132494
+ status: "applied",
132495
+ applied: true,
132496
+ changed: result.applied,
132497
+ target,
132498
+ commandReference: {
132499
+ command: commandReferenceForListCommand(result.command),
132500
+ actorId: input.actorId ?? "v3-ai-api",
132501
+ origin: input.origin ?? "agent",
132502
+ emittedAtUtc: currentAuditTimestamp(runtime)
132503
+ },
132504
+ listReadback: listOperationReadback(result, resolvedTarget.before)
132505
+ };
132506
+ }
132507
+ function resolveListOperationTarget(runtime, targetInput) {
132508
+ const lists = createListsFamily(runtime);
132509
+ if ("actionHandle" in targetInput) {
132510
+ const addressKey = listAddressKeyFromActionHandle(targetInput.actionHandle);
132511
+ if (!addressKey) {
132512
+ const detail = blockerWithOwner(
132513
+ `actions:list-operation:action-handle-malformed:${targetInput.actionHandle}`,
132514
+ "unsupported",
132515
+ "List operations require an opaque list action handle from runtime.lists or list editable-target evidence.",
132516
+ "Call runtime.lists.list or refresh the scope bundle and retry with a list-action:* or scope-command:text-leaf:* list handle.",
132517
+ "L07 runtime list commands and L08 semantic scopes"
132518
+ );
132519
+ return {
132520
+ ok: false,
132521
+ blockers: Object.freeze([detail.code]),
132522
+ blockerDetails: Object.freeze([detail])
132523
+ };
132524
+ }
132525
+ const before2 = lists.get({ addressKey });
132526
+ if (!before2) {
132527
+ const detail = blockerWithOwner(
132528
+ `actions:list-operation:action-handle-not-found:${targetInput.actionHandle}`,
132529
+ "unresolved-target",
132530
+ "No current list target matches the supplied opaque action handle.",
132531
+ "Refresh runtime.lists.list or the scope bundle, then retry with the current list action handle.",
132532
+ "L07 runtime list commands"
132533
+ );
132534
+ return {
132535
+ ok: false,
132536
+ blockers: Object.freeze([detail.code]),
132537
+ blockerDetails: Object.freeze([detail])
132538
+ };
132539
+ }
132540
+ return { ok: true, input: { addressKey }, before: before2 };
132541
+ }
132542
+ const resolvedScope = resolveScopeExactTarget(runtime, targetInput, "listOperation");
132543
+ if (!resolvedScope.ok) return resolvedScope;
130417
132544
  const scope = resolvedScope.target.scope;
130418
132545
  if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
130419
- return blockedApply(
132546
+ const detail = blocker(
130420
132547
  `actions:list-operation:target-kind-unsupported:${scope.kind}`,
130421
132548
  "unsupported",
130422
- "List operations require a paragraph-like scope handle.",
130423
- "Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
132549
+ "List operations require a list item or command-safe paragraph-like scope target.",
132550
+ "Use ai.actions.locateAll, ai.listScopes, or runtime.lists.list to select a current list item target."
130424
132551
  );
132552
+ return {
132553
+ ok: false,
132554
+ blockers: Object.freeze([detail.code]),
132555
+ blockerDetails: Object.freeze([detail])
132556
+ };
130425
132557
  }
130426
- const paragraphIndex = paragraphIndexFromHandle(scope.handle);
130427
- if (paragraphIndex === null) {
130428
- return blockedApply(
130429
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
132558
+ const bundle = createScopeCompilerService(runtime).compileBundleById(
132559
+ scope.handle.scopeId,
132560
+ currentAuditTimestamp(runtime)
132561
+ );
132562
+ const entries = bundle?.evidence.editableTargets?.entries ?? [];
132563
+ const listEntry = entries.find(
132564
+ (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"
132565
+ );
132566
+ if (!listEntry) {
132567
+ const detail = blockerWithOwner(
132568
+ `actions:list-operation:list-target-missing:${scope.handle.scopeId}`,
130430
132569
  "blocked",
130431
- "The list operation target did not resolve to a current paragraph index.",
130432
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
130433
- [
130434
- blockerWithOwner(
130435
- `actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
130436
- "blocked",
130437
- "The list operation target did not resolve to a current paragraph index.",
130438
- "Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
130439
- "L08 semantic scope compiler"
130440
- )
130441
- ]
132570
+ "The selected scope does not expose a command-safe list structure target.",
132571
+ "Refresh the scope bundle and route missing list editable-target evidence to L08/L07; do not derive a paragraph index from the scope handle.",
132572
+ "L08 semantic scopes and L07 runtime list commands"
130442
132573
  );
132574
+ return {
132575
+ ok: false,
132576
+ blockers: Object.freeze([detail.code]),
132577
+ blockerDetails: Object.freeze([detail])
132578
+ };
130443
132579
  }
130444
- const command = listCommandForOperation(input.operation, paragraphIndex, actionOrigin(runtime, input));
130445
- const before = runtime.getCanonicalDocument();
130446
- runtime.dispatch(command);
130447
- const changed = runtime.getCanonicalDocument() !== before;
130448
- if (!changed) {
130449
- return blockedApply(
130450
- `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
132580
+ const currentTarget = collectEditableTargetRefs(runtime.getCanonicalDocument()).find(
132581
+ (target) => target.targetKey === listEntry.targetKey || target.listAddress?.addressKey === listEntry.runtimeCommand.canonicalAddress?.addressKey
132582
+ );
132583
+ if (!currentTarget) {
132584
+ const detail = blockerWithOwner(
132585
+ `actions:list-operation:list-target-not-current:${scope.handle.scopeId}`,
132586
+ "unresolved-target",
132587
+ "The list editable target evidence is stale in the current document.",
132588
+ "Refresh the scope bundle and retry with the current opaque list target.",
132589
+ "L08 semantic scopes and L07 runtime list commands"
132590
+ );
132591
+ return {
132592
+ ok: false,
132593
+ blockers: Object.freeze([detail.code]),
132594
+ blockerDetails: Object.freeze([detail])
132595
+ };
132596
+ }
132597
+ const before = lists.get({ editableTarget: currentTarget });
132598
+ if (!before) {
132599
+ const detail = blockerWithOwner(
132600
+ `actions:list-operation:list-readback-missing:${scope.handle.scopeId}`,
130451
132601
  "blocked",
130452
- "The list runtime command produced no document change for the selected scope.",
130453
- "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
130454
- [
130455
- blockerWithOwner(
130456
- `actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
130457
- "blocked",
130458
- "The list runtime command produced no document change for the selected scope.",
130459
- "Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
130460
- "L03 numbering/list semantics and L07 runtime list commands"
130461
- )
130462
- ]
132602
+ "The list target was present in scope evidence but runtime.lists could not produce readback.",
132603
+ "Route the target with editable-target evidence to L07 runtime list readback.",
132604
+ "L07 runtime list commands"
130463
132605
  );
132606
+ return {
132607
+ ok: false,
132608
+ blockers: Object.freeze([detail.code]),
132609
+ blockerDetails: Object.freeze([detail])
132610
+ };
130464
132611
  }
130465
- const compiledAfter = createScopeCompilerService(runtime).compileScopeById(scope.handle.scopeId);
130466
132612
  return {
130467
- status: "applied",
130468
- applied: true,
130469
- changed: true,
130470
- target: compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeTarget(resolvedScope.target),
130471
- commandReference: {
130472
- command: command.type,
130473
- actorId: input.actorId ?? "v3-ai-api",
130474
- origin: input.origin ?? "agent",
130475
- emittedAtUtc: command.origin?.timestamp ?? currentAuditTimestamp(runtime)
130476
- }
132613
+ ok: true,
132614
+ input: { editableTarget: currentTarget },
132615
+ before,
132616
+ scopeHandle: scope.handle
132617
+ };
132618
+ }
132619
+ function listAddressKeyFromActionHandle(actionHandle) {
132620
+ const listPrefix = "list-action:";
132621
+ if (actionHandle.startsWith(listPrefix)) return actionHandle.slice(listPrefix.length) || null;
132622
+ const scopeCommandPrefix = "scope-command:text-leaf:";
132623
+ if (actionHandle.startsWith(scopeCommandPrefix)) {
132624
+ return actionHandle.slice(scopeCommandPrefix.length) || null;
132625
+ }
132626
+ return null;
132627
+ }
132628
+ function runtimeListCommandForOperation(operation) {
132629
+ switch (operation.kind) {
132630
+ case "toggle":
132631
+ return operation.listKind === "numbered" ? "toggle-numbered" : "toggle-bulleted";
132632
+ case "indent":
132633
+ return "indent";
132634
+ case "outdent":
132635
+ return "outdent";
132636
+ case "restart-numbering":
132637
+ return "restart-numbering";
132638
+ case "continue-numbering":
132639
+ return "continue-numbering";
132640
+ }
132641
+ }
132642
+ function commandReferenceForListCommand(command) {
132643
+ switch (command) {
132644
+ case "toggle-numbered":
132645
+ case "toggle-bulleted":
132646
+ return "list.toggle";
132647
+ case "indent":
132648
+ return "list.indent";
132649
+ case "outdent":
132650
+ return "list.outdent";
132651
+ case "restart-numbering":
132652
+ return "list.restart-numbering";
132653
+ case "continue-numbering":
132654
+ return "list.continue-numbering";
132655
+ default:
132656
+ return "list.toggle";
132657
+ }
132658
+ }
132659
+ function listOperationReadback(result, fallbackBefore) {
132660
+ const before = sanitizeListReadback(result.before ?? fallbackBefore);
132661
+ const after = sanitizeListReadback(result.after);
132662
+ return {
132663
+ ...before ? { before } : {},
132664
+ ...after ? { after } : {}
132665
+ };
132666
+ }
132667
+ function sanitizeListReadback(readback) {
132668
+ if (!readback) return void 0;
132669
+ return {
132670
+ actionHandle: readback.actionHandle,
132671
+ kind: readback.kind,
132672
+ storyKey: readback.storyKey,
132673
+ addressKey: readback.addressKey,
132674
+ numberingInstanceId: readback.numberingInstanceId,
132675
+ ...readback.abstractNumberingId ? { abstractNumberingId: readback.abstractNumberingId } : {},
132676
+ level: readback.level,
132677
+ ...readback.listKind ? { listKind: readback.listKind } : {},
132678
+ editability: readback.editability,
132679
+ blockers: readback.blockers,
132680
+ supportedCommands: readback.supportedCommands,
132681
+ unsupportedCommands: readback.unsupportedCommands,
132682
+ staleDiscriminators: readback.staleDiscriminators
132683
+ };
132684
+ }
132685
+ function summarizeListTarget(target, readback) {
132686
+ const current = readback ?? target.before;
132687
+ return {
132688
+ kind: "list-item",
132689
+ ...target.scopeHandle ? { handle: target.scopeHandle } : {},
132690
+ ...current?.actionHandle ? { actionHandle: current.actionHandle } : {},
132691
+ canRewriteText: current?.editability === "editable" && (current.blockers.length ?? 0) === 0,
132692
+ canInsertAdjacentText: false,
132693
+ canFlag: Boolean(target.scopeHandle),
132694
+ canMark: Boolean(target.scopeHandle)
130477
132695
  };
130478
132696
  }
132697
+ function listBlockerDetails(prefix, blockers) {
132698
+ return Object.freeze(
132699
+ blockers.map(
132700
+ (entry) => blockerWithOwner(
132701
+ `${prefix}:${entry.code}:${entry.addressKey ?? entry.targetKey ?? "unknown"}`,
132702
+ entry.code === "list-command-unsupported" ? "unsupported" : "blocked",
132703
+ entry.message,
132704
+ "Refresh the opaque list target/readback and route persistent blockers to the owning runtime list layer.",
132705
+ entry.ownerLayer === "L07" ? "L07 runtime list commands" : entry.ownerLayer
132706
+ )
132707
+ )
132708
+ );
132709
+ }
130479
132710
  function projectTableApplyResult(result) {
130480
132711
  return {
130481
132712
  status: result.applied ? "applied" : "blocked",
@@ -130603,36 +132834,6 @@ function tableSelectionStepHasDescriptor(operation) {
130603
132834
  operation && "selectionDescriptor" in operation && operation.selectionDescriptor
130604
132835
  );
130605
132836
  }
130606
- function listCommandForOperation(operation, paragraphIndex, origin) {
130607
- switch (operation.kind) {
130608
- case "toggle":
130609
- return {
130610
- type: "list.toggle",
130611
- kind: operation.listKind,
130612
- paragraphIndexes: [paragraphIndex],
130613
- origin
130614
- };
130615
- case "indent":
130616
- return { type: "list.indent", paragraphIndexes: [paragraphIndex], origin };
130617
- case "outdent":
130618
- return { type: "list.outdent", paragraphIndexes: [paragraphIndex], origin };
130619
- case "restart-numbering":
130620
- return {
130621
- type: "list.restart-numbering",
130622
- paragraphIndex,
130623
- ...operation.startAt !== void 0 ? { startAt: operation.startAt } : {},
130624
- origin
130625
- };
130626
- case "continue-numbering":
130627
- return { type: "list.continue-numbering", paragraphIndex, origin };
130628
- }
130629
- }
130630
- function paragraphIndexFromHandle(handle) {
130631
- const raw = handle.semanticPath[handle.semanticPath.length - 1];
130632
- if (raw === void 0) return null;
130633
- const index = Number(raw);
130634
- return Number.isSafeInteger(index) && index >= 0 ? index : null;
130635
- }
130636
132837
  function actionOrigin(runtime, input) {
130637
132838
  return { source: "api", timestamp: currentAuditTimestamp(runtime) };
130638
132839
  }
@@ -130997,7 +133198,8 @@ function createApiV3(handle, opts) {
130997
133198
  chart: createChartFamily(handle),
130998
133199
  search: createSearchFamily(handle),
130999
133200
  table: createTableFamily(handle),
131000
- viewport: createViewportFamily2(handle)
133201
+ viewport: createViewportFamily2(handle),
133202
+ lists: createListsFamily(handle)
131001
133203
  };
131002
133204
  const ui = opts?.ui ? createUiApi(handle, opts.ui) : void 0;
131003
133205
  const api = ui ? { runtime, ai, ui } : { runtime, ai };
@@ -133363,6 +135565,7 @@ var WordReviewEditor = (0, import_react71.forwardRef)(
133363
135565
  // auto-bracketing in insertFragment + our explicit startAction around
133364
135566
  // the delete, so the undo history sees a single atomic action.
133365
135567
  onDropFragment: (meta) => {
135568
+ const dropEditableTarget = meta.editableTarget ?? activeRuntime.getRenderSnapshot().selection.editableTarget;
133366
135569
  if (meta.effect === "move" && meta.sourceRange) {
133367
135570
  activeRuntime.startAction("drag-move");
133368
135571
  try {
@@ -133372,16 +135575,21 @@ var WordReviewEditor = (0, import_react71.forwardRef)(
133372
135575
  to: meta.sourceRange.to,
133373
135576
  assoc: { start: -1, end: 1 }
133374
135577
  });
133375
- activeRuntime.insertFragment(meta.fragment);
135578
+ const currentEditableTarget = activeRuntime.getRenderSnapshot().selection.editableTarget;
135579
+ const editableTarget = currentEditableTarget?.targetKey === dropEditableTarget?.targetKey ? currentEditableTarget : dropEditableTarget;
135580
+ activeRuntime.insertFragment(
135581
+ meta.fragment,
135582
+ void 0,
135583
+ editableTarget ? { editableTarget } : void 0
135584
+ );
133376
135585
  } finally {
133377
135586
  activeRuntime.endAction();
133378
135587
  }
133379
135588
  } else {
133380
- const editableTarget = activeRuntime.getRenderSnapshot().selection.editableTarget;
133381
135589
  activeRuntime.insertFragment(
133382
135590
  meta.fragment,
133383
135591
  void 0,
133384
- editableTarget ? { editableTarget } : void 0
135592
+ dropEditableTarget ? { editableTarget: dropEditableTarget } : void 0
133385
135593
  );
133386
135594
  }
133387
135595
  },
@@ -136363,7 +138571,7 @@ function normalizeForHashing(doc) {
136363
138571
  };
136364
138572
  }
136365
138573
  async function computeCanonicalDocumentHash(doc) {
136366
- return sha256Hex5(stableStringify(normalizeForHashing(doc)));
138574
+ return sha256Hex5(stableStringify2(normalizeForHashing(doc)));
136367
138575
  }
136368
138576
 
136369
138577
  // src/runtime/prerender/font-fingerprint.ts