@beyondwork/docx-react-component 1.0.120 → 1.0.122

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 (116) hide show
  1. package/README.md +1 -0
  2. package/dist/api/public-types.cjs +1713 -55
  3. package/dist/api/public-types.d.cts +2 -2
  4. package/dist/api/public-types.d.ts +2 -2
  5. package/dist/api/public-types.js +6 -6
  6. package/dist/api/v3.cjs +4958 -406
  7. package/dist/api/v3.d.cts +3 -3
  8. package/dist/api/v3.d.ts +3 -3
  9. package/dist/api/v3.js +14 -14
  10. package/dist/{canonical-document-fNawStsc.d.cts → canonical-document-ByIqTd4s.d.cts} +9 -1
  11. package/dist/{canonical-document-fNawStsc.d.ts → canonical-document-ByIqTd4s.d.ts} +9 -1
  12. package/dist/{chunk-5RNMPLXU.js → chunk-37SEJQ3G.js} +4 -4
  13. package/dist/{chunk-FXGQM2JB.js → chunk-3OFSP2IX.js} +11 -5
  14. package/dist/{chunk-U5BSQXF4.js → chunk-3OHVK2D6.js} +70 -12
  15. package/dist/{chunk-AUQDC5BD.js → chunk-3TUQCHYT.js} +101 -2
  16. package/dist/{chunk-SJSMYEMU.js → chunk-B4YHWFE3.js} +3 -3
  17. package/dist/{chunk-XC56YLIS.js → chunk-C2LWJ4CZ.js} +4 -0
  18. package/dist/{chunk-VDIUVT46.js → chunk-CX42VC67.js} +1 -1
  19. package/dist/{chunk-KCHEAX4Z.js → chunk-EMDH4IQN.js} +148 -70
  20. package/dist/{chunk-TMQGWF7R.js → chunk-G3B2OBCZ.js} +352 -17
  21. package/dist/{chunk-VCL5MJMZ.js → chunk-GON2DNTE.js} +149 -28
  22. package/dist/{chunk-WVZX4NYG.js → chunk-GZW2ERUO.js} +601 -47
  23. package/dist/{chunk-WDNEPRFW.js → chunk-ICX54W4U.js} +1 -1
  24. package/dist/{chunk-FIGWJ43K.js → chunk-IT2DK3A7.js} +1883 -90
  25. package/dist/{chunk-2ZWFQ74R.js → chunk-OBCP6VTG.js} +1 -1
  26. package/dist/{chunk-FLNQY74K.js → chunk-OYGMRRR7.js} +1 -1
  27. package/dist/{chunk-MPYYBRVN.js → chunk-PCXTMEGY.js} +782 -124
  28. package/dist/{chunk-4JNUMMM7.js → chunk-PGGPPZ65.js} +17 -2
  29. package/dist/{chunk-KHZNNBTN.js → chunk-QFU7ZOAD.js} +43 -39
  30. package/dist/{chunk-4ZNQFWFM.js → chunk-QIO6V46H.js} +84 -4
  31. package/dist/{chunk-IQ2VJEF6.js → chunk-QNGJRZ2D.js} +1 -1
  32. package/dist/{chunk-BM5NSDII.js → chunk-S4ANOS2M.js} +2 -2
  33. package/dist/{chunk-AQA7OZ2R.js → chunk-TFSXOIAI.js} +959 -43
  34. package/dist/{chunk-NQZUGMLW.js → chunk-TMU7JMXX.js} +184 -32
  35. package/dist/{chunk-KD5K5XIA.js → chunk-UHQOUTAX.js} +568 -88
  36. package/dist/{chunk-327AIUXL.js → chunk-UWDWGQH5.js} +11 -4
  37. package/dist/{chunk-BBB4GSDB.js → chunk-XVFENXLK.js} +2 -2
  38. package/dist/{chunk-MUEN2WOG.js → chunk-ZKSDVHGH.js} +6 -3
  39. package/dist/compare.cjs +17 -2
  40. package/dist/compare.d.cts +1 -1
  41. package/dist/compare.d.ts +1 -1
  42. package/dist/compare.js +3 -3
  43. package/dist/core/commands/formatting-commands.d.cts +2 -2
  44. package/dist/core/commands/formatting-commands.d.ts +2 -2
  45. package/dist/core/commands/image-commands.cjs +814 -45
  46. package/dist/core/commands/image-commands.d.cts +2 -2
  47. package/dist/core/commands/image-commands.d.ts +2 -2
  48. package/dist/core/commands/image-commands.js +8 -8
  49. package/dist/core/commands/section-layout-commands.d.cts +2 -2
  50. package/dist/core/commands/section-layout-commands.d.ts +2 -2
  51. package/dist/core/commands/style-commands.d.cts +2 -2
  52. package/dist/core/commands/style-commands.d.ts +2 -2
  53. package/dist/core/commands/table-structure-commands.cjs +750 -42
  54. package/dist/core/commands/table-structure-commands.d.cts +2 -2
  55. package/dist/core/commands/table-structure-commands.d.ts +2 -2
  56. package/dist/core/commands/table-structure-commands.js +6 -6
  57. package/dist/core/commands/text-commands.cjs +910 -57
  58. package/dist/core/commands/text-commands.d.cts +2 -2
  59. package/dist/core/commands/text-commands.d.ts +2 -2
  60. package/dist/core/commands/text-commands.js +8 -8
  61. package/dist/core/selection/mapping.d.cts +2 -2
  62. package/dist/core/selection/mapping.d.ts +2 -2
  63. package/dist/core/state/editor-state.cjs +17 -2
  64. package/dist/core/state/editor-state.d.cts +2 -2
  65. package/dist/core/state/editor-state.d.ts +2 -2
  66. package/dist/core/state/editor-state.js +2 -2
  67. package/dist/index.cjs +6203 -625
  68. package/dist/index.d.cts +5 -5
  69. package/dist/index.d.ts +5 -5
  70. package/dist/index.js +299 -67
  71. package/dist/io/docx-session.cjs +354 -102
  72. package/dist/io/docx-session.d.cts +4 -4
  73. package/dist/io/docx-session.d.ts +4 -4
  74. package/dist/io/docx-session.js +5 -5
  75. package/dist/legal.cjs +183 -31
  76. package/dist/legal.d.cts +1 -1
  77. package/dist/legal.d.ts +1 -1
  78. package/dist/legal.js +3 -3
  79. package/dist/{loader-CaohrhNl.d.ts → loader-BF8ju_LK.d.ts} +22 -4
  80. package/dist/{loader-BpCyGnZl.d.cts → loader-g54WRvj1.d.cts} +22 -4
  81. package/dist/{public-types-Dpch9JG0.d.cts → public-types-D_y4Ptcj.d.cts} +747 -21
  82. package/dist/{public-types-C948HNVF.d.ts → public-types-Dl1jiWjk.d.ts} +747 -21
  83. package/dist/public-types.cjs +1713 -55
  84. package/dist/public-types.d.cts +2 -2
  85. package/dist/public-types.d.ts +2 -2
  86. package/dist/public-types.js +6 -6
  87. package/dist/runtime/collab.cjs +4 -0
  88. package/dist/runtime/collab.d.cts +3 -3
  89. package/dist/runtime/collab.d.ts +3 -3
  90. package/dist/runtime/collab.js +2 -2
  91. package/dist/runtime/document-runtime.cjs +3699 -507
  92. package/dist/runtime/document-runtime.d.cts +2 -2
  93. package/dist/runtime/document-runtime.d.ts +2 -2
  94. package/dist/runtime/document-runtime.js +18 -18
  95. package/dist/{session-Dqg17V3s.d.ts → session-C1EPAkcI.d.ts} +3 -3
  96. package/dist/{session-BlXE5zxz.d.cts → session-D15QOO0Q.d.cts} +3 -3
  97. package/dist/session.cjs +951 -104
  98. package/dist/session.d.cts +5 -5
  99. package/dist/session.d.ts +5 -5
  100. package/dist/session.js +10 -8
  101. package/dist/tailwind.cjs +1752 -91
  102. package/dist/tailwind.d.cts +2 -2
  103. package/dist/tailwind.d.ts +2 -2
  104. package/dist/tailwind.js +10 -10
  105. package/dist/{types-C9vZVpKy.d.cts → types-BoSRp2Vg.d.cts} +2 -2
  106. package/dist/{types-B1tlF1bq.d.ts → types-DEvRwq9C.d.ts} +2 -2
  107. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
  108. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
  109. package/dist/ui-tailwind/editor-surface/search-plugin.js +7 -7
  110. package/dist/ui-tailwind/theme/editor-theme.css +5 -5
  111. package/dist/ui-tailwind.cjs +1752 -91
  112. package/dist/ui-tailwind.d.cts +8 -8
  113. package/dist/ui-tailwind.d.ts +8 -8
  114. package/dist/ui-tailwind.js +10 -10
  115. package/package.json +17 -5
  116. package/dist/ui-tailwind/theme/tokens.css +0 -382
@@ -7,27 +7,27 @@ import {
7
7
  resolveSectionForStoryTarget,
8
8
  searchSecondaryStories,
9
9
  searchSurfaceBlocks
10
- } from "./chunk-TMQGWF7R.js";
10
+ } from "./chunk-G3B2OBCZ.js";
11
11
  import {
12
12
  createEditorSurfaceSnapshot,
13
13
  createFormattingContext,
14
14
  resolveTableStyleResolution
15
- } from "./chunk-AUQDC5BD.js";
15
+ } from "./chunk-3TUQCHYT.js";
16
16
  import {
17
17
  createSelectionSnapshot
18
- } from "./chunk-FLNQY74K.js";
18
+ } from "./chunk-OYGMRRR7.js";
19
19
  import {
20
20
  normalizeParsedTextDocument
21
- } from "./chunk-327AIUXL.js";
21
+ } from "./chunk-UWDWGQH5.js";
22
22
  import {
23
23
  buildFieldRegistry,
24
24
  parseMainDocumentXml,
25
25
  parseTocLevelRange
26
- } from "./chunk-NQZUGMLW.js";
26
+ } from "./chunk-TMU7JMXX.js";
27
27
  import {
28
28
  collectEditableTargetRefs,
29
29
  validateEditableTargetRef
30
- } from "./chunk-AQA7OZ2R.js";
30
+ } from "./chunk-TFSXOIAI.js";
31
31
  import {
32
32
  serializeMainDocument
33
33
  } from "./chunk-EB6M3GE6.js";
@@ -36,7 +36,7 @@ import {
36
36
  } from "./chunk-UFVDIR2C.js";
37
37
  import {
38
38
  createPublicRangeAnchor
39
- } from "./chunk-4JNUMMM7.js";
39
+ } from "./chunk-PGGPPZ65.js";
40
40
  import {
41
41
  MAIN_STORY_TARGET,
42
42
  storyTargetsEqual
@@ -372,75 +372,83 @@ function deriveWorkflowEditableTargetBlockerFacts(input) {
372
372
  (input.targetRanges ?? []).map((range) => [range.targetKey, range])
373
373
  );
374
374
  const currentTargets = input.currentTargets ? new Map(input.currentTargets.map((target) => [target.targetKey, target])) : null;
375
- const auditOperations = new Map(
376
- (input.auditIntents ?? []).map((intent) => [intent.targetKey, intent.operation])
377
- );
375
+ const auditOperations = auditOperationsByTarget(input.auditIntents);
378
376
  for (const target of input.targets) {
379
- const auditOperation = auditOperations.get(target.targetKey);
377
+ const targetAuditOperations = auditOperationsForTarget(target, auditOperations);
380
378
  if (input.activeStoryKey !== void 0 && input.activeStoryKey !== null && target.storyKey !== input.activeStoryKey) {
381
- facts.push({
382
- ...baseFact(target),
383
- blocker: "wrong-story",
384
- refusalId: "editable_target_wrong_story",
385
- source: "target-resolution",
386
- audit: auditFact(target, "wrong-story", "editable_target_wrong_story", auditOperation),
387
- message: `Editable target belongs to story "${target.storyKey}", not active story "${input.activeStoryKey}".`
388
- });
379
+ for (const auditOperation of targetAuditOperations) {
380
+ facts.push({
381
+ ...baseFact(target),
382
+ blocker: "wrong-story",
383
+ refusalId: "editable_target_wrong_story",
384
+ source: "target-resolution",
385
+ audit: auditFact(target, "wrong-story", "editable_target_wrong_story", auditOperation),
386
+ message: `Editable target belongs to story "${target.storyKey}", not active story "${input.activeStoryKey}".`
387
+ });
388
+ }
389
389
  }
390
390
  const currentTarget = currentTargets?.get(target.targetKey);
391
391
  if (currentTargets && (!currentTarget || !sameResolvedTarget(target, currentTarget))) {
392
- facts.push({
393
- ...baseFact(target),
394
- blocker: "stale-ref",
395
- refusalId: "editable_target_stale",
396
- source: "target-resolution",
397
- audit: auditFact(target, "stale-ref", "editable_target_stale", auditOperation),
398
- message: "Editable target ref is stale for the current canonical document."
399
- });
392
+ for (const auditOperation of targetAuditOperations) {
393
+ facts.push({
394
+ ...baseFact(target),
395
+ blocker: "stale-ref",
396
+ refusalId: "editable_target_stale",
397
+ source: "target-resolution",
398
+ audit: auditFact(target, "stale-ref", "editable_target_stale", auditOperation),
399
+ message: "Editable target ref is stale for the current canonical document."
400
+ });
401
+ }
400
402
  }
401
403
  for (const blocker of target.posture.blockers) {
402
404
  const refusalId = refusalIdForPostureBlocker(target, blocker);
403
405
  const message = messageForPostureBlocker(blocker);
404
- facts.push({
405
- ...baseFact(target),
406
- blocker,
407
- refusalId,
408
- source: "editable-target-posture",
409
- audit: auditFact(
410
- target,
411
- auditCategoryForPostureBlocker(target, blocker),
406
+ for (const auditOperation of targetAuditOperations) {
407
+ facts.push({
408
+ ...baseFact(target),
409
+ blocker,
412
410
  refusalId,
413
- auditOperation
414
- ),
415
- ...message !== void 0 ? { message } : {}
416
- });
411
+ source: "editable-target-posture",
412
+ audit: auditFact(
413
+ target,
414
+ auditCategoryForPostureBlocker(target, blocker),
415
+ refusalId,
416
+ auditOperation
417
+ ),
418
+ ...message !== void 0 ? { message } : {}
419
+ });
420
+ }
417
421
  }
418
422
  const range = targetRanges.get(target.targetKey);
419
423
  const protectedRange = range && input.protectionSnapshot ? findProtectionBlocker(input.protectionSnapshot, range) : null;
420
424
  if (protectedRange) {
421
- facts.push({
422
- ...baseFact(target),
423
- blocker: "protected-range",
424
- refusalId: "protected_range",
425
- source: "protection-snapshot",
426
- audit: auditFact(target, "protected-content", "protected_range", auditOperation),
427
- message: protectedRange.enforcementReason,
428
- rangeId: protectedRange.rangeId
429
- });
425
+ for (const auditOperation of targetAuditOperations) {
426
+ facts.push({
427
+ ...baseFact(target),
428
+ blocker: "protected-range",
429
+ refusalId: "protected_range",
430
+ source: "protection-snapshot",
431
+ audit: auditFact(target, "protected-content", "protected_range", auditOperation),
432
+ message: protectedRange.enforcementReason,
433
+ rangeId: protectedRange.rangeId
434
+ });
435
+ }
430
436
  }
431
437
  for (const reason of input.guard?.blockedReasons ?? []) {
432
438
  const mapped = mapGuardReason(reason);
433
439
  if (!mapped) continue;
434
- facts.push({
435
- ...baseFact(target),
436
- blocker: mapped.blocker,
437
- refusalId: reason.code,
438
- source: "interaction-guard",
439
- audit: auditFact(target, mapped.auditCategory, reason.code, auditOperation),
440
- message: reason.message,
441
- ...reason.scopeId !== void 0 ? { scopeId: reason.scopeId } : {},
442
- ...reason.workItemId !== void 0 ? { workItemId: reason.workItemId } : {}
443
- });
440
+ for (const auditOperation of targetAuditOperations) {
441
+ facts.push({
442
+ ...baseFact(target),
443
+ blocker: mapped.blocker,
444
+ refusalId: reason.code,
445
+ source: "interaction-guard",
446
+ audit: auditFact(target, mapped.auditCategory, reason.code, auditOperation),
447
+ message: reason.message,
448
+ ...reason.scopeId !== void 0 ? { scopeId: reason.scopeId } : {},
449
+ ...reason.workItemId !== void 0 ? { workItemId: reason.workItemId } : {}
450
+ });
451
+ }
444
452
  }
445
453
  }
446
454
  return Object.freeze(dedupeFacts(facts).sort(compareFacts));
@@ -453,6 +461,22 @@ function baseFact(target) {
453
461
  blockPath: target.blockPath
454
462
  };
455
463
  }
464
+ function auditOperationsByTarget(auditIntents) {
465
+ const byTarget = /* @__PURE__ */ new Map();
466
+ for (const intent of auditIntents ?? []) {
467
+ const operations = byTarget.get(intent.targetKey);
468
+ if (!operations) {
469
+ byTarget.set(intent.targetKey, [intent.operation]);
470
+ } else if (!operations.includes(intent.operation)) {
471
+ operations.push(intent.operation);
472
+ }
473
+ }
474
+ return byTarget;
475
+ }
476
+ function auditOperationsForTarget(target, auditOperations) {
477
+ const operations = auditOperations.get(target.targetKey);
478
+ return operations && operations.length > 0 ? operations : [auditOperationForTarget(target)];
479
+ }
456
480
  function findProtectionBlocker(protection, targetRange) {
457
481
  const enforcedRanges = protection.ranges.filter(
458
482
  (range) => range.enforced && typeof range.start === "number" && typeof range.end === "number"
@@ -553,7 +577,7 @@ function auditOperationForTarget(target) {
553
577
  case "field":
554
578
  return target.field?.fieldFamily === "TOC" ? "toc-refresh" : "field-update";
555
579
  case "link-bookmark":
556
- return target.kind === "bookmark-anchor" ? "bookmark-update" : "hyperlink-update";
580
+ return target.kind === "bookmark-anchor" || target.kind === "bookmark-content-range" ? "bookmark-update" : "hyperlink-update";
557
581
  case "comment-revision":
558
582
  return target.kind === "revision-anchor" ? "revision-action" : "comment-action";
559
583
  case "metadata":
@@ -567,7 +591,7 @@ function auditOperationForTarget(target) {
567
591
  }
568
592
  }
569
593
  function sameResolvedTarget(left, right) {
570
- return left.kind === right.kind && left.storyKey === right.storyKey && left.blockPath === right.blockPath && left.leafPath === right.leafPath && left.commandFamily === right.commandFamily && left.editability === right.editability && stableJson(left.sourceRef) === stableJson(right.sourceRef) && stableJson(left.staleCheck) === stableJson(right.staleCheck);
594
+ return left.kind === right.kind && left.storyKey === right.storyKey && left.blockPath === right.blockPath && left.leafPath === right.leafPath && left.commandFamily === right.commandFamily && left.editability === right.editability && stableJson(left.sourceRef) === stableJson(right.sourceRef) && stableJson(left.staleCheck) === stableJson(right.staleCheck) && stableJson(left.editableOwner) === stableJson(right.editableOwner);
571
595
  }
572
596
  function stableJson(value) {
573
597
  if (Array.isArray(value)) {
@@ -2003,7 +2027,8 @@ function compileTableScope(entry, options = {}) {
2003
2027
  classifications: entry.classifications,
2004
2028
  content: {
2005
2029
  text: summary,
2006
- excerpt: buildExcerpt(summary)
2030
+ excerpt: buildExcerpt(summary),
2031
+ authority: "structural-summary"
2007
2032
  },
2008
2033
  formatting,
2009
2034
  layout: {},
@@ -2045,7 +2070,9 @@ function compileTableCellScope(entry, options = {}) {
2045
2070
  classifications: entry.classifications,
2046
2071
  content: {
2047
2072
  text: summary,
2048
- excerpt: buildExcerpt(summary)
2073
+ excerpt: buildExcerpt(summary),
2074
+ authority: "structural-summary",
2075
+ editableTextPath: "evidence.editableTargets.entries[].readback"
2049
2076
  },
2050
2077
  formatting,
2051
2078
  layout: {},
@@ -2077,7 +2104,8 @@ function compileTableRowScope(entry, options = {}) {
2077
2104
  classifications: entry.classifications,
2078
2105
  content: {
2079
2106
  text: summary,
2080
- excerpt: buildExcerpt(summary)
2107
+ excerpt: buildExcerpt(summary),
2108
+ authority: "structural-summary"
2081
2109
  },
2082
2110
  formatting,
2083
2111
  layout: {},
@@ -2918,6 +2946,22 @@ function supported(reason, warnings = []) {
2918
2946
  ...hasWarnings ? { warnings: freezeList(warnings) } : {}
2919
2947
  };
2920
2948
  }
2949
+ function supportedCommand(reason, entries, warnings = []) {
2950
+ const verdict = supported(reason, warnings);
2951
+ const actionHandles = freezeUnique(
2952
+ entries.flatMap(
2953
+ (entry) => entry.runtimeCommand.actionHandle ? [entry.runtimeCommand.actionHandle] : []
2954
+ )
2955
+ );
2956
+ const intents = freezeUnique(
2957
+ entries.flatMap((entry) => [...entry.runtimeCommand.intents])
2958
+ );
2959
+ return {
2960
+ ...verdict,
2961
+ ...actionHandles.length > 0 ? { actionHandles } : {},
2962
+ ...intents.length > 0 ? { intents } : {}
2963
+ };
2964
+ }
2921
2965
  function unsupported(reason, blockers = [reason], warnings = []) {
2922
2966
  return {
2923
2967
  supported: false,
@@ -2959,6 +3003,27 @@ function contentControlBlockers(context) {
2959
3003
  if (entries.length === 0) return [];
2960
3004
  return freezeList(entries.map((entry) => `preserve:content-control:${entry.evidenceId}`));
2961
3005
  }
3006
+ function generatedOrLinkedContentBlockers(context) {
3007
+ const blockers = [];
3008
+ for (const entry of context?.editableTargets?.entries ?? []) {
3009
+ if (entry.kind === "field-result-text") {
3010
+ blockers.push("field-generated-text");
3011
+ continue;
3012
+ }
3013
+ if (entry.kind === "bookmark-anchor") {
3014
+ blockers.push("link-bookmark:bookmark-anchor");
3015
+ continue;
3016
+ }
3017
+ if (entry.kind === "hyperlink-text") {
3018
+ blockers.push("link-bookmark:hyperlink-text");
3019
+ continue;
3020
+ }
3021
+ if (entry.commandFamily === "link-bookmark" && entry.runtimeCommand.status !== "supported") {
3022
+ blockers.push(`link-bookmark:${entry.kind}`);
3023
+ }
3024
+ }
3025
+ return freezeUnique(blockers);
3026
+ }
2962
3027
  function evidenceWarnings(context) {
2963
3028
  const warnings = [];
2964
3029
  if (context?.layout) {
@@ -2991,9 +3056,11 @@ function editableTargetWarnings(context) {
2991
3056
  [...kinds].sort().map((kind) => `editable-target:${kind}:available`)
2992
3057
  );
2993
3058
  }
2994
- function commandSafeTargetWarnings(entries) {
2995
- return freezeUnique(
2996
- entries.map((entry) => `editable-target:${entry.kind}:available`).sort()
3059
+ function supportedCommandTargets(context, predicate) {
3060
+ return Object.freeze(
3061
+ [...context?.editableTargets?.entries ?? []].filter(
3062
+ (entry) => entry.runtimeCommand.status === "supported" && predicate(entry)
3063
+ )
2997
3064
  );
2998
3065
  }
2999
3066
  function commandTargetBlockers(entries) {
@@ -3008,6 +3075,9 @@ function commandTargetBlockers(entries) {
3008
3075
  }
3009
3076
  return freezeUnique(blockers.filter((blocker) => blocker.length > 0));
3010
3077
  }
3078
+ function hasRuntimeIntent(entry, intent) {
3079
+ return entry.runtimeCommand.intents.includes(intent);
3080
+ }
3011
3081
  function insertCapability(scope, edge, context) {
3012
3082
  const guard = guardBlocker(scope);
3013
3083
  if (guard) return blocked(guard);
@@ -3029,10 +3099,26 @@ function insertCapability(scope, edge, context) {
3029
3099
  function replaceTextCapability(scope, context) {
3030
3100
  const guard = guardBlocker(scope);
3031
3101
  if (guard) return blocked(guard);
3102
+ if (scope.kind === "list-item") {
3103
+ return blocked(
3104
+ "compile-blocked:list-text:authoritative-readback-required",
3105
+ [
3106
+ "capability:list-item:authoritative-readback-required",
3107
+ "capability:list-text:export-persistent-target-required"
3108
+ ]
3109
+ );
3110
+ }
3032
3111
  const contentControls = contentControlBlockers(context);
3033
3112
  if (contentControls.length > 0) {
3034
3113
  return blocked("preserve:content-control", contentControls);
3035
3114
  }
3115
+ const generatedOrLinked = generatedOrLinkedContentBlockers(context);
3116
+ if (generatedOrLinked.length > 0) {
3117
+ return blocked(
3118
+ "compile-refused:paragraph:generated-or-linked-content",
3119
+ generatedOrLinked
3120
+ );
3121
+ }
3036
3122
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
3037
3123
  if (scope.kind === "table-cell") {
3038
3124
  if (context?.tableCellTextRange?.status === "ok") {
@@ -3080,6 +3166,13 @@ function replaceFragmentCapability(scope, context) {
3080
3166
  if (contentControls.length > 0) {
3081
3167
  return blocked("preserve:content-control", contentControls);
3082
3168
  }
3169
+ const generatedOrLinked = generatedOrLinkedContentBlockers(context);
3170
+ if (generatedOrLinked.length > 0) {
3171
+ return blocked(
3172
+ "compile-refused:paragraph:generated-or-linked-content",
3173
+ generatedOrLinked
3174
+ );
3175
+ }
3083
3176
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
3084
3177
  const reason = scope.kind === "scope" && scope.replaceability.reason ? MULTI_PARAGRAPH_REPLACEMENT_REFUSAL : `compile-refused:${scope.kind}`;
3085
3178
  return unsupported(
@@ -3140,14 +3233,38 @@ function taxonomyRefusalCapability(action) {
3140
3233
  ]
3141
3234
  );
3142
3235
  }
3236
+ function fieldUpdateCapability(context) {
3237
+ const supportedTargets = supportedCommandTargets(
3238
+ context,
3239
+ (entry) => entry.commandFamily === "field" && (entry.runtimeCommand.intents.includes("field-update") || entry.runtimeCommand.intents.includes("toc-refresh"))
3240
+ );
3241
+ if (supportedTargets.length === 0) return taxonomyRefusalCapability("field-update");
3242
+ return supportedCommand(
3243
+ supportedTargets.some((entry) => entry.runtimeCommand.intents.includes("toc-refresh")) ? "compile-supported:field-update:toc-refresh-target" : "compile-supported:field-update:editable-target",
3244
+ supportedTargets
3245
+ );
3246
+ }
3143
3247
  function linkEditCapability(context) {
3144
3248
  const supportedDisplayTextTarget = context?.editableTargets?.entries.find(
3145
3249
  (entry) => entry.kind === "hyperlink-text" && entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("hyperlink-display-text-edit")
3146
3250
  );
3147
- if (!supportedDisplayTextTarget) {
3251
+ const supportedDestinationTarget = context?.editableTargets?.entries.find(
3252
+ (entry) => entry.kind === "hyperlink-destination" && entry.commandFamily === "link-bookmark" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("hyperlink-update")
3253
+ );
3254
+ if (!supportedDisplayTextTarget && !supportedDestinationTarget) {
3148
3255
  return taxonomyRefusalCapability("link-edit");
3149
3256
  }
3150
- return supported("compile-supported:link-edit:hyperlink-display-text");
3257
+ return supportedCommand(
3258
+ supportedDestinationTarget ? "compile-supported:link-edit:hyperlink-destination" : "compile-supported:link-edit:hyperlink-display-text",
3259
+ supportedDestinationTarget ? [supportedDestinationTarget] : supportedDisplayTextTarget ? [supportedDisplayTextTarget] : []
3260
+ );
3261
+ }
3262
+ function bookmarkEditCapability(context) {
3263
+ const supportedRangeTarget = context?.editableTargets?.entries.find(
3264
+ (entry) => entry.kind === "bookmark-content-range" && entry.commandFamily === "link-bookmark" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("bookmark-update")
3265
+ );
3266
+ if (!supportedRangeTarget) return taxonomyRefusalCapability("bookmark-edit");
3267
+ return supportedCommand("compile-supported:bookmark-edit:content-range", [supportedRangeTarget]);
3151
3268
  }
3152
3269
  function tableTextCapability(scope, context) {
3153
3270
  const guard = guardBlocker(scope);
@@ -3159,10 +3276,10 @@ function tableTextCapability(scope, context) {
3159
3276
  (entry) => entry.runtimeTextCommand.status === "supported" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("text-leaf-edit")
3160
3277
  );
3161
3278
  if (supportedTargets.length > 0) {
3162
- return supported(
3279
+ return supportedCommand(
3163
3280
  "compile-supported:table-text:editable-target",
3281
+ supportedTargets,
3164
3282
  [
3165
- ...commandSafeTargetWarnings(supportedTargets),
3166
3283
  ...evidenceWarnings(context)
3167
3284
  ]
3168
3285
  );
@@ -3203,10 +3320,10 @@ function tableStructureCapability(scope, context) {
3203
3320
  (entry) => entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("table-structure-action")
3204
3321
  );
3205
3322
  if (supportedTargets.length > 0) {
3206
- return supported(
3323
+ return supportedCommand(
3207
3324
  "compile-supported:table-structure:editable-target",
3325
+ supportedTargets,
3208
3326
  [
3209
- ...commandSafeTargetWarnings(supportedTargets),
3210
3327
  ...tableFormattingWarnings(scope),
3211
3328
  ...evidenceWarnings(context)
3212
3329
  ]
@@ -3288,15 +3405,13 @@ function listTextCapability(scope, context) {
3288
3405
  (entry) => entry.commandFamily === "text-leaf" && LIST_TEXT_TARGET_KINDS.has(entry.kind) && (entry.relation === "exact-scope" || entry.relation === "descendant-of-scope")
3289
3406
  );
3290
3407
  const supportedTargets = listTextTargets.filter(
3291
- (entry) => entry.runtimeTextCommand.status === "supported" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("text-leaf-edit")
3408
+ (entry) => entry.runtimeTextCommand.status === "supported" && entry.runtimeCommand.status === "supported" && hasRuntimeIntent(entry, "list-text-edit")
3292
3409
  );
3293
3410
  if (supportedTargets.length > 0) {
3294
- return supported(
3411
+ return supportedCommand(
3295
3412
  "compile-supported:list-text:editable-target",
3296
- [
3297
- ...commandSafeTargetWarnings(supportedTargets),
3298
- ...evidenceWarnings(context)
3299
- ]
3413
+ supportedTargets,
3414
+ evidenceWarnings(context)
3300
3415
  );
3301
3416
  }
3302
3417
  if (listTextTargets.length > 0) {
@@ -3318,7 +3433,7 @@ function listTextCapability(scope, context) {
3318
3433
  ]
3319
3434
  );
3320
3435
  }
3321
- function listStructureCapability(scope) {
3436
+ function listStructureCapability(scope, context) {
3322
3437
  if (scope.kind !== "list-item") {
3323
3438
  return unsupported(
3324
3439
  "compile-unsupported:list-structure:unsupported-target-family",
@@ -3328,6 +3443,26 @@ function listStructureCapability(scope) {
3328
3443
  ]
3329
3444
  );
3330
3445
  }
3446
+ const listStructureTargets = (context?.editableTargets?.entries ?? []).filter(
3447
+ (entry) => entry.commandFamily === "text-leaf" && LIST_TEXT_TARGET_KINDS.has(entry.kind) && (entry.relation === "exact-scope" || entry.relation === "descendant-of-scope")
3448
+ );
3449
+ const supportedTargets = listStructureTargets.filter(
3450
+ (entry) => entry.runtimeCommand.status === "supported" && hasRuntimeIntent(entry, "list-structure-action")
3451
+ );
3452
+ if (supportedTargets.length > 0) {
3453
+ return supportedCommand(
3454
+ "compile-supported:list-structure:editable-target",
3455
+ supportedTargets,
3456
+ evidenceWarnings(context)
3457
+ );
3458
+ }
3459
+ if (listStructureTargets.length > 0) {
3460
+ const blockers = commandTargetBlockers(listStructureTargets);
3461
+ return blocked(
3462
+ "compile-blocked:list-structure:target-ref-blocked",
3463
+ blockers.length > 0 ? blockers : ["compile-blocked:list-structure:target-ref-blocked"]
3464
+ );
3465
+ }
3331
3466
  return unsupported(
3332
3467
  "compile-unsupported:list-structure:no-target-family",
3333
3468
  [
@@ -3348,14 +3483,14 @@ function deriveScopeCapabilities(scope, context = {}) {
3348
3483
  canApplyFormatting: formattingCapability(scope, context),
3349
3484
  canClearFormattingLayer: formattingCapability(scope, context),
3350
3485
  canAttachMetadata: metadataCapability(scope),
3351
- canUpdateField: taxonomyRefusalCapability("field-update"),
3486
+ canUpdateField: fieldUpdateCapability(context),
3352
3487
  canEditLink: linkEditCapability(context),
3353
- canEditBookmark: taxonomyRefusalCapability("bookmark-edit"),
3488
+ canEditBookmark: bookmarkEditCapability(context),
3354
3489
  canEditTableText: tableTextCapability(scope, context),
3355
3490
  canEditTableStructure: tableStructureCapability(scope, context),
3356
3491
  canUseTableContinuationEvidence: tableContinuationEvidenceCapability(scope, context),
3357
3492
  canEditListText: listTextCapability(scope, context),
3358
- canEditListStructure: listStructureCapability(scope)
3493
+ canEditListStructure: listStructureCapability(scope, context)
3359
3494
  };
3360
3495
  }
3361
3496
 
@@ -3662,6 +3797,22 @@ function relationForTarget(target, scope, entry) {
3662
3797
  if (entry?.kind === "revision" && target.kind === "revision-anchor" && target.review?.reviewId === entry.revision.changeId) {
3663
3798
  return "exact-scope";
3664
3799
  }
3800
+ if (entry?.kind === "field") {
3801
+ if (target.kind === "field-result-text" && target.blockPath === `main/block[${entry.blockIndex}]/inline[${entry.inlineIndex}]`) {
3802
+ return "exact-scope";
3803
+ }
3804
+ if ((target.kind === "field-region-refresh" || target.kind === "toc-region-refresh") && target.field?.canonicalFieldId !== void 0 && target.field.canonicalFieldId === entry.field.canonicalFieldId) {
3805
+ return "exact-scope";
3806
+ }
3807
+ }
3808
+ if (entry && (entry.kind === "paragraph" || entry.kind === "heading" || entry.kind === "list-item")) {
3809
+ if ((target.kind === "field-region-refresh" || target.kind === "toc-region-refresh") && target.field?.canonicalFieldId !== void 0 && paragraphHasFieldId(entry.paragraph, target.field.canonicalFieldId)) {
3810
+ return "descendant-of-scope";
3811
+ }
3812
+ if (target.kind === "bookmark-content-range" && target.link?.bookmarkId !== void 0 && paragraphHasBookmarkId(entry.paragraph, target.link.bookmarkId)) {
3813
+ return "descendant-of-scope";
3814
+ }
3815
+ }
3665
3816
  const parsedTargetPath = parsePath(target.blockPath);
3666
3817
  if (!parsedTargetPath) return null;
3667
3818
  const targetTokens = parsedTargetPath.tokens;
@@ -3681,6 +3832,27 @@ function relationForTarget(target, scope, entry) {
3681
3832
  if (targetTokens.length === scopeTokens.length) return "exact-scope";
3682
3833
  return targetInScope ? "descendant-of-scope" : "scope-descendant-of-target";
3683
3834
  }
3835
+ function paragraphHasFieldId(paragraph, canonicalFieldId) {
3836
+ return paragraph.children.some(
3837
+ (child) => child.type === "field" && child.canonicalFieldId === canonicalFieldId
3838
+ );
3839
+ }
3840
+ function paragraphHasBookmarkId(paragraph, bookmarkId) {
3841
+ return paragraph.children.some(
3842
+ (child) => (child.type === "bookmark_start" || child.type === "bookmark_end") && child.bookmarkId === bookmarkId
3843
+ );
3844
+ }
3845
+ function commandActionHandleForAddress(commandFamily, address) {
3846
+ return address ? `scope-command:${commandFamily}:${address.addressKey}` : void 0;
3847
+ }
3848
+ function withCommandAction(evidence, target) {
3849
+ if (evidence.status !== "supported" || !target.canonicalAddress) return evidence;
3850
+ return {
3851
+ ...evidence,
3852
+ actionHandle: commandActionHandleForAddress(target.commandFamily, target.canonicalAddress),
3853
+ canonicalAddress: target.canonicalAddress
3854
+ };
3855
+ }
3684
3856
  function runtimeTextCommandEvidence(target, workflowBlockers) {
3685
3857
  const shapeIssues = validateEditableTargetRef(target);
3686
3858
  if (shapeIssues.length > 0) {
@@ -3730,6 +3902,11 @@ var TABLE_TEXT_TARGET_KINDS2 = /* @__PURE__ */ new Set([
3730
3902
  "nested-table-cell-paragraph-text",
3731
3903
  "sdt-table-cell-paragraph-text"
3732
3904
  ]);
3905
+ var LIST_TEXT_TARGET_KINDS2 = /* @__PURE__ */ new Set([
3906
+ "paragraph-text",
3907
+ "sdt-paragraph-text",
3908
+ "secondary-story-paragraph-text"
3909
+ ]);
3733
3910
  function tableTextScopeReplacementPosture(target) {
3734
3911
  if (target.posture.preserveOnly) {
3735
3912
  return {
@@ -3760,15 +3937,25 @@ function tableTextScopeReplacementPosture(target) {
3760
3937
  reason: "secondary-story-table-text-target-not-callable-by-scope-replacement"
3761
3938
  };
3762
3939
  }
3763
- function runtimeCommandEvidence(target, workflowBlockers, textCommand) {
3764
- if (target.commandFamily === "text-leaf") {
3940
+ function runtimeCommandEvidence(target, workflowBlockers, textCommand, scopeKind) {
3941
+ const shapeIssues = validateEditableTargetRef(target);
3942
+ if (shapeIssues.length > 0) {
3765
3943
  return {
3766
- status: textCommand.status,
3944
+ status: "blocked",
3767
3945
  commandFamily: target.commandFamily,
3768
3946
  intents: commandIntentsForTarget(target),
3947
+ reason: "editable_target_malformed",
3948
+ blockers: Object.freeze(["editable_target_malformed"])
3949
+ };
3950
+ }
3951
+ if (target.commandFamily === "text-leaf") {
3952
+ return withCommandAction({
3953
+ status: textCommand.status,
3954
+ commandFamily: target.commandFamily,
3955
+ intents: commandIntentsForTarget(target, scopeKind),
3769
3956
  reason: textCommand.reason,
3770
3957
  ...textCommand.blockers ? { blockers: textCommand.blockers } : {}
3771
- };
3958
+ }, target);
3772
3959
  }
3773
3960
  if (target.commandFamily === "comment-revision") {
3774
3961
  const isOpen = target.review?.status === "open";
@@ -3793,16 +3980,22 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand) {
3793
3980
  blockers: Object.freeze(actionableBlockers.map((fact) => fact.blocker))
3794
3981
  };
3795
3982
  }
3796
- return {
3983
+ return withCommandAction({
3797
3984
  status: "supported",
3798
3985
  commandFamily: target.commandFamily,
3799
3986
  intents: commandIntentsForTarget(target),
3800
3987
  reason: commentRevisionCommandReason(target, true)
3801
- };
3988
+ }, target);
3802
3989
  }
3803
3990
  if (target.commandFamily === "table-structure") {
3804
3991
  return tableStructureCommandEvidence(target, workflowBlockers);
3805
3992
  }
3993
+ if (target.commandFamily === "field") {
3994
+ return generatedFieldCommandEvidence(target, workflowBlockers);
3995
+ }
3996
+ if (target.commandFamily === "link-bookmark") {
3997
+ return linkBookmarkCommandEvidence(target, workflowBlockers);
3998
+ }
3806
3999
  const actionableWorkflowBlockers = workflowBlockers.filter(
3807
4000
  (fact) => fact.source !== "editable-target-posture"
3808
4001
  );
@@ -3815,6 +4008,8 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand) {
3815
4008
  blockers: Object.freeze(actionableWorkflowBlockers.map((fact) => fact.blocker))
3816
4009
  };
3817
4010
  }
4011
+ const commandSupported = supportedNonTextCommandEvidence(target);
4012
+ if (commandSupported) return commandSupported;
3818
4013
  return {
3819
4014
  status: "blocked",
3820
4015
  commandFamily: target.commandFamily,
@@ -3828,6 +4023,158 @@ function runtimeCommandEvidence(target, workflowBlockers, textCommand) {
3828
4023
  ])
3829
4024
  };
3830
4025
  }
4026
+ function generatedFieldCommandEvidence(target, workflowBlockers) {
4027
+ const actionableWorkflowBlockers = workflowBlockers.filter(
4028
+ (fact) => fact.source !== "editable-target-posture"
4029
+ );
4030
+ if (actionableWorkflowBlockers.length > 0) {
4031
+ return {
4032
+ status: "blocked",
4033
+ commandFamily: target.commandFamily,
4034
+ intents: commandIntentsForTarget(target),
4035
+ reason: actionableWorkflowBlockers[0]?.refusalId ?? "editable_target_blocked_by_workflow",
4036
+ blockers: Object.freeze(actionableWorkflowBlockers.map((fact) => fact.blocker))
4037
+ };
4038
+ }
4039
+ if (target.posture.preserveOnly || target.posture.blockers.includes("preserve-only")) {
4040
+ return {
4041
+ status: "blocked",
4042
+ commandFamily: target.commandFamily,
4043
+ intents: commandIntentsForTarget(target),
4044
+ reason: target.field?.fieldFamily === "TOC" ? "l07:toc-refresh-preserve-only-refused" : "l07:field-update-preserve-only-refused",
4045
+ blockers: Object.freeze([...target.posture.blockers])
4046
+ };
4047
+ }
4048
+ if (contentControlLocksTarget(target)) {
4049
+ return {
4050
+ status: "blocked",
4051
+ commandFamily: target.commandFamily,
4052
+ intents: commandIntentsForTarget(target),
4053
+ reason: "editable_target_non_editable",
4054
+ blockers: Object.freeze(["locked-content-control"])
4055
+ };
4056
+ }
4057
+ if (target.kind !== "field-result-text" && target.kind !== "field-region-refresh" && target.kind !== "toc-region-refresh") {
4058
+ return {
4059
+ status: "blocked",
4060
+ commandFamily: target.commandFamily,
4061
+ intents: commandIntentsForTarget(target),
4062
+ reason: "editable_target_kind_unsupported",
4063
+ blockers: Object.freeze(["editable_target_kind_unsupported"])
4064
+ };
4065
+ }
4066
+ if (!target.canonicalAddress) {
4067
+ return {
4068
+ status: "blocked",
4069
+ commandFamily: target.commandFamily,
4070
+ intents: commandIntentsForTarget(target),
4071
+ reason: "editable_target_command_address_missing",
4072
+ blockers: Object.freeze(["editable_target_command_address_missing"])
4073
+ };
4074
+ }
4075
+ const expectedScope = target.kind === "toc-region-refresh" ? "toc-refresh" : "field-refresh";
4076
+ if (target.canonicalAddress.operationScope !== expectedScope || target.field?.refreshStatus === "preserve-only" || !onlyBlockers(target.posture.blockers, ["field-generated-text", "unmodeled-target"])) {
4077
+ return {
4078
+ status: "blocked",
4079
+ commandFamily: target.commandFamily,
4080
+ intents: commandIntentsForTarget(target),
4081
+ reason: commandRefusalReasonForTarget(target),
4082
+ blockers: Object.freeze([...target.posture.blockers])
4083
+ };
4084
+ }
4085
+ return withCommandAction({
4086
+ status: "supported",
4087
+ commandFamily: target.commandFamily,
4088
+ intents: commandIntentsForTarget(target),
4089
+ reason: target.kind === "toc-region-refresh" || target.field?.fieldFamily === "TOC" ? "l07:toc-refresh-target-supported" : "l07:field-refresh-target-supported"
4090
+ }, target);
4091
+ }
4092
+ function linkBookmarkCommandEvidence(target, workflowBlockers) {
4093
+ const actionableWorkflowBlockers = workflowBlockers.filter(
4094
+ (fact) => fact.source !== "editable-target-posture"
4095
+ );
4096
+ if (actionableWorkflowBlockers.length > 0) {
4097
+ return {
4098
+ status: "blocked",
4099
+ commandFamily: target.commandFamily,
4100
+ intents: commandIntentsForTarget(target),
4101
+ reason: actionableWorkflowBlockers[0]?.refusalId ?? "editable_target_blocked_by_workflow",
4102
+ blockers: Object.freeze(actionableWorkflowBlockers.map((fact) => fact.blocker))
4103
+ };
4104
+ }
4105
+ if (target.posture.preserveOnly || target.posture.blockers.includes("preserve-only")) {
4106
+ return {
4107
+ status: "blocked",
4108
+ commandFamily: target.commandFamily,
4109
+ intents: commandIntentsForTarget(target),
4110
+ reason: commandRefusalReasonForTarget(target),
4111
+ blockers: Object.freeze([...target.posture.blockers])
4112
+ };
4113
+ }
4114
+ if (contentControlLocksTarget(target)) {
4115
+ return {
4116
+ status: "blocked",
4117
+ commandFamily: target.commandFamily,
4118
+ intents: commandIntentsForTarget(target),
4119
+ reason: "editable_target_non_editable",
4120
+ blockers: Object.freeze(["locked-content-control"])
4121
+ };
4122
+ }
4123
+ if (target.kind === "hyperlink-destination") {
4124
+ if (!target.canonicalAddress || target.canonicalAddress.operationScope !== "link-destination" || !onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
4125
+ return {
4126
+ status: "blocked",
4127
+ commandFamily: target.commandFamily,
4128
+ intents: commandIntentsForTarget(target),
4129
+ reason: commandRefusalReasonForTarget(target),
4130
+ blockers: Object.freeze([...target.posture.blockers])
4131
+ };
4132
+ }
4133
+ return withCommandAction({
4134
+ status: "supported",
4135
+ commandFamily: target.commandFamily,
4136
+ intents: commandIntentsForTarget(target),
4137
+ reason: "l07:hyperlink-destination-target-supported"
4138
+ }, target);
4139
+ }
4140
+ if (target.kind === "bookmark-content-range") {
4141
+ if (!target.canonicalAddress || target.canonicalAddress.operationScope !== "bookmark-range" || !onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
4142
+ return {
4143
+ status: "blocked",
4144
+ commandFamily: target.commandFamily,
4145
+ intents: commandIntentsForTarget(target),
4146
+ reason: commandRefusalReasonForTarget(target),
4147
+ blockers: Object.freeze([...target.posture.blockers])
4148
+ };
4149
+ }
4150
+ return withCommandAction({
4151
+ status: "supported",
4152
+ commandFamily: target.commandFamily,
4153
+ intents: commandIntentsForTarget(target),
4154
+ reason: "l07:bookmark-content-range-target-supported"
4155
+ }, target);
4156
+ }
4157
+ return {
4158
+ status: "blocked",
4159
+ commandFamily: target.commandFamily,
4160
+ intents: commandIntentsForTarget(target),
4161
+ reason: "l07:bookmark-boundary-mutation-refused",
4162
+ blockers: Object.freeze(["bookmark-boundary-mutation"])
4163
+ };
4164
+ }
4165
+ function supportedNonTextCommandEvidence(target) {
4166
+ if (target.commandFamily === "field") {
4167
+ return generatedFieldCommandEvidence(target, []);
4168
+ }
4169
+ if (target.commandFamily === "link-bookmark") {
4170
+ return linkBookmarkCommandEvidence(target, []);
4171
+ }
4172
+ return null;
4173
+ }
4174
+ function onlyBlockers(blockers, allowed) {
4175
+ const allowedSet = new Set(allowed);
4176
+ return blockers.every((blocker) => allowedSet.has(blocker));
4177
+ }
3831
4178
  function tableStructureCommandEvidence(target, workflowBlockers) {
3832
4179
  const shapeIssues = validateEditableTargetRef(target);
3833
4180
  if (shapeIssues.length > 0) {
@@ -3897,12 +4244,12 @@ function tableStructureCommandEvidence(target, workflowBlockers) {
3897
4244
  blockers: Object.freeze(["editable_target_table_path_unsupported"])
3898
4245
  };
3899
4246
  }
3900
- return {
4247
+ return withCommandAction({
3901
4248
  status: "supported",
3902
4249
  commandFamily: target.commandFamily,
3903
4250
  intents: commandIntentsForTarget(target),
3904
4251
  reason: tableStructureSupportedReason(target)
3905
- };
4252
+ }, target);
3906
4253
  }
3907
4254
  function tableStructureSupportedReason(target) {
3908
4255
  const table = target.table;
@@ -3927,17 +4274,32 @@ function commentRevisionCommandReason(target, isOpen) {
3927
4274
  }
3928
4275
  return isOpen ? "l07:comment-command-family-supported" : "l07:comment-command-not-open";
3929
4276
  }
3930
- function commandIntentsForTarget(target) {
4277
+ function commandIntentsForTarget(target, scopeKind) {
3931
4278
  switch (target.commandFamily) {
3932
4279
  case "text-leaf":
3933
- return target.kind === "hyperlink-text" ? Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]) : Object.freeze(["text-leaf-edit"]);
4280
+ if (target.kind === "hyperlink-text") {
4281
+ return Object.freeze(["text-leaf-edit", "hyperlink-display-text-edit"]);
4282
+ }
4283
+ if (target.table?.operationScope === "text") {
4284
+ return Object.freeze([
4285
+ "text-leaf-edit",
4286
+ "table-text-paste",
4287
+ "table-text-drop",
4288
+ "table-structured-fragment-paste",
4289
+ "table-structured-fragment-drop"
4290
+ ]);
4291
+ }
4292
+ if (scopeKind === "list-item" && LIST_TEXT_TARGET_KINDS2.has(target.kind)) {
4293
+ return Object.freeze(["text-leaf-edit", "list-text-edit", "list-structure-action"]);
4294
+ }
4295
+ return Object.freeze(["text-leaf-edit"]);
3934
4296
  case "field":
3935
4297
  return Object.freeze(
3936
4298
  target.field?.fieldFamily === "TOC" ? ["toc-refresh", "field-update"] : ["field-update"]
3937
4299
  );
3938
4300
  case "link-bookmark":
3939
4301
  return Object.freeze(
3940
- target.kind === "bookmark-anchor" ? ["bookmark-update"] : ["hyperlink-update"]
4302
+ target.kind === "bookmark-anchor" || target.kind === "bookmark-content-range" ? ["bookmark-update"] : ["hyperlink-update"]
3941
4303
  );
3942
4304
  case "comment-revision":
3943
4305
  return Object.freeze(
@@ -3950,7 +4312,7 @@ function commandIntentsForTarget(target) {
3950
4312
  case "preserve-only-refusal":
3951
4313
  return target.object?.objectKind === "chart" ? Object.freeze(["chart-edit", "preserve-only-refusal"]) : target.object?.objectKind === "custom-xml" ? Object.freeze(["custom-xml-update", "preserve-only-refusal"]) : target.object?.objectKind === "ole" ? Object.freeze(["embedded-content-update", "preserve-only-refusal"]) : isOpaqueObjectTarget(target) ? Object.freeze(["opaque-content-preserve", "preserve-only-refusal"]) : isImageObjectTarget(target) ? Object.freeze(["image-layout", "image-frame", "preserve-only-refusal"]) : Object.freeze(["preserve-only-refusal"]);
3952
4314
  case "table-structure":
3953
- return Object.freeze(["table-structure-action"]);
4315
+ return target.table?.operationScope === "span" ? Object.freeze(["table-structure-action", "table-merge-cells", "table-split-cell"]) : Object.freeze(["table-structure-action"]);
3954
4316
  }
3955
4317
  }
3956
4318
  function commandRefusalReasonForTarget(target) {
@@ -3961,7 +4323,10 @@ function commandRefusalReasonForTarget(target) {
3961
4323
  }
3962
4324
  return target.posture.preserveOnly ? "l07:field-update-preserve-only-refused" : "l07:field-update-command-family-unimplemented";
3963
4325
  case "link-bookmark":
3964
- return target.kind === "bookmark-anchor" ? "l07:bookmark-command-family-unimplemented" : "l07:hyperlink-command-family-unimplemented";
4326
+ if (target.kind === "bookmark-anchor") {
4327
+ return "l07:bookmark-boundary-mutation-refused";
4328
+ }
4329
+ return target.kind === "bookmark-content-range" ? "l07:bookmark-command-family-unimplemented" : "l07:hyperlink-command-family-unimplemented";
3965
4330
  case "comment-revision":
3966
4331
  return target.kind === "revision-anchor" ? "l07:revision-command-family-unimplemented" : "l07:comment-command-family-unimplemented";
3967
4332
  case "object":
@@ -4067,12 +4432,14 @@ function readbackForTarget(document, target, runtimeTextCommand) {
4067
4432
  }
4068
4433
  const leafBlock = resolveMainStoryLeafBlock(document, target.leafPath);
4069
4434
  if (!leafBlock) return void 0;
4435
+ const text = collectBlockText(leafBlock);
4070
4436
  return {
4071
- text: collectBlockText(leafBlock),
4437
+ text,
4438
+ isEmpty: text.length === 0,
4072
4439
  source: "canonical-text-leaf"
4073
4440
  };
4074
4441
  }
4075
- function projectTarget(document, target, relation, workflowBlockers) {
4442
+ function projectTarget(document, target, relation, workflowBlockers, scopeKind) {
4076
4443
  const runtimeTextCommand = runtimeTextCommandEvidence(target, workflowBlockers);
4077
4444
  const readback = readbackForTarget(document, target, runtimeTextCommand);
4078
4445
  return {
@@ -4091,11 +4458,13 @@ function projectTarget(document, target, relation, workflowBlockers) {
4091
4458
  ...target.review ? { review: target.review } : {},
4092
4459
  ...target.object ? { object: target.object } : {},
4093
4460
  ...target.table ? { table: target.table } : {},
4461
+ ...target.editableOwner ? { editableOwner: target.editableOwner } : {},
4462
+ ...target.canonicalAddress ? { canonicalAddress: target.canonicalAddress } : {},
4094
4463
  staleCheck: target.staleCheck,
4095
4464
  posture: target.posture,
4096
4465
  ...workflowBlockers.length > 0 ? { workflowBlockers: Object.freeze([...workflowBlockers]) } : {},
4097
4466
  runtimeTextCommand,
4098
- runtimeCommand: runtimeCommandEvidence(target, workflowBlockers, runtimeTextCommand),
4467
+ runtimeCommand: runtimeCommandEvidence(target, workflowBlockers, runtimeTextCommand, scopeKind),
4099
4468
  ...readback ? { readback } : {}
4100
4469
  };
4101
4470
  }
@@ -4114,7 +4483,7 @@ function deriveScopeEditableTargetEvidence(document, scope, entry, options = {})
4114
4483
  (left, right) => left.source.localeCompare(right.source) || left.blocker.localeCompare(right.blocker) || left.refusalId.localeCompare(right.refusalId)
4115
4484
  )
4116
4485
  );
4117
- return projectTarget(document, target, relation, workflowBlockers);
4486
+ return projectTarget(document, target, relation, workflowBlockers, scope.kind);
4118
4487
  }).filter((target) => target !== null).sort((a, b) => a.targetKey.localeCompare(b.targetKey));
4119
4488
  const blockers = /* @__PURE__ */ new Set();
4120
4489
  let supportedTextTargetCount = 0;
@@ -4211,42 +4580,76 @@ function verticalMergeFact(table, rowIndex, cellIndex) {
4211
4580
  role: "none"
4212
4581
  };
4213
4582
  }
4214
- function cellFact(table, rowIndex, cellIndex) {
4583
+ function textTargetsForCell(editableTargets, blockIndex, rowIndex, cellIndex) {
4584
+ if (!editableTargets) return Object.freeze([]);
4585
+ const tablePath = `main/block[${blockIndex}]`;
4586
+ return Object.freeze(
4587
+ editableTargets.entries.filter(
4588
+ (target) => target.commandFamily === "text-leaf" && target.table?.operationScope === "text" && target.table.tablePath === tablePath && target.table.rowIndex === rowIndex && target.table.cellIndex === cellIndex
4589
+ )
4590
+ );
4591
+ }
4592
+ function textState(targets, readbacks) {
4593
+ if (targets.length === 0) return "unavailable";
4594
+ if (readbacks.length === 0) {
4595
+ return targets.some((target) => target.runtimeTextCommand.status === "supported") ? "unavailable" : "blocked";
4596
+ }
4597
+ return readbacks.some((readback) => !readback.isEmpty) ? "non-empty" : "empty";
4598
+ }
4599
+ function cellFact(table, blockIndex, rowIndex, cellIndex, editableTargets) {
4215
4600
  const row = table.rows[rowIndex];
4216
4601
  const cell = row.cells[cellIndex];
4217
4602
  const merge = verticalMergeFact(table, rowIndex, cellIndex);
4603
+ const textTargets = textTargetsForCell(editableTargets, blockIndex, rowIndex, cellIndex);
4604
+ const supportedTextTargetCount = textTargets.filter(
4605
+ (target) => target.runtimeTextCommand.status === "supported"
4606
+ ).length;
4607
+ const readbacks = Object.freeze(
4608
+ textTargets.map((target) => target.readback).filter((readback) => Boolean(readback))
4609
+ );
4218
4610
  return {
4219
4611
  rowIndex,
4220
4612
  cellIndex,
4613
+ ...cell.sourceRef ? { cellSourceRef: cell.sourceRef } : {},
4221
4614
  gridColumnStart: merge.gridColumnStart,
4222
4615
  gridSpan: merge.gridSpan,
4223
4616
  verticalMerge: merge,
4224
- childBlockCount: cell.children.length
4617
+ childBlockCount: cell.children.length,
4618
+ textTargetCount: textTargets.length,
4619
+ supportedTextTargetCount,
4620
+ textState: textState(textTargets, readbacks),
4621
+ ...readbacks.length > 0 ? { textReadbacks: readbacks } : {}
4225
4622
  };
4226
4623
  }
4227
- function rowFact(table, rowIndex) {
4624
+ function rowFact(table, blockIndex, rowIndex, editableTargets) {
4228
4625
  const row = table.rows[rowIndex];
4229
4626
  return {
4230
4627
  rowIndex,
4628
+ ...row.sourceRef ? { rowSourceRef: row.sourceRef } : {},
4231
4629
  cellCount: row.cells.length,
4232
4630
  ...row.gridBefore !== void 0 ? { gridBefore: row.gridBefore } : {},
4233
4631
  ...row.gridAfter !== void 0 ? { gridAfter: row.gridAfter } : {},
4234
4632
  repeatedHeader: row.isHeader === true,
4235
4633
  splitRowAllowed: row.cantSplit !== true,
4236
- cells: Object.freeze(row.cells.map((_, cellIndex) => cellFact(table, rowIndex, cellIndex)))
4634
+ cells: Object.freeze(
4635
+ row.cells.map((_, cellIndex) => cellFact(table, blockIndex, rowIndex, cellIndex, editableTargets))
4636
+ )
4237
4637
  };
4238
4638
  }
4239
- function structureFacts(entry) {
4639
+ function structureFacts(entry, editableTargets) {
4240
4640
  const table = entry.table;
4241
- const rows = Object.freeze(table.rows.map((_, rowIndex) => rowFact(table, rowIndex)));
4641
+ const rows = Object.freeze(
4642
+ table.rows.map((_, rowIndex) => rowFact(table, entry.blockIndex, rowIndex, editableTargets))
4643
+ );
4242
4644
  return {
4645
+ ...table.sourceRef ? { sourceRef: table.sourceRef } : {},
4243
4646
  rowCount: table.rows.length,
4244
4647
  columnCount: columnCount(table),
4245
4648
  ...entry.kind === "table" ? { rows } : {},
4246
4649
  ...entry.kind === "table-row" ? { row: rows[entry.rowIndex] } : {},
4247
4650
  ...entry.kind === "table-cell" ? {
4248
4651
  row: rows[entry.rowIndex],
4249
- cell: cellFact(table, entry.rowIndex, entry.cellIndex)
4652
+ cell: cellFact(table, entry.blockIndex, entry.rowIndex, entry.cellIndex, editableTargets)
4250
4653
  } : {}
4251
4654
  };
4252
4655
  }
@@ -4285,23 +4688,33 @@ function tableAction(entry, index) {
4285
4688
  if (!family || !entry.table) return null;
4286
4689
  const status = actionStatus(entry);
4287
4690
  const blockers = actionBlockers(entry);
4691
+ const fullFidelityTableText = family === "table-text" && Boolean(entry.readback);
4692
+ const actionHandle = entry.canonicalAddress ? `table-action:${entry.canonicalAddress.addressKey}` : `table-action:${family}:${index}`;
4288
4693
  return {
4289
- handle: `table-action:${family}:${index}`,
4694
+ handle: actionHandle,
4290
4695
  family,
4291
4696
  status,
4292
- posture: status === "supported" ? "warn-and-proceed" : "hard-refusal",
4697
+ posture: status === "supported" ? fullFidelityTableText ? "supported" : "warn-and-proceed" : "hard-refusal",
4293
4698
  reason: actionReason(entry),
4294
4699
  operationScope: entry.table.operationScope,
4295
4700
  targetKind: entry.kind,
4296
4701
  relation: entry.relation,
4297
4702
  intents: entry.runtimeCommand.intents,
4703
+ ...entry.runtimeCommand.actionHandle ? { actionHandle: entry.runtimeCommand.actionHandle } : {},
4704
+ ...entry.canonicalAddress ? { canonicalAddress: entry.canonicalAddress } : {},
4705
+ ...entry.readback ? { readback: entry.readback } : {},
4298
4706
  ...blockers && blockers.length > 0 ? { blockers } : {},
4299
- ...status === "supported" ? { warnings: [`editable-target:${entry.kind}:available`] } : {},
4707
+ ...status === "supported" && !fullFidelityTableText ? { warnings: [`editable-target:${entry.kind}:available`] } : {},
4300
4708
  diagnostics: {
4301
- targetKey: entry.targetKey,
4302
- blockPath: entry.blockPath,
4303
- leafPath: entry.leafPath,
4304
- rawTableKey: entry.table.tableKey
4709
+ ...entry.table.sourceRef ? { sourceRef: entry.table.sourceRef } : {},
4710
+ ...entry.table.rowSourceRef ? { rowSourceRef: entry.table.rowSourceRef } : {},
4711
+ ...entry.table.cellSourceRef ? { cellSourceRef: entry.table.cellSourceRef } : {},
4712
+ raw: {
4713
+ targetKey: entry.targetKey,
4714
+ blockPath: entry.blockPath,
4715
+ leafPath: entry.leafPath,
4716
+ tableKey: entry.table.tableKey
4717
+ }
4305
4718
  }
4306
4719
  };
4307
4720
  }
@@ -4331,7 +4744,7 @@ function deriveScopeTableEvidence(scope, entry, options = {}) {
4331
4744
  status: "present",
4332
4745
  semanticHandle: semanticHandle(scope, entry),
4333
4746
  diagnostics: diagnostics(scope, entry),
4334
- structure: structureFacts(entry),
4747
+ structure: structureFacts(entry, options.editableTargets),
4335
4748
  actions: actions(options.editableTargets),
4336
4749
  ...fragments ? { layoutFragments: fragments } : {}
4337
4750
  };
@@ -4493,6 +4906,8 @@ function mapSemanticEntry(entry) {
4493
4906
  return null;
4494
4907
  }
4495
4908
  const targetKey = stringValue(entry.editableTargetKey);
4909
+ const editableTargetOwnerKey = stringValue(entry.editableTargetOwnerKey);
4910
+ const editableTargetOwnerReason = stringValue(entry.editableTargetOwnerReason);
4496
4911
  const blockers = stringList(entry.editableTargetBlockers);
4497
4912
  const divergenceIds = stringList(entry.layoutDivergenceIds);
4498
4913
  const layoutDivergenceObjectIds = stringList(entry.layoutDivergenceObjectIds);
@@ -4522,7 +4937,13 @@ function mapSemanticEntry(entry) {
4522
4937
  ...stringValue(entry.editableTargetCommandFamily) ? { commandFamily: stringValue(entry.editableTargetCommandFamily) } : {},
4523
4938
  ...stringValue(entry.editableTargetEditability) ? { editability: stringValue(entry.editableTargetEditability) } : {},
4524
4939
  ...blockers ? { blockers } : {},
4525
- ...stringValue(entry.editableTargetLeafPath) ? { leafPath: stringValue(entry.editableTargetLeafPath) } : {}
4940
+ ...stringValue(entry.editableTargetLeafPath) ? { leafPath: stringValue(entry.editableTargetLeafPath) } : {},
4941
+ ...editableTargetOwnerKey ? {
4942
+ editableOwner: {
4943
+ targetKey: editableTargetOwnerKey,
4944
+ reason: editableTargetOwnerReason ?? "unknown"
4945
+ }
4946
+ } : {}
4526
4947
  }
4527
4948
  } : {},
4528
4949
  ...entry.sourceIdentity ? { sourceIdentity: { ...entry.sourceIdentity } } : {}
@@ -5811,7 +6232,7 @@ function collectPreservationVerdict(inputs, blockedReasons, warnings) {
5811
6232
  }
5812
6233
  if (!document) return;
5813
6234
  const pm = positionMap ?? buildScopePositionMap(document);
5814
- const range = inputs.enumeratedScope ? resolveScopeRange(inputs.enumeratedScope, scope.handle, pm) : scope.handle.stableRef.kind === "scope-id" ? pm.markerScopes.get(scope.handle.stableRef.value) ?? null : null;
6235
+ const range = inputs.enumeratedScope ? resolveScopeRange(inputs.enumeratedScope, scope.handle, pm) : scope.handle.stableRef.kind === "scope-id" ? pm.markerScopes.get(scope.handle.stableRef.value) ?? null : rangeFromSemanticPath(scope.handle.semanticPath, pm);
5815
6236
  const verdict = computePreservationVerdict(document, range, pm);
5816
6237
  if (verdict.replaceable) return;
5817
6238
  const opaqueOptIn = inputs.preservePolicy?.opaqueFragments === true;
@@ -5843,6 +6264,134 @@ function collectCompatibilityVerdict(runtime, blockedReasons, warnings) {
5843
6264
  }
5844
6265
  }
5845
6266
  }
6267
+ function collectCapabilityVerdict(inputs, blockedReasons, warnings) {
6268
+ if (inputs.operation === "replace" && inputs.proposedContent.kind === "text") {
6269
+ for (const blocker of generatedOrLinkedContentBlockers2(inputs)) {
6270
+ blockedReasons.push(blocker);
6271
+ warnings.push({
6272
+ code: blocker,
6273
+ message: blocker === "field-generated-text" ? "target range includes generated field text; use a field/TOC command path or refuse" : "target range includes bookmark/link boundary content; use a link/bookmark command path or refuse",
6274
+ source: "capability"
6275
+ });
6276
+ }
6277
+ }
6278
+ if (inputs.scope.kind === "list-item" && inputs.operation === "replace" && inputs.proposedContent.kind === "text") {
6279
+ const code = "capability:list-item:authoritative-readback-required";
6280
+ blockedReasons.push(code);
6281
+ warnings.push({
6282
+ code,
6283
+ message: "list-item flat text replacement is blocked until the runtime validates an export-persistent list text command path",
6284
+ source: "capability"
6285
+ });
6286
+ }
6287
+ }
6288
+ function generatedOrLinkedContentBlockers2(inputs) {
6289
+ const { document, scope, positionMap } = inputs;
6290
+ if (!document) return [];
6291
+ const pm = positionMap ?? buildScopePositionMap(document);
6292
+ const range = inputs.enumeratedScope ? resolveScopeRange(inputs.enumeratedScope, scope.handle, pm) : scope.handle.stableRef.kind === "scope-id" ? pm.markerScopes.get(scope.handle.stableRef.value) ?? null : rangeFromSemanticPath(scope.handle.semanticPath, pm);
6293
+ if (!range) return [];
6294
+ const root = document.content;
6295
+ if (!root || root.type !== "doc" || !Array.isArray(root.children)) return [];
6296
+ const blockers = /* @__PURE__ */ new Set();
6297
+ if (scope.kind === "table-cell") {
6298
+ const cell = tableCellFromSemanticPath(root, scope.handle.semanticPath);
6299
+ if (cell) {
6300
+ collectGeneratedOrLinkedBlocksBlockers(cell.children, blockers);
6301
+ return [...blockers].sort();
6302
+ }
6303
+ }
6304
+ root.children.forEach((block, blockIndex) => {
6305
+ if (block.type !== "paragraph") return;
6306
+ const blockRange = pm.blocks.get(blockIndex);
6307
+ if (!blockRange || !rangesOverlapOrTouch(range, blockRange)) return;
6308
+ collectGeneratedOrLinkedInlineBlockers(block, blockIndex, pm, range, blockers);
6309
+ });
6310
+ return [...blockers].sort();
6311
+ }
6312
+ function tableCellFromSemanticPath(root, semanticPath) {
6313
+ const tableIndex = semanticPath.findIndex((part) => part === "table");
6314
+ const rowIndex = semanticPath.findIndex((part) => part === "row");
6315
+ const cellIndex = semanticPath.findIndex((part) => part === "cell");
6316
+ const tableBlockIndex = tableIndex >= 0 ? Number(semanticPath[tableIndex + 1]) : NaN;
6317
+ const rowOrdinal = rowIndex >= 0 ? Number(semanticPath[rowIndex + 1]) : NaN;
6318
+ const cellOrdinal = cellIndex >= 0 ? Number(semanticPath[cellIndex + 1]) : NaN;
6319
+ if (!Number.isInteger(tableBlockIndex) || !Number.isInteger(rowOrdinal) || !Number.isInteger(cellOrdinal)) {
6320
+ return null;
6321
+ }
6322
+ const table = root.children[tableBlockIndex];
6323
+ if (!table || table.type !== "table") return null;
6324
+ return table.rows[rowOrdinal]?.cells[cellOrdinal] ?? null;
6325
+ }
6326
+ function collectGeneratedOrLinkedBlocksBlockers(blocks, blockers) {
6327
+ for (const block of blocks) {
6328
+ if (block.type === "paragraph") {
6329
+ for (const child of block.children) {
6330
+ collectGeneratedOrLinkedNodeBlockers(child, blockers);
6331
+ }
6332
+ continue;
6333
+ }
6334
+ if (block.type === "table") {
6335
+ for (const row of block.rows) {
6336
+ for (const cell of row.cells) {
6337
+ collectGeneratedOrLinkedBlocksBlockers(cell.children, blockers);
6338
+ }
6339
+ }
6340
+ continue;
6341
+ }
6342
+ if (block.type === "sdt") {
6343
+ collectGeneratedOrLinkedBlocksBlockers(block.children, blockers);
6344
+ }
6345
+ }
6346
+ }
6347
+ function rangeFromSemanticPath(semanticPath, positionMap) {
6348
+ const paragraphIndex = semanticPath.findIndex(
6349
+ (part) => part === "paragraph" || part === "heading" || part === "list-item"
6350
+ );
6351
+ if (paragraphIndex < 0) return null;
6352
+ const rawIndex = semanticPath[paragraphIndex + 1] ?? semanticPath[semanticPath.length - 1];
6353
+ const blockIndex = rawIndex !== void 0 ? Number(rawIndex) : NaN;
6354
+ if (!Number.isInteger(blockIndex)) return null;
6355
+ return positionMap.blocks.get(blockIndex) ?? null;
6356
+ }
6357
+ function collectGeneratedOrLinkedInlineBlockers(paragraph, blockIndex, positionMap, range, blockers) {
6358
+ paragraph.children.forEach((child, inlineIndex) => {
6359
+ const inlineRange = positionMap.inlines.get(`${blockIndex}:${inlineIndex}`);
6360
+ if (!inlineRange || !rangesOverlapOrTouch(range, inlineRange)) return;
6361
+ collectGeneratedOrLinkedNodeBlockers(child, blockers);
6362
+ });
6363
+ }
6364
+ function collectGeneratedOrLinkedNodeBlockers(node, blockers) {
6365
+ switch (node.type) {
6366
+ case "field":
6367
+ blockers.add("field-generated-text");
6368
+ node.children.forEach(
6369
+ (child) => collectGeneratedOrLinkedNodeBlockers(child, blockers)
6370
+ );
6371
+ break;
6372
+ case "bookmark_start":
6373
+ case "bookmark_end":
6374
+ blockers.add("link-bookmark:bookmark-anchor");
6375
+ break;
6376
+ case "hyperlink":
6377
+ blockers.add("link-bookmark:hyperlink-text");
6378
+ node.children.forEach(
6379
+ (child) => collectGeneratedOrLinkedNodeBlockers(child, blockers)
6380
+ );
6381
+ break;
6382
+ default:
6383
+ break;
6384
+ }
6385
+ }
6386
+ function rangesOverlapOrTouch(left, right) {
6387
+ if (right.from === right.to) {
6388
+ return right.from >= left.from && right.from <= left.to;
6389
+ }
6390
+ if (left.from === left.to) {
6391
+ return left.from >= right.from && left.from <= right.to;
6392
+ }
6393
+ return left.from < right.to && right.from < left.to;
6394
+ }
5846
6395
  function collectPolicyVerdict(actionId, blockedReasons, warnings) {
5847
6396
  let policy;
5848
6397
  try {
@@ -5882,6 +6431,7 @@ function composeScopeValidation(inputs) {
5882
6431
  collectGuardVerdict(inputs.scope, inputs.runtime, blockedReasons, warnings);
5883
6432
  collectPreservationVerdict(inputs, blockedReasons, warnings);
5884
6433
  collectCompatibilityVerdict(inputs.runtime, blockedReasons, warnings);
6434
+ collectCapabilityVerdict(inputs, blockedReasons, warnings);
5885
6435
  const actionId = inputs.actionId ?? inferActionId(inputs.operation, inputs.proposedContent);
5886
6436
  const approval = collectPolicyVerdict(actionId, blockedReasons, warnings);
5887
6437
  const safe = blockedReasons.length === 0;
@@ -6124,8 +6674,10 @@ function applyScopeReplacement(inputs) {
6124
6674
  proposed.targetHandle.scopeId
6125
6675
  );
6126
6676
  const proposedText = proposed.proposedContent.kind === "text" ? proposed.proposedContent.text ?? "" : null;
6127
- const shouldVerifyReadback = inputs.sink.verifyReadback === true && proposed.preserve?.opaqueFragments === true;
6128
- const readbackFailureReason = shouldVerifyReadback && documentHashAfter === documentHashBefore && posture === "direct-edit" && !readback ? `apply-readback-unresolvable:${proposed.targetHandle.scopeId}` : shouldVerifyReadback && documentHashAfter === documentHashBefore && posture === "direct-edit" && proposed.operation === "replace" && proposedText !== null && proposedText !== resolvedScope.content.text && readback?.scope.content.text === resolvedScope.content.text ? `apply-readback-unchanged:${proposed.targetHandle.scopeId}` : void 0;
6677
+ const paragraphLikeReadback = resolvedScope.kind === "paragraph" || resolvedScope.kind === "heading" || resolvedScope.kind === "list-item";
6678
+ const shouldVerifyExactReadback = inputs.sink.verifyReadback === true && paragraphLikeReadback && resolvedScope.handle.provenance !== "marker-backed" && proposed.preserve?.opaqueFragments !== true && posture === "direct-edit" && proposed.operation === "replace" && proposedText !== null && proposedText !== resolvedScope.content.text;
6679
+ const shouldVerifyOpaqueNoopReadback = inputs.sink.verifyReadback === true && proposed.preserve?.opaqueFragments === true;
6680
+ const readbackFailureReason = shouldVerifyExactReadback && !readback ? `apply-readback-unresolvable:${proposed.targetHandle.scopeId}` : shouldVerifyExactReadback && readback?.scope.content.text !== proposedText ? readback?.scope.content.text === resolvedScope.content.text ? `apply-readback-unchanged:${proposed.targetHandle.scopeId}` : `apply-readback-mismatch:${proposed.targetHandle.scopeId}` : shouldVerifyOpaqueNoopReadback && documentHashAfter === documentHashBefore && posture === "direct-edit" && !readback ? `apply-readback-unresolvable:${proposed.targetHandle.scopeId}` : shouldVerifyOpaqueNoopReadback && documentHashAfter === documentHashBefore && posture === "direct-edit" && proposed.operation === "replace" && proposedText !== null && proposedText !== resolvedScope.content.text && readback?.scope.content.text === resolvedScope.content.text ? `apply-readback-unchanged:${proposed.targetHandle.scopeId}` : void 0;
6129
6681
  if (readbackFailureReason) {
6130
6682
  const blockers = Object.freeze([
6131
6683
  readbackFailureReason,
@@ -7283,7 +7835,8 @@ function resolveEditableTextTarget(input) {
7283
7835
  "Editable target ref does not belong to the active story."
7284
7836
  );
7285
7837
  }
7286
- const current = collectEditableTargetRefs(input.document).find(
7838
+ const currentTargets = collectEditableTargetRefs(input.document);
7839
+ const current = currentTargets.find(
7287
7840
  (candidate) => candidate.targetKey === input.target.targetKey
7288
7841
  );
7289
7842
  if (!current) {
@@ -7310,19 +7863,26 @@ function resolveEditableTextTarget(input) {
7310
7863
  `Editable target command family "${input.target.commandFamily}" is not supported by text commands.`
7311
7864
  );
7312
7865
  }
7313
- if (input.target.editability !== "editable" || input.target.posture.blockers.length > 0) {
7866
+ const dispatchTarget = resolveEditableDispatchTarget(currentTargets, current);
7867
+ if (!dispatchTarget) {
7868
+ return reject(
7869
+ "editable_target_synthetic_layout_cell",
7870
+ "Vertical-merge continuation cells are synthetic layout cells and do not have an editable restart-cell owner."
7871
+ );
7872
+ }
7873
+ if (dispatchTarget.editability !== "editable" || dispatchTarget.posture.blockers.length > 0) {
7314
7874
  return reject(
7315
7875
  "editable_target_non_editable",
7316
- `Editable target is not editable${input.target.posture.blockers.length > 0 ? `: ${input.target.posture.blockers.join(", ")}` : "."}`
7876
+ `Editable target is not editable${dispatchTarget.posture.blockers.length > 0 ? `: ${dispatchTarget.posture.blockers.join(", ")}` : "."}`
7317
7877
  );
7318
7878
  }
7319
- if (contentControlLocksTarget2(input.target)) {
7879
+ if (contentControlLocksTarget2(dispatchTarget)) {
7320
7880
  return reject(
7321
7881
  "editable_target_non_editable",
7322
7882
  "Editable target is inside a locked content control."
7323
7883
  );
7324
7884
  }
7325
- const range = locateTargetRange(input.document, input.surface, current);
7885
+ const range = locateTargetRange(input.document, input.surface, dispatchTarget);
7326
7886
  if (!range) {
7327
7887
  return reject(
7328
7888
  "editable_target_surface_unavailable",
@@ -7351,6 +7911,97 @@ function resolveEditableTextTarget(input) {
7351
7911
  range: { from: range.from, to: range.to }
7352
7912
  };
7353
7913
  }
7914
+ function resolveEditableCommandTarget(input) {
7915
+ if (!input.target) {
7916
+ return rejectCommand(
7917
+ "editable_target_required",
7918
+ "Runtime target commands require a validated editable target ref."
7919
+ );
7920
+ }
7921
+ const shapeIssues = validateEditableTargetRef(input.target);
7922
+ if (shapeIssues.length > 0) {
7923
+ return rejectCommand(
7924
+ "editable_target_malformed",
7925
+ `Editable target ref is malformed: ${shapeIssues[0]?.path ?? "$"}.`
7926
+ );
7927
+ }
7928
+ const activeStoryKey = input.activeStoryKey ?? input.target.storyKey;
7929
+ if (input.target.storyKey !== activeStoryKey) {
7930
+ return rejectCommand(
7931
+ "editable_target_wrong_story",
7932
+ "Editable target ref does not belong to the active story."
7933
+ );
7934
+ }
7935
+ if (!input.commandFamilies.includes(input.target.commandFamily)) {
7936
+ return rejectCommand(
7937
+ "editable_target_command_family_unsupported",
7938
+ `Editable target command family "${input.target.commandFamily}" is not supported by this command.`
7939
+ );
7940
+ }
7941
+ if (input.targetKinds && !input.targetKinds.includes(input.target.kind)) {
7942
+ return rejectCommand(
7943
+ "editable_target_kind_unsupported",
7944
+ `Editable target kind "${input.target.kind}" is not supported by this command.`
7945
+ );
7946
+ }
7947
+ const currentTargets = collectEditableTargetRefs(input.document);
7948
+ const current = currentTargets.find(
7949
+ (candidate) => candidate.targetKey === input.target?.targetKey
7950
+ );
7951
+ if (!current) {
7952
+ return rejectCommand(
7953
+ "editable_target_not_found",
7954
+ "Editable target ref no longer resolves in the current canonical document."
7955
+ );
7956
+ }
7957
+ if (!sameResolvedTarget2(input.target, current)) {
7958
+ return rejectCommand(
7959
+ "editable_target_stale",
7960
+ "Editable target ref is stale for the current canonical document."
7961
+ );
7962
+ }
7963
+ if (contentControlLocksTarget2(current)) {
7964
+ return rejectCommand(
7965
+ "editable_target_non_editable",
7966
+ "Editable target is inside a locked content control."
7967
+ );
7968
+ }
7969
+ if (current.posture.preserveOnly || current.posture.blockers.includes("preserve-only")) {
7970
+ return rejectCommand(
7971
+ "editable_target_preserve_only",
7972
+ "Editable target is preserve-only and cannot be mutated by runtime commands."
7973
+ );
7974
+ }
7975
+ if (!input.allowGeneratedPosture) {
7976
+ const blockers = current.posture.blockers.filter(
7977
+ (blocker) => blocker !== "unmodeled-target"
7978
+ );
7979
+ if (current.editability !== "editable" || blockers.length > 0) {
7980
+ return rejectCommand(
7981
+ "editable_target_non_editable",
7982
+ `Editable target is not editable${blockers.length > 0 ? `: ${blockers.join(", ")}` : "."}`
7983
+ );
7984
+ }
7985
+ }
7986
+ return {
7987
+ kind: "accepted",
7988
+ target: current
7989
+ };
7990
+ }
7991
+ function resolveEditableDispatchTarget(currentTargets, target) {
7992
+ if (!isSyntheticLayoutContinuationTarget(target)) return target;
7993
+ const owner = target.editableOwner;
7994
+ if (!owner) return null;
7995
+ const ownerTarget = currentTargets.find((candidate) => candidate.targetKey === owner.targetKey);
7996
+ if (!ownerTarget) return null;
7997
+ if (ownerTarget.commandFamily !== "text-leaf" || ownerTarget.storyKey !== owner.storyKey || ownerTarget.blockPath !== owner.blockPath || ownerTarget.leafPath !== owner.leafPath || ownerTarget.kind !== owner.kind) {
7998
+ return null;
7999
+ }
8000
+ return ownerTarget;
8001
+ }
8002
+ function isSyntheticLayoutContinuationTarget(target) {
8003
+ return target.commandFamily === "text-leaf" && target.table?.operationScope === "text" && target.table.verticalMerge === "continue" && target.posture.blockers.includes("synthetic-layout-cell");
8004
+ }
7354
8005
  function contentControlLocksTarget2(target) {
7355
8006
  const lock = target.contentControl?.lock;
7356
8007
  return lock !== void 0 && lock !== "unlocked" && lock !== "none";
@@ -7361,8 +8012,14 @@ function reject(code, message) {
7361
8012
  blockedReason: { code, message }
7362
8013
  };
7363
8014
  }
8015
+ function rejectCommand(code, message) {
8016
+ return {
8017
+ kind: "rejected",
8018
+ blockedReason: { code, message }
8019
+ };
8020
+ }
7364
8021
  function sameResolvedTarget2(left, right) {
7365
- return left.kind === right.kind && left.storyKey === right.storyKey && left.blockPath === right.blockPath && left.leafPath === right.leafPath && left.commandFamily === right.commandFamily && left.editability === right.editability && left.staleCheck.paragraphTextHash === right.staleCheck.paragraphTextHash && left.staleCheck.paragraphTextLength === right.staleCheck.paragraphTextLength && left.staleCheck.inlineCount === right.staleCheck.inlineCount && left.staleCheck.targetHash === right.staleCheck.targetHash && left.staleCheck.targetTextLength === right.staleCheck.targetTextLength && left.staleCheck.childCount === right.staleCheck.childCount && left.staleCheck.blockType === right.staleCheck.blockType && left.staleCheck.wordParaId === right.staleCheck.wordParaId && left.staleCheck.wordTextId === right.staleCheck.wordTextId && jsonStable(left.staleCheck.sourceRef) === jsonStable(right.staleCheck.sourceRef) && jsonStable(left.sourceRef) === jsonStable(right.sourceRef) && jsonStable(left.table) === jsonStable(right.table);
8022
+ return left.kind === right.kind && left.storyKey === right.storyKey && left.blockPath === right.blockPath && left.leafPath === right.leafPath && left.commandFamily === right.commandFamily && left.editability === right.editability && left.staleCheck.paragraphTextHash === right.staleCheck.paragraphTextHash && left.staleCheck.paragraphTextLength === right.staleCheck.paragraphTextLength && left.staleCheck.inlineCount === right.staleCheck.inlineCount && left.staleCheck.targetHash === right.staleCheck.targetHash && left.staleCheck.targetTextLength === right.staleCheck.targetTextLength && left.staleCheck.childCount === right.staleCheck.childCount && left.staleCheck.blockType === right.staleCheck.blockType && left.staleCheck.wordParaId === right.staleCheck.wordParaId && left.staleCheck.wordTextId === right.staleCheck.wordTextId && jsonStable(left.staleCheck.sourceRef) === jsonStable(right.staleCheck.sourceRef) && jsonStable(left.sourceRef) === jsonStable(right.sourceRef) && jsonStable(left.table) === jsonStable(right.table) && jsonStable(left.editableOwner) === jsonStable(right.editableOwner);
7366
8023
  }
7367
8024
  function locateTargetRange(document, surface, target) {
7368
8025
  if (target.kind === "hyperlink-text") {
@@ -7853,5 +8510,6 @@ export {
7853
8510
  parseCanonicalFragmentFromWordML,
7854
8511
  serializeFragmentToWordML,
7855
8512
  resolveEditableTextTarget,
8513
+ resolveEditableCommandTarget,
7856
8514
  resolveEditableTableStructureTarget
7857
8515
  };