@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
@@ -21,6 +21,43 @@ var __copyProps = (to, from, except, desc) => {
21
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
22
 
23
23
  // src/runtime/layout/resolved-formatting-state.ts
24
+ function createLayoutReadyFormattingSnapshot(formatting, runs = /* @__PURE__ */ new Map()) {
25
+ const snapshot = {
26
+ version: 1,
27
+ paragraph: {
28
+ spacingBefore: formatting.spacingBefore,
29
+ spacingAfter: formatting.spacingAfter,
30
+ lineHeight: formatting.lineHeight,
31
+ lineRule: formatting.lineRule,
32
+ indentLeft: formatting.indentLeft,
33
+ indentRight: formatting.indentRight,
34
+ firstLineIndent: formatting.firstLineIndent,
35
+ hangingIndent: formatting.hangingIndent,
36
+ fontSizeHalfPoints: formatting.fontSizeHalfPoints,
37
+ averageCharWidthTwips: formatting.averageCharWidthTwips,
38
+ tabStops: formatting.tabStops.map((tab) => ({ ...tab })),
39
+ defaultTabInterval: formatting.defaultTabInterval,
40
+ keepNext: formatting.keepNext,
41
+ keepLines: formatting.keepLines,
42
+ pageBreakBefore: formatting.pageBreakBefore,
43
+ widowControl: formatting.widowControl,
44
+ contextualSpacing: formatting.contextualSpacing,
45
+ ...formatting.numberingMarkerBox ? { numberingMarkerBox: { ...formatting.numberingMarkerBox } } : {}
46
+ },
47
+ runs: Array.from(runs.entries()).map(([runId, run]) => ({
48
+ runId,
49
+ ...run.fontFamily ? { fontFamily: run.fontFamily } : {},
50
+ ...typeof run.fontSizeHalfPoints === "number" ? { fontSizeHalfPoints: run.fontSizeHalfPoints } : {},
51
+ bold: Boolean(run.bold),
52
+ italic: Boolean(run.italic),
53
+ verticalAlign: run.verticalAlign ?? "baseline"
54
+ })).sort((a, b) => a.runId.localeCompare(b.runId))
55
+ };
56
+ return {
57
+ ...snapshot,
58
+ hash: hashStable(snapshot)
59
+ };
60
+ }
24
61
  function resolveBlockFormatting(block, defaultTabInterval = 720, themeFonts) {
25
62
  if (block.kind !== "paragraph") {
26
63
  return null;
@@ -263,6 +300,29 @@ function buildRunFormattingMap(block) {
263
300
  }
264
301
  return runs;
265
302
  }
303
+ function hashStable(value) {
304
+ return fnv1a(stableStringify3(value)).toString(36);
305
+ }
306
+ function stableStringify3(value) {
307
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
308
+ if (Array.isArray(value)) {
309
+ return `[${value.map((item) => stableStringify3(item)).join(",")}]`;
310
+ }
311
+ if (value instanceof Map) {
312
+ return stableStringify3(Array.from(value.entries()));
313
+ }
314
+ const object = value;
315
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
316
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify3(object[key])}`).join(",")}}`;
317
+ }
318
+ function fnv1a(input) {
319
+ let hash = 2166136261;
320
+ for (let i = 0; i < input.length; i += 1) {
321
+ hash ^= input.charCodeAt(i);
322
+ hash = Math.imul(hash, 16777619);
323
+ }
324
+ return hash >>> 0;
325
+ }
266
326
  var FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_AVG_CHAR_WIDTH, DEFAULT_FONT_SIZE_HALF_POINTS, DEFAULT_LINE_HEIGHT_FACTOR, TWIPS_PER_POINT;
267
327
  var init_resolved_formatting_state = __esm({
268
328
  "src/runtime/layout/resolved-formatting-state.ts"() {
@@ -4856,6 +4916,7 @@ function collectCanonicalLayoutInputs(doc) {
4856
4916
  const blockContexts = collectStoryBlockContexts(doc);
4857
4917
  return {
4858
4918
  stories: collectCanonicalStoryIdentities(doc),
4919
+ layoutIdentities: collectLayoutInputIdentities(doc, blockContexts),
4859
4920
  fieldRegions: collectCanonicalFieldRegionIdentities(doc),
4860
4921
  numbering: collectCanonicalNumberingLayoutInputs(doc, blockContexts),
4861
4922
  tables: collectCanonicalTableLayoutInputs(blockContexts),
@@ -4864,6 +4925,116 @@ function collectCanonicalLayoutInputs(doc) {
4864
4925
  editableTargets: collectEditableTargetRefs(doc)
4865
4926
  };
4866
4927
  }
4928
+ function collectLayoutInputIdentities(doc, contexts = collectStoryBlockContexts(doc)) {
4929
+ const identities = [];
4930
+ const editableTargets = collectEditableTargetRefs(doc);
4931
+ const editableByStoryBlock = new Map(
4932
+ editableTargets.map((target) => [`${target.storyKey}:${target.blockPath}`, target])
4933
+ );
4934
+ for (const context of contexts) {
4935
+ walkBlocks(context.blocks, context.storyKey, context.basePath, {
4936
+ paragraph(paragraph, blockPath) {
4937
+ const styleNumbering = paragraph.numbering === void 0 && paragraph.styleId !== void 0 ? resolveParagraphStyleNumbering(doc, paragraph.styleId) : void 0;
4938
+ const numbering = paragraph.numbering ?? styleNumbering;
4939
+ const editableTargetRef = editableByStoryBlock.get(`${context.storyKey}:${blockPath}`);
4940
+ identities.push(
4941
+ createLayoutInputIdentity({
4942
+ storyKey: context.storyKey,
4943
+ blockPath,
4944
+ block: paragraph,
4945
+ ...editableTargetRef !== void 0 ? { editableTargetRef } : {},
4946
+ ...numbering !== void 0 ? {
4947
+ list: {
4948
+ numberingInstanceId: numbering.numberingInstanceId,
4949
+ level: numbering.level ?? 0,
4950
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
4951
+ }
4952
+ } : {}
4953
+ })
4954
+ );
4955
+ },
4956
+ table(table, blockPath) {
4957
+ identities.push(
4958
+ createLayoutInputIdentity({
4959
+ storyKey: context.storyKey,
4960
+ blockPath,
4961
+ block: table,
4962
+ table: { tableBlockPath: blockPath }
4963
+ })
4964
+ );
4965
+ },
4966
+ inline(inline, blockPath, inlinePath) {
4967
+ const objectId = objectIdForInline(inline);
4968
+ if (!objectId) return;
4969
+ const sourceRef = sourceRefForInline(inline);
4970
+ identities.push({
4971
+ storyKey: context.storyKey,
4972
+ blockPath,
4973
+ blockId: `${context.storyKey}:${inlinePath}`,
4974
+ ...sourceRef !== void 0 ? { sourceRef } : {},
4975
+ object: {
4976
+ objectId,
4977
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
4978
+ }
4979
+ });
4980
+ }
4981
+ });
4982
+ }
4983
+ return identities;
4984
+ }
4985
+ function createLayoutInputIdentity(input) {
4986
+ const sourceRef = sourceRefForBlock(input.block);
4987
+ 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}`;
4988
+ return {
4989
+ storyKey: input.storyKey,
4990
+ blockPath: input.blockPath,
4991
+ blockId,
4992
+ ...sourceRef !== void 0 ? { sourceRef } : {},
4993
+ ...input.editableTargetRef !== void 0 ? { editableTargetRef: input.editableTargetRef } : {},
4994
+ ...input.table !== void 0 ? { table: input.table } : {},
4995
+ ...input.list !== void 0 ? { list: input.list } : {}
4996
+ };
4997
+ }
4998
+ function sourceRefForBlock(block) {
4999
+ return "sourceRef" in block ? block.sourceRef : void 0;
5000
+ }
5001
+ function sourceRefForInline(inline) {
5002
+ return "sourceRef" in inline ? inline.sourceRef : void 0;
5003
+ }
5004
+ function objectIdForInline(inline) {
5005
+ switch (inline.type) {
5006
+ case "image":
5007
+ return inline.mediaId;
5008
+ case "drawing_frame":
5009
+ return objectIdForDrawingContent(inline.content) ?? inline.sourceRef?.sourceId;
5010
+ case "shape":
5011
+ case "vml_shape":
5012
+ case "wordart":
5013
+ return inline.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
5014
+ case "chart_preview":
5015
+ case "smartart_preview":
5016
+ return inline.previewMediaId ?? `raw:${sha256TextHex(inline.rawXml).slice(0, 16)}`;
5017
+ case "ole_embed":
5018
+ return inline.relationshipId ?? inline.id ?? inline.sourceRef?.sourceId;
5019
+ case "opaque_inline":
5020
+ return inline.fragmentId;
5021
+ default:
5022
+ return void 0;
5023
+ }
5024
+ }
5025
+ function objectIdForDrawingContent(content) {
5026
+ switch (content.type) {
5027
+ case "picture":
5028
+ return content.mediaId ?? content.packagePartName ?? content.blipRef;
5029
+ case "chart_preview":
5030
+ case "smartart_preview":
5031
+ return content.previewMediaId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
5032
+ case "shape":
5033
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
5034
+ case "opaque":
5035
+ return content.preserveOnlyObject?.sourceId ?? `raw:${sha256TextHex(content.rawXml).slice(0, 16)}`;
5036
+ }
5037
+ }
4867
5038
  function collectEditableTargetRefs(doc, cache) {
4868
5039
  const targets = [];
4869
5040
  for (const context of collectStoryBlockContexts(doc)) {
@@ -7012,6 +7183,16 @@ function collectCanonicalNumberingLayoutInputs(doc, contexts = collectStoryBlock
7012
7183
  const instance = doc.numbering.instances[numbering.numberingInstanceId];
7013
7184
  const abstractDefinition = instance?.abstractNumberingId === void 0 ? void 0 : doc.numbering.abstractDefinitions[instance.abstractNumberingId];
7014
7185
  inputs.push({
7186
+ identity: createLayoutInputIdentity({
7187
+ storyKey: context.storyKey,
7188
+ blockPath,
7189
+ block: paragraph,
7190
+ list: {
7191
+ numberingInstanceId: numbering.numberingInstanceId,
7192
+ level: numbering.level ?? 0,
7193
+ ...paragraph.numbering?.sourceRef !== void 0 ? { markerSourceRef: paragraph.numbering.sourceRef } : {}
7194
+ }
7195
+ }),
7015
7196
  numberingKey: `${context.storyKey}:${blockPath}:numbering`,
7016
7197
  storyKey: context.storyKey,
7017
7198
  blockPath,
@@ -7313,6 +7494,12 @@ function projectTableLayoutInput(table, storyKey2, blockPath) {
7313
7494
  (row2, rowIndex) => projectTableRowLayoutInput(row2, tableKey, rowIndex)
7314
7495
  );
7315
7496
  return {
7497
+ identity: createLayoutInputIdentity({
7498
+ storyKey: storyKey2,
7499
+ blockPath,
7500
+ block: table,
7501
+ table: { tableBlockPath: blockPath }
7502
+ }),
7316
7503
  tableKey,
7317
7504
  storyKey: storyKey2,
7318
7505
  blockPath,
@@ -7400,6 +7587,16 @@ function projectDrawingFrameAnchor(node, storyKey2, blockPath, inlinePath) {
7400
7587
  const objectKey = `${storyKey2}:${inlinePath}`;
7401
7588
  const textBoxBody = projectTextBoxBodyLayoutInput(content, objectKey, sourceRef, `${inlinePath}/txbx`);
7402
7589
  return {
7590
+ identity: {
7591
+ storyKey: storyKey2,
7592
+ blockPath,
7593
+ blockId: objectKey,
7594
+ ...sourceRef !== void 0 ? { sourceRef } : {},
7595
+ object: {
7596
+ objectId: objectKey,
7597
+ ...sourceRef !== void 0 ? { anchorSourceRef: sourceRef } : {}
7598
+ }
7599
+ },
7403
7600
  objectKey,
7404
7601
  storyKey: storyKey2,
7405
7602
  blockPath,
@@ -7521,8 +7718,19 @@ function projectTextBoxRunLayoutInput(inline, paragraphKey, inlineIndex) {
7521
7718
  }
7522
7719
  function projectLegacyImageAnchor(doc, node, storyKey2, blockPath, inlinePath) {
7523
7720
  const media = doc.media.items[node.mediaId];
7721
+ const objectKey = `${storyKey2}:${inlinePath}`;
7524
7722
  return {
7525
- objectKey: `${storyKey2}:${inlinePath}`,
7723
+ identity: {
7724
+ storyKey: storyKey2,
7725
+ blockPath,
7726
+ blockId: objectKey,
7727
+ ...node.sourceRef !== void 0 ? { sourceRef: node.sourceRef } : {},
7728
+ object: {
7729
+ objectId: objectKey,
7730
+ ...node.sourceRef !== void 0 ? { anchorSourceRef: node.sourceRef } : {}
7731
+ }
7732
+ },
7733
+ objectKey,
7526
7734
  storyKey: storyKey2,
7527
7735
  blockPath,
7528
7736
  inlinePath,
@@ -9317,12 +9525,13 @@ function getLevelStartAt(level, levelDefinitions) {
9317
9525
  return levelDefinitions.get(level)?.startAt ?? DEFAULT_NUMBERING_START_AT;
9318
9526
  }
9319
9527
  function getNumberingFormatPosture(format, value) {
9320
- if (!isSupportedNumberingFormat(format)) {
9528
+ const registryEntry = getNumberingFormatRegistryEntry(format);
9529
+ if (!registryEntry || registryEntry.renderSupport === "approximated") {
9321
9530
  return {
9322
9531
  status: "approximated",
9323
9532
  requestedFormat: format,
9324
9533
  renderedFormat: "decimal",
9325
- reason: "unsupported-numbering-format-decimal-fallback"
9534
+ reason: registryEntry?.fallbackReason ?? "unsupported-numbering-format-decimal-fallback"
9326
9535
  };
9327
9536
  }
9328
9537
  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)) {
@@ -9353,56 +9562,148 @@ function renderLevelText(text, counters, levelDefinitions) {
9353
9562
  });
9354
9563
  return rendered.trim().length > 0 ? rendered : null;
9355
9564
  }
9356
- var SUPPORTED_NUMBERING_FORMATS = /* @__PURE__ */ new Set([
9357
- "decimal",
9358
- "decimalZero",
9359
- "upperLetter",
9360
- "lowerLetter",
9361
- "upperRoman",
9362
- "lowerRoman",
9363
- "hex",
9364
- "ordinal",
9365
- "cardinalText",
9366
- "ordinalText",
9367
- "chicago",
9368
- "bullet",
9369
- "none"
9370
- ]);
9371
9565
  function isSupportedNumberingFormat(format) {
9372
- return SUPPORTED_NUMBERING_FORMATS.has(format);
9566
+ return getNumberingFormatRegistryEntry(format)?.renderSupport === "supported";
9567
+ }
9568
+ function getNumberingFormatRegistryEntry(format) {
9569
+ return NUMBERING_FORMAT_REGISTRY.get(format);
9373
9570
  }
9374
9571
  function formatCounter(value, format) {
9375
- switch (format) {
9376
- case "decimal":
9377
- return String(value);
9378
- case "decimalZero":
9379
- return String(value).padStart(2, "0");
9380
- case "upperLetter":
9381
- return toAlphabetic2(value).toUpperCase();
9382
- case "lowerLetter":
9383
- return toAlphabetic2(value).toLowerCase();
9384
- case "upperRoman":
9385
- return toRoman2(value).toUpperCase();
9386
- case "lowerRoman":
9387
- return toRoman2(value).toLowerCase();
9388
- case "hex":
9389
- return value >= 0 ? value.toString(16).toUpperCase() : String(value);
9390
- case "ordinal":
9391
- return toOrdinal2(value);
9392
- case "cardinalText":
9393
- return toCardinalText2(value);
9394
- case "ordinalText":
9395
- return toOrdinalText2(value);
9396
- case "chicago":
9397
- return toChicago2(value);
9398
- case "bullet":
9399
- return "";
9400
- case "none":
9401
- return "";
9402
- default:
9403
- return String(value);
9404
- }
9572
+ return getNumberingFormatRegistryEntry(format)?.render(value) ?? String(value);
9405
9573
  }
9574
+ var exactNumberingFormatEntries = [
9575
+ {
9576
+ format: "decimal",
9577
+ renderSupport: "supported",
9578
+ renderedFormat: "decimal",
9579
+ supportsMutation: true,
9580
+ render: (value) => String(value)
9581
+ },
9582
+ {
9583
+ format: "decimalZero",
9584
+ renderSupport: "supported",
9585
+ renderedFormat: "decimalZero",
9586
+ supportsMutation: true,
9587
+ render: (value) => String(value).padStart(2, "0")
9588
+ },
9589
+ {
9590
+ format: "upperLetter",
9591
+ renderSupport: "supported",
9592
+ renderedFormat: "upperLetter",
9593
+ supportsMutation: true,
9594
+ render: (value) => toAlphabetic2(value).toUpperCase()
9595
+ },
9596
+ {
9597
+ format: "lowerLetter",
9598
+ renderSupport: "supported",
9599
+ renderedFormat: "lowerLetter",
9600
+ supportsMutation: true,
9601
+ render: (value) => toAlphabetic2(value).toLowerCase()
9602
+ },
9603
+ {
9604
+ format: "upperRoman",
9605
+ renderSupport: "supported",
9606
+ renderedFormat: "upperRoman",
9607
+ supportsMutation: true,
9608
+ render: (value) => toRoman2(value).toUpperCase()
9609
+ },
9610
+ {
9611
+ format: "lowerRoman",
9612
+ renderSupport: "supported",
9613
+ renderedFormat: "lowerRoman",
9614
+ supportsMutation: true,
9615
+ render: (value) => toRoman2(value).toLowerCase()
9616
+ },
9617
+ {
9618
+ format: "hex",
9619
+ renderSupport: "supported",
9620
+ renderedFormat: "hex",
9621
+ supportsMutation: true,
9622
+ render: (value) => value >= 0 ? value.toString(16).toUpperCase() : String(value)
9623
+ },
9624
+ {
9625
+ format: "ordinal",
9626
+ renderSupport: "supported",
9627
+ renderedFormat: "ordinal",
9628
+ supportsMutation: true,
9629
+ render: toOrdinal2
9630
+ },
9631
+ {
9632
+ format: "cardinalText",
9633
+ renderSupport: "supported",
9634
+ renderedFormat: "cardinalText",
9635
+ supportsMutation: true,
9636
+ render: toCardinalText2
9637
+ },
9638
+ {
9639
+ format: "ordinalText",
9640
+ renderSupport: "supported",
9641
+ renderedFormat: "ordinalText",
9642
+ supportsMutation: true,
9643
+ render: toOrdinalText2
9644
+ },
9645
+ {
9646
+ format: "chicago",
9647
+ renderSupport: "supported",
9648
+ renderedFormat: "chicago",
9649
+ supportsMutation: true,
9650
+ render: toChicago2
9651
+ },
9652
+ {
9653
+ format: "bullet",
9654
+ renderSupport: "supported",
9655
+ renderedFormat: "bullet",
9656
+ supportsMutation: true,
9657
+ render: () => ""
9658
+ },
9659
+ {
9660
+ format: "none",
9661
+ renderSupport: "supported",
9662
+ renderedFormat: "none",
9663
+ supportsMutation: true,
9664
+ render: () => ""
9665
+ }
9666
+ ];
9667
+ var approximatedDecimalFormats = [
9668
+ "decimalEnclosedCircle",
9669
+ "decimalEnclosedFullstop",
9670
+ "decimalEnclosedParen",
9671
+ "decimalFullWidth",
9672
+ "decimalHalfWidth",
9673
+ "aiueo",
9674
+ "iroha",
9675
+ "ganada",
9676
+ "chosung",
9677
+ "russianLower",
9678
+ "russianUpper",
9679
+ "hebrew1",
9680
+ "hebrew2",
9681
+ "arabicAlpha",
9682
+ "arabicAbjad",
9683
+ "thaiLetters",
9684
+ "thaiNumbers",
9685
+ "hindiLetters",
9686
+ "hindiNumbers",
9687
+ "ideographDigital",
9688
+ "ideographTraditional",
9689
+ "chineseCounting",
9690
+ "japaneseCounting",
9691
+ "japaneseLegal"
9692
+ ];
9693
+ var approximatedNumberingFormatEntries = approximatedDecimalFormats.map((format) => ({
9694
+ format,
9695
+ renderSupport: "approximated",
9696
+ renderedFormat: "decimal",
9697
+ supportsMutation: false,
9698
+ fallbackReason: "unsupported-numbering-format-decimal-fallback",
9699
+ render: (value) => String(value)
9700
+ }));
9701
+ var NUMBERING_FORMAT_REGISTRY = new Map(
9702
+ [...exactNumberingFormatEntries, ...approximatedNumberingFormatEntries].map((entry) => [
9703
+ entry.format,
9704
+ entry
9705
+ ])
9706
+ );
9406
9707
  function toOrdinal2(value) {
9407
9708
  if (value <= 0) return String(value);
9408
9709
  const lastTwo = value % 100;
@@ -10056,12 +10357,36 @@ var DEFAULT_HYPERLINK_COLOR_HEX = "0563C1";
10056
10357
  function resolveHyperlinkRunFormatting(input, catalog, resolver) {
10057
10358
  const augmentedInput = input.characterStyleId === void 0 ? { ...input, characterStyleId: HYPERLINK_CHARACTER_STYLE_ID } : input;
10058
10359
  const cascade = resolveEffectiveRunFormatting(augmentedInput, catalog);
10059
- const resolvedColor = resolveHyperlinkColorHex(cascade, resolver);
10360
+ const resolvedColor = resolveHyperlinkColorHex(
10361
+ stripInheritedColorHexForImplicitHyperlink(input, catalog, cascade),
10362
+ resolver
10363
+ );
10060
10364
  if (resolvedColor && resolvedColor !== cascade.colorHex) {
10061
10365
  return { ...cascade, colorHex: resolvedColor };
10062
10366
  }
10063
10367
  return cascade;
10064
10368
  }
10369
+ function hasDirectNonAutoColor(input) {
10370
+ return Boolean(input.direct?.colorHex && input.direct.colorHex !== "auto");
10371
+ }
10372
+ function characterStyleDeclaresColorHex(styleId, catalog) {
10373
+ if (!catalog) return false;
10374
+ const chain = resolveCharacterStyleChain(styleId, catalog);
10375
+ return chain.some((chainStyleId) => {
10376
+ const colorHex = catalog.characters[chainStyleId]?.runProperties?.colorHex;
10377
+ return Boolean(colorHex && colorHex !== "auto");
10378
+ });
10379
+ }
10380
+ function stripInheritedColorHexForImplicitHyperlink(originalInput, catalog, cascade) {
10381
+ if (originalInput.characterStyleId !== void 0 || hasDirectNonAutoColor(originalInput) || !cascade.colorHex || cascade.colorHex === "auto" || characterStyleDeclaresColorHex(HYPERLINK_CHARACTER_STYLE_ID, catalog)) {
10382
+ return cascade;
10383
+ }
10384
+ const inheritedCascade = resolveEffectiveRunFormatting(originalInput, catalog);
10385
+ if (inheritedCascade.colorHex !== cascade.colorHex) {
10386
+ return cascade;
10387
+ }
10388
+ return { ...cascade, colorHex: void 0 };
10389
+ }
10065
10390
  function resolveHyperlinkColorHex(cascade, resolver) {
10066
10391
  if (cascade.colorHex && cascade.colorHex !== "auto") {
10067
10392
  return cascade.colorHex;
@@ -10901,6 +11226,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
10901
11226
  ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
10902
11227
  });
10903
11228
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
11229
+ const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
10904
11230
  const activeStoryBlockPathBase = getActiveStoryBlockPathBase(document2, activeStory);
10905
11231
  chartModelStore.beginBuildPass(document2);
10906
11232
  const unsupportedNumberingFormatsSeen = options.emitFormattingTelemetry ? /* @__PURE__ */ new Set() : null;
@@ -10924,6 +11250,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
10924
11250
  activeStory.kind !== "main",
10925
11251
  !isInViewport,
10926
11252
  editableTargetsByBlockPath,
11253
+ layoutIdentitiesByBlockPath,
10927
11254
  `${activeStoryBlockPathBase}/block[${index}]`
10928
11255
  );
10929
11256
  if (isInViewport) {
@@ -10990,7 +11317,7 @@ function isIndexInAnyRange(index, ranges) {
10990
11317
  }
10991
11318
  return false;
10992
11319
  }
10993
- function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath) {
11320
+ function createSurfaceBlock(block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, blockPath) {
10994
11321
  if (block.type === "opaque_block") {
10995
11322
  const fragment = getOpaqueFragment(document2.preservation, block.fragmentId);
10996
11323
  const descriptor = fragment ? describeOpaqueFragment(fragment) : null;
@@ -11027,6 +11354,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11027
11354
  promoteSecondaryStoryTextBoxes,
11028
11355
  cullBuild,
11029
11356
  editableTargetsByBlockPath,
11357
+ layoutIdentitiesByBlockPath,
11030
11358
  blockPath
11031
11359
  );
11032
11360
  }
@@ -11067,6 +11395,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11067
11395
  promoteSecondaryStoryTextBoxes,
11068
11396
  cullBuild,
11069
11397
  editableTargetsByBlockPath,
11398
+ layoutIdentitiesByBlockPath,
11070
11399
  blockPath
11071
11400
  );
11072
11401
  }
@@ -11149,10 +11478,11 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11149
11478
  cullBuild,
11150
11479
  editableTargetsByBlockPath,
11151
11480
  blockPath,
11152
- blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0
11481
+ blockPath !== void 0 ? editableTargetsByBlockPath.get(blockPath) : void 0,
11482
+ blockPath !== void 0 ? layoutIdentitiesByBlockPath?.get(blockPath) : void 0
11153
11483
  );
11154
11484
  }
11155
- function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, tablePath) {
11485
+ function createTableBlock(tableIndex, table, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, tablePath) {
11156
11486
  const lockedFragmentIds = [];
11157
11487
  let innerCursor = cursor;
11158
11488
  if (cullBuild) {
@@ -11169,6 +11499,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11169
11499
  promoteSecondaryStoryTextBoxes,
11170
11500
  true,
11171
11501
  editableTargetsByBlockPath,
11502
+ layoutIdentitiesByBlockPath,
11172
11503
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
11173
11504
  );
11174
11505
  lockedFragmentIds.push(...result.lockedFragmentIds);
@@ -11184,6 +11515,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11184
11515
  to: innerCursor,
11185
11516
  styleId: table.styleId,
11186
11517
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
11518
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
11187
11519
  gridColumns: table.gridColumns,
11188
11520
  rows: []
11189
11521
  },
@@ -11221,6 +11553,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11221
11553
  promoteSecondaryStoryTextBoxes,
11222
11554
  cullBuild,
11223
11555
  editableTargetsByBlockPath,
11556
+ layoutIdentitiesByBlockPath,
11224
11557
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
11225
11558
  );
11226
11559
  cellContent.push(result.block);
@@ -11309,6 +11642,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11309
11642
  to: innerCursor,
11310
11643
  styleId: table.styleId,
11311
11644
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
11645
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
11312
11646
  gridColumns: table.gridColumns,
11313
11647
  ...gridColumnsRelative ? { gridColumnsRelative } : {},
11314
11648
  ...resolvedTable.table?.alignment ? { alignment: resolvedTable.table.alignment } : {},
@@ -11496,7 +11830,7 @@ function resolveCellBorderStyles(borders, tableBorders, position) {
11496
11830
  if (left) result.borderLeft = left;
11497
11831
  return result;
11498
11832
  }
11499
- function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, sdtPath) {
11833
+ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, layoutIdentitiesByBlockPath, sdtPath) {
11500
11834
  const children = [];
11501
11835
  const lockedFragmentIds = [];
11502
11836
  let innerCursor = cursor;
@@ -11513,6 +11847,7 @@ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formatting
11513
11847
  promoteSecondaryStoryTextBoxes,
11514
11848
  cullBuild,
11515
11849
  editableTargetsByBlockPath,
11850
+ layoutIdentitiesByBlockPath,
11516
11851
  sdtPath !== void 0 ? `${sdtPath}/block[${childIndex}]` : void 0
11517
11852
  );
11518
11853
  children.push(result.block);
@@ -11555,7 +11890,7 @@ function getRecursableSdtBlockedReasonCode(block) {
11555
11890
  ].filter(Boolean).join(" ").toLowerCase();
11556
11891
  return searchText.includes("table of contents") || /\btoc\b/u.test(searchText) ? "workflow_preserve_only" : null;
11557
11892
  }
11558
- function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget) {
11893
+ function createParagraphBlock(paragraphIndex, paragraph, document2, start, formattingContext, promoteSecondaryStoryTextBoxes, cullBuild = false, editableTargetsByBlockPath = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH, blockPath, editableTarget, layoutIdentity) {
11559
11894
  const themeResolver = formattingContext.theme;
11560
11895
  const effectiveNumbering = formattingContext.resolveEffectiveParagraphNumbering(paragraph);
11561
11896
  let resolvedNumbering = null;
@@ -11578,6 +11913,7 @@ function createParagraphBlock(paragraphIndex, paragraph, document2, start, forma
11578
11913
  from: start,
11579
11914
  to: start,
11580
11915
  ...editableTarget !== void 0 ? { editableTarget } : {},
11916
+ ...layoutIdentity !== void 0 ? { layoutIdentity } : {},
11581
11917
  ...paragraph.styleId ? { styleId: paragraph.styleId } : {},
11582
11918
  ...effectiveNumbering ? { numbering: effectiveNumbering } : {},
11583
11919
  ...resolvedNumbering ? {
@@ -18920,6 +19256,133 @@ function rangesOverlap2(leftFrom, leftTo, rightFrom, rightTo) {
18920
19256
  return leftFrom < rightTo && leftTo > rightFrom;
18921
19257
  }
18922
19258
 
19259
+ // src/io/ooxml/numbering-catalog-mutation.ts
19260
+ function cloneNumberingCatalog(catalog) {
19261
+ return structuredClone(catalog);
19262
+ }
19263
+ function getListKind(catalog, numberingInstanceId) {
19264
+ const instance = catalog.instances[numberingInstanceId];
19265
+ const definition = instance ? catalog.abstractDefinitions[instance.abstractNumberingId] : void 0;
19266
+ const levelZero = definition?.levels.find((level) => level.level === 0);
19267
+ if (!levelZero) return void 0;
19268
+ return levelZero.format === "bullet" ? "bulleted" : "numbered";
19269
+ }
19270
+ function ensureDefaultListInstance(catalog, kind) {
19271
+ const existing = Object.values(catalog.instances).find(
19272
+ (instance) => getListKind(catalog, instance.numberingInstanceId) === kind
19273
+ );
19274
+ if (existing) return existing.numberingInstanceId;
19275
+ const abstractNumberingId = allocateAbstractDefinition(catalog, {
19276
+ levels: defaultLevels(kind),
19277
+ multiLevelType: "hybridMultilevel"
19278
+ });
19279
+ return allocateNumberingInstance(catalog, abstractNumberingId);
19280
+ }
19281
+ function allocateNumberingInstance(catalog, abstractNumberingId, overrides = []) {
19282
+ const numberingInstanceId = nextCanonicalNumericId(
19283
+ "num:",
19284
+ Object.keys(catalog.instances)
19285
+ );
19286
+ catalog.instances[numberingInstanceId] = {
19287
+ numberingInstanceId,
19288
+ abstractNumberingId,
19289
+ overrides: cloneOverrides(overrides)
19290
+ };
19291
+ return numberingInstanceId;
19292
+ }
19293
+ function allocateAbstractDefinition(catalog, input) {
19294
+ const abstractNumberingId = input.abstractNumberingId && !catalog.abstractDefinitions[input.abstractNumberingId] ? input.abstractNumberingId : nextCanonicalNumericId("abstract-num:", Object.keys(catalog.abstractDefinitions));
19295
+ catalog.abstractDefinitions[abstractNumberingId] = {
19296
+ abstractNumberingId,
19297
+ levels: structuredClone(input.levels),
19298
+ ...input.nsid ? { nsid: input.nsid } : { nsid: freshLongHex(catalog, abstractNumberingId, "nsid") },
19299
+ ...input.multiLevelType ? { multiLevelType: input.multiLevelType } : {},
19300
+ ...input.tplc ? { tplc: input.tplc } : { tplc: freshLongHex(catalog, abstractNumberingId, "tmpl") },
19301
+ ...input.styleLink ? { styleLink: input.styleLink } : {},
19302
+ ...input.numStyleLink ? { numStyleLink: input.numStyleLink } : {}
19303
+ };
19304
+ return abstractNumberingId;
19305
+ }
19306
+ function setStartOverride(catalog, numberingInstanceId, level, startAt) {
19307
+ const instance = catalog.instances[numberingInstanceId];
19308
+ if (!instance) return;
19309
+ catalog.instances[numberingInstanceId] = {
19310
+ ...instance,
19311
+ overrides: mergeOverride(instance.overrides, { level, startAt })
19312
+ };
19313
+ }
19314
+ function mergeOverride(overrides, nextOverride) {
19315
+ return [
19316
+ ...overrides.filter((override) => override.level !== nextOverride.level),
19317
+ structuredClone(nextOverride)
19318
+ ].sort((left, right) => left.level - right.level);
19319
+ }
19320
+ function cloneOverrides(overrides) {
19321
+ return overrides.map((override) => structuredClone(override));
19322
+ }
19323
+ function defaultLevels(kind) {
19324
+ if (kind === "bulleted") {
19325
+ return [
19326
+ { level: 0, format: "bullet", text: "\u2022" },
19327
+ { level: 1, format: "bullet", text: "o" },
19328
+ { level: 2, format: "bullet", text: "\u25A0" },
19329
+ { level: 3, format: "bullet", text: "\u2022" },
19330
+ { level: 4, format: "bullet", text: "o" },
19331
+ { level: 5, format: "bullet", text: "\u25A0" },
19332
+ { level: 6, format: "bullet", text: "\u2022" },
19333
+ { level: 7, format: "bullet", text: "o" },
19334
+ { level: 8, format: "bullet", text: "\u25A0" }
19335
+ ];
19336
+ }
19337
+ return [
19338
+ { level: 0, format: "decimal", text: "%1." },
19339
+ { level: 1, format: "decimal", text: "%1.%2." },
19340
+ { level: 2, format: "lowerLetter", text: "(%3)" },
19341
+ { level: 3, format: "lowerRoman", text: "(%4)" },
19342
+ { level: 4, format: "decimal", text: "%5." },
19343
+ { level: 5, format: "lowerLetter", text: "(%6)" },
19344
+ { level: 6, format: "lowerRoman", text: "(%7)" },
19345
+ { level: 7, format: "decimal", text: "%8." },
19346
+ { level: 8, format: "lowerLetter", text: "(%9)" }
19347
+ ];
19348
+ }
19349
+ function nextCanonicalNumericId(prefix, ids) {
19350
+ return `${prefix}${nextNumericString(ids.map((id) => stripPrefix(id, prefix)))}`;
19351
+ }
19352
+ function nextNumericString(ids) {
19353
+ const used = new Set(ids);
19354
+ let next = 1;
19355
+ for (const id of ids) {
19356
+ const parsed = Number.parseInt(id, 10);
19357
+ if (Number.isFinite(parsed) && parsed >= next) next = parsed + 1;
19358
+ }
19359
+ while (used.has(String(next))) next += 1;
19360
+ return String(next);
19361
+ }
19362
+ function stripPrefix(value, prefix) {
19363
+ return value.startsWith(prefix) ? value.slice(prefix.length) : value;
19364
+ }
19365
+ function freshLongHex(catalog, abstractNumberingId, salt) {
19366
+ const existing = new Set(
19367
+ Object.values(catalog.abstractDefinitions).map((definition) => salt === "nsid" ? definition.nsid : definition.tplc).filter((value) => Boolean(value))
19368
+ );
19369
+ let attempt = 0;
19370
+ while (attempt < 1e3) {
19371
+ const candidate = hashLongHex(`${salt}:${abstractNumberingId}:${attempt}`);
19372
+ if (!existing.has(candidate)) return candidate;
19373
+ attempt += 1;
19374
+ }
19375
+ return hashLongHex(`${salt}:${abstractNumberingId}:${Date.now()}`);
19376
+ }
19377
+ function hashLongHex(input) {
19378
+ let hash = 2166136261;
19379
+ for (let index = 0; index < input.length; index += 1) {
19380
+ hash ^= input.charCodeAt(index);
19381
+ hash = Math.imul(hash, 16777619) >>> 0;
19382
+ }
19383
+ return hash.toString(16).toUpperCase().padStart(8, "0").slice(-8);
19384
+ }
19385
+
18923
19386
  // src/core/commands/list-commands.ts
18924
19387
  function toggleNumberedList(document2, paragraphIndexes, context, options = {}) {
18925
19388
  return toggleListKind(document2, paragraphIndexes, "numbered", context, options);
@@ -18964,7 +19427,7 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
18964
19427
  affectedParagraphIndexes: []
18965
19428
  };
18966
19429
  }
18967
- const catalog = ensureNumberingCatalog(working.numbering);
19430
+ const catalog = cloneNumberingCatalog(working.numbering);
18968
19431
  const existingInstance = catalog.instances[target.numbering.numberingInstanceId];
18969
19432
  if (!existingInstance) {
18970
19433
  return {
@@ -18972,15 +19435,12 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
18972
19435
  affectedParagraphIndexes: []
18973
19436
  };
18974
19437
  }
18975
- const numberingInstanceId = createNumberingInstanceId(catalog);
18976
- catalog.instances[numberingInstanceId] = {
18977
- numberingInstanceId,
18978
- abstractNumberingId: existingInstance.abstractNumberingId,
18979
- overrides: mergeOverride(existingInstance.overrides, {
18980
- level: target.numbering.level,
18981
- startAt
18982
- })
18983
- };
19438
+ const numberingInstanceId = allocateNumberingInstance(
19439
+ catalog,
19440
+ existingInstance.abstractNumberingId,
19441
+ existingInstance.overrides
19442
+ );
19443
+ setStartOverride(catalog, numberingInstanceId, target.numbering.level, startAt);
18984
19444
  const affectedParagraphIndexes = [];
18985
19445
  for (let index = resolvedParagraphIndex; index < paragraphs.length; index += 1) {
18986
19446
  const paragraph = paragraphs[index];
@@ -19034,7 +19494,7 @@ function continueNumbering(document2, paragraphIndex, context, options = {}) {
19034
19494
  affectedParagraphIndexes: []
19035
19495
  };
19036
19496
  }
19037
- const catalog = ensureNumberingCatalog(working.numbering);
19497
+ const catalog = cloneNumberingCatalog(working.numbering);
19038
19498
  const currentInstanceId = target.numbering.numberingInstanceId;
19039
19499
  const currentKind = getListKind(catalog, currentInstanceId);
19040
19500
  if (!currentKind) {
@@ -19101,7 +19561,7 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
19101
19561
  affectedParagraphIndexes: []
19102
19562
  };
19103
19563
  }
19104
- const catalog = ensureNumberingCatalog(working.numbering);
19564
+ const catalog = cloneNumberingCatalog(working.numbering);
19105
19565
  const allAlreadyKind = normalizedIndexes.every((index) => {
19106
19566
  const paragraph = paragraphs[index];
19107
19567
  return paragraph.numbering ? getListKind(catalog, paragraph.numbering.numberingInstanceId) === kind : false;
@@ -19116,7 +19576,7 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
19116
19576
  affectedParagraphIndexes: normalizedIndexes
19117
19577
  };
19118
19578
  }
19119
- const numberingInstanceId = findAdjacentCompatibleInstance(paragraphs, catalog, normalizedIndexes[0], kind) ?? ensureDefaultInstance(catalog, kind);
19579
+ const numberingInstanceId = findAdjacentCompatibleInstance(paragraphs, catalog, normalizedIndexes[0], kind) ?? ensureDefaultListInstance(catalog, kind);
19120
19580
  for (const index of normalizedIndexes) {
19121
19581
  const paragraph = paragraphs[index];
19122
19582
  paragraph.numbering = {
@@ -19281,23 +19741,6 @@ function sortJson(value) {
19281
19741
  }
19282
19742
  return value;
19283
19743
  }
19284
- function ensureDefaultInstance(catalog, kind) {
19285
- const existing = Object.values(catalog.instances).find(
19286
- (instance) => getListKind(catalog, instance.numberingInstanceId) === kind
19287
- );
19288
- if (existing) {
19289
- return existing.numberingInstanceId;
19290
- }
19291
- const abstractNumberingId = createAbstractNumberingId(catalog);
19292
- catalog.abstractDefinitions[abstractNumberingId] = kind === "bulleted" ? createDefaultBulletedDefinition(abstractNumberingId) : createDefaultNumberedDefinition(abstractNumberingId);
19293
- const numberingInstanceId = createNumberingInstanceId(catalog);
19294
- catalog.instances[numberingInstanceId] = {
19295
- numberingInstanceId,
19296
- abstractNumberingId,
19297
- overrides: []
19298
- };
19299
- return numberingInstanceId;
19300
- }
19301
19744
  function findAdjacentCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
19302
19745
  const previous = paragraphs[fromIndex - 1];
19303
19746
  if (previous?.numbering) {
@@ -19320,69 +19763,6 @@ function findPreviousCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
19320
19763
  }
19321
19764
  return void 0;
19322
19765
  }
19323
- function getListKind(catalog, numberingInstanceId) {
19324
- const instance = catalog.instances[numberingInstanceId];
19325
- const definition = instance ? catalog.abstractDefinitions[instance.abstractNumberingId] : void 0;
19326
- const levelZero = definition?.levels.find((level) => level.level === 0);
19327
- if (!levelZero) {
19328
- return void 0;
19329
- }
19330
- return levelZero.format === "bullet" ? "bulleted" : "numbered";
19331
- }
19332
- function mergeOverride(overrides, nextOverride) {
19333
- const filtered = overrides.filter((override) => override.level !== nextOverride.level);
19334
- filtered.push(nextOverride);
19335
- return filtered.sort((left, right) => left.level - right.level);
19336
- }
19337
- function createDefaultNumberedDefinition(abstractNumberingId) {
19338
- return {
19339
- abstractNumberingId,
19340
- levels: [
19341
- { level: 0, format: "decimal", text: "%1." },
19342
- { level: 1, format: "decimal", text: "%1.%2." },
19343
- { level: 2, format: "lowerLetter", text: "(%3)" },
19344
- { level: 3, format: "lowerRoman", text: "(%4)" },
19345
- { level: 4, format: "decimal", text: "%5." },
19346
- { level: 5, format: "lowerLetter", text: "(%6)" },
19347
- { level: 6, format: "lowerRoman", text: "(%7)" },
19348
- { level: 7, format: "decimal", text: "%8." },
19349
- { level: 8, format: "lowerLetter", text: "(%9)" }
19350
- ]
19351
- };
19352
- }
19353
- function createDefaultBulletedDefinition(abstractNumberingId) {
19354
- return {
19355
- abstractNumberingId,
19356
- levels: [
19357
- { level: 0, format: "bullet", text: "\u2022" },
19358
- { level: 1, format: "bullet", text: "o" },
19359
- { level: 2, format: "bullet", text: "\u25A0" },
19360
- { level: 3, format: "bullet", text: "\u2022" },
19361
- { level: 4, format: "bullet", text: "o" },
19362
- { level: 5, format: "bullet", text: "\u25A0" },
19363
- { level: 6, format: "bullet", text: "\u2022" },
19364
- { level: 7, format: "bullet", text: "o" },
19365
- { level: 8, format: "bullet", text: "\u25A0" }
19366
- ]
19367
- };
19368
- }
19369
- function createAbstractNumberingId(catalog) {
19370
- let index = 1;
19371
- while (catalog.abstractDefinitions[`abstract-num:generated-${index}`]) {
19372
- index += 1;
19373
- }
19374
- return `abstract-num:generated-${index}`;
19375
- }
19376
- function createNumberingInstanceId(catalog) {
19377
- let index = 1;
19378
- while (catalog.instances[`num:generated-${index}`]) {
19379
- index += 1;
19380
- }
19381
- return `num:generated-${index}`;
19382
- }
19383
- function ensureNumberingCatalog(value) {
19384
- return structuredClone(value);
19385
- }
19386
19766
  function cloneEnvelope(document2, timestamp) {
19387
19767
  return {
19388
19768
  ...structuredClone(document2),
@@ -20108,7 +20488,7 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20108
20488
  );
20109
20489
  workingDocument = collapseResult.document;
20110
20490
  workingSelection = collapseResult.selection;
20111
- if (context.textTarget?.kind === "table-paragraph") {
20491
+ if (context.textTarget?.kind === "table-paragraph" || context.textTarget?.kind === "text-leaf") {
20112
20492
  workingContext = {
20113
20493
  ...context,
20114
20494
  textTarget: {
@@ -20130,11 +20510,11 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20130
20510
  selection
20131
20511
  };
20132
20512
  }
20133
- const targetedTablePath = workingContext.textTarget?.kind === "table-paragraph" ? workingContext.textTarget.blockPath : void 0;
20134
- if (targetedTablePath) {
20513
+ const targetedBlockPath = workingContext.textTarget?.kind === "table-paragraph" || workingContext.textTarget?.kind === "text-leaf" ? workingContext.textTarget.blockPath : void 0;
20514
+ if (targetedBlockPath) {
20135
20515
  const targeted = insertFragmentBlocksAfterPath(
20136
20516
  splitResult.document,
20137
- targetedTablePath,
20517
+ targetedBlockPath,
20138
20518
  fragment.blocks
20139
20519
  );
20140
20520
  if (targeted) {
@@ -25010,7 +25390,7 @@ function buildFormattingDebugEntry(inputs) {
25010
25390
  }
25011
25391
 
25012
25392
  // src/runtime/layout/layout-engine-version.ts
25013
- var LAYOUT_ENGINE_VERSION = 88;
25393
+ var LAYOUT_ENGINE_VERSION = 93;
25014
25394
 
25015
25395
  // src/runtime/layout/compat-input-ledger.ts
25016
25396
  var DEFAULT_COMPATIBILITY_MODE = 15;
@@ -28046,6 +28426,76 @@ function diffRenderFrames(prev, next) {
28046
28426
  }
28047
28427
  return { addedPages, removedPages, unchangedPages, changedPages };
28048
28428
  }
28429
+ function createPagePatchPlan(prev, next, diff = diffRenderFrames(prev, next), options = {}) {
28430
+ const prevByIndex = indexPagesByIndex(prev?.pages ?? []);
28431
+ const nextByIndex = indexPagesByIndex(next.pages);
28432
+ const addedPages = diff.addedPages.map(
28433
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
28434
+ );
28435
+ const removedPages = diff.removedPages.map(
28436
+ (pageIndex) => pageIdForIndex(prevByIndex, pageIndex)
28437
+ );
28438
+ const unchangedPages = diff.unchangedPages.map(
28439
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
28440
+ );
28441
+ const changedPages = diff.changedPages.map(
28442
+ (entry) => createPagePatchEntry(entry, pageIdForIndex(nextByIndex, entry.pageIndex), options)
28443
+ );
28444
+ return {
28445
+ layoutRevision: options.layoutRevision ?? next.revision,
28446
+ geometryRevision: options.geometryRevision ?? next.revision,
28447
+ addedPages,
28448
+ removedPages,
28449
+ unchangedPages,
28450
+ changedPages,
28451
+ mountChanges: resolveMountChanges({
28452
+ addedPages,
28453
+ removedPages,
28454
+ mountedPageIds: options.mountedPageIds,
28455
+ requestedMountPageIds: options.requestedMountPageIds
28456
+ })
28457
+ };
28458
+ }
28459
+ function createPagePatchEntry(entry, pageId, options) {
28460
+ const regionChanges = entry.regions.map((region) => region.kind);
28461
+ const fragmentChanges = uniqueStrings(
28462
+ entry.regions.flatMap((region) => region.changedBlockIds)
28463
+ );
28464
+ const overlayLaneChanges = !entry.pageFrameChanged && entry.regions.length === 0 ? ["chrome-reservations"] : [];
28465
+ return {
28466
+ pageId,
28467
+ pageIndex: entry.pageIndex,
28468
+ frameChanged: entry.pageFrameChanged === true,
28469
+ regionChanges,
28470
+ fragmentChanges,
28471
+ lineBoxChanges: fragmentChanges,
28472
+ overlayLaneChanges,
28473
+ reason: options.reason ?? (overlayLaneChanges.length > 0 ? "overlay" : "layout")
28474
+ };
28475
+ }
28476
+ function resolveMountChanges(input) {
28477
+ if (!input.mountedPageIds || !input.requestedMountPageIds) {
28478
+ return {
28479
+ mountPageIds: input.addedPages,
28480
+ unmountPageIds: input.removedPages
28481
+ };
28482
+ }
28483
+ const mounted = new Set(input.mountedPageIds);
28484
+ const requested = new Set(input.requestedMountPageIds);
28485
+ return {
28486
+ mountPageIds: input.requestedMountPageIds.filter((pageId) => !mounted.has(pageId)),
28487
+ unmountPageIds: input.mountedPageIds.filter((pageId) => !requested.has(pageId))
28488
+ };
28489
+ }
28490
+ function indexPagesByIndex(pages) {
28491
+ return new Map(pages.map((page) => [page.page.pageIndex, page]));
28492
+ }
28493
+ function pageIdForIndex(pages, pageIndex) {
28494
+ return pages.get(pageIndex)?.page.pageId ?? `page:${pageIndex}`;
28495
+ }
28496
+ function uniqueStrings(values) {
28497
+ return [...new Set(values)];
28498
+ }
28049
28499
  function diffPage(prev, next, prevIndex, nextIndex) {
28050
28500
  const changed = [];
28051
28501
  const bodyChanges = diffRegion(prev.regions.body, next.regions.body, prevIndex, nextIndex);
@@ -28293,10 +28743,12 @@ function createRenderKernel(input) {
28293
28743
  emit({ kind: "frame_built", revision: frame.revision, reason: "full" });
28294
28744
  const diffT0 = typeof performance !== "undefined" ? performance.now() : 0;
28295
28745
  const diff = diffRenderFrames(lastEmittedFrame, frame);
28746
+ const patchPlan = createPagePatchPlan(lastEmittedFrame, frame, diff);
28747
+ recordPagePatchPlanTelemetry(patchPlan);
28296
28748
  if (diffT0 > 0) {
28297
28749
  recordPerfSample("render.frame_diff", performance.now() - diffT0);
28298
28750
  }
28299
- emit({ kind: "frame_diff", revision: frame.revision, diff });
28751
+ emit({ kind: "frame_diff", revision: frame.revision, diff, patchPlan });
28300
28752
  lastEmittedFrame = frame;
28301
28753
  }
28302
28754
  return frame;
@@ -28319,6 +28771,35 @@ function createRenderKernel(input) {
28319
28771
  }
28320
28772
  };
28321
28773
  }
28774
+ function recordPagePatchPlanTelemetry(plan) {
28775
+ incrementInvalidationCounter(
28776
+ "pageRender.patch.addedPages",
28777
+ plan.addedPages.length
28778
+ );
28779
+ incrementInvalidationCounter(
28780
+ "pageRender.patch.removedPages",
28781
+ plan.removedPages.length
28782
+ );
28783
+ incrementInvalidationCounter(
28784
+ "pageRender.patch.unchangedPages",
28785
+ plan.unchangedPages.length
28786
+ );
28787
+ incrementInvalidationCounter(
28788
+ "pageRender.patch.changedFragments",
28789
+ plan.changedPages.reduce(
28790
+ (total, page) => total + page.fragmentChanges.length,
28791
+ 0
28792
+ )
28793
+ );
28794
+ incrementInvalidationCounter(
28795
+ "pageRender.patch.mountedPages",
28796
+ plan.mountChanges.mountPageIds.length
28797
+ );
28798
+ incrementInvalidationCounter(
28799
+ "pageRender.patch.unmountedPages",
28800
+ plan.mountChanges.unmountPageIds.length
28801
+ );
28802
+ }
28322
28803
  function buildPage(page, topPx, zoom, activeStory, facet) {
28323
28804
  const layout = page.layout;
28324
28805
  const widthPx = layout.pageWidth * zoom.pxPerTwip;
@@ -29695,7 +30176,7 @@ function emptyCompatibilityReport() {
29695
30176
  }
29696
30177
 
29697
30178
  // src/runtime/debug/runtime-debug-facet.ts
29698
- function createRuntimeDebugFacet(getRuntime, bus) {
30179
+ function createRuntimeDebugFacet(getRuntime, bus, options = {}) {
29699
30180
  return {
29700
30181
  bus,
29701
30182
  getSnapshot(query) {
@@ -29713,7 +30194,318 @@ function createRuntimeDebugFacet(getRuntime, bus) {
29713
30194
  },
29714
30195
  getChannels() {
29715
30196
  return bus.getChannels();
30197
+ },
30198
+ getHotEditTraces() {
30199
+ return options.getHotEditTraces?.() ?? [];
30200
+ }
30201
+ };
30202
+ }
30203
+
30204
+ // src/runtime/hot-edit/hot-edit-trace.ts
30205
+ var HOT_EDIT_TRACE_LIMIT = 64;
30206
+ var HotEditTraceRecorder = class {
30207
+ traces = [];
30208
+ sequence = 0;
30209
+ begin(input) {
30210
+ this.sequence += 1;
30211
+ return {
30212
+ sequence: this.sequence,
30213
+ commandType: input.commandType,
30214
+ startedAtUtc: input.nowUtc,
30215
+ startedAtMs: nowMs(),
30216
+ startCounters: input.counters.snapshot()
30217
+ };
30218
+ }
30219
+ complete(pending, input) {
30220
+ const endCounters = input.counters.snapshot();
30221
+ const refreshAllCount = counterDelta(pending.startCounters, endCounters, "refresh.all");
30222
+ const patchHitCount = counterDelta(pending.startCounters, endCounters, "surface.localText.patchHit");
30223
+ const patchBudgetFallbackCount = counterDelta(
30224
+ pending.startCounters,
30225
+ endCounters,
30226
+ "surface.localText.patchBudgetFallback"
30227
+ );
30228
+ const boundedEditableTargetBuildCount = counterDelta(
30229
+ pending.startCounters,
30230
+ endCounters,
30231
+ "runtime.editableTargets.boundedBuilds"
30232
+ );
30233
+ const tier = refineTier(input.tier, {
30234
+ refreshAllCount,
30235
+ patchHitCount,
30236
+ patchBudgetFallbackCount,
30237
+ boundedEditableTargetBuildCount
30238
+ });
30239
+ const trace = {
30240
+ sequence: pending.sequence,
30241
+ startedAtUtc: pending.startedAtUtc,
30242
+ commandType: pending.commandType,
30243
+ tier,
30244
+ patchMissReason: input.patchMissReason,
30245
+ totalDurationUs: Math.max(0, Math.round((nowMs() - pending.startedAtMs) * 1e3)),
30246
+ refreshAllCount,
30247
+ patchHitCount,
30248
+ patchBudgetFallbackCount,
30249
+ boundedEditableTargetBuildCount
30250
+ };
30251
+ this.traces.push(trace);
30252
+ if (this.traces.length > HOT_EDIT_TRACE_LIMIT) {
30253
+ this.traces.splice(0, this.traces.length - HOT_EDIT_TRACE_LIMIT);
30254
+ }
30255
+ input.counters.increment(`hotEdit.tier.${trace.tier}`);
30256
+ if (trace.patchMissReason !== "none") {
30257
+ input.counters.increment(`hotEdit.patchMiss.${trace.patchMissReason}`);
30258
+ }
30259
+ return trace;
30260
+ }
30261
+ getTraces() {
30262
+ return Object.freeze([...this.traces]);
30263
+ }
30264
+ clear() {
30265
+ this.traces.splice(0, this.traces.length);
30266
+ }
30267
+ };
30268
+ function refineTier(requested, counters) {
30269
+ if (requested === "blocked") return requested;
30270
+ if (counters.refreshAllCount > 0) return "full-refresh";
30271
+ if (counters.patchBudgetFallbackCount > 0 || counters.boundedEditableTargetBuildCount > 0) {
30272
+ return "bounded-projection";
30273
+ }
30274
+ if (counters.patchHitCount > 0) return "patch";
30275
+ return requested;
30276
+ }
30277
+ function counterDelta(start, end, key) {
30278
+ return (end[key] ?? 0) - (start[key] ?? 0);
30279
+ }
30280
+ function nowMs() {
30281
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
30282
+ }
30283
+
30284
+ // src/runtime/hot-edit/surface-local-patcher.ts
30285
+ function patchSurfaceTextSegment(surface, patch) {
30286
+ const delta = patch.insertedText.length - Math.max(0, patch.to - patch.from);
30287
+ let patched = false;
30288
+ const blocks = surface.blocks.map((block) => {
30289
+ const result = patchBlock(block, patch, delta);
30290
+ if (result.patched) patched = true;
30291
+ return result.block;
30292
+ });
30293
+ if (!patched) {
30294
+ return { status: "miss", reason: "segment-not-found" };
30295
+ }
30296
+ return {
30297
+ status: "patched",
30298
+ surface: {
30299
+ ...surface,
30300
+ storySize: surface.storySize + delta,
30301
+ plainText: surface.plainText.slice(0, patch.from) + patch.insertedText + surface.plainText.slice(patch.to),
30302
+ blocks
30303
+ }
30304
+ };
30305
+ }
30306
+ function patchBlock(block, patch, delta) {
30307
+ if (patch.to < block.from) return { block, patched: false };
30308
+ if (patch.from > block.to) {
30309
+ return { block: shiftBlock(block, delta), patched: false };
30310
+ }
30311
+ switch (block.kind) {
30312
+ case "paragraph":
30313
+ return patchParagraph(block, patch, delta);
30314
+ case "sdt_block": {
30315
+ let patched = false;
30316
+ const children = block.children.map((child) => {
30317
+ const result = patchBlock(child, patch, delta);
30318
+ patched ||= result.patched;
30319
+ return result.block;
30320
+ });
30321
+ return {
30322
+ block: patched ? { ...block, to: block.to + delta, children } : block,
30323
+ patched
30324
+ };
30325
+ }
30326
+ case "table": {
30327
+ let patched = false;
30328
+ const rows = block.rows.map((row2) => ({
30329
+ ...row2,
30330
+ cells: row2.cells.map((cell) => ({
30331
+ ...cell,
30332
+ content: cell.content.map((child) => {
30333
+ const result = patchBlock(child, patch, delta);
30334
+ patched ||= result.patched;
30335
+ return result.block;
30336
+ })
30337
+ }))
30338
+ }));
30339
+ return {
30340
+ block: patched ? { ...block, to: block.to + delta, rows } : block,
30341
+ patched
30342
+ };
30343
+ }
30344
+ case "opaque_block":
30345
+ return { block, patched: false };
30346
+ }
30347
+ }
30348
+ function patchParagraph(block, patch, delta) {
30349
+ if (patch.from < block.from || patch.to > block.to) {
30350
+ return { block, patched: false };
30351
+ }
30352
+ const segmentIndex = block.segments.findIndex(
30353
+ (segment2) => segment2.kind === "text" && patch.from >= segment2.from && patch.to <= segment2.to
30354
+ );
30355
+ if (segmentIndex < 0) return { block, patched: false };
30356
+ const segment = block.segments[segmentIndex];
30357
+ if (segment.kind !== "text") return { block, patched: false };
30358
+ const localFrom = patch.from - segment.from;
30359
+ const localTo = patch.to - segment.from;
30360
+ const segments = block.segments.map((candidate, index) => {
30361
+ if (index < segmentIndex) return candidate;
30362
+ if (index === segmentIndex) {
30363
+ return {
30364
+ ...segment,
30365
+ text: segment.text.slice(0, localFrom) + patch.insertedText + segment.text.slice(localTo),
30366
+ to: segment.to + delta
30367
+ };
30368
+ }
30369
+ return shiftInlineSegment(candidate, delta);
30370
+ });
30371
+ return {
30372
+ block: {
30373
+ ...block,
30374
+ to: block.to + delta,
30375
+ segments
30376
+ },
30377
+ patched: true
30378
+ };
30379
+ }
30380
+ function shiftBlock(block, delta) {
30381
+ if (delta === 0) return block;
30382
+ switch (block.kind) {
30383
+ case "paragraph":
30384
+ return {
30385
+ ...block,
30386
+ from: block.from + delta,
30387
+ to: block.to + delta,
30388
+ segments: block.segments.map((segment) => shiftInlineSegment(segment, delta))
30389
+ };
30390
+ case "sdt_block":
30391
+ return {
30392
+ ...block,
30393
+ from: block.from + delta,
30394
+ to: block.to + delta,
30395
+ children: block.children.map((child) => shiftBlock(child, delta))
30396
+ };
30397
+ case "table":
30398
+ return {
30399
+ ...block,
30400
+ from: block.from + delta,
30401
+ to: block.to + delta,
30402
+ rows: block.rows.map((row2) => ({
30403
+ ...row2,
30404
+ cells: row2.cells.map((cell) => ({
30405
+ ...cell,
30406
+ content: cell.content.map((child) => shiftBlock(child, delta))
30407
+ }))
30408
+ }))
30409
+ };
30410
+ case "opaque_block":
30411
+ return {
30412
+ ...block,
30413
+ from: block.from + delta,
30414
+ to: block.to + delta
30415
+ };
30416
+ }
30417
+ }
30418
+ function shiftInlineSegment(segment, delta) {
30419
+ if (delta === 0) return segment;
30420
+ return {
30421
+ ...segment,
30422
+ from: segment.from + delta,
30423
+ to: segment.to + delta
30424
+ };
30425
+ }
30426
+
30427
+ // src/runtime/hot-edit/hot-edit-profiler.ts
30428
+ var HEAVY_REVIEW_ITEM_THRESHOLD = 24;
30429
+ var DENSE_BLOCK_NODE_THRESHOLD = 256;
30430
+ function deriveHotEditDocumentProfile(document2) {
30431
+ const blocks = document2.content.children ?? [];
30432
+ let tableBlockCount = 0;
30433
+ let maxNestedSurfaceNodesPerBlock = 0;
30434
+ for (const block of blocks) {
30435
+ if (block.type === "table") tableBlockCount += 1;
30436
+ maxNestedSurfaceNodesPerBlock = Math.max(
30437
+ maxNestedSurfaceNodesPerBlock,
30438
+ countBlockNodes(block)
30439
+ );
30440
+ }
30441
+ const commentCount = Object.keys(document2.review.comments ?? {}).length;
30442
+ const revisionCount = Object.keys(document2.review.revisions ?? {}).length;
30443
+ const secondaryStoryBlockCount = countSecondaryStoryBlocks(document2);
30444
+ return {
30445
+ blockCount: blocks.length,
30446
+ tableBlockCount,
30447
+ maxNestedSurfaceNodesPerBlock,
30448
+ commentCount,
30449
+ revisionCount,
30450
+ secondaryStoryBlockCount,
30451
+ hasHeavyReview: commentCount + revisionCount >= HEAVY_REVIEW_ITEM_THRESHOLD,
30452
+ hasDenseTables: tableBlockCount > 0 && maxNestedSurfaceNodesPerBlock > DENSE_BLOCK_NODE_THRESHOLD
30453
+ };
30454
+ }
30455
+ function countBlockNodes(block) {
30456
+ const value = block;
30457
+ let total = 1;
30458
+ if (Array.isArray(value.children)) {
30459
+ total += value.children.length;
30460
+ for (const child of value.children) {
30461
+ if (isBlockLike(child)) total += countBlockNodes(child);
30462
+ }
30463
+ }
30464
+ if (Array.isArray(value.rows)) {
30465
+ for (const row2 of value.rows) {
30466
+ total += 1;
30467
+ for (const cell of row2.cells ?? []) {
30468
+ total += 1;
30469
+ for (const child of cell.children ?? []) {
30470
+ total += countBlockNodes(child);
30471
+ }
30472
+ }
29716
30473
  }
30474
+ }
30475
+ return total;
30476
+ }
30477
+ function isBlockLike(value) {
30478
+ return Boolean(value && typeof value === "object" && "type" in value);
30479
+ }
30480
+ function countSecondaryStoryBlocks(document2) {
30481
+ const subParts = document2.subParts;
30482
+ if (!subParts) return 0;
30483
+ let count = 0;
30484
+ for (const value of Object.values(subParts)) {
30485
+ count += countBlocksInUnknownStory(value);
30486
+ }
30487
+ return count;
30488
+ }
30489
+ function countBlocksInUnknownStory(value) {
30490
+ if (!value || typeof value !== "object") return 0;
30491
+ const candidate = value;
30492
+ if (Array.isArray(candidate.children)) return candidate.children.length;
30493
+ if (Array.isArray(candidate.content?.children)) return candidate.content.children.length;
30494
+ return Object.values(value).reduce((total, entry) => total + countBlocksInUnknownStory(entry), 0);
30495
+ }
30496
+
30497
+ // src/runtime/hot-edit/hot-edit-policy.ts
30498
+ var DEFAULT_MAX_SHIFTED_SURFACE_NODES = 5e3;
30499
+ var DEFAULT_MAX_SHIFTED_NODES_PER_BLOCK = 256;
30500
+ var DEFAULT_SELECTION_CORRIDOR_BLOCK_RADIUS = 8;
30501
+ var HEAVY_DOCUMENT_SELECTION_CORRIDOR_BLOCK_RADIUS = 12;
30502
+ function resolveHotEditPolicy(profile) {
30503
+ const complexGeometry = profile.hasDenseTables || profile.secondaryStoryBlockCount > 0;
30504
+ return {
30505
+ maxShiftedSurfaceNodes: DEFAULT_MAX_SHIFTED_SURFACE_NODES,
30506
+ maxShiftedNodesPerBlock: DEFAULT_MAX_SHIFTED_NODES_PER_BLOCK,
30507
+ selectionCorridorBlockRadius: complexGeometry ? HEAVY_DOCUMENT_SELECTION_CORRIDOR_BLOCK_RADIUS : DEFAULT_SELECTION_CORRIDOR_BLOCK_RADIUS,
30508
+ preferBoundedProjectionForDenseTails: profile.hasDenseTables
29717
30509
  };
29718
30510
  }
29719
30511
 
@@ -30857,26 +31649,35 @@ function buildParagraphPropertiesXml(paragraph) {
30857
31649
  if (frameXml) children.push(frameXml);
30858
31650
  }
30859
31651
  pushOnOffParagraphProperty(children, "widowControl", paragraph.widowControl);
30860
- if (paragraph.outlineLevel !== void 0) {
30861
- children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
30862
- }
30863
31652
  if (paragraph.numbering) {
30864
31653
  children.push(serializeParagraphNumberingProperties(paragraph.numbering));
30865
31654
  }
31655
+ pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
31656
+ if (paragraph.borders) {
31657
+ const bordersXml = serializeParagraphBorders(paragraph.borders);
31658
+ if (bordersXml) children.push(bordersXml);
31659
+ }
31660
+ if (paragraph.shading) {
31661
+ const shadingXml = serializeParagraphShading(paragraph.shading);
31662
+ if (shadingXml) children.push(shadingXml);
31663
+ }
31664
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
31665
+ const tabsXml = paragraph.tabStops.map((tab) => {
31666
+ const leaderAttr = tab.leader ? ` w:leader="${escapeXmlAttribute2(tab.leader)}"` : "";
31667
+ return `<w:tab w:val="${escapeXmlAttribute2(tab.align)}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
31668
+ }).join("");
31669
+ children.push(`<w:tabs>${tabsXml}</w:tabs>`);
31670
+ }
31671
+ pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
30866
31672
  if (paragraph.spacing) {
30867
31673
  const s = paragraph.spacing;
30868
31674
  const attrs = [];
30869
31675
  if (s.before !== void 0) attrs.push(`w:before="${twip(s.before)}"`);
30870
31676
  if (s.after !== void 0) attrs.push(`w:after="${twip(s.after)}"`);
30871
31677
  if (s.line !== void 0) attrs.push(`w:line="${twip(s.line)}"`);
30872
- if (s.lineRule !== void 0) attrs.push(`w:lineRule="${s.lineRule}"`);
31678
+ if (s.lineRule !== void 0) attrs.push(`w:lineRule="${escapeXmlAttribute2(s.lineRule)}"`);
30873
31679
  if (attrs.length > 0) children.push(`<w:spacing ${attrs.join(" ")}/>`);
30874
31680
  }
30875
- if (paragraph.contextualSpacing === true) {
30876
- children.push("<w:contextualSpacing/>");
30877
- } else if (paragraph.contextualSpacing === false) {
30878
- children.push(`<w:contextualSpacing w:val="false"/>`);
30879
- }
30880
31681
  if (paragraph.indentation) {
30881
31682
  const ind = paragraph.indentation;
30882
31683
  const attrs = [];
@@ -30886,33 +31687,20 @@ function buildParagraphPropertiesXml(paragraph) {
30886
31687
  if (ind.hanging !== void 0) attrs.push(`w:hanging="${twip(ind.hanging)}"`);
30887
31688
  if (attrs.length > 0) children.push(`<w:ind ${attrs.join(" ")}/>`);
30888
31689
  }
30889
- if (paragraph.alignment) {
30890
- children.push(`<w:jc w:val="${paragraph.alignment}"/>`);
31690
+ if (paragraph.contextualSpacing === true) {
31691
+ children.push("<w:contextualSpacing/>");
31692
+ } else if (paragraph.contextualSpacing === false) {
31693
+ children.push(`<w:contextualSpacing w:val="false"/>`);
30891
31694
  }
30892
- if (paragraph.borders) {
30893
- const bordersXml = serializeParagraphBorders(paragraph.borders);
30894
- if (bordersXml) {
30895
- children.push(bordersXml);
30896
- }
31695
+ if (paragraph.alignment) {
31696
+ children.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
30897
31697
  }
30898
- if (paragraph.shading) {
30899
- const shadingXml = serializeParagraphShading(paragraph.shading);
30900
- if (shadingXml) {
30901
- children.push(shadingXml);
30902
- }
31698
+ if (paragraph.outlineLevel !== void 0) {
31699
+ children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
30903
31700
  }
30904
- pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
30905
- pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
30906
31701
  if (paragraph.cnfStyle) {
30907
31702
  children.push(`<w:cnfStyle w:val="${escapeXmlAttribute2(paragraph.cnfStyle)}"/>`);
30908
31703
  }
30909
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
30910
- const tabsXml = paragraph.tabStops.map((tab) => {
30911
- const leaderAttr = tab.leader ? ` w:leader="${tab.leader}"` : "";
30912
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
30913
- }).join("");
30914
- children.push(`<w:tabs>${tabsXml}</w:tabs>`);
30915
- }
30916
31704
  if (children.length === 0) return "";
30917
31705
  return `<w:pPr>${children.join("")}</w:pPr>`;
30918
31706
  }
@@ -31477,79 +32265,86 @@ function serializeRunProperties(marks) {
31477
32265
  if (!marks || marks.length === 0) {
31478
32266
  return "";
31479
32267
  }
32268
+ const orderedTypes = [
32269
+ "fontFamily",
32270
+ "bold",
32271
+ "italic",
32272
+ "allCaps",
32273
+ "smallCaps",
32274
+ "strikethrough",
32275
+ "doubleStrikethrough",
32276
+ "shadow",
32277
+ "emboss",
32278
+ "imprint",
32279
+ "vanish",
32280
+ "textColor",
32281
+ "charSpacing",
32282
+ "kerning",
32283
+ "position",
32284
+ "fontSize",
32285
+ "highlight",
32286
+ "underline",
32287
+ "backgroundColor",
32288
+ "lang",
32289
+ "textFill"
32290
+ ];
31480
32291
  const markParts = [];
31481
- for (const mark of marks) {
31482
- switch (mark.type) {
31483
- case "bold":
31484
- markParts.push("<w:b/>");
31485
- break;
31486
- case "italic":
31487
- markParts.push("<w:i/>");
31488
- break;
31489
- case "underline":
31490
- markParts.push(`<w:u w:val="single"/>`);
31491
- break;
31492
- case "strikethrough":
31493
- markParts.push("<w:strike/>");
31494
- break;
31495
- case "doubleStrikethrough":
31496
- markParts.push("<w:dstrike/>");
31497
- break;
31498
- case "vanish":
31499
- markParts.push("<w:vanish/>");
31500
- break;
31501
- case "lang":
31502
- markParts.push(`<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`);
31503
- break;
31504
- case "highlight":
31505
- markParts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
31506
- break;
31507
- case "backgroundColor":
31508
- markParts.push(
31509
- `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`
31510
- );
31511
- break;
31512
- case "charSpacing":
31513
- markParts.push(`<w:spacing w:val="${mark.val}"/>`);
31514
- break;
31515
- case "kerning":
31516
- markParts.push(`<w:kern w:val="${mark.val}"/>`);
31517
- break;
31518
- case "emboss":
31519
- markParts.push("<w:emboss/>");
31520
- break;
31521
- case "imprint":
31522
- markParts.push("<w:imprint/>");
31523
- break;
31524
- case "shadow":
31525
- markParts.push("<w:shadow/>");
31526
- break;
31527
- case "position":
31528
- markParts.push(`<w:position w:val="${mark.val}"/>`);
31529
- break;
31530
- case "textFill":
31531
- markParts.push(mark.xml);
31532
- break;
31533
- case "fontFamily":
31534
- markParts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
31535
- break;
31536
- case "fontSize":
31537
- markParts.push(`<w:sz w:val="${mark.val}"/>`);
31538
- break;
31539
- case "textColor":
31540
- markParts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
31541
- break;
31542
- case "smallCaps":
31543
- markParts.push("<w:smallCaps/>");
31544
- break;
31545
- case "allCaps":
31546
- markParts.push("<w:caps/>");
31547
- break;
32292
+ for (const type of orderedTypes) {
32293
+ for (const mark of marks) {
32294
+ if (mark.type !== type) continue;
32295
+ const xml = serializeRunPropertyMark(mark);
32296
+ if (xml) markParts.push(xml);
31548
32297
  }
31549
32298
  }
31550
32299
  const children = markParts.join("");
31551
32300
  return children.length > 0 ? `<w:rPr>${children}</w:rPr>` : "";
31552
32301
  }
32302
+ function serializeRunPropertyMark(mark) {
32303
+ switch (mark.type) {
32304
+ case "bold":
32305
+ return "<w:b/>";
32306
+ case "italic":
32307
+ return "<w:i/>";
32308
+ case "underline":
32309
+ return `<w:u w:val="single"/>`;
32310
+ case "strikethrough":
32311
+ return "<w:strike/>";
32312
+ case "doubleStrikethrough":
32313
+ return "<w:dstrike/>";
32314
+ case "vanish":
32315
+ return "<w:vanish/>";
32316
+ case "lang":
32317
+ return `<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`;
32318
+ case "highlight":
32319
+ return `<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`;
32320
+ case "backgroundColor":
32321
+ return `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`;
32322
+ case "charSpacing":
32323
+ return `<w:spacing w:val="${mark.val}"/>`;
32324
+ case "kerning":
32325
+ return `<w:kern w:val="${mark.val}"/>`;
32326
+ case "emboss":
32327
+ return "<w:emboss/>";
32328
+ case "imprint":
32329
+ return "<w:imprint/>";
32330
+ case "shadow":
32331
+ return "<w:shadow/>";
32332
+ case "position":
32333
+ return `<w:position w:val="${mark.val}"/>`;
32334
+ case "textFill":
32335
+ return mark.xml;
32336
+ case "fontFamily":
32337
+ return `<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`;
32338
+ case "fontSize":
32339
+ return `<w:sz w:val="${mark.val}"/>`;
32340
+ case "textColor":
32341
+ return `<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`;
32342
+ case "smallCaps":
32343
+ return "<w:smallCaps/>";
32344
+ case "allCaps":
32345
+ return "<w:caps/>";
32346
+ }
32347
+ }
31553
32348
  function requiresPreservedSpace(text) {
31554
32349
  return /^\s/.test(text) || /\s$/.test(text) || text.includes(" ");
31555
32350
  }
@@ -32943,6 +33738,12 @@ function resolveScopeRange(entry, handle, positionMap) {
32943
33738
  if (inlineRange) return inlineRange;
32944
33739
  return positionMap.blocks.get(entry.blockIndex) ?? null;
32945
33740
  }
33741
+ case "image": {
33742
+ const key = `${entry.blockIndex}:${entry.inlineIndex}`;
33743
+ const inlineRange = positionMap.inlines.get(key);
33744
+ if (inlineRange) return inlineRange;
33745
+ return positionMap.blocks.get(entry.blockIndex) ?? null;
33746
+ }
32946
33747
  case "comment-thread":
32947
33748
  return anchorToRange(entry.thread.anchor);
32948
33749
  case "revision":
@@ -33081,6 +33882,11 @@ function deriveReplaceability(kind, provenance) {
33081
33882
  level: "preserve-only",
33082
33883
  reason: "field-result-is-computed-preserve-only"
33083
33884
  };
33885
+ case "image":
33886
+ return {
33887
+ level: "preserve-only",
33888
+ reason: "image-object-command-required"
33889
+ };
33084
33890
  case "comment-thread":
33085
33891
  return {
33086
33892
  level: "preserve-only",
@@ -33785,6 +34591,55 @@ function compileHeadingScope(entry, options = {}) {
33785
34591
  };
33786
34592
  }
33787
34593
 
34594
+ // src/runtime/scopes/scope-kinds/image.ts
34595
+ function imageAltText(image) {
34596
+ if (image.type === "image") return image.altText ?? "";
34597
+ return image.anchor.docPr?.descr ?? image.anchor.docPr?.name ?? "";
34598
+ }
34599
+ function imageMediaId(image) {
34600
+ if (image.type === "image") return image.mediaId;
34601
+ return image.content.type === "picture" ? image.content.mediaId : void 0;
34602
+ }
34603
+ function imageDisplay(image) {
34604
+ if (image.type === "image") return image.display;
34605
+ return image.anchor.display;
34606
+ }
34607
+ function imageSourceKind(image) {
34608
+ return image.type === "image" ? "legacy-image" : "drawing-picture";
34609
+ }
34610
+ function compileImageScope(entry) {
34611
+ const { handle, image } = entry;
34612
+ const altText = imageAltText(image);
34613
+ const mediaId = imageMediaId(image);
34614
+ const display = imageDisplay(image);
34615
+ const sourceKind = imageSourceKind(image);
34616
+ return {
34617
+ handle,
34618
+ kind: "image",
34619
+ classifications: entry.classifications,
34620
+ content: {
34621
+ text: altText,
34622
+ excerpt: buildExcerpt(altText),
34623
+ authority: "structural-summary"
34624
+ },
34625
+ formatting: {},
34626
+ layout: display ? { flowKind: display } : {},
34627
+ geometry: {},
34628
+ workflow: { scopeIds: [], effectiveMode: "edit" },
34629
+ replaceability: deriveReplaceability("image", handle.provenance),
34630
+ audit: {
34631
+ source: "runtime",
34632
+ derivedFrom: [
34633
+ "canonical",
34634
+ `image-source:${sourceKind}`,
34635
+ ...mediaId ? [`media-id:${mediaId}`] : []
34636
+ ],
34637
+ confidence: "medium"
34638
+ },
34639
+ partial: true
34640
+ };
34641
+ }
34642
+
33788
34643
  // src/runtime/scopes/scope-kinds/list-item.ts
33789
34644
  function compileListItemScope(entry, options = {}) {
33790
34645
  const { handle, paragraph } = entry;
@@ -34373,6 +35228,8 @@ function compileScope(entry, optionsOrCatalog) {
34373
35228
  });
34374
35229
  case "field":
34375
35230
  return compileFieldScope(entry);
35231
+ case "image":
35232
+ return compileImageScope(entry);
34376
35233
  case "comment-thread":
34377
35234
  return compileCommentThreadScope(entry);
34378
35235
  case "revision":
@@ -34561,6 +35418,44 @@ function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentSco
34561
35418
  }
34562
35419
  return out;
34563
35420
  }
35421
+ function isImageInline(child) {
35422
+ return child.type === "image" || child.type === "drawing_frame" && child.content.type === "picture";
35423
+ }
35424
+ function enumerateImagesInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
35425
+ const out = [];
35426
+ for (let i = 0; i < paragraph.children.length; i += 1) {
35427
+ const child = paragraph.children[i];
35428
+ if (!isImageInline(child)) continue;
35429
+ const semanticPath = [
35430
+ "body",
35431
+ "paragraph",
35432
+ String(blockIndex),
35433
+ "image",
35434
+ String(i)
35435
+ ];
35436
+ const scopeId = `image:${blockIndex}:${i}`;
35437
+ const handle = {
35438
+ scopeId,
35439
+ documentId,
35440
+ storyTarget: MAIN_STORY2,
35441
+ semanticPath,
35442
+ parentScopeId,
35443
+ stableRef: { kind: "semantic-path", value: semanticPath.join("/") },
35444
+ provenance: "derived",
35445
+ rangePrecision: "canonical"
35446
+ };
35447
+ out.push({
35448
+ kind: "image",
35449
+ handle,
35450
+ image: child,
35451
+ paragraph,
35452
+ blockIndex,
35453
+ inlineIndex: i,
35454
+ classifications: Object.freeze([])
35455
+ });
35456
+ }
35457
+ return out;
35458
+ }
34564
35459
  function enumerateCommentThreads(document2, documentId) {
34565
35460
  const review = document2.review;
34566
35461
  const comments = review?.comments;
@@ -34691,6 +35586,8 @@ function enumerateScopes(document2, inputs = {}) {
34691
35586
  });
34692
35587
  const fields = enumerateFieldsInParagraph(block, index, documentId, scopeId);
34693
35588
  for (const field of fields) results.push(field);
35589
+ const images = enumerateImagesInParagraph(block, index, documentId, scopeId);
35590
+ for (const image of images) results.push(image);
34694
35591
  continue;
34695
35592
  }
34696
35593
  if (block.type === "table") {
@@ -35080,6 +35977,15 @@ var LIST_TEXT_TARGET_KINDS = /* @__PURE__ */ new Set([
35080
35977
  "sdt-paragraph-text",
35081
35978
  "secondary-story-paragraph-text"
35082
35979
  ]);
35980
+ var OBJECT_COMMAND_INTENTS = /* @__PURE__ */ new Set([
35981
+ "image-layout",
35982
+ "image-frame",
35983
+ "chart-edit",
35984
+ "custom-xml-update",
35985
+ "embedded-content-update",
35986
+ "opaque-content-preserve",
35987
+ "object-edit"
35988
+ ]);
35083
35989
  function freezeList(values) {
35084
35990
  return Object.freeze([...values]);
35085
35991
  }
@@ -35624,6 +36530,35 @@ function listStructureCapability(scope, context) {
35624
36530
  ]
35625
36531
  );
35626
36532
  }
36533
+ function objectEditCapability(context) {
36534
+ const objectTargets = (context?.editableTargets?.entries ?? []).filter(
36535
+ (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))
36536
+ );
36537
+ const supportedTargets = objectTargets.filter(
36538
+ (entry) => entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.some((intent) => OBJECT_COMMAND_INTENTS.has(intent))
36539
+ );
36540
+ if (supportedTargets.length > 0) {
36541
+ return supportedCommand(
36542
+ "compile-supported:object-edit:editable-target",
36543
+ supportedTargets
36544
+ );
36545
+ }
36546
+ if (objectTargets.length > 0) {
36547
+ const blockers = commandTargetBlockers(objectTargets);
36548
+ return blocked(
36549
+ "compile-blocked:object-edit:target-ref-blocked",
36550
+ blockers.length > 0 ? blockers : ["compile-blocked:object-edit:target-ref-blocked"]
36551
+ );
36552
+ }
36553
+ return unsupported(
36554
+ "compile-unsupported:object-edit:no-target-family",
36555
+ [
36556
+ "compile-unsupported:object-edit:no-target-family",
36557
+ "capability:object-edit:l02-object-target-required",
36558
+ "capability:object-edit:l07-command-support-required"
36559
+ ]
36560
+ );
36561
+ }
35627
36562
  function deriveScopeCapabilities(scope, context = {}) {
35628
36563
  return {
35629
36564
  canReplaceText: replaceTextCapability(scope, context),
@@ -35640,7 +36575,8 @@ function deriveScopeCapabilities(scope, context = {}) {
35640
36575
  canEditTableStructure: tableStructureCapability(scope, context),
35641
36576
  canUseTableContinuationEvidence: tableContinuationEvidenceCapability(scope, context),
35642
36577
  canEditListText: listTextCapability(scope, context),
35643
- canEditListStructure: listStructureCapability(scope, context)
36578
+ canEditListStructure: listStructureCapability(scope, context),
36579
+ canEditObject: objectEditCapability(context)
35644
36580
  };
35645
36581
  }
35646
36582
 
@@ -35912,6 +36848,11 @@ function tokensForScope(entry) {
35912
36848
  { kind: "row", index: entry.rowIndex },
35913
36849
  { kind: "cell", index: entry.cellIndex }
35914
36850
  ]);
