@beyondwork/docx-react-component 1.0.132 → 1.0.134

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 (94) hide show
  1. package/dist/api/public-types.cjs +161 -68
  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 +3 -3
  5. package/dist/api/v3.cjs +9878 -7387
  6. package/dist/api/v3.d.cts +2 -2
  7. package/dist/api/v3.d.ts +2 -2
  8. package/dist/api/v3.js +10 -10
  9. package/dist/{chunk-QUTVR72L.js → chunk-3YR47WTD.js} +296 -587
  10. package/dist/{chunk-RYMMKOFI.js → chunk-5KTJKTNE.js} +32 -0
  11. package/dist/{chunk-UP2KDOYE.js → chunk-74R5B2EZ.js} +6 -2
  12. package/dist/{chunk-6736GA6J.js → chunk-7Y6JCIK3.js} +1 -1
  13. package/dist/{chunk-43JAPM2F.js → chunk-EBSI6VQX.js} +546 -144
  14. package/dist/{chunk-JVTDBX67.js → chunk-EFEW7BTT.js} +2 -2
  15. package/dist/{chunk-YUHNDEV5.js → chunk-ESEEWELA.js} +3534 -1870
  16. package/dist/{chunk-XYTWOJII.js → chunk-IJD6D7HU.js} +745 -103
  17. package/dist/{chunk-UFPBYJMA.js → chunk-INLRCC4N.js} +2 -2
  18. package/dist/{chunk-N5FTU4HZ.js → chunk-MQ5GAJ54.js} +68 -39
  19. package/dist/{chunk-W2I47J2Q.js → chunk-NJFKPDNG.js} +216 -2
  20. package/dist/{chunk-LPLJZJT2.js → chunk-O4EDZR44.js} +131 -70
  21. package/dist/{chunk-4HGFJ6Z2.js → chunk-PZIEOEJZ.js} +1 -1
  22. package/dist/{chunk-C5LXKR54.js → chunk-QTRJLKR2.js} +1 -1
  23. package/dist/{chunk-SZ6BJA4Q.js → chunk-REFHJ2FN.js} +3 -3
  24. package/dist/{chunk-ZDYGRO2Z.js → chunk-RP76USJE.js} +1 -1
  25. package/dist/{chunk-RBWJHRNP.js → chunk-T66OS7MN.js} +8 -3
  26. package/dist/{chunk-ALWXYGXP.js → chunk-V2JF42SI.js} +2 -2
  27. package/dist/{chunk-CDEZGLQ3.js → chunk-VA24T4EB.js} +1 -1
  28. package/dist/{chunk-6TLZ6CMP.js → chunk-WDDFU2N2.js} +2 -2
  29. package/dist/{chunk-U3UMKA7B.js → chunk-XBQFDBXE.js} +1 -1
  30. package/dist/core/commands/formatting-commands.d.cts +1 -1
  31. package/dist/core/commands/formatting-commands.d.ts +1 -1
  32. package/dist/core/commands/image-commands.cjs +32 -0
  33. package/dist/core/commands/image-commands.d.cts +1 -1
  34. package/dist/core/commands/image-commands.d.ts +1 -1
  35. package/dist/core/commands/image-commands.js +5 -5
  36. package/dist/core/commands/section-layout-commands.d.cts +1 -1
  37. package/dist/core/commands/section-layout-commands.d.ts +1 -1
  38. package/dist/core/commands/style-commands.d.cts +1 -1
  39. package/dist/core/commands/style-commands.d.ts +1 -1
  40. package/dist/core/commands/table-structure-commands.cjs +32 -0
  41. package/dist/core/commands/table-structure-commands.d.cts +1 -1
  42. package/dist/core/commands/table-structure-commands.d.ts +1 -1
  43. package/dist/core/commands/table-structure-commands.js +4 -4
  44. package/dist/core/commands/text-commands.cjs +99 -38
  45. package/dist/core/commands/text-commands.d.cts +12 -1
  46. package/dist/core/commands/text-commands.d.ts +12 -1
  47. package/dist/core/commands/text-commands.js +5 -5
  48. package/dist/core/selection/mapping.d.cts +1 -1
  49. package/dist/core/selection/mapping.d.ts +1 -1
  50. package/dist/core/state/editor-state.d.cts +1 -1
  51. package/dist/core/state/editor-state.d.ts +1 -1
  52. package/dist/index.cjs +5365 -2298
  53. package/dist/index.d.cts +4 -4
  54. package/dist/index.d.ts +4 -4
  55. package/dist/index.js +388 -63
  56. package/dist/io/docx-session.cjs +7 -2
  57. package/dist/io/docx-session.d.cts +3 -3
  58. package/dist/io/docx-session.d.ts +3 -3
  59. package/dist/io/docx-session.js +4 -4
  60. package/dist/legal.js +3 -3
  61. package/dist/{loader-MAa8VpzW.d.cts → loader-CK3lZy4h.d.cts} +2 -2
  62. package/dist/{loader-CfpeEPAa.d.ts → loader-CQXplstv.d.ts} +2 -2
  63. package/dist/{public-types-KBS6JnOs.d.cts → public-types-BR1SYK2F.d.cts} +783 -189
  64. package/dist/{public-types-Cjs8glST.d.ts → public-types-DXNZVKrS.d.ts} +783 -189
  65. package/dist/public-types.cjs +161 -68
  66. package/dist/public-types.d.cts +1 -1
  67. package/dist/public-types.d.ts +1 -1
  68. package/dist/public-types.js +3 -3
  69. package/dist/runtime/collab.d.cts +2 -2
  70. package/dist/runtime/collab.d.ts +2 -2
  71. package/dist/runtime/document-runtime.cjs +1597 -444
  72. package/dist/runtime/document-runtime.d.cts +1 -1
  73. package/dist/runtime/document-runtime.d.ts +1 -1
  74. package/dist/runtime/document-runtime.js +14 -14
  75. package/dist/{session-CkoH8FoY.d.ts → session-C9UjrhJF.d.ts} +2 -2
  76. package/dist/{session-wwe0Gib-.d.cts → session-CSbwkgII.d.cts} +2 -2
  77. package/dist/session.cjs +7 -2
  78. package/dist/session.d.cts +4 -4
  79. package/dist/session.d.ts +4 -4
  80. package/dist/session.js +5 -5
  81. package/dist/tailwind.cjs +451 -650
  82. package/dist/tailwind.d.cts +1 -1
  83. package/dist/tailwind.d.ts +1 -1
  84. package/dist/tailwind.js +7 -7
  85. package/dist/{types-B3SGRW0w.d.cts → types-CZtAueri.d.cts} +1 -1
  86. package/dist/{types-CH7NWqVL.d.ts → types-RzkCXDNV.d.ts} +1 -1
  87. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +2 -2
  88. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +2 -2
  89. package/dist/ui-tailwind/editor-surface/search-plugin.js +4 -4
  90. package/dist/ui-tailwind.cjs +451 -650
  91. package/dist/ui-tailwind.d.cts +3 -2
  92. package/dist/ui-tailwind.d.ts +3 -2
  93. package/dist/ui-tailwind.js +7 -7
  94. package/package.json +1 -1
@@ -6636,7 +6636,20 @@ function sourceJoinHash(refs) {
6636
6636
  if (sourceIds.length === 0) return void 0;
6637
6637
  return hashText(sourceIds.join("\0"));
6638
6638
  }
6639
+ var numberingCatalogHashCache = /* @__PURE__ */ new WeakMap();
6640
+ var numberingTargetHashCache = /* @__PURE__ */ new WeakMap();
6639
6641
  function createNumberingCatalogRevisionHash(doc) {
6642
+ const numbering = doc.numbering;
6643
+ if (numbering !== void 0) {
6644
+ const cached = numberingCatalogHashCache.get(numbering);
6645
+ if (cached !== void 0) return cached;
6646
+ const fresh = computeNumberingCatalogRevisionHash(doc);
6647
+ numberingCatalogHashCache.set(numbering, fresh);
6648
+ return fresh;
6649
+ }
6650
+ return computeNumberingCatalogRevisionHash(doc);
6651
+ }
6652
+ function computeNumberingCatalogRevisionHash(doc) {
6640
6653
  const catalog = doc.numbering ?? { abstractDefinitions: {}, instances: {} };
6641
6654
  const abstractDefinitions = catalog.abstractDefinitions ?? {};
6642
6655
  const instances = catalog.instances ?? {};
@@ -6687,8 +6700,27 @@ function createNumberingCatalogRevisionHash(doc) {
6687
6700
  }));
6688
6701
  }
6689
6702
  function createNumberingTargetRevisionHash(doc) {
6703
+ const numbering = doc.numbering;
6704
+ const styles = doc.styles;
6705
+ if (numbering !== void 0 && styles !== void 0) {
6706
+ let inner = numberingTargetHashCache.get(numbering);
6707
+ if (inner === void 0) {
6708
+ inner = /* @__PURE__ */ new WeakMap();
6709
+ numberingTargetHashCache.set(numbering, inner);
6710
+ }
6711
+ const cached = inner.get(styles);
6712
+ if (cached !== void 0) return cached;
6713
+ const fresh = computeNumberingTargetRevisionHash(doc);
6714
+ inner.set(styles, fresh);
6715
+ return fresh;
6716
+ }
6717
+ return computeNumberingTargetRevisionHash(doc);
6718
+ }
6719
+ function computeNumberingTargetRevisionHash(doc) {
6690
6720
  const paragraphStyles = doc.styles?.paragraphs ?? {};
6691
6721
  return hashText(JSON.stringify({
6722
+ // Reuses the memoized catalog hash on the hot path so a typing edit
6723
+ // pays for the catalog hash at most once across both helpers.
6692
6724
  numberingCatalogHash: createNumberingCatalogRevisionHash(doc),
6693
6725
  paragraphStyleNumbering: Object.keys(paragraphStyles).sort().map((styleId) => {
6694
6726
  const style = paragraphStyles[styleId];
@@ -10790,8 +10822,8 @@ function toLayoutTabStops(tabStops, source) {
10790
10822
  }
10791
10823
  function mergeLayoutTabStops(...groups) {
10792
10824
  const byKey = /* @__PURE__ */ new Map();
10793
- for (const group of groups) {
10794
- for (const tabStop of group ?? []) {
10825
+ for (const group2 of groups) {
10826
+ for (const tabStop of group2 ?? []) {
10795
10827
  const key = [
10796
10828
  tabStop.source,
10797
10829
  tabStop.positionTwips,
@@ -11376,7 +11408,7 @@ function collectParagraphCompatFlags(paragraph) {
11376
11408
  return flags;
11377
11409
  }
11378
11410
  function mergeCompatFlags(...groups) {
11379
- return [...new Set(groups.flatMap((group) => group ?? []))].sort();
11411
+ return [...new Set(groups.flatMap((group2) => group2 ?? []))].sort();
11380
11412
  }
11381
11413
  function buildRevisionRangeIndex(doc) {
11382
11414
  const records = [];
@@ -13586,7 +13618,38 @@ var TextTransactionError = class extends Error {
13586
13618
  this.code = code;
13587
13619
  }
13588
13620
  };
13621
+ function isUsableScopeSurface(surface, document2) {
13622
+ if (surface.viewportBlockRanges !== null) return false;
13623
+ const root = document2.content;
13624
+ if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
13625
+ return false;
13626
+ }
13627
+ return surface.blocks.length === root.children.length;
13628
+ }
13629
+ function createScopeSurfaceCache(document2, selection, precomputedSurface, telemetry) {
13630
+ let cached;
13631
+ if (precomputedSurface && isUsableScopeSurface(precomputedSurface, document2)) {
13632
+ cached = precomputedSurface;
13633
+ telemetry?.onPrecomputedSurfaceHit?.();
13634
+ }
13635
+ return {
13636
+ get() {
13637
+ if (cached) return cached;
13638
+ telemetry?.onScopeSurfaceBuild?.();
13639
+ cached = createEditorSurfaceSnapshot(document2, selection, void 0, {
13640
+ editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
13641
+ });
13642
+ return cached;
13643
+ }
13644
+ };
13645
+ }
13589
13646
  function applyTextTransaction(document2, selection, intent, options) {
13647
+ const scopeSurfaceCache = createScopeSurfaceCache(
13648
+ document2,
13649
+ selection,
13650
+ options.precomputedSurface,
13651
+ options.surfaceTelemetry
13652
+ );
13590
13653
  if (options.textTarget) {
13591
13654
  if (options.textTarget.kind === "hyperlink-display") {
13592
13655
  const targetedHyperlinkResult = tryApplyHyperlinkDisplayTransaction(
@@ -13622,7 +13685,8 @@ function applyTextTransaction(document2, selection, intent, options) {
13622
13685
  document2,
13623
13686
  selection,
13624
13687
  intent,
13625
- options
13688
+ options,
13689
+ scopeSurfaceCache
13626
13690
  );
13627
13691
  if (targetedTableResult) {
13628
13692
  return targetedTableResult;
@@ -13636,7 +13700,8 @@ function applyTextTransaction(document2, selection, intent, options) {
13636
13700
  const tableStructureRefusal = resolveTargetlessTableStructureInsertRefusal(
13637
13701
  document2,
13638
13702
  selection,
13639
- intent
13703
+ intent,
13704
+ scopeSurfaceCache
13640
13705
  );
13641
13706
  if (tableStructureRefusal) {
13642
13707
  throw new TextTransactionError("unsupported_content", tableStructureRefusal);
@@ -13646,7 +13711,8 @@ function applyTextTransaction(document2, selection, intent, options) {
13646
13711
  document2,
13647
13712
  selection,
13648
13713
  intent,
13649
- options
13714
+ options,
13715
+ scopeSurfaceCache
13650
13716
  );
13651
13717
  if (topLevelParagraphResult) {
13652
13718
  return topLevelParagraphResult;
@@ -13655,7 +13721,8 @@ function applyTextTransaction(document2, selection, intent, options) {
13655
13721
  document2,
13656
13722
  selection,
13657
13723
  intent,
13658
- options
13724
+ options,
13725
+ scopeSurfaceCache
13659
13726
  );
13660
13727
  if (sdtScopedResult) {
13661
13728
  return sdtScopedResult;
@@ -13664,7 +13731,8 @@ function applyTextTransaction(document2, selection, intent, options) {
13664
13731
  document2,
13665
13732
  selection,
13666
13733
  intent,
13667
- options
13734
+ options,
13735
+ scopeSurfaceCache
13668
13736
  );
13669
13737
  if (tableScopedResult) {
13670
13738
  return tableScopedResult;
@@ -13717,9 +13785,9 @@ function applyLinearTextTransaction(document2, selection, intent, options) {
13717
13785
  storyText: createPlainText(nextStory)
13718
13786
  };
13719
13787
  }
13720
- function tryApplyTableParagraphTransaction(document2, selection, intent, options) {
13788
+ function tryApplyTableParagraphTransaction(document2, selection, intent, options, scopeSurfaceCache) {
13721
13789
  const tableTarget = options.textTarget?.kind === "table-paragraph" ? options.textTarget : void 0;
13722
- const scope = resolveTableParagraphScope(document2, selection, tableTarget);
13790
+ const scope = resolveTableParagraphScope(document2, selection, tableTarget, scopeSurfaceCache);
13723
13791
  if (!scope) {
13724
13792
  return null;
13725
13793
  }
@@ -13842,7 +13910,11 @@ function tryApplyTargetedTextLeafTransaction(document2, selection, intent, optio
13842
13910
  };
13843
13911
  }
13844
13912
  function tryApplyHyperlinkDisplayTransaction(document2, selection, intent, options) {
13845
- const scope = resolveTargetedHyperlinkDisplayScope(document2, selection, options.textTarget);
13913
+ const scope = resolveTargetedHyperlinkDisplayScope(
13914
+ document2,
13915
+ selection,
13916
+ options.textTarget
13917
+ );
13846
13918
  if (!scope) {
13847
13919
  return null;
13848
13920
  }
@@ -13900,8 +13972,8 @@ function tryApplyHyperlinkDisplayTransaction(document2, selection, intent, optio
13900
13972
  storyText: localResult.storyText
13901
13973
  };
13902
13974
  }
13903
- function tryApplyTopLevelSdtParagraphTransaction(document2, selection, intent, options) {
13904
- const scope = resolveTopLevelSdtParagraphScope(document2, selection);
13975
+ function tryApplyTopLevelSdtParagraphTransaction(document2, selection, intent, options, scopeSurfaceCache) {
13976
+ const scope = resolveTopLevelSdtParagraphScope(document2, selection, scopeSurfaceCache);
13905
13977
  if (!scope) {
13906
13978
  return null;
13907
13979
  }
@@ -13953,8 +14025,8 @@ function tryApplyTopLevelSdtParagraphTransaction(document2, selection, intent, o
13953
14025
  storyText: localResult.storyText
13954
14026
  };
13955
14027
  }
13956
- function tryApplyTopLevelParagraphTransaction(document2, selection, intent, options) {
13957
- const scope = resolveTopLevelParagraphScope(document2, selection);
14028
+ function tryApplyTopLevelParagraphTransaction(document2, selection, intent, options, scopeSurfaceCache) {
14029
+ const scope = resolveTopLevelParagraphScope(document2, selection, scopeSurfaceCache);
13958
14030
  if (!scope) {
13959
14031
  return null;
13960
14032
  }
@@ -14064,17 +14136,15 @@ function resolveTargetedHyperlinkDisplayScope(document2, selection, target) {
14064
14136
  replace: resolved.replace
14065
14137
  };
14066
14138
  }
14067
- function resolveTableParagraphScope(document2, selection, target) {
14139
+ function resolveTableParagraphScope(document2, selection, target, scopeSurfaceCache) {
14068
14140
  if (target) {
14069
- return resolveTargetedTableParagraphScope(document2, selection, target);
14141
+ return resolveTargetedTableParagraphScope(document2, selection, target, scopeSurfaceCache);
14070
14142
  }
14071
14143
  const root = document2.content;
14072
14144
  if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
14073
14145
  return null;
14074
14146
  }
14075
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14076
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14077
- });
14147
+ const surface = scopeSurfaceCache.get();
14078
14148
  const selectionFrom = Math.min(selection.anchor, selection.head);
14079
14149
  const selectionTo = Math.max(selection.anchor, selection.head);
14080
14150
  for (let blockIndex = 0; blockIndex < root.children.length; blockIndex += 1) {
@@ -14179,7 +14249,7 @@ function resolveTableParagraphScope(document2, selection, target) {
14179
14249
  }
14180
14250
  return null;
14181
14251
  }
14182
- function resolveTargetlessTableStructureInsertRefusal(document2, selection, intent) {
14252
+ function resolveTargetlessTableStructureInsertRefusal(document2, selection, intent, scopeSurfaceCache) {
14183
14253
  if (!isCollapsedInsertIntent(intent, selection)) {
14184
14254
  return null;
14185
14255
  }
@@ -14187,9 +14257,7 @@ function resolveTargetlessTableStructureInsertRefusal(document2, selection, inte
14187
14257
  if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
14188
14258
  return null;
14189
14259
  }
14190
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14191
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14192
- });
14260
+ const surface = scopeSurfaceCache.get();
14193
14261
  if (!selectionTouchesTableStructureBoundary(surface.blocks, selection.anchor)) {
14194
14262
  return null;
14195
14263
  }
@@ -14203,7 +14271,7 @@ function isCollapsedInsertIntent(intent, selection) {
14203
14271
  const rangeTo = intent.range ? Math.max(intent.range.from, intent.range.to) : Math.max(selection.anchor, selection.head);
14204
14272
  return rangeFrom === rangeTo;
14205
14273
  }
14206
- function resolveTargetedTableParagraphScope(document2, selection, target) {
14274
+ function resolveTargetedTableParagraphScope(document2, selection, target, scopeSurfaceCache) {
14207
14275
  const root = document2.content;
14208
14276
  if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
14209
14277
  return null;
@@ -14216,7 +14284,8 @@ function resolveTargetedTableParagraphScope(document2, selection, target) {
14216
14284
  root,
14217
14285
  document2,
14218
14286
  selection,
14219
- target
14287
+ target,
14288
+ scopeSurfaceCache
14220
14289
  );
14221
14290
  if (targetScope) {
14222
14291
  return targetScope;
@@ -14239,9 +14308,7 @@ function resolveTargetedTableParagraphScope(document2, selection, target) {
14239
14308
  if (cell.verticalMerge === "continue") {
14240
14309
  return "unsupported";
14241
14310
  }
14242
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14243
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14244
- });
14311
+ const surface = scopeSurfaceCache.get();
14245
14312
  const surfaceBlock = surface.blocks[target.tableBlockIndex];
14246
14313
  const surfaceRow = surfaceBlock?.kind === "table" ? surfaceBlock.rows[target.rowIndex] : void 0;
14247
14314
  const surfaceCell = surfaceRow?.cells[target.cellIndex];
@@ -14305,16 +14372,14 @@ function resolveTargetedTableParagraphScope(document2, selection, target) {
14305
14372
  }
14306
14373
  };
14307
14374
  }
14308
- function resolveTargetedTableParagraphScopeByPath(root, document2, selection, target) {
14375
+ function resolveTargetedTableParagraphScopeByPath(root, document2, selection, target, scopeSurfaceCache) {
14309
14376
  if (!target.blockPath) return null;
14310
14377
  const tokens = parseCanonicalBlockPath(target.blockPath);
14311
14378
  if (!tokens) return null;
14312
14379
  const resolved = resolveBlockPathInCanonicalBlocks(root.children, tokens, 0);
14313
14380
  if (!resolved || resolved.block.type !== "paragraph") return null;
14314
14381
  if (resolved.hasVerticalMergeContinuation) return null;
14315
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14316
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14317
- });
14382
+ const surface = scopeSurfaceCache.get();
14318
14383
  const surfaceResolved = resolveBlockPathInSurfaceBlocks(surface.blocks, tokens, 0);
14319
14384
  if (!surfaceResolved || surfaceResolved.kind !== "paragraph") return null;
14320
14385
  if (surfaceResolved.hasVerticalMergeContinuation) return null;
@@ -14769,14 +14834,12 @@ function resolveBlockPathInSurfaceBlocks(blocks, tokens, tokenIndex, hasVertical
14769
14834
  hasVerticalMergeContinuation || cell.verticalMerge === "continue"
14770
14835
  );
14771
14836
  }
14772
- function resolveTopLevelParagraphScope(document2, selection) {
14837
+ function resolveTopLevelParagraphScope(document2, selection, scopeSurfaceCache) {
14773
14838
  const root = document2.content;
14774
14839
  if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
14775
14840
  return null;
14776
14841
  }
14777
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14778
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14779
- });
14842
+ const surface = scopeSurfaceCache.get();
14780
14843
  const selectionFrom = Math.min(selection.anchor, selection.head);
14781
14844
  const selectionTo = Math.max(selection.anchor, selection.head);
14782
14845
  for (let blockIndex = 0; blockIndex < root.children.length; blockIndex += 1) {
@@ -14805,14 +14868,12 @@ function resolveTopLevelParagraphScope(document2, selection) {
14805
14868
  }
14806
14869
  return null;
14807
14870
  }
14808
- function resolveTopLevelSdtParagraphScope(document2, selection) {
14871
+ function resolveTopLevelSdtParagraphScope(document2, selection, scopeSurfaceCache) {
14809
14872
  const root = document2.content;
14810
14873
  if (!root || root.type !== "doc" || !Array.isArray(root.children)) {
14811
14874
  return null;
14812
14875
  }
14813
- const surface = createEditorSurfaceSnapshot(document2, selection, void 0, {
14814
- editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX
14815
- });
14876
+ const surface = scopeSurfaceCache.get();
14816
14877
  const selectionFrom = Math.min(selection.anchor, selection.head);
14817
14878
  const selectionTo = Math.max(selection.anchor, selection.head);
14818
14879
  for (let blockIndex = 0; blockIndex < root.children.length; blockIndex += 1) {
@@ -15564,7 +15625,7 @@ function getTableCellRange(table, cell) {
15564
15625
  function breadcrumbItemsEqual(left, right) {
15565
15626
  return left.kind === right.kind && left.label === right.label && left.from === right.from && left.to === right.to;
15566
15627
  }
15567
- function applyFormattingOperationToDocument(document2, snapshot, operation) {
15628
+ function applyFormattingOperationToDocument(document2, snapshot, operation2) {
15568
15629
  const surface = snapshot.surface;
15569
15630
  if (!surface) {
15570
15631
  return {
@@ -15578,7 +15639,7 @@ function applyFormattingOperationToDocument(document2, snapshot, operation) {
15578
15639
  let changed = false;
15579
15640
  const selectionFrom = Math.min(snapshot.selection.anchor, snapshot.selection.head);
15580
15641
  const selectionTo = Math.max(snapshot.selection.anchor, snapshot.selection.head);
15581
- if (operation.type === "set-alignment" || operation.type === "indent" || operation.type === "outdent") {
15642
+ if (operation2.type === "set-alignment" || operation2.type === "indent" || operation2.type === "outdent") {
15582
15643
  visitParagraphBindings(root.children, surface.blocks, (paragraph, paragraphSurface) => {
15583
15644
  if (!selectionTouchesRange(
15584
15645
  snapshot.selection.anchor,
@@ -15588,7 +15649,7 @@ function applyFormattingOperationToDocument(document2, snapshot, operation) {
15588
15649
  )) {
15589
15650
  return;
15590
15651
  }
15591
- const paragraphChanged = operation.type === "set-alignment" ? applyAlignment(paragraph, operation.alignment) : applyIndentation(paragraph, operation.type === "indent" ? 1 : -1);
15652
+ const paragraphChanged = operation2.type === "set-alignment" ? applyAlignment(paragraph, operation2.alignment) : applyIndentation(paragraph, operation2.type === "indent" ? 1 : -1);
15592
15653
  changed = changed || paragraphChanged;
15593
15654
  });
15594
15655
  return {
@@ -15604,7 +15665,7 @@ function applyFormattingOperationToDocument(document2, snapshot, operation) {
15604
15665
  changed: false
15605
15666
  };
15606
15667
  }
15607
- const nextMarks = resolveMarkUpdater(snapshot, operation);
15668
+ const nextMarks = resolveMarkUpdater(snapshot, operation2);
15608
15669
  visitParagraphBindings(root.children, surface.blocks, (paragraph, paragraphSurface) => {
15609
15670
  if (!rangesOverlap(
15610
15671
  selectionFrom,
@@ -15633,10 +15694,10 @@ function applyFormattingOperationToDocument(document2, snapshot, operation) {
15633
15694
  changed
15634
15695
  };
15635
15696
  }
15636
- function applyTextMarkOperationToDocumentRange(document2, range, operation) {
15697
+ function applyTextMarkOperationToDocumentRange(document2, range, operation2) {
15637
15698
  const inputFrom = Math.min(range.from, range.to);
15638
15699
  const inputTo = Math.max(range.from, range.to);
15639
- const expandedRange = operation.type === "clear-mark" && operation.expandToFullHighlight === true ? expandRangeToCanonicalHighlightExtent(document2, inputFrom, inputTo) : { from: inputFrom, to: inputTo };
15700
+ const expandedRange = operation2.type === "clear-mark" && operation2.expandToFullHighlight === true ? expandRangeToCanonicalHighlightExtent(document2, inputFrom, inputTo) : { from: inputFrom, to: inputTo };
15640
15701
  const selectionFrom = expandedRange.from;
15641
15702
  const selectionTo = expandedRange.to;
15642
15703
  const selection = {
@@ -15655,17 +15716,17 @@ function applyTextMarkOperationToDocumentRange(document2, range, operation) {
15655
15716
  }
15656
15717
  const nextDocument = structuredClone(document2);
15657
15718
  const root = nextDocument.content;
15658
- const updateMarks = resolveTextMarkRangeUpdater(operation);
15719
+ const updateMarks = resolveTextMarkRangeUpdater(operation2);
15659
15720
  let changed = false;
15660
15721
  let cursor = 0;
15661
15722
  for (let blockIndex = 0; blockIndex < root.children.length; blockIndex += 1) {
15662
15723
  const block = root.children[blockIndex];
15663
15724
  const blockFrom = cursor;
15664
- const blockLength = block?.type === "paragraph" ? block.children.reduce(
15725
+ const blockLength2 = block?.type === "paragraph" ? block.children.reduce(
15665
15726
  (total, child) => total + inlineNodeLength(child),
15666
15727
  0
15667
15728
  ) : 1;
15668
- const blockTo = blockFrom + blockLength;
15729
+ const blockTo = blockFrom + blockLength2;
15669
15730
  if (block?.type === "paragraph" && rangesOverlap(selectionFrom, selectionTo, blockFrom, blockTo)) {
15670
15731
  const transformed = transformInlineNodes(
15671
15732
  block.children,
@@ -15698,11 +15759,11 @@ function expandRangeToCanonicalHighlightExtent(document2, inputFrom, inputTo) {
15698
15759
  for (let blockIndex = 0; blockIndex < root.children.length; blockIndex += 1) {
15699
15760
  const block = root.children[blockIndex];
15700
15761
  const blockFrom = cursor;
15701
- const blockLength = block.type === "paragraph" ? block.children.reduce(
15762
+ const blockLength2 = block.type === "paragraph" ? block.children.reduce(
15702
15763
  (total, child) => total + inlineNodeLength(child),
15703
15764
  0
15704
15765
  ) : 1;
15705
- const blockTo = blockFrom + blockLength;
15766
+ const blockTo = blockFrom + blockLength2;
15706
15767
  if (block.type === "paragraph" && rangesOverlap(inputFrom, inputTo, blockFrom, blockTo)) {
15707
15768
  const spans = collectInlineHighlightSpans(block.children, blockFrom).spans;
15708
15769
  const expanded = expandRangeWithinHighlightSpans(spans, inputFrom, inputTo);
@@ -15791,39 +15852,39 @@ function marksHaveVisualHighlight(marks) {
15791
15852
  (mark) => mark.type === "highlight" || mark.type === "backgroundColor"
15792
15853
  ) ?? false;
15793
15854
  }
15794
- function resolveTextMarkRangeUpdater(operation) {
15795
- if (operation.type === "set-mark") {
15855
+ function resolveTextMarkRangeUpdater(operation2) {
15856
+ if (operation2.type === "set-mark") {
15796
15857
  return (marks) => {
15797
15858
  const nextMarks = cloneMarks3(marks).filter(
15798
- (candidate) => candidate.type !== operation.mark.type
15859
+ (candidate) => candidate.type !== operation2.mark.type
15799
15860
  );
15800
- nextMarks.push({ ...operation.mark });
15861
+ nextMarks.push({ ...operation2.mark });
15801
15862
  return nextMarks.length > 0 ? nextMarks : void 0;
15802
15863
  };
15803
15864
  }
15804
15865
  return (marks) => {
15805
15866
  const nextMarks = cloneMarks3(marks).filter((candidate) => {
15806
- if (operation.mark === "visualHighlight") {
15867
+ if (operation2.mark === "visualHighlight") {
15807
15868
  return candidate.type !== "highlight" && candidate.type !== "backgroundColor";
15808
15869
  }
15809
- return candidate.type !== operation.mark;
15870
+ return candidate.type !== operation2.mark;
15810
15871
  });
15811
15872
  return nextMarks.length > 0 ? nextMarks : void 0;
15812
15873
  };
15813
15874
  }
15814
- function resolveMarkUpdater(snapshot, operation) {
15875
+ function resolveMarkUpdater(snapshot, operation2) {
15815
15876
  const formatting = getFormattingStateFromRenderSnapshot(snapshot);
15816
- switch (operation.type) {
15877
+ switch (operation2.type) {
15817
15878
  case "toggle":
15818
- return (marks) => toggleMarks(marks, operation.mark, !formatting[operation.mark]);
15879
+ return (marks) => toggleMarks(marks, operation2.mark, !formatting[operation2.mark]);
15819
15880
  case "set-font-family":
15820
- return (marks) => setMarkValue(marks, "fontFamily", sanitizeFontFamily(operation.fontFamily));
15881
+ return (marks) => setMarkValue(marks, "fontFamily", sanitizeFontFamily(operation2.fontFamily));
15821
15882
  case "set-font-size":
15822
- return (marks) => setMarkValue(marks, "fontSize", sanitizeFontSize(operation.size));
15883
+ return (marks) => setMarkValue(marks, "fontSize", sanitizeFontSize(operation2.size));
15823
15884
  case "set-text-color":
15824
- return (marks) => setMarkValue(marks, "textColor", sanitizeColor(operation.color));
15885
+ return (marks) => setMarkValue(marks, "textColor", sanitizeColor(operation2.color));
15825
15886
  case "set-highlight-color":
15826
- return (marks) => setMarkValue(marks, "backgroundColor", sanitizeColor(operation.color));
15887
+ return (marks) => setMarkValue(marks, "backgroundColor", sanitizeColor(operation2.color));
15827
15888
  }
15828
15889
  }
15829
15890
  function applyAlignment(paragraph, alignment) {
@@ -17158,7 +17219,7 @@ function findCellIndexAtColumn(row2, logicalColumn) {
17158
17219
  }
17159
17220
 
17160
17221
  // src/core/commands/table-structure-commands.ts
17161
- function applyTableStructureOperation(document2, snapshot, selectionDescriptor, operation) {
17222
+ function applyTableStructureOperation(document2, snapshot, selectionDescriptor, operation2) {
17162
17223
  const root = document2.content;
17163
17224
  const fallbackSelection = toInternalSelectionSnapshot(snapshot.selection);
17164
17225
  if (!root || root.type !== "doc") {
@@ -17172,11 +17233,11 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17172
17233
  if (!target || target.type !== "table") {
17173
17234
  return createNoopStructuralMutation(document2, fallbackSelection);
17174
17235
  }
17175
- switch (operation.type) {
17236
+ switch (operation2.type) {
17176
17237
  case "delete-table":
17177
17238
  return deleteTableBlock(document2, root, effectiveSelection.tableBlockIndex, fallbackSelection);
17178
17239
  case "set-cell-background":
17179
- return setCellBackground(document2, root, target, effectiveSelection, operation.color, fallbackSelection);
17240
+ return setCellBackground(document2, root, target, effectiveSelection, operation2.color, fallbackSelection);
17180
17241
  case "split-cell":
17181
17242
  return splitSelectedCell(document2, root, target, effectiveSelection, fallbackSelection);
17182
17243
  case "merge-cells":
@@ -17194,20 +17255,20 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17194
17255
  case "delete-column":
17195
17256
  return deleteColumn(document2, root, target, effectiveSelection, fallbackSelection);
17196
17257
  case "set-table-width":
17197
- return patchTable(document2, root, target, effectiveSelection, { width: operation.width }, fallbackSelection);
17258
+ return patchTable(document2, root, target, effectiveSelection, { width: operation2.width }, fallbackSelection);
17198
17259
  case "set-table-alignment":
17199
- return patchTable(document2, root, target, effectiveSelection, { alignment: operation.alignment }, fallbackSelection);
17260
+ return patchTable(document2, root, target, effectiveSelection, { alignment: operation2.alignment }, fallbackSelection);
17200
17261
  case "set-table-indent":
17201
- return patchTable(document2, root, target, effectiveSelection, { indent: operation.indent }, fallbackSelection);
17262
+ return patchTable(document2, root, target, effectiveSelection, { indent: operation2.indent }, fallbackSelection);
17202
17263
  case "set-table-layout-mode":
17203
- return patchTable(document2, root, target, effectiveSelection, { layoutMode: operation.mode }, fallbackSelection);
17264
+ return patchTable(document2, root, target, effectiveSelection, { layoutMode: operation2.mode }, fallbackSelection);
17204
17265
  case "set-table-cell-margins":
17205
17266
  return patchTable(
17206
17267
  document2,
17207
17268
  root,
17208
17269
  target,
17209
17270
  effectiveSelection,
17210
- { cellMargins: mergeTableCellMargins(target.cellMargins, operation.margins) },
17271
+ { cellMargins: mergeTableCellMargins(target.cellMargins, operation2.margins) },
17211
17272
  fallbackSelection
17212
17273
  );
17213
17274
  case "set-table-borders":
@@ -17216,7 +17277,7 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17216
17277
  root,
17217
17278
  target,
17218
17279
  effectiveSelection,
17219
- { borders: mergeTableBorders(target.borders, operation.borders) },
17280
+ { borders: mergeTableBorders(target.borders, operation2.borders) },
17220
17281
  fallbackSelection
17221
17282
  );
17222
17283
  case "set-table-style":
@@ -17225,7 +17286,7 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17225
17286
  root,
17226
17287
  target,
17227
17288
  effectiveSelection,
17228
- { styleId: operation.styleId ?? void 0 },
17289
+ { styleId: operation2.styleId ?? void 0 },
17229
17290
  fallbackSelection
17230
17291
  );
17231
17292
  case "set-table-caption":
@@ -17234,7 +17295,7 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17234
17295
  root,
17235
17296
  target,
17236
17297
  effectiveSelection,
17237
- { caption: operation.caption ?? void 0 },
17298
+ { caption: operation2.caption ?? void 0 },
17238
17299
  fallbackSelection
17239
17300
  );
17240
17301
  case "set-table-description":
@@ -17243,82 +17304,82 @@ function applyTableStructureOperation(document2, snapshot, selectionDescriptor,
17243
17304
  root,
17244
17305
  target,
17245
17306
  effectiveSelection,
17246
- { description: operation.description ?? void 0 },
17307
+ { description: operation2.description ?? void 0 },
17247
17308
  fallbackSelection
17248
17309
  );
17249
17310
  case "set-column-width":
17250
- return setColumnWidth(document2, root, target, effectiveSelection, operation.columnIndex, operation.twips, fallbackSelection);
17311
+ return setColumnWidth(document2, root, target, effectiveSelection, operation2.columnIndex, operation2.twips, fallbackSelection);
17251
17312
  case "distribute-columns-evenly":
17252
- return distributeColumnsEvenly(document2, root, target, effectiveSelection, operation.columnRange, fallbackSelection);
17313
+ return distributeColumnsEvenly(document2, root, target, effectiveSelection, operation2.columnRange, fallbackSelection);
17253
17314
  case "set-row-height":
17254
- return patchRow(document2, root, target, effectiveSelection, operation.rowIndex, {
17255
- height: operation.twips,
17256
- heightRule: operation.rule
17315
+ return patchRow(document2, root, target, effectiveSelection, operation2.rowIndex, {
17316
+ height: operation2.twips,
17317
+ heightRule: operation2.rule
17257
17318
  }, fallbackSelection);
17258
17319
  case "set-row-cant-split":
17259
- return patchRow(document2, root, target, effectiveSelection, operation.rowIndex, {
17260
- cantSplit: operation.value
17320
+ return patchRow(document2, root, target, effectiveSelection, operation2.rowIndex, {
17321
+ cantSplit: operation2.value
17261
17322
  }, fallbackSelection);
17262
17323
  case "set-row-is-header":
17263
- return patchRow(document2, root, target, effectiveSelection, operation.rowIndex, {
17264
- isHeader: operation.value
17324
+ return patchRow(document2, root, target, effectiveSelection, operation2.rowIndex, {
17325
+ isHeader: operation2.value
17265
17326
  }, fallbackSelection);
17266
17327
  case "set-row-alignment":
17267
- return patchRow(document2, root, target, effectiveSelection, operation.rowIndex, {
17268
- horizontalAlignment: operation.alignment
17328
+ return patchRow(document2, root, target, effectiveSelection, operation2.rowIndex, {
17329
+ horizontalAlignment: operation2.alignment
17269
17330
  }, fallbackSelection);
17270
17331
  case "insert-rows":
17271
- return insertRows(document2, root, target, effectiveSelection, operation.rowIndex, operation.at, operation.count, fallbackSelection);
17332
+ return insertRows(document2, root, target, effectiveSelection, operation2.rowIndex, operation2.at, operation2.count, fallbackSelection);
17272
17333
  case "insert-columns":
17273
- return insertColumns(document2, root, target, effectiveSelection, operation.columnIndex, operation.at, operation.count, operation.widths, fallbackSelection);
17334
+ return insertColumns(document2, root, target, effectiveSelection, operation2.columnIndex, operation2.at, operation2.count, operation2.widths, fallbackSelection);
17274
17335
  case "set-cell-borders":
17275
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17336
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17276
17337
  ...cell,
17277
- borders: mergeCellBorders(cell.borders, operation.borders)
17338
+ borders: mergeCellBorders(cell.borders, operation2.borders)
17278
17339
  }), fallbackSelection);
17279
17340
  case "set-cell-shading":
17280
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => {
17281
- if (operation.shading === null) {
17341
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => {
17342
+ if (operation2.shading === null) {
17282
17343
  const { shading: _drop, ...rest } = cell;
17283
17344
  return rest;
17284
17345
  }
17285
- return { ...cell, shading: { ...cell.shading ?? {}, ...operation.shading } };
17346
+ return { ...cell, shading: { ...cell.shading ?? {}, ...operation2.shading } };
17286
17347
  }, fallbackSelection);
17287
17348
  case "clear-cell-shading":
17288
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => {
17349
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => {
17289
17350
  const { shading: _drop, ...rest } = cell;
17290
17351
  return rest;
17291
17352
  }, fallbackSelection);
17292
17353
  case "set-cell-margins":
17293
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17354
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17294
17355
  ...cell,
17295
- margins: { ...cell.margins ?? {}, ...operation.margins }
17356
+ margins: { ...cell.margins ?? {}, ...operation2.margins }
17296
17357
  }), fallbackSelection);
17297
17358
  case "set-cell-vertical-align":
17298
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17359
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17299
17360
  ...cell,
17300
- verticalAlign: operation.align
17361
+ verticalAlign: operation2.align
17301
17362
  }), fallbackSelection);
17302
17363
  case "set-cell-text-direction":
17303
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17364
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17304
17365
  ...cell,
17305
- textDirection: operation.direction
17366
+ textDirection: operation2.direction
17306
17367
  }), fallbackSelection);
17307
17368
  case "set-cell-no-wrap":
17308
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17369
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17309
17370
  ...cell,
17310
- noWrap: operation.value
17371
+ noWrap: operation2.value
17311
17372
  }), fallbackSelection);
17312
17373
  case "set-cell-fit-text":
17313
- return patchCells(document2, root, target, effectiveSelection, operation.locator, (cell) => ({
17374
+ return patchCells(document2, root, target, effectiveSelection, operation2.locator, (cell) => ({
17314
17375
  ...cell,
17315
- fitText: operation.value
17376
+ fitText: operation2.value
17316
17377
  }), fallbackSelection);
17317
17378
  default:
17318
17379
  return createNoopStructuralMutation(document2, fallbackSelection);
17319
17380
  }
17320
17381
  }
17321
- function applyTableStructureOperationForEditableTarget(document2, snapshot, selectionDescriptor, operation, targetRef) {
17382
+ function applyTableStructureOperationForEditableTarget(document2, snapshot, selectionDescriptor, operation2, targetRef) {
17322
17383
  const tableIdentity = targetRef.table;
17323
17384
  const fallbackSelection = toInternalSelectionSnapshot(snapshot.selection);
17324
17385
  if (!tableIdentity || targetRef.commandFamily !== "table-structure") {
@@ -17353,7 +17414,7 @@ function applyTableStructureOperationForEditableTarget(document2, snapshot, sele
17353
17414
  localDocument,
17354
17415
  snapshot,
17355
17416
  { ...effectiveSelection, tableBlockIndex: 0 },
17356
- operation
17417
+ operation2
17357
17418
  );
17358
17419
  if (!localResult.changed) {
17359
17420
  return createNoopStructuralMutation(document2, fallbackSelection);
@@ -18785,9 +18846,11 @@ function applyRevisionAction(options) {
18785
18846
  );
18786
18847
  }
18787
18848
  const slice = story.units.slice(range.from, range.to);
18788
- if (slice.some(
18789
- (unit) => unit.kind === "paragraph_break" || unit.kind === "opaque_block" || unit.kind === "structural_block"
18790
- )) {
18849
+ const touchesStructuralContent = slice.some(
18850
+ (unit) => unit.kind === "opaque_block" || unit.kind === "structural_block"
18851
+ );
18852
+ const touchesParagraphBoundary = slice.some((unit) => unit.kind === "paragraph_break");
18853
+ if (touchesStructuralContent || touchesParagraphBoundary && !canApplyRuntimeTextBlockDeletion(revision, options.intent)) {
18791
18854
  return skippedResult(
18792
18855
  options,
18793
18856
  "structural-range",
@@ -18795,7 +18858,7 @@ function applyRevisionAction(options) {
18795
18858
  );
18796
18859
  }
18797
18860
  if (slice.some(
18798
- (unit) => unit.kind === "image" || unit.kind === "opaque_inline"
18861
+ (unit) => unit.kind === "image" || unit.kind === "opaque_inline" || unit.kind === "protected_inline"
18799
18862
  )) {
18800
18863
  return skippedResult(
18801
18864
  options,
@@ -18850,6 +18913,9 @@ function applyRevisionAction(options) {
18850
18913
  detachedRevisionIds: findNewDetachedRevisionIds(options.store, nextStore)
18851
18914
  };
18852
18915
  }
18916
+ function canApplyRuntimeTextBlockDeletion(revision, intent) {
18917
+ return revision.metadata.source === "runtime" && (intent === "accept" && revision.kind === "deletion" && (revision.metadata.semanticKind === "replacement" || revision.metadata.semanticKind === "deletion") || intent === "reject" && revision.kind === "insertion" && (revision.metadata.semanticKind === "replacement" || revision.metadata.semanticKind === "structural-change"));
18918
+ }
18853
18919
  function applyPairedMoveAction(options, revision) {
18854
18920
  const resultingStatus = toResultingStatus(options.intent);
18855
18921
  return {
@@ -21027,13 +21093,14 @@ function mapRecordAnchor(anchor, mapping) {
21027
21093
  return toPublicAnchorProjection(mapAnchor(toInternalAnchorProjection(anchor), mapping));
21028
21094
  }
21029
21095
  function withCommandTextTarget(context, command) {
21030
- if (!command.textTarget || context.textTarget === command.textTarget) {
21031
- return context;
21096
+ let next = context;
21097
+ if (next.precomputedSurface === void 0 && next.renderSnapshot?.surface !== void 0) {
21098
+ next = { ...next, precomputedSurface: next.renderSnapshot.surface };
21032
21099
  }
21033
- return {
21034
- ...context,
21035
- textTarget: command.textTarget
21036
- };
21100
+ if (command.textTarget && next.textTarget !== command.textTarget) {
21101
+ next = { ...next, textTarget: command.textTarget };
21102
+ }
21103
+ return next;
21037
21104
  }
21038
21105
  function listCommandContext(context) {
21039
21106
  return {
@@ -21232,6 +21299,11 @@ function executeEditorCommand(state, command, context) {
21232
21299
  );
21233
21300
  return buildDocumentReplaceTransaction(state, context, result);
21234
21301
  }
21302
+ case "fragment.insert-tracked": {
21303
+ const result = applySuggestingFragmentInsert(state, command.fragment, context);
21304
+ if (result) return result;
21305
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
21306
+ }
21235
21307
  case "runtime.set-read-only":
21236
21308
  return createTransaction(
21237
21309
  {
@@ -22477,7 +22549,7 @@ function applyTextCommand(state, timestamp, apply) {
22477
22549
  const reviewState = remapReviewStateAfterContentChange(
22478
22550
  state,
22479
22551
  result.document,
22480
- result.mapping
22552
+ result.mapping ?? createEmptyMapping()
22481
22553
  );
22482
22554
  const scopeTagTouches = collectScopeTagTouches(
22483
22555
  state.document.review.comments,
@@ -22817,6 +22889,38 @@ function isSingleParagraphSuggestingRange(document2, from, to) {
22817
22889
  );
22818
22890
  return ranges.some((range) => from >= range.start && to <= range.end);
22819
22891
  }
22892
+ function isTextOnlySuggestingRange(document2, from, to) {
22893
+ const story = parseTextStory(document2.content);
22894
+ const range = normalizeTextStoryRange(story, from, to);
22895
+ if (!range) {
22896
+ return isSingleParagraphSuggestingRange(document2, from, to);
22897
+ }
22898
+ const rootStoryTextOnly = story.units.slice(range.from, range.to).every(isSuggestingTextReplacementUnit);
22899
+ if (rootStoryTextOnly) return true;
22900
+ return isSingleParagraphSuggestingRange(document2, from, to);
22901
+ }
22902
+ function normalizeTextStoryRange(story, from, to) {
22903
+ const start = Math.min(from, to);
22904
+ const end = Math.max(from, to);
22905
+ if (start < 0 || end > story.size) return null;
22906
+ return { from: start, to: end };
22907
+ }
22908
+ function isSuggestingTextReplacementUnit(unit) {
22909
+ switch (unit.kind) {
22910
+ case "text":
22911
+ case "tab":
22912
+ case "hard_break":
22913
+ case "paragraph_break":
22914
+ case "scope_marker":
22915
+ return true;
22916
+ case "protected_inline":
22917
+ case "image":
22918
+ case "opaque_inline":
22919
+ case "opaque_block":
22920
+ case "structural_block":
22921
+ return false;
22922
+ }
22923
+ }
22820
22924
  function collectSuggestingParagraphRanges(blocks, startCursor, output, addRootParagraphBoundaries) {
22821
22925
  let cursor = startCursor;
22822
22926
  for (let index = 0; index < blocks.length; index += 1) {
@@ -22889,10 +22993,10 @@ function applySuggestingInsert(state, text, context, formatting) {
22889
22993
  const from = Math.min(selection.anchor, selection.head);
22890
22994
  const to = Math.max(selection.anchor, selection.head);
22891
22995
  const isCollapsed = from === to;
22892
- if (!isCollapsed && !isSingleParagraphSuggestingRange(state.document, from, to)) {
22996
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
22893
22997
  return createSuggestingUnsupportedTransaction(
22894
22998
  state,
22895
- "Suggesting mode does not yet support multi-paragraph replacement ranges."
22999
+ "Suggesting mode text replacement ranges must contain only editable text and paragraph breaks."
22896
23000
  );
22897
23001
  }
22898
23002
  if (isCollapsed) {
@@ -23001,7 +23105,7 @@ function applySuggestingInsert(state, text, context, formatting) {
23001
23105
  const reviewState = remapReviewStateAfterContentChange(
23002
23106
  state,
23003
23107
  result.document,
23004
- result.mapping
23108
+ result.mapping ?? createEmptyMapping()
23005
23109
  );
23006
23110
  const replacementSuggestionId = createSuggestingRevisionId(
23007
23111
  reviewState.document.review.revisions,
@@ -23066,6 +23170,131 @@ function applySuggestingInsert(state, text, context, formatting) {
23066
23170
  activeCommentId: reviewState.activeCommentId
23067
23171
  }
23068
23172
  },
23173
+ {
23174
+ historyBoundary: "push",
23175
+ markDirty: true,
23176
+ mapping: result.mapping ?? createEmptyMapping(),
23177
+ effects: {
23178
+ ...reviewState.effects,
23179
+ revisionAuthored: { changeId: insertionRevision.changeId, kind: "insertion" }
23180
+ }
23181
+ }
23182
+ );
23183
+ }
23184
+ function applySuggestingFragmentInsert(state, fragment, context) {
23185
+ if (state.readOnly) {
23186
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23187
+ }
23188
+ if (fragment.blocks.length === 0) {
23189
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23190
+ }
23191
+ if (!isTrackableSuggestingFragment(fragment)) {
23192
+ return createSuggestingUnsupportedTransaction(
23193
+ state,
23194
+ "Suggesting mode structured fragment replacement supports paragraph/text fragments only."
23195
+ );
23196
+ }
23197
+ const authorId = context.defaultAuthorId ?? "unknown";
23198
+ const selection = state.selection;
23199
+ const from = Math.min(selection.anchor, selection.head);
23200
+ const to = Math.max(selection.anchor, selection.head);
23201
+ const isCollapsed = from === to;
23202
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
23203
+ return createSuggestingUnsupportedTransaction(
23204
+ state,
23205
+ "Suggesting mode structured fragment replacement ranges must contain only editable text and paragraph breaks."
23206
+ );
23207
+ }
23208
+ const storyBefore = parseTextStory(state.document.content);
23209
+ const insertSelection = createSelectionSnapshot(to, to);
23210
+ const result = structureLayer.applyFragmentInsert(
23211
+ state.document,
23212
+ insertSelection,
23213
+ fragment,
23214
+ context
23215
+ );
23216
+ if (!result.changed) {
23217
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23218
+ }
23219
+ const storyAfter = parseTextStory(result.document.content);
23220
+ const insertedFrom = to;
23221
+ const insertedTo = to + Math.max(0, storyAfter.size - storyBefore.size);
23222
+ if (insertedTo <= insertedFrom) {
23223
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23224
+ }
23225
+ const reviewState = remapReviewStateAfterContentChange(
23226
+ state,
23227
+ result.document,
23228
+ result.mapping ?? createEmptyMapping()
23229
+ );
23230
+ const replacementSuggestionId = !isCollapsed ? createSuggestingRevisionId(
23231
+ reviewState.document.review.revisions,
23232
+ context.timestamp,
23233
+ authorId
23234
+ ) : void 0;
23235
+ let deletionRevision = !isCollapsed ? createAuthoredRevision(
23236
+ reviewState.document.review.revisions,
23237
+ "deletion",
23238
+ from,
23239
+ to,
23240
+ authorId,
23241
+ context.timestamp,
23242
+ createSuggestionMetadata({
23243
+ suggestionId: replacementSuggestionId,
23244
+ semanticKind: "replacement"
23245
+ })
23246
+ ) : void 0;
23247
+ const insertionRevision = createAuthoredRevision(
23248
+ {
23249
+ ...reviewState.document.review.revisions,
23250
+ ...deletionRevision ? { [deletionRevision.changeId]: deletionRevision } : {}
23251
+ },
23252
+ "insertion",
23253
+ insertedFrom,
23254
+ insertedTo,
23255
+ authorId,
23256
+ context.timestamp,
23257
+ createSuggestionMetadata(
23258
+ deletionRevision ? {
23259
+ suggestionId: replacementSuggestionId,
23260
+ semanticKind: "replacement",
23261
+ linkedRevisionIds: [deletionRevision.changeId]
23262
+ } : {
23263
+ semanticKind: "structural-change"
23264
+ }
23265
+ )
23266
+ );
23267
+ if (deletionRevision) {
23268
+ deletionRevision = {
23269
+ ...deletionRevision,
23270
+ metadata: {
23271
+ ...deletionRevision.metadata,
23272
+ linkedRevisionIds: [insertionRevision.changeId]
23273
+ }
23274
+ };
23275
+ }
23276
+ const finalDocument = {
23277
+ ...reviewState.document,
23278
+ review: {
23279
+ ...reviewState.document.review,
23280
+ revisions: {
23281
+ ...reviewState.document.review.revisions,
23282
+ ...deletionRevision ? { [deletionRevision.changeId]: deletionRevision } : {},
23283
+ [insertionRevision.changeId]: insertionRevision
23284
+ }
23285
+ }
23286
+ };
23287
+ return createTransaction(
23288
+ {
23289
+ ...state,
23290
+ document: finalDocument,
23291
+ selection: createSelectionSnapshot(insertedTo, insertedTo),
23292
+ warnings: reviewState.warnings,
23293
+ runtime: {
23294
+ ...state.runtime,
23295
+ activeCommentId: reviewState.activeCommentId
23296
+ }
23297
+ },
23069
23298
  {
23070
23299
  historyBoundary: "push",
23071
23300
  markDirty: true,
@@ -23077,6 +23306,10 @@ function applySuggestingInsert(state, text, context, formatting) {
23077
23306
  }
23078
23307
  );
23079
23308
  }
23309
+ function isTrackableSuggestingFragment(fragment) {
23310
+ const story = parseTextStory({ type: "doc", children: fragment.blocks });
23311
+ return story.units.every(isSuggestingTextReplacementUnit);
23312
+ }
23080
23313
  function applySuggestingDelete(state, direction, context) {
23081
23314
  if (state.readOnly) {
23082
23315
  return createTransaction(state, { historyBoundary: "skip", markDirty: false });
@@ -23086,10 +23319,10 @@ function applySuggestingDelete(state, direction, context) {
23086
23319
  const from = Math.min(selection.anchor, selection.head);
23087
23320
  const to = Math.max(selection.anchor, selection.head);
23088
23321
  const isCollapsed = from === to;
23089
- if (!isCollapsed && !isSingleParagraphSuggestingRange(state.document, from, to)) {
23322
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
23090
23323
  return createSuggestingUnsupportedTransaction(
23091
23324
  state,
23092
- "Suggesting mode does not yet support multi-paragraph deletion ranges."
23325
+ "Suggesting mode text deletion ranges must contain only editable text and paragraph breaks."
23093
23326
  );
23094
23327
  }
23095
23328
  let deleteFrom;
@@ -24427,9 +24660,9 @@ function createSuggestionsSnapshot(trackedChanges) {
24427
24660
  const groups = /* @__PURE__ */ new Map();
24428
24661
  for (const revision of trackedChanges.revisions) {
24429
24662
  const suggestionId = revision.suggestionId ?? revision.revisionId;
24430
- const group = groups.get(suggestionId);
24431
- if (group) {
24432
- group.push(revision);
24663
+ const group2 = groups.get(suggestionId);
24664
+ if (group2) {
24665
+ group2.push(revision);
24433
24666
  } else {
24434
24667
  groups.set(suggestionId, [revision]);
24435
24668
  }
@@ -24476,15 +24709,15 @@ function createSuggestionsSnapshot(trackedChanges) {
24476
24709
  };
24477
24710
  }
24478
24711
 
24479
- // src/runtime/workflow/scope-resolver.ts
24480
- function inlineLength(node) {
24712
+ // src/core/commands/scope-coordinate-walk.ts
24713
+ function inlineLengthForScopeCoordinates(node) {
24481
24714
  switch (node.type) {
24482
24715
  case "text":
24483
24716
  return Array.from(node.text).length;
24484
24717
  case "hyperlink":
24485
24718
  case "field":
24486
24719
  return node.children.reduce(
24487
- (total, child) => total + inlineLength(child),
24720
+ (total, child) => total + inlineLengthForScopeCoordinates(child),
24488
24721
  0
24489
24722
  );
24490
24723
  case "bookmark_start":
@@ -24496,29 +24729,241 @@ function inlineLength(node) {
24496
24729
  return 1;
24497
24730
  }
24498
24731
  }
24499
- function walkParagraphs2(document2) {
24500
- const envelope = document2;
24501
- const root = "content" in envelope ? envelope.content : document2;
24502
- const out = [];
24732
+ function paragraphLengthForScopeCoordinates(paragraph) {
24733
+ return paragraph.children.reduce(
24734
+ (total, child) => total + inlineLengthForScopeCoordinates(child),
24735
+ 0
24736
+ );
24737
+ }
24738
+ function computeScopeStoryLayout(document2) {
24739
+ const root = document2.content;
24740
+ const paragraphSlots = [];
24741
+ const blockSlots = [];
24742
+ if (!root || root.type !== "doc") {
24743
+ return { paragraphSlots, blockSlots, storyLength: 0 };
24744
+ }
24503
24745
  let cursor = 0;
24504
24746
  for (let index = 0; index < root.children.length; index += 1) {
24505
24747
  const block = root.children[index];
24506
- if (block && block.type === "paragraph") {
24507
- out.push({ paragraph: block, from: cursor });
24508
- cursor += block.children.reduce(
24509
- (total, child) => total + inlineLength(child),
24510
- 0
24511
- );
24512
- } else if (block && block.type === "table") {
24513
- cursor += 1;
24514
- } else {
24748
+ if (!block) continue;
24749
+ cursor = walkBlockForScopeCoordinates(
24750
+ block,
24751
+ cursor,
24752
+ [{ kind: "block", index }],
24753
+ index,
24754
+ paragraphSlots,
24755
+ blockSlots
24756
+ );
24757
+ if (index < root.children.length - 1 && root.children[index + 1]?.type === "paragraph") {
24515
24758
  cursor += 1;
24516
24759
  }
24517
- if (index < root.children.length - 1) {
24518
- cursor += 1;
24760
+ }
24761
+ return { paragraphSlots, blockSlots, storyLength: cursor };
24762
+ }
24763
+ function findParagraphSlotAtPosition(layout, position, edge) {
24764
+ const matches = layout.paragraphSlots.filter(
24765
+ (slot) => position >= slot.from && position <= slot.to
24766
+ );
24767
+ if (matches.length === 0) return null;
24768
+ if (edge === "start") {
24769
+ return matches.find((slot) => slot.from === position) ?? matches.find((slot) => position > slot.from && position <= slot.to) ?? matches[0];
24770
+ }
24771
+ return matches.find((slot) => slot.to === position) ?? matches.find((slot) => position >= slot.from && position < slot.to) ?? matches[matches.length - 1];
24772
+ }
24773
+ function findOwningBlockSlotAtPosition(layout, position) {
24774
+ const matches = layout.blockSlots.filter(
24775
+ (slot) => position >= slot.from && position <= slot.to
24776
+ );
24777
+ if (matches.length === 0) return null;
24778
+ return matches.reduce(
24779
+ (best, slot) => slot.path.length >= best.path.length ? slot : best
24780
+ );
24781
+ }
24782
+ function sameScopeParagraphPath(a, b) {
24783
+ return scopeParagraphPathKey(a) === scopeParagraphPathKey(b);
24784
+ }
24785
+ function replaceParagraphChildrenAtPath(root, path, children) {
24786
+ return {
24787
+ ...root,
24788
+ children: replaceInBlockList(root.children, path, children)
24789
+ };
24790
+ }
24791
+ function removeScopeMarkersFromBlockList(blocks, scopeId) {
24792
+ let mutated = false;
24793
+ const nextBlocks = blocks.map((block) => {
24794
+ const result = removeScopeMarkersFromBlock(block, scopeId);
24795
+ if (result.mutated) mutated = true;
24796
+ return result.block;
24797
+ });
24798
+ return { blocks: nextBlocks, mutated };
24799
+ }
24800
+ function walkBlockForScopeCoordinates(block, cursor, path, rootBlockIndex, paragraphSlots, blockSlots) {
24801
+ const from = cursor;
24802
+ if (block.type === "paragraph") {
24803
+ const to2 = cursor + paragraphLengthForScopeCoordinates(block);
24804
+ const slot = { path, rootBlockIndex, kind: block.type, from, to: to2 };
24805
+ blockSlots.push(slot);
24806
+ paragraphSlots.push({ ...slot, paragraph: block });
24807
+ return to2;
24808
+ }
24809
+ if (block.type === "table") {
24810
+ let innerCursor = cursor;
24811
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
24812
+ const row2 = block.rows[rowIndex];
24813
+ for (let cellIndex = 0; cellIndex < row2.cells.length; cellIndex += 1) {
24814
+ const cell = row2.cells[cellIndex];
24815
+ for (let childIndex = 0; childIndex < cell.children.length; childIndex += 1) {
24816
+ const child = cell.children[childIndex];
24817
+ innerCursor = walkBlockForScopeCoordinates(
24818
+ child,
24819
+ innerCursor,
24820
+ [
24821
+ ...path,
24822
+ { kind: "tableCell", rowIndex, cellIndex },
24823
+ { kind: "block", index: childIndex }
24824
+ ],
24825
+ rootBlockIndex,
24826
+ paragraphSlots,
24827
+ blockSlots
24828
+ );
24829
+ }
24830
+ }
24519
24831
  }
24832
+ blockSlots.push({
24833
+ path,
24834
+ rootBlockIndex,
24835
+ kind: block.type,
24836
+ from,
24837
+ to: innerCursor
24838
+ });
24839
+ return innerCursor;
24520
24840
  }
24521
- return out;
24841
+ if (block.type === "sdt") {
24842
+ const descriptor = describeStructuredWrapperBlock(block, {
24843
+ projectVisibleTocContentControls: true
24844
+ });
24845
+ if (descriptor) {
24846
+ const to2 = cursor + 1;
24847
+ blockSlots.push({ path, rootBlockIndex, kind: block.type, from, to: to2 });
24848
+ return to2;
24849
+ }
24850
+ let innerCursor = cursor;
24851
+ for (let childIndex = 0; childIndex < block.children.length; childIndex += 1) {
24852
+ const child = block.children[childIndex];
24853
+ innerCursor = walkBlockForScopeCoordinates(
24854
+ child,
24855
+ innerCursor,
24856
+ [...path, { kind: "sdtChildren" }, { kind: "block", index: childIndex }],
24857
+ rootBlockIndex,
24858
+ paragraphSlots,
24859
+ blockSlots
24860
+ );
24861
+ }
24862
+ blockSlots.push({
24863
+ path,
24864
+ rootBlockIndex,
24865
+ kind: block.type,
24866
+ from,
24867
+ to: innerCursor
24868
+ });
24869
+ return innerCursor;
24870
+ }
24871
+ const to = cursor + 1;
24872
+ blockSlots.push({ path, rootBlockIndex, kind: block.type, from, to });
24873
+ return to;
24874
+ }
24875
+ function replaceInBlockList(blocks, path, children) {
24876
+ const [head, ...rest] = path;
24877
+ if (!head || head.kind !== "block") return [...blocks];
24878
+ return blocks.map((block, index) => {
24879
+ if (index !== head.index) return block;
24880
+ if (rest.length === 0) {
24881
+ return block.type === "paragraph" ? { ...block, children } : block;
24882
+ }
24883
+ return replaceInsideBlock(block, rest, children);
24884
+ });
24885
+ }
24886
+ function replaceInsideBlock(block, path, children) {
24887
+ const [head, ...rest] = path;
24888
+ if (!head) return block;
24889
+ if (block.type === "table" && head.kind === "tableCell") {
24890
+ const rows = block.rows.map((row2, rowIndex) => {
24891
+ if (rowIndex !== head.rowIndex) return row2;
24892
+ const cells = row2.cells.map((cell, cellIndex) => {
24893
+ if (cellIndex !== head.cellIndex) return cell;
24894
+ return {
24895
+ ...cell,
24896
+ children: replaceInBlockList(cell.children, rest, children)
24897
+ };
24898
+ });
24899
+ return { ...row2, cells };
24900
+ });
24901
+ return { ...block, rows };
24902
+ }
24903
+ if (block.type === "sdt" && head.kind === "sdtChildren") {
24904
+ return {
24905
+ ...block,
24906
+ children: replaceInBlockList(block.children, rest, children)
24907
+ };
24908
+ }
24909
+ return block;
24910
+ }
24911
+ function removeScopeMarkersFromBlock(block, scopeId) {
24912
+ if (block.type === "paragraph") {
24913
+ const kept = block.children.filter((child) => {
24914
+ return !((child.type === "scope_marker_start" || child.type === "scope_marker_end") && child.scopeId === scopeId);
24915
+ });
24916
+ if (kept.length === block.children.length) {
24917
+ return { block, mutated: false };
24918
+ }
24919
+ return { block: { ...block, children: kept }, mutated: true };
24920
+ }
24921
+ if (block.type === "table") {
24922
+ let mutated = false;
24923
+ const rows = block.rows.map((row2) => {
24924
+ let rowMutated = false;
24925
+ const cells = row2.cells.map((cell) => {
24926
+ const result = removeScopeMarkersFromBlockList(cell.children, scopeId);
24927
+ if (result.mutated) {
24928
+ mutated = true;
24929
+ rowMutated = true;
24930
+ }
24931
+ return result.mutated ? { ...cell, children: result.blocks } : cell;
24932
+ });
24933
+ return rowMutated ? { ...row2, cells } : row2;
24934
+ });
24935
+ return mutated ? { block: { ...block, rows }, mutated: true } : { block, mutated: false };
24936
+ }
24937
+ if (block.type === "sdt") {
24938
+ const result = removeScopeMarkersFromBlockList(block.children, scopeId);
24939
+ return result.mutated ? { block: { ...block, children: result.blocks }, mutated: true } : { block, mutated: false };
24940
+ }
24941
+ if (block.type === "custom_xml") {
24942
+ const result = removeScopeMarkersFromBlockList(block.children, scopeId);
24943
+ return result.mutated ? { block: { ...block, children: result.blocks }, mutated: true } : { block, mutated: false };
24944
+ }
24945
+ return { block, mutated: false };
24946
+ }
24947
+ function scopeParagraphPathKey(path) {
24948
+ return path.map((step) => {
24949
+ if (step.kind === "block") return `b:${step.index}`;
24950
+ if (step.kind === "tableCell") {
24951
+ return `tc:${step.rowIndex}:${step.cellIndex}`;
24952
+ }
24953
+ return "sdt";
24954
+ }).join("/");
24955
+ }
24956
+
24957
+ // src/runtime/workflow/scope-resolver.ts
24958
+ function walkParagraphs2(document2) {
24959
+ const envelope = document2;
24960
+ return computeScopeStoryLayout(envelope).paragraphSlots.map((slot) => ({
24961
+ paragraph: slot.paragraph,
24962
+ from: slot.from
24963
+ }));
24964
+ }
24965
+ function inlineLength(node) {
24966
+ return inlineLengthForScopeCoordinates(node);
24522
24967
  }
24523
24968
  function collectScopeLocations(document2) {
24524
24969
  const locations = /* @__PURE__ */ new Map();
@@ -25718,7 +26163,7 @@ function buildFormattingDebugEntry(inputs) {
25718
26163
  }
25719
26164
 
25720
26165
  // src/runtime/layout/layout-engine-version.ts
25721
- var LAYOUT_ENGINE_VERSION = 93;
26166
+ var LAYOUT_ENGINE_VERSION = 94;
25722
26167
 
25723
26168
  // src/runtime/layout/compat-input-ledger.ts
25724
26169
  var DEFAULT_COMPATIBILITY_MODE = 15;
@@ -26929,9 +27374,13 @@ function appendFragmentLayoutObjectSemanticEntries(input) {
26929
27374
  numberingCanonicalAddressKey: numbering.canonicalAddressKey,
26930
27375
  numberingListAddressKey: numbering.listAddressKey,
26931
27376
  numberingMarkerText: numbering.markerText,
27377
+ numberingMarkerTextPosture: numbering.markerTextPosture,
26932
27378
  numberingMarkerSuffix: numbering.markerSuffix,
26933
27379
  numberingFormat: numbering.format,
26934
27380
  numberingFormatPosture: numbering.formatPosture,
27381
+ numberingPicBulletId: numbering.picBulletId,
27382
+ numberingPicBulletMediaId: numbering.picBulletMediaId,
27383
+ numberingPictureBulletPosture: numbering.pictureBulletPosture,
26935
27384
  numberingUnavailableReasons: numbering.unavailableReasons,
26936
27385
  markerGlyphRect: markerProjection.rect,
26937
27386
  markerLaneRect: markerProjection.markerLaneRect,
@@ -33849,9 +34298,9 @@ function messageForPostureBlocker(blocker) {
33849
34298
  return void 0;
33850
34299
  }
33851
34300
  }
33852
- function auditFact(target, category, reason, operation) {
34301
+ function auditFact(target, category, reason, operation2) {
33853
34302
  return {
33854
- operation: operation ?? auditOperationForTarget(target),
34303
+ operation: operation2 ?? auditOperationForTarget(target),
33855
34304
  category,
33856
34305
  dispatch: "blocked-before-mutation",
33857
34306
  reason
@@ -33942,25 +34391,43 @@ function computeBlockPositions(document2) {
33942
34391
  let cursor = 0;
33943
34392
  for (let index = 0; index < root.children.length; index += 1) {
33944
34393
  const block = root.children[index];
34394
+ if (!block) continue;
33945
34395
  const from = cursor;
33946
- let length = 0;
33947
- if (block && block.type === "paragraph") {
33948
- length = block.children.reduce(
33949
- (total, child) => total + inlineLength2(child),
33950
- 0
33951
- );
33952
- } else {
33953
- length = 1;
33954
- }
33955
- cursor += length;
33956
- const to = cursor;
33957
- out.push({ blockIndex: index, from, to });
33958
- if (index < root.children.length - 1) {
34396
+ cursor += blockLength(block);
34397
+ out.push({ blockIndex: index, from, to: cursor });
34398
+ if (index < root.children.length - 1 && root.children[index + 1]?.type === "paragraph") {
33959
34399
  cursor += 1;
33960
34400
  }
33961
34401
  }
33962
34402
  return out;
33963
34403
  }
34404
+ function blockLength(block) {
34405
+ if (block.type === "paragraph") {
34406
+ return block.children.reduce(
34407
+ (total, child) => total + inlineLength2(child),
34408
+ 0
34409
+ );
34410
+ }
34411
+ if (block.type === "table") {
34412
+ let total = 0;
34413
+ for (const row2 of block.rows) {
34414
+ for (const cell of row2.cells) {
34415
+ for (const child of cell.children) {
34416
+ total += blockLength(child);
34417
+ }
34418
+ }
34419
+ }
34420
+ return total;
34421
+ }
34422
+ if (block.type === "sdt") {
34423
+ const descriptor = describeStructuredWrapperBlock(block, {
34424
+ projectVisibleTocContentControls: true
34425
+ });
34426
+ if (descriptor) return 1;
34427
+ return block.children.reduce((total, child) => total + blockLength(child), 0);
34428
+ }
34429
+ return 1;
34430
+ }
33964
34431
  function computeInlinePositions(document2, blocks) {
33965
34432
  const envelope = document2;
33966
34433
  const root = "content" in envelope ? envelope.content : document2;
@@ -34177,8 +34644,8 @@ function deriveReplaceability(kind, provenance) {
34177
34644
  if (kind === "scope") {
34178
34645
  if (provenance === "marker-backed") {
34179
34646
  return {
34180
- level: "preserve-only",
34181
- reason: "multi-paragraph-replace-not-implemented"
34647
+ level: "text-only",
34648
+ reason: "marker-backed-multi-paragraph-preserves-anchor"
34182
34649
  };
34183
34650
  }
34184
34651
  return {
@@ -35238,11 +35705,9 @@ function compileParagraphReplacement(entry, proposed, options) {
35238
35705
  };
35239
35706
  }
35240
35707
  if (proposed.proposedContent.kind === "structured") {
35241
- if (options.posture === "suggest-mode") {
35242
- return null;
35243
- }
35244
35708
  const fragment = proposed.proposedContent.structured;
35245
35709
  if (!isStructuredReplacementContent(fragment)) return null;
35710
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
35246
35711
  const blockCount = fragment.blocks.length;
35247
35712
  const summaryScope = rangeKind === "inline-marker" ? `paragraph #${entry.blockIndex} inline-marker range [${effectiveRange.from}..${effectiveRange.to}]` : rangeKind === "opaque-preserving-text" ? `paragraph #${entry.blockIndex} opaque-preserving text range [${effectiveRange.from}..${effectiveRange.to}]` : `paragraph #${entry.blockIndex}`;
35248
35713
  const actionVerb = proposed.operation === "insert-before" ? "insert before" : proposed.operation === "insert-after" ? "insert after" : "replace";
@@ -35253,8 +35718,8 @@ function compileParagraphReplacement(entry, proposed, options) {
35253
35718
  operation: proposed.operation,
35254
35719
  steps: Object.freeze([
35255
35720
  {
35256
- kind: "fragment-replace",
35257
- summary: actionSummary,
35721
+ kind: stepKind,
35722
+ summary: stepKind === "fragment-replace" ? actionSummary : `suggest-mode ${actionSummary}`,
35258
35723
  ...textLeafEditableTargetHint(entry, blockRange) ? { editableTargetHint: textLeafEditableTargetHint(entry, blockRange) } : {},
35259
35724
  range: { from: operationRange.from, to: operationRange.to },
35260
35725
  fragment
@@ -35340,6 +35805,56 @@ function compileScopeKind(entry, options) {
35340
35805
  partial: true
35341
35806
  };
35342
35807
  }
35808
+ function compileScopeReplacement(entry, proposed, options) {
35809
+ if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace") {
35810
+ return null;
35811
+ }
35812
+ const markerRange = buildScopePositionMap(options.document).markerScopes.get(
35813
+ entry.handle.scopeId
35814
+ );
35815
+ if (!markerRange) return null;
35816
+ if (proposed.proposedContent.kind === "text") {
35817
+ const text = proposed.proposedContent.text ?? "";
35818
+ const stepKind = options.posture === "suggest-mode" ? "text-insert-tracked" : "text-replace";
35819
+ return {
35820
+ scopeId: entry.handle.scopeId,
35821
+ targetKind: "scope",
35822
+ operation: proposed.operation,
35823
+ steps: Object.freeze([
35824
+ {
35825
+ kind: stepKind,
35826
+ summary: stepKind === "text-replace" ? `replace multi-paragraph scope ${entry.handle.scopeId} text [${markerRange.from}..${markerRange.to}] (len ${text.length})` : `suggest-mode replace multi-paragraph scope ${entry.handle.scopeId} text [${markerRange.from}..${markerRange.to}] (len ${text.length})`,
35827
+ range: { from: markerRange.from, to: markerRange.to },
35828
+ text,
35829
+ ...proposed.formatting ? { formatting: proposed.formatting } : {}
35830
+ }
35831
+ ]),
35832
+ ...proposed.preserve ? { preserve: proposed.preserve } : {},
35833
+ posture: options.posture
35834
+ };
35835
+ }
35836
+ if (proposed.proposedContent.kind === "structured") {
35837
+ const fragment = proposed.proposedContent.structured;
35838
+ if (!isStructuredReplacementContent(fragment)) return null;
35839
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
35840
+ return {
35841
+ scopeId: entry.handle.scopeId,
35842
+ targetKind: "scope",
35843
+ operation: proposed.operation,
35844
+ steps: Object.freeze([
35845
+ {
35846
+ kind: stepKind,
35847
+ summary: stepKind === "fragment-replace" ? `replace multi-paragraph scope ${entry.handle.scopeId} with structured fragment [${markerRange.from}..${markerRange.to}] (${fragment.blocks.length} block(s))` : `suggest-mode replace multi-paragraph scope ${entry.handle.scopeId} with structured fragment [${markerRange.from}..${markerRange.to}] (${fragment.blocks.length} block(s))`,
35848
+ range: { from: markerRange.from, to: markerRange.to },
35849
+ fragment
35850
+ }
35851
+ ]),
35852
+ ...proposed.preserve ? { preserve: proposed.preserve } : {},
35853
+ posture: options.posture
35854
+ };
35855
+ }
35856
+ return null;
35857
+ }
35343
35858
 
35344
35859
  // src/runtime/scopes/scope-kinds/table.ts
35345
35860
  function compileTableScope(entry, options = {}) {
@@ -35711,6 +36226,99 @@ function paragraphFirstMarkerStart(paragraph, knownScopeIds) {
35711
36226
  }
35712
36227
  return null;
35713
36228
  }
36229
+ function paragraphHasMarkerEnd(paragraph, scopeId) {
36230
+ return paragraph.children.some(
36231
+ (child) => child.type === "scope_marker_end" && child.scopeId === scopeId
36232
+ );
36233
+ }
36234
+ function paragraphSameParagraphMarkerScopeId(paragraph, knownScopeIds) {
36235
+ const markerScopeId = paragraphFirstMarkerStart(paragraph, knownScopeIds);
36236
+ if (!markerScopeId) return null;
36237
+ return paragraphHasMarkerEnd(paragraph, markerScopeId) ? markerScopeId : null;
36238
+ }
36239
+ function markerStableRefOverride(markerScopeId, semanticPath, overlay) {
36240
+ const hint = stableRefHintForScopeId(markerScopeId, overlay);
36241
+ if (hint === "semantic-path") {
36242
+ return {
36243
+ kind: "semantic-path",
36244
+ value: semanticPath.join("/")
36245
+ };
36246
+ }
36247
+ return { kind: "scope-id", value: markerScopeId };
36248
+ }
36249
+ function enumerateNestedMarkerBackedParagraphs(blocks, input) {
36250
+ const out = [];
36251
+ for (let childIndex = 0; childIndex < blocks.length; childIndex += 1) {
36252
+ const block = blocks[childIndex];
36253
+ if (!block) continue;
36254
+ if (block.type === "paragraph") {
36255
+ const markerScopeId = paragraphSameParagraphMarkerScopeId(
36256
+ block,
36257
+ input.knownOverlayScopeIds
36258
+ );
36259
+ if (!markerScopeId) continue;
36260
+ const kind = detectParagraphKind(block);
36261
+ const semanticPath = [
36262
+ ...input.semanticPrefix,
36263
+ kind,
36264
+ String(childIndex)
36265
+ ];
36266
+ const handle = buildHandle(
36267
+ markerScopeId,
36268
+ input.documentId,
36269
+ semanticPath,
36270
+ "marker-backed",
36271
+ "marker-backed",
36272
+ input.parentScopeId,
36273
+ markerStableRefOverride(markerScopeId, semanticPath, input.overlay)
36274
+ );
36275
+ out.push({
36276
+ kind,
36277
+ handle,
36278
+ paragraph: block,
36279
+ blockIndex: input.rootBlockIndex,
36280
+ classifications: input.classificationIndex.get(markerScopeId) ?? Object.freeze([])
36281
+ });
36282
+ continue;
36283
+ }
36284
+ if (block.type === "sdt") {
36285
+ out.push(
36286
+ ...enumerateNestedMarkerBackedParagraphs(block.children, {
36287
+ ...input,
36288
+ semanticPrefix: [
36289
+ ...input.semanticPrefix,
36290
+ "sdt",
36291
+ String(childIndex)
36292
+ ]
36293
+ })
36294
+ );
36295
+ continue;
36296
+ }
36297
+ if (block.type === "table") {
36298
+ for (let rowIdx = 0; rowIdx < block.rows.length; rowIdx += 1) {
36299
+ const row2 = block.rows[rowIdx];
36300
+ for (let cellIdx = 0; cellIdx < row2.cells.length; cellIdx += 1) {
36301
+ const cell = row2.cells[cellIdx];
36302
+ out.push(
36303
+ ...enumerateNestedMarkerBackedParagraphs(cell.children, {
36304
+ ...input,
36305
+ semanticPrefix: [
36306
+ ...input.semanticPrefix,
36307
+ "table",
36308
+ String(childIndex),
36309
+ "row",
36310
+ String(rowIdx),
36311
+ "cell",
36312
+ String(cellIdx)
36313
+ ]
36314
+ })
36315
+ );
36316
+ }
36317
+ }
36318
+ }
36319
+ }
36320
+ return out;
36321
+ }
35714
36322
  function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
35715
36323
  const out = [];
35716
36324
  for (let i = 0; i < paragraph.children.length; i += 1) {
@@ -35993,6 +36601,17 @@ function enumerateScopes(document2, inputs = {}) {
35993
36601
  cellIndex: cellIdx,
35994
36602
  classifications: Object.freeze([])
35995
36603
  });
36604
+ for (const nested of enumerateNestedMarkerBackedParagraphs(cell.children, {
36605
+ documentId,
36606
+ rootBlockIndex: index,
36607
+ semanticPrefix: cellSemanticPath,
36608
+ parentScopeId: cellScopeId,
36609
+ knownOverlayScopeIds,
36610
+ classificationIndex,
36611
+ overlay: inputs.overlay
36612
+ })) {
36613
+ results.push(nested);
36614
+ }
35996
36615
  }
35997
36616
  }
35998
36617
  }
@@ -36244,31 +36863,6 @@ function deriveScopeAdjacentGeometryEvidence(scope, entry, provider) {
36244
36863
  };
36245
36864
  }
36246
36865
 
36247
- // src/runtime/scopes/multi-paragraph-refusal.ts
36248
- var MULTI_PARAGRAPH_REPLACEMENT_REFUSAL = "compile-refused:scope:multi-paragraph-replace-not-implemented";
36249
- function shapeBlocker(shape) {
36250
- switch (shape) {
36251
- case "text":
36252
- return "compile-refused:scope:multi-paragraph-text-replace-not-implemented";
36253
- case "fragment":
36254
- return "compile-refused:scope:multi-paragraph-fragment-replace-not-implemented";
36255
- default:
36256
- return "compile-refused:scope:multi-paragraph-replace-shape-not-implemented";
36257
- }
36258
- }
36259
- function multiParagraphReplacementBlockers(shape = "unknown") {
36260
- return Object.freeze([
36261
- MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
36262
- shapeBlocker(shape),
36263
- "capability:scope:block-granular-replacement-lowering-required",
36264
- "capability:scope:provenance:marker-backed-required",
36265
- "capability:scope:layout-completeness-required",
36266
- "capability:scope:geometry-completeness-required",
36267
- "capability:scope:continuation-state-required",
36268
- "capability:scope:preservation-verdict-required"
36269
- ]);
36270
- }
36271
-
36272
36866
  // src/runtime/scopes/capabilities.ts
36273
36867
  var PARAGRAPH_LIKE = /* @__PURE__ */ new Set([
36274
36868
  "paragraph",
@@ -36485,10 +37079,9 @@ function replaceTextCapability(scope, context) {
36485
37079
  if (guard) return blocked(guard);
36486
37080
  if (scope.kind === "list-item") {
36487
37081
  return blocked(
36488
- "compile-blocked:list-text:authoritative-readback-required",
37082
+ "compile-blocked:list-text:exact-list-text-handle-required",
36489
37083
  [
36490
- "capability:list-item:authoritative-readback-required",
36491
- "capability:list-text:export-persistent-target-required"
37084
+ "capability:list-item:exact-list-text-handle-required"
36492
37085
  ]
36493
37086
  );
36494
37087
  }
@@ -36503,6 +37096,15 @@ function replaceTextCapability(scope, context) {
36503
37096
  generatedOrLinked
36504
37097
  );
36505
37098
  }
37099
+ if (scope.kind === "scope") {
37100
+ return supported(
37101
+ "compile-supported:scope:multi-paragraph-text-replace",
37102
+ [
37103
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37104
+ ...evidenceWarnings(context)
37105
+ ]
37106
+ );
37107
+ }
36506
37108
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
36507
37109
  if (scope.kind === "table-cell") {
36508
37110
  if (context?.tableCellTextRange?.status === "ok") {
@@ -36524,12 +37126,8 @@ function replaceTextCapability(scope, context) {
36524
37126
  ]
36525
37127
  );
36526
37128
  }
36527
- const reason = scope.kind === "scope" && scope.replaceability.reason ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : `compile-refused:${scope.kind}`;
36528
- return unsupported(
36529
- reason,
36530
- scope.kind === "scope" ? multiParagraphReplacementBlockers("text") : [reason],
36531
- evidenceWarnings(context)
36532
- );
37129
+ const reason = `compile-refused:${scope.kind}`;
37130
+ return unsupported(reason, [reason], evidenceWarnings(context));
36533
37131
  }
36534
37132
  if (scope.replaceability.level === "blocked" || scope.replaceability.level === "preserve-only" || scope.replaceability.level === "formatting-only") {
36535
37133
  const reason = scope.replaceability.reason ? `replaceability:${scope.replaceability.reason}` : `replaceability:${scope.replaceability.level}`;
@@ -36557,24 +37155,25 @@ function replaceFragmentCapability(scope, context) {
36557
37155
  generatedOrLinked
36558
37156
  );
36559
37157
  }
36560
- if (!PARAGRAPH_LIKE.has(scope.kind)) {
36561
- const reason = scope.kind === "scope" && scope.replaceability.reason ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : `compile-refused:${scope.kind}`;
36562
- return unsupported(
36563
- reason,
36564
- scope.kind === "scope" ? multiParagraphReplacementBlockers("fragment") : [reason],
36565
- evidenceWarnings(context)
37158
+ if (scope.kind === "scope") {
37159
+ return supported(
37160
+ "compile-supported:scope:multi-paragraph-fragment-replace",
37161
+ [
37162
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37163
+ ...evidenceWarnings(context)
37164
+ ]
36566
37165
  );
36567
37166
  }
36568
- if (scope.workflow.effectiveMode === "suggest") {
36569
- return unsupported(
36570
- `compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
36571
- [`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
36572
- ["guard:suggest-mode"]
36573
- );
37167
+ if (!PARAGRAPH_LIKE.has(scope.kind)) {
37168
+ const reason = `compile-refused:${scope.kind}`;
37169
+ return unsupported(reason, [reason], evidenceWarnings(context));
36574
37170
  }
36575
37171
  return supported(
36576
- "compile-supported:paragraph-like:fragment-replace",
36577
- evidenceWarnings(context)
37172
+ scope.workflow.effectiveMode === "suggest" ? "compile-supported:paragraph-like:fragment-replace-tracked" : "compile-supported:paragraph-like:fragment-replace",
37173
+ [
37174
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37175
+ ...evidenceWarnings(context)
37176
+ ]
36578
37177
  );
36579
37178
  }
36580
37179
  function formattingCapability(scope, context) {
@@ -37628,6 +38227,14 @@ function supportedNonTextCommandEvidence(target) {
37628
38227
  if (target.commandFamily === "link-bookmark") {
37629
38228
  return linkBookmarkCommandEvidence(target, []);
37630
38229
  }
38230
+ if (target.commandFamily === "object" && isImageObjectTarget(target) && typeof target.object?.mediaId === "string" && target.object.mediaId.length > 0 && onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
38231
+ return {
38232
+ status: "supported",
38233
+ commandFamily: target.commandFamily,
38234
+ intents: commandIntentsForTarget(target),
38235
+ reason: "l07:image-layout-target-supported"
38236
+ };
38237
+ }
37631
38238
  return null;
37632
38239
  }
37633
38240
  function onlyBlockers(blockers, allowed) {
@@ -39155,6 +39762,231 @@ function compileScopeBundleById(scopeId, inputs) {
39155
39762
  return compileScopeBundle(compiled, { ...inputs, scopes });
39156
39763
  }
39157
39764
 
39765
+ // src/runtime/scopes/editable-graph.ts
39766
+ function uniq(values) {
39767
+ return Object.freeze([...new Set(values.filter(Boolean))]);
39768
+ }
39769
+ function commandFamilyForTarget(target) {
39770
+ const intents = target.runtimeCommand.intents;
39771
+ if (target.table?.operationScope === "text" && target.runtimeCommand.commandFamily === "text-leaf") {
39772
+ return "table-text";
39773
+ }
39774
+ if (intents.some((intent) => intent.startsWith("table-"))) {
39775
+ if (intents.some((intent) => intent.includes("structure"))) {
39776
+ return "table-structure";
39777
+ }
39778
+ return "table-text";
39779
+ }
39780
+ if (intents.some((intent) => intent.startsWith("list-"))) {
39781
+ if (intents.some((intent) => intent.includes("structure"))) {
39782
+ return "list-structure";
39783
+ }
39784
+ return "list-text";
39785
+ }
39786
+ return target.runtimeCommand.commandFamily;
39787
+ }
39788
+ function familyForTarget(target) {
39789
+ if (target.runtimeCommand.intents.some(
39790
+ (intent) => intent === "table-structure-action" || intent === "table-merge-cells" || intent === "table-split-cell" || intent === "list-structure-action"
39791
+ )) {
39792
+ return "structure";
39793
+ }
39794
+ if (target.runtimeCommand.intents.some(
39795
+ (intent) => intent === "field-update" || intent === "toc-refresh" || intent === "hyperlink-update" || intent === "bookmark-update" || intent === "custom-xml-update"
39796
+ )) {
39797
+ return "value";
39798
+ }
39799
+ if (target.runtimeCommand.commandFamily === "object" || target.runtimeCommand.intents.some(
39800
+ (intent) => intent === "object-edit" || intent === "image-layout" || intent === "image-frame" || intent === "chart-edit" || intent === "embedded-content-update"
39801
+ )) {
39802
+ return "object";
39803
+ }
39804
+ if (target.runtimeCommand.commandFamily === "metadata") return "metadata";
39805
+ if (target.runtimeCommand.commandFamily === "preserve-only-refusal" || target.posture.preserveOnly || target.kind === "opaque-content") {
39806
+ return "preservation";
39807
+ }
39808
+ return "text";
39809
+ }
39810
+ function statusForTarget(target, family) {
39811
+ if (family === "preservation") return "preserve";
39812
+ if (target.runtimeCommand.status === "supported" && target.runtimeCommand.actionHandle) {
39813
+ return "ready";
39814
+ }
39815
+ if (target.runtimeCommand.status === "supported") return "diagnostic";
39816
+ return "needs-command";
39817
+ }
39818
+ function blockersForTarget(target) {
39819
+ return uniq([
39820
+ ...target.posture.blockers,
39821
+ ...target.runtimeTextCommand.blockers ?? [],
39822
+ ...target.runtimeCommand.blockers ?? [],
39823
+ ...target.workflowBlockers?.map((blocker) => blocker.blocker) ?? []
39824
+ ]);
39825
+ }
39826
+ function projectEditableTarget(target) {
39827
+ const family = familyForTarget(target);
39828
+ const commandFamily = commandFamilyForTarget(target);
39829
+ return Object.freeze({
39830
+ targetId: target.targetKey,
39831
+ family,
39832
+ kind: target.kind,
39833
+ relation: target.relation,
39834
+ status: statusForTarget(target, family),
39835
+ commandFamily,
39836
+ ...target.runtimeCommand.actionHandle ? { actionHandle: target.runtimeCommand.actionHandle } : {},
39837
+ ...target.runtimeCommand.canonicalAddress ? { canonicalAddress: target.runtimeCommand.canonicalAddress } : {},
39838
+ ...target.readback ? { readback: target.readback } : {},
39839
+ blockers: blockersForTarget(target),
39840
+ warnings: Object.freeze([]),
39841
+ intents: Object.freeze([...target.runtimeCommand.intents])
39842
+ });
39843
+ }
39844
+ function projectTableAction(action) {
39845
+ const family = action.family === "table-structure" ? "structure" : "text";
39846
+ const blockers = uniq(action.blockers ?? []);
39847
+ return Object.freeze({
39848
+ targetId: action.handle,
39849
+ family,
39850
+ kind: action.targetKind,
39851
+ relation: action.relation,
39852
+ status: action.status === "supported" && action.actionHandle ? "ready" : action.status === "supported" ? "diagnostic" : "needs-command",
39853
+ commandFamily: action.family,
39854
+ ...action.actionHandle ? { actionHandle: action.actionHandle } : {},
39855
+ ...action.canonicalAddress ? { canonicalAddress: action.canonicalAddress } : {},
39856
+ ...action.readback ? { readback: action.readback } : {},
39857
+ blockers,
39858
+ warnings: Object.freeze([...action.warnings ?? []]),
39859
+ intents: Object.freeze([...action.intents])
39860
+ });
39861
+ }
39862
+ function metadataTarget(bundle) {
39863
+ return Object.freeze({
39864
+ targetId: `${bundle.scope.handle.scopeId}:metadata`,
39865
+ family: "metadata",
39866
+ kind: "scope-metadata",
39867
+ status: "ready",
39868
+ commandFamily: "metadata",
39869
+ actionHandle: `scope-metadata:${bundle.scope.handle.scopeId}`,
39870
+ blockers: Object.freeze([]),
39871
+ warnings: Object.freeze([]),
39872
+ intents: Object.freeze(["metadata-update"])
39873
+ });
39874
+ }
39875
+ function scopeReplacementTarget(bundle) {
39876
+ const verdict = bundle.evidence.capabilities?.canReplaceText;
39877
+ if (!verdict?.supported) return null;
39878
+ return Object.freeze({
39879
+ targetId: `${bundle.scope.handle.scopeId}:scope-replace-text`,
39880
+ family: "text",
39881
+ kind: `${bundle.scope.kind}:scope-replacement`,
39882
+ relation: "exact-scope",
39883
+ status: "ready",
39884
+ commandFamily: "scope-replacement",
39885
+ blockers: Object.freeze([]),
39886
+ warnings: Object.freeze([...verdict.warnings ?? []]),
39887
+ intents: Object.freeze(["text-leaf-edit"])
39888
+ });
39889
+ }
39890
+ function group(targets, family) {
39891
+ return Object.freeze(targets.filter((target) => target.family === family));
39892
+ }
39893
+ function operation(kind, targets) {
39894
+ const ready = targets.filter((target) => target.status === "ready");
39895
+ const pending = targets.filter((target) => target.status === "needs-command");
39896
+ return Object.freeze({
39897
+ kind,
39898
+ status: ready.length > 0 ? "ready" : pending.length > 0 ? "needs-command" : "needs-target",
39899
+ targetIds: Object.freeze(ready.map((target) => target.targetId)),
39900
+ commandFamilies: uniq(ready.map((target) => target.commandFamily ?? ""))
39901
+ });
39902
+ }
39903
+ function contentModelFor(textTargets, valueTargets, structureTargets, objectTargets) {
39904
+ const families = [
39905
+ textTargets.length > 0 ? "text" : "",
39906
+ valueTargets.length > 0 ? "value" : "",
39907
+ structureTargets.length > 0 ? "structure" : "",
39908
+ objectTargets.length > 0 ? "object" : ""
39909
+ ].filter(Boolean);
39910
+ if (families.length === 0) return "metadata";
39911
+ if (families.length > 1) return "mixed";
39912
+ return families[0];
39913
+ }
39914
+ function summaryFor(model, targets) {
39915
+ const ready = targets.filter((target) => target.status === "ready").length;
39916
+ const needsCommand = targets.filter(
39917
+ (target) => target.status === "needs-command"
39918
+ ).length;
39919
+ const preserve = targets.filter((target) => target.status === "preserve").length;
39920
+ return `${model} scope editable graph: ${ready} ready target(s), ${needsCommand} target(s) needing command, ${preserve} preserve target(s)`;
39921
+ }
39922
+ function compileScopeEditableGraph(bundle) {
39923
+ const scopeTextTarget = scopeReplacementTarget(bundle);
39924
+ const projected = [
39925
+ ...bundle.evidence.editableTargets?.entries.map(projectEditableTarget) ?? [],
39926
+ ...bundle.evidence.table?.actions.map(projectTableAction) ?? [],
39927
+ ...scopeTextTarget ? [scopeTextTarget] : [],
39928
+ metadataTarget(bundle)
39929
+ ];
39930
+ const byId = /* @__PURE__ */ new Map();
39931
+ for (const target of projected) {
39932
+ const existing = byId.get(target.targetId);
39933
+ if (!existing || existing.status !== "ready") {
39934
+ byId.set(target.targetId, target);
39935
+ }
39936
+ }
39937
+ const targets = Object.freeze([...byId.values()]);
39938
+ const textTargets = group(targets, "text");
39939
+ const valueTargets = group(targets, "value");
39940
+ const structureTargets = group(targets, "structure");
39941
+ const objectTargets = group(targets, "object");
39942
+ const metadataTargets = group(targets, "metadata");
39943
+ const preservationTargets = group(targets, "preservation");
39944
+ const contentModel = contentModelFor(
39945
+ textTargets,
39946
+ valueTargets,
39947
+ structureTargets,
39948
+ objectTargets
39949
+ );
39950
+ const operations = Object.freeze([
39951
+ operation("replace-text", textTargets),
39952
+ operation("replace-value", valueTargets),
39953
+ operation("edit-structure", structureTargets),
39954
+ operation("edit-object", objectTargets),
39955
+ operation("attach-metadata", metadataTargets)
39956
+ ]);
39957
+ const readyTargetCount = targets.filter((target) => target.status === "ready").length;
39958
+ const needsCommandTargetCount = targets.filter(
39959
+ (target) => target.status === "needs-command"
39960
+ ).length;
39961
+ const preserveTargetCount = targets.filter(
39962
+ (target) => target.status === "preserve"
39963
+ ).length;
39964
+ const blockers = uniq(targets.flatMap((target) => target.blockers));
39965
+ const warnings = uniq(targets.flatMap((target) => target.warnings));
39966
+ const readiness = Object.freeze({
39967
+ status: readyTargetCount > metadataTargets.length ? needsCommandTargetCount > 0 || preserveTargetCount > 0 ? "partial" : "ready" : "metadata-only",
39968
+ readyTargetCount,
39969
+ needsCommandTargetCount,
39970
+ preserveTargetCount,
39971
+ blockers,
39972
+ warnings
39973
+ });
39974
+ return Object.freeze({
39975
+ scope: bundle.scope.handle,
39976
+ contentModel,
39977
+ targets,
39978
+ textTargets,
39979
+ valueTargets,
39980
+ structureTargets,
39981
+ objectTargets,
39982
+ metadataTargets,
39983
+ preservationTargets,
39984
+ operations,
39985
+ readiness,
39986
+ summary: summaryFor(contentModel, targets)
39987
+ });
39988
+ }
39989
+
39158
39990
  // src/runtime/scopes/issue-lifecycle.ts
39159
39991
  function documentHash(doc) {
39160
39992
  let textLength = 0;
@@ -39307,11 +40139,11 @@ function computePreservationVerdict(document2, range, positionMap) {
39307
40139
  }
39308
40140
 
39309
40141
  // src/runtime/scopes/action-validation.ts
39310
- function inferActionId(operation, content) {
39311
- if (operation === "formatting") {
40142
+ function inferActionId(operation2, content) {
40143
+ if (operation2 === "formatting") {
39312
40144
  return "fix_formatting";
39313
40145
  }
39314
- switch (operation) {
40146
+ switch (operation2) {
39315
40147
  case "replace":
39316
40148
  return content.kind === "text" ? "rewrite_paragraph" : "generate_text";
39317
40149
  case "insert-before":
@@ -39462,11 +40294,11 @@ function collectCapabilityVerdict(inputs, blockedReasons, warnings) {
39462
40294
  }
39463
40295
  }
39464
40296
  if (inputs.scope.kind === "list-item" && inputs.operation === "replace" && inputs.proposedContent.kind === "text") {
39465
- const code = "capability:list-item:authoritative-readback-required";
40297
+ const code = "capability:list-item:exact-list-text-handle-required";
39466
40298
  blockedReasons.push(code);
39467
40299
  warnings.push({
39468
40300
  code,
39469
- message: "list-item flat text replacement is blocked until the runtime validates an export-persistent list text command path",
40301
+ message: "list-item flat text replacement is blocked unless the caller uses an exact list-text action handle",
39470
40302
  source: "capability"
39471
40303
  });
39472
40304
  }
@@ -39703,11 +40535,44 @@ function compileReplacement(inputs) {
39703
40535
  inputs.posture,
39704
40536
  inputs.tableCellTextRange
39705
40537
  );
40538
+ case "scope":
40539
+ if (inputs.enumeratedScope.kind === "scope") {
40540
+ return compileScopeReplacement(inputs.enumeratedScope, inputs.proposed, {
40541
+ document: inputs.document,
40542
+ posture: inputs.posture
40543
+ });
40544
+ }
40545
+ return null;
39706
40546
  default:
39707
40547
  return null;
39708
40548
  }
39709
40549
  }
39710
40550
 
40551
+ // src/runtime/scopes/multi-paragraph-refusal.ts
40552
+ var MULTI_PARAGRAPH_REPLACEMENT_REFUSAL = "compile-refused:scope:multi-paragraph-replace-not-implemented";
40553
+ function shapeBlocker(shape) {
40554
+ switch (shape) {
40555
+ case "text":
40556
+ return "compile-refused:scope:multi-paragraph-text-replace-not-implemented";
40557
+ case "fragment":
40558
+ return "compile-refused:scope:multi-paragraph-fragment-replace-not-implemented";
40559
+ default:
40560
+ return "compile-refused:scope:multi-paragraph-replace-shape-not-implemented";
40561
+ }
40562
+ }
40563
+ function multiParagraphReplacementBlockers(shape = "unknown") {
40564
+ return Object.freeze([
40565
+ MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
40566
+ shapeBlocker(shape),
40567
+ "capability:scope:block-granular-replacement-lowering-required",
40568
+ "capability:scope:provenance:marker-backed-required",
40569
+ "capability:scope:layout-completeness-required",
40570
+ "capability:scope:geometry-completeness-required",
40571
+ "capability:scope:continuation-state-required",
40572
+ "capability:scope:preservation-verdict-required"
40573
+ ]);
40574
+ }
40575
+
39711
40576
  // src/runtime/scopes/replacement/apply.ts
39712
40577
  function documentHash2(doc) {
39713
40578
  let hash = 2166136261;
@@ -39870,14 +40735,16 @@ function applyScopeReplacement(inputs) {
39870
40735
  });
39871
40736
  if (!plan) {
39872
40737
  const paragraphLike = resolvedScope.kind === "paragraph" || resolvedScope.kind === "heading" || resolvedScope.kind === "list-item";
39873
- const blockers = resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
40738
+ const blockers = resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
40739
+ `compile-refused:scope:operation-not-implemented:${proposed.operation}`
40740
+ ] : resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
39874
40741
  proposed.proposedContent.kind === "structured" ? "fragment" : "text"
39875
40742
  ) : paragraphLike && proposed.operation === "replace" && proposed.preserve?.opaqueFragments === true ? [
39876
40743
  `compile-refused:${resolvedScope.kind}:opaque-preserving-text-target-unavailable`
39877
40744
  ] : resolvedScope.kind === "table-cell" ? [tableCellRefusalBlocker(tableCellTextRange)] : paragraphLike && proposed.operation !== "replace" ? [
39878
40745
  `compile-refused:${resolvedScope.kind}:operation-not-implemented:${proposed.operation}`
39879
40746
  ] : [`compile-refused:${resolvedScope.kind}`];
39880
- const blocker = resolvedScope.kind === "scope" ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : blockers[0] ?? `compile-refused:${resolvedScope.kind}`;
40747
+ const blocker = blockers[0] ?? `compile-refused:${resolvedScope.kind}`;
39881
40748
  const refused = {
39882
40749
  safe: false,
39883
40750
  posture: "hard-refusal",
@@ -40356,10 +41223,10 @@ function createScopeCompilerService(runtime) {
40356
41223
  emitMetadataAudit(request) {
40357
41224
  try {
40358
41225
  const snapshot = request.targetScopeSnapshot;
40359
- const operation = "annotate";
41226
+ const operation2 = "annotate";
40360
41227
  const proposed = {
40361
41228
  targetHandle: snapshot.handle,
40362
- operation,
41229
+ operation: operation2,
40363
41230
  proposedContent: {
40364
41231
  kind: "structured",
40365
41232
  structured: {
@@ -40434,6 +41301,10 @@ function createScopeCompilerService(runtime) {
40434
41301
  ...editableTargetCache ? { editableTargetCache } : {}
40435
41302
  });
40436
41303
  },
41304
+ compileEditableGraphById(scopeId, nowUtc) {
41305
+ const bundle = this.compileBundleById(scopeId, nowUtc);
41306
+ return bundle ? compileScopeEditableGraph(bundle) : null;
41307
+ },
40437
41308
  buildReplacementScope(targetHandle, input) {
40438
41309
  return proposeReplacement({
40439
41310
  targetHandle,
@@ -41300,7 +42171,8 @@ function buildResolvedSections(document2) {
41300
42171
  const mainSurface = createEditorSurfaceSnapshot(
41301
42172
  document2,
41302
42173
  createSelectionSnapshot(0, 0),
41303
- MAIN_STORY_TARGET
42174
+ MAIN_STORY_TARGET,
42175
+ { editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX }
41304
42176
  );
41305
42177
  const sections = [];
41306
42178
  let sectionStart = 0;
@@ -47368,11 +48240,45 @@ function createLayoutEngine(options = {}) {
47368
48240
  const telemetryBus = options.telemetryBus;
47369
48241
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
47370
48242
  const listeners = /* @__PURE__ */ new Set();
47371
- let cachedKey = null;
47372
- let cachedGraph = null;
47373
- let cachedFormatting = null;
47374
- let cachedMapper = null;
48243
+ let cachedFull = null;
48244
+ let cachedWindowed = null;
47375
48245
  let previousPageCount = 0;
48246
+ function isFullViewportKey(key) {
48247
+ return key === FULL_VIEWPORT_WINDOW_KEY;
48248
+ }
48249
+ function getCachedSlot(viewportWindowKeyValue) {
48250
+ return isFullViewportKey(viewportWindowKeyValue) ? cachedFull : cachedWindowed;
48251
+ }
48252
+ function preferredCachedGraph() {
48253
+ return cachedFull?.graph ?? cachedWindowed?.graph ?? null;
48254
+ }
48255
+ function clearAllSlots() {
48256
+ cachedFull = null;
48257
+ cachedWindowed = null;
48258
+ }
48259
+ function clearWindowedSlot() {
48260
+ cachedWindowed = null;
48261
+ }
48262
+ function evictStaleSiblingSlot(freshKey) {
48263
+ const sibling = isFullViewportKey(freshKey.viewportWindowKey) ? cachedWindowed : cachedFull;
48264
+ if (sibling === null) return;
48265
+ if (sibling.key.content === freshKey.content && sibling.key.styles === freshKey.styles && sibling.key.subParts === freshKey.subParts) {
48266
+ return;
48267
+ }
48268
+ if (isFullViewportKey(freshKey.viewportWindowKey)) {
48269
+ cachedWindowed = null;
48270
+ } else {
48271
+ cachedFull = null;
48272
+ }
48273
+ }
48274
+ function commitSlot(slot) {
48275
+ if (isFullViewportKey(slot.key.viewportWindowKey)) {
48276
+ cachedFull = slot;
48277
+ } else {
48278
+ cachedWindowed = slot;
48279
+ }
48280
+ evictStaleSiblingSlot(slot.key);
48281
+ }
47376
48282
  let pendingInvalidation = null;
47377
48283
  function emit(event) {
47378
48284
  for (const listener of listeners) {
@@ -47518,27 +48424,31 @@ function createLayoutEngine(options = {}) {
47518
48424
  subParts: document2.subParts,
47519
48425
  anchors: layoutInputs.anchors
47520
48426
  });
48427
+ const priorGraphForMaterialization = preferredCachedGraph();
47521
48428
  const graph = applyViewportWindowMaterialization(
47522
48429
  measuredGraph,
47523
48430
  viewportWindow,
47524
- cachedGraph
48431
+ priorGraphForMaterialization
47525
48432
  );
47526
- const dirtyFamilies = computeFieldDirtiness(cachedGraph, graph);
48433
+ const priorGraphForFieldDirtiness = preferredCachedGraph();
48434
+ const dirtyFamilies = computeFieldDirtiness(priorGraphForFieldDirtiness, graph);
47527
48435
  for (const family of dirtyFamilies) {
47528
48436
  dirtyFieldFamilies.add(family);
47529
48437
  }
47530
48438
  const formatting = buildResolvedFormattingState(document2, mainSurface);
47531
48439
  const currentPageCount = graph.contentPageCount;
47532
48440
  const pageCountDelta = currentPageCount !== previousPageCount ? { previous: previousPageCount, current: currentPageCount } : void 0;
47533
- cachedKey = {
47534
- content: document2.content,
47535
- styles: document2.styles,
47536
- subParts: document2.subParts,
47537
- viewportWindowKey: viewportWindowKey(viewportWindow)
47538
- };
47539
- cachedGraph = graph;
47540
- cachedFormatting = formatting;
47541
- cachedMapper = createPageFragmentMapper(graph);
48441
+ commitSlot({
48442
+ key: {
48443
+ content: document2.content,
48444
+ styles: document2.styles,
48445
+ subParts: document2.subParts,
48446
+ viewportWindowKey: viewportWindowKey(viewportWindow)
48447
+ },
48448
+ graph,
48449
+ formatting,
48450
+ mapper: createPageFragmentMapper(graph)
48451
+ });
47542
48452
  if (pageCountDelta) {
47543
48453
  emit({
47544
48454
  kind: "page_count_changed",
@@ -47579,7 +48489,7 @@ function createLayoutEngine(options = {}) {
47579
48489
  return graph;
47580
48490
  }
47581
48491
  function incrementalRelayout(input, pending) {
47582
- const priorGraph = cachedGraph;
48492
+ const priorGraph = cachedFull?.graph ?? null;
47583
48493
  const range = pending.result.dirtyPageRange;
47584
48494
  if (!priorGraph || !range) return null;
47585
48495
  const telemetryOn = telemetryBus?.isEnabled("layout") ?? false;
@@ -47672,6 +48582,22 @@ function createLayoutEngine(options = {}) {
47672
48582
  deriveDocumentPageSnapshots(splicedGraph)
47673
48583
  );
47674
48584
  const pageCountDelta = currentPageCount !== previousPageCount ? { previous: previousPageCount, current: currentPageCount } : void 0;
48585
+ const priorMapper = cachedFull?.mapper ?? null;
48586
+ commitSlot({
48587
+ key: {
48588
+ content: document2.content,
48589
+ styles: document2.styles,
48590
+ subParts: document2.subParts,
48591
+ viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
48592
+ },
48593
+ graph: splicedGraph,
48594
+ formatting,
48595
+ mapper: rebuildMapper(
48596
+ priorMapper ?? createPageFragmentMapper(splicedGraph),
48597
+ splicedGraph,
48598
+ firstDirty
48599
+ )
48600
+ });
47675
48601
  if (pageCountDelta) {
47676
48602
  emit({
47677
48603
  kind: "page_count_changed",
@@ -47701,19 +48627,6 @@ function createLayoutEngine(options = {}) {
47701
48627
  ...dirtyFamilies.length > 0 ? { dirtyFieldFamilies: dirtyFamilies } : {},
47702
48628
  ...pageCountDelta ? { pageCountDelta } : {}
47703
48629
  });
47704
- cachedKey = {
47705
- content: document2.content,
47706
- styles: document2.styles,
47707
- subParts: document2.subParts,
47708
- viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
47709
- };
47710
- cachedGraph = splicedGraph;
47711
- cachedFormatting = formatting;
47712
- cachedMapper = rebuildMapper(
47713
- cachedMapper ?? createPageFragmentMapper(splicedGraph),
47714
- splicedGraph,
47715
- firstDirty
47716
- );
47717
48630
  if (telemetryOn) {
47718
48631
  emitRecomputeCompleted(
47719
48632
  "bounded",
@@ -47730,13 +48643,14 @@ function createLayoutEngine(options = {}) {
47730
48643
  const document2 = input.document;
47731
48644
  const normalizedWindow = normalizeViewportPageWindow(input.viewportPageWindow);
47732
48645
  const currentViewportWindowKey = viewportWindowKey(normalizedWindow);
47733
- const keyEqual = cachedGraph !== null && cachedKey !== null && cachedKey.content === document2.content && cachedKey.styles === document2.styles && cachedKey.subParts === document2.subParts && cachedKey.viewportWindowKey === currentViewportWindowKey;
48646
+ const slot = getCachedSlot(currentViewportWindowKey);
48647
+ const keyEqual = slot !== null && slot.key.content === document2.content && slot.key.styles === document2.styles && slot.key.subParts === document2.subParts && slot.key.viewportWindowKey === currentViewportWindowKey;
47734
48648
  if (keyEqual && pendingInvalidation === null) {
47735
- return cachedGraph;
48649
+ return slot.graph;
47736
48650
  }
47737
48651
  const pending = pendingInvalidation;
47738
48652
  pendingInvalidation = null;
47739
- if (pending !== null && pending.result.scope === "bounded" && cachedGraph !== null && normalizedWindow === void 0) {
48653
+ if (pending !== null && pending.result.scope === "bounded" && cachedFull !== null && normalizedWindow === void 0) {
47740
48654
  const spliced = incrementalRelayout(input, pending);
47741
48655
  if (spliced !== null) {
47742
48656
  return spliced;
@@ -47745,16 +48659,39 @@ function createLayoutEngine(options = {}) {
47745
48659
  }
47746
48660
  return fullRebuild(input, pending?.reason);
47747
48661
  }
48662
+ function ensureSlotMapperAndFormatting(slot, document2) {
48663
+ if (slot.mapper === null) {
48664
+ slot.mapper = createPageFragmentMapper(slot.graph);
48665
+ }
48666
+ if (slot.formatting === null) {
48667
+ const mainSurface = createEditorSurfaceSnapshot(
48668
+ document2,
48669
+ createSelectionSnapshot(0, 0),
48670
+ MAIN_STORY_TARGET
48671
+ );
48672
+ slot.formatting = buildResolvedFormattingState(document2, mainSurface);
48673
+ }
48674
+ }
47748
48675
  function getMapper(input) {
47749
48676
  getGraphInternal(input);
47750
- return cachedMapper;
48677
+ const currentViewportWindowKey = viewportWindowKey(
48678
+ normalizeViewportPageWindow(input.viewportPageWindow)
48679
+ );
48680
+ const slot = getCachedSlot(currentViewportWindowKey);
48681
+ ensureSlotMapperAndFormatting(slot, input.document);
48682
+ return slot.mapper;
47751
48683
  }
47752
48684
  function getFormatting(input) {
47753
48685
  getGraphInternal(input);
47754
- return cachedFormatting;
48686
+ const currentViewportWindowKey = viewportWindowKey(
48687
+ normalizeViewportPageWindow(input.viewportPageWindow)
48688
+ );
48689
+ const slot = getCachedSlot(currentViewportWindowKey);
48690
+ ensureSlotMapperAndFormatting(slot, input.document);
48691
+ return slot.formatting;
47755
48692
  }
47756
48693
  if (autoUpgradeToCanvas && options.measurementProvider === void 0 && typeof document !== "undefined" && typeof HTMLCanvasElement !== "undefined") {
47757
- const readCachedRevision = () => cachedGraph?.revision ?? 0;
48694
+ const readCachedRevision = () => preferredCachedGraph()?.revision ?? 0;
47758
48695
  void (async () => {
47759
48696
  try {
47760
48697
  const mod = await Promise.resolve().then(() => (init_measurement_backend_canvas(), measurement_backend_canvas_exports));
@@ -47764,10 +48701,7 @@ function createLayoutEngine(options = {}) {
47764
48701
  options.measurementCache,
47765
48702
  telemetryBus
47766
48703
  );
47767
- cachedKey = null;
47768
- cachedGraph = null;
47769
- cachedFormatting = null;
47770
- cachedMapper = null;
48704
+ clearAllSlots();
47771
48705
  emit({
47772
48706
  kind: "measurement_backend_ready",
47773
48707
  revision: readCachedRevision(),
@@ -47811,22 +48745,19 @@ function createLayoutEngine(options = {}) {
47811
48745
  return getMapper(input);
47812
48746
  },
47813
48747
  invalidate(reason) {
47814
- const result = analyzeInvalidation(reason, cachedGraph);
48748
+ const result = analyzeInvalidation(reason, preferredCachedGraph());
47815
48749
  for (const family of result.dirtyFieldFamilies) {
47816
48750
  dirtyFieldFamilies.add(family);
47817
48751
  }
47818
48752
  if (result.scope === "bounded") {
47819
48753
  pendingInvalidation = { reason, result };
47820
48754
  } else {
47821
- cachedKey = null;
47822
- cachedGraph = null;
47823
- cachedFormatting = null;
47824
- cachedMapper = null;
48755
+ clearAllSlots();
47825
48756
  pendingInvalidation = { reason, result };
47826
48757
  }
47827
48758
  },
47828
48759
  analyzeInvalidation(reason) {
47829
- return analyzeInvalidation(reason, cachedGraph);
48760
+ return analyzeInvalidation(reason, preferredCachedGraph());
47830
48761
  },
47831
48762
  getDirtyFieldFamilies() {
47832
48763
  return Array.from(dirtyFieldFamilies);
@@ -47854,14 +48785,11 @@ function createLayoutEngine(options = {}) {
47854
48785
  telemetryBus
47855
48786
  );
47856
48787
  if (previousFidelity !== provider.fidelity) {
47857
- cachedKey = null;
47858
- cachedGraph = null;
47859
- cachedFormatting = null;
47860
- cachedMapper = null;
48788
+ clearAllSlots();
47861
48789
  }
47862
48790
  emit({
47863
48791
  kind: "measurement_backend_ready",
47864
- revision: cachedGraph?.revision ?? 0,
48792
+ revision: preferredCachedGraph()?.revision ?? 0,
47865
48793
  fidelity: provider.fidelity
47866
48794
  });
47867
48795
  },
@@ -47876,30 +48804,33 @@ function createLayoutEngine(options = {}) {
47876
48804
  */
47877
48805
  invalidateMeasurementCache() {
47878
48806
  measurementProvider.invalidateCache();
47879
- cachedKey = null;
47880
- cachedGraph = null;
47881
- cachedFormatting = null;
47882
- cachedMapper = null;
48807
+ clearAllSlots();
47883
48808
  },
47884
48809
  getMeasurementCacheStats() {
47885
48810
  if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
47886
48811
  return measurementProvider.measurementCacheStats();
47887
48812
  },
47888
48813
  /**
47889
- * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
47890
- * Populates both `cachedGraph` and `cachedKey` (keyed on the provided
47891
- * document's identity-equal slots) so the next getPageGraph query
47892
- * returns the seeded graph directly. Any subsequent mutation
47893
- * invalidates normally through the existing path.
48814
+ * L7 Phase 2.5 — seed the full-slot cached graph from a prerender
48815
+ * envelope. Populates the full slot (graph + key, with formatting and
48816
+ * mapper computed lazily on first read) so the next viewport-
48817
+ * independent `getPageGraph` query returns the seeded graph directly.
48818
+ * Any subsequent mutation invalidates normally through the existing
48819
+ * path; sibling eviction in `commitSlot` clears `cachedWindowed` if
48820
+ * it carried a different document tuple.
47894
48821
  */
47895
48822
  seedCachedGraph(graph, document2) {
47896
- cachedGraph = graph;
47897
- cachedKey = {
47898
- content: document2.content,
47899
- styles: document2.styles,
47900
- subParts: document2.subParts,
47901
- viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
47902
- };
48823
+ commitSlot({
48824
+ key: {
48825
+ content: document2.content,
48826
+ styles: document2.styles,
48827
+ subParts: document2.subParts,
48828
+ viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
48829
+ },
48830
+ graph,
48831
+ formatting: null,
48832
+ mapper: null
48833
+ });
47903
48834
  previousPageCount = graph.contentPageCount;
47904
48835
  }
47905
48836
  };
@@ -48167,7 +49098,7 @@ function attachScopeCardModel(input) {
48167
49098
  primaryAnchorRect: null,
48168
49099
  ...anchor ? { anchor } : {},
48169
49100
  ...issue ? { issue } : {},
48170
- suggestionGroupIds: suggestionGroups.map((group) => group.groupId),
49101
+ suggestionGroupIds: suggestionGroups.map((group2) => group2.groupId),
48171
49102
  suggestionGroups,
48172
49103
  reviewActionCount: reviewActions.length,
48173
49104
  reviewActions,
@@ -48178,7 +49109,7 @@ function attachScopeCardModel(input) {
48178
49109
  }
48179
49110
  function resolveSuggestionGroupsForIssue(issueId, suggestions) {
48180
49111
  if (!issueId || !suggestions?.groups) return [];
48181
- return suggestions.groups.filter((group) => group.issueId === issueId);
49112
+ return suggestions.groups.filter((group2) => group2.issueId === issueId);
48182
49113
  }
48183
49114
  function resolveReviewActionsForIssue(issueId, metadata, externalResolutions) {
48184
49115
  if (!issueId || !metadata || metadata.length === 0) return [];
@@ -48253,39 +49184,8 @@ function insertScopeMarkers(document2, params) {
48253
49184
  to: normalizedTo
48254
49185
  };
48255
49186
  }
48256
- const paraSlots = [];
48257
- let cursor = 0;
48258
- let fromBlockIndex = -1;
48259
- let fromBlockKind = null;
48260
- let toBlockIndex = -1;
48261
- let toBlockKind = null;
48262
- let storyLength = 0;
48263
- for (let i = 0; i < root.children.length; i += 1) {
48264
- const block = root.children[i];
48265
- const blockFrom = cursor;
48266
- let blockLength;
48267
- if (block.type === "paragraph") {
48268
- blockLength = block.children.reduce(
48269
- (total, child) => total + inlineLength4(child),
48270
- 0
48271
- );
48272
- paraSlots.push({ index: i, from: blockFrom, to: blockFrom + blockLength });
48273
- } else {
48274
- blockLength = 1;
48275
- }
48276
- const blockTo = blockFrom + blockLength;
48277
- if (fromBlockIndex === -1 && normalizedFrom >= blockFrom && normalizedFrom <= blockTo) {
48278
- fromBlockIndex = i;
48279
- fromBlockKind = block.type;
48280
- }
48281
- if (toBlockIndex === -1 && normalizedTo >= blockFrom && normalizedTo <= blockTo) {
48282
- toBlockIndex = i;
48283
- toBlockKind = block.type;
48284
- }
48285
- cursor = blockTo;
48286
- if (i < root.children.length - 1) cursor += 1;
48287
- storyLength = cursor;
48288
- }
49187
+ const layout = computeScopeStoryLayout(document2);
49188
+ const storyLength = layout.storyLength;
48289
49189
  if (normalizedFrom < 0 || normalizedTo > storyLength) {
48290
49190
  return {
48291
49191
  status: "range-out-of-bounds",
@@ -48295,73 +49195,71 @@ function insertScopeMarkers(document2, params) {
48295
49195
  storyLength
48296
49196
  };
48297
49197
  }
48298
- if (fromBlockIndex === -1 || toBlockIndex === -1) {
48299
- return {
48300
- status: "range-out-of-bounds",
48301
- scopeId,
48302
- from: normalizedFrom,
48303
- to: normalizedTo,
48304
- storyLength
48305
- };
48306
- }
48307
- if (fromBlockKind !== "paragraph" || toBlockKind !== "paragraph") {
48308
- const nonParaIndex = fromBlockKind !== "paragraph" ? fromBlockIndex : toBlockIndex;
48309
- const nonParaKind = fromBlockKind !== "paragraph" ? fromBlockKind : toBlockKind;
49198
+ const startSlot = findParagraphSlotAtPosition(
49199
+ layout,
49200
+ normalizedFrom,
49201
+ "start"
49202
+ );
49203
+ const endSlot = findParagraphSlotAtPosition(layout, normalizedTo, "end");
49204
+ if (!startSlot || !endSlot) {
49205
+ const offenderPosition = !startSlot ? normalizedFrom : normalizedTo;
49206
+ const owner = findOwningBlockSlotAtPosition(layout, offenderPosition);
48310
49207
  return {
48311
49208
  status: "non-paragraph-target",
48312
49209
  scopeId,
48313
49210
  from: normalizedFrom,
48314
49211
  to: normalizedTo,
48315
- blockIndex: nonParaIndex,
48316
- blockKind: nonParaKind ?? "unknown"
49212
+ blockIndex: owner?.rootBlockIndex ?? -1,
49213
+ blockKind: owner?.kind ?? "unknown"
48317
49214
  };
48318
49215
  }
48319
- const startSlot = paraSlots.find((s) => s.index === fromBlockIndex);
48320
- const endSlot = paraSlots.find((s) => s.index === toBlockIndex);
48321
- if (startSlot.index === endSlot.index) {
49216
+ if (sameScopeParagraphPath(startSlot.path, endSlot.path)) {
48322
49217
  const newChildren = injectMarkersIntoInlineList(
48323
- root.children[startSlot.index].children,
49218
+ startSlot.paragraph.children,
48324
49219
  scopeId,
48325
49220
  normalizedFrom - startSlot.from,
48326
49221
  normalizedTo - startSlot.from,
48327
49222
  "both"
48328
49223
  );
48329
- const children2 = root.children.map(
48330
- (block, i) => i === startSlot.index ? { ...block, children: newChildren } : block
49224
+ const nextRoot = replaceParagraphChildrenAtPath(
49225
+ root,
49226
+ startSlot.path,
49227
+ newChildren
48331
49228
  );
48332
49229
  return {
48333
49230
  status: "planted",
48334
- document: { ...document2, content: { ...root, children: children2 } },
49231
+ document: { ...document2, content: nextRoot },
48335
49232
  scopeId,
48336
49233
  plantedRange: { from: normalizedFrom, to: normalizedTo }
48337
49234
  };
48338
49235
  }
48339
- const children = root.children.map((block, i) => {
48340
- if (i === startSlot.index) {
48341
- const newChildren = injectMarkersIntoInlineList(
48342
- block.children,
48343
- scopeId,
48344
- normalizedFrom - startSlot.from,
48345
- Number.POSITIVE_INFINITY,
48346
- "start-only"
48347
- );
48348
- return { ...block, children: newChildren };
48349
- }
48350
- if (i === endSlot.index) {
48351
- const newChildren = injectMarkersIntoInlineList(
48352
- block.children,
48353
- scopeId,
48354
- Number.NEGATIVE_INFINITY,
48355
- normalizedTo - endSlot.from,
48356
- "end-only"
48357
- );
48358
- return { ...block, children: newChildren };
48359
- }
48360
- return block;
48361
- });
49236
+ const startChildren = injectMarkersIntoInlineList(
49237
+ startSlot.paragraph.children,
49238
+ scopeId,
49239
+ normalizedFrom - startSlot.from,
49240
+ Number.POSITIVE_INFINITY,
49241
+ "start-only"
49242
+ );
49243
+ const endChildren = injectMarkersIntoInlineList(
49244
+ endSlot.paragraph.children,
49245
+ scopeId,
49246
+ Number.NEGATIVE_INFINITY,
49247
+ normalizedTo - endSlot.from,
49248
+ "end-only"
49249
+ );
49250
+ const rootWithStart = replaceParagraphChildrenAtPath(
49251
+ root,
49252
+ startSlot.path,
49253
+ startChildren
49254
+ );
49255
+ const rootWithBoth = replaceParagraphChildrenAtPath(
49256
+ rootWithStart,
49257
+ endSlot.path,
49258
+ endChildren
49259
+ );
48362
49260
  return {
48363
49261
  status: "planted",
48364
- document: { ...document2, content: { ...root, children } },
49262
+ document: { ...document2, content: rootWithBoth },
48365
49263
  scopeId,
48366
49264
  plantedRange: { from: normalizedFrom, to: normalizedTo }
48367
49265
  };
@@ -48369,43 +49267,15 @@ function insertScopeMarkers(document2, params) {
48369
49267
  function removeScopeMarkers(document2, scopeId) {
48370
49268
  const root = document2.content;
48371
49269
  if (!root || root.type !== "doc") return document2;
48372
- let mutated = false;
48373
- const children = root.children.map((block) => {
48374
- if (block.type !== "paragraph") return block;
48375
- const kept = block.children.filter((child) => {
48376
- if ((child.type === "scope_marker_start" || child.type === "scope_marker_end") && child.scopeId === scopeId) {
48377
- mutated = true;
48378
- return false;
48379
- }
48380
- return true;
48381
- });
48382
- if (kept.length === block.children.length) return block;
48383
- return { ...block, children: kept };
48384
- });
48385
- if (!mutated) return document2;
49270
+ const result = removeScopeMarkersFromBlockList(root.children, scopeId);
49271
+ if (!result.mutated) return document2;
48386
49272
  return {
48387
49273
  ...document2,
48388
- content: { ...root, children }
49274
+ content: { ...root, children: result.blocks }
48389
49275
  };
48390
49276
  }
48391
49277
  function inlineLength4(node) {
48392
- switch (node.type) {
48393
- case "text":
48394
- return Array.from(node.text).length;
48395
- case "hyperlink":
48396
- case "field":
48397
- return node.children.reduce(
48398
- (total, child) => total + inlineLength4(child),
48399
- 0
48400
- );
48401
- case "bookmark_start":
48402
- case "bookmark_end":
48403
- case "scope_marker_start":
48404
- case "scope_marker_end":
48405
- return 0;
48406
- default:
48407
- return 1;
48408
- }
49278
+ return inlineLengthForScopeCoordinates(node);
48409
49279
  }
48410
49280
  function injectMarkersIntoInlineList(inlines, scopeId, startOffset, endOffset, mode) {
48411
49281
  const start = { type: "scope_marker_start", scopeId };
@@ -48673,11 +49543,11 @@ function projectEntries(kind, input) {
48673
49543
  }
48674
49544
  function projectIssueEntries(input) {
48675
49545
  const suggestionGroupsByIssueId = /* @__PURE__ */ new Map();
48676
- for (const group of input.suggestions?.groups ?? []) {
48677
- if (!group.issueId) continue;
48678
- const ids = suggestionGroupsByIssueId.get(group.issueId) ?? [];
48679
- ids.push(group.groupId);
48680
- suggestionGroupsByIssueId.set(group.issueId, ids);
49546
+ for (const group2 of input.suggestions?.groups ?? []) {
49547
+ if (!group2.issueId) continue;
49548
+ const ids = suggestionGroupsByIssueId.get(group2.issueId) ?? [];
49549
+ ids.push(group2.groupId);
49550
+ suggestionGroupsByIssueId.set(group2.issueId, ids);
48681
49551
  }
48682
49552
  return (input.workflowMarkup?.metadata ?? []).filter((entry) => entry.metadataId === ISSUE_METADATA_ID).map((entry) => {
48683
49553
  const issueValue = isRecord3(entry.value) ? entry.value : void 0;
@@ -51038,13 +51908,13 @@ function createLayoutFacet(input) {
51038
51908
  createSelectionSnapshot(0, 0)
51039
51909
  );
51040
51910
  let cursor = 0;
51041
- for (const group of groups) {
51042
- for (let i = 0; i < group.count; i += 1) {
51911
+ for (const group2 of groups) {
51912
+ for (let i = 0; i < group2.count; i += 1) {
51043
51913
  const blockSnapshot = surface.blocks[cursor + i];
51044
51914
  if (!blockSnapshot) continue;
51045
51915
  blocks.push({
51046
51916
  blockId: blockSnapshot.blockId,
51047
- fragmentId: `endnote-${group.noteId}-${i}`,
51917
+ fragmentId: `endnote-${group2.noteId}-${i}`,
51048
51918
  pageIndex: -1,
51049
51919
  regionKind: "endnote-area",
51050
51920
  runtimeFromOffset: blockSnapshot.from,
@@ -51053,7 +51923,7 @@ function createLayoutFacet(input) {
51053
51923
  blockSnapshot
51054
51924
  });
51055
51925
  }
51056
- cursor += group.count;
51926
+ cursor += group2.count;
51057
51927
  }
51058
51928
  }
51059
51929
  }
@@ -52448,9 +53318,9 @@ function resolveFootnoteAreaRegionBlocks(node, document2) {
52448
53318
  );
52449
53319
  const blocks = [];
52450
53320
  let cursor = 0;
52451
- for (const group of groups) {
52452
- const fragmentBase = group.allocation.fragmentId ?? `note-${node.pageIndex}-${group.allocation.noteId}`;
52453
- for (let i = 0; i < group.count; i += 1) {
53321
+ for (const group2 of groups) {
53322
+ const fragmentBase = group2.allocation.fragmentId ?? `note-${node.pageIndex}-${group2.allocation.noteId}`;
53323
+ for (let i = 0; i < group2.count; i += 1) {
52454
53324
  const blockSnapshot = surface.blocks[cursor + i];
52455
53325
  if (!blockSnapshot) continue;
52456
53326
  blocks.push({
@@ -52460,11 +53330,11 @@ function resolveFootnoteAreaRegionBlocks(node, document2) {
52460
53330
  regionKind: "footnote-area",
52461
53331
  runtimeFromOffset: blockSnapshot.from,
52462
53332
  runtimeToOffset: blockSnapshot.to,
52463
- heightTwips: group.allocation.reservedHeightTwips,
53333
+ heightTwips: group2.allocation.reservedHeightTwips,
52464
53334
  blockSnapshot
52465
53335
  });
52466
53336
  }
52467
- cursor += group.count;
53337
+ cursor += group2.count;
52468
53338
  }
52469
53339
  return Object.freeze(blocks);
52470
53340
  }
@@ -55251,6 +56121,14 @@ function createDocumentRuntime(options) {
55251
56121
  let effectiveMarkupModeProvider;
55252
56122
  const perfCounters = new PerfCounters();
55253
56123
  const hotEditTraces = new HotEditTraceRecorder();
56124
+ const scopeSurfaceTelemetry = {
56125
+ onPrecomputedSurfaceHit: () => {
56126
+ perfCounters.increment("surface.scope.precomputedSurfaceHit");
56127
+ },
56128
+ onScopeSurfaceBuild: () => {
56129
+ perfCounters.increment("surface.scope.builds");
56130
+ }
56131
+ };
55254
56132
  let cachedHotEditPolicy = null;
55255
56133
  function getHotEditPolicy() {
55256
56134
  if (cachedHotEditPolicy?.document === state.document) {
@@ -55699,7 +56577,7 @@ function createDocumentRuntime(options) {
55699
56577
  viewportRangesKey: rangesKey,
55700
56578
  snapshot: surface
55701
56579
  };
55702
- const fullSurface = fullSurfaceForCache ?? (surface.viewportBlockRanges === null ? surface : void 0);
56580
+ const fullSurface = fullSurfaceForCache?.viewportBlockRanges === null ? fullSurfaceForCache : surface.viewportBlockRanges === null ? surface : void 0;
55703
56581
  if (fullSurface) {
55704
56582
  cachedFullSurface = {
55705
56583
  content: state.document.content,
@@ -57262,6 +58140,7 @@ function createDocumentRuntime(options) {
57262
58140
  documentMode: workflowCoordinator.getEffectiveDocumentMode(commandSelection),
57263
58141
  defaultAuthorId: defaultAuthorId ?? void 0,
57264
58142
  renderSnapshot: cachedRenderSnapshot,
58143
+ surfaceTelemetry: scopeSurfaceTelemetry,
57265
58144
  activeStoryKey: canonicalEditableTargetStoryKey(activeStory),
57266
58145
  editableTargetCache: editableTargetBlockCache,
57267
58146
  ...resolvedFragmentTextTarget ? { textTarget: resolvedFragmentTextTarget } : {}
@@ -57354,7 +58233,20 @@ function createDocumentRuntime(options) {
57354
58233
  selection: prepared.selection
57355
58234
  };
57356
58235
  resolvedReplayTextTarget = prepared.textTarget;
57357
- } else if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
58236
+ } else {
58237
+ const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58238
+ command,
58239
+ document: replayState.document,
58240
+ selection: replayState.selection,
58241
+ surface: replaySnapshot.surface?.blocks ?? [],
58242
+ storyTarget: replayStory,
58243
+ timestamp: context.timestamp
58244
+ });
58245
+ if (selectedListItemDeleteCommand) {
58246
+ executableCommand = selectedListItemDeleteCommand;
58247
+ }
58248
+ }
58249
+ if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
57358
58250
  const prepared = prepareModeledTargetCommandForExecution(
57359
58251
  command,
57360
58252
  replayState.document,
@@ -57498,7 +58390,20 @@ function createDocumentRuntime(options) {
57498
58390
  selection: prepared.selection
57499
58391
  };
57500
58392
  resolvedReplayTextTarget = prepared.textTarget;
57501
- } else if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
58393
+ } else {
58394
+ const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58395
+ command,
58396
+ document: stateForCommand.document,
58397
+ selection: stateForCommand.selection,
58398
+ surface: snapshotForCommand.surface?.blocks ?? [],
58399
+ storyTarget: replayStory,
58400
+ timestamp: context.timestamp
58401
+ });
58402
+ if (selectedListItemDeleteCommand) {
58403
+ executableCommand = selectedListItemDeleteCommand;
58404
+ }
58405
+ }
58406
+ if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
57502
58407
  const prepared = prepareModeledTargetCommandForExecution(
57503
58408
  command,
57504
58409
  stateForCommand.document,
@@ -57631,9 +58536,9 @@ function createDocumentRuntime(options) {
57631
58536
  emitError(toRuntimeError(error));
57632
58537
  }
57633
58538
  },
57634
- applyFormattingOperation(operation) {
58539
+ applyFormattingOperation(operation2) {
57635
58540
  try {
57636
- const commandName = getFormattingOperationCommandName(operation);
58541
+ const commandName = getFormattingOperationCommandName(operation2);
57637
58542
  const snapshot = cachedRenderSnapshot;
57638
58543
  if (!snapshot.isReady || snapshot.readOnly || snapshot.fatalError) {
57639
58544
  return;
@@ -57661,11 +58566,11 @@ function createDocumentRuntime(options) {
57661
58566
  };
57662
58567
  const suggesting = workflowCoordinator.getEffectiveDocumentMode(state.selection) === "suggesting";
57663
58568
  const timestamp = clock();
57664
- if (snapshot.selection.isCollapsed && operation.type !== "set-alignment" && operation.type !== "indent" && operation.type !== "outdent") {
58569
+ if (snapshot.selection.isCollapsed && operation2.type !== "set-alignment" && operation2.type !== "indent" && operation2.type !== "outdent") {
57665
58570
  const storyKey2 = storyTargetKey(activeStory);
57666
58571
  const nextMarks = updatePendingInsertionMarks(
57667
58572
  pendingInsertionMarksByStory.get(storyKey2)?.marks ?? marksFromFormattingState(getFormattingStateFromRenderSnapshot(snapshot)),
57668
- operation
58573
+ operation2
57669
58574
  );
57670
58575
  if (nextMarks && nextMarks.length > 0) {
57671
58576
  pendingInsertionMarksByStory.set(storyKey2, {
@@ -57680,7 +58585,7 @@ function createDocumentRuntime(options) {
57680
58585
  return;
57681
58586
  }
57682
58587
  if (suggesting) {
57683
- if (operation.type === "set-alignment" || operation.type === "indent" || operation.type === "outdent") {
58588
+ if (operation2.type === "set-alignment" || operation2.type === "indent" || operation2.type === "outdent") {
57684
58589
  const paragraphContext = resolveActiveParagraphContext(localSnapshot);
57685
58590
  if (!paragraphContext) {
57686
58591
  return;
@@ -57689,7 +58594,7 @@ function createDocumentRuntime(options) {
57689
58594
  const result3 = applyFormattingOperationToDocument(
57690
58595
  localDocument,
57691
58596
  localSnapshot,
57692
- operation
58597
+ operation2
57693
58598
  );
57694
58599
  if (!result3.changed) {
57695
58600
  return;
@@ -57736,7 +58641,7 @@ function createDocumentRuntime(options) {
57736
58641
  const result2 = applyFormattingOperationToDocument(
57737
58642
  localDocument,
57738
58643
  localSnapshot,
57739
- operation
58644
+ operation2
57740
58645
  );
57741
58646
  if (!result2.changed) {
57742
58647
  return;
@@ -57771,7 +58676,7 @@ function createDocumentRuntime(options) {
57771
58676
  const result = applyFormattingOperationToDocument(
57772
58677
  localDocument,
57773
58678
  localSnapshot,
57774
- operation
58679
+ operation2
57775
58680
  );
57776
58681
  if (!result.changed) {
57777
58682
  return;
@@ -57919,6 +58824,50 @@ function createDocumentRuntime(options) {
57919
58824
  } catch (error) {
57920
58825
  emitError(toRuntimeError(error));
57921
58826
  }
58827
+ } else if (step.kind === "fragment-replace-tracked" && step.range && step.fragment && Array.isArray(step.fragment.blocks)) {
58828
+ const editableTarget = resolveEditableTargetHint(step.editableTargetHint);
58829
+ if (editableTarget === null) {
58830
+ emit({
58831
+ type: "command_blocked",
58832
+ documentId: state.documentId,
58833
+ command: "applyScopeReplacement",
58834
+ reasons: [{
58835
+ code: "unsupported_surface",
58836
+ message: "Scope replacement editable target no longer resolves."
58837
+ }]
58838
+ });
58839
+ continue;
58840
+ }
58841
+ const dispatchRange = mapSemanticStepRangeToEditableTarget(
58842
+ step.range,
58843
+ step.editableTargetHint,
58844
+ editableTarget?.range
58845
+ );
58846
+ const anchor = {
58847
+ kind: "range",
58848
+ from: dispatchRange.from,
58849
+ to: dispatchRange.to,
58850
+ assoc: { start: -1, end: 1 }
58851
+ };
58852
+ const timestamp = clock();
58853
+ try {
58854
+ applyTextCommandInActiveStory(
58855
+ {
58856
+ type: "fragment.insert-tracked",
58857
+ fragment: step.fragment,
58858
+ ...editableTarget?.target ? { editableTarget: editableTarget.target } : {},
58859
+ origin: createOrigin("api", timestamp)
58860
+ },
58861
+ {
58862
+ selection: createSelectionFromPublicAnchor(anchor),
58863
+ blockedCommandName: "applyScopeReplacement",
58864
+ documentModeOverride: "suggesting",
58865
+ skipWorkflowGuard: true
58866
+ }
58867
+ );
58868
+ } catch (error) {
58869
+ emitError(toRuntimeError(error));
58870
+ }
57922
58871
  } else if (step.kind === "text-insert-tracked" && step.range && typeof step.text === "string") {
57923
58872
  const editableTarget = resolveEditableTargetHint(step.editableTargetHint);
57924
58873
  if (editableTarget === null) {
@@ -58018,7 +58967,7 @@ function createDocumentRuntime(options) {
58018
58967
  if (step.kind !== "formatting-apply" || !step.range || !step.formattingAction) {
58019
58968
  continue;
58020
58969
  }
58021
- const operation = step.formattingAction.kind === "clear-mark" ? {
58970
+ const operation2 = step.formattingAction.kind === "clear-mark" ? {
58022
58971
  type: "clear-mark",
58023
58972
  mark: step.formattingAction.mark,
58024
58973
  ...step.formattingAction.expandToFullHighlight === true ? { expandToFullHighlight: true } : {}
@@ -58029,7 +58978,7 @@ function createDocumentRuntime(options) {
58029
58978
  const result = applyTextMarkOperationToDocumentRange(
58030
58979
  state.document,
58031
58980
  step.range,
58032
- operation
58981
+ operation2
58033
58982
  );
58034
58983
  if (!result.changed) {
58035
58984
  continue;
@@ -59534,7 +60483,8 @@ function createDocumentRuntime(options) {
59534
60483
  applyViewportRanges(getSelectionCorridorViewportRanges(cachedRenderSnapshot.surface));
59535
60484
  }
59536
60485
  const tValidation0 = performance.now();
59537
- const patchedFullLocalTextSurface = useLocalTextCommitSnapshot ? tryPatchLocalTextSurface(cachedRenderSnapshot.surface, transaction.mapping) : null;
60486
+ const patchSourceSurface = getReusableCachedFullSurface(previous.document, activeStory) ?? cachedRenderSnapshot.surface;
60487
+ const patchedFullLocalTextSurface = useLocalTextCommitSnapshot ? tryPatchLocalTextSurface(patchSourceSurface, transaction.mapping) : null;
59538
60488
  const patchedLocalTextSurface = patchedFullLocalTextSurface ? createLocalTextCorridorSurfaceFromFullSurface(patchedFullLocalTextSurface) : null;
59539
60489
  if (patchedLocalTextSurface) {
59540
60490
  cachePatchedLocalTextSurface(
@@ -59878,11 +60828,14 @@ function createDocumentRuntime(options) {
59878
60828
  }
59879
60829
  const listBoundaryDeleteUsesStoryText = targetResolution?.kind === "accepted" && editableTarget?.listAddress?.operationScope === "list-text" && isTopLevelMainStoryBlockPath(editableTarget.blockPath) && selection.isCollapsed && (commandForDispatch.type === "text.delete-backward" && selection.anchor === targetResolution.range.from || commandForDispatch.type === "text.delete-forward" && selection.anchor === targetResolution.range.to);
59880
60830
  const textTarget = targetResolution?.kind === "accepted" && !listBoundaryDeleteUsesStoryText ? targetResolution.textTarget : legacyTextTarget;
60831
+ const fullScopeSurface = cachedFullSurface?.snapshot;
59881
60832
  const context = {
59882
60833
  timestamp,
59883
60834
  documentMode: textOptions.documentModeOverride ?? workflowCoordinator.getEffectiveDocumentMode(selection),
59884
60835
  defaultAuthorId: defaultAuthorId ?? void 0,
59885
60836
  renderSnapshot: cachedRenderSnapshot,
60837
+ surfaceTelemetry: scopeSurfaceTelemetry,
60838
+ ...fullScopeSurface ? { precomputedSurface: fullScopeSurface } : {},
59886
60839
  activeStoryKey: canonicalEditableTargetStoryKey(activeStory),
59887
60840
  editableTargetCache: editableTargetBlockCache,
59888
60841
  activeStorySize: cachedRenderSnapshot.surface?.storySize,
@@ -59897,6 +60850,38 @@ function createDocumentRuntime(options) {
59897
60850
  const preSelection = selection;
59898
60851
  const preActiveStory = activeStory;
59899
60852
  const priorDocument = state.document;
60853
+ const selectedListItemDelete = createSelectedListItemDeleteReplacement({
60854
+ command: commandForDispatch,
60855
+ document: state.document,
60856
+ editableTarget,
60857
+ selection,
60858
+ targetResolution,
60859
+ timestamp
60860
+ });
60861
+ if (selectedListItemDelete && activeStory.kind === "main" && context.documentMode !== "suggesting") {
60862
+ const replacementCommand = {
60863
+ type: "document.replace",
60864
+ document: selectedListItemDelete.document,
60865
+ selection: selectedListItemDelete.selection,
60866
+ mapping: selectedListItemDelete.mapping,
60867
+ protectionSelection: selection,
60868
+ origin: commandForDispatch.origin
60869
+ };
60870
+ const transaction = executeEditorCommand(baseState, replacementCommand, context);
60871
+ commit(transaction);
60872
+ options.onCommandApplied?.(commandForDispatch, transaction, context, {
60873
+ preSelection,
60874
+ activeStory: preActiveStory,
60875
+ priorDocument
60876
+ });
60877
+ return completeDispatch(classifyAck({
60878
+ command: commandForDispatch,
60879
+ opId,
60880
+ priorState: baseState,
60881
+ transaction,
60882
+ newRevisionToken: state.revisionToken
60883
+ }));
60884
+ }
59900
60885
  if (activeStory.kind === "main") {
59901
60886
  const mainTransaction = executeEditorCommand(baseState, commandForDispatch, context);
59902
60887
  commit(mainTransaction);
@@ -60208,6 +61193,7 @@ function createDocumentRuntime(options) {
60208
61193
  documentMode: workflowCoordinator.getEffectiveDocumentMode(state.selection),
60209
61194
  defaultAuthorId: defaultAuthorId ?? void 0,
60210
61195
  renderSnapshot: cachedRenderSnapshot,
61196
+ surfaceTelemetry: scopeSurfaceTelemetry,
60211
61197
  activeStoryKey: canonicalEditableTargetStoryKey(activeStory),
60212
61198
  editableTargetCache: editableTargetBlockCache
60213
61199
  };
@@ -60612,22 +61598,22 @@ function marksFromFormattingState(formatting) {
60612
61598
  }
60613
61599
  return marks;
60614
61600
  }
60615
- function updatePendingInsertionMarks(currentMarks, operation) {
60616
- switch (operation.type) {
61601
+ function updatePendingInsertionMarks(currentMarks, operation2) {
61602
+ switch (operation2.type) {
60617
61603
  case "toggle":
60618
- return togglePendingMark(currentMarks, operation.mark);
61604
+ return togglePendingMark(currentMarks, operation2.mark);
60619
61605
  case "set-font-family":
60620
- return setPendingMarkValue(currentMarks, "fontFamily", operation.fontFamily?.trim() || null);
61606
+ return setPendingMarkValue(currentMarks, "fontFamily", operation2.fontFamily?.trim() || null);
60621
61607
  case "set-font-size":
60622
61608
  return setPendingMarkValue(
60623
61609
  currentMarks,
60624
61610
  "fontSize",
60625
- typeof operation.size === "number" && Number.isFinite(operation.size) ? Math.round(operation.size * 2) : null
61611
+ typeof operation2.size === "number" && Number.isFinite(operation2.size) ? Math.round(operation2.size * 2) : null
60626
61612
  );
60627
61613
  case "set-text-color":
60628
- return setPendingMarkValue(currentMarks, "textColor", normalizePendingColor(operation.color));
61614
+ return setPendingMarkValue(currentMarks, "textColor", normalizePendingColor(operation2.color));
60629
61615
  case "set-highlight-color":
60630
- return setPendingMarkValue(currentMarks, "backgroundColor", normalizePendingColor(operation.color));
61616
+ return setPendingMarkValue(currentMarks, "backgroundColor", normalizePendingColor(operation2.color));
60631
61617
  }
60632
61618
  }
60633
61619
  function togglePendingMark(marks, mark) {
@@ -61090,7 +62076,8 @@ function getStoryPlainText(document2, storyTarget, cache) {
61090
62076
  const plainText = createEditorSurfaceSnapshot(
61091
62077
  document2,
61092
62078
  createSelectionSnapshot(0, 0),
61093
- storyTarget
62079
+ storyTarget,
62080
+ { editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX }
61094
62081
  ).plainText;
61095
62082
  cache.set(key, plainText);
61096
62083
  return plainText;
@@ -62775,6 +63762,172 @@ function stripStoryTarget(selection) {
62775
63762
  function isTopLevelMainStoryBlockPath(blockPath) {
62776
63763
  return typeof blockPath === "string" && /^main\/block\[\d+\]$/u.test(blockPath);
62777
63764
  }
63765
+ function createSelectedListItemDeleteReplacement(input) {
63766
+ const { command, document: document2, editableTarget, selection, targetResolution, timestamp } = input;
63767
+ if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
63768
+ return null;
63769
+ }
63770
+ if (selection.isCollapsed || editableTarget?.listAddress?.operationScope !== "list-text" || targetResolution?.kind !== "accepted") {
63771
+ return null;
63772
+ }
63773
+ const selectionFrom = Math.min(selection.anchor, selection.head);
63774
+ const selectionTo = Math.max(selection.anchor, selection.head);
63775
+ if (selectionFrom !== targetResolution.range.from || selectionTo !== targetResolution.range.to) {
63776
+ return null;
63777
+ }
63778
+ const root = document2.content;
63779
+ const replacement = removeNumberedParagraphAtMainStoryPath(root.children, editableTarget.blockPath);
63780
+ if (!replacement) {
63781
+ return null;
63782
+ }
63783
+ const nextDocument = {
63784
+ ...document2,
63785
+ updatedAt: timestamp,
63786
+ content: {
63787
+ ...root,
63788
+ children: replacement.blocks
63789
+ }
63790
+ };
63791
+ const nextStorySize = parseTextStory(nextDocument.content).size;
63792
+ const nextAnchor = Math.min(selectionFrom, nextStorySize);
63793
+ return {
63794
+ document: nextDocument,
63795
+ selection: createSelectionSnapshot(nextAnchor, nextAnchor),
63796
+ mapping: {
63797
+ steps: [{
63798
+ from: selectionFrom,
63799
+ to: selectionTo,
63800
+ insertSize: 0
63801
+ }],
63802
+ metadata: {
63803
+ invalidatesStructures: true
63804
+ }
63805
+ }
63806
+ };
63807
+ }
63808
+ function removeNumberedParagraphAtMainStoryPath(blocks, blockPath) {
63809
+ const tokens = parseMainStoryBlockPathTokens(blockPath);
63810
+ if (!tokens) return null;
63811
+ return removeNumberedParagraphFromBlocks(blocks, tokens);
63812
+ }
63813
+ function removeNumberedParagraphFromBlocks(blocks, tokens) {
63814
+ const [token, ...rest] = tokens;
63815
+ if (!token || token.kind !== "block") return null;
63816
+ const block = blocks[token.index];
63817
+ if (!block) return null;
63818
+ if (rest.length === 0) {
63819
+ if (block.type !== "paragraph" || !block.numbering) {
63820
+ return null;
63821
+ }
63822
+ if (blocks.length === 1) {
63823
+ return { blocks: [{ type: "paragraph", children: [] }] };
63824
+ }
63825
+ return {
63826
+ blocks: [
63827
+ ...blocks.slice(0, token.index),
63828
+ ...blocks.slice(token.index + 1)
63829
+ ]
63830
+ };
63831
+ }
63832
+ const next = rest[0];
63833
+ if (block.type === "table" && next?.kind === "row") {
63834
+ const updatedTable = removeNumberedParagraphFromTable(block, rest);
63835
+ if (!updatedTable) return null;
63836
+ return {
63837
+ blocks: [
63838
+ ...blocks.slice(0, token.index),
63839
+ updatedTable,
63840
+ ...blocks.slice(token.index + 1)
63841
+ ]
63842
+ };
63843
+ }
63844
+ if ((block.type === "sdt" || block.type === "custom_xml") && next?.kind === "block") {
63845
+ const updatedChildren = removeNumberedParagraphFromBlocks(block.children, rest);
63846
+ if (!updatedChildren) return null;
63847
+ return {
63848
+ blocks: [
63849
+ ...blocks.slice(0, token.index),
63850
+ { ...block, children: updatedChildren.blocks },
63851
+ ...blocks.slice(token.index + 1)
63852
+ ]
63853
+ };
63854
+ }
63855
+ return null;
63856
+ }
63857
+ function removeNumberedParagraphFromTable(table, tokens) {
63858
+ const [rowToken, cellToken, ...childTokens] = tokens;
63859
+ if (rowToken?.kind !== "row" || cellToken?.kind !== "cell" || childTokens[0]?.kind !== "block") {
63860
+ return null;
63861
+ }
63862
+ const row2 = table.rows[rowToken.index];
63863
+ const cell = row2?.cells[cellToken.index];
63864
+ if (!row2 || !cell) return null;
63865
+ const updatedChildren = removeNumberedParagraphFromBlocks(cell.children, childTokens);
63866
+ if (!updatedChildren) return null;
63867
+ const nextCells = [
63868
+ ...row2.cells.slice(0, cellToken.index),
63869
+ { ...cell, children: updatedChildren.blocks },
63870
+ ...row2.cells.slice(cellToken.index + 1)
63871
+ ];
63872
+ const nextRows = [
63873
+ ...table.rows.slice(0, rowToken.index),
63874
+ { ...row2, cells: nextCells },
63875
+ ...table.rows.slice(rowToken.index + 1)
63876
+ ];
63877
+ return { ...table, rows: nextRows };
63878
+ }
63879
+ function createSelectedListItemDeleteReplayCommand(input) {
63880
+ const { command, document: document2, selection, surface, storyTarget, timestamp } = input;
63881
+ if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
63882
+ return null;
63883
+ }
63884
+ const editableTarget = command.editableTarget;
63885
+ if (!editableTarget) {
63886
+ return null;
63887
+ }
63888
+ const targetResolution = resolveEditableTextTarget({
63889
+ document: document2,
63890
+ selection,
63891
+ surface,
63892
+ target: editableTarget,
63893
+ activeStoryKey: canonicalEditableTargetStoryKey(storyTarget)
63894
+ });
63895
+ const replacement = createSelectedListItemDeleteReplacement({
63896
+ command,
63897
+ document: document2,
63898
+ editableTarget,
63899
+ selection,
63900
+ targetResolution,
63901
+ timestamp
63902
+ });
63903
+ if (!replacement || storyTarget.kind !== "main") {
63904
+ return null;
63905
+ }
63906
+ return {
63907
+ type: "document.replace",
63908
+ document: replacement.document,
63909
+ selection: replacement.selection,
63910
+ mapping: replacement.mapping,
63911
+ protectionSelection: selection,
63912
+ origin: command.origin
63913
+ };
63914
+ }
63915
+ function parseMainStoryBlockPathTokens(blockPath) {
63916
+ const parts = blockPath?.split("/") ?? [];
63917
+ if (parts[0] !== "main" || parts.length < 2) {
63918
+ return null;
63919
+ }
63920
+ const tokens = [];
63921
+ for (const part of parts.slice(1)) {
63922
+ const match = /^(block|row|cell)\[(\d+)\]$/u.exec(part);
63923
+ if (!match) return null;
63924
+ tokens.push({
63925
+ kind: match[1],
63926
+ index: Number.parseInt(match[2], 10)
63927
+ });
63928
+ }
63929
+ return tokens;
63930
+ }
62778
63931
  function toInternalSelectionSnapshot2(selection) {
62779
63932
  return {
62780
63933
  anchor: selection.anchor,
@@ -62790,10 +63943,10 @@ function toInternalSelectionSnapshot2(selection) {
62790
63943
  )
62791
63944
  };
62792
63945
  }
62793
- function getFormattingOperationCommandName(operation) {
62794
- switch (operation.type) {
63946
+ function getFormattingOperationCommandName(operation2) {
63947
+ switch (operation2.type) {
62795
63948
  case "toggle":
62796
- return `toggle${operation.mark.charAt(0).toUpperCase()}${operation.mark.slice(1)}`;
63949
+ return `toggle${operation2.mark.charAt(0).toUpperCase()}${operation2.mark.slice(1)}`;
62797
63950
  case "set-font-family":
62798
63951
  return "setFontFamily";
62799
63952
  case "set-font-size":