@beyondwork/docx-react-component 1.0.133 → 1.0.135

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 (83) hide show
  1. package/dist/api/public-types.cjs +23 -3
  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 +2 -2
  5. package/dist/api/v3.cjs +708 -47
  6. package/dist/api/v3.d.cts +2 -2
  7. package/dist/api/v3.d.ts +2 -2
  8. package/dist/api/v3.js +4 -4
  9. package/dist/{chunk-REFHJ2FN.js → chunk-2BNXARVO.js} +3 -3
  10. package/dist/{chunk-INLRCC4N.js → chunk-4CIHTMCH.js} +2 -2
  11. package/dist/{chunk-224TSMEB.js → chunk-5CCYF333.js} +138 -42
  12. package/dist/{chunk-MQ5GAJ54.js → chunk-BJXSMPHD.js} +1 -1
  13. package/dist/{chunk-OTRVGNZQ.js → chunk-EPFVMUKF.js} +548 -3
  14. package/dist/{chunk-XBQFDBXE.js → chunk-EZFF6GKF.js} +9 -2
  15. package/dist/{chunk-S3PEKX6H.js → chunk-FGJTOFZY.js} +72 -554
  16. package/dist/{chunk-3JEE5RJU.js → chunk-GIFXKIM5.js} +612 -34
  17. package/dist/{chunk-57HTKX3P.js → chunk-H4HI6RUE.js} +1 -1
  18. package/dist/{chunk-KL4TZSZV.js → chunk-HWMPNLEF.js} +1 -1
  19. package/dist/{chunk-ZFCZ7XXH.js → chunk-NEMOQ4QR.js} +1 -1
  20. package/dist/{chunk-CVSD3UNK.js → chunk-P7XDEVS6.js} +15 -2
  21. package/dist/{chunk-QTRJLKR2.js → chunk-TSNK4ECL.js} +1 -1
  22. package/dist/{chunk-WDDFU2N2.js → chunk-UR2LW63N.js} +1 -1
  23. package/dist/core/commands/formatting-commands.d.cts +1 -1
  24. package/dist/core/commands/formatting-commands.d.ts +1 -1
  25. package/dist/core/commands/image-commands.cjs +9 -2
  26. package/dist/core/commands/image-commands.d.cts +1 -1
  27. package/dist/core/commands/image-commands.d.ts +1 -1
  28. package/dist/core/commands/image-commands.js +4 -4
  29. package/dist/core/commands/section-layout-commands.d.cts +1 -1
  30. package/dist/core/commands/section-layout-commands.d.ts +1 -1
  31. package/dist/core/commands/style-commands.d.cts +1 -1
  32. package/dist/core/commands/style-commands.d.ts +1 -1
  33. package/dist/core/commands/table-structure-commands.cjs +9 -2
  34. package/dist/core/commands/table-structure-commands.d.cts +1 -1
  35. package/dist/core/commands/table-structure-commands.d.ts +1 -1
  36. package/dist/core/commands/table-structure-commands.js +3 -3
  37. package/dist/core/commands/text-commands.cjs +9 -2
  38. package/dist/core/commands/text-commands.d.cts +1 -1
  39. package/dist/core/commands/text-commands.d.ts +1 -1
  40. package/dist/core/commands/text-commands.js +4 -4
  41. package/dist/core/selection/mapping.d.cts +1 -1
  42. package/dist/core/selection/mapping.d.ts +1 -1
  43. package/dist/core/state/editor-state.d.cts +1 -1
  44. package/dist/core/state/editor-state.d.ts +1 -1
  45. package/dist/index.cjs +1478 -636
  46. package/dist/index.d.cts +4 -4
  47. package/dist/index.d.ts +4 -4
  48. package/dist/index.js +113 -26
  49. package/dist/io/docx-session.d.cts +3 -3
  50. package/dist/io/docx-session.d.ts +3 -3
  51. package/dist/{loader-B2H99237.d.cts → loader-BQ7AB-0v.d.cts} +2 -2
  52. package/dist/{loader-DfTjqVwn.d.ts → loader-Cy6OYBfn.d.ts} +2 -2
  53. package/dist/{public-types-S8gTYwKo.d.cts → public-types-D31xKNGc.d.cts} +146 -3
  54. package/dist/{public-types-B5lOUIrP.d.ts → public-types-DqYt8GdP.d.ts} +146 -3
  55. package/dist/public-types.cjs +23 -3
  56. package/dist/public-types.d.cts +1 -1
  57. package/dist/public-types.d.ts +1 -1
  58. package/dist/public-types.js +2 -2
  59. package/dist/runtime/collab.d.cts +2 -2
  60. package/dist/runtime/collab.d.ts +2 -2
  61. package/dist/runtime/document-runtime.cjs +760 -68
  62. package/dist/runtime/document-runtime.d.cts +1 -1
  63. package/dist/runtime/document-runtime.d.ts +1 -1
  64. package/dist/runtime/document-runtime.js +10 -10
  65. package/dist/{session-CR2A1hGZ.d.cts → session-DA-F2fCw.d.cts} +2 -2
  66. package/dist/{session-CBDIOYXA.d.ts → session-DqL8H0oZ.d.ts} +2 -2
  67. package/dist/session.d.cts +4 -4
  68. package/dist/session.d.ts +4 -4
  69. package/dist/tailwind.cjs +81 -554
  70. package/dist/tailwind.d.cts +1 -1
  71. package/dist/tailwind.d.ts +1 -1
  72. package/dist/tailwind.js +5 -5
  73. package/dist/{types-yty2K-hk.d.cts → types-B2y94n5t.d.cts} +1 -1
  74. package/dist/{types-B-90ywjU.d.ts → types-SllbCtGs.d.ts} +1 -1
  75. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +11 -0
  76. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +2 -2
  77. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +2 -2
  78. package/dist/ui-tailwind/editor-surface/search-plugin.js +3 -3
  79. package/dist/ui-tailwind.cjs +81 -554
  80. package/dist/ui-tailwind.d.cts +2 -2
  81. package/dist/ui-tailwind.d.ts +2 -2
  82. package/dist/ui-tailwind.js +5 -5
  83. package/package.json +1 -1
package/dist/api/v3.cjs CHANGED
@@ -33897,16 +33897,12 @@ function replaceTextCapability(scope, context) {
33897
33897
  );
33898
33898
  }
33899
33899
  if (scope.kind === "scope") {
33900
- if (scope.workflow.effectiveMode === "suggest") {
33901
- return unsupported(
33902
- "compile-refused:scope:multi-paragraph-suggesting-not-implemented",
33903
- ["compile-refused:scope:multi-paragraph-suggesting-not-implemented"],
33904
- ["guard:suggest-mode", ...evidenceWarnings(context)]
33905
- );
33906
- }
33907
33900
  return supported(
33908
33901
  "compile-supported:scope:multi-paragraph-text-replace",
33909
- evidenceWarnings(context)
33902
+ [
33903
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
33904
+ ...evidenceWarnings(context)
33905
+ ]
33910
33906
  );
33911
33907
  }
33912
33908
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
@@ -33960,34 +33956,24 @@ function replaceFragmentCapability(scope, context) {
33960
33956
  );
33961
33957
  }
33962
33958
  if (scope.kind === "scope") {
33963
- if (scope.workflow.effectiveMode === "suggest") {
33964
- return unsupported(
33965
- "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented",
33966
- [
33967
- "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented"
33968
- ],
33969
- ["guard:suggest-mode", ...evidenceWarnings(context)]
33970
- );
33971
- }
33972
33959
  return supported(
33973
33960
  "compile-supported:scope:multi-paragraph-fragment-replace",
33974
- evidenceWarnings(context)
33961
+ [
33962
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
33963
+ ...evidenceWarnings(context)
33964
+ ]
33975
33965
  );
33976
33966
  }
33977
33967
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
33978
33968
  const reason = `compile-refused:${scope.kind}`;
33979
33969
  return unsupported(reason, [reason], evidenceWarnings(context));
33980
33970
  }
33981
- if (scope.workflow.effectiveMode === "suggest") {
33982
- return unsupported(
33983
- `compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
33984
- [`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
33985
- ["guard:suggest-mode"]
33986
- );
33987
- }
33988
33971
  return supported(
33989
- "compile-supported:paragraph-like:fragment-replace",
33990
- evidenceWarnings(context)
33972
+ scope.workflow.effectiveMode === "suggest" ? "compile-supported:paragraph-like:fragment-replace-tracked" : "compile-supported:paragraph-like:fragment-replace",
33973
+ [
33974
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
33975
+ ...evidenceWarnings(context)
33976
+ ]
33991
33977
  );
33992
33978
  }
33993
33979
  function formattingCapability(scope, context) {
@@ -35262,6 +35248,14 @@ function supportedNonTextCommandEvidence(target) {
35262
35248
  if (target.commandFamily === "link-bookmark") {
35263
35249
  return linkBookmarkCommandEvidence(target, []);
35264
35250
  }
35251
+ if (target.commandFamily === "object" && isImageObjectTarget(target) && typeof target.object?.mediaId === "string" && target.object.mediaId.length > 0 && onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
35252
+ return {
35253
+ status: "supported",
35254
+ commandFamily: target.commandFamily,
35255
+ intents: commandIntentsForTarget(target),
35256
+ reason: "l07:image-layout-target-supported"
35257
+ };
35258
+ }
35265
35259
  return null;
35266
35260
  }
35267
35261
  function onlyBlockers(blockers, allowed) {
@@ -39079,10 +39073,16 @@ var FormattingContextImpl = class {
39079
39073
  // surface-projection calls `resolveParagraphCascade` on the same
39080
39074
  // paragraph via `resolveParagraph` + numbering resolve + marker-rPr;
39081
39075
  // each of those re-walked the style catalog before.
39082
- paragraphCascadeCache = /* @__PURE__ */ new WeakMap();
39076
+ //
39077
+ // Defaults to a per-context instance, but the runtime can supply a
39078
+ // persistent `WeakMap` via `FormattingContextOptions.paragraphCascadeCache`
39079
+ // so unchanged paragraphs don't re-cascade on every keystroke. See the
39080
+ // contract on the option for invalidation semantics.
39081
+ paragraphCascadeCache;
39083
39082
  constructor(doc, opts) {
39084
39083
  this.doc = doc;
39085
39084
  this.opts = opts;
39085
+ this.paragraphCascadeCache = opts.paragraphCascadeCache ?? /* @__PURE__ */ new WeakMap();
39086
39086
  const canonicalTheme = doc.subParts?.canonicalTheme;
39087
39087
  this.theme = opts.themeResolver ?? (canonicalTheme ? new ThemeColorResolver(canonicalTheme) : void 0);
39088
39088
  this.numbering = opts.numberingPrefixResolver ?? createNumberingPrefixResolver(doc.numbering);
@@ -40380,11 +40380,9 @@ function compileParagraphReplacement(entry, proposed, options) {
40380
40380
  };
40381
40381
  }
40382
40382
  if (proposed.proposedContent.kind === "structured") {
40383
- if (options.posture === "suggest-mode") {
40384
- return null;
40385
- }
40386
40383
  const fragment = proposed.proposedContent.structured;
40387
40384
  if (!isStructuredReplacementContent(fragment)) return null;
40385
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
40388
40386
  const blockCount = fragment.blocks.length;
40389
40387
  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}`;
40390
40388
  const actionVerb = proposed.operation === "insert-before" ? "insert before" : proposed.operation === "insert-after" ? "insert after" : "replace";
@@ -40395,8 +40393,8 @@ function compileParagraphReplacement(entry, proposed, options) {
40395
40393
  operation: proposed.operation,
40396
40394
  steps: Object.freeze([
40397
40395
  {
40398
- kind: "fragment-replace",
40399
- summary: actionSummary,
40396
+ kind: stepKind,
40397
+ summary: stepKind === "fragment-replace" ? actionSummary : `suggest-mode ${actionSummary}`,
40400
40398
  ...textLeafEditableTargetHint(entry, blockRange) ? { editableTargetHint: textLeafEditableTargetHint(entry, blockRange) } : {},
40401
40399
  range: { from: operationRange.from, to: operationRange.to },
40402
40400
  fragment
@@ -40483,7 +40481,7 @@ function compileScopeKind(entry, options) {
40483
40481
  };
40484
40482
  }
40485
40483
  function compileScopeReplacement(entry, proposed, options) {
40486
- if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace" || options.posture !== "direct-edit") {
40484
+ if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace") {
40487
40485
  return null;
40488
40486
  }
40489
40487
  const markerRange = buildScopePositionMap(options.document).markerScopes.get(
@@ -40492,14 +40490,15 @@ function compileScopeReplacement(entry, proposed, options) {
40492
40490
  if (!markerRange) return null;
40493
40491
  if (proposed.proposedContent.kind === "text") {
40494
40492
  const text = proposed.proposedContent.text ?? "";
40493
+ const stepKind = options.posture === "suggest-mode" ? "text-insert-tracked" : "text-replace";
40495
40494
  return {
40496
40495
  scopeId: entry.handle.scopeId,
40497
40496
  targetKind: "scope",
40498
40497
  operation: proposed.operation,
40499
40498
  steps: Object.freeze([
40500
40499
  {
40501
- kind: "text-replace",
40502
- summary: `replace multi-paragraph scope ${entry.handle.scopeId} text [${markerRange.from}..${markerRange.to}] (len ${text.length})`,
40500
+ kind: stepKind,
40501
+ 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})`,
40503
40502
  range: { from: markerRange.from, to: markerRange.to },
40504
40503
  text,
40505
40504
  ...proposed.formatting ? { formatting: proposed.formatting } : {}
@@ -40512,14 +40511,15 @@ function compileScopeReplacement(entry, proposed, options) {
40512
40511
  if (proposed.proposedContent.kind === "structured") {
40513
40512
  const fragment = proposed.proposedContent.structured;
40514
40513
  if (!isStructuredReplacementContent(fragment)) return null;
40514
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
40515
40515
  return {
40516
40516
  scopeId: entry.handle.scopeId,
40517
40517
  targetKind: "scope",
40518
40518
  operation: proposed.operation,
40519
40519
  steps: Object.freeze([
40520
40520
  {
40521
- kind: "fragment-replace",
40522
- summary: `replace multi-paragraph scope ${entry.handle.scopeId} with structured fragment [${markerRange.from}..${markerRange.to}] (${fragment.blocks.length} block(s))`,
40521
+ kind: stepKind,
40522
+ 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))`,
40523
40523
  range: { from: markerRange.from, to: markerRange.to },
40524
40524
  fragment
40525
40525
  }
@@ -40901,6 +40901,99 @@ function paragraphFirstMarkerStart(paragraph, knownScopeIds) {
40901
40901
  }
40902
40902
  return null;
40903
40903
  }
40904
+ function paragraphHasMarkerEnd(paragraph, scopeId) {
40905
+ return paragraph.children.some(
40906
+ (child) => child.type === "scope_marker_end" && child.scopeId === scopeId
40907
+ );
40908
+ }
40909
+ function paragraphSameParagraphMarkerScopeId(paragraph, knownScopeIds) {
40910
+ const markerScopeId = paragraphFirstMarkerStart(paragraph, knownScopeIds);
40911
+ if (!markerScopeId) return null;
40912
+ return paragraphHasMarkerEnd(paragraph, markerScopeId) ? markerScopeId : null;
40913
+ }
40914
+ function markerStableRefOverride(markerScopeId, semanticPath, overlay) {
40915
+ const hint = stableRefHintForScopeId(markerScopeId, overlay);
40916
+ if (hint === "semantic-path") {
40917
+ return {
40918
+ kind: "semantic-path",
40919
+ value: semanticPath.join("/")
40920
+ };
40921
+ }
40922
+ return { kind: "scope-id", value: markerScopeId };
40923
+ }
40924
+ function enumerateNestedMarkerBackedParagraphs(blocks, input) {
40925
+ const out = [];
40926
+ for (let childIndex = 0; childIndex < blocks.length; childIndex += 1) {
40927
+ const block = blocks[childIndex];
40928
+ if (!block) continue;
40929
+ if (block.type === "paragraph") {
40930
+ const markerScopeId = paragraphSameParagraphMarkerScopeId(
40931
+ block,
40932
+ input.knownOverlayScopeIds
40933
+ );
40934
+ if (!markerScopeId) continue;
40935
+ const kind = detectParagraphKind(block);
40936
+ const semanticPath = [
40937
+ ...input.semanticPrefix,
40938
+ kind,
40939
+ String(childIndex)
40940
+ ];
40941
+ const handle = buildHandle(
40942
+ markerScopeId,
40943
+ input.documentId,
40944
+ semanticPath,
40945
+ "marker-backed",
40946
+ "marker-backed",
40947
+ input.parentScopeId,
40948
+ markerStableRefOverride(markerScopeId, semanticPath, input.overlay)
40949
+ );
40950
+ out.push({
40951
+ kind,
40952
+ handle,
40953
+ paragraph: block,
40954
+ blockIndex: input.rootBlockIndex,
40955
+ classifications: input.classificationIndex.get(markerScopeId) ?? Object.freeze([])
40956
+ });
40957
+ continue;
40958
+ }
40959
+ if (block.type === "sdt") {
40960
+ out.push(
40961
+ ...enumerateNestedMarkerBackedParagraphs(block.children, {
40962
+ ...input,
40963
+ semanticPrefix: [
40964
+ ...input.semanticPrefix,
40965
+ "sdt",
40966
+ String(childIndex)
40967
+ ]
40968
+ })
40969
+ );
40970
+ continue;
40971
+ }
40972
+ if (block.type === "table") {
40973
+ for (let rowIdx = 0; rowIdx < block.rows.length; rowIdx += 1) {
40974
+ const row = block.rows[rowIdx];
40975
+ for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx += 1) {
40976
+ const cell = row.cells[cellIdx];
40977
+ out.push(
40978
+ ...enumerateNestedMarkerBackedParagraphs(cell.children, {
40979
+ ...input,
40980
+ semanticPrefix: [
40981
+ ...input.semanticPrefix,
40982
+ "table",
40983
+ String(childIndex),
40984
+ "row",
40985
+ String(rowIdx),
40986
+ "cell",
40987
+ String(cellIdx)
40988
+ ]
40989
+ })
40990
+ );
40991
+ }
40992
+ }
40993
+ }
40994
+ }
40995
+ return out;
40996
+ }
40904
40997
  function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
40905
40998
  const out = [];
40906
40999
  for (let i = 0; i < paragraph.children.length; i += 1) {
@@ -41183,6 +41276,17 @@ function enumerateScopes(document2, inputs = {}) {
41183
41276
  cellIndex: cellIdx,
41184
41277
  classifications: Object.freeze([])
41185
41278
  });
41279
+ for (const nested of enumerateNestedMarkerBackedParagraphs(cell.children, {
41280
+ documentId,
41281
+ rootBlockIndex: index,
41282
+ semanticPrefix: cellSemanticPath,
41283
+ parentScopeId: cellScopeId,
41284
+ knownOverlayScopeIds,
41285
+ classificationIndex,
41286
+ overlay: inputs.overlay
41287
+ })) {
41288
+ results.push(nested);
41289
+ }
41186
41290
  }
41187
41291
  }
41188
41292
  }
@@ -43381,9 +43485,7 @@ function applyScopeReplacement(inputs) {
43381
43485
  });
43382
43486
  if (!plan) {
43383
43487
  const paragraphLike = resolvedScope.kind === "paragraph" || resolvedScope.kind === "heading" || resolvedScope.kind === "list-item";
43384
- const blockers = resolvedScope.kind === "scope" && posture === "suggest-mode" ? [
43385
- proposed.proposedContent.kind === "structured" ? "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented" : "compile-refused:scope:multi-paragraph-suggesting-not-implemented"
43386
- ] : resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
43488
+ const blockers = resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
43387
43489
  `compile-refused:scope:operation-not-implemented:${proposed.operation}`
43388
43490
  ] : resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
43389
43491
  proposed.proposedContent.kind === "structured" ? "fragment" : "text"
@@ -45044,6 +45146,7 @@ var PICTURE_EFFECT_SCHEME_ALIASES = {
45044
45146
  bg2: "lt2"
45045
45147
  };
45046
45148
  var EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH = /* @__PURE__ */ new Map();
45149
+ var NO_EDITABLE_TARGETS_INDEX = EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH;
45047
45150
  function indexEditableTargetsByBlockPath(document2) {
45048
45151
  const targets = collectEditableTargetRefs(document2);
45049
45152
  if (targets.length === 0) return EMPTY_EDITABLE_TARGETS_BY_BLOCK_PATH;
@@ -45093,7 +45196,8 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
45093
45196
  const formattingContext = createFormattingContext(document2, {
45094
45197
  ...options.revisionMarkupMode ? { revisionMarkupMode: options.revisionMarkupMode } : {},
45095
45198
  ...options.getEffectiveMarkupMode ? { getEffectiveMarkupMode: options.getEffectiveMarkupMode } : {},
45096
- ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
45199
+ ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {},
45200
+ ...options.paragraphCascadeCache ? { paragraphCascadeCache: options.paragraphCascadeCache } : {}
45097
45201
  });
45098
45202
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
45099
45203
  const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
@@ -50037,7 +50141,8 @@ function buildResolvedSections(document2) {
50037
50141
  const mainSurface = createEditorSurfaceSnapshot(
50038
50142
  document2,
50039
50143
  createSelectionSnapshot(0, 0),
50040
- MAIN_STORY_TARGET
50144
+ MAIN_STORY_TARGET,
50145
+ { editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX }
50041
50146
  );
50042
50147
  const sections = [];
50043
50148
  let sectionStart = 0;
@@ -56457,6 +56562,8 @@ function buildSearchPattern(query, options = {}) {
56457
56562
  function findSearchMatches(text, query, options = {}) {
56458
56563
  const pattern = buildSearchPattern(query, options);
56459
56564
  if (!pattern) return [];
56565
+ const limit = normalizeSearchLimit(options.limit);
56566
+ if (limit === 0) return [];
56460
56567
  const results = [];
56461
56568
  let match;
56462
56569
  pattern.lastIndex = 0;
@@ -56467,12 +56574,21 @@ function findSearchMatches(text, query, options = {}) {
56467
56574
  text: match[0],
56468
56575
  index: results.length
56469
56576
  });
56577
+ if (results.length >= limit) {
56578
+ break;
56579
+ }
56470
56580
  if (match[0].length === 0) {
56471
56581
  pattern.lastIndex += 1;
56472
56582
  }
56473
56583
  }
56474
56584
  return results;
56475
56585
  }
56586
+ function normalizeSearchLimit(limit) {
56587
+ if (limit === void 0 || !Number.isFinite(limit)) {
56588
+ return Number.POSITIVE_INFINITY;
56589
+ }
56590
+ return Math.max(0, Math.floor(limit));
56591
+ }
56476
56592
  function createSearchExcerpt(text, from, to, radius = 24) {
56477
56593
  const safeFrom = Math.max(0, Math.min(from, text.length));
56478
56594
  const safeTo = Math.max(safeFrom, Math.min(to, text.length));
@@ -60856,6 +60972,216 @@ function createOutlineFamily(runtime) {
60856
60972
  };
60857
60973
  }
60858
60974
 
60975
+ // src/api/v3/ai/object.ts
60976
+ var applyObjectActionMetadata = {
60977
+ name: "ai.applyObjectAction",
60978
+ status: "live-with-adapter",
60979
+ sourceLayer: "runtime-core",
60980
+ liveEvidence: {
60981
+ runnerTest: "test/api/v3/ai/ai-object-actions.test.ts",
60982
+ commit: "refactor-09-object-actions"
60983
+ },
60984
+ uxIntent: {
60985
+ uiVisible: true,
60986
+ expectsUxResponse: "inline-change",
60987
+ expectedDelta: "image object layout changes"
60988
+ },
60989
+ agentMetadata: {
60990
+ readOrMutate: "mutate",
60991
+ boundedScope: "scope",
60992
+ auditCategory: "object-action-apply",
60993
+ contextPromptShape: "Apply an image object layout operation to an opaque actionHandle from ai.listObjectActions; preserve-only objects return typed blockers."
60994
+ },
60995
+ stateClass: "A-canonical",
60996
+ persistsTo: "canonical",
60997
+ broadcastsVia: "crdt",
60998
+ rwdReference: "\xA7AI API \xA7 ai.applyObjectAction"
60999
+ };
61000
+ function createObjectActionFamily(runtime) {
61001
+ const compiler = createScopeCompilerService(runtime);
61002
+ return {
61003
+ listObjectActions(input) {
61004
+ const bundle = compiler.compileBundleById(input.handle.scopeId, input.nowUtc);
61005
+ if (!bundle) {
61006
+ return {
61007
+ scopeId: input.handle.scopeId,
61008
+ actions: [],
61009
+ blockers: [`scope-not-resolvable:${input.handle.scopeId}`]
61010
+ };
61011
+ }
61012
+ const seed = documentSeed2(runtime);
61013
+ return {
61014
+ scopeId: input.handle.scopeId,
61015
+ actions: bundle.evidence.editableTargets?.entries.filter(isSupportedImageObjectEvidence).map((entry) => projectObjectDescriptor(runtime, seed, entry)) ?? []
61016
+ };
61017
+ },
61018
+ applyObjectAction(input) {
61019
+ emitUxResponse(runtime, {
61020
+ apiFn: applyObjectActionMetadata.name,
61021
+ intent: "object-action-apply",
61022
+ mockOrLive: applyObjectActionMetadata.status,
61023
+ uiVisible: true,
61024
+ expectedDelta: applyObjectActionMetadata.uxIntent.expectedDelta
61025
+ });
61026
+ const nowUtc = currentAuditTimestamp(runtime);
61027
+ const proposalId = input.proposalId ?? mockId(documentSeed2(runtime), `object-action-${input.actionHandle || "missing-handle"}-${input.operation.kind}`);
61028
+ if (!input.actionHandle) {
61029
+ return blockedResult2(input, proposalId, {
61030
+ code: "object-action-handle-required",
61031
+ category: "unresolved-target",
61032
+ message: "Object actions require an actionHandle from ai.listObjectActions.",
61033
+ nextStep: "Call ai.listObjectActions for the owning scope and retry with one returned actionHandle.",
61034
+ operation: input.operation.kind
61035
+ });
61036
+ }
61037
+ const target = resolveObjectActionHandle(runtime, documentSeed2(runtime), input.actionHandle);
61038
+ if (!target) {
61039
+ return blockedResult2(input, proposalId, {
61040
+ code: `object-action-handle-not-found:${input.actionHandle}`,
61041
+ category: "unresolved-target",
61042
+ message: "No current object action matches the supplied opaque action handle.",
61043
+ nextStep: "Refresh the scope bundle or call ai.listObjectActions before retrying.",
61044
+ actionHandle: input.actionHandle,
61045
+ operation: input.operation.kind
61046
+ });
61047
+ }
61048
+ if (!isSupportedImageTarget(target)) {
61049
+ return blockedResult2(input, proposalId, {
61050
+ code: "object-action-image-target-required",
61051
+ category: "blocked-target",
61052
+ message: "The supplied object action handle no longer identifies a mutable image target.",
61053
+ nextStep: "Refresh object actions; chart, OLE, custom XML, opaque, and stale object targets remain refused.",
61054
+ actionHandle: input.actionHandle,
61055
+ operation: input.operation.kind
61056
+ });
61057
+ }
61058
+ const mediaId = target.object?.mediaId;
61059
+ if (!mediaId) {
61060
+ return blockedResult2(input, proposalId, {
61061
+ code: "object-action-media-id-required",
61062
+ category: "blocked-target",
61063
+ message: "Image object layout mutation requires a mediaId-backed target.",
61064
+ nextStep: "Retry only with an image action handle whose readback includes mediaId.",
61065
+ actionHandle: input.actionHandle,
61066
+ operation: input.operation.kind
61067
+ });
61068
+ }
61069
+ const before = runtime.getCanonicalDocument();
61070
+ runtime.dispatch({
61071
+ type: "image.set-layout",
61072
+ mediaId,
61073
+ dimensions: {
61074
+ widthEmu: input.operation.dimensions.widthEmu,
61075
+ heightEmu: input.operation.dimensions.heightEmu
61076
+ },
61077
+ origin: { source: "api", timestamp: nowUtc }
61078
+ });
61079
+ const changed = runtime.getCanonicalDocument() !== before;
61080
+ if (!changed) {
61081
+ return blockedResult2(input, proposalId, {
61082
+ code: `object-action-noop:${input.operation.kind}:${input.actionHandle}`,
61083
+ category: "runtime-noop",
61084
+ message: "The runtime accepted the image target but the object operation produced no document change.",
61085
+ nextStep: "Refresh object actions and verify the image media item still exists before retrying.",
61086
+ actionHandle: input.actionHandle,
61087
+ operation: input.operation.kind
61088
+ });
61089
+ }
61090
+ return {
61091
+ proposalId,
61092
+ applied: true,
61093
+ changed: true,
61094
+ actionHandle: input.actionHandle,
61095
+ operation: input.operation.kind,
61096
+ commandReference: {
61097
+ command: "image.set-layout",
61098
+ actorId: input.actorId ?? "v3-ai-api",
61099
+ origin: input.origin ?? "agent",
61100
+ emittedAtUtc: nowUtc
61101
+ },
61102
+ readback: objectReadback(runtime, target),
61103
+ posture: "supported",
61104
+ support: objectActionSupport()
61105
+ };
61106
+ }
61107
+ };
61108
+ }
61109
+ function isSupportedImageObjectEvidence(entry) {
61110
+ return entry.kind === "object-anchor" && entry.commandFamily === "object" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("image-layout") && isImageKind(entry.object?.objectKind) && typeof entry.object?.mediaId === "string";
61111
+ }
61112
+ function projectObjectDescriptor(runtime, seed, entry) {
61113
+ return {
61114
+ actionHandle: objectActionHandle(seed, entry.targetKey),
61115
+ objectKind: entry.object?.objectKind ?? "unknown",
61116
+ targetKind: entry.kind,
61117
+ relation: entry.relation,
61118
+ callableOperations: ["set-image-layout"],
61119
+ supportedOperations: ["set-image-layout"],
61120
+ readback: objectReadback(runtime, entry),
61121
+ reason: entry.runtimeCommand.reason
61122
+ };
61123
+ }
61124
+ function resolveObjectActionHandle(runtime, seed, actionHandle) {
61125
+ return collectEditableTargetRefs(runtime.getCanonicalDocument()).find(
61126
+ (target) => objectActionHandle(seed, target.targetKey) === actionHandle && isSupportedImageTarget(target)
61127
+ ) ?? null;
61128
+ }
61129
+ function isSupportedImageTarget(target) {
61130
+ return target.kind === "object-anchor" && target.commandFamily === "object" && isImageKind(target.object?.objectKind) && typeof target.object?.mediaId === "string" && target.object.mediaId.length > 0 && onlyObjectActionBlockers(target.posture.blockers, ["unmodeled-target"]);
61131
+ }
61132
+ function isImageKind(kind) {
61133
+ return kind === "legacy-image" || kind === "picture";
61134
+ }
61135
+ function onlyObjectActionBlockers(blockers, allowed) {
61136
+ const allowedSet = new Set(allowed);
61137
+ return blockers.every((blocker2) => allowedSet.has(blocker2));
61138
+ }
61139
+ function objectReadback(runtime, target) {
61140
+ const mediaId = target.object?.mediaId;
61141
+ if (!mediaId) return {};
61142
+ const media = runtime.getCanonicalDocument().media.items[mediaId];
61143
+ return {
61144
+ mediaId,
61145
+ ...media?.widthEmu !== void 0 ? { widthEmu: media.widthEmu } : {},
61146
+ ...media?.heightEmu !== void 0 ? { heightEmu: media.heightEmu } : {}
61147
+ };
61148
+ }
61149
+ function blockedResult2(input, proposalId, detail) {
61150
+ return {
61151
+ proposalId,
61152
+ applied: false,
61153
+ changed: false,
61154
+ actionHandle: input.actionHandle,
61155
+ operation: input.operation.kind,
61156
+ reason: detail.code,
61157
+ blockers: [detail.code],
61158
+ blockerDetails: [detail]
61159
+ };
61160
+ }
61161
+ function objectActionSupport() {
61162
+ return {
61163
+ undo: "supported",
61164
+ replay: "supported",
61165
+ exportReopen: "supported",
61166
+ audit: "command-reference"
61167
+ };
61168
+ }
61169
+ function objectActionHandle(seed, targetKey) {
61170
+ return `object-action:${hashOpaque2(`${seed}::${targetKey}`)}`;
61171
+ }
61172
+ function documentSeed2(runtime) {
61173
+ const state = runtime.getSessionState();
61174
+ return state.documentId ?? "document";
61175
+ }
61176
+ function hashOpaque2(input) {
61177
+ let h = 2166136261;
61178
+ for (let i = 0; i < input.length; i += 1) {
61179
+ h ^= input.charCodeAt(i);
61180
+ h = Math.imul(h, 16777619);
61181
+ }
61182
+ return (h >>> 0).toString(36).padStart(7, "0");
61183
+ }
61184
+
60859
61185
  // src/api/v3/ai/actions.ts
60860
61186
  function actionMethodMetadata(method, readOrMutate, auditCategory, contextPromptShape, uxIntent) {
60861
61187
  return {
@@ -60900,6 +61226,17 @@ var locateAllMetadata = actionMethodMetadata(
60900
61226
  "Find scope/table-text targets by query; table text matches include readback {text,isEmpty}.",
60901
61227
  { uiVisible: false, expectsUxResponse: "none" }
60902
61228
  );
61229
+ var createPlaceholderScopesMetadata = actionMethodMetadata(
61230
+ "createPlaceholderScopes",
61231
+ "mutate",
61232
+ "actions-placeholder-scopes",
61233
+ "Create marker-backed workflow scopes for bracketed placeholder matches in paragraphs, tables, and lists using one bounded regex search.",
61234
+ {
61235
+ uiVisible: true,
61236
+ expectsUxResponse: "scope-created",
61237
+ expectedDelta: "placeholder text ranges become workflow scopes"
61238
+ }
61239
+ );
60903
61240
  var rewriteMetadata = actionMethodMetadata(
60904
61241
  "rewrite",
60905
61242
  "mutate",
@@ -61112,6 +61449,7 @@ var ACTION_METHODS = Object.freeze([
61112
61449
  "discover",
61113
61450
  "locate",
61114
61451
  "locateAll",
61452
+ "createPlaceholderScopes",
61115
61453
  "rewrite",
61116
61454
  "rewriteAll",
61117
61455
  "insertText",
@@ -61137,6 +61475,8 @@ var DEFAULT_LOCATE_LIMIT = 20;
61137
61475
  var DEFAULT_REWRITE_ALL_LIMIT = 10;
61138
61476
  var DEFAULT_TABLE_TEXT_SCOPE_LIMIT = 3;
61139
61477
  var DEFAULT_PLAN_STEP_LIMIT = 20;
61478
+ var DEFAULT_PLACEHOLDER_QUERY = "\\[[^\\[\\]]{1,200}\\]";
61479
+ var DEFAULT_PLACEHOLDER_LIMIT = 200;
61140
61480
  function createActionsFamily(runtime) {
61141
61481
  const category = {
61142
61482
  discover(input) {
@@ -61191,6 +61531,9 @@ function createActionsFamily(runtime) {
61191
61531
  locateAll(input) {
61192
61532
  return locateAll(runtime, input);
61193
61533
  },
61534
+ createPlaceholderScopes(input) {
61535
+ return createPlaceholderScopes(runtime, input ?? {});
61536
+ },
61194
61537
  rewrite(input) {
61195
61538
  if (input.text === void 0) {
61196
61539
  return blockedApply(
@@ -61959,6 +62302,169 @@ function locateAll(runtime, input) {
61959
62302
  ...matches.length === 0 ? { blockers: Object.freeze([`actions:locate:not-found:${input.query}`]) } : {}
61960
62303
  };
61961
62304
  }
62305
+ function createPlaceholderScopes(runtime, input) {
62306
+ const query = input.query ?? DEFAULT_PLACEHOLDER_QUERY;
62307
+ if (!query) {
62308
+ const detail = blocker(
62309
+ "actions:placeholder-scopes:query-required",
62310
+ "input",
62311
+ "Placeholder scope creation requires a non-empty query.",
62312
+ "Retry with a non-empty query string or omit query to use the bracketed-placeholder default."
62313
+ );
62314
+ return {
62315
+ status: "blocked",
62316
+ totalHits: 0,
62317
+ created: 0,
62318
+ blocked: 0,
62319
+ scopes: Object.freeze([]),
62320
+ blockers: Object.freeze([detail.code]),
62321
+ blockerDetails: Object.freeze([detail])
62322
+ };
62323
+ }
62324
+ const limit = Math.max(0, input.limit ?? DEFAULT_PLACEHOLDER_LIMIT);
62325
+ let hits;
62326
+ try {
62327
+ hits = runtime.findAllText(query, {
62328
+ regex: input.regex ?? true,
62329
+ matchCase: input.matchCase ?? true,
62330
+ limit
62331
+ });
62332
+ } catch {
62333
+ const detail = blocker(
62334
+ "actions:placeholder-scopes:invalid-query",
62335
+ "input",
62336
+ "The placeholder query could not be compiled or searched.",
62337
+ "Retry with a valid literal query or JavaScript regex pattern."
62338
+ );
62339
+ return {
62340
+ status: "blocked",
62341
+ totalHits: 0,
62342
+ created: 0,
62343
+ blocked: 0,
62344
+ scopes: Object.freeze([]),
62345
+ blockers: Object.freeze([detail.code]),
62346
+ blockerDetails: Object.freeze([detail])
62347
+ };
62348
+ }
62349
+ if (hits.length === 0) {
62350
+ return {
62351
+ status: "not-found",
62352
+ totalHits: 0,
62353
+ created: 0,
62354
+ blocked: 0,
62355
+ scopes: Object.freeze([]),
62356
+ blockers: Object.freeze([`actions:placeholder-scopes:not-found:${query}`])
62357
+ };
62358
+ }
62359
+ const surface = runtime.getRenderSnapshot().surface;
62360
+ const orderedHits = hits.filter((hit) => hit.kind === "range").map((hit, originalIndex) => ({
62361
+ hit,
62362
+ originalIndex,
62363
+ text: surface ? textForSurfaceRange(surface.blocks, hit.from, hit.to) : ""
62364
+ })).sort((a, b) => b.hit.from - a.hit.from || b.originalIndex - a.originalIndex);
62365
+ if (orderedHits.length === 0) {
62366
+ const detail = blocker(
62367
+ "actions:placeholder-scopes:no-range-hits",
62368
+ "unsupported",
62369
+ "The placeholder query matched only projections that cannot be converted into workflow ranges.",
62370
+ "Retry with a text query that resolves to concrete document text ranges."
62371
+ );
62372
+ return {
62373
+ status: "blocked",
62374
+ totalHits: hits.length,
62375
+ created: 0,
62376
+ blocked: hits.length,
62377
+ scopes: Object.freeze([]),
62378
+ blockers: Object.freeze([detail.code]),
62379
+ blockerDetails: Object.freeze([detail])
62380
+ };
62381
+ }
62382
+ const compiler = createScopeCompilerService(runtime);
62383
+ const byOriginalIndex = /* @__PURE__ */ new Map();
62384
+ for (const item of orderedHits) {
62385
+ const text = item.text || query;
62386
+ const label = input.labelPrefix === void 0 ? text : `${input.labelPrefix} ${item.originalIndex + 1}`;
62387
+ const result = createScopeFromAnchor(runtime, {
62388
+ anchor: { from: item.hit.from, to: item.hit.to },
62389
+ mode: input.mode ?? "edit",
62390
+ label,
62391
+ ...input.visibility ? { visibility: input.visibility } : {},
62392
+ ...input.guardPolicy ? { guardPolicy: input.guardPolicy } : {},
62393
+ ...input.assoc ? { assoc: input.assoc } : {},
62394
+ ...input.stableRefHint ? { stableRefHint: input.stableRefHint } : {}
62395
+ });
62396
+ if (result.status === "created") {
62397
+ const compiled = compiler.compileScopeById(result.scopeId)?.scope;
62398
+ byOriginalIndex.set(item.originalIndex, {
62399
+ status: "created",
62400
+ text,
62401
+ excerpt: excerpt(text),
62402
+ scopeId: result.scopeId,
62403
+ ...compiled?.handle ? { handle: compiled.handle } : {}
62404
+ });
62405
+ continue;
62406
+ }
62407
+ const detail = blockerWithOwner(
62408
+ `actions:placeholder-scopes:create-refused:${result.reason}`,
62409
+ "blocked",
62410
+ "The placeholder match could not be converted into a workflow scope.",
62411
+ "Refresh the document and retry; if it still refuses, route the target to the workflow/scope writer owner.",
62412
+ "L06"
62413
+ );
62414
+ byOriginalIndex.set(item.originalIndex, {
62415
+ status: "blocked",
62416
+ text,
62417
+ excerpt: excerpt(text),
62418
+ blockers: Object.freeze([detail.code]),
62419
+ blockerDetails: Object.freeze([detail])
62420
+ });
62421
+ }
62422
+ const scopes = orderedHits.map((item) => byOriginalIndex.get(item.originalIndex)).filter((value) => value !== void 0);
62423
+ const created = scopes.filter((scope) => scope.status === "created").length;
62424
+ const blocked2 = scopes.length - created;
62425
+ const blockerDetails = scopes.flatMap((scope) => scope.blockerDetails ?? []);
62426
+ return {
62427
+ status: created === scopes.length ? "created" : created > 0 ? "partial" : "blocked",
62428
+ totalHits: hits.length,
62429
+ created,
62430
+ blocked: blocked2,
62431
+ scopes: Object.freeze(scopes),
62432
+ ...blockerDetails.length > 0 ? {
62433
+ blockers: Object.freeze(blockerDetails.map((detail) => detail.code)),
62434
+ blockerDetails: Object.freeze(blockerDetails)
62435
+ } : {}
62436
+ };
62437
+ }
62438
+ function textForSurfaceRange(blocks, from, to) {
62439
+ let acc = "";
62440
+ const visit = (items) => {
62441
+ for (const block of items) {
62442
+ if (block.kind === "paragraph") {
62443
+ for (const segment of block.segments) {
62444
+ if (segment.kind !== "text") continue;
62445
+ if (segment.from >= to) return;
62446
+ if (segment.to <= from) continue;
62447
+ acc += segment.text.slice(
62448
+ Math.max(0, from - segment.from),
62449
+ Math.min(segment.text.length, to - segment.from)
62450
+ );
62451
+ }
62452
+ continue;
62453
+ }
62454
+ if (block.kind === "table") {
62455
+ for (const row of block.rows) {
62456
+ for (const cell of row.cells) visit(cell.content);
62457
+ }
62458
+ continue;
62459
+ }
62460
+ if (block.kind === "sdt_block") {
62461
+ visit(block.children);
62462
+ }
62463
+ }
62464
+ };
62465
+ visit(blocks);
62466
+ return acc;
62467
+ }
61962
62468
  function validateTemplateTargets(runtime, input) {
61963
62469
  if (!Array.isArray(input.targets) || input.targets.length === 0) {
61964
62470
  const detail = blocker(
@@ -62379,6 +62885,15 @@ function applyRewrite(runtime, target, input) {
62379
62885
  if (!listTextTarget.ok) return blockedApplyFromResolution(listTextTarget);
62380
62886
  return applyEditableTextRewrite(runtime, listTextTarget.target, input);
62381
62887
  }
62888
+ const tableTextTarget = uniqueTableTextActionForScope(runtime, target.handle);
62889
+ if (tableTextTarget.ok && tableTextTarget.action.readback?.text === target.scope.content.text) {
62890
+ return applyTableScopedMarkerRewrite(
62891
+ runtime,
62892
+ target,
62893
+ tableTextTarget.action,
62894
+ input
62895
+ );
62896
+ }
62382
62897
  const rewriteBlocker = scopeRewriteCapabilityBlocker(target.scope);
62383
62898
  if (rewriteBlocker) {
62384
62899
  return blockedApply(
@@ -62511,6 +63026,106 @@ function applyEditableTextRewrite(runtime, target, input) {
62511
63026
  afterReadback
62512
63027
  };
62513
63028
  }
63029
+ function applyTableScopedMarkerRewrite(runtime, target, action, input) {
63030
+ const workflowScope = runtime.getScope(target.handle.scopeId);
63031
+ const editableTarget = tableEditableTargetForActionHandle(
63032
+ runtime,
63033
+ action.actionHandle
63034
+ );
63035
+ if (!workflowScope || workflowScope.anchor.kind !== "range" || !editableTarget) {
63036
+ return blockedApply(
63037
+ `actions:rewrite:table-marker-target-unresolved:${target.handle.scopeId}`,
63038
+ "unresolved-target",
63039
+ "The table-contained marker scope could not be joined to a current table text target.",
63040
+ "Refresh placeholder scopes and retry; route persistent failures to L08 table scope target mapping."
63041
+ );
63042
+ }
63043
+ const before = runtime.getCanonicalDocument();
63044
+ try {
63045
+ runtime.dispatch({
63046
+ type: "selection.set",
63047
+ selection: {
63048
+ anchor: workflowScope.anchor.from,
63049
+ head: workflowScope.anchor.to,
63050
+ isCollapsed: workflowScope.anchor.from === workflowScope.anchor.to,
63051
+ activeRange: {
63052
+ kind: "range",
63053
+ from: workflowScope.anchor.from,
63054
+ to: workflowScope.anchor.to,
63055
+ assoc: workflowScope.anchor.assoc ?? { start: -1, end: 1 }
63056
+ }
63057
+ },
63058
+ origin: actionOrigin(runtime, input)
63059
+ });
63060
+ runtime.applyActiveStoryTextCommand({
63061
+ type: "text.insert",
63062
+ text: input.text,
63063
+ editableTarget,
63064
+ origin: actionOrigin(runtime, input)
63065
+ });
63066
+ } catch {
63067
+ return blockedApply(
63068
+ `actions:rewrite:table-marker-command-refused:${target.handle.scopeId}`,
63069
+ "blocked",
63070
+ "The table-contained marker scope could not be rewritten by the runtime text command.",
63071
+ "Retry with an exact table text actionHandle; route repeated failures to L07 table text command support.",
63072
+ [
63073
+ blockerWithOwner(
63074
+ `actions:rewrite:table-marker-command-refused:${target.handle.scopeId}`,
63075
+ "blocked",
63076
+ "The table-contained marker scope could not be rewritten by the runtime text command.",
63077
+ "Retry with an exact table text actionHandle; route repeated failures to L07 table text command support.",
63078
+ "L07 runtime text command support"
63079
+ )
63080
+ ]
63081
+ );
63082
+ }
63083
+ const changed = runtime.getCanonicalDocument() !== before;
63084
+ const paragraph = resolveParagraphTextTarget2(
63085
+ runtime.getCanonicalDocument(),
63086
+ editableTarget
63087
+ );
63088
+ const afterText = paragraph ? collectInlineText3(paragraph.children) : void 0;
63089
+ const afterReadback = afterText === void 0 ? void 0 : { text: afterText, isEmpty: afterText.length === 0 };
63090
+ if (!changed || afterReadback?.text !== input.text) {
63091
+ return {
63092
+ status: "blocked",
63093
+ applied: false,
63094
+ changed,
63095
+ target: summarizeTarget({ kind: "table-text", action }),
63096
+ posture: "suspect-readback",
63097
+ blockers: Object.freeze([
63098
+ `actions:rewrite:table-marker-readback-mismatch:${target.handle.scopeId}`
63099
+ ]),
63100
+ blockerDetails: Object.freeze([
63101
+ blocker(
63102
+ `actions:rewrite:table-marker-readback-mismatch:${target.handle.scopeId}`,
63103
+ "blocked",
63104
+ "The table-contained marker rewrite did not produce the requested exact table-cell readback.",
63105
+ "Treat the mutation as suspect. Re-read the target and export before claiming success."
63106
+ )
63107
+ ]),
63108
+ ...afterReadback ? { afterReadback } : {}
63109
+ };
63110
+ }
63111
+ return {
63112
+ status: "applied",
63113
+ applied: true,
63114
+ changed: true,
63115
+ target: summarizeTarget({
63116
+ kind: "table-text",
63117
+ action: { ...action, readback: afterReadback }
63118
+ }),
63119
+ commandReference: {
63120
+ command: "text.insert",
63121
+ actorId: input.actorId ?? "v3-ai-api",
63122
+ origin: input.origin ?? "agent",
63123
+ emittedAtUtc: currentAuditTimestamp(runtime)
63124
+ },
63125
+ ...action.readback ? { beforeReadback: action.readback } : {},
63126
+ afterReadback
63127
+ };
63128
+ }
62514
63129
  function projectRewriteScopeResult(runtime, result, target, beforeText, proposedText, documentMutated) {
62515
63130
  if (!result.applied) return projectApplyResult(result, target);
62516
63131
  const compiledAfter = createScopeCompilerService(runtime).compileScopeById(
@@ -62627,6 +63242,51 @@ function tableTextActionsForScope(runtime, handle) {
62627
63242
  });
62628
63243
  return result.actions.filter((action) => action.family === "table-text");
62629
63244
  }
63245
+ function uniqueTableTextActionForScope(runtime, handle) {
63246
+ const actions2 = tableTextActionsForScope(runtime, handle).filter(
63247
+ (action) => action.readback !== void 0
63248
+ );
63249
+ if (actions2.length === 1 && actions2[0]) {
63250
+ return { ok: true, action: actions2[0] };
63251
+ }
63252
+ if (actions2.length === 0) {
63253
+ return {
63254
+ ok: false,
63255
+ blockers: Object.freeze(["actions:rewrite:table-text-target-missing"]),
63256
+ blockerDetails: Object.freeze([
63257
+ blocker(
63258
+ "actions:rewrite:table-text-target-missing",
63259
+ "unsupported",
63260
+ "The scope does not expose a unique command-safe table text target.",
63261
+ "Use a paragraph/list scope, or retry with an exact table text actionHandle returned by ai.actions.locateAll."
63262
+ )
63263
+ ])
63264
+ };
63265
+ }
63266
+ return {
63267
+ ok: false,
63268
+ blockers: Object.freeze(["actions:rewrite:table-text-target-ambiguous"]),
63269
+ blockerDetails: Object.freeze([
63270
+ blocker(
63271
+ "actions:rewrite:table-text-target-ambiguous",
63272
+ "ambiguous-target",
63273
+ "The scope exposes multiple table text targets, so broad rewrite would be ambiguous.",
63274
+ "Retry with the exact table text actionHandle returned by ai.actions.locateAll or ai.listTableActions."
63275
+ )
63276
+ ])
63277
+ };
63278
+ }
63279
+ function tableEditableTargetForActionHandle(runtime, actionHandle) {
63280
+ for (const target of collectEditableTargetRefs(runtime.getCanonicalDocument())) {
63281
+ if (target.commandFamily !== "text-leaf" || target.table?.operationScope !== "text") {
63282
+ continue;
63283
+ }
63284
+ if (deriveTableActionHandleForTarget(runtime, target.targetKey) === actionHandle) {
63285
+ return target;
63286
+ }
63287
+ }
63288
+ return null;
63289
+ }
62630
63290
  function editableTextActionsForScope(runtime, handle) {
62631
63291
  const bundle = createScopeCompilerService(runtime).compileBundleById(
62632
63292
  handle.scopeId,
@@ -65080,7 +65740,7 @@ function createOverlaysFamily(ctx) {
65080
65740
  return {
65081
65741
  handle: {
65082
65742
  kind: "table-action",
65083
- id: tableActionHandle2(documentSeed2(), targetKey),
65743
+ id: tableActionHandle2(documentSeed3(), targetKey),
65084
65744
  targetKind,
65085
65745
  commandFamily,
65086
65746
  scope: tableActionScope(entry)
@@ -65104,9 +65764,9 @@ function createOverlaysFamily(ctx) {
65104
65764
  };
65105
65765
  }
65106
65766
  function tableActionHandle2(seed, targetKey) {
65107
- return `table-action:${hashOpaque2(`${seed}::${targetKey}`)}`;
65767
+ return `table-action:${hashOpaque3(`${seed}::${targetKey}`)}`;
65108
65768
  }
65109
- function hashOpaque2(input) {
65769
+ function hashOpaque3(input) {
65110
65770
  let h = 2166136261;
65111
65771
  for (let i = 0; i < input.length; i += 1) {
65112
65772
  h ^= input.charCodeAt(i);
@@ -65114,7 +65774,7 @@ function createOverlaysFamily(ctx) {
65114
65774
  }
65115
65775
  return (h >>> 0).toString(36).padStart(7, "0");
65116
65776
  }
65117
- function documentSeed2() {
65777
+ function documentSeed3() {
65118
65778
  return ctx.handle.getSessionState().documentId ?? "document";
65119
65779
  }
65120
65780
  function modeledTargetSourceIdentity(target) {
@@ -66443,6 +67103,7 @@ function createApiV3(handle, opts) {
66443
67103
  ...createStatsFamily(handle),
66444
67104
  ...createOutlineFamily(handle),
66445
67105
  ...createTableActionFamily(handle),
67106
+ ...createObjectActionFamily(handle),
66446
67107
  ...createActionsFamily(handle)
66447
67108
  };
66448
67109
  const runtime = {