36851
+ case "image":
36852
+ return Object.freeze([
36853
+ { kind: "block", index: entry.blockIndex },
36854
+ { kind: "inline", index: entry.inlineIndex }
36855
+ ]);
35915
36856
  default:
35916
36857
  return null;
35917
36858
  }
@@ -35995,6 +36936,19 @@ function paragraphHasBookmarkId(paragraph, bookmarkId) {
35995
36936
  function commandActionHandleForAddress(commandFamily, address) {
35996
36937
  return address ? `scope-command:${commandFamily}:${address.addressKey}` : void 0;
35997
36938
  }
36939
+ function layoutIdentityIndexes(document2) {
36940
+ const byTargetKey = /* @__PURE__ */ new Map();
36941
+ const byStoryBlockPath = /* @__PURE__ */ new Map();
36942
+ for (const identity of collectLayoutInputIdentities(document2)) {
36943
+ byStoryBlockPath.set(`${identity.storyKey}:${identity.blockPath}`, identity);
36944
+ const targetKey = identity.editableTargetRef?.targetKey;
36945
+ if (targetKey) byTargetKey.set(targetKey, identity);
36946
+ }
36947
+ return { byTargetKey, byStoryBlockPath };
36948
+ }
36949
+ function layoutIdentityForTarget(target, indexes) {
36950
+ return indexes.byTargetKey.get(target.targetKey) ?? indexes.byStoryBlockPath.get(`${target.storyKey}:${target.blockPath}`);
36951
+ }
35998
36952
  function withCommandAction(evidence, target, canonicalAddress = target.canonicalAddress) {
35999
36953
  if (evidence.status !== "supported" || !canonicalAddress) return evidence;
36000
36954
  return {
@@ -36006,6 +36960,24 @@ function withCommandAction(evidence, target, canonicalAddress = target.canonical
36006
36960
  function commandAddressForTarget(target, scopeKind) {
36007
36961
  return scopeKind === "list-item" && target.listAddress !== void 0 ? target.listAddress : target.canonicalAddress;
36008
36962
  }
36963
+ function freezeCommandIntents(intents) {
36964
+ return Object.freeze([...new Set(intents)]);
36965
+ }
36966
+ function relatedListActionHandlesForTarget(target, primaryAddress) {
36967
+ if (target.commandFamily !== "text-leaf" || target.listAddress === void 0 || !LIST_TEXT_TARGET_KINDS2.has(target.kind) || target.listAddress.addressKey === primaryAddress?.addressKey) {
36968
+ return void 0;
36969
+ }
36970
+ const actionHandle = commandActionHandleForAddress(target.commandFamily, target.listAddress);
36971
+ if (!actionHandle) return void 0;
36972
+ return Object.freeze([
36973
+ {
36974
+ actionHandle,
36975
+ canonicalAddress: target.listAddress,
36976
+ intents: freezeCommandIntents(["list-text-edit", "list-structure-action"]),
36977
+ reason: "l07:list-address-target-supported"
36978
+ }
36979
+ ]);
36980
+ }
36009
36981
  function runtimeTextCommandEvidence(target, workflowBlockers) {
36010
36982
  const shapeIssues = validateEditableTargetRef(target);
36011
36983
  if (shapeIssues.length > 0) {
@@ -36105,13 +37077,16 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand, scopeKind
36105
37077
  };
36106
37078
  }
36107
37079
  if (target.commandFamily === "text-leaf") {
37080
+ const canonicalAddress = commandAddressForTarget(target, scopeKind);
37081
+ const relatedActionHandles = relatedListActionHandlesForTarget(target, canonicalAddress);
36108
37082
  return withCommandAction({
36109
37083
  status: textCommand.status,
36110
37084
  commandFamily: target.commandFamily,
36111
37085
  intents: commandIntentsForTarget(target, scopeKind),
36112
37086
  reason: textCommand.reason,
37087
+ ...relatedActionHandles !== void 0 ? { relatedActionHandles } : {},
36113
37088
  ...textCommand.blockers ? { blockers: textCommand.blockers } : {}
36114
- }, target, commandAddressForTarget(target, scopeKind));
37089
+ }, target, canonicalAddress);
36115
37090
  }
36116
37091
  if (target.commandFamily === "comment-revision") {
36117
37092
  const isOpen = target.review?.status === "open";
@@ -36436,19 +37411,21 @@ function commandIntentsForTarget(target, scopeKind) {
36436
37411
  if (target.kind === "hyperlink-text") {
36437
37412
  return Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]);
36438
37413
  }
36439
- if (target.table?.operationScope === "text") {
36440
- return Object.freeze([
36441
- "text-leaf-edit",
36442
- "table-text-paste",
36443
- "table-text-drop",
36444
- "table-structured-fragment-paste",
36445
- "table-structured-fragment-drop"
36446
- ]);
36447
- }
36448
- if (scopeKind === "list-item" && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
36449
- return Object.freeze(["text-leaf-edit", "list-text-edit", "list-structure-action"]);
37414
+ {
37415
+ const intents = ["text-leaf-edit"];
37416
+ if (target.table?.operationScope === "text") {
37417
+ intents.push(
37418
+ "table-text-paste",
37419
+ "table-text-drop",
37420
+ "table-structured-fragment-paste",
37421
+ "table-structured-fragment-drop"
37422
+ );
37423
+ }
37424
+ if ((scopeKind === "list-item" || target.listAddress !== void 0) && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
37425
+ intents.push("list-text-edit", "list-structure-action");
37426
+ }
37427
+ return freezeCommandIntents(intents);
36450
37428
  }
36451
- return Object.freeze(["text-leaf-edit"]);
36452
37429
  case "field":
36453
37430
  return Object.freeze(
36454
37431
  target.field?.fieldFamily === "TOC" ? ["toc-refresh", "field-update"] : ["field-update"]
@@ -36595,7 +37572,7 @@ function readbackForTarget(document2, target, runtimeTextCommand) {
36595
37572
  source: "canonical-text-leaf"
36596
37573
  };
36597
37574
  }
36598
- function projectTarget(document2, target, relation, workflowBlockers, scopeKind) {
37575
+ function projectTarget(document2, target, relation, workflowBlockers, scopeKind, layoutIdentity) {
36599
37576
  const runtimeTextCommand = runtimeTextCommandEvidence(target, workflowBlockers);
36600
37577
  const readback = readbackForTarget(document2, target, runtimeTextCommand);
36601
37578
  return {
@@ -36616,6 +37593,7 @@ function projectTarget(document2, target, relation, workflowBlockers, scopeKind)
36616
37593
  ...target.table ? { table: target.table } : {},
36617
37594
  ...target.editableOwner ? { editableOwner: target.editableOwner } : {},
36618
37595
  ...target.canonicalAddress ? { canonicalAddress: target.canonicalAddress } : {},
37596
+ ...layoutIdentity ? { layoutIdentity } : {},
36619
37597
  staleCheck: target.staleCheck,
36620
37598
  posture: target.posture,
36621
37599
  ...workflowBlockers.length > 0 ? { workflowBlockers: Object.freeze([...workflowBlockers]) } : {},
@@ -36631,7 +37609,7 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
36631
37609
  facts.push(projectWorkflowBlockerFact(fact));
36632
37610
  factsByTargetKey.set(fact.targetKey, facts);
36633
37611
  }
36634
- const entries = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
37612
+ const relatedTargets = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
36635
37613
  const relation = relationForTarget(target, scope, entry);
36636
37614
  if (!relation) return null;
36637
37615
  const workflowBlockers = Object.freeze(
@@ -36639,8 +37617,19 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
36639
37617
  (left, right) => left.source.localeCompare(right.source) || left.blocker.localeCompare(right.blocker) || left.refusalId.localeCompare(right.refusalId)
36640
37618
  )
36641
37619
  );
36642
- return projectTarget(document2, target, relation, workflowBlockers, scope.kind);
36643
- }).filter((target) => target !== null).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
37620
+ return { target, relation, workflowBlockers };
37621
+ }).filter((target) => target !== null);
37622
+ const layoutIndexes = relatedTargets.length > 0 ? layoutIdentityIndexes(document2) : void 0;
37623
+ const entries = relatedTargets.map(
37624
+ ({ target, relation, workflowBlockers }) => projectTarget(
37625
+ document2,
37626
+ target,
37627
+ relation,
37628
+ workflowBlockers,
37629
+ scope.kind,
37630
+ layoutIndexes ? layoutIdentityForTarget(target, layoutIndexes) : void 0
37631
+ )
37632
+ ).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
36644
37633
  const blockers = /* @__PURE__ */ new Set();
