@beyondwork/docx-react-component 1.0.129 → 1.0.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/api/public-types.cjs +595 -18
  2. package/dist/api/public-types.d.cts +1 -1
  3. package/dist/api/public-types.d.ts +1 -1
  4. package/dist/api/public-types.js +4 -4
  5. package/dist/api/v3.cjs +1164 -332
  6. package/dist/api/v3.d.cts +2 -2
  7. package/dist/api/v3.d.ts +2 -2
  8. package/dist/api/v3.js +13 -13
  9. package/dist/{chunk-OTQIW2TC.js → chunk-35RHOE6I.js} +105 -4
  10. package/dist/{chunk-PGKUJZXV.js → chunk-3YCQM2RV.js} +6 -6
  11. package/dist/{chunk-JJGVE5J7.js → chunk-4YCWECLZ.js} +1 -1
  12. package/dist/{chunk-EZKJXIPH.js → chunk-6TBLDBCL.js} +1 -1
  13. package/dist/{chunk-SKPTKQHF.js → chunk-7G5GR3VV.js} +122 -23
  14. package/dist/{chunk-HUIHBBAQ.js → chunk-A3GSNB4G.js} +17 -6
  15. package/dist/{chunk-5DGKFNQT.js → chunk-A66ZVUAT.js} +150 -1
  16. package/dist/{chunk-YIYM4ZAP.js → chunk-CI2TD3T4.js} +1 -1
  17. package/dist/{chunk-63FYIGCT.js → chunk-DGA7M77X.js} +2 -2
  18. package/dist/{chunk-EB6M3GE6.js → chunk-FM4K4XFJ.js} +100 -97
  19. package/dist/{chunk-VNLDQJ47.js → chunk-HYHCRMR7.js} +1 -1
  20. package/dist/{chunk-DJU2W4E4.js → chunk-KNHMXKC6.js} +2 -2
  21. package/dist/{chunk-Q3QYGKFE.js → chunk-M7YRJX6V.js} +10 -21
  22. package/dist/{chunk-KFCQYZXR.js → chunk-OVLZQ6FZ.js} +61 -0
  23. package/dist/{chunk-W34X3KBR.js → chunk-PHMWH23E.js} +1 -1
  24. package/dist/{chunk-DDN2AIGE.js → chunk-Q7Y57KOK.js} +2 -2
  25. package/dist/{chunk-LJH64PV3.js → chunk-QXKQPUOM.js} +3 -3
  26. package/dist/{chunk-CX42VC67.js → chunk-SYQWQ6FE.js} +1 -1
  27. package/dist/{chunk-5DSHUYSY.js → chunk-T5YYFDZB.js} +1 -1
  28. package/dist/{chunk-RMRTQGW3.js → chunk-THVM6EP5.js} +371 -13
  29. package/dist/{chunk-XMHSGPLN.js → chunk-VRKK2CSZ.js} +111 -90
  30. package/dist/{chunk-OL2UEHRP.js → chunk-WUDSNHWF.js} +1 -1
  31. package/dist/{chunk-XQCAMKIQ.js → chunk-WZDKNF37.js} +250 -106
  32. package/dist/{chunk-PRAZBHNF.js → chunk-YLL7MF5C.js} +15 -15
  33. package/dist/{chunk-YZDZ4FGR.js → chunk-ZVC23LKV.js} +1 -1
  34. package/dist/compare.cjs +100 -97
  35. package/dist/compare.js +3 -3
  36. package/dist/core/commands/formatting-commands.d.cts +1 -1
  37. package/dist/core/commands/formatting-commands.d.ts +1 -1
  38. package/dist/core/commands/image-commands.cjs +16 -5
  39. package/dist/core/commands/image-commands.d.cts +1 -1
  40. package/dist/core/commands/image-commands.d.ts +1 -1
  41. package/dist/core/commands/image-commands.js +5 -5
  42. package/dist/core/commands/section-layout-commands.d.cts +1 -1
  43. package/dist/core/commands/section-layout-commands.d.ts +1 -1
  44. package/dist/core/commands/style-commands.d.cts +1 -1
  45. package/dist/core/commands/style-commands.d.ts +1 -1
  46. package/dist/core/commands/table-structure-commands.cjs +16 -5
  47. package/dist/core/commands/table-structure-commands.d.cts +1 -1
  48. package/dist/core/commands/table-structure-commands.d.ts +1 -1
  49. package/dist/core/commands/table-structure-commands.js +4 -4
  50. package/dist/core/commands/text-commands.cjs +16 -5
  51. package/dist/core/commands/text-commands.d.cts +1 -1
  52. package/dist/core/commands/text-commands.d.ts +1 -1
  53. package/dist/core/commands/text-commands.js +5 -5
  54. package/dist/core/selection/mapping.d.cts +1 -1
  55. package/dist/core/selection/mapping.d.ts +1 -1
  56. package/dist/core/state/editor-state.d.cts +1 -1
  57. package/dist/core/state/editor-state.d.ts +1 -1
  58. package/dist/index.cjs +1280 -347
  59. package/dist/index.d.cts +4 -4
  60. package/dist/index.d.ts +4 -4
  61. package/dist/index.js +24 -24
  62. package/dist/io/docx-session.cjs +216 -203
  63. package/dist/io/docx-session.d.cts +3 -3
  64. package/dist/io/docx-session.d.ts +3 -3
  65. package/dist/io/docx-session.js +6 -6
  66. package/dist/legal.cjs +9 -20
  67. package/dist/legal.js +3 -3
  68. package/dist/{loader-4qsw4eIU.d.ts → loader-B-aL5HGD.d.ts} +2 -2
  69. package/dist/{loader-B8TKhmQi.d.cts → loader-DiY_ZgKl.d.cts} +2 -2
  70. package/dist/{measurement-backend-canvas-Q3MJMEYX.js → measurement-backend-canvas-F7ZYDACK.js} +1 -1
  71. package/dist/{public-types-p9b8rfy8.d.ts → public-types-DyqnxxO9.d.ts} +124 -1
  72. package/dist/{public-types-B5CRoR6f.d.cts → public-types-gvubspUI.d.cts} +124 -1
  73. package/dist/public-types.cjs +595 -18
  74. package/dist/public-types.d.cts +1 -1
  75. package/dist/public-types.d.ts +1 -1
  76. package/dist/public-types.js +4 -4
  77. package/dist/runtime/collab.d.cts +2 -2
  78. package/dist/runtime/collab.d.ts +2 -2
  79. package/dist/runtime/document-runtime.cjs +908 -129
  80. package/dist/runtime/document-runtime.d.cts +1 -1
  81. package/dist/runtime/document-runtime.d.ts +1 -1
  82. package/dist/runtime/document-runtime.js +17 -17
  83. package/dist/{session-BnGIjaex.d.cts → session-BUN6B-Vj.d.cts} +2 -2
  84. package/dist/{session-vEYKf-w3.d.ts → session-CDB0hohT.d.ts} +2 -2
  85. package/dist/session.cjs +216 -203
  86. package/dist/session.d.cts +4 -4
  87. package/dist/session.d.ts +4 -4
  88. package/dist/session.js +7 -7
  89. package/dist/tailwind.cjs +595 -18
  90. package/dist/tailwind.d.cts +1 -1
  91. package/dist/tailwind.d.ts +1 -1
  92. package/dist/tailwind.js +8 -8
  93. package/dist/{types-BLuvZ6cQ.d.cts → types-C4bz3kDU.d.cts} +1 -1
  94. package/dist/{types-Dutlyj0T.d.ts → types-VWH6CRvG.d.ts} +1 -1
  95. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +2 -2
  96. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +2 -2
  97. package/dist/ui-tailwind/editor-surface/search-plugin.js +5 -5
  98. package/dist/ui-tailwind.cjs +595 -18
  99. package/dist/ui-tailwind.d.cts +2 -2
  100. package/dist/ui-tailwind.d.ts +2 -2
  101. package/dist/ui-tailwind.js +8 -8
  102. package/package.json +4 -1
@@ -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,
@@ -11018,6 +11226,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
11018
11226
  ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
11019
11227
  });
11020
11228
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
11229
+ const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
11021
11230
  const activeStoryBlockPathBase = getActiveStoryBlockPathBase(document2, activeStory);
11022
11231
  chartModelStore.beginBuildPass(document2);
11023
11232
  const unsupportedNumberingFormatsSeen = options.emitFormattingTelemetry ? /* @__PURE__ */ new Set() : null;
@@ -11041,6 +11250,7 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
11041
11250
  activeStory.kind !== "main",
11042
11251
  !isInViewport,
11043
11252
  editableTargetsByBlockPath,
11253
+ layoutIdentitiesByBlockPath,
11044
11254
  `${activeStoryBlockPathBase}/block[${index}]`
11045
11255
  );
11046
11256
  if (isInViewport) {
@@ -11107,7 +11317,7 @@ function isIndexInAnyRange(index, ranges) {
11107
11317
  }
11108
11318
  return false;
11109
11319
  }
11110
- 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) {
11111
11321
  if (block.type === "opaque_block") {
11112
11322
  const fragment = getOpaqueFragment(document2.preservation, block.fragmentId);
11113
11323
  const descriptor = fragment ? describeOpaqueFragment(fragment) : null;
@@ -11144,6 +11354,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11144
11354
  promoteSecondaryStoryTextBoxes,
11145
11355
  cullBuild,
11146
11356
  editableTargetsByBlockPath,
11357
+ layoutIdentitiesByBlockPath,
11147
11358
  blockPath
11148
11359
  );
11149
11360
  }
@@ -11184,6 +11395,7 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11184
11395
  promoteSecondaryStoryTextBoxes,
11185
11396
  cullBuild,
11186
11397
  editableTargetsByBlockPath,
11398
+ layoutIdentitiesByBlockPath,
11187
11399
  blockPath
11188
11400
  );
11189
11401
  }
@@ -11266,10 +11478,11 @@ function createSurfaceBlock(block, document2, cursor, counters, formattingContex
11266
11478
  cullBuild,
11267
11479
  editableTargetsByBlockPath,
11268
11480
  blockPath,
11269
- 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
11270
11483
  );
11271
11484
  }
11272
- 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) {
11273
11486
  const lockedFragmentIds = [];
11274
11487
  let innerCursor = cursor;
11275
11488
  if (cullBuild) {
@@ -11286,6 +11499,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11286
11499
  promoteSecondaryStoryTextBoxes,
11287
11500
  true,
11288
11501
  editableTargetsByBlockPath,
11502
+ layoutIdentitiesByBlockPath,
11289
11503
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
11290
11504
  );
11291
11505
  lockedFragmentIds.push(...result.lockedFragmentIds);
@@ -11301,6 +11515,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11301
11515
  to: innerCursor,
11302
11516
  styleId: table.styleId,
11303
11517
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
11518
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
11304
11519
  gridColumns: table.gridColumns,
11305
11520
  rows: []
11306
11521
  },
@@ -11338,6 +11553,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11338
11553
  promoteSecondaryStoryTextBoxes,
11339
11554
  cullBuild,
11340
11555
  editableTargetsByBlockPath,
11556
+ layoutIdentitiesByBlockPath,
11341
11557
  tablePath !== void 0 ? `${tablePath}/row[${rowIndex}]/cell[${cellIndex}]/block[${childIndex}]` : void 0
11342
11558
  );
11343
11559
  cellContent.push(result.block);
@@ -11426,6 +11642,7 @@ function createTableBlock(tableIndex, table, document2, cursor, counters, format
11426
11642
  to: innerCursor,
11427
11643
  styleId: table.styleId,
11428
11644
  ...table.sourceRef !== void 0 ? { sourceRef: table.sourceRef } : {},
11645
+ ...tablePath !== void 0 && layoutIdentitiesByBlockPath?.get(tablePath) !== void 0 ? { layoutIdentity: layoutIdentitiesByBlockPath.get(tablePath) } : {},
11429
11646
  gridColumns: table.gridColumns,
11430
11647
  ...gridColumnsRelative ? { gridColumnsRelative } : {},
11431
11648
  ...resolvedTable.table?.alignment ? { alignment: resolvedTable.table.alignment } : {},
@@ -11613,7 +11830,7 @@ function resolveCellBorderStyles(borders, tableBorders, position) {
11613
11830
  if (left) result.borderLeft = left;
11614
11831
  return result;
11615
11832
  }
11616
- 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) {
11617
11834
  const children = [];
11618
11835
  const lockedFragmentIds = [];
11619
11836
  let innerCursor = cursor;
@@ -11630,6 +11847,7 @@ function createSdtBlock(sdtIndex, block, document2, cursor, counters, formatting
11630
11847
  promoteSecondaryStoryTextBoxes,
11631
11848
  cullBuild,
11632
11849
  editableTargetsByBlockPath,
11850
+ layoutIdentitiesByBlockPath,
11633
11851
  sdtPath !== void 0 ? `${sdtPath}/block[${childIndex}]` : void 0
11634
11852
  );
11635
11853
  children.push(result.block);
@@ -11672,7 +11890,7 @@ function getRecursableSdtBlockedReasonCode(block) {
11672
11890
  ].filter(Boolean).join(" ").toLowerCase();
11673
11891
  return searchText.includes("table of contents") || /\btoc\b/u.test(searchText) ? "workflow_preserve_only" : null;
11674
11892
  }
11675
- 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) {
11676
11894
  const themeResolver = formattingContext.theme;
11677
11895
  const effectiveNumbering = formattingContext.resolveEffectiveParagraphNumbering(paragraph);
11678
11896
  let resolvedNumbering = null;
@@ -11695,6 +11913,7 @@ function createParagraphBlock(paragraphIndex, paragraph, document2, start, forma
11695
11913
  from: start,
11696
11914
  to: start,
11697
11915
  ...editableTarget !== void 0 ? { editableTarget } : {},
11916
+ ...layoutIdentity !== void 0 ? { layoutIdentity } : {},
11698
11917
  ...paragraph.styleId ? { styleId: paragraph.styleId } : {},
11699
11918
  ...effectiveNumbering ? { numbering: effectiveNumbering } : {},
11700
11919
  ...resolvedNumbering ? {
@@ -25171,7 +25390,7 @@ function buildFormattingDebugEntry(inputs) {
25171
25390
  }
25172
25391
 
25173
25392
  // src/runtime/layout/layout-engine-version.ts
25174
- var LAYOUT_ENGINE_VERSION = 89;
25393
+ var LAYOUT_ENGINE_VERSION = 93;
25175
25394
 
25176
25395
  // src/runtime/layout/compat-input-ledger.ts
25177
25396
  var DEFAULT_COMPATIBILITY_MODE = 15;
@@ -28207,6 +28426,76 @@ function diffRenderFrames(prev, next) {
28207
28426
  }
28208
28427
  return { addedPages, removedPages, unchangedPages, changedPages };
28209
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
+ }
28210
28499
  function diffPage(prev, next, prevIndex, nextIndex) {
28211
28500
  const changed = [];
28212
28501
  const bodyChanges = diffRegion(prev.regions.body, next.regions.body, prevIndex, nextIndex);
@@ -28454,10 +28743,12 @@ function createRenderKernel(input) {
28454
28743
  emit({ kind: "frame_built", revision: frame.revision, reason: "full" });
28455
28744
  const diffT0 = typeof performance !== "undefined" ? performance.now() : 0;
28456
28745
  const diff = diffRenderFrames(lastEmittedFrame, frame);
28746
+ const patchPlan = createPagePatchPlan(lastEmittedFrame, frame, diff);
28747
+ recordPagePatchPlanTelemetry(patchPlan);
28457
28748
  if (diffT0 > 0) {
28458
28749
  recordPerfSample("render.frame_diff", performance.now() - diffT0);
28459
28750
  }
28460
- emit({ kind: "frame_diff", revision: frame.revision, diff });
28751
+ emit({ kind: "frame_diff", revision: frame.revision, diff, patchPlan });
28461
28752
  lastEmittedFrame = frame;
28462
28753
  }
28463
28754
  return frame;
@@ -28480,6 +28771,35 @@ function createRenderKernel(input) {
28480
28771
  }
28481
28772
  };
28482
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
+ }
28483
28803
  function buildPage(page, topPx, zoom, activeStory, facet) {
28484
28804
  const layout = page.layout;
28485
28805
  const widthPx = layout.pageWidth * zoom.pxPerTwip;
@@ -31329,26 +31649,35 @@ function buildParagraphPropertiesXml(paragraph) {
31329
31649
  if (frameXml) children.push(frameXml);
31330
31650
  }
31331
31651
  pushOnOffParagraphProperty(children, "widowControl", paragraph.widowControl);
31332
- if (paragraph.outlineLevel !== void 0) {
31333
- children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
31334
- }
31335
31652
  if (paragraph.numbering) {
31336
31653
  children.push(serializeParagraphNumberingProperties(paragraph.numbering));
31337
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);
31338
31672
  if (paragraph.spacing) {
31339
31673
  const s = paragraph.spacing;
31340
31674
  const attrs = [];
31341
31675
  if (s.before !== void 0) attrs.push(`w:before="${twip(s.before)}"`);
31342
31676
  if (s.after !== void 0) attrs.push(`w:after="${twip(s.after)}"`);
31343
31677
  if (s.line !== void 0) attrs.push(`w:line="${twip(s.line)}"`);
31344
- if (s.lineRule !== void 0) attrs.push(`w:lineRule="${s.lineRule}"`);
31678
+ if (s.lineRule !== void 0) attrs.push(`w:lineRule="${escapeXmlAttribute2(s.lineRule)}"`);
31345
31679
  if (attrs.length > 0) children.push(`<w:spacing ${attrs.join(" ")}/>`);
31346
31680
  }
31347
- if (paragraph.contextualSpacing === true) {
31348
- children.push("<w:contextualSpacing/>");
31349
- } else if (paragraph.contextualSpacing === false) {
31350
- children.push(`<w:contextualSpacing w:val="false"/>`);
31351
- }
31352
31681
  if (paragraph.indentation) {
31353
31682
  const ind = paragraph.indentation;
31354
31683
  const attrs = [];
@@ -31358,33 +31687,20 @@ function buildParagraphPropertiesXml(paragraph) {
31358
31687
  if (ind.hanging !== void 0) attrs.push(`w:hanging="${twip(ind.hanging)}"`);
31359
31688
  if (attrs.length > 0) children.push(`<w:ind ${attrs.join(" ")}/>`);
31360
31689
  }
31361
- if (paragraph.alignment) {
31362
- 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"/>`);
31363
31694
  }
31364
- if (paragraph.borders) {
31365
- const bordersXml = serializeParagraphBorders(paragraph.borders);
31366
- if (bordersXml) {
31367
- children.push(bordersXml);
31368
- }
31695
+ if (paragraph.alignment) {
31696
+ children.push(`<w:jc w:val="${escapeXmlAttribute2(paragraph.alignment)}"/>`);
31369
31697
  }
31370
- if (paragraph.shading) {
31371
- const shadingXml = serializeParagraphShading(paragraph.shading);
31372
- if (shadingXml) {
31373
- children.push(shadingXml);
31374
- }
31698
+ if (paragraph.outlineLevel !== void 0) {
31699
+ children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
31375
31700
  }
31376
- pushOnOffParagraphProperty(children, "bidi", paragraph.bidi);
31377
- pushOnOffParagraphProperty(children, "suppressLineNumbers", paragraph.suppressLineNumbers);
31378
31701
  if (paragraph.cnfStyle) {
31379
31702
  children.push(`<w:cnfStyle w:val="${escapeXmlAttribute2(paragraph.cnfStyle)}"/>`);
31380
31703
  }
31381
- if (paragraph.tabStops && paragraph.tabStops.length > 0) {
31382
- const tabsXml = paragraph.tabStops.map((tab) => {
31383
- const leaderAttr = tab.leader ? ` w:leader="${tab.leader}"` : "";
31384
- return `<w:tab w:val="${tab.align}" w:pos="${twip(tab.position)}"${leaderAttr}/>`;
31385
- }).join("");
31386
- children.push(`<w:tabs>${tabsXml}</w:tabs>`);
31387
- }
31388
31704
  if (children.length === 0) return "";
31389
31705
  return `<w:pPr>${children.join("")}</w:pPr>`;
31390
31706
  }
@@ -31949,79 +32265,86 @@ function serializeRunProperties(marks) {
31949
32265
  if (!marks || marks.length === 0) {
31950
32266
  return "";
31951
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
+ ];
31952
32291
  const markParts = [];
31953
- for (const mark of marks) {
31954
- switch (mark.type) {
31955
- case "bold":
31956
- markParts.push("<w:b/>");
31957
- break;
31958
- case "italic":
31959
- markParts.push("<w:i/>");
31960
- break;
31961
- case "underline":
31962
- markParts.push(`<w:u w:val="single"/>`);
31963
- break;
31964
- case "strikethrough":
31965
- markParts.push("<w:strike/>");
31966
- break;
31967
- case "doubleStrikethrough":
31968
- markParts.push("<w:dstrike/>");
31969
- break;
31970
- case "vanish":
31971
- markParts.push("<w:vanish/>");
31972
- break;
31973
- case "lang":
31974
- markParts.push(`<w:lang w:val="${escapeXmlAttribute2(mark.val)}"/>`);
31975
- break;
31976
- case "highlight":
31977
- markParts.push(`<w:highlight w:val="${escapeXmlAttribute2(mark.val)}"/>`);
31978
- break;
31979
- case "backgroundColor":
31980
- markParts.push(
31981
- `<w:shd w:val="clear" w:color="auto" w:fill="${escapeXmlAttribute2(mark.color)}"/>`
31982
- );
31983
- break;
31984
- case "charSpacing":
31985
- markParts.push(`<w:spacing w:val="${mark.val}"/>`);
31986
- break;
31987
- case "kerning":
31988
- markParts.push(`<w:kern w:val="${mark.val}"/>`);
31989
- break;
31990
- case "emboss":
31991
- markParts.push("<w:emboss/>");
31992
- break;
31993
- case "imprint":
31994
- markParts.push("<w:imprint/>");
31995
- break;
31996
- case "shadow":
31997
- markParts.push("<w:shadow/>");
31998
- break;
31999
- case "position":
32000
- markParts.push(`<w:position w:val="${mark.val}"/>`);
32001
- break;
32002
- case "textFill":
32003
- markParts.push(mark.xml);
32004
- break;
32005
- case "fontFamily":
32006
- markParts.push(`<w:rFonts w:ascii="${escapeXmlAttribute2(mark.val)}" w:hAnsi="${escapeXmlAttribute2(mark.val)}"/>`);
32007
- break;
32008
- case "fontSize":
32009
- markParts.push(`<w:sz w:val="${mark.val}"/>`);
32010
- break;
32011
- case "textColor":
32012
- markParts.push(`<w:color w:val="${escapeXmlAttribute2(mark.color)}"/>`);
32013
- break;
32014
- case "smallCaps":
32015
- markParts.push("<w:smallCaps/>");
32016
- break;
32017
- case "allCaps":
32018
- markParts.push("<w:caps/>");
32019
- 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);
32020
32297
  }
32021
32298
  }
32022
32299
  const children = markParts.join("");
32023
32300
  return children.length > 0 ? `<w:rPr>${children}</w:rPr>` : "";
32024
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
+ }
32025
32348
  function requiresPreservedSpace(text) {
32026
32349
  return /^\s/.test(text) || /\s$/.test(text) || text.includes(" ");
32027
32350
  }
@@ -36613,6 +36936,19 @@ function paragraphHasBookmarkId(paragraph, bookmarkId) {
36613
36936
  function commandActionHandleForAddress(commandFamily, address) {
36614
36937
  return address ? `scope-command:${commandFamily}:${address.addressKey}` : void 0;
36615
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
+ }
36616
36952
  function withCommandAction(evidence, target, canonicalAddress = target.canonicalAddress) {
36617
36953
  if (evidence.status !== "supported" || !canonicalAddress) return evidence;
36618
36954
  return {
@@ -36624,6 +36960,24 @@ function withCommandAction(evidence, target, canonicalAddress = target.canonical
36624
36960
  function commandAddressForTarget(target, scopeKind) {
36625
36961
  return scopeKind === "list-item" && target.listAddress !== void 0 ? target.listAddress : target.canonicalAddress;
36626
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
+ }
36627
36981
  function runtimeTextCommandEvidence(target, workflowBlockers) {
36628
36982
  const shapeIssues = validateEditableTargetRef(target);
36629
36983
  if (shapeIssues.length > 0) {
@@ -36723,13 +37077,16 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand, scopeKind
36723
37077
  };
36724
37078
  }
36725
37079
  if (target.commandFamily === "text-leaf") {
37080
+ const canonicalAddress = commandAddressForTarget(target, scopeKind);
37081
+ const relatedActionHandles = relatedListActionHandlesForTarget(target, canonicalAddress);
36726
37082
  return withCommandAction({
36727
37083
  status: textCommand.status,
36728
37084
  commandFamily: target.commandFamily,
36729
37085
  intents: commandIntentsForTarget(target, scopeKind),
36730
37086
  reason: textCommand.reason,
37087
+ ...relatedActionHandles !== void 0 ? { relatedActionHandles } : {},
36731
37088
  ...textCommand.blockers ? { blockers: textCommand.blockers } : {}
36732
- }, target, commandAddressForTarget(target, scopeKind));
37089
+ }, target, canonicalAddress);
36733
37090
  }
36734
37091
  if (target.commandFamily === "comment-revision") {
36735
37092
  const isOpen = target.review?.status === "open";
@@ -37054,19 +37411,21 @@ function commandIntentsForTarget(target, scopeKind) {
37054
37411
  if (target.kind === "hyperlink-text") {
37055
37412
  return Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]);
37056
37413
  }
37057
- if (target.table?.operationScope === "text") {
37058
- return Object.freeze([
37059
- "text-leaf-edit",
37060
- "table-text-paste",
37061
- "table-text-drop",
37062
- "table-structured-fragment-paste",
37063
- "table-structured-fragment-drop"
37064
- ]);
37065
- }
37066
- if (scopeKind === "list-item" && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
37067
- 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);
37068
37428
  }
37069
- return Object.freeze(["text-leaf-edit"]);
37070
37429
  case "field":
37071
37430
  return Object.freeze(
37072
37431
  target.field?.fieldFamily === "TOC" ? ["toc-refresh", "field-update"] : ["field-update"]
@@ -37213,7 +37572,7 @@ function readbackForTarget(document2, target, runtimeTextCommand) {
37213
37572
  source: "canonical-text-leaf"
37214
37573
  };
37215
37574
  }
37216
- function projectTarget(document2, target, relation, workflowBlockers, scopeKind) {
37575
+ function projectTarget(document2, target, relation, workflowBlockers, scopeKind, layoutIdentity) {
37217
37576
  const runtimeTextCommand = runtimeTextCommandEvidence(target, workflowBlockers);
37218
37577
  const readback = readbackForTarget(document2, target, runtimeTextCommand);
37219
37578
  return {
@@ -37234,6 +37593,7 @@ function projectTarget(document2, target, relation, workflowBlockers, scopeKind)
37234
37593
  ...target.table ? { table: target.table } : {},
37235
37594
  ...target.editableOwner ? { editableOwner: target.editableOwner } : {},
37236
37595
  ...target.canonicalAddress ? { canonicalAddress: target.canonicalAddress } : {},
37596
+ ...layoutIdentity ? { layoutIdentity } : {},
37237
37597
  staleCheck: target.staleCheck,
37238
37598
  posture: target.posture,
37239
37599
  ...workflowBlockers.length > 0 ? { workflowBlockers: Object.freeze([...workflowBlockers]) } : {},
@@ -37249,7 +37609,7 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
37249
37609
  facts.push(projectWorkflowBlockerFact(fact));
37250
37610
  factsByTargetKey.set(fact.targetKey, facts);
37251
37611
  }
37252
- const entries = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
37612
+ const relatedTargets = collectEditableTargetRefs(document2, options.editableTargetCache).map((target) => {
37253
37613
  const relation = relationForTarget(target, scope, entry);
37254
37614
  if (!relation) return null;
37255
37615
  const workflowBlockers = Object.freeze(
@@ -37257,8 +37617,19 @@ function deriveScopeEditableTargetEvidence(document2, scope, entry, options = {}
37257
37617
  (left, right) => left.source.localeCompare(right.source) || left.blocker.localeCompare(right.blocker) || left.refusalId.localeCompare(right.refusalId)
37258
37618
  )
37259
37619
  );
37260
- return projectTarget(document2, target, relation, workflowBlockers, scope.kind);
37261
- }).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));
37262
37633
  const blockers = /* @__PURE__ */ new Set();
37263
37634
  let supportedTextTargetCount = 0;
37264
37635
  for (const target of entries) {
@@ -37322,15 +37693,65 @@ function projectEntry2(entry) {
37322
37693
  ...entry.posture.preserveOnly ? { preserveOnly: true } : {}
37323
37694
  };
37324
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
+ }
37325
37744
  function deriveScopeObjectEvidence(editableTargets) {
37326
37745
  const entries = Object.freeze(
37327
37746
  [...editableTargets?.entries ?? []].filter(isObjectTarget).map(projectEntry2).sort((left, right) => left.targetKey.localeCompare(right.targetKey))
37328
37747
  );
37329
37748
  const blockers = unique2(entries.flatMap((entry) => [...entry.blockers]));
37749
+ const families = summarizeFamilies(entries);
37330
37750
  return {
37331
37751
  status: entries.length > 0 ? "present" : "none",
37332
37752
  count: entries.length,
37333
37753
  blockers,
37754
+ families,
37334
37755
  entries
37335
37756
  };
37336
37757
  }
@@ -46069,9 +46490,327 @@ async function createCanvasProvider(fontLoader) {
46069
46490
  }
46070
46491
  }
46071
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
+
46072
46805
  // src/runtime/layout/layout-engine-instance.ts
46073
46806
  var FULL_VIEWPORT_WINDOW_KEY = "full";
46074
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
+ }
46075
46814
  function normalizeViewportPageWindow(window2) {
46076
46815
  if (!window2) return void 0;
46077
46816
  const buffer = Number.isFinite(window2.bufferPages) ? Math.max(0, Math.floor(window2.bufferPages ?? 0)) : DEFAULT_VIEWPORT_PAGE_WINDOW_BUFFER;
@@ -46267,8 +47006,22 @@ function recordFullRebuildReason(reasonKind) {
46267
47006
  probe.invalidationCounts[totalKey] = (probe.invalidationCounts[totalKey] ?? 0) + 1;
46268
47007
  probe.invalidationCounts[byReasonKey] = (probe.invalidationCounts[byReasonKey] ?? 0) + 1;
46269
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
+ }
46270
47019
  function createLayoutEngine(options = {}) {
46271
- let measurementProvider = options.measurementProvider ?? createEmpiricalMeasurementProvider();
47020
+ let measurementProvider = wrapMeasurementProvider(
47021
+ options.measurementProvider ?? createEmpiricalMeasurementProvider(),
47022
+ options.measurementCache,
47023
+ options.telemetryBus
47024
+ );
46272
47025
  const autoUpgradeToCanvas = options.autoUpgradeToCanvasBackend !== false;
46273
47026
  const telemetryBus = options.telemetryBus;
46274
47027
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
@@ -46368,13 +47121,20 @@ function createLayoutEngine(options = {}) {
46368
47121
  const pageCountBeforeRecompute = previousPageCount;
46369
47122
  const document2 = input.document;
46370
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
+ );
46371
47131
  const mainSurface = createEditorSurfaceSnapshot(
46372
47132
  document2,
46373
47133
  createSelectionSnapshot(0, 0),
46374
- MAIN_STORY_TARGET
47134
+ MAIN_STORY_TARGET,
47135
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
46375
47136
  );
46376
47137
  const sections = buildResolvedSections(document2);
46377
- const layoutInputs = collectCanonicalLayoutInputs(document2);
46378
47138
  const fieldRegions = layoutInputs.fieldRegions;
46379
47139
  const pageStack = buildPageStackWithSplits(
46380
47140
  document2,
@@ -46486,10 +47246,18 @@ function createLayoutEngine(options = {}) {
46486
47246
  const firstDirty = range.firstPageIndex;
46487
47247
  if (firstDirty < 0 || firstDirty >= priorGraph.pages.length) return null;
46488
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
+ );
46489
47256
  const mainSurface = createEditorSurfaceSnapshot(
46490
47257
  document2,
46491
47258
  createSelectionSnapshot(0, 0),
46492
- MAIN_STORY_TARGET
47259
+ MAIN_STORY_TARGET,
47260
+ { editableTargetsByBlockPath, layoutIdentitiesByBlockPath }
46493
47261
  );
46494
47262
  const sections = buildResolvedSections(document2);
46495
47263
  const dirtyPage = priorGraph.pages[firstDirty];
@@ -46518,7 +47286,6 @@ function createLayoutEngine(options = {}) {
46518
47286
  const freshSnapshotsToRebuild = freshSnapshots.slice(0, convergenceIndex);
46519
47287
  const convergedTailStart = convergenceIndex < freshSnapshots.length ? firstDirty + convergenceIndex : void 0;
46520
47288
  const freshStories = resolvePageStories(freshSnapshotsToRebuild);
46521
- const layoutInputs = collectCanonicalLayoutInputs(document2);
46522
47289
  const fieldRegions = layoutInputs.fieldRegions;
46523
47290
  const freshBodyFragmentsByPageIndex = projectSurfaceBlocksToPageFragments(
46524
47291
  mainSurface,
@@ -46650,7 +47417,11 @@ function createLayoutEngine(options = {}) {
46650
47417
  try {
46651
47418
  const mod = await Promise.resolve().then(() => (init_measurement_backend_canvas(), measurement_backend_canvas_exports));
46652
47419
  const canvasProvider = mod.createCanvasBackend();
46653
- measurementProvider = canvasProvider;
47420
+ measurementProvider = wrapMeasurementProvider(
47421
+ canvasProvider,
47422
+ options.measurementCache,
47423
+ telemetryBus
47424
+ );
46654
47425
  cachedKey = null;
46655
47426
  cachedGraph = null;
46656
47427
  cachedFormatting = null;
@@ -46735,7 +47506,11 @@ function createLayoutEngine(options = {}) {
46735
47506
  },
46736
47507
  swapMeasurementProvider(provider) {
46737
47508
  const previousFidelity = measurementProvider.fidelity;
46738
- measurementProvider = provider;
47509
+ measurementProvider = wrapMeasurementProvider(
47510
+ provider,
47511
+ options.measurementCache,
47512
+ telemetryBus
47513
+ );
46739
47514
  if (previousFidelity !== provider.fidelity) {
46740
47515
  cachedKey = null;
46741
47516
  cachedGraph = null;
@@ -46764,6 +47539,10 @@ function createLayoutEngine(options = {}) {
46764
47539
  cachedFormatting = null;
46765
47540
  cachedMapper = null;
46766
47541
  },
47542
+ getMeasurementCacheStats() {
47543
+ if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
47544
+ return measurementProvider.measurementCacheStats();
47545
+ },
46767
47546
  /**
46768
47547
  * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
46769
47548
  * Populates both `cachedGraph` and `cachedKey` (keyed on the provided