@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
@@ -7,32 +7,33 @@ import {
7
7
  resolveSectionForStoryTarget,
8
8
  searchSecondaryStories,
9
9
  searchSurfaceBlocks
10
- } from "./chunk-LPLJZJT2.js";
10
+ } from "./chunk-O4EDZR44.js";
11
11
  import {
12
12
  createEditorSurfaceSnapshot,
13
13
  createFormattingContext,
14
14
  resolveTableStyleResolution
15
- } from "./chunk-U3UMKA7B.js";
15
+ } from "./chunk-XBQFDBXE.js";
16
16
  import {
17
17
  createSelectionSnapshot
18
18
  } from "./chunk-OYGMRRR7.js";
19
19
  import {
20
20
  normalizeParsedTextDocument
21
- } from "./chunk-4HGFJ6Z2.js";
21
+ } from "./chunk-PZIEOEJZ.js";
22
22
  import {
23
23
  buildFieldRegistry,
24
24
  parseMainDocumentXml,
25
25
  parseTocLevelRange
26
- } from "./chunk-RBWJHRNP.js";
26
+ } from "./chunk-T66OS7MN.js";
27
27
  import {
28
28
  collectEditableTargetRefs,
29
29
  collectLayoutInputIdentities,
30
30
  validateEditableTargetRef
31
- } from "./chunk-RYMMKOFI.js";
31
+ } from "./chunk-5KTJKTNE.js";
32
32
  import {
33
33
  serializeMainDocument
34
34
  } from "./chunk-FM4K4XFJ.js";
35
35
  import {
36
+ describeStructuredWrapperBlock,
36
37
  findOpaqueFragmentsIntersectingRange
37
38
  } from "./chunk-UFVDIR2C.js";
38
39
  import {
@@ -43,15 +44,15 @@ import {
43
44
  storyTargetsEqual
44
45
  } from "./chunk-M2HUK3KF.js";
45
46
 
46
- // src/runtime/workflow/scope-resolver.ts
47
- function inlineLength(node) {
47
+ // src/core/commands/scope-coordinate-walk.ts
48
+ function inlineLengthForScopeCoordinates(node) {
48
49
  switch (node.type) {
49
50
  case "text":
50
51
  return Array.from(node.text).length;
51
52
  case "hyperlink":
52
53
  case "field":
53
54
  return node.children.reduce(
54
- (total, child) => total + inlineLength(child),
55
+ (total, child) => total + inlineLengthForScopeCoordinates(child),
55
56
  0
56
57
  );
57
58
  case "bookmark_start":
@@ -63,29 +64,241 @@ function inlineLength(node) {
63
64
  return 1;
64
65
  }
65
66
  }
66
- function walkParagraphs(document) {
67
- const envelope = document;
68
- const root = "content" in envelope ? envelope.content : document;
69
- const out = [];
67
+ function paragraphLengthForScopeCoordinates(paragraph) {
68
+ return paragraph.children.reduce(
69
+ (total, child) => total + inlineLengthForScopeCoordinates(child),
70
+ 0
71
+ );
72
+ }
73
+ function computeScopeStoryLayout(document) {
74
+ const root = document.content;
75
+ const paragraphSlots = [];
76
+ const blockSlots = [];
77
+ if (!root || root.type !== "doc") {
78
+ return { paragraphSlots, blockSlots, storyLength: 0 };
79
+ }
70
80
  let cursor = 0;
71
81
  for (let index = 0; index < root.children.length; index += 1) {
72
82
  const block = root.children[index];
73
- if (block && block.type === "paragraph") {
74
- out.push({ paragraph: block, from: cursor });
75
- cursor += block.children.reduce(
76
- (total, child) => total + inlineLength(child),
77
- 0
78
- );
79
- } else if (block && block.type === "table") {
80
- cursor += 1;
81
- } else {
83
+ if (!block) continue;
84
+ cursor = walkBlockForScopeCoordinates(
85
+ block,
86
+ cursor,
87
+ [{ kind: "block", index }],
88
+ index,
89
+ paragraphSlots,
90
+ blockSlots
91
+ );
92
+ if (index < root.children.length - 1 && root.children[index + 1]?.type === "paragraph") {
82
93
  cursor += 1;
83
94
  }
84
- if (index < root.children.length - 1) {
85
- cursor += 1;
95
+ }
96
+ return { paragraphSlots, blockSlots, storyLength: cursor };
97
+ }
98
+ function findParagraphSlotAtPosition(layout, position, edge) {
99
+ const matches = layout.paragraphSlots.filter(
100
+ (slot) => position >= slot.from && position <= slot.to
101
+ );
102
+ if (matches.length === 0) return null;
103
+ if (edge === "start") {
104
+ return matches.find((slot) => slot.from === position) ?? matches.find((slot) => position > slot.from && position <= slot.to) ?? matches[0];
105
+ }
106
+ return matches.find((slot) => slot.to === position) ?? matches.find((slot) => position >= slot.from && position < slot.to) ?? matches[matches.length - 1];
107
+ }
108
+ function findOwningBlockSlotAtPosition(layout, position) {
109
+ const matches = layout.blockSlots.filter(
110
+ (slot) => position >= slot.from && position <= slot.to
111
+ );
112
+ if (matches.length === 0) return null;
113
+ return matches.reduce(
114
+ (best, slot) => slot.path.length >= best.path.length ? slot : best
115
+ );
116
+ }
117
+ function sameScopeParagraphPath(a, b) {
118
+ return scopeParagraphPathKey(a) === scopeParagraphPathKey(b);
119
+ }
120
+ function replaceParagraphChildrenAtPath(root, path, children) {
121
+ return {
122
+ ...root,
123
+ children: replaceInBlockList(root.children, path, children)
124
+ };
125
+ }
126
+ function removeScopeMarkersFromBlockList(blocks, scopeId) {
127
+ let mutated = false;
128
+ const nextBlocks = blocks.map((block) => {
129
+ const result = removeScopeMarkersFromBlock(block, scopeId);
130
+ if (result.mutated) mutated = true;
131
+ return result.block;
132
+ });
133
+ return { blocks: nextBlocks, mutated };
134
+ }
135
+ function walkBlockForScopeCoordinates(block, cursor, path, rootBlockIndex, paragraphSlots, blockSlots) {
136
+ const from = cursor;
137
+ if (block.type === "paragraph") {
138
+ const to2 = cursor + paragraphLengthForScopeCoordinates(block);
139
+ const slot = { path, rootBlockIndex, kind: block.type, from, to: to2 };
140
+ blockSlots.push(slot);
141
+ paragraphSlots.push({ ...slot, paragraph: block });
142
+ return to2;
143
+ }
144
+ if (block.type === "table") {
145
+ let innerCursor = cursor;
146
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
147
+ const row = block.rows[rowIndex];
148
+ for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex += 1) {
149
+ const cell = row.cells[cellIndex];
150
+ for (let childIndex = 0; childIndex < cell.children.length; childIndex += 1) {
151
+ const child = cell.children[childIndex];
152
+ innerCursor = walkBlockForScopeCoordinates(
153
+ child,
154
+ innerCursor,
155
+ [
156
+ ...path,
157
+ { kind: "tableCell", rowIndex, cellIndex },
158
+ { kind: "block", index: childIndex }
159
+ ],
160
+ rootBlockIndex,
161
+ paragraphSlots,
162
+ blockSlots
163
+ );
164
+ }
165
+ }
166
+ }
167
+ blockSlots.push({
168
+ path,
169
+ rootBlockIndex,
170
+ kind: block.type,
171
+ from,
172
+ to: innerCursor
173
+ });
174
+ return innerCursor;
175
+ }
176
+ if (block.type === "sdt") {
177
+ const descriptor = describeStructuredWrapperBlock(block, {
178
+ projectVisibleTocContentControls: true
179
+ });
180
+ if (descriptor) {
181
+ const to2 = cursor + 1;
182
+ blockSlots.push({ path, rootBlockIndex, kind: block.type, from, to: to2 });
183
+ return to2;
184
+ }
185
+ let innerCursor = cursor;
186
+ for (let childIndex = 0; childIndex < block.children.length; childIndex += 1) {
187
+ const child = block.children[childIndex];
188
+ innerCursor = walkBlockForScopeCoordinates(
189
+ child,
190
+ innerCursor,
191
+ [...path, { kind: "sdtChildren" }, { kind: "block", index: childIndex }],
192
+ rootBlockIndex,
193
+ paragraphSlots,
194
+ blockSlots
195
+ );
86
196
  }
197
+ blockSlots.push({
198
+ path,
199
+ rootBlockIndex,
200
+ kind: block.type,
201
+ from,
202
+ to: innerCursor
203
+ });
204
+ return innerCursor;
87
205
  }
88
- return out;
206
+ const to = cursor + 1;
207
+ blockSlots.push({ path, rootBlockIndex, kind: block.type, from, to });
208
+ return to;
209
+ }
210
+ function replaceInBlockList(blocks, path, children) {
211
+ const [head, ...rest] = path;
212
+ if (!head || head.kind !== "block") return [...blocks];
213
+ return blocks.map((block, index) => {
214
+ if (index !== head.index) return block;
215
+ if (rest.length === 0) {
216
+ return block.type === "paragraph" ? { ...block, children } : block;
217
+ }
218
+ return replaceInsideBlock(block, rest, children);
219
+ });
220
+ }
221
+ function replaceInsideBlock(block, path, children) {
222
+ const [head, ...rest] = path;
223
+ if (!head) return block;
224
+ if (block.type === "table" && head.kind === "tableCell") {
225
+ const rows = block.rows.map((row, rowIndex) => {
226
+ if (rowIndex !== head.rowIndex) return row;
227
+ const cells = row.cells.map((cell, cellIndex) => {
228
+ if (cellIndex !== head.cellIndex) return cell;
229
+ return {
230
+ ...cell,
231
+ children: replaceInBlockList(cell.children, rest, children)
232
+ };
233
+ });
234
+ return { ...row, cells };
235
+ });
236
+ return { ...block, rows };
237
+ }
238
+ if (block.type === "sdt" && head.kind === "sdtChildren") {
239
+ return {
240
+ ...block,
241
+ children: replaceInBlockList(block.children, rest, children)
242
+ };
243
+ }
244
+ return block;
245
+ }
246
+ function removeScopeMarkersFromBlock(block, scopeId) {
247
+ if (block.type === "paragraph") {
248
+ const kept = block.children.filter((child) => {
249
+ return !((child.type === "scope_marker_start" || child.type === "scope_marker_end") && child.scopeId === scopeId);
250
+ });
251
+ if (kept.length === block.children.length) {
252
+ return { block, mutated: false };
253
+ }
254
+ return { block: { ...block, children: kept }, mutated: true };
255
+ }
256
+ if (block.type === "table") {
257
+ let mutated = false;
258
+ const rows = block.rows.map((row) => {
259
+ let rowMutated = false;
260
+ const cells = row.cells.map((cell) => {
261
+ const result = removeScopeMarkersFromBlockList(cell.children, scopeId);
262
+ if (result.mutated) {
263
+ mutated = true;
264
+ rowMutated = true;
265
+ }
266
+ return result.mutated ? { ...cell, children: result.blocks } : cell;
267
+ });
268
+ return rowMutated ? { ...row, cells } : row;
269
+ });
270
+ return mutated ? { block: { ...block, rows }, mutated: true } : { block, mutated: false };
271
+ }
272
+ if (block.type === "sdt") {
273
+ const result = removeScopeMarkersFromBlockList(block.children, scopeId);
274
+ return result.mutated ? { block: { ...block, children: result.blocks }, mutated: true } : { block, mutated: false };
275
+ }
276
+ if (block.type === "custom_xml") {
277
+ const result = removeScopeMarkersFromBlockList(block.children, scopeId);
278
+ return result.mutated ? { block: { ...block, children: result.blocks }, mutated: true } : { block, mutated: false };
279
+ }
280
+ return { block, mutated: false };
281
+ }
282
+ function scopeParagraphPathKey(path) {
283
+ return path.map((step) => {
284
+ if (step.kind === "block") return `b:${step.index}`;
285
+ if (step.kind === "tableCell") {
286
+ return `tc:${step.rowIndex}:${step.cellIndex}`;
287
+ }
288
+ return "sdt";
289
+ }).join("/");
290
+ }
291
+
292
+ // src/runtime/workflow/scope-resolver.ts
293
+ function walkParagraphs(document) {
294
+ const envelope = document;
295
+ return computeScopeStoryLayout(envelope).paragraphSlots.map((slot) => ({
296
+ paragraph: slot.paragraph,
297
+ from: slot.from
298
+ }));
299
+ }
300
+ function inlineLength(node) {
301
+ return inlineLengthForScopeCoordinates(node);
89
302
  }
90
303
  function collectScopeLocations(document) {
91
304
  const locations = /* @__PURE__ */ new Map();
@@ -611,9 +824,9 @@ function messageForPostureBlocker(blocker) {
611
824
  return void 0;
612
825
  }
613
826
  }
614
- function auditFact(target, category, reason, operation) {
827
+ function auditFact(target, category, reason, operation2) {
615
828
  return {
616
- operation: operation ?? auditOperationForTarget(target),
829
+ operation: operation2 ?? auditOperationForTarget(target),
617
830
  category,
618
831
  dispatch: "blocked-before-mutation",
619
832
  reason
@@ -704,25 +917,43 @@ function computeBlockPositions(document) {
704
917
  let cursor = 0;
705
918
  for (let index = 0; index < root.children.length; index += 1) {
706
919
  const block = root.children[index];
920
+ if (!block) continue;
707
921
  const from = cursor;
708
- let length = 0;
709
- if (block && block.type === "paragraph") {
710
- length = block.children.reduce(
711
- (total, child) => total + inlineLength2(child),
712
- 0
713
- );
714
- } else {
715
- length = 1;
716
- }
717
- cursor += length;
718
- const to = cursor;
719
- out.push({ blockIndex: index, from, to });
720
- if (index < root.children.length - 1) {
922
+ cursor += blockLength(block);
923
+ out.push({ blockIndex: index, from, to: cursor });
924
+ if (index < root.children.length - 1 && root.children[index + 1]?.type === "paragraph") {
721
925
  cursor += 1;
722
926
  }
723
927
  }
724
928
  return out;
725
929
  }
930
+ function blockLength(block) {
931
+ if (block.type === "paragraph") {
932
+ return block.children.reduce(
933
+ (total, child) => total + inlineLength2(child),
934
+ 0
935
+ );
936
+ }
937
+ if (block.type === "table") {
938
+ let total = 0;
939
+ for (const row of block.rows) {
940
+ for (const cell of row.cells) {
941
+ for (const child of cell.children) {
942
+ total += blockLength(child);
943
+ }
944
+ }
945
+ }
946
+ return total;
947
+ }
948
+ if (block.type === "sdt") {
949
+ const descriptor = describeStructuredWrapperBlock(block, {
950
+ projectVisibleTocContentControls: true
951
+ });
952
+ if (descriptor) return 1;
953
+ return block.children.reduce((total, child) => total + blockLength(child), 0);
954
+ }
955
+ return 1;
956
+ }
726
957
  function computeInlinePositions(document, blocks) {
727
958
  const envelope = document;
728
959
  const root = "content" in envelope ? envelope.content : document;
@@ -970,8 +1201,8 @@ function deriveReplaceability(kind, provenance) {
970
1201
  if (kind === "scope") {
971
1202
  if (provenance === "marker-backed") {
972
1203
  return {
973
- level: "preserve-only",
974
- reason: "multi-paragraph-replace-not-implemented"
1204
+ level: "text-only",
1205
+ reason: "marker-backed-multi-paragraph-preserves-anchor"
975
1206
  };
976
1207
  }
977
1208
  return {
@@ -2031,11 +2262,9 @@ function compileParagraphReplacement(entry, proposed, options) {
2031
2262
  };
2032
2263
  }
2033
2264
  if (proposed.proposedContent.kind === "structured") {
2034
- if (options.posture === "suggest-mode") {
2035
- return null;
2036
- }
2037
2265
  const fragment = proposed.proposedContent.structured;
2038
2266
  if (!isStructuredReplacementContent(fragment)) return null;
2267
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
2039
2268
  const blockCount = fragment.blocks.length;
2040
2269
  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}`;
2041
2270
  const actionVerb = proposed.operation === "insert-before" ? "insert before" : proposed.operation === "insert-after" ? "insert after" : "replace";
@@ -2046,8 +2275,8 @@ function compileParagraphReplacement(entry, proposed, options) {
2046
2275
  operation: proposed.operation,
2047
2276
  steps: Object.freeze([
2048
2277
  {
2049
- kind: "fragment-replace",
2050
- summary: actionSummary,
2278
+ kind: stepKind,
2279
+ summary: stepKind === "fragment-replace" ? actionSummary : `suggest-mode ${actionSummary}`,
2051
2280
  ...textLeafEditableTargetHint(entry, blockRange) ? { editableTargetHint: textLeafEditableTargetHint(entry, blockRange) } : {},
2052
2281
  range: { from: operationRange.from, to: operationRange.to },
2053
2282
  fragment
@@ -2133,6 +2362,56 @@ function compileScopeKind(entry, options) {
2133
2362
  partial: true
2134
2363
  };
2135
2364
  }
2365
+ function compileScopeReplacement(entry, proposed, options) {
2366
+ if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace") {
2367
+ return null;
2368
+ }
2369
+ const markerRange = buildScopePositionMap(options.document).markerScopes.get(
2370
+ entry.handle.scopeId
2371
+ );
2372
+ if (!markerRange) return null;
2373
+ if (proposed.proposedContent.kind === "text") {
2374
+ const text = proposed.proposedContent.text ?? "";
2375
+ const stepKind = options.posture === "suggest-mode" ? "text-insert-tracked" : "text-replace";
2376
+ return {
2377
+ scopeId: entry.handle.scopeId,
2378
+ targetKind: "scope",
2379
+ operation: proposed.operation,
2380
+ steps: Object.freeze([
2381
+ {
2382
+ kind: stepKind,
2383
+ 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})`,
2384
+ range: { from: markerRange.from, to: markerRange.to },
2385
+ text,
2386
+ ...proposed.formatting ? { formatting: proposed.formatting } : {}
2387
+ }
2388
+ ]),
2389
+ ...proposed.preserve ? { preserve: proposed.preserve } : {},
2390
+ posture: options.posture
2391
+ };
2392
+ }
2393
+ if (proposed.proposedContent.kind === "structured") {
2394
+ const fragment = proposed.proposedContent.structured;
2395
+ if (!isStructuredReplacementContent(fragment)) return null;
2396
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
2397
+ return {
2398
+ scopeId: entry.handle.scopeId,
2399
+ targetKind: "scope",
2400
+ operation: proposed.operation,
2401
+ steps: Object.freeze([
2402
+ {
2403
+ kind: stepKind,
2404
+ 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))`,
2405
+ range: { from: markerRange.from, to: markerRange.to },
2406
+ fragment
2407
+ }
2408
+ ]),
2409
+ ...proposed.preserve ? { preserve: proposed.preserve } : {},
2410
+ posture: options.posture
2411
+ };
2412
+ }
2413
+ return null;
2414
+ }
2136
2415
 
2137
2416
  // src/runtime/scopes/scope-kinds/table.ts
2138
2417
  function compileTableScope(entry, options = {}) {
@@ -2504,6 +2783,99 @@ function paragraphFirstMarkerStart(paragraph, knownScopeIds) {
2504
2783
  }
2505
2784
  return null;
2506
2785
  }
2786
+ function paragraphHasMarkerEnd(paragraph, scopeId) {
2787
+ return paragraph.children.some(
2788
+ (child) => child.type === "scope_marker_end" && child.scopeId === scopeId
2789
+ );
2790
+ }
2791
+ function paragraphSameParagraphMarkerScopeId(paragraph, knownScopeIds) {
2792
+ const markerScopeId = paragraphFirstMarkerStart(paragraph, knownScopeIds);
2793
+ if (!markerScopeId) return null;
2794
+ return paragraphHasMarkerEnd(paragraph, markerScopeId) ? markerScopeId : null;
2795
+ }
2796
+ function markerStableRefOverride(markerScopeId, semanticPath, overlay) {
2797
+ const hint = stableRefHintForScopeId(markerScopeId, overlay);
2798
+ if (hint === "semantic-path") {
2799
+ return {
2800
+ kind: "semantic-path",
2801
+ value: semanticPath.join("/")
2802
+ };
2803
+ }
2804
+ return { kind: "scope-id", value: markerScopeId };
2805
+ }
2806
+ function enumerateNestedMarkerBackedParagraphs(blocks, input) {
2807
+ const out = [];
2808
+ for (let childIndex = 0; childIndex < blocks.length; childIndex += 1) {
2809
+ const block = blocks[childIndex];
2810
+ if (!block) continue;
2811
+ if (block.type === "paragraph") {
2812
+ const markerScopeId = paragraphSameParagraphMarkerScopeId(
2813
+ block,
2814
+ input.knownOverlayScopeIds
2815
+ );
2816
+ if (!markerScopeId) continue;
2817
+ const kind = detectParagraphKind(block);
2818
+ const semanticPath = [
2819
+ ...input.semanticPrefix,
2820
+ kind,
2821
+ String(childIndex)
2822
+ ];
2823
+ const handle = buildHandle(
2824
+ markerScopeId,
2825
+ input.documentId,
2826
+ semanticPath,
2827
+ "marker-backed",
2828
+ "marker-backed",
2829
+ input.parentScopeId,
2830
+ markerStableRefOverride(markerScopeId, semanticPath, input.overlay)
2831
+ );
2832
+ out.push({
2833
+ kind,
2834
+ handle,
2835
+ paragraph: block,
2836
+ blockIndex: input.rootBlockIndex,
2837
+ classifications: input.classificationIndex.get(markerScopeId) ?? Object.freeze([])
2838
+ });
2839
+ continue;
2840
+ }
2841
+ if (block.type === "sdt") {
2842
+ out.push(
2843
+ ...enumerateNestedMarkerBackedParagraphs(block.children, {
2844
+ ...input,
2845
+ semanticPrefix: [
2846
+ ...input.semanticPrefix,
2847
+ "sdt",
2848
+ String(childIndex)
2849
+ ]
2850
+ })
2851
+ );
2852
+ continue;
2853
+ }
2854
+ if (block.type === "table") {
2855
+ for (let rowIdx = 0; rowIdx < block.rows.length; rowIdx += 1) {
2856
+ const row = block.rows[rowIdx];
2857
+ for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx += 1) {
2858
+ const cell = row.cells[cellIdx];
2859
+ out.push(
2860
+ ...enumerateNestedMarkerBackedParagraphs(cell.children, {
2861
+ ...input,
2862
+ semanticPrefix: [
2863
+ ...input.semanticPrefix,
2864
+ "table",
2865
+ String(childIndex),
2866
+ "row",
2867
+ String(rowIdx),
2868
+ "cell",
2869
+ String(cellIdx)
2870
+ ]
2871
+ })
2872
+ );
2873
+ }
2874
+ }
2875
+ }
2876
+ }
2877
+ return out;
2878
+ }
2507
2879
  function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
2508
2880
  const out = [];
2509
2881
  for (let i = 0; i < paragraph.children.length; i += 1) {
@@ -2786,6 +3158,17 @@ function enumerateScopes(document, inputs = {}) {
2786
3158
  cellIndex: cellIdx,
2787
3159
  classifications: Object.freeze([])
2788
3160
  });
3161
+ for (const nested of enumerateNestedMarkerBackedParagraphs(cell.children, {
3162
+ documentId,
3163
+ rootBlockIndex: index,
3164
+ semanticPrefix: cellSemanticPath,
3165
+ parentScopeId: cellScopeId,
3166
+ knownOverlayScopeIds,
3167
+ classificationIndex,
3168
+ overlay: inputs.overlay
3169
+ })) {
3170
+ results.push(nested);
3171
+ }
2789
3172
  }
2790
3173
  }
2791
3174
  }
@@ -3037,31 +3420,6 @@ function deriveScopeAdjacentGeometryEvidence(scope, entry, provider) {
3037
3420
  };
3038
3421
  }
3039
3422
 
3040
- // src/runtime/scopes/multi-paragraph-refusal.ts
3041
- var MULTI_PARAGRAPH_REPLACEMENT_REFUSAL = "compile-refused:scope:multi-paragraph-replace-not-implemented";
3042
- function shapeBlocker(shape) {
3043
- switch (shape) {
3044
- case "text":
3045
- return "compile-refused:scope:multi-paragraph-text-replace-not-implemented";
3046
- case "fragment":
3047
- return "compile-refused:scope:multi-paragraph-fragment-replace-not-implemented";
3048
- default:
3049
- return "compile-refused:scope:multi-paragraph-replace-shape-not-implemented";
3050
- }
3051
- }
3052
- function multiParagraphReplacementBlockers(shape = "unknown") {
3053
- return Object.freeze([
3054
- MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
3055
- shapeBlocker(shape),
3056
- "capability:scope:block-granular-replacement-lowering-required",
3057
- "capability:scope:provenance:marker-backed-required",
3058
- "capability:scope:layout-completeness-required",
3059
- "capability:scope:geometry-completeness-required",
3060
- "capability:scope:continuation-state-required",
3061
- "capability:scope:preservation-verdict-required"
3062
- ]);
3063
- }
3064
-
3065
3423
  // src/runtime/scopes/capabilities.ts
3066
3424
  var PARAGRAPH_LIKE = /* @__PURE__ */ new Set([
3067
3425
  "paragraph",
@@ -3278,10 +3636,9 @@ function replaceTextCapability(scope, context) {
3278
3636
  if (guard) return blocked(guard);
3279
3637
  if (scope.kind === "list-item") {
3280
3638
  return blocked(
3281
- "compile-blocked:list-text:authoritative-readback-required",
3639
+ "compile-blocked:list-text:exact-list-text-handle-required",
3282
3640
  [
3283
- "capability:list-item:authoritative-readback-required",
3284
- "capability:list-text:export-persistent-target-required"
3641
+ "capability:list-item:exact-list-text-handle-required"
3285
3642
  ]
3286
3643
  );
3287
3644
  }
@@ -3296,6 +3653,15 @@ function replaceTextCapability(scope, context) {
3296
3653
  generatedOrLinked
3297
3654
  );
3298
3655
  }
3656
+ if (scope.kind === "scope") {
3657
+ return supported(
3658
+ "compile-supported:scope:multi-paragraph-text-replace",
3659
+ [
3660
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
3661
+ ...evidenceWarnings(context)
3662
+ ]
3663
+ );
3664
+ }
3299
3665
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
3300
3666
  if (scope.kind === "table-cell") {
3301
3667
  if (context?.tableCellTextRange?.status === "ok") {
@@ -3317,12 +3683,8 @@ function replaceTextCapability(scope, context) {
3317
3683
  ]
3318
3684
  );
3319
3685
  }
3320
- const reason = scope.kind === "scope" && scope.replaceability.reason ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : `compile-refused:${scope.kind}`;
3321
- return unsupported(
3322
- reason,
3323
- scope.kind === "scope" ? multiParagraphReplacementBlockers("text") : [reason],
3324
- evidenceWarnings(context)
3325
- );
3686
+ const reason = `compile-refused:${scope.kind}`;
3687
+ return unsupported(reason, [reason], evidenceWarnings(context));
3326
3688
  }
3327
3689
  if (scope.replaceability.level === "blocked" || scope.replaceability.level === "preserve-only" || scope.replaceability.level === "formatting-only") {
3328
3690
  const reason = scope.replaceability.reason ? `replaceability:${scope.replaceability.reason}` : `replaceability:${scope.replaceability.level}`;
@@ -3350,24 +3712,25 @@ function replaceFragmentCapability(scope, context) {
3350
3712
  generatedOrLinked
3351
3713
  );
3352
3714
  }
3353
- if (!PARAGRAPH_LIKE.has(scope.kind)) {
3354
- const reason = scope.kind === "scope" && scope.replaceability.reason ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : `compile-refused:${scope.kind}`;
3355
- return unsupported(
3356
- reason,
3357
- scope.kind === "scope" ? multiParagraphReplacementBlockers("fragment") : [reason],
3358
- evidenceWarnings(context)
3715
+ if (scope.kind === "scope") {
3716
+ return supported(
3717
+ "compile-supported:scope:multi-paragraph-fragment-replace",
3718
+ [
3719
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
3720
+ ...evidenceWarnings(context)
3721
+ ]
3359
3722
  );
3360
3723
  }
3361
- if (scope.workflow.effectiveMode === "suggest") {
3362
- return unsupported(
3363
- `compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
3364
- [`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
3365
- ["guard:suggest-mode"]
3366
- );
3724
+ if (!PARAGRAPH_LIKE.has(scope.kind)) {
3725
+ const reason = `compile-refused:${scope.kind}`;
3726
+ return unsupported(reason, [reason], evidenceWarnings(context));
3367
3727
  }
3368
3728
  return supported(
3369
- "compile-supported:paragraph-like:fragment-replace",
3370
- evidenceWarnings(context)
3729
+ scope.workflow.effectiveMode === "suggest" ? "compile-supported:paragraph-like:fragment-replace-tracked" : "compile-supported:paragraph-like:fragment-replace",
3730
+ [
3731
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
3732
+ ...evidenceWarnings(context)
3733
+ ]
3371
3734
  );
3372
3735
  }
3373
3736
  function formattingCapability(scope, context) {
@@ -4421,6 +4784,14 @@ function supportedNonTextCommandEvidence(target) {
4421
4784
  if (target.commandFamily === "link-bookmark") {
4422
4785
  return linkBookmarkCommandEvidence(target, []);
4423
4786
  }
4787
+ if (target.commandFamily === "object" && isImageObjectTarget(target) && typeof target.object?.mediaId === "string" && target.object.mediaId.length > 0 && onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
4788
+ return {
4789
+ status: "supported",
4790
+ commandFamily: target.commandFamily,
4791
+ intents: commandIntentsForTarget(target),
4792
+ reason: "l07:image-layout-target-supported"
4793
+ };
4794
+ }
4424
4795
  return null;
4425
4796
  }
4426
4797
  function onlyBlockers(blockers, allowed) {
@@ -5948,6 +6319,231 @@ function compileScopeBundleById(scopeId, inputs) {
5948
6319
  return compileScopeBundle(compiled, { ...inputs, scopes });
5949
6320
  }
5950
6321
 
6322
+ // src/runtime/scopes/editable-graph.ts
6323
+ function uniq(values) {
6324
+ return Object.freeze([...new Set(values.filter(Boolean))]);
6325
+ }
6326
+ function commandFamilyForTarget(target) {
6327
+ const intents = target.runtimeCommand.intents;
6328
+ if (target.table?.operationScope === "text" && target.runtimeCommand.commandFamily === "text-leaf") {
6329
+ return "table-text";
6330
+ }
6331
+ if (intents.some((intent) => intent.startsWith("table-"))) {
6332
+ if (intents.some((intent) => intent.includes("structure"))) {
6333
+ return "table-structure";
6334
+ }
6335
+ return "table-text";
6336
+ }
6337
+ if (intents.some((intent) => intent.startsWith("list-"))) {
6338
+ if (intents.some((intent) => intent.includes("structure"))) {
6339
+ return "list-structure";
6340
+ }
6341
+ return "list-text";
6342
+ }
6343
+ return target.runtimeCommand.commandFamily;
6344
+ }
6345
+ function familyForTarget(target) {
6346
+ if (target.runtimeCommand.intents.some(
6347
+ (intent) => intent === "table-structure-action" || intent === "table-merge-cells" || intent === "table-split-cell" || intent === "list-structure-action"
6348
+ )) {
6349
+ return "structure";
6350
+ }
6351
+ if (target.runtimeCommand.intents.some(
6352
+ (intent) => intent === "field-update" || intent === "toc-refresh" || intent === "hyperlink-update" || intent === "bookmark-update" || intent === "custom-xml-update"
6353
+ )) {
6354
+ return "value";
6355
+ }
6356
+ if (target.runtimeCommand.commandFamily === "object" || target.runtimeCommand.intents.some(
6357
+ (intent) => intent === "object-edit" || intent === "image-layout" || intent === "image-frame" || intent === "chart-edit" || intent === "embedded-content-update"
6358
+ )) {
6359
+ return "object";
6360
+ }
6361
+ if (target.runtimeCommand.commandFamily === "metadata") return "metadata";
6362
+ if (target.runtimeCommand.commandFamily === "preserve-only-refusal" || target.posture.preserveOnly || target.kind === "opaque-content") {
6363
+ return "preservation";
6364
+ }
6365
+ return "text";
6366
+ }
6367
+ function statusForTarget(target, family) {
6368
+ if (family === "preservation") return "preserve";
6369
+ if (target.runtimeCommand.status === "supported" && target.runtimeCommand.actionHandle) {
6370
+ return "ready";
6371
+ }
6372
+ if (target.runtimeCommand.status === "supported") return "diagnostic";
6373
+ return "needs-command";
6374
+ }
6375
+ function blockersForTarget(target) {
6376
+ return uniq([
6377
+ ...target.posture.blockers,
6378
+ ...target.runtimeTextCommand.blockers ?? [],
6379
+ ...target.runtimeCommand.blockers ?? [],
6380
+ ...target.workflowBlockers?.map((blocker) => blocker.blocker) ?? []
6381
+ ]);
6382
+ }
6383
+ function projectEditableTarget(target) {
6384
+ const family = familyForTarget(target);
6385
+ const commandFamily = commandFamilyForTarget(target);
6386
+ return Object.freeze({
6387
+ targetId: target.targetKey,
6388
+ family,
6389
+ kind: target.kind,
6390
+ relation: target.relation,
6391
+ status: statusForTarget(target, family),
6392
+ commandFamily,
6393
+ ...target.runtimeCommand.actionHandle ? { actionHandle: target.runtimeCommand.actionHandle } : {},
6394
+ ...target.runtimeCommand.canonicalAddress ? { canonicalAddress: target.runtimeCommand.canonicalAddress } : {},
6395
+ ...target.readback ? { readback: target.readback } : {},
6396
+ blockers: blockersForTarget(target),
6397
+ warnings: Object.freeze([]),
6398
+ intents: Object.freeze([...target.runtimeCommand.intents])
6399
+ });
6400
+ }
6401
+ function projectTableAction(action) {
6402
+ const family = action.family === "table-structure" ? "structure" : "text";
6403
+ const blockers = uniq(action.blockers ?? []);
6404
+ return Object.freeze({
6405
+ targetId: action.handle,
6406
+ family,
6407
+ kind: action.targetKind,
6408
+ relation: action.relation,
6409
+ status: action.status === "supported" && action.actionHandle ? "ready" : action.status === "supported" ? "diagnostic" : "needs-command",
6410
+ commandFamily: action.family,
6411
+ ...action.actionHandle ? { actionHandle: action.actionHandle } : {},
6412
+ ...action.canonicalAddress ? { canonicalAddress: action.canonicalAddress } : {},
6413
+ ...action.readback ? { readback: action.readback } : {},
6414
+ blockers,
6415
+ warnings: Object.freeze([...action.warnings ?? []]),
6416
+ intents: Object.freeze([...action.intents])
6417
+ });
6418
+ }
6419
+ function metadataTarget(bundle) {
6420
+ return Object.freeze({
6421
+ targetId: `${bundle.scope.handle.scopeId}:metadata`,
6422
+ family: "metadata",
6423
+ kind: "scope-metadata",
6424
+ status: "ready",
6425
+ commandFamily: "metadata",
6426
+ actionHandle: `scope-metadata:${bundle.scope.handle.scopeId}`,
6427
+ blockers: Object.freeze([]),
6428
+ warnings: Object.freeze([]),
6429
+ intents: Object.freeze(["metadata-update"])
6430
+ });
6431
+ }
6432
+ function scopeReplacementTarget(bundle) {
6433
+ const verdict = bundle.evidence.capabilities?.canReplaceText;
6434
+ if (!verdict?.supported) return null;
6435
+ return Object.freeze({
6436
+ targetId: `${bundle.scope.handle.scopeId}:scope-replace-text`,
6437
+ family: "text",
6438
+ kind: `${bundle.scope.kind}:scope-replacement`,
6439
+ relation: "exact-scope",
6440
+ status: "ready",
6441
+ commandFamily: "scope-replacement",
6442
+ blockers: Object.freeze([]),
6443
+ warnings: Object.freeze([...verdict.warnings ?? []]),
6444
+ intents: Object.freeze(["text-leaf-edit"])
6445
+ });
6446
+ }
6447
+ function group(targets, family) {
6448
+ return Object.freeze(targets.filter((target) => target.family === family));
6449
+ }
6450
+ function operation(kind, targets) {
6451
+ const ready = targets.filter((target) => target.status === "ready");
6452
+ const pending = targets.filter((target) => target.status === "needs-command");
6453
+ return Object.freeze({
6454
+ kind,
6455
+ status: ready.length > 0 ? "ready" : pending.length > 0 ? "needs-command" : "needs-target",
6456
+ targetIds: Object.freeze(ready.map((target) => target.targetId)),
6457
+ commandFamilies: uniq(ready.map((target) => target.commandFamily ?? ""))
6458
+ });
6459
+ }
6460
+ function contentModelFor(textTargets, valueTargets, structureTargets, objectTargets) {
6461
+ const families = [
6462
+ textTargets.length > 0 ? "text" : "",
6463
+ valueTargets.length > 0 ? "value" : "",
6464
+ structureTargets.length > 0 ? "structure" : "",
6465
+ objectTargets.length > 0 ? "object" : ""
6466
+ ].filter(Boolean);
6467
+ if (families.length === 0) return "metadata";
6468
+ if (families.length > 1) return "mixed";
6469
+ return families[0];
6470
+ }
6471
+ function summaryFor(model, targets) {
6472
+ const ready = targets.filter((target) => target.status === "ready").length;
6473
+ const needsCommand = targets.filter(
6474
+ (target) => target.status === "needs-command"
6475
+ ).length;
6476
+ const preserve = targets.filter((target) => target.status === "preserve").length;
6477
+ return `${model} scope editable graph: ${ready} ready target(s), ${needsCommand} target(s) needing command, ${preserve} preserve target(s)`;
6478
+ }
6479
+ function compileScopeEditableGraph(bundle) {
6480
+ const scopeTextTarget = scopeReplacementTarget(bundle);
6481
+ const projected = [
6482
+ ...bundle.evidence.editableTargets?.entries.map(projectEditableTarget) ?? [],
6483
+ ...bundle.evidence.table?.actions.map(projectTableAction) ?? [],
6484
+ ...scopeTextTarget ? [scopeTextTarget] : [],
6485
+ metadataTarget(bundle)
6486
+ ];
6487
+ const byId = /* @__PURE__ */ new Map();
6488
+ for (const target of projected) {
6489
+ const existing = byId.get(target.targetId);
6490
+ if (!existing || existing.status !== "ready") {
6491
+ byId.set(target.targetId, target);
6492
+ }
6493
+ }
6494
+ const targets = Object.freeze([...byId.values()]);
6495
+ const textTargets = group(targets, "text");
6496
+ const valueTargets = group(targets, "value");
6497
+ const structureTargets = group(targets, "structure");
6498
+ const objectTargets = group(targets, "object");
6499
+ const metadataTargets = group(targets, "metadata");
6500
+ const preservationTargets = group(targets, "preservation");
6501
+ const contentModel = contentModelFor(
6502
+ textTargets,
6503
+ valueTargets,
6504
+ structureTargets,
6505
+ objectTargets
6506
+ );
6507
+ const operations = Object.freeze([
6508
+ operation("replace-text", textTargets),
6509
+ operation("replace-value", valueTargets),
6510
+ operation("edit-structure", structureTargets),
6511
+ operation("edit-object", objectTargets),
6512
+ operation("attach-metadata", metadataTargets)
6513
+ ]);
6514
+ const readyTargetCount = targets.filter((target) => target.status === "ready").length;
6515
+ const needsCommandTargetCount = targets.filter(
6516
+ (target) => target.status === "needs-command"
6517
+ ).length;
6518
+ const preserveTargetCount = targets.filter(
6519
+ (target) => target.status === "preserve"
6520
+ ).length;
6521
+ const blockers = uniq(targets.flatMap((target) => target.blockers));
6522
+ const warnings = uniq(targets.flatMap((target) => target.warnings));
6523
+ const readiness = Object.freeze({
6524
+ status: readyTargetCount > metadataTargets.length ? needsCommandTargetCount > 0 || preserveTargetCount > 0 ? "partial" : "ready" : "metadata-only",
6525
+ readyTargetCount,
6526
+ needsCommandTargetCount,
6527
+ preserveTargetCount,
6528
+ blockers,
6529
+ warnings
6530
+ });
6531
+ return Object.freeze({
6532
+ scope: bundle.scope.handle,
6533
+ contentModel,
6534
+ targets,
6535
+ textTargets,
6536
+ valueTargets,
6537
+ structureTargets,
6538
+ objectTargets,
6539
+ metadataTargets,
6540
+ preservationTargets,
6541
+ operations,
6542
+ readiness,
6543
+ summary: summaryFor(contentModel, targets)
6544
+ });
6545
+ }
6546
+
5951
6547
  // src/runtime/scopes/issue-lifecycle.ts
5952
6548
  function documentHash(doc) {
5953
6549
  let textLength = 0;
@@ -6499,11 +7095,11 @@ function computePreservationVerdict(document, range, positionMap) {
6499
7095
  }
6500
7096
 
6501
7097
  // src/runtime/scopes/action-validation.ts
6502
- function inferActionId(operation, content) {
6503
- if (operation === "formatting") {
7098
+ function inferActionId(operation2, content) {
7099
+ if (operation2 === "formatting") {
6504
7100
  return "fix_formatting";
6505
7101
  }
6506
- switch (operation) {
7102
+ switch (operation2) {
6507
7103
  case "replace":
6508
7104
  return content.kind === "text" ? "rewrite_paragraph" : "generate_text";
6509
7105
  case "insert-before":
@@ -6654,11 +7250,11 @@ function collectCapabilityVerdict(inputs, blockedReasons, warnings) {
6654
7250
  }
6655
7251
  }
6656
7252
  if (inputs.scope.kind === "list-item" && inputs.operation === "replace" && inputs.proposedContent.kind === "text") {
6657
- const code = "capability:list-item:authoritative-readback-required";
7253
+ const code = "capability:list-item:exact-list-text-handle-required";
6658
7254
  blockedReasons.push(code);
6659
7255
  warnings.push({
6660
7256
  code,
6661
- message: "list-item flat text replacement is blocked until the runtime validates an export-persistent list text command path",
7257
+ message: "list-item flat text replacement is blocked unless the caller uses an exact list-text action handle",
6662
7258
  source: "capability"
6663
7259
  });
6664
7260
  }
@@ -6895,11 +7491,44 @@ function compileReplacement(inputs) {
6895
7491
  inputs.posture,
6896
7492
  inputs.tableCellTextRange
6897
7493
  );
7494
+ case "scope":
7495
+ if (inputs.enumeratedScope.kind === "scope") {
7496
+ return compileScopeReplacement(inputs.enumeratedScope, inputs.proposed, {
7497
+ document: inputs.document,
7498
+ posture: inputs.posture
7499
+ });
7500
+ }
7501
+ return null;
6898
7502
  default:
6899
7503
  return null;
6900
7504
  }
6901
7505
  }
6902
7506
 
7507
+ // src/runtime/scopes/multi-paragraph-refusal.ts
7508
+ var MULTI_PARAGRAPH_REPLACEMENT_REFUSAL = "compile-refused:scope:multi-paragraph-replace-not-implemented";
7509
+ function shapeBlocker(shape) {
7510
+ switch (shape) {
7511
+ case "text":
7512
+ return "compile-refused:scope:multi-paragraph-text-replace-not-implemented";
7513
+ case "fragment":
7514
+ return "compile-refused:scope:multi-paragraph-fragment-replace-not-implemented";
7515
+ default:
7516
+ return "compile-refused:scope:multi-paragraph-replace-shape-not-implemented";
7517
+ }
7518
+ }
7519
+ function multiParagraphReplacementBlockers(shape = "unknown") {
7520
+ return Object.freeze([
7521
+ MULTI_PARAGRAPH_REPLACEMENT_REFUSAL,
7522
+ shapeBlocker(shape),
7523
+ "capability:scope:block-granular-replacement-lowering-required",
7524
+ "capability:scope:provenance:marker-backed-required",
7525
+ "capability:scope:layout-completeness-required",
7526
+ "capability:scope:geometry-completeness-required",
7527
+ "capability:scope:continuation-state-required",
7528
+ "capability:scope:preservation-verdict-required"
7529
+ ]);
7530
+ }
7531
+
6903
7532
  // src/runtime/scopes/replacement/apply.ts
6904
7533
  function documentHash2(doc) {
6905
7534
  let hash = 2166136261;
@@ -7062,14 +7691,16 @@ function applyScopeReplacement(inputs) {
7062
7691
  });
7063
7692
  if (!plan) {
7064
7693
  const paragraphLike = resolvedScope.kind === "paragraph" || resolvedScope.kind === "heading" || resolvedScope.kind === "list-item";
7065
- const blockers = resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
7694
+ const blockers = resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
7695
+ `compile-refused:scope:operation-not-implemented:${proposed.operation}`
7696
+ ] : resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
7066
7697
  proposed.proposedContent.kind === "structured" ? "fragment" : "text"
7067
7698
  ) : paragraphLike && proposed.operation === "replace" && proposed.preserve?.opaqueFragments === true ? [
7068
7699
  `compile-refused:${resolvedScope.kind}:opaque-preserving-text-target-unavailable`
7069
7700
  ] : resolvedScope.kind === "table-cell" ? [tableCellRefusalBlocker(tableCellTextRange)] : paragraphLike && proposed.operation !== "replace" ? [
7070
7701
  `compile-refused:${resolvedScope.kind}:operation-not-implemented:${proposed.operation}`
7071
7702
  ] : [`compile-refused:${resolvedScope.kind}`];
7072
- const blocker = resolvedScope.kind === "scope" ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : blockers[0] ?? `compile-refused:${resolvedScope.kind}`;
7703
+ const blocker = blockers[0] ?? `compile-refused:${resolvedScope.kind}`;
7073
7704
  const refused = {
7074
7705
  safe: false,
7075
7706
  posture: "hard-refusal",
@@ -7548,10 +8179,10 @@ function createScopeCompilerService(runtime) {
7548
8179
  emitMetadataAudit(request) {
7549
8180
  try {
7550
8181
  const snapshot = request.targetScopeSnapshot;
7551
- const operation = "annotate";
8182
+ const operation2 = "annotate";
7552
8183
  const proposed = {
7553
8184
  targetHandle: snapshot.handle,
7554
- operation,
8185
+ operation: operation2,
7555
8186
  proposedContent: {
7556
8187
  kind: "structured",
7557
8188
  structured: {
@@ -7626,6 +8257,10 @@ function createScopeCompilerService(runtime) {
7626
8257
  ...editableTargetCache ? { editableTargetCache } : {}
7627
8258
  });
7628
8259
  },
8260
+ compileEditableGraphById(scopeId, nowUtc) {
8261
+ const bundle = this.compileBundleById(scopeId, nowUtc);
8262
+ return bundle ? compileScopeEditableGraph(bundle) : null;
8263
+ },
7629
8264
  buildReplacementScope(targetHandle, input) {
7630
8265
  return proposeReplacement({
7631
8266
  targetHandle,
@@ -9057,6 +9692,13 @@ export {
9057
9692
  ensureDefaultListInstance,
9058
9693
  allocateNumberingInstance,
9059
9694
  setStartOverride,
9695
+ inlineLengthForScopeCoordinates,
9696
+ computeScopeStoryLayout,
9697
+ findParagraphSlotAtPosition,
9698
+ findOwningBlockSlotAtPosition,
9699
+ sameScopeParagraphPath,
9700
+ replaceParagraphChildrenAtPath,
9701
+ removeScopeMarkersFromBlockList,
9060
9702
  collectScopeLocations,
9061
9703
  resolveScope,
9062
9704
  findAllScopesAt,