36645
37634
  let supportedTextTargetCount = 0;
36646
37635
  for (const target of entries) {
@@ -36659,6 +37648,114 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
36659
37648
  };
36660
37649
  }
36661
37650
 
37651
+ // src/runtime/scopes/object-evidence.ts
37652
+ var OBJECT_INTENTS = /* @__PURE__ */ new Set([
37653
+ "image-layout",
37654
+ "image-frame",
37655
+ "chart-edit",
37656
+ "custom-xml-update",
37657
+ "embedded-content-update",
37658
+ "opaque-content-preserve",
37659
+ "object-edit",
37660
+ "preserve-only-refusal"
37661
+ ]);
37662
+ function unique2(values) {
37663
+ return Object.freeze([...new Set(values.filter((value) => value.length > 0))]);
37664
+ }
37665
+ function isObjectTarget(entry) {
37666
+ 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));
37667
+ }
37668
+ function blockersFor(entry) {
37669
+ return unique2([
37670
+ ...entry.posture.blockers,
37671
+ ...entry.runtimeCommand.blockers ?? [],
37672
+ ...entry.runtimeTextCommand.blockers ?? [],
37673
+ ...(entry.workflowBlockers ?? []).flatMap((blocker) => [
37674
+ blocker.blocker,
37675
+ blocker.refusalId
37676
+ ]),
37677
+ entry.runtimeCommand.status === "blocked" ? entry.runtimeCommand.reason : ""
37678
+ ]);
37679
+ }
37680
+ function projectEntry2(entry) {
37681
+ const blockers = blockersFor(entry);
37682
+ return {
37683
+ targetKey: entry.targetKey,
37684
+ kind: entry.kind,
37685
+ ...entry.object?.objectKind ? { objectKind: entry.object.objectKind } : {},
37686
+ relation: entry.relation,
37687
+ commandFamily: entry.commandFamily,
37688
+ editability: entry.editability,
37689
+ ...entry.sourceRef ? { sourceRef: entry.sourceRef } : {},
37690
+ ...entry.object ? { object: entry.object } : {},
37691
+ runtimeCommand: entry.runtimeCommand,
37692
+ blockers,
37693
+ ...entry.posture.preserveOnly ? { preserveOnly: true } : {}
37694
+ };
37695
+ }
37696
+ function familyKindFor(entry) {
37697
+ return entry.objectKind ?? "unknown";
37698
+ }
37699
+ function familyStatusFor(entries) {
37700
+ if (entries.some((entry) => entry.runtimeCommand.status === "supported")) {
37701
+ return "supported";
37702
+ }
37703
+ if (entries.length > 0 && entries.every(
37704
+ (entry) => entry.preserveOnly === true || entry.commandFamily === "preserve-only-refusal"
37705
+ )) {
37706
+ return "preserve-only";
37707
+ }
37708
+ return "blocked";
37709
+ }
37710
+ function summarizeFamily(objectKind, entries) {
37711
+ const sortedEntries = [...entries].sort(
37712
+ (left, right) => left.targetKey.localeCompare(right.targetKey)
37713
+ );
37714
+ const preserveOnly = sortedEntries.some((entry) => entry.preserveOnly === true);
37715
+ return {
37716
+ objectKind,
37717
+ status: familyStatusFor(sortedEntries),
37718
+ count: sortedEntries.length,
37719
+ targetKeys: unique2(sortedEntries.map((entry) => entry.targetKey)),
37720
+ relations: unique2(sortedEntries.map((entry) => entry.relation)),
37721
+ commandFamilies: unique2(sortedEntries.map((entry) => entry.commandFamily)),
37722
+ runtimeIntents: unique2(
37723
+ sortedEntries.flatMap((entry) => [...entry.runtimeCommand.intents])
37724
+ ),
37725
+ blockers: unique2(sortedEntries.flatMap((entry) => [...entry.blockers])),
37726
+ ...preserveOnly ? { preserveOnly: true } : {}
37727
+ };
37728
+ }
37729
+ function summarizeFamilies(entries) {
37730
+ const byFamily = /* @__PURE__ */ new Map();
37731
+ for (const entry of entries) {
37732
+ const objectKind = familyKindFor(entry);
37733
+ const bucket = byFamily.get(objectKind);
37734
+ if (bucket) {
37735
+ bucket.push(entry);
37736
+ continue;
37737
+ }
37738
+ byFamily.set(objectKind, [entry]);
37739
+ }
37740
+ return Object.freeze(
37741
+ [...byFamily.entries()].map(([objectKind, familyEntries]) => summarizeFamily(objectKind, familyEntries)).sort((left, right) => left.objectKind.localeCompare(right.objectKind))
37742
+ );
37743
+ }
37744
+ function deriveScopeObjectEvidence(editableTargets) {
37745
+ const entries = Object.freeze(
37746
+ [...editableTargets?.entries ?? []].filter(isObjectTarget).map(projectEntry2).sort((left, right) => left.targetKey.localeCompare(right.targetKey))
37747
+ );
37748
+ const blockers = unique2(entries.flatMap((entry) => [...entry.blockers]));
37749
+ const families = summarizeFamilies(entries);
37750
+ return {
37751
+ status: entries.length > 0 ? "present" : "none",
37752
+ count: entries.length,
37753
+ blockers,
37754
+ families,
37755
+ entries
37756
+ };
37757
+ }
37758
+
36662
37759
  // src/runtime/scopes/table-evidence.ts
36663
37760
  function isTableFamilyEntry(entry) {
36664
37761
  return entry?.kind === "table" || entry?.kind === "table-row" || entry?.kind === "table-cell";
@@ -37228,7 +38325,7 @@ function deriveScopeGeometryEvidence(scopeId, provider, context) {
37228
38325
  function freezeList2(values) {
37229
38326
  return values ? Object.freeze([...values]) : void 0;
37230
38327
  }
37231
- function unique2(values) {
38328
+ function unique3(values) {
37232
38329
  return Object.freeze([...new Set(values)]);
37233
38330
  }
37234
38331
  function parseTableFamilyScopeId2(scopeId) {
@@ -37258,7 +38355,7 @@ function candidateTableBlockIds(blockIndex, mapped) {
37258
38355
  const mappedId = mapped?.get(blockIndex);
37259
38356
  if (mappedId) ids.push(mappedId);
37260
38357
  ids.push(`table-${blockIndex}`, `table:${blockIndex}`, `block-${blockIndex}`);
37261
- return unique2(ids);
38358
+ return unique3(ids);
37262
38359
  }
37263
38360
  function rowInFragment(fragment, rowIndex) {
37264
38361
  const range = fragment.tableRowRange;
@@ -37295,19 +38392,19 @@ function projectTableFramePage(fragment) {
37295
38392
  ...fragment.tableRowRange ? { rowRange: { ...fragment.tableRowRange } } : {},
37296
38393
  ...fragment.continuation?.continuesFromPreviousPage !== void 0 ? { continuesFromPreviousPage: fragment.continuation.continuesFromPreviousPage } : {},
37297
38394
  ...fragment.continuation?.continuesToNextPage !== void 0 ? { continuesToNextPage: fragment.continuation.continuesToNextPage } : {},
37298
- ...repeated ? { repeatedHeaderRowIndexes: unique2(repeated) } : {},
38395
+ ...repeated ? { repeatedHeaderRowIndexes: unique3(repeated) } : {},
37299
38396
  ...splitRowCarry ? { splitRowCarry: Object.freeze(splitRowCarry.map((item) => ({ ...item }))) } : {},
37300
38397
  ...carry ? { verticalMergeCarry: Object.freeze(carry.map((item) => ({ ...item }))) } : {}
37301
38398
  };
37302
38399
  }
37303
38400
  function projectTableFrame(blockId, parsed, fragments) {
37304
- const pageIds = unique2(fragments.map((fragment) => fragment.pageId));
37305
- const pageSliceIds = unique2(fragments.map((fragment) => fragment.fragmentId));
37306
- const layoutObjectIds = unique2(
38401
+ const pageIds = unique3(fragments.map((fragment) => fragment.pageId));
38402
+ const pageSliceIds = unique3(fragments.map((fragment) => fragment.fragmentId));
38403
+ const layoutObjectIds = unique3(
37307
38404
  fragments.map((fragment) => fragment.layoutObject?.objectId).filter((objectId) => typeof objectId === "string" && objectId.length > 0)
37308
38405
  );
37309
38406
  const rowRangesByPage = Object.freeze(fragments.map(projectTableFramePage));
37310
- const repeatedHeaderRowIndexes = unique2(
38407
+ const repeatedHeaderRowIndexes = unique3(
37311
38408
  fragments.flatMap((fragment) => fragment.continuation?.repeatedHeaderRowIndexes ?? [])
37312
38409
  );
37313
38410
  const verticalMergeCarry = Object.freeze(
@@ -37332,7 +38429,7 @@ function projectTableFrame(blockId, parsed, fragments) {
37332
38429
  };
37333
38430
  }
37334
38431
  function continuationFromTableFrame(fragments) {
37335
- const pageIds = unique2(fragments.map((fragment) => fragment.pageId));
38432
+ const pageIds = unique3(fragments.map((fragment) => fragment.pageId));
37336
38433
  return {
37337
38434
  ...pageIds.length > 0 ? { pageIds } : {},
37338
38435
  pageCount: pageIds.length,
@@ -37630,6 +38727,7 @@ function composeEvidence(inputs) {
37630
38727
  ...editableTargets ? { editableTargets } : {},
37631
38728
  layout
37632
38729
  });
38730
+ const objects = deriveScopeObjectEvidence(editableTargets);
37633
38731
  return {
37634
38732
  formattingSummary: formattingSummaryOf(scope),
37635
38733
  reviewItemIds,
@@ -37640,6 +38738,7 @@ function composeEvidence(inputs) {
37640
38738
  ...adjacentGeometry ? { adjacentGeometry } : {},
37641
38739
  visualization: deriveScopeVisualization(scope),
37642
38740
  ...editableTargets ? { editableTargets } : {},
38741
+ objects,
37643
38742
  ...table ? { table } : {},
37644
38743
  contentControls,
37645
38744
  capabilities,
@@ -42492,6 +43591,14 @@ function paginateSectionBlocksWithSplits(section, blocks, layout, footnotes, mea
42492
43591
  pushPage(block.from);
42493
43592
  continue;
42494
43593
  }
43594
+ if (isStandalonePageBreakParagraph(block)) {
43595
+ if (columnHeight > 0) {
43596
+ pushPage(nextBoundary);
43597
+ } else {
43598
+ pageStart = Math.max(pageStart, Math.min(nextBoundary, section.end));
43599
+ }
43600
+ break;
43601
+ }
42495
43602
  const effectiveNoteHeight = estimateFootnoteReservation(
42496
43603
  block,
42497
43604
  footnotes,
@@ -42666,6 +43773,21 @@ function hasPageBreak(block) {
42666
43773
  }
42667
43774
  return nestedBlocks(block).some(hasPageBreak);
42668
43775
  }
43776
+ function isStandalonePageBreakParagraph(block) {
43777
+ if (block.kind !== "paragraph") return false;
43778
+ let sawPageBreak = false;
43779
+ for (const segment of block.segments) {
43780
+ if (segment.kind === "opaque_inline" && segment.label === "Page break") {
43781
+ sawPageBreak = true;
43782
+ continue;
43783
+ }
43784
+ if (segment.kind === "text" && segment.text.trim().length === 0) {
43785
+ continue;
43786
+ }
43787
+ return false;
43788
+ }
43789
+ return sawPageBreak;
43790
+ }
42669
43791
  function nestedBlocks(block) {
42670
43792
  if (block.kind === "sdt_block") {
42671
43793
  return block.children;
@@ -44150,6 +45272,9 @@ function projectSurfaceBlocksToPageFragments(surface, pages, splits, columnByBlo
44150
45272
  for (let blockIndex = 0; blockIndex < surface.blocks.length; blockIndex += 1) {
44151
45273
  const block = surface.blocks[blockIndex];
44152
45274
  const blockPath = `main/block[${blockIndex}]`;
45275
+ if (isStandalonePageBreakParagraph2(block)) {
45276
+ continue;
45277
+ }
44153
45278
  if (block.kind === "table") {
44154
45279
  const tableSliceList = splits?.tablesByBlockId.get(block.blockId);
44155
45280
  if (tableSliceList && tableSliceList.length > 1) {
@@ -44235,6 +45360,23 @@ function projectSurfaceBlocksToPageFragments(surface, pages, splits, columnByBlo
44235
45360
  }
44236
45361
  return byPage;
44237
45362
  }
45363
+ function isStandalonePageBreakParagraph2(block) {
45364
+ if (block.kind !== "paragraph") {
45365
+ return false;
45366
+ }
45367
+ let sawPageBreak = false;
45368
+ for (const segment of block.segments) {
45369
+ if (segment.kind === "opaque_inline" && segment.label === "Page break") {
45370
+ sawPageBreak = true;
45371
+ continue;
45372
+ }
45373
+ if (segment.kind === "text" && segment.text.trim().length === 0) {
45374
+ continue;
45375
+ }
45376
+ return false;
45377
+ }
45378
+ return sawPageBreak;
45379
+ }
44238
45380
  function projectLineBoxesForPageFragments(pages, fragmentsByPageIndex, fragmentMeasurementsByPageIndex, surface) {
44239
45381
  const byPage = /* @__PURE__ */ new Map();
44240
45382
  const blocksById = surface ? new Map(surface.blocks.map((block) => [block.blockId, block])) : /* @__PURE__ */ new Map();
@@ -44747,13 +45889,11 @@ function buildBookmarkRanges(targets) {
44747
45889
  }
44748
45890
  var EMPTY_NUMBERING_INPUT_INDEX = {
44749
45891
  byNumberingKey: /* @__PURE__ */ new Map(),
44750
- byBlockPath: /* @__PURE__ */ new Map(),
44751
- byParagraphIndex: /* @__PURE__ */ new Map()
45892
+ byBlockPath: /* @__PURE__ */ new Map()
44752
45893
  };
44753
45894
  function buildNumberingInputIndex(numberingInputs) {
44754
45895
  const byNumberingKey = /* @__PURE__ */ new Map();
44755
45896
  const byBlockPath = /* @__PURE__ */ new Map();
44756
- const byParagraph = /* @__PURE__ */ new Map();
44757
45897
  for (const input of numberingInputs) {
44758
45898
  if (!byNumberingKey.has(input.numberingKey)) {
44759
45899
  byNumberingKey.set(input.numberingKey, input);
@@ -44761,11 +45901,8 @@ function buildNumberingInputIndex(numberingInputs) {
44761
45901
  if (!byBlockPath.has(input.blockPath)) {
44762
45902
  byBlockPath.set(input.blockPath, input);
44763
45903
  }
44764
- if (!byParagraph.has(input.paragraphIndex)) {
44765
- byParagraph.set(input.paragraphIndex, input);
44766
- }
44767
45904
  }
44768
- return { byNumberingKey, byBlockPath, byParagraphIndex: byParagraph };
45905
+ return { byNumberingKey, byBlockPath };
44769
45906
  }
44770
45907
  function collectBookmarkRangeLayoutFacts(fragmentId, blockPath, bookmarkRanges) {
44771
45908
  if (!blockPath || !bookmarkRanges || bookmarkRanges.length === 0) return [];
@@ -44885,8 +46022,7 @@ function collectNumberingLayoutFactsForBlock(block, blockPath, numberingIndex, n
44885
46022
  const canonical = lookupNumberingInput(
44886
46023
  numberingIndex,
44887
46024
  numberingKey,
44888
- context.path,
44889
- paragraphIndex
46025
+ context.path
44890
46026
  );
44891
46027
  const target = findNumberingTarget(numberingTargets, context.path);
44892
46028
  const numberingLayoutId = numberingKey ?? [
@@ -44926,7 +46062,7 @@ function findNumberingTarget(numberingTargets, blockPath) {
44926
46062
  if (blockPath === void 0) return void 0;
44927
46063
  return numberingTargets.find((target) => target.blockPath === blockPath);
44928
46064
  }
44929
- function lookupNumberingInput(numberingIndex, numberingKey, blockPath, paragraphIndex) {
46065
+ function lookupNumberingInput(numberingIndex, numberingKey, blockPath) {
44930
46066
  if (!numberingIndex) return void 0;
44931
46067
  if (numberingKey !== void 0) {
44932
46068
  const byKey = numberingIndex.byNumberingKey.get(numberingKey);
@@ -44936,7 +46072,7 @@ function lookupNumberingInput(numberingIndex, numberingKey, blockPath, paragraph
44936
46072
  const byPath = numberingIndex.byBlockPath.get(blockPath);
44937
46073
  if (byPath !== void 0) return byPath;
44938
46074
  }
44939
- return paragraphIndex !== void 0 ? numberingIndex.byParagraphIndex.get(paragraphIndex) : void 0;
46075
+ return void 0;
44940
46076
  }
44941
46077
  function collectNumberingUnavailableReasons(numbering, canonical) {
44942
46078
  const reasons = [];
@@ -45354,9 +46490,327 @@ async function createCanvasProvider(fontLoader) {
45354
46490
  }
45355
46491
  }
45356
46492
 
46493
+ // src/runtime/layout/persistent-layout-measurement-cache.ts
46494
+ init_resolved_formatting_state();
46495
+ var DEFAULT_MAX_ENTRIES = 1e4;
46496
+ var DEFAULT_MAX_ESTIMATED_BYTES = 50 * 1024 * 1024;
46497
+ function createPersistentLayoutMeasurementCache(options = {}) {
46498
+ const maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_MAX_ENTRIES);
46499
+ const maxEstimatedBytes = Math.max(
46500
+ 1024,
46501
+ options.maxEstimatedBytes ?? DEFAULT_MAX_ESTIMATED_BYTES
46502
+ );
46503
+ const entries = /* @__PURE__ */ new Map();
46504
+ const counters = {
46505
+ hits: 0,
46506
+ misses: 0,
46507
+ sets: 0,
46508
+ evictions: 0,
46509
+ clears: 0,
46510
+ estimatedBytes: 0,
46511
+ invalidatedByReason: {}
46512
+ };
46513
+ function evictOldest() {
46514
+ const oldest = entries.keys().next();
46515
+ if (oldest.done) return;
46516
+ const entry = entries.get(oldest.value);
46517
+ if (entry) {
46518
+ counters.estimatedBytes -= entry.estimatedBytes;
46519
+ }
46520
+ entries.delete(oldest.value);
46521
+ counters.evictions += 1;
46522
+ }
46523
+ return {
46524
+ get(key) {
46525
+ const cacheKey = serializeCacheKey(key);
46526
+ const entry = entries.get(cacheKey);
46527
+ if (!entry) {
46528
+ counters.misses += 1;
46529
+ return void 0;
46530
+ }
46531
+ entries.delete(cacheKey);
46532
+ entries.set(cacheKey, entry);
46533
+ counters.hits += 1;
46534
+ return entry.value;
46535
+ },
46536
+ set(key, value) {
46537
+ const cacheKey = serializeCacheKey(key);
46538
+ const previous = entries.get(cacheKey);
46539
+ if (previous) {
46540
+ counters.estimatedBytes -= previous.estimatedBytes;
46541
+ entries.delete(cacheKey);
46542
+ }
46543
+ const estimatedBytes = estimateBytes(key, value);
46544
+ entries.set(cacheKey, { key, value, estimatedBytes });
46545
+ counters.estimatedBytes += estimatedBytes;
46546
+ counters.sets += 1;
46547
+ while (entries.size > maxEntries || counters.estimatedBytes > maxEstimatedBytes) {
46548
+ evictOldest();
46549
+ }
46550
+ },
46551
+ invalidate(reason, predicate) {
46552
+ if (!predicate) {
46553
+ const removed2 = entries.size;
46554
+ counters.estimatedBytes = 0;
46555
+ entries.clear();
46556
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed2;
46557
+ return;
46558
+ }
46559
+ let removed = 0;
46560
+ for (const [cacheKey, entry] of entries) {
46561
+ if (!predicate(entry.key)) continue;
46562
+ counters.estimatedBytes -= entry.estimatedBytes;
46563
+ entries.delete(cacheKey);
46564
+ removed += 1;
46565
+ }
46566
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
46567
+ },
46568
+ clear(reason) {
46569
+ const removed = entries.size;
46570
+ counters.estimatedBytes = 0;
46571
+ entries.clear();
46572
+ counters.clears += 1;
46573
+ counters.invalidatedByReason[reason] = (counters.invalidatedByReason[reason] ?? 0) + removed;
46574
+ },
46575
+ stats() {
46576
+ return {
46577
+ hits: counters.hits,
46578
+ misses: counters.misses,
46579
+ sets: counters.sets,
46580
+ evictions: counters.evictions,
46581
+ clears: counters.clears,
46582
+ size: entries.size,
46583
+ estimatedBytes: Math.max(0, counters.estimatedBytes),
46584
+ invalidatedByReason: { ...counters.invalidatedByReason }
46585
+ };
46586
+ }
46587
+ };
46588
+ }
46589
+ function createCachedLayoutMeasurementProvider(delegate, options = {}) {
46590
+ const cache = options.cache ?? createPersistentLayoutMeasurementCache(options);
46591
+ function emitCounter(type, key) {
46592
+ const telemetryBus = options.telemetryBus;
46593
+ if (!telemetryBus?.isEnabled("layout")) return;
46594
+ telemetryBus.emitLazy("layout", () => ({
46595
+ type,
46596
+ payload: key ? {
46597
+ measurementKind: key.measurementKind,
46598
+ blockId: key.blockId,
46599
+ backendVersion: key.backendVersion
46600
+ } : void 0
46601
+ }));
46602
+ }
46603
+ return {
46604
+ get fidelity() {
46605
+ return delegate.fidelity;
46606
+ },
46607
+ measurementCache: cache,
46608
+ measurementCacheStats() {
46609
+ return cache.stats();
46610
+ },
46611
+ whenReady() {
46612
+ return delegate.whenReady();
46613
+ },
46614
+ measureLineFragments(input) {
46615
+ const key = buildLineFragmentsKey(input, delegate, options);
46616
+ const cached = cache.get(key);
46617
+ if (cached) {
46618
+ emitCounter("pageRender.measurement.hit", key);
46619
+ return cloneMeasuredLineFragments(cached);
46620
+ }
46621
+ emitCounter("pageRender.measurement.miss", key);
46622
+ const measured = delegate.measureLineFragments(input);
46623
+ cache.set(key, cloneMeasuredLineFragments(measured));
46624
+ return measured;
46625
+ },
46626
+ measureInlineObject(input) {
46627
+ const key = buildInlineObjectKey(input, delegate, options);
46628
+ const cached = cache.get(key);
46629
+ if (cached) {
46630
+ emitCounter("pageRender.measurement.hit", key);
46631
+ return { ...cached };
46632
+ }
46633
+ emitCounter("pageRender.measurement.miss", key);
46634
+ const measured = delegate.measureInlineObject(input);
46635
+ cache.set(key, { ...measured });
46636
+ return measured;
46637
+ },
46638
+ measureTableBlock(input) {
46639
+ const key = buildTableBlockKey(input, delegate, options);
46640
+ const cached = cache.get(key);
46641
+ if (cached) {
46642
+ emitCounter("pageRender.measurement.hit", key);
46643
+ return cloneMeasuredTableBlock(cached);
46644
+ }
46645
+ emitCounter("pageRender.measurement.miss", key);
46646
+ const measured = delegate.measureTableBlock(input);
46647
+ cache.set(key, cloneMeasuredTableBlock(measured));
46648
+ return measured;
46649
+ },
46650
+ invalidateCache() {
46651
+ delegate.invalidateCache();
46652
+ cache.clear("provider.invalidateCache");
46653
+ emitCounter("pageRender.measurement.invalidated.provider.invalidateCache");
46654
+ }
46655
+ };
46656
+ }
46657
+ function isCachedLayoutMeasurementProvider(provider) {
46658
+ return typeof provider.measurementCacheStats === "function";
46659
+ }
46660
+ function buildLineFragmentsKey(input, delegate, options) {
46661
+ const block = input.block;
46662
+ const identity = measurementIdentityForBlock(block);
46663
+ return {
46664
+ measurementKind: "line-fragments",
46665
+ blockId: identity.blockId,
46666
+ blockPath: identity.blockPath,
46667
+ contentHash: hashStable2({
46668
+ kind: block.kind,
46669
+ identity: identity.identityHash,
46670
+ segments: block.segments,
46671
+ numbering: block.numbering,
46672
+ numberingPrefix: block.numberingPrefix,
46673
+ numberingSuffix: block.numberingSuffix,
46674
+ resolvedNumbering: block.resolvedNumbering
46675
+ }),
46676
+ formattingHash: createLayoutReadyFormattingSnapshot(
46677
+ input.formatting,
46678
+ input.runs
46679
+ ).hash,
46680
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
46681
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
46682
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
46683
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
46684
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
46685
+ };
46686
+ }
46687
+ function buildInlineObjectKey(input, delegate, options) {
46688
+ return {
46689
+ measurementKind: "inline-object",
46690
+ blockId: "inline-object",
46691
+ blockPath: "inline-object",
46692
+ contentHash: hashStable2({ display: input.display }),
46693
+ formattingHash: "format:none",
46694
+ constraintsHash: hashStable2({
46695
+ widthTwips: input.widthTwips,
46696
+ heightTwips: input.heightTwips
46697
+ }),
46698
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
46699
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
46700
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
46701
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
46702
+ };
46703
+ }
46704
+ function buildTableBlockKey(input, delegate, options) {
46705
+ const block = input.block;
46706
+ const identity = measurementIdentityForBlock(block);
46707
+ return {
46708
+ measurementKind: "table-block",
46709
+ blockId: identity.blockId,
46710
+ blockPath: identity.blockPath,
46711
+ contentHash: hashStable2({
46712
+ kind: block.kind,
46713
+ identity: identity.identityHash,
46714
+ rows: block.rows,
46715
+ gridColumns: block.gridColumns,
46716
+ gridColumnsRelative: block.gridColumnsRelative,
46717
+ tableResolved: block.tableResolved,
46718
+ tblLook: block.tblLook
46719
+ }),
46720
+ formattingHash: hashStable2({
46721
+ styleId: block.styleId,
46722
+ alignment: block.alignment
46723
+ }),
46724
+ constraintsHash: hashStable2({ columnWidth: input.columnWidth }),
46725
+ sectionHash: resolveOptionValue(options.sectionHash, "section:provider-input"),
46726
+ fontEpoch: resolveOptionValue(options.fontEpoch, "font:default"),
46727
+ backendVersion: resolveOptionValue(options.backendVersion, delegate.fidelity),
46728
+ displayModeHash: resolveOptionValue(options.displayModeHash, "display:default")
46729
+ };
46730
+ }
46731
+ function measurementIdentityForBlock(block) {
46732
+ const identity = block.layoutIdentity;
46733
+ if (!identity) {
46734
+ return {
46735
+ blockId: block.blockId,
46736
+ blockPath: blockPathForBlock(block),
46737
+ identityHash: "identity:legacy"
46738
+ };
46739
+ }
46740
+ return {
46741
+ blockId: identity.blockId,
46742
+ blockPath: identity.blockPath,
46743
+ identityHash: hashStable2({
46744
+ storyKey: identity.storyKey,
46745
+ blockId: identity.blockId,
46746
+ blockPath: identity.blockPath,
46747
+ sourceRef: identity.sourceRef,
46748
+ table: identity.table,
46749
+ list: identity.list,
46750
+ field: identity.field,
46751
+ object: identity.object
46752
+ })
46753
+ };
46754
+ }
46755
+ function blockPathForBlock(block) {
46756
+ return `${block.from}:${block.to}`;
46757
+ }
46758
+ function resolveOptionValue(option, fallback) {
46759
+ if (typeof option === "function") return option();
46760
+ return option ?? fallback;
46761
+ }
46762
+ function serializeCacheKey(key) {
46763
+ return stableStringify4(key);
46764
+ }
46765
+ function estimateBytes(key, value) {
46766
+ return stableStringify4(key).length + stableStringify4(value).length;
46767
+ }
46768
+ function cloneMeasuredLineFragments(value) {
46769
+ return {
46770
+ lineCount: value.lineCount,
46771
+ maxLineWidth: value.maxLineWidth,
46772
+ lineHeights: [...value.lineHeights]
46773
+ };
46774
+ }
46775
+ function cloneMeasuredTableBlock(value) {
46776
+ return {
46777
+ totalHeightTwips: value.totalHeightTwips,
46778
+ rowHeights: [...value.rowHeights]
46779
+ };
46780
+ }
46781
+ function hashStable2(value) {
46782
+ return fnv1a2(stableStringify4(value)).toString(36);
46783
+ }
46784
+ function stableStringify4(value) {
46785
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
46786
+ if (Array.isArray(value)) {
46787
+ return `[${value.map((item) => stableStringify4(item)).join(",")}]`;
46788
+ }
46789
+ if (value instanceof Map) {
46790
+ return stableStringify4(Array.from(value.entries()));
46791
+ }
46792
+ const object = value;
46793
+ const keys = Object.keys(object).filter((key) => object[key] !== void 0 && typeof object[key] !== "function").sort();
46794
+ return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify4(object[key])}`).join(",")}}`;
46795
+ }
46796
+ function fnv1a2(input) {
46797
+ let hash = 2166136261;
46798
+ for (let i = 0; i < input.length; i += 1) {
46799
+ hash ^= input.charCodeAt(i);
46800
+ hash = Math.imul(hash, 16777619);
46801
+ }
46802
+ return hash >>> 0;
46803
+ }
46804
+
45357
46805
  // src/runtime/layout/layout-engine-instance.ts
45358
46806
  var FULL_VIEWPORT_WINDOW_KEY = "full";
45359
46807
  var DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER = 1;
46808
+ function indexLayoutIdentitiesByBlockPath(identities) {
46809
+ return new Map(identities.map((identity) => [identity.blockPath, identity]));
46810
+ }
46811
+ function indexEditableTargetsByBlockPath2(targets) {
46812
+ return new Map(targets.map((target) => [target.blockPath, target]));
46813
+ }
45360
46814
  function normalizeViewportPageWindow(window2) {
45361
46815
  if (!window2) return void 0;
45362
46816
  const buffer = Number.isFinite(window2.bufferPages) ? Math.max(0, Math.floor(window2.bufferPages ?? 0)) : DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER;
@@ -45552,8 +47006,22 @@ function recordFullRebuildReason(reasonKind) {
45552
47006
  probe.invalidationCounts[totalKey] = (probe.invalidationCounts[totalKey] ?? 0) + 1;
45553
47007
  probe.invalidationCounts[byReasonKey] = (probe.invalidationCounts[byReasonKey] ?? 0) + 1;
45554
47008
  }
47009
+ function wrapMeasurementProvider(provider, measurementCache, telemetryBus) {
47010
+ if (!measurementCache || isCachedLayoutMeasurementProvider(provider)) {
47011
+ return provider;
47012
+ }
47013
+ const cacheOptions = measurementCache === true ? {} : measurementCache;
47014
+ return createCachedLayoutMeasurementProvider(provider, {
47015
+ ...cacheOptions,
47016
+ ...telemetryBus ? { telemetryBus } : {}
47017
+ });
47018
+ }
45555
47019
  function createLayoutEngine(options = {}) {
45556
- let measurementProvider = options.measurementProvider ?? createEmpiricalMeasurementProvider();
47020
+ let measurementProvider = wrapMeasurementProvider(
47021
+ options.measurementProvider ?? createEmpiricalMeasurementProvider(),
47022
+ options.measurementCache,
47023
+ options.telemetryBus
47024
+ );
45557
47025
  const autoUpgradeToCanvas = options.autoUpgradeToCanvasBackend !== false;
45558
47026
  const telemetryBus = options.telemetryBus;
45559
47027
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
@@ -45653,13 +47121,20 @@ function createLayoutEngine(options = {}) {
45653
47121
  const pageCountBeforeRecompute = previousPageCount;
45654
47122
  const document2 = input.document;
45655
47123
  const viewportWindow = normalizeViewportPageWindow(input.viewportPageWindow);
47124
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
47125
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
47126
+ layoutInputs.layoutIdentities
47127
+ );
47128
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
47129
+ layoutInputs.editableTargets
47130
+ );
45656
47131
  const mainSurface = createEditorSurfaceSnapshot(
45657
47132
  document2,
45658
47133
  createSelectionSnapshot(0, 0),
45659
- MAIN_STORY_TARGET
47134
+ MAIN_STORY_TARGET,
47135
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
45660
47136
  );
45661
47137
  const sections = buildResolvedSections(document2);
45662
- const layoutInputs = collectCanonicalLayoutInputs(document2);
45663
47138
  const fieldRegions = layoutInputs.fieldRegions;
45664
47139
  const pageStack = buildPageStackWithSplits(
45665
47140
  document2,
@@ -45771,10 +47246,18 @@ function createLayoutEngine(options = {}) {
45771
47246
  const firstDirty = range.firstPageIndex;
45772
47247
  if (firstDirty < 0 || firstDirty >= priorGraph.pages.length) return null;
45773
47248
  const document2 = input.document;
47249
+ const layoutInputs = collectCanonicalLayoutInputs(document2);
47250
+ const layoutIdentitiesByBlockPath = indexLayoutIdentitiesByBlockPath(
47251
+ layoutInputs.layoutIdentities
47252
+ );
47253
+ const editableTargetsByBlockPath = indexEditableTargetsByBlockPath2(
47254
+ layoutInputs.editableTargets
47255
+ );
45774
47256
  const mainSurface = createEditorSurfaceSnapshot(
45775
47257
  document2,
45776
47258
  createSelectionSnapshot(0, 0),
45777
- MAIN_STORY_TARGET
47259
+ MAIN_STORY_TARGET,
47260
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
45778
47261
  );
45779
47262
  const sections = buildResolvedSections(document2);
45780
47263
  const dirtyPage = priorGraph.pages[firstDirty];
@@ -45803,7 +47286,6 @@ function createLayoutEngine(options = {}) {
45803
47286
  const freshSnapshotsToRebuild = freshSnapshots.slice(0, convergenceIndex);
45804
47287
  const convergedTailStart = convergenceIndex < freshSnapshots.length ? firstDirty + convergenceIndex : void 0;
45805
47288
  const freshStories = resolvePageStories(freshSnapshotsToRebuild);
45806
- const layoutInputs = collectCanonicalLayoutInputs(document2);
45807
47289
  const fieldRegions = layoutInputs.fieldRegions;
45808
47290
  const freshBodyFragmentsByPageIndex = projectSurfaceBlocksToPageFragments(
45809
47291
  mainSurface,
@@ -45935,7 +47417,11 @@ function createLayoutEngine(options = {}) {
45935
47417
  try {
45936
47418
  const mod = await Promise.resolve().then(() => (init_measurement_backend_canvas(), measurement_backend_canvas_exports));
45937
47419
  const canvasProvider = mod.createCanvasBackend();
45938
- measurementProvider = canvasProvider;
47420
+ measurementProvider = wrapMeasurementProvider(
47421
+ canvasProvider,
47422
+ options.measurementCache,
47423
+ telemetryBus
47424
+ );
45939
47425
  cachedKey = null;
45940
47426
  cachedGraph = null;
45941
47427
  cachedFormatting = null;
@@ -46020,7 +47506,11 @@ function createLayoutEngine(options = {}) {
46020
47506
  },
46021
47507
  swapMeasurementProvider(provider) {
46022
47508
  const previousFidelity = measurementProvider.fidelity;
46023
- measurementProvider = provider;
47509
+ measurementProvider = wrapMeasurementProvider(
47510
+ provider,
47511
+ options.measurementCache,
47512
+ telemetryBus
47513
+ );
46024
47514
  if (previousFidelity !== provider.fidelity) {
46025
47515
  cachedKey = null;
46026
47516
  cachedGraph = null;
@@ -46049,6 +47539,10 @@ function createLayoutEngine(options = {}) {
46049
47539
  cachedFormatting = null;
46050
47540
  cachedMapper = null;
46051
47541
  },
47542
+ getMeasurementCacheStats() {
47543
+ if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
47544
+ return measurementProvider.measurementCacheStats();
47545
+ },
46052
47546
  /**
46053
47547
  * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
46054
47548
  * Populates both `cachedGraph` and `cachedKey` (keyed on the provided
@@ -53301,8 +54795,6 @@ function sortJson2(value) {
53301
54795
 
53302
54796
  // src/runtime/document-runtime.ts
53303
54797
  var CANONICAL_BLOCK_REFS_SYMBOL2 = /* @__PURE__ */ Symbol.for("wre.canonical-block-refs");
53304
- var LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES = 5e3;
53305
- var LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK = 256;
53306
54798
  function getLocalTextPatchMetadata(mapping) {
53307
54799
  const metadata = mapping.metadata?.localTextPatch;
53308
54800
  if (!metadata || typeof metadata !== "object") {
@@ -53409,6 +54901,17 @@ function createDocumentRuntime(options) {
53409
54901
  const loadScheduler = options.loadScheduler ?? createLoadScheduler({ backendOverride: "sync" });
53410
54902
  let effectiveMarkupModeProvider;
53411
54903
  const perfCounters = new PerfCounters();
54904
+ const hotEditTraces = new HotEditTraceRecorder();
54905
+ let cachedHotEditPolicy = null;
54906
+ function getHotEditPolicy() {
54907
+ if (cachedHotEditPolicy?.document === state.document) {
54908
+ return cachedHotEditPolicy.policy;
54909
+ }
54910
+ const profile = deriveHotEditDocumentProfile(state.document);
54911
+ const policy = resolveHotEditPolicy(profile);
54912
+ cachedHotEditPolicy = { document: state.document, profile, policy };
54913
+ return policy;
54914
+ }
53412
54915
  let analyticsEmitScheduled = false;
53413
54916
  let analyticsEmitScheduleMode = "none";
53414
54917
  let deferNextContextAnalyticsEmit = false;
@@ -53480,7 +54983,6 @@ function createDocumentRuntime(options) {
53480
54983
  let viewportBlockRanges = null;
53481
54984
  let viewportRangesKey = serializeViewportRanges(viewportBlockRanges);
53482
54985
  let viewportBlocksPerPageEstimate = null;
53483
- const EDITING_CORRIDOR_BLOCK_RADIUS = 8;
53484
54986
  const getRuntimeForLayoutFacet = () => {
53485
54987
  if (!runtimeRef) {
53486
54988
  throw new Error("Document runtime viewport methods are not initialized");
@@ -53814,8 +55316,8 @@ function createDocumentRuntime(options) {
53814
55316
  return viewportBlockRanges;
53815
55317
  }
53816
55318
  const corridor = {
53817
- start: Math.max(0, caretBlockIndex - EDITING_CORRIDOR_BLOCK_RADIUS),
53818
- end: Math.min(previousSurface.blocks.length, caretBlockIndex + EDITING_CORRIDOR_BLOCK_RADIUS + 1)
55319
+ start: Math.max(0, caretBlockIndex - getHotEditPolicy().selectionCorridorBlockRadius),
55320
+ end: Math.min(previousSurface.blocks.length, caretBlockIndex + getHotEditPolicy().selectionCorridorBlockRadius + 1)
53819
55321
  };
53820
55322
  return normalizeViewportRanges([...viewportBlockRanges ?? [], corridor]);
53821
55323
  }
@@ -53896,8 +55398,12 @@ function createDocumentRuntime(options) {
53896
55398
  (block2) => block2.kind === "paragraph" && editFrom >= block2.from && editTo <= block2.to
53897
55399
  );
53898
55400
  if (blockIndex < 0) {
53899
- perfCounters.increment("surface.localText.patchMiss");
53900
- return null;
55401
+ return tryPatchNestedLocalTextSurface(
55402
+ previousSurface,
55403
+ editFrom,
55404
+ editTo,
55405
+ patch.insertedText
55406
+ );
53901
55407
  }
53902
55408
  const block = previousSurface.blocks[blockIndex];
53903
55409
  if (block.kind !== "paragraph") {
@@ -53972,6 +55478,34 @@ function createDocumentRuntime(options) {
53972
55478
  perfCounters.increment("surface.localText.total.us", Math.round((performance.now() - tTotal0) * 1e3));
53973
55479
  }
53974
55480
  }
55481
+ function tryPatchNestedLocalTextSurface(previousSurface, editFrom, editTo, insertedText) {
55482
+ const shiftBudget = estimateLocalTextPatchShiftBudget(previousSurface.blocks, 0);
55483
+ perfCounters.increment("surface.localText.shiftedBlocks", shiftBudget.shiftedBlocks);
55484
+ perfCounters.increment("surface.localText.shiftedNodes", shiftBudget.shiftedNodes);
55485
+ if (!shiftBudget.withinBudget) {
55486
+ perfCounters.increment("surface.localText.patchBudgetFallback");
55487
+ return null;
55488
+ }
55489
+ const patched = patchSurfaceTextSegment(previousSurface, {
55490
+ from: editFrom,
55491
+ to: editTo,
55492
+ insertedText
55493
+ });
55494
+ if (patched.status !== "patched") {
55495
+ perfCounters.increment("surface.localText.patchMiss");
55496
+ return null;
55497
+ }
55498
+ const refs = getSurfaceCanonicalBlockRefs(previousSurface);
55499
+ if (refs) {
55500
+ const nextRefs = state.document.content.children.map(
55501
+ (block, index) => block ?? refs[index] ?? null
55502
+ );
55503
+ attachSurfaceCanonicalBlockRefs(patched.surface, nextRefs);
55504
+ }
55505
+ perfCounters.increment("surface.localText.patchHit");
55506
+ perfCounters.increment("surface.localText.patchDelta", insertedText.length - (editTo - editFrom));
55507
+ return patched.surface;
55508
+ }
53975
55509
  function shiftSurfaceInlineSegment(segment, delta) {
53976
55510
  if (delta === 0) return segment;
53977
55511
  return {
@@ -54019,25 +55553,26 @@ function createDocumentRuntime(options) {
54019
55553
  }
54020
55554
  }
54021
55555
  function estimateLocalTextPatchShiftBudget(blocks, startIndex) {
55556
+ const policy = getHotEditPolicy();
54022
55557
  const shiftedBlocks = Math.max(0, blocks.length - startIndex);
54023
55558
  let shiftedNodes = 0;
54024
55559
  for (let index = startIndex; index < blocks.length; index += 1) {
54025
55560
  const shiftedBlockNodes = countSurfaceShiftNodesUpTo(
54026
55561
  blocks[index],
54027
55562
  Math.min(
54028
- LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK,
54029
- LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES - shiftedNodes
55563
+ policy.maxShiftedNodesPerBlock,
55564
+ policy.maxShiftedSurfaceNodes - shiftedNodes
54030
55565
  ) + 1
54031
55566
  );
54032
55567
  shiftedNodes += shiftedBlockNodes;
54033
- if (shiftedBlockNodes > LOCAL_TEXT_PATCH_MAX_SHIFTED_NODES_PER_BLOCK) {
55568
+ if (shiftedBlockNodes > policy.maxShiftedNodesPerBlock) {
54034
55569
  return {
54035
55570
  shiftedBlocks,
54036
55571
  shiftedNodes,
54037
55572
  withinBudget: false
54038
55573
  };
54039
55574
  }
54040
- if (shiftedNodes > LOCAL_TEXT_PATCH_MAX_SHIFTED_SURFACE_NODES) {
55575
+ if (shiftedNodes > policy.maxShiftedSurfaceNodes) {
54041
55576
  return {
54042
55577
  shiftedBlocks,
54043
55578
  shiftedNodes,
@@ -55001,7 +56536,9 @@ function createDocumentRuntime(options) {
55001
56536
  });
55002
56537
  const r5ScratchReplayState = { ...state };
55003
56538
  const r5ScratchReplaySnapshot = { ...cachedRenderSnapshot };
55004
- const debugFacet = createRuntimeDebugFacet(() => runtime, telemetryBus);
56539
+ const debugFacet = createRuntimeDebugFacet(() => runtime, telemetryBus, {
56540
+ getHotEditTraces: () => hotEditTraces.getTraces()
56541
+ });
55005
56542
  function prepareTableStructureCommandForExecution(command, document2, storyTarget) {
55006
56543
  const resolution = resolveEditableTableStructureTarget({
55007
56544
  document: document2,
@@ -55098,13 +56635,12 @@ function createDocumentRuntime(options) {
55098
56635
  if (!command.editableTarget) {
55099
56636
  return { kind: "accepted", selection };
55100
56637
  }
55101
- const preserveCallerSelection = command.editableTarget.listAddress?.operationScope === "list-text";
55102
56638
  const resolution = resolveEditableTextTarget({
55103
56639
  document: document2,
55104
56640
  surface,
55105
56641
  target: command.editableTarget,
55106
56642
  activeStoryKey: canonicalEditableTargetStoryKey(storyTarget),
55107
- ...preserveCallerSelection ? { selection } : {},
56643
+ selection,
55108
56644
  editableTargetCache: editableTargetBlockCache
55109
56645
  });
55110
56646
  if (resolution.kind === "rejected") {
@@ -55122,7 +56658,7 @@ function createDocumentRuntime(options) {
55122
56658
  }
55123
56659
  return {
55124
56660
  kind: "accepted",
55125
- selection: preserveCallerSelection ? selection : createSelectionSnapshot(resolution.range.from, resolution.range.to),
56661
+ selection,
55126
56662
  ...resolution.textTarget ? { textTarget: resolution.textTarget } : {}
55127
56663
  };
55128
56664
  }
@@ -56298,12 +57834,23 @@ function createDocumentRuntime(options) {
56298
57834
  return actionDepth > 0;
56299
57835
  },
56300
57836
  applyActiveStoryTextCommand(command) {
57837
+ const pendingHotEditTrace = hotEditTraces.begin({
57838
+ commandType: command.type,
57839
+ nowUtc: clock(),
57840
+ counters: perfCounters
57841
+ });
56301
57842
  try {
56302
- return applyTextCommandInActiveStory(command);
57843
+ const ack = applyTextCommandInActiveStory(command);
57844
+ hotEditTraces.complete(pendingHotEditTrace, {
57845
+ tier: classifyHotEditTier(ack),
57846
+ patchMissReason: classifyHotEditPatchMissReason(ack),
57847
+ counters: perfCounters
57848
+ });
57849
+ return ack;
56303
57850
  } catch (error) {
56304
57851
  const runtimeError = toRuntimeError(error);
56305
57852
  emitError(runtimeError);
56306
- return {
57853
+ const ack = {
56307
57854
  kind: "rejected",
56308
57855
  refreshClass: "blocked",
56309
57856
  opId: command.origin?.opId,
@@ -56315,6 +57862,12 @@ function createDocumentRuntime(options) {
56315
57862
  }
56316
57863
  ]
56317
57864
  };
57865
+ hotEditTraces.complete(pendingHotEditTrace, {
57866
+ tier: "blocked",
57867
+ patchMissReason: "participant-blocked",
57868
+ counters: perfCounters
57869
+ });
57870
+ return ack;
56318
57871
  }
56319
57872
  },
56320
57873
  addComment(params) {
@@ -58090,6 +59643,27 @@ function createDocumentRuntime(options) {
58090
59643
  newRevisionToken: state.revisionToken
58091
59644
  }));
58092
59645
  }
59646
+ function classifyHotEditTier(ack) {
59647
+ if (ack.kind === "rejected" || ack.refreshClass === "blocked") {
59648
+ return "blocked";
59649
+ }
59650
+ if (ack.refreshClass === "full-projection" || ack.kind === "structural-divergence") {
59651
+ return "full-refresh";
59652
+ }
59653
+ if (ack.refreshClass === "local-text-equivalent" || ack.refreshClass === "selection-only" || ack.refreshClass === "surface-only") {
59654
+ return "patch";
59655
+ }
59656
+ return "full-refresh";
59657
+ }
59658
+ function classifyHotEditPatchMissReason(ack) {
59659
+ if (ack.kind === "rejected" || ack.refreshClass === "blocked") {
59660
+ return "participant-blocked";
59661
+ }
59662
+ if (ack.refreshClass === "full-projection" || ack.kind === "structural-divergence") {
59663
+ return "effect-disqualified";
59664
+ }
59665
+ return "none";
59666
+ }
58093
59667
  function classifyAck(params) {
58094
59668
  const { opId, priorState, transaction, newRevisionToken } = params;
58095
59669
  const meta = transaction.mapping.metadata ?? {};