@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
@@ -11011,10 +11011,16 @@ var FormattingContextImpl = class {
11011
11011
  // surface-projection calls `resolveParagraphCascade` on the same
11012
11012
  // paragraph via `resolveParagraph` + numbering resolve + marker-rPr;
11013
11013
  // each of those re-walked the style catalog before.
11014
- paragraphCascadeCache = /* @__PURE__ */ new WeakMap();
11014
+ //
11015
+ // Defaults to a per-context instance, but the runtime can supply a
11016
+ // persistent `WeakMap` via `FormattingContextOptions.paragraphCascadeCache`
11017
+ // so unchanged paragraphs don't re-cascade on every keystroke. See the
11018
+ // contract on the option for invalidation semantics.
11019
+ paragraphCascadeCache;
11015
11020
  constructor(doc, opts) {
11016
11021
  this.doc = doc;
11017
11022
  this.opts = opts;
11023
+ this.paragraphCascadeCache = opts.paragraphCascadeCache ?? /* @__PURE__ */ new WeakMap();
11018
11024
  const canonicalTheme = doc.subParts?.canonicalTheme;
11019
11025
  this.theme = opts.themeResolver ?? (canonicalTheme ? new ThemeColorResolver(canonicalTheme) : void 0);
11020
11026
  this.numbering = opts.numberingPrefixResolver ?? createNumberingPrefixResolver(doc.numbering);
@@ -11580,7 +11586,8 @@ function createEditorSurfaceSnapshot(document2, _selection, activeStory = { kind
11580
11586
  const formattingContext = createFormattingContext(document2, {
11581
11587
  ...options.revisionMarkupMode ? { revisionMarkupMode: options.revisionMarkupMode } : {},
11582
11588
  ...options.getEffectiveMarkupMode ? { getEffectiveMarkupMode: options.getEffectiveMarkupMode } : {},
11583
- ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {}
11589
+ ...options.authorColorPalette ? { authorColorPalette: options.authorColorPalette } : {},
11590
+ ...options.paragraphCascadeCache ? { paragraphCascadeCache: options.paragraphCascadeCache } : {}
11584
11591
  });
11585
11592
  const editableTargetsByBlockPath = options.editableTargetsByBlockPath ?? indexEditableTargetsByBlockPath(document2);
11586
11593
  const layoutIdentitiesByBlockPath = options.layoutIdentitiesByBlockPath;
@@ -18846,9 +18853,11 @@ function applyRevisionAction(options) {
18846
18853
  );
18847
18854
  }
18848
18855
  const slice = story.units.slice(range.from, range.to);
18849
- if (slice.some(
18850
- (unit) => unit.kind === "paragraph_break" || unit.kind === "opaque_block" || unit.kind === "structural_block"
18851
- )) {
18856
+ const touchesStructuralContent = slice.some(
18857
+ (unit) => unit.kind === "opaque_block" || unit.kind === "structural_block"
18858
+ );
18859
+ const touchesParagraphBoundary = slice.some((unit) => unit.kind === "paragraph_break");
18860
+ if (touchesStructuralContent || touchesParagraphBoundary && !canApplyRuntimeTextBlockDeletion(revision, options.intent)) {
18852
18861
  return skippedResult(
18853
18862
  options,
18854
18863
  "structural-range",
@@ -18856,7 +18865,7 @@ function applyRevisionAction(options) {
18856
18865
  );
18857
18866
  }
18858
18867
  if (slice.some(
18859
- (unit) => unit.kind === "image" || unit.kind === "opaque_inline"
18868
+ (unit) => unit.kind === "image" || unit.kind === "opaque_inline" || unit.kind === "protected_inline"
18860
18869
  )) {
18861
18870
  return skippedResult(
18862
18871
  options,
@@ -18911,6 +18920,9 @@ function applyRevisionAction(options) {
18911
18920
  detachedRevisionIds: findNewDetachedRevisionIds(options.store, nextStore)
18912
18921
  };
18913
18922
  }
18923
+ function canApplyRuntimeTextBlockDeletion(revision, intent) {
18924
+ return revision.metadata.source === "runtime" && (intent === "accept" && revision.kind === "deletion" && (revision.metadata.semanticKind === "replacement" || revision.metadata.semanticKind === "deletion") || intent === "reject" && revision.kind === "insertion" && (revision.metadata.semanticKind === "replacement" || revision.metadata.semanticKind === "structural-change"));
18925
+ }
18914
18926
  function applyPairedMoveAction(options, revision) {
18915
18927
  const resultingStatus = toResultingStatus(options.intent);
18916
18928
  return {
@@ -21294,6 +21306,11 @@ function executeEditorCommand(state, command, context) {
21294
21306
  );
21295
21307
  return buildDocumentReplaceTransaction(state, context, result);
21296
21308
  }
21309
+ case "fragment.insert-tracked": {
21310
+ const result = applySuggestingFragmentInsert(state, command.fragment, context);
21311
+ if (result) return result;
21312
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
21313
+ }
21297
21314
  case "runtime.set-read-only":
21298
21315
  return createTransaction(
21299
21316
  {
@@ -22539,7 +22556,7 @@ function applyTextCommand(state, timestamp, apply) {
22539
22556
  const reviewState = remapReviewStateAfterContentChange(
22540
22557
  state,
22541
22558
  result.document,
22542
- result.mapping
22559
+ result.mapping ?? createEmptyMapping()
22543
22560
  );
22544
22561
  const scopeTagTouches = collectScopeTagTouches(
22545
22562
  state.document.review.comments,
@@ -22879,6 +22896,38 @@ function isSingleParagraphSuggestingRange(document2, from, to) {
22879
22896
  );
22880
22897
  return ranges.some((range) => from >= range.start && to <= range.end);
22881
22898
  }
22899
+ function isTextOnlySuggestingRange(document2, from, to) {
22900
+ const story = parseTextStory(document2.content);
22901
+ const range = normalizeTextStoryRange(story, from, to);
22902
+ if (!range) {
22903
+ return isSingleParagraphSuggestingRange(document2, from, to);
22904
+ }
22905
+ const rootStoryTextOnly = story.units.slice(range.from, range.to).every(isSuggestingTextReplacementUnit);
22906
+ if (rootStoryTextOnly) return true;
22907
+ return isSingleParagraphSuggestingRange(document2, from, to);
22908
+ }
22909
+ function normalizeTextStoryRange(story, from, to) {
22910
+ const start = Math.min(from, to);
22911
+ const end = Math.max(from, to);
22912
+ if (start < 0 || end > story.size) return null;
22913
+ return { from: start, to: end };
22914
+ }
22915
+ function isSuggestingTextReplacementUnit(unit) {
22916
+ switch (unit.kind) {
22917
+ case "text":
22918
+ case "tab":
22919
+ case "hard_break":
22920
+ case "paragraph_break":
22921
+ case "scope_marker":
22922
+ return true;
22923
+ case "protected_inline":
22924
+ case "image":
22925
+ case "opaque_inline":
22926
+ case "opaque_block":
22927
+ case "structural_block":
22928
+ return false;
22929
+ }
22930
+ }
22882
22931
  function collectSuggestingParagraphRanges(blocks, startCursor, output, addRootParagraphBoundaries) {
22883
22932
  let cursor = startCursor;
22884
22933
  for (let index = 0; index < blocks.length; index += 1) {
@@ -22951,10 +23000,10 @@ function applySuggestingInsert(state, text, context, formatting) {
22951
23000
  const from = Math.min(selection.anchor, selection.head);
22952
23001
  const to = Math.max(selection.anchor, selection.head);
22953
23002
  const isCollapsed = from === to;
22954
- if (!isCollapsed && !isSingleParagraphSuggestingRange(state.document, from, to)) {
23003
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
22955
23004
  return createSuggestingUnsupportedTransaction(
22956
23005
  state,
22957
- "Suggesting mode does not yet support multi-paragraph replacement ranges."
23006
+ "Suggesting mode text replacement ranges must contain only editable text and paragraph breaks."
22958
23007
  );
22959
23008
  }
22960
23009
  if (isCollapsed) {
@@ -23063,7 +23112,7 @@ function applySuggestingInsert(state, text, context, formatting) {
23063
23112
  const reviewState = remapReviewStateAfterContentChange(
23064
23113
  state,
23065
23114
  result.document,
23066
- result.mapping
23115
+ result.mapping ?? createEmptyMapping()
23067
23116
  );
23068
23117
  const replacementSuggestionId = createSuggestingRevisionId(
23069
23118
  reviewState.document.review.revisions,
@@ -23128,6 +23177,131 @@ function applySuggestingInsert(state, text, context, formatting) {
23128
23177
  activeCommentId: reviewState.activeCommentId
23129
23178
  }
23130
23179
  },
23180
+ {
23181
+ historyBoundary: "push",
23182
+ markDirty: true,
23183
+ mapping: result.mapping ?? createEmptyMapping(),
23184
+ effects: {
23185
+ ...reviewState.effects,
23186
+ revisionAuthored: { changeId: insertionRevision.changeId, kind: "insertion" }
23187
+ }
23188
+ }
23189
+ );
23190
+ }
23191
+ function applySuggestingFragmentInsert(state, fragment, context) {
23192
+ if (state.readOnly) {
23193
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23194
+ }
23195
+ if (fragment.blocks.length === 0) {
23196
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23197
+ }
23198
+ if (!isTrackableSuggestingFragment(fragment)) {
23199
+ return createSuggestingUnsupportedTransaction(
23200
+ state,
23201
+ "Suggesting mode structured fragment replacement supports paragraph/text fragments only."
23202
+ );
23203
+ }
23204
+ const authorId = context.defaultAuthorId ?? "unknown";
23205
+ const selection = state.selection;
23206
+ const from = Math.min(selection.anchor, selection.head);
23207
+ const to = Math.max(selection.anchor, selection.head);
23208
+ const isCollapsed = from === to;
23209
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
23210
+ return createSuggestingUnsupportedTransaction(
23211
+ state,
23212
+ "Suggesting mode structured fragment replacement ranges must contain only editable text and paragraph breaks."
23213
+ );
23214
+ }
23215
+ const storyBefore = parseTextStory(state.document.content);
23216
+ const insertSelection = createSelectionSnapshot(to, to);
23217
+ const result = structureLayer.applyFragmentInsert(
23218
+ state.document,
23219
+ insertSelection,
23220
+ fragment,
23221
+ context
23222
+ );
23223
+ if (!result.changed) {
23224
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23225
+ }
23226
+ const storyAfter = parseTextStory(result.document.content);
23227
+ const insertedFrom = to;
23228
+ const insertedTo = to + Math.max(0, storyAfter.size - storyBefore.size);
23229
+ if (insertedTo <= insertedFrom) {
23230
+ return createTransaction(state, { historyBoundary: "skip", markDirty: false });
23231
+ }
23232
+ const reviewState = remapReviewStateAfterContentChange(
23233
+ state,
23234
+ result.document,
23235
+ result.mapping ?? createEmptyMapping()
23236
+ );
23237
+ const replacementSuggestionId = !isCollapsed ? createSuggestingRevisionId(
23238
+ reviewState.document.review.revisions,
23239
+ context.timestamp,
23240
+ authorId
23241
+ ) : void 0;
23242
+ let deletionRevision = !isCollapsed ? createAuthoredRevision(
23243
+ reviewState.document.review.revisions,
23244
+ "deletion",
23245
+ from,
23246
+ to,
23247
+ authorId,
23248
+ context.timestamp,
23249
+ createSuggestionMetadata({
23250
+ suggestionId: replacementSuggestionId,
23251
+ semanticKind: "replacement"
23252
+ })
23253
+ ) : void 0;
23254
+ const insertionRevision = createAuthoredRevision(
23255
+ {
23256
+ ...reviewState.document.review.revisions,
23257
+ ...deletionRevision ? { [deletionRevision.changeId]: deletionRevision } : {}
23258
+ },
23259
+ "insertion",
23260
+ insertedFrom,
23261
+ insertedTo,
23262
+ authorId,
23263
+ context.timestamp,
23264
+ createSuggestionMetadata(
23265
+ deletionRevision ? {
23266
+ suggestionId: replacementSuggestionId,
23267
+ semanticKind: "replacement",
23268
+ linkedRevisionIds: [deletionRevision.changeId]
23269
+ } : {
23270
+ semanticKind: "structural-change"
23271
+ }
23272
+ )
23273
+ );
23274
+ if (deletionRevision) {
23275
+ deletionRevision = {
23276
+ ...deletionRevision,
23277
+ metadata: {
23278
+ ...deletionRevision.metadata,
23279
+ linkedRevisionIds: [insertionRevision.changeId]
23280
+ }
23281
+ };
23282
+ }
23283
+ const finalDocument = {
23284
+ ...reviewState.document,
23285
+ review: {
23286
+ ...reviewState.document.review,
23287
+ revisions: {
23288
+ ...reviewState.document.review.revisions,
23289
+ ...deletionRevision ? { [deletionRevision.changeId]: deletionRevision } : {},
23290
+ [insertionRevision.changeId]: insertionRevision
23291
+ }
23292
+ }
23293
+ };
23294
+ return createTransaction(
23295
+ {
23296
+ ...state,
23297
+ document: finalDocument,
23298
+ selection: createSelectionSnapshot(insertedTo, insertedTo),
23299
+ warnings: reviewState.warnings,
23300
+ runtime: {
23301
+ ...state.runtime,
23302
+ activeCommentId: reviewState.activeCommentId
23303
+ }
23304
+ },
23131
23305
  {
23132
23306
  historyBoundary: "push",
23133
23307
  markDirty: true,
@@ -23139,6 +23313,10 @@ function applySuggestingInsert(state, text, context, formatting) {
23139
23313
  }
23140
23314
  );
23141
23315
  }
23316
+ function isTrackableSuggestingFragment(fragment) {
23317
+ const story = parseTextStory({ type: "doc", children: fragment.blocks });
23318
+ return story.units.every(isSuggestingTextReplacementUnit);
23319
+ }
23142
23320
  function applySuggestingDelete(state, direction, context) {
23143
23321
  if (state.readOnly) {
23144
23322
  return createTransaction(state, { historyBoundary: "skip", markDirty: false });
@@ -23148,10 +23326,10 @@ function applySuggestingDelete(state, direction, context) {
23148
23326
  const from = Math.min(selection.anchor, selection.head);
23149
23327
  const to = Math.max(selection.anchor, selection.head);
23150
23328
  const isCollapsed = from === to;
23151
- if (!isCollapsed && !isSingleParagraphSuggestingRange(state.document, from, to)) {
23329
+ if (!isCollapsed && !isTextOnlySuggestingRange(state.document, from, to)) {
23152
23330
  return createSuggestingUnsupportedTransaction(
23153
23331
  state,
23154
- "Suggesting mode does not yet support multi-paragraph deletion ranges."
23332
+ "Suggesting mode text deletion ranges must contain only editable text and paragraph breaks."
23155
23333
  );
23156
23334
  }
23157
23335
  let deleteFrom;
@@ -35534,11 +35712,9 @@ function compileParagraphReplacement(entry, proposed, options) {
35534
35712
  };
35535
35713
  }
35536
35714
  if (proposed.proposedContent.kind === "structured") {
35537
- if (options.posture === "suggest-mode") {
35538
- return null;
35539
- }
35540
35715
  const fragment = proposed.proposedContent.structured;
35541
35716
  if (!isStructuredReplacementContent(fragment)) return null;
35717
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
35542
35718
  const blockCount = fragment.blocks.length;
35543
35719
  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}`;
35544
35720
  const actionVerb = proposed.operation === "insert-before" ? "insert before" : proposed.operation === "insert-after" ? "insert after" : "replace";
@@ -35549,8 +35725,8 @@ function compileParagraphReplacement(entry, proposed, options) {
35549
35725
  operation: proposed.operation,
35550
35726
  steps: Object.freeze([
35551
35727
  {
35552
- kind: "fragment-replace",
35553
- summary: actionSummary,
35728
+ kind: stepKind,
35729
+ summary: stepKind === "fragment-replace" ? actionSummary : `suggest-mode ${actionSummary}`,
35554
35730
  ...textLeafEditableTargetHint(entry, blockRange) ? { editableTargetHint: textLeafEditableTargetHint(entry, blockRange) } : {},
35555
35731
  range: { from: operationRange.from, to: operationRange.to },
35556
35732
  fragment
@@ -35637,7 +35813,7 @@ function compileScopeKind(entry, options) {
35637
35813
  };
35638
35814
  }
35639
35815
  function compileScopeReplacement(entry, proposed, options) {
35640
- if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace" || options.posture !== "direct-edit") {
35816
+ if (entry.handle.provenance !== "marker-backed" || proposed.operation !== "replace") {
35641
35817
  return null;
35642
35818
  }
35643
35819
  const markerRange = buildScopePositionMap(options.document).markerScopes.get(
@@ -35646,14 +35822,15 @@ function compileScopeReplacement(entry, proposed, options) {
35646
35822
  if (!markerRange) return null;
35647
35823
  if (proposed.proposedContent.kind === "text") {
35648
35824
  const text = proposed.proposedContent.text ?? "";
35825
+ const stepKind = options.posture === "suggest-mode" ? "text-insert-tracked" : "text-replace";
35649
35826
  return {
35650
35827
  scopeId: entry.handle.scopeId,
35651
35828
  targetKind: "scope",
35652
35829
  operation: proposed.operation,
35653
35830
  steps: Object.freeze([
35654
35831
  {
35655
- kind: "text-replace",
35656
- summary: `replace multi-paragraph scope ${entry.handle.scopeId} text [${markerRange.from}..${markerRange.to}] (len ${text.length})`,
35832
+ kind: stepKind,
35833
+ 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})`,
35657
35834
  range: { from: markerRange.from, to: markerRange.to },
35658
35835
  text,
35659
35836
  ...proposed.formatting ? { formatting: proposed.formatting } : {}
@@ -35666,14 +35843,15 @@ function compileScopeReplacement(entry, proposed, options) {
35666
35843
  if (proposed.proposedContent.kind === "structured") {
35667
35844
  const fragment = proposed.proposedContent.structured;
35668
35845
  if (!isStructuredReplacementContent(fragment)) return null;
35846
+ const stepKind = options.posture === "suggest-mode" ? "fragment-replace-tracked" : "fragment-replace";
35669
35847
  return {
35670
35848
  scopeId: entry.handle.scopeId,
35671
35849
  targetKind: "scope",
35672
35850
  operation: proposed.operation,
35673
35851
  steps: Object.freeze([
35674
35852
  {
35675
- kind: "fragment-replace",
35676
- summary: `replace multi-paragraph scope ${entry.handle.scopeId} with structured fragment [${markerRange.from}..${markerRange.to}] (${fragment.blocks.length} block(s))`,
35853
+ kind: stepKind,
35854
+ 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))`,
35677
35855
  range: { from: markerRange.from, to: markerRange.to },
35678
35856
  fragment
35679
35857
  }
@@ -36055,6 +36233,99 @@ function paragraphFirstMarkerStart(paragraph, knownScopeIds) {
36055
36233
  }
36056
36234
  return null;
36057
36235
  }
36236
+ function paragraphHasMarkerEnd(paragraph, scopeId) {
36237
+ return paragraph.children.some(
36238
+ (child) => child.type === "scope_marker_end" && child.scopeId === scopeId
36239
+ );
36240
+ }
36241
+ function paragraphSameParagraphMarkerScopeId(paragraph, knownScopeIds) {
36242
+ const markerScopeId = paragraphFirstMarkerStart(paragraph, knownScopeIds);
36243
+ if (!markerScopeId) return null;
36244
+ return paragraphHasMarkerEnd(paragraph, markerScopeId) ? markerScopeId : null;
36245
+ }
36246
+ function markerStableRefOverride(markerScopeId, semanticPath, overlay) {
36247
+ const hint = stableRefHintForScopeId(markerScopeId, overlay);
36248
+ if (hint === "semantic-path") {
36249
+ return {
36250
+ kind: "semantic-path",
36251
+ value: semanticPath.join("/")
36252
+ };
36253
+ }
36254
+ return { kind: "scope-id", value: markerScopeId };
36255
+ }
36256
+ function enumerateNestedMarkerBackedParagraphs(blocks, input) {
36257
+ const out = [];
36258
+ for (let childIndex = 0; childIndex < blocks.length; childIndex += 1) {
36259
+ const block = blocks[childIndex];
36260
+ if (!block) continue;
36261
+ if (block.type === "paragraph") {
36262
+ const markerScopeId = paragraphSameParagraphMarkerScopeId(
36263
+ block,
36264
+ input.knownOverlayScopeIds
36265
+ );
36266
+ if (!markerScopeId) continue;
36267
+ const kind = detectParagraphKind(block);
36268
+ const semanticPath = [
36269
+ ...input.semanticPrefix,
36270
+ kind,
36271
+ String(childIndex)
36272
+ ];
36273
+ const handle = buildHandle(
36274
+ markerScopeId,
36275
+ input.documentId,
36276
+ semanticPath,
36277
+ "marker-backed",
36278
+ "marker-backed",
36279
+ input.parentScopeId,
36280
+ markerStableRefOverride(markerScopeId, semanticPath, input.overlay)
36281
+ );
36282
+ out.push({
36283
+ kind,
36284
+ handle,
36285
+ paragraph: block,
36286
+ blockIndex: input.rootBlockIndex,
36287
+ classifications: input.classificationIndex.get(markerScopeId) ?? Object.freeze([])
36288
+ });
36289
+ continue;
36290
+ }
36291
+ if (block.type === "sdt") {
36292
+ out.push(
36293
+ ...enumerateNestedMarkerBackedParagraphs(block.children, {
36294
+ ...input,
36295
+ semanticPrefix: [
36296
+ ...input.semanticPrefix,
36297
+ "sdt",
36298
+ String(childIndex)
36299
+ ]
36300
+ })
36301
+ );
36302
+ continue;
36303
+ }
36304
+ if (block.type === "table") {
36305
+ for (let rowIdx = 0; rowIdx < block.rows.length; rowIdx += 1) {
36306
+ const row2 = block.rows[rowIdx];
36307
+ for (let cellIdx = 0; cellIdx < row2.cells.length; cellIdx += 1) {
36308
+ const cell = row2.cells[cellIdx];
36309
+ out.push(
36310
+ ...enumerateNestedMarkerBackedParagraphs(cell.children, {
36311
+ ...input,
36312
+ semanticPrefix: [
36313
+ ...input.semanticPrefix,
36314
+ "table",
36315
+ String(childIndex),
36316
+ "row",
36317
+ String(rowIdx),
36318
+ "cell",
36319
+ String(cellIdx)
36320
+ ]
36321
+ })
36322
+ );
36323
+ }
36324
+ }
36325
+ }
36326
+ }
36327
+ return out;
36328
+ }
36058
36329
  function enumerateFieldsInParagraph(paragraph, blockIndex, documentId, parentScopeId) {
36059
36330
  const out = [];
36060
36331
  for (let i = 0; i < paragraph.children.length; i += 1) {
@@ -36337,6 +36608,17 @@ function enumerateScopes(document2, inputs = {}) {
36337
36608
  cellIndex: cellIdx,
36338
36609
  classifications: Object.freeze([])
36339
36610
  });
36611
+ for (const nested of enumerateNestedMarkerBackedParagraphs(cell.children, {
36612
+ documentId,
36613
+ rootBlockIndex: index,
36614
+ semanticPrefix: cellSemanticPath,
36615
+ parentScopeId: cellScopeId,
36616
+ knownOverlayScopeIds,
36617
+ classificationIndex,
36618
+ overlay: inputs.overlay
36619
+ })) {
36620
+ results.push(nested);
36621
+ }
36340
36622
  }
36341
36623
  }
36342
36624
  }
@@ -36822,16 +37104,12 @@ function replaceTextCapability(scope, context) {
36822
37104
  );
36823
37105
  }
36824
37106
  if (scope.kind === "scope") {
36825
- if (scope.workflow.effectiveMode === "suggest") {
36826
- return unsupported(
36827
- "compile-refused:scope:multi-paragraph-suggesting-not-implemented",
36828
- ["compile-refused:scope:multi-paragraph-suggesting-not-implemented"],
36829
- ["guard:suggest-mode", ...evidenceWarnings(context)]
36830
- );
36831
- }
36832
37107
  return supported(
36833
37108
  "compile-supported:scope:multi-paragraph-text-replace",
36834
- evidenceWarnings(context)
37109
+ [
37110
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37111
+ ...evidenceWarnings(context)
37112
+ ]
36835
37113
  );
36836
37114
  }
36837
37115
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
@@ -36885,34 +37163,24 @@ function replaceFragmentCapability(scope, context) {
36885
37163
  );
36886
37164
  }
36887
37165
  if (scope.kind === "scope") {
36888
- if (scope.workflow.effectiveMode === "suggest") {
36889
- return unsupported(
36890
- "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented",
36891
- [
36892
- "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented"
36893
- ],
36894
- ["guard:suggest-mode", ...evidenceWarnings(context)]
36895
- );
36896
- }
36897
37166
  return supported(
36898
37167
  "compile-supported:scope:multi-paragraph-fragment-replace",
36899
- evidenceWarnings(context)
37168
+ [
37169
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37170
+ ...evidenceWarnings(context)
37171
+ ]
36900
37172
  );
36901
37173
  }
36902
37174
  if (!PARAGRAPH_LIKE.has(scope.kind)) {
36903
37175
  const reason = `compile-refused:${scope.kind}`;
36904
37176
  return unsupported(reason, [reason], evidenceWarnings(context));
36905
37177
  }
36906
- if (scope.workflow.effectiveMode === "suggest") {
36907
- return unsupported(
36908
- `compile-refused:${scope.kind}:structured-suggesting-not-implemented`,
36909
- [`compile-refused:${scope.kind}:structured-suggesting-not-implemented`],
36910
- ["guard:suggest-mode"]
36911
- );
36912
- }
36913
37178
  return supported(
36914
- "compile-supported:paragraph-like:fragment-replace",
36915
- evidenceWarnings(context)
37179
+ scope.workflow.effectiveMode === "suggest" ? "compile-supported:paragraph-like:fragment-replace-tracked" : "compile-supported:paragraph-like:fragment-replace",
37180
+ [
37181
+ ...scope.workflow.effectiveMode === "suggest" ? ["guard:suggest-mode"] : [],
37182
+ ...evidenceWarnings(context)
37183
+ ]
36916
37184
  );
36917
37185
  }
36918
37186
  function formattingCapability(scope, context) {
@@ -37966,6 +38234,14 @@ function supportedNonTextCommandEvidence(target) {
37966
38234
  if (target.commandFamily === "link-bookmark") {
37967
38235
  return linkBookmarkCommandEvidence(target, []);
37968
38236
  }
38237
+ if (target.commandFamily === "object" && isImageObjectTarget(target) && typeof target.object?.mediaId === "string" && target.object.mediaId.length > 0 && onlyBlockers(target.posture.blockers, ["unmodeled-target"])) {
38238
+ return {
38239
+ status: "supported",
38240
+ commandFamily: target.commandFamily,
38241
+ intents: commandIntentsForTarget(target),
38242
+ reason: "l07:image-layout-target-supported"
38243
+ };
38244
+ }
37969
38245
  return null;
37970
38246
  }
37971
38247
  function onlyBlockers(blockers, allowed) {
@@ -40466,9 +40742,7 @@ function applyScopeReplacement(inputs) {
40466
40742
  });
40467
40743
  if (!plan) {
40468
40744
  const paragraphLike = resolvedScope.kind === "paragraph" || resolvedScope.kind === "heading" || resolvedScope.kind === "list-item";
40469
- const blockers = resolvedScope.kind === "scope" && posture === "suggest-mode" ? [
40470
- proposed.proposedContent.kind === "structured" ? "compile-refused:scope:multi-paragraph-structured-suggesting-not-implemented" : "compile-refused:scope:multi-paragraph-suggesting-not-implemented"
40471
- ] : resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
40745
+ const blockers = resolvedScope.kind === "scope" && proposed.operation !== "replace" ? [
40472
40746
  `compile-refused:scope:operation-not-implemented:${proposed.operation}`
40473
40747
  ] : resolvedScope.kind === "scope" ? multiParagraphReplacementBlockers(
40474
40748
  proposed.proposedContent.kind === "structured" ? "fragment" : "text"
@@ -41259,6 +41533,8 @@ function buildSearchPattern(query, options = {}) {
41259
41533
  function findSearchMatches(text, query, options = {}) {
41260
41534
  const pattern = buildSearchPattern(query, options);
41261
41535
  if (!pattern) return [];
41536
+ const limit = normalizeSearchLimit(options.limit);
41537
+ if (limit === 0) return [];
41262
41538
  const results = [];
41263
41539
  let match;
41264
41540
  pattern.lastIndex = 0;
@@ -41269,12 +41545,21 @@ function findSearchMatches(text, query, options = {}) {
41269
41545
  text: match[0],
41270
41546
  index: results.length
41271
41547
  });
41548
+ if (results.length >= limit) {
41549
+ break;
41550
+ }
41272
41551
  if (match[0].length === 0) {
41273
41552
  pattern.lastIndex += 1;
41274
41553
  }
41275
41554
  }
41276
41555
  return results;
41277
41556
  }
41557
+ function normalizeSearchLimit(limit) {
41558
+ if (limit === void 0 || !Number.isFinite(limit)) {
41559
+ return Number.POSITIVE_INFINITY;
41560
+ }
41561
+ return Math.max(0, Math.floor(limit));
41562
+ }
41278
41563
  function createSearchExcerpt(text, from, to, radius = 24) {
41279
41564
  const safeFrom = Math.max(0, Math.min(from, text.length));
41280
41565
  const safeTo = Math.max(safeFrom, Math.min(to, text.length));
@@ -41904,7 +42189,8 @@ function buildResolvedSections(document2) {
41904
42189
  const mainSurface = createEditorSurfaceSnapshot(
41905
42190
  document2,
41906
42191
  createSelectionSnapshot(0, 0),
41907
- MAIN_STORY_TARGET
42192
+ MAIN_STORY_TARGET,
42193
+ { editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX }
41908
42194
  );
41909
42195
  const sections = [];
41910
42196
  let sectionStart = 0;
@@ -55942,6 +56228,36 @@ function createDocumentRuntime(options) {
55942
56228
  let viewportBlockRanges = null;
55943
56229
  let viewportRangesKey = serializeViewportRanges(viewportBlockRanges);
55944
56230
  let viewportBlocksPerPageEstimate = null;
56231
+ let fullSurfaceWarmupScheduled = false;
56232
+ let fullSurfaceWarmupCompleted = false;
56233
+ function scheduleFullSurfaceWarmupAfterCull() {
56234
+ if (fullSurfaceWarmupScheduled || fullSurfaceWarmupCompleted) return;
56235
+ fullSurfaceWarmupScheduled = true;
56236
+ perfCounters.increment("surface.fullSurfaceWarmup.scheduled");
56237
+ runOnIdle(() => {
56238
+ fullSurfaceWarmupScheduled = false;
56239
+ if (cachedFullSurface) {
56240
+ perfCounters.increment("surface.fullSurfaceWarmup.alreadyCached");
56241
+ fullSurfaceWarmupCompleted = true;
56242
+ return;
56243
+ }
56244
+ const t0 = performance.now();
56245
+ try {
56246
+ getCachedFullSurface(state.document, activeStory);
56247
+ fullSurfaceWarmupCompleted = true;
56248
+ perfCounters.increment("surface.fullSurfaceWarmup.committed");
56249
+ } catch (error) {
56250
+ perfCounters.increment("surface.fullSurfaceWarmup.failed");
56251
+ fullSurfaceWarmupCompleted = false;
56252
+ void error;
56253
+ } finally {
56254
+ perfCounters.increment(
56255
+ "surface.fullSurfaceWarmup.us",
56256
+ Math.round((performance.now() - t0) * 1e3)
56257
+ );
56258
+ }
56259
+ });
56260
+ }
55945
56261
  const getRuntimeForLayoutFacet = () => {
55946
56262
  if (!runtimeRef) {
55947
56263
  throw new Error("Document runtime viewport methods are not initialized");
@@ -56134,6 +56450,15 @@ function createDocumentRuntime(options) {
56134
56450
  let cachedSurface;
56135
56451
  let cachedFullSurface;
56136
56452
  const editableTargetBlockCache = createEditableTargetBlockCache();
56453
+ let paragraphCascadeCache = /* @__PURE__ */ new WeakMap();
56454
+ let paragraphCascadeCacheStylesRef = null;
56455
+ function ensureParagraphCascadeCacheForStyles(nextStyles) {
56456
+ if (paragraphCascadeCacheStylesRef !== nextStyles) {
56457
+ paragraphCascadeCache = /* @__PURE__ */ new WeakMap();
56458
+ paragraphCascadeCacheStylesRef = nextStyles;
56459
+ }
56460
+ return paragraphCascadeCache;
56461
+ }
56137
56462
  let cachedEditableTargetMap = null;
56138
56463
  function getEditableTargetsByBlockPath(document2) {
56139
56464
  if (cachedEditableTargetMap !== null && cachedEditableTargetMap.document === document2) {
@@ -56187,6 +56512,7 @@ function createDocumentRuntime(options) {
56187
56512
  const snapshot = createEditorSurfaceSnapshot(document2, state.selection, nextActiveStory, {
56188
56513
  viewportBlockRanges: null,
56189
56514
  editableTargetsByBlockPath: getEditableTargetsByBlockPath(document2),
56515
+ paragraphCascadeCache: ensureParagraphCascadeCacheForStyles(document2.styles),
56190
56516
  ...effectiveMarkupModeProvider ? { getEffectiveMarkupMode: effectiveMarkupModeProvider } : {}
56191
56517
  });
56192
56518
  recordPerfSample("snapshot.surface");
@@ -56227,6 +56553,7 @@ function createDocumentRuntime(options) {
56227
56553
  activeStoryKey,
56228
56554
  surfaceViewportRanges
56229
56555
  ),
56556
+ paragraphCascadeCache: ensureParagraphCascadeCacheForStyles(document2.styles),
56230
56557
  ...effectiveMarkupModeProvider ? { getEffectiveMarkupMode: effectiveMarkupModeProvider } : {}
56231
56558
  });
56232
56559
  recordPerfSample("snapshot.surface");
@@ -56438,7 +56765,17 @@ function createDocumentRuntime(options) {
56438
56765
  }
56439
56766
  }
56440
56767
  function tryPatchNestedLocalTextSurface(previousSurface, editFrom, editTo, insertedText) {
56441
- const shiftBudget = estimateLocalTextPatchShiftBudget(previousSurface.blocks, 0);
56768
+ const containerIndex = previousSurface.blocks.findIndex(
56769
+ (block) => editFrom >= block.from && editTo <= block.to
56770
+ );
56771
+ if (containerIndex < 0) {
56772
+ perfCounters.increment("surface.localText.patchMiss");
56773
+ return null;
56774
+ }
56775
+ const shiftBudget = estimateLocalTextPatchShiftBudget(
56776
+ previousSurface.blocks,
56777
+ containerIndex + 1
56778
+ );
56442
56779
  perfCounters.increment("surface.localText.shiftedBlocks", shiftBudget.shiftedBlocks);
56443
56780
  perfCounters.increment("surface.localText.shiftedNodes", shiftBudget.shiftedNodes);
56444
56781
  if (!shiftBudget.withinBudget) {
@@ -56518,19 +56855,9 @@ function createDocumentRuntime(options) {
56518
56855
  for (let index = startIndex; index < blocks.length; index += 1) {
56519
56856
  const shiftedBlockNodes = countSurfaceShiftNodesUpTo(
56520
56857
  blocks[index],
56521
- Math.min(
56522
- policy.maxShiftedNodesPerBlock,
56523
- policy.maxShiftedSurfaceNodes - shiftedNodes
56524
- ) + 1
56858
+ policy.maxShiftedSurfaceNodes - shiftedNodes + 1
56525
56859
  );
56526
56860
  shiftedNodes += shiftedBlockNodes;
56527
- if (shiftedBlockNodes > policy.maxShiftedNodesPerBlock) {
56528
- return {
56529
- shiftedBlocks,
56530
- shiftedNodes,
56531
- withinBudget: false
56532
- };
56533
- }
56534
56861
  if (shiftedNodes > policy.maxShiftedSurfaceNodes) {
56535
56862
  return {
56536
56863
  shiftedBlocks,
@@ -57202,6 +57529,9 @@ function createDocumentRuntime(options) {
57202
57529
  viewportBlockRangesOverride: firstSurfaceViewportBlockRanges
57203
57530
  }) : getCachedSurface(state.document, activeStory)
57204
57531
  );
57532
+ if (firstSurfaceViewportBlockRanges) {
57533
+ scheduleFullSurfaceWarmupAfterCull();
57534
+ }
57205
57535
  const snapshot = {
57206
57536
  documentId: state.documentId,
57207
57537
  sessionId: state.sessionId,
@@ -57315,6 +57645,9 @@ function createDocumentRuntime(options) {
57315
57645
  activeStoryKey,
57316
57646
  viewportBlockRanges
57317
57647
  ),
57648
+ paragraphCascadeCache: ensureParagraphCascadeCacheForStyles(
57649
+ state.document.styles
57650
+ ),
57318
57651
  ...effectiveMarkupModeProvider ? { getEffectiveMarkupMode: effectiveMarkupModeProvider } : {}
57319
57652
  }
57320
57653
  );
@@ -57965,7 +58298,20 @@ function createDocumentRuntime(options) {
57965
58298
  selection: prepared.selection
57966
58299
  };
57967
58300
  resolvedReplayTextTarget = prepared.textTarget;
57968
- } else if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
58301
+ } else {
58302
+ const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58303
+ command,
58304
+ document: replayState.document,
58305
+ selection: replayState.selection,
58306
+ surface: replaySnapshot.surface?.blocks ?? [],
58307
+ storyTarget: replayStory,
58308
+ timestamp: context.timestamp
58309
+ });
58310
+ if (selectedListItemDeleteCommand) {
58311
+ executableCommand = selectedListItemDeleteCommand;
58312
+ }
58313
+ }
58314
+ if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
57969
58315
  const prepared = prepareModeledTargetCommandForExecution(
57970
58316
  command,
57971
58317
  replayState.document,
@@ -58109,7 +58455,20 @@ function createDocumentRuntime(options) {
58109
58455
  selection: prepared.selection
58110
58456
  };
58111
58457
  resolvedReplayTextTarget = prepared.textTarget;
58112
- } else if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
58458
+ } else {
58459
+ const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58460
+ command,
58461
+ document: stateForCommand.document,
58462
+ selection: stateForCommand.selection,
58463
+ surface: snapshotForCommand.surface?.blocks ?? [],
58464
+ storyTarget: replayStory,
58465
+ timestamp: context.timestamp
58466
+ });
58467
+ if (selectedListItemDeleteCommand) {
58468
+ executableCommand = selectedListItemDeleteCommand;
58469
+ }
58470
+ }
58471
+ if (command.type === "field.refresh" || command.type === "toc.refresh" || command.type === "bookmark.edit-content" || command.type === "hyperlink.update-destination") {
58113
58472
  const prepared = prepareModeledTargetCommandForExecution(
58114
58473
  command,
58115
58474
  stateForCommand.document,
@@ -58530,6 +58889,50 @@ function createDocumentRuntime(options) {
58530
58889
  } catch (error) {
58531
58890
  emitError(toRuntimeError(error));
58532
58891
  }
58892
+ } else if (step.kind === "fragment-replace-tracked" && step.range && step.fragment && Array.isArray(step.fragment.blocks)) {
58893
+ const editableTarget = resolveEditableTargetHint(step.editableTargetHint);
58894
+ if (editableTarget === null) {
58895
+ emit({
58896
+ type: "command_blocked",
58897
+ documentId: state.documentId,
58898
+ command: "applyScopeReplacement",
58899
+ reasons: [{
58900
+ code: "unsupported_surface",
58901
+ message: "Scope replacement editable target no longer resolves."
58902
+ }]
58903
+ });
58904
+ continue;
58905
+ }
58906
+ const dispatchRange = mapSemanticStepRangeToEditableTarget(
58907
+ step.range,
58908
+ step.editableTargetHint,
58909
+ editableTarget?.range
58910
+ );
58911
+ const anchor = {
58912
+ kind: "range",
58913
+ from: dispatchRange.from,
58914
+ to: dispatchRange.to,
58915
+ assoc: { start: -1, end: 1 }
58916
+ };
58917
+ const timestamp = clock();
58918
+ try {
58919
+ applyTextCommandInActiveStory(
58920
+ {
58921
+ type: "fragment.insert-tracked",
58922
+ fragment: step.fragment,
58923
+ ...editableTarget?.target ? { editableTarget: editableTarget.target } : {},
58924
+ origin: createOrigin("api", timestamp)
58925
+ },
58926
+ {
58927
+ selection: createSelectionFromPublicAnchor(anchor),
58928
+ blockedCommandName: "applyScopeReplacement",
58929
+ documentModeOverride: "suggesting",
58930
+ skipWorkflowGuard: true
58931
+ }
58932
+ );
58933
+ } catch (error) {
58934
+ emitError(toRuntimeError(error));
58935
+ }
58533
58936
  } else if (step.kind === "text-insert-tracked" && step.range && typeof step.text === "string") {
58534
58937
  const editableTarget = resolveEditableTargetHint(step.editableTargetHint);
58535
58938
  if (editableTarget === null) {
@@ -60512,6 +60915,39 @@ function createDocumentRuntime(options) {
60512
60915
  const preSelection = selection;
60513
60916
  const preActiveStory = activeStory;
60514
60917
  const priorDocument = state.document;
60918
+ const selectedListItemDelete = createSelectedListItemDeleteReplacement({
60919
+ command: commandForDispatch,
60920
+ document: state.document,
60921
+ editableTarget,
60922
+ selection,
60923
+ storyTarget: activeStory,
60924
+ targetResolution,
60925
+ timestamp
60926
+ });
60927
+ if (selectedListItemDelete && context.documentMode !== "suggesting") {
60928
+ const replacementCommand = {
60929
+ type: "document.replace",
60930
+ document: selectedListItemDelete.document,
60931
+ selection: selectedListItemDelete.selection,
60932
+ mapping: selectedListItemDelete.mapping,
60933
+ protectionSelection: selection,
60934
+ origin: commandForDispatch.origin
60935
+ };
60936
+ const transaction = executeEditorCommand(baseState, replacementCommand, context);
60937
+ commit(transaction);
60938
+ options.onCommandApplied?.(commandForDispatch, transaction, context, {
60939
+ preSelection,
60940
+ activeStory: preActiveStory,
60941
+ priorDocument
60942
+ });
60943
+ return completeDispatch(classifyAck({
60944
+ command: commandForDispatch,
60945
+ opId,
60946
+ priorState: baseState,
60947
+ transaction,
60948
+ newRevisionToken: state.revisionToken
60949
+ }));
60950
+ }
60515
60951
  if (activeStory.kind === "main") {
60516
60952
  const mainTransaction = executeEditorCommand(baseState, commandForDispatch, context);
60517
60953
  commit(mainTransaction);
@@ -60753,6 +61189,9 @@ function createDocumentRuntime(options) {
60753
61189
  });
60754
61190
  }
60755
61191
  function scheduleIdleContextAnalytics(callback) {
61192
+ runOnIdle(callback);
61193
+ }
61194
+ function runOnIdle(callback) {
60756
61195
  const requestIdle = globalThis.requestIdleCallback;
60757
61196
  if (typeof requestIdle === "function") {
60758
61197
  requestIdle(callback, { timeout: 250 });
@@ -61706,7 +62145,8 @@ function getStoryPlainText(document2, storyTarget, cache) {
61706
62145
  const plainText = createEditorSurfaceSnapshot(
61707
62146
  document2,
61708
62147
  createSelectionSnapshot(0, 0),
61709
- storyTarget
62148
+ storyTarget,
62149
+ { editableTargetsByBlockPath: NO_EDITABLE_TARGETS_INDEX }
61710
62150
  ).plainText;
61711
62151
  cache.set(key, plainText);
61712
62152
  return plainText;
@@ -63391,6 +63831,258 @@ function stripStoryTarget(selection) {
63391
63831
  function isTopLevelMainStoryBlockPath(blockPath) {
63392
63832
  return typeof blockPath === "string" && /^main\/block\[\d+\]$/u.test(blockPath);
63393
63833
  }
63834
+ function createSelectedListItemDeleteReplacement(input) {
63835
+ const { command, document: document2, editableTarget, selection, storyTarget, targetResolution, timestamp } = input;
63836
+ if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
63837
+ return null;
63838
+ }
63839
+ if (selection.isCollapsed || editableTarget?.listAddress?.operationScope !== "list-text" || targetResolution?.kind !== "accepted") {
63840
+ return null;
63841
+ }
63842
+ const selectionFrom = Math.min(selection.anchor, selection.head);
63843
+ const selectionTo = Math.max(selection.anchor, selection.head);
63844
+ if (selectionFrom !== targetResolution.range.from || selectionTo !== targetResolution.range.to) {
63845
+ return null;
63846
+ }
63847
+ const storyBlocks = getStoryBlocks(document2, storyTarget);
63848
+ const replacement = removeNumberedParagraphAtStoryPath(storyBlocks, editableTarget.blockPath, storyTarget);
63849
+ if (!replacement) {
63850
+ return null;
63851
+ }
63852
+ const nextDocument = replaceStoryBlocks({
63853
+ ...document2,
63854
+ updatedAt: timestamp
63855
+ }, storyTarget, replacement.blocks);
63856
+ const nextStorySize = parseTextStory({
63857
+ type: "doc",
63858
+ children: [...getStoryBlocks(nextDocument, storyTarget)]
63859
+ }).size;
63860
+ const nextAnchor = Math.min(selectionFrom, nextStorySize);
63861
+ return {
63862
+ document: nextDocument,
63863
+ selection: createSelectionSnapshot(nextAnchor, nextAnchor),
63864
+ mapping: {
63865
+ steps: [{
63866
+ from: selectionFrom,
63867
+ to: selectionTo,
63868
+ insertSize: 0
63869
+ }],
63870
+ metadata: {
63871
+ invalidatesStructures: true
63872
+ }
63873
+ }
63874
+ };
63875
+ }
63876
+ function removeNumberedParagraphAtStoryPath(blocks, blockPath, storyTarget) {
63877
+ const tokens = parseStoryBlockPathTokens(blockPath, storyTarget);
63878
+ if (!tokens) return null;
63879
+ return removeNumberedParagraphFromBlocks(blocks, tokens);
63880
+ }
63881
+ function removeNumberedParagraphFromBlocks(blocks, tokens) {
63882
+ const [token, ...rest] = tokens;
63883
+ if (!token || token.kind !== "block") return null;
63884
+ const block = blocks[token.index];
63885
+ if (!block) return null;
63886
+ if (rest.length === 0) {
63887
+ if (block.type !== "paragraph" || !block.numbering) {
63888
+ return null;
63889
+ }
63890
+ if (blocks.length === 1) {
63891
+ return { blocks: [{ type: "paragraph", children: [] }] };
63892
+ }
63893
+ return {
63894
+ blocks: [
63895
+ ...blocks.slice(0, token.index),
63896
+ ...blocks.slice(token.index + 1)
63897
+ ]
63898
+ };
63899
+ }
63900
+ const next = rest[0];
63901
+ if (block.type === "table" && next?.kind === "row") {
63902
+ const updatedTable = removeNumberedParagraphFromTable(block, rest);
63903
+ if (!updatedTable) return null;
63904
+ return {
63905
+ blocks: [
63906
+ ...blocks.slice(0, token.index),
63907
+ updatedTable,
63908
+ ...blocks.slice(token.index + 1)
63909
+ ]
63910
+ };
63911
+ }
63912
+ if ((block.type === "sdt" || block.type === "custom_xml") && next?.kind === "block") {
63913
+ const updatedChildren = removeNumberedParagraphFromBlocks(block.children, rest);
63914
+ if (!updatedChildren) return null;
63915
+ return {
63916
+ blocks: [
63917
+ ...blocks.slice(0, token.index),
63918
+ { ...block, children: updatedChildren.blocks },
63919
+ ...blocks.slice(token.index + 1)
63920
+ ]
63921
+ };
63922
+ }
63923
+ if (block.type === "paragraph" && next?.kind === "inline") {
63924
+ const updatedParagraph = removeNumberedParagraphFromTextBoxInline(block, rest);
63925
+ if (!updatedParagraph) return null;
63926
+ return {
63927
+ blocks: [
63928
+ ...blocks.slice(0, token.index),
63929
+ updatedParagraph,
63930
+ ...blocks.slice(token.index + 1)
63931
+ ]
63932
+ };
63933
+ }
63934
+ return null;
63935
+ }
63936
+ function removeNumberedParagraphFromTextBoxInline(paragraph, tokens) {
63937
+ const [inlineToken, textBoxToken, ...childTokens] = tokens;
63938
+ if (inlineToken?.kind !== "inline" || textBoxToken?.kind !== "txbx" || childTokens[0]?.kind !== "block") {
63939
+ return null;
63940
+ }
63941
+ const inline = paragraph.children[inlineToken.index];
63942
+ if (!inline) return null;
63943
+ const updatedInline = removeNumberedParagraphFromInlineTextBox(inline, childTokens);
63944
+ if (!updatedInline) return null;
63945
+ return {
63946
+ ...paragraph,
63947
+ children: [
63948
+ ...paragraph.children.slice(0, inlineToken.index),
63949
+ updatedInline,
63950
+ ...paragraph.children.slice(inlineToken.index + 1)
63951
+ ]
63952
+ };
63953
+ }
63954
+ function removeNumberedParagraphFromInlineTextBox(inline, tokens) {
63955
+ if (inline.type === "shape" && inline.txbxBlocks) {
63956
+ const updatedBlocks = removeNumberedParagraphFromBlocks(inline.txbxBlocks, tokens);
63957
+ return updatedBlocks ? { ...inline, txbxBlocks: updatedBlocks.blocks } : null;
63958
+ }
63959
+ if (inline.type === "drawing_frame" && inline.content.type === "shape" && inline.content.txbxBlocks) {
63960
+ const updatedBlocks = removeNumberedParagraphFromBlocks(inline.content.txbxBlocks, tokens);
63961
+ return updatedBlocks ? {
63962
+ ...inline,
63963
+ content: {
63964
+ ...inline.content,
63965
+ txbxBlocks: updatedBlocks.blocks
63966
+ }
63967
+ } : null;
63968
+ }
63969
+ return null;
63970
+ }
63971
+ function removeNumberedParagraphFromTable(table, tokens) {
63972
+ const [rowToken, cellToken, ...childTokens] = tokens;
63973
+ if (rowToken?.kind !== "row" || cellToken?.kind !== "cell" || childTokens[0]?.kind !== "block") {
63974
+ return null;
63975
+ }
63976
+ const row2 = table.rows[rowToken.index];
63977
+ const cell = row2?.cells[cellToken.index];
63978
+ if (!row2 || !cell) return null;
63979
+ const updatedChildren = removeNumberedParagraphFromBlocks(cell.children, childTokens);
63980
+ if (!updatedChildren) return null;
63981
+ const nextCells = [
63982
+ ...row2.cells.slice(0, cellToken.index),
63983
+ { ...cell, children: updatedChildren.blocks },
63984
+ ...row2.cells.slice(cellToken.index + 1)
63985
+ ];
63986
+ const nextRows = [
63987
+ ...table.rows.slice(0, rowToken.index),
63988
+ { ...row2, cells: nextCells },
63989
+ ...table.rows.slice(rowToken.index + 1)
63990
+ ];
63991
+ return { ...table, rows: nextRows };
63992
+ }
63993
+ function createSelectedListItemDeleteReplayCommand(input) {
63994
+ const { command, document: document2, selection, surface, storyTarget, timestamp } = input;
63995
+ if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
63996
+ return null;
63997
+ }
63998
+ const editableTarget = command.editableTarget;
63999
+ if (!editableTarget) {
64000
+ return null;
64001
+ }
64002
+ const targetResolution = resolveEditableTextTarget({
64003
+ document: document2,
64004
+ selection,
64005
+ surface,
64006
+ target: editableTarget,
64007
+ activeStoryKey: canonicalEditableTargetStoryKey(storyTarget)
64008
+ });
64009
+ const resolvedEditableTarget = targetResolution.kind === "accepted" ? editableTarget : storyTarget.kind !== "main" && blockPathBelongsToStoryTarget(editableTarget.blockPath, storyTarget) ? resolveEditableCommandTarget({
64010
+ document: document2,
64011
+ target: editableTarget,
64012
+ activeStoryKey: canonicalEditableTargetStoryKey(storyTarget),
64013
+ commandFamilies: ["text-leaf"]
64014
+ }) : null;
64015
+ const selectedRange = {
64016
+ from: Math.min(selection.anchor, selection.head),
64017
+ to: Math.max(selection.anchor, selection.head)
64018
+ };
64019
+ const replacement = createSelectedListItemDeleteReplacement({
64020
+ command,
64021
+ document: document2,
64022
+ editableTarget: resolvedEditableTarget && resolvedEditableTarget.kind === "accepted" ? resolvedEditableTarget.target : editableTarget,
64023
+ selection,
64024
+ storyTarget,
64025
+ targetResolution: resolvedEditableTarget && resolvedEditableTarget.kind === "accepted" ? {
64026
+ kind: "accepted",
64027
+ range: selectedRange
64028
+ } : targetResolution,
64029
+ timestamp
64030
+ });
64031
+ if (!replacement) {
64032
+ return null;
64033
+ }
64034
+ return {
64035
+ type: "document.replace",
64036
+ document: replacement.document,
64037
+ selection: replacement.selection,
64038
+ mapping: replacement.mapping,
64039
+ protectionSelection: selection,
64040
+ origin: command.origin
64041
+ };
64042
+ }
64043
+ function parseStoryBlockPathTokens(blockPath, storyTarget) {
64044
+ if (!blockPath) {
64045
+ return null;
64046
+ }
64047
+ const firstTokenMatch = /\/(?:block|row|cell)\[\d+\]/u.exec(blockPath);
64048
+ if (!firstTokenMatch?.index) {
64049
+ return null;
64050
+ }
64051
+ const storyPrefix = blockPath.slice(0, firstTokenMatch.index);
64052
+ if (!blockPathBelongsToStoryTarget(storyPrefix, storyTarget)) {
64053
+ return null;
64054
+ }
64055
+ const parts = blockPath.slice(firstTokenMatch.index + 1).split("/");
64056
+ const tokens = [];
64057
+ for (const part of parts) {
64058
+ if (part === "txbx") {
64059
+ tokens.push({ kind: "txbx" });
64060
+ continue;
64061
+ }
64062
+ const match = /^(block|row|cell|inline)\[(\d+)\]$/u.exec(part);
64063
+ if (!match) return null;
64064
+ const kind = match[1];
64065
+ tokens.push({
64066
+ kind,
64067
+ index: Number.parseInt(match[2], 10)
64068
+ });
64069
+ }
64070
+ return tokens;
64071
+ }
64072
+ function blockPathBelongsToStoryTarget(storyPrefix, storyTarget) {
64073
+ switch (storyTarget.kind) {
64074
+ case "main":
64075
+ return storyPrefix === "main" || storyPrefix.startsWith("main/");
64076
+ case "header":
64077
+ return storyPrefix.startsWith("header:");
64078
+ case "footer":
64079
+ return storyPrefix.startsWith("footer:");
64080
+ case "footnote":
64081
+ return storyPrefix === `footnote:${storyTarget.noteId}` || storyPrefix.startsWith(`footnote:${storyTarget.noteId}/`);
64082
+ case "endnote":
64083
+ return storyPrefix === `endnote:${storyTarget.noteId}` || storyPrefix.startsWith(`endnote:${storyTarget.noteId}/`);
64084
+ }
64085
+ }
63394
64086
  function toInternalSelectionSnapshot2(selection) {
63395
64087
  return {
63396
64088
  anchor: selection.anchor,