@beyondwork/docx-react-component 1.0.135 → 1.0.137

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 (62) hide show
  1. package/dist/api/public-types.d.cts +2 -2
  2. package/dist/api/public-types.d.ts +2 -2
  3. package/dist/api/v3.cjs +827 -59
  4. package/dist/api/v3.d.cts +3 -3
  5. package/dist/api/v3.d.ts +3 -3
  6. package/dist/api/v3.js +2 -2
  7. package/dist/{canonical-document-CfZIc-fC.d.cts → canonical-document-COmM7v11.d.cts} +1 -1
  8. package/dist/{canonical-document-CfZIc-fC.d.ts → canonical-document-COmM7v11.d.ts} +1 -1
  9. package/dist/{chunk-EPFVMUKF.js → chunk-M3AEVSKU.js} +793 -56
  10. package/dist/{chunk-5CCYF333.js → chunk-NYIMPM3Z.js} +143 -1
  11. package/dist/{chunk-GIFXKIM5.js → chunk-SMBDQV73.js} +688 -57
  12. package/dist/compare.d.cts +1 -1
  13. package/dist/compare.d.ts +1 -1
  14. package/dist/core/commands/formatting-commands.d.cts +2 -2
  15. package/dist/core/commands/formatting-commands.d.ts +2 -2
  16. package/dist/core/commands/image-commands.d.cts +2 -2
  17. package/dist/core/commands/image-commands.d.ts +2 -2
  18. package/dist/core/commands/section-layout-commands.d.cts +2 -2
  19. package/dist/core/commands/section-layout-commands.d.ts +2 -2
  20. package/dist/core/commands/style-commands.d.cts +2 -2
  21. package/dist/core/commands/style-commands.d.ts +2 -2
  22. package/dist/core/commands/table-structure-commands.d.cts +2 -2
  23. package/dist/core/commands/table-structure-commands.d.ts +2 -2
  24. package/dist/core/commands/text-commands.d.cts +2 -2
  25. package/dist/core/commands/text-commands.d.ts +2 -2
  26. package/dist/core/selection/mapping.d.cts +2 -2
  27. package/dist/core/selection/mapping.d.ts +2 -2
  28. package/dist/core/state/editor-state.d.cts +2 -2
  29. package/dist/core/state/editor-state.d.ts +2 -2
  30. package/dist/index.cjs +1808 -190
  31. package/dist/index.d.cts +5 -5
  32. package/dist/index.d.ts +5 -5
  33. package/dist/index.js +144 -32
  34. package/dist/io/docx-session.d.cts +4 -4
  35. package/dist/io/docx-session.d.ts +4 -4
  36. package/dist/legal.d.cts +1 -1
  37. package/dist/legal.d.ts +1 -1
  38. package/dist/{loader-BQ7AB-0v.d.cts → loader-BB7tRkY2.d.cts} +3 -3
  39. package/dist/{loader-Cy6OYBfn.d.ts → loader-DGPbaboy.d.ts} +3 -3
  40. package/dist/{public-types-D31xKNGc.d.cts → public-types-CIvw1GQa.d.cts} +142 -9
  41. package/dist/{public-types-DqYt8GdP.d.ts → public-types-COCDrgVX.d.ts} +142 -9
  42. package/dist/public-types.d.cts +2 -2
  43. package/dist/public-types.d.ts +2 -2
  44. package/dist/runtime/collab.d.cts +3 -3
  45. package/dist/runtime/collab.d.ts +3 -3
  46. package/dist/runtime/document-runtime.cjs +825 -54
  47. package/dist/runtime/document-runtime.d.cts +2 -2
  48. package/dist/runtime/document-runtime.d.ts +2 -2
  49. package/dist/runtime/document-runtime.js +2 -2
  50. package/dist/{session-DA-F2fCw.d.cts → session-CVU-rtCd.d.cts} +3 -3
  51. package/dist/{session-DqL8H0oZ.d.ts → session-DOqy1-Lf.d.ts} +3 -3
  52. package/dist/session.d.cts +5 -5
  53. package/dist/session.d.ts +5 -5
  54. package/dist/tailwind.d.cts +2 -2
  55. package/dist/tailwind.d.ts +2 -2
  56. package/dist/{types-SllbCtGs.d.ts → types-CB_tOnVp.d.ts} +2 -2
  57. package/dist/{types-B2y94n5t.d.cts → types-CZAll7OS.d.cts} +2 -2
  58. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
  59. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
  60. package/dist/ui-tailwind.d.cts +3 -3
  61. package/dist/ui-tailwind.d.ts +3 -3
  62. package/package.json +1 -1
@@ -19712,6 +19712,106 @@ function setStartOverride(catalog, numberingInstanceId, level, startAt) {
19712
19712
  overrides: mergeOverride(instance.overrides, { level, startAt })
19713
19713
  };
19714
19714
  }
19715
+ function mergeFragmentNumberingCatalog(target, fragment) {
19716
+ const catalog = cloneNumberingCatalog(target);
19717
+ const abstractIdMap = /* @__PURE__ */ new Map();
19718
+ const instanceIdMap = /* @__PURE__ */ new Map();
19719
+ const picBulletIdMap = /* @__PURE__ */ new Map();
19720
+ if (!fragment) {
19721
+ return { catalog, abstractIdMap, instanceIdMap, picBulletIdMap };
19722
+ }
19723
+ for (const [picBulletId, entry] of Object.entries(fragment.numPicBullets ?? {})) {
19724
+ const mappedId = mapPicBulletId(catalog, picBulletId, entry);
19725
+ picBulletIdMap.set(picBulletId, mappedId);
19726
+ }
19727
+ for (const [abstractId, definition] of Object.entries(fragment.abstractDefinitions)) {
19728
+ const mappedId = mapAbstractDefinition(catalog, definition, picBulletIdMap);
19729
+ abstractIdMap.set(abstractId, mappedId);
19730
+ }
19731
+ for (const [instanceId, instance] of Object.entries(fragment.instances)) {
19732
+ const mappedAbstractId = abstractIdMap.get(instance.abstractNumberingId) ?? instance.abstractNumberingId;
19733
+ const mappedId = mapNumberingInstance(
19734
+ catalog,
19735
+ instance,
19736
+ mappedAbstractId,
19737
+ picBulletIdMap
19738
+ );
19739
+ instanceIdMap.set(instanceId, mappedId);
19740
+ }
19741
+ return { catalog, abstractIdMap, instanceIdMap, picBulletIdMap };
19742
+ }
19743
+ function mapPicBulletId(catalog, picBulletId, entry) {
19744
+ const existing = catalog.numPicBullets?.[picBulletId];
19745
+ if (existing && existing.rawXml === entry.rawXml) return picBulletId;
19746
+ const mappedId = existing ? nextNumericString(Object.keys(catalog.numPicBullets ?? {})) : picBulletId;
19747
+ const numPicBullets = { ...catalog.numPicBullets ?? {} };
19748
+ numPicBullets[mappedId] = {
19749
+ ...stripSourceRef(structuredClone(entry)),
19750
+ numPicBulletId: mappedId
19751
+ };
19752
+ catalog.numPicBullets = numPicBullets;
19753
+ return mappedId;
19754
+ }
19755
+ function mapAbstractDefinition(catalog, definition, picBulletIdMap) {
19756
+ const existing = catalog.abstractDefinitions[definition.abstractNumberingId];
19757
+ if (existing && sameJson(comparableAbstract(existing), comparableAbstract(definition))) {
19758
+ return definition.abstractNumberingId;
19759
+ }
19760
+ const mappedId = existing ? nextCanonicalNumericId("abstract-num:", Object.keys(catalog.abstractDefinitions)) : definition.abstractNumberingId;
19761
+ catalog.abstractDefinitions[mappedId] = {
19762
+ ...stripSourceRef(structuredClone(definition)),
19763
+ abstractNumberingId: mappedId,
19764
+ nsid: freshLongHex(catalog, mappedId, "nsid"),
19765
+ tplc: freshLongHex(catalog, mappedId, "tmpl"),
19766
+ levels: definition.levels.map((level) => remapLevelPicBullet(level, picBulletIdMap))
19767
+ };
19768
+ return mappedId;
19769
+ }
19770
+ function mapNumberingInstance(catalog, instance, abstractNumberingId, picBulletIdMap) {
19771
+ const existing = catalog.instances[instance.numberingInstanceId];
19772
+ const candidate = {
19773
+ ...instance,
19774
+ abstractNumberingId,
19775
+ overrides: instance.overrides.map(
19776
+ (override) => remapOverridePicBullet(override, picBulletIdMap)
19777
+ )
19778
+ };
19779
+ if (existing && sameJson(comparableInstance(existing), comparableInstance(candidate))) {
19780
+ return instance.numberingInstanceId;
19781
+ }
19782
+ const mappedId = existing ? nextCanonicalNumericId("num:", Object.keys(catalog.instances)) : instance.numberingInstanceId;
19783
+ catalog.instances[mappedId] = {
19784
+ ...stripSourceRef(structuredClone(instance)),
19785
+ overrides: candidate.overrides.map((override) => stripOverrideSourceRefs(override)),
19786
+ numberingInstanceId: mappedId,
19787
+ abstractNumberingId
19788
+ };
19789
+ return mappedId;
19790
+ }
19791
+ function remapLevelPicBullet(level, picBulletIdMap) {
19792
+ const cloned = stripSourceRef(structuredClone(level));
19793
+ if (!level.picBulletId) return cloned;
19794
+ return {
19795
+ ...cloned,
19796
+ picBulletId: picBulletIdMap.get(level.picBulletId) ?? level.picBulletId
19797
+ };
19798
+ }
19799
+ function remapOverridePicBullet(override, picBulletIdMap) {
19800
+ const cloned = stripOverrideSourceRefs(structuredClone(override));
19801
+ if (!override.levelDefinition) return cloned;
19802
+ return {
19803
+ ...cloned,
19804
+ levelDefinition: remapLevelPicBullet(override.levelDefinition, picBulletIdMap)
19805
+ };
19806
+ }
19807
+ function stripOverrideSourceRefs(override) {
19808
+ const cloned = stripSourceRef(override);
19809
+ if (!override.levelDefinition) return cloned;
19810
+ return {
19811
+ ...cloned,
19812
+ levelDefinition: stripSourceRef(override.levelDefinition)
19813
+ };
19814
+ }
19715
19815
  function mergeOverride(overrides, nextOverride) {
19716
19816
  return [
19717
19817
  ...overrides.filter((override) => override.level !== nextOverride.level),
@@ -19783,6 +19883,28 @@ function hashLongHex(input) {
19783
19883
  }
19784
19884
  return hash.toString(16).toUpperCase().padStart(8, "0").slice(-8);
19785
19885
  }
19886
+ function stripSourceRef(value) {
19887
+ const { sourceRef: _sourceRef, ...rest } = value;
19888
+ return rest;
19889
+ }
19890
+ function comparableAbstract(definition) {
19891
+ return {
19892
+ ...stripSourceRef(definition),
19893
+ levels: definition.levels.map((level) => stripSourceRef(level))
19894
+ };
19895
+ }
19896
+ function comparableInstance(instance) {
19897
+ return {
19898
+ ...stripSourceRef(instance),
19899
+ overrides: instance.overrides.map((override) => ({
19900
+ ...stripSourceRef(override),
19901
+ ...override.levelDefinition ? { levelDefinition: stripSourceRef(override.levelDefinition) } : {}
19902
+ }))
19903
+ };
19904
+ }
19905
+ function sameJson(left, right) {
19906
+ return JSON.stringify(left) === JSON.stringify(right);
19907
+ }
19786
19908
 
19787
19909
  // src/core/commands/list-commands.ts
19788
19910
  function toggleNumberedList(document2, paragraphIndexes, context, options = {}) {
@@ -19821,7 +19943,7 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
19821
19943
  affectedParagraphIndexes: []
19822
19944
  };
19823
19945
  }
19824
- const target = paragraphs[resolvedParagraphIndex];
19946
+ const target = paragraphs[resolvedParagraphIndex]?.paragraph;
19825
19947
  if (!target?.numbering) {
19826
19948
  return {
19827
19949
  document: working,
@@ -19843,8 +19965,13 @@ function restartNumbering(document2, paragraphIndex, context, startAt = 1, optio
19843
19965
  );
19844
19966
  setStartOverride(catalog, numberingInstanceId, target.numbering.level, startAt);
19845
19967
  const affectedParagraphIndexes = [];
19968
+ const targetStoryKey = paragraphs[resolvedParagraphIndex]?.storyKey;
19846
19969
  for (let index = resolvedParagraphIndex; index < paragraphs.length; index += 1) {
19847
- const paragraph = paragraphs[index];
19970
+ const entry = paragraphs[index];
19971
+ if (entry?.storyKey !== targetStoryKey) {
19972
+ break;
19973
+ }
19974
+ const paragraph = entry.paragraph;
19848
19975
  if (!paragraph?.numbering) {
19849
19976
  break;
19850
19977
  }
@@ -19888,7 +20015,7 @@ function continueNumbering(document2, paragraphIndex, context, options = {}) {
19888
20015
  affectedParagraphIndexes: []
19889
20016
  };
19890
20017
  }
19891
- const target = paragraphs[resolvedParagraphIndex];
20018
+ const target = paragraphs[resolvedParagraphIndex]?.paragraph;
19892
20019
  if (!target?.numbering) {
19893
20020
  return {
19894
20021
  document: working,
@@ -19917,8 +20044,13 @@ function continueNumbering(document2, paragraphIndex, context, options = {}) {
19917
20044
  };
19918
20045
  }
19919
20046
  const affectedParagraphIndexes = [];
20047
+ const targetStoryKey = paragraphs[resolvedParagraphIndex]?.storyKey;
19920
20048
  for (let index = resolvedParagraphIndex; index < paragraphs.length; index += 1) {
19921
- const paragraph = paragraphs[index];
20049
+ const entry = paragraphs[index];
20050
+ if (entry?.storyKey !== targetStoryKey) {
20051
+ break;
20052
+ }
20053
+ const paragraph = entry.paragraph;
19922
20054
  if (!paragraph?.numbering) {
19923
20055
  break;
19924
20056
  }
@@ -19964,12 +20096,13 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
19964
20096
  }
19965
20097
  const catalog = cloneNumberingCatalog(working.numbering);
19966
20098
  const allAlreadyKind = normalizedIndexes.every((index) => {
19967
- const paragraph = paragraphs[index];
19968
- return paragraph.numbering ? getListKind(catalog, paragraph.numbering.numberingInstanceId) === kind : false;
20099
+ const paragraph = paragraphs[index]?.paragraph;
20100
+ return paragraph?.numbering ? getListKind(catalog, paragraph.numbering.numberingInstanceId) === kind : false;
19969
20101
  });
19970
20102
  if (allAlreadyKind) {
19971
20103
  for (const index of normalizedIndexes) {
19972
- delete paragraphs[index].numbering;
20104
+ const paragraph = paragraphs[index]?.paragraph;
20105
+ if (paragraph) delete paragraph.numbering;
19973
20106
  }
19974
20107
  working.numbering = catalog;
19975
20108
  return {
@@ -19979,7 +20112,8 @@ function toggleListKind(document2, paragraphIndexes, kind, context, options) {
19979
20112
  }
19980
20113
  const numberingInstanceId = findAdjacentCompatibleInstance(paragraphs, catalog, normalizedIndexes[0], kind) ?? ensureDefaultListInstance(catalog, kind);
19981
20114
  for (const index of normalizedIndexes) {
19982
- const paragraph = paragraphs[index];
20115
+ const paragraph = paragraphs[index]?.paragraph;
20116
+ if (!paragraph) continue;
19983
20117
  paragraph.numbering = {
19984
20118
  numberingInstanceId,
19985
20119
  level: clampLevel(paragraph.numbering?.level ?? 0)
@@ -20010,10 +20144,11 @@ function adjustListLevels(document2, paragraphIndexes, delta, context, options)
20010
20144
  };
20011
20145
  }
20012
20146
  const affectedParagraphIndexes = resolved.paragraphIndexes.filter(
20013
- (index) => Boolean(paragraphs[index]?.numbering)
20147
+ (index) => Boolean(paragraphs[index]?.paragraph.numbering)
20014
20148
  );
20015
20149
  for (const index of affectedParagraphIndexes) {
20016
- const paragraph = paragraphs[index];
20150
+ const paragraph = paragraphs[index]?.paragraph;
20151
+ if (!paragraph) continue;
20017
20152
  if (!paragraph.numbering) {
20018
20153
  continue;
20019
20154
  }
@@ -20033,17 +20168,21 @@ function normalizeListCommandTargets(options) {
20033
20168
  }
20034
20169
  function resolveListCommandParagraphIndexes(document2, paragraphs, paragraphIndexes, context, editableTargets) {
20035
20170
  if (editableTargets.length === 0) {
20036
- return { paragraphIndexes: normalizeParagraphIndexes(paragraphs, paragraphIndexes) };
20171
+ return {
20172
+ paragraphIndexes: normalizeParagraphIndexesForStory(
20173
+ paragraphs,
20174
+ paragraphIndexes,
20175
+ context.activeStoryKey
20176
+ )
20177
+ };
20037
20178
  }
20038
20179
  const currentTargets = collectEditableTargetRefs(document2, context.editableTargetCache);
20039
- const paragraphIndexByTargetKey = /* @__PURE__ */ new Map();
20040
- let paragraphIndex = 0;
20041
- for (const target of currentTargets) {
20042
- if (!isParagraphTextTarget(target)) continue;
20043
- if (!paragraphIndexByTargetKey.has(target.targetKey)) {
20044
- paragraphIndexByTargetKey.set(target.targetKey, paragraphIndex);
20045
- paragraphIndex += 1;
20046
- }
20180
+ const paragraphIndexByAddress = /* @__PURE__ */ new Map();
20181
+ for (let index = 0; index < paragraphs.length; index += 1) {
20182
+ const entry = paragraphs[index];
20183
+ if (!entry) continue;
20184
+ paragraphIndexByAddress.set(`${entry.storyKey}
20185
+ ${entry.blockPath}`, index);
20047
20186
  }
20048
20187
  const resolvedIndexes = [];
20049
20188
  for (const target of editableTargets) {
@@ -20068,7 +20207,8 @@ function resolveListCommandParagraphIndexes(document2, paragraphs, paragraphInde
20068
20207
  }
20069
20208
  };
20070
20209
  }
20071
- const currentIndex = paragraphIndexByTargetKey.get(current.targetKey);
20210
+ const currentIndex = paragraphIndexByAddress.get(`${current.storyKey}
20211
+ ${current.blockPath}`);
20072
20212
  if (currentIndex === void 0) {
20073
20213
  return {
20074
20214
  paragraphIndexes: [],
@@ -20143,18 +20283,28 @@ function sortJson(value) {
20143
20283
  return value;
20144
20284
  }
20145
20285
  function findAdjacentCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
20286
+ const current = paragraphs[fromIndex];
20146
20287
  const previous = paragraphs[fromIndex - 1];
20147
- if (previous?.numbering) {
20148
- const previousKind = getListKind(catalog, previous.numbering.numberingInstanceId);
20288
+ if (previous?.storyKey !== current?.storyKey) {
20289
+ return void 0;
20290
+ }
20291
+ const previousParagraph = previous?.paragraph;
20292
+ if (previousParagraph?.numbering) {
20293
+ const previousKind = getListKind(catalog, previousParagraph.numbering.numberingInstanceId);
20149
20294
  if (previousKind === kind) {
20150
- return previous.numbering.numberingInstanceId;
20295
+ return previousParagraph.numbering.numberingInstanceId;
20151
20296
  }
20152
20297
  }
20153
20298
  return void 0;
20154
20299
  }
20155
20300
  function findPreviousCompatibleInstance(paragraphs, catalog, fromIndex, kind) {
20301
+ const storyKey2 = paragraphs[fromIndex]?.storyKey;
20156
20302
  for (let index = fromIndex - 1; index >= 0; index -= 1) {
20157
- const paragraph = paragraphs[index];
20303
+ const entry = paragraphs[index];
20304
+ if (entry?.storyKey !== storyKey2) {
20305
+ break;
20306
+ }
20307
+ const paragraph = entry.paragraph;
20158
20308
  if (!paragraph?.numbering) {
20159
20309
  continue;
20160
20310
  }
@@ -20173,7 +20323,14 @@ function cloneEnvelope(document2, timestamp) {
20173
20323
  function captureEditableParagraphs(document2) {
20174
20324
  if (isDocumentRoot(document2.content)) {
20175
20325
  const paragraphs = [];
20176
- collectEditableParagraphs(document2.content.children, paragraphs);
20326
+ for (const context of collectStoryBlockContexts(document2)) {
20327
+ collectEditableParagraphs(
20328
+ context.blocks,
20329
+ paragraphs,
20330
+ context.storyKey,
20331
+ context.basePath
20332
+ );
20333
+ }
20177
20334
  return paragraphs;
20178
20335
  }
20179
20336
  const fallback = {
@@ -20181,23 +20338,39 @@ function captureEditableParagraphs(document2) {
20181
20338
  children: [{ type: "paragraph", children: [] }]
20182
20339
  };
20183
20340
  document2.content = fallback;
20184
- return fallback.children;
20341
+ return [{
20342
+ paragraph: fallback.children[0],
20343
+ storyKey: "main",
20344
+ blockPath: "main/block[0]"
20345
+ }];
20185
20346
  }
20186
- function collectEditableParagraphs(blocks, output) {
20187
- for (const block of blocks) {
20347
+ function collectEditableParagraphs(blocks, output, storyKey2, basePath) {
20348
+ for (let blockIndex = 0; blockIndex < blocks.length; blockIndex += 1) {
20349
+ const block = blocks[blockIndex];
20350
+ if (!block) continue;
20351
+ const blockPath = `${basePath}/block[${blockIndex}]`;
20188
20352
  switch (block.type) {
20189
20353
  case "paragraph":
20190
- output.push(block);
20354
+ output.push({ paragraph: block, storyKey: storyKey2, blockPath });
20191
20355
  break;
20192
20356
  case "table":
20193
- for (const row2 of block.rows) {
20194
- for (const cell of row2.cells) {
20195
- collectEditableParagraphs(cell.children, output);
20357
+ for (let rowIndex = 0; rowIndex < block.rows.length; rowIndex += 1) {
20358
+ const row2 = block.rows[rowIndex];
20359
+ if (!row2) continue;
20360
+ for (let cellIndex = 0; cellIndex < row2.cells.length; cellIndex += 1) {
20361
+ const cell = row2.cells[cellIndex];
20362
+ if (!cell) continue;
20363
+ collectEditableParagraphs(
20364
+ cell.children,
20365
+ output,
20366
+ storyKey2,
20367
+ `${blockPath}/row[${rowIndex}]/cell[${cellIndex}]`
20368
+ );
20196
20369
  }
20197
20370
  }
20198
20371
  break;
20199
20372
  case "sdt":
20200
- collectEditableParagraphs(block.children, output);
20373
+ collectEditableParagraphs(block.children, output, storyKey2, blockPath);
20201
20374
  break;
20202
20375
  case "custom_xml":
20203
20376
  break;
@@ -20209,6 +20382,18 @@ function collectEditableParagraphs(blocks, output) {
20209
20382
  function normalizeParagraphIndexes(paragraphs, paragraphIndexes) {
20210
20383
  return [...new Set(paragraphIndexes)].filter((index) => Number.isInteger(index) && index >= 0 && index < paragraphs.length).sort((left, right) => left - right);
20211
20384
  }
20385
+ function normalizeParagraphIndexesForStory(paragraphs, paragraphIndexes, storyKey2) {
20386
+ if (storyKey2 === void 0) {
20387
+ return normalizeParagraphIndexes(paragraphs, paragraphIndexes);
20388
+ }
20389
+ const storyParagraphIndexes = [];
20390
+ for (let index = 0; index < paragraphs.length; index += 1) {
20391
+ if (paragraphs[index]?.storyKey === storyKey2) {
20392
+ storyParagraphIndexes.push(index);
20393
+ }
20394
+ }
20395
+ return [...new Set(paragraphIndexes)].filter((index) => Number.isInteger(index) && index >= 0 && index < storyParagraphIndexes.length).map((index) => storyParagraphIndexes[index]).sort((left, right) => left - right);
20396
+ }
20212
20397
  function clampLevel(level) {
20213
20398
  return Math.max(0, Math.min(8, level));
20214
20399
  }
@@ -20890,8 +21075,9 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20890
21075
  workingDocument = collapseResult.document;
20891
21076
  workingSelection = collapseResult.selection;
20892
21077
  if (context.textTarget?.kind === "table-paragraph" || context.textTarget?.kind === "text-leaf") {
21078
+ const { precomputedSurface: _precomputedSurface, ...contextWithoutPrecomputedSurface } = context;
20893
21079
  workingContext = {
20894
- ...context,
21080
+ ...contextWithoutPrecomputedSurface,
20895
21081
  textTarget: {
20896
21082
  ...context.textTarget,
20897
21083
  paragraphEnd: Math.max(
@@ -20903,7 +21089,11 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20903
21089
  }
20904
21090
  }
20905
21091
  const splitResult = splitParagraph(workingDocument, workingSelection, workingContext);
20906
- const splitRoot = splitResult.document.content;
21092
+ const preparedFragment = prepareFragmentNumberingForInsertion(
21093
+ splitResult.document,
21094
+ fragment
21095
+ );
21096
+ const splitRoot = preparedFragment.document.content;
20907
21097
  if (!splitRoot || splitRoot.type !== "doc") {
20908
21098
  return {
20909
21099
  changed: false,
@@ -20914,9 +21104,9 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20914
21104
  const targetedBlockPath = workingContext.textTarget?.kind === "table-paragraph" || workingContext.textTarget?.kind === "text-leaf" ? workingContext.textTarget.blockPath : void 0;
20915
21105
  if (targetedBlockPath) {
20916
21106
  const targeted = insertFragmentBlocksAfterPath(
20917
- splitResult.document,
21107
+ preparedFragment.document,
20918
21108
  targetedBlockPath,
20919
- fragment.blocks
21109
+ preparedFragment.blocks
20920
21110
  );
20921
21111
  if (targeted) {
20922
21112
  return {
@@ -20948,7 +21138,7 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20948
21138
  const rightHalfIndex = scope.blockIndex + 1;
20949
21139
  const splicedChildren = [
20950
21140
  ...splitRoot.children.slice(0, rightHalfIndex),
20951
- ...fragment.blocks.map((block) => cloneBlock(block)),
21141
+ ...preparedFragment.blocks.map((block) => cloneBlock(block)),
20952
21142
  ...splitRoot.children.slice(rightHalfIndex)
20953
21143
  ];
20954
21144
  const nextRoot = {
@@ -20956,7 +21146,7 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20956
21146
  children: splicedChildren
20957
21147
  };
20958
21148
  const nextDocument = {
20959
- ...splitResult.document,
21149
+ ...preparedFragment.document,
20960
21150
  updatedAt: context.timestamp,
20961
21151
  content: nextRoot
20962
21152
  };
@@ -20968,14 +21158,47 @@ function applyFragmentInsert(document2, selection, fragment, context) {
20968
21158
  mapping: createEmptyMapping()
20969
21159
  };
20970
21160
  }
20971
- function cloneBlock(block) {
20972
- if (block.type === "paragraph") {
20973
- return {
20974
- ...block,
20975
- children: block.children.map((child) => ({ ...child }))
21161
+ function prepareFragmentNumberingForInsertion(document2, fragment) {
21162
+ if (!fragment.numbering) {
21163
+ return { document: document2, blocks: fragment.blocks };
21164
+ }
21165
+ const merged = mergeFragmentNumberingCatalog(document2.numbering, fragment.numbering);
21166
+ return {
21167
+ document: {
21168
+ ...document2,
21169
+ numbering: merged.catalog
21170
+ },
21171
+ blocks: fragment.blocks.map((block) => cloneBlock(block, merged.instanceIdMap))
21172
+ };
21173
+ }
21174
+ function cloneBlock(block, numberingInstanceIdMap) {
21175
+ const cloned = structuredClone(block);
21176
+ remapNumberingReferences(cloned, numberingInstanceIdMap);
21177
+ return cloned;
21178
+ }
21179
+ function remapNumberingReferences(value, numberingInstanceIdMap) {
21180
+ if (!numberingInstanceIdMap?.size || !value || typeof value !== "object") {
21181
+ return;
21182
+ }
21183
+ if (Array.isArray(value)) {
21184
+ for (const entry of value) {
21185
+ remapNumberingReferences(entry, numberingInstanceIdMap);
21186
+ }
21187
+ return;
21188
+ }
21189
+ const record = value;
21190
+ if (record.numbering?.numberingInstanceId) {
21191
+ const mappedId = numberingInstanceIdMap.get(record.numbering.numberingInstanceId) ?? record.numbering.numberingInstanceId;
21192
+ record.numbering = {
21193
+ ...record.numbering,
21194
+ numberingInstanceId: mappedId
20976
21195
  };
20977
21196
  }
20978
- return JSON.parse(JSON.stringify(block));
21197
+ remapNumberingReferences(record.children, numberingInstanceIdMap);
21198
+ remapNumberingReferences(record.rows, numberingInstanceIdMap);
21199
+ remapNumberingReferences(record.cells, numberingInstanceIdMap);
21200
+ remapNumberingReferences(record.txbxBlocks, numberingInstanceIdMap);
21201
+ remapNumberingReferences(record.content, numberingInstanceIdMap);
20979
21202
  }
20980
21203
  function insertFragmentBlocksAfterPath(document2, blockPath, fragmentBlocks) {
20981
21204
  const tokens = parseCanonicalBlockPath2(blockPath);
@@ -35639,13 +35862,32 @@ function storyKeyFromHandle(handle) {
35639
35862
  function textLeafEditableTargetHint(entry, semanticBlockRange) {
35640
35863
  const storyKey2 = storyKeyFromHandle(entry.handle);
35641
35864
  if (!storyKey2) return void 0;
35865
+ const semanticBlockPath = textLeafBlockPathFromSemanticPath(entry.handle.semanticPath, storyKey2);
35642
35866
  return {
35643
35867
  kind: "text-leaf",
35644
35868
  storyKey: storyKey2,
35645
- blockPath: `${storyKey2}/block[${entry.blockIndex}]`,
35869
+ blockPath: semanticBlockPath ?? `${storyKey2}/block[${entry.blockIndex}]`,
35646
35870
  semanticBlockRange
35647
35871
  };
35648
35872
  }
35873
+ function textLeafBlockPathFromSemanticPath(semanticPath, storyKey2) {
35874
+ if (!semanticPath) return null;
35875
+ const tableIndex = semanticPath.indexOf("table");
35876
+ const rowIndex = semanticPath.indexOf("row", tableIndex + 1);
35877
+ const cellIndex = semanticPath.indexOf("cell", rowIndex + 1);
35878
+ const paragraphIndex = semanticPath.indexOf("paragraph", cellIndex + 1);
35879
+ if (tableIndex < 0 || rowIndex < 0 || cellIndex < 0 || paragraphIndex < 0) {
35880
+ return null;
35881
+ }
35882
+ const table = Number(semanticPath[tableIndex + 1]);
35883
+ const row2 = Number(semanticPath[rowIndex + 1]);
35884
+ const cell = Number(semanticPath[cellIndex + 1]);
35885
+ const paragraph = Number(semanticPath[paragraphIndex + 1]);
35886
+ if (!Number.isInteger(table) || !Number.isInteger(row2) || !Number.isInteger(cell) || !Number.isInteger(paragraph)) {
35887
+ return null;
35888
+ }
35889
+ return `${storyKey2}/block[${table}]/row[${row2}]/cell[${cell}]/block[${paragraph}]`;
35890
+ }
35649
35891
  function compileParagraphReplacement(entry, proposed, options) {
35650
35892
  if (proposed.operation !== "replace" && proposed.operation !== "insert-before" && proposed.operation !== "insert-after") {
35651
35893
  return null;
@@ -49192,6 +49434,16 @@ function coerceIssueValue2(value) {
49192
49434
  function insertScopeMarkers(document2, params) {
49193
49435
  const { scopeId } = params;
49194
49436
  const root = document2.content;
49437
+ if (!Number.isFinite(params.from) || !Number.isFinite(params.to)) {
49438
+ const from = Number.isFinite(params.from) ? params.from : 0;
49439
+ const to = Number.isFinite(params.to) ? params.to : from;
49440
+ return {
49441
+ status: "non-finite-range",
49442
+ scopeId,
49443
+ from,
49444
+ to
49445
+ };
49446
+ }
49195
49447
  const normalizedFrom = Math.min(params.from, params.to);
49196
49448
  const normalizedTo = Math.max(params.from, params.to);
49197
49449
  if (!root || root.type !== "doc" || root.children.length === 0) {
@@ -49644,6 +49896,14 @@ function isRecord3(value) {
49644
49896
  }
49645
49897
 
49646
49898
  // src/runtime/workflow/coordinator.ts
49899
+ function createScopeMarkerMutationMapping() {
49900
+ return {
49901
+ steps: [],
49902
+ metadata: {
49903
+ layoutNeutralScopeMarkers: true
49904
+ }
49905
+ };
49906
+ }
49647
49907
  var MODE_RESTRICTIVENESS = {
49648
49908
  edit: 0,
49649
49909
  suggest: 1,
@@ -49951,6 +50211,60 @@ function createWorkflowCoordinator(deps) {
49951
50211
  activeStory: deps.getActiveStory()
49952
50212
  };
49953
50213
  }
50214
+ function createPlantFailureResult(input) {
50215
+ const { scopeId, anchor, assoc, plantResult } = input;
50216
+ return {
50217
+ scopeId: "",
50218
+ anchor: {
50219
+ kind: "range",
50220
+ from: anchor.from,
50221
+ to: anchor.to,
50222
+ assoc
50223
+ },
50224
+ plantStatus: {
50225
+ planted: false,
50226
+ reason: plantResult.status,
50227
+ ...plantResult.status === "non-paragraph-target" ? {
50228
+ blockIndex: plantResult.blockIndex,
50229
+ blockKind: plantResult.blockKind
50230
+ } : {},
50231
+ ...plantResult.status === "range-out-of-bounds" ? { storyLength: plantResult.storyLength } : {},
50232
+ requestedFrom: plantResult.from,
50233
+ requestedTo: plantResult.to
50234
+ }
50235
+ };
50236
+ }
50237
+ function buildWorkflowScope(input) {
50238
+ const { params, scopeId, publicAnchor } = input;
50239
+ return {
50240
+ scopeId,
50241
+ mode: params.mode ?? "comment",
50242
+ anchor: publicAnchor,
50243
+ ...params.storyTarget ? { storyTarget: params.storyTarget } : {},
50244
+ ...params.label ? { label: params.label } : {},
50245
+ ...params.visibility ? { visibility: params.visibility } : {},
50246
+ ...params.guardPolicy ? { guardPolicy: params.guardPolicy } : {},
50247
+ ...params.scopeMetadataFields && params.scopeMetadataFields.length > 0 ? { metadata: [...params.scopeMetadataFields] } : {}
50248
+ };
50249
+ }
50250
+ function buildWorkflowMetadataEntry(input) {
50251
+ const { params, scopeId, publicAnchor } = input;
50252
+ if (!params.persistence || params.persistence === "runtime-only") return null;
50253
+ const requestedMetadata = params.metadata ?? {};
50254
+ const entryPersistence = requestedMetadata.metadataPersistence ?? (params.persistence === "session" ? "external" : "internal");
50255
+ return {
50256
+ entryId: requestedMetadata.entryId ?? `scope-metadata-${scopeId}`,
50257
+ metadataId: requestedMetadata.metadataId ?? "workflow.scope",
50258
+ anchor: publicAnchor,
50259
+ ...params.storyTarget ? { storyTarget: params.storyTarget } : {},
50260
+ scopeId,
50261
+ ...requestedMetadata.workItemId ? { workItemId: requestedMetadata.workItemId } : {},
50262
+ ...requestedMetadata.value !== void 0 ? { value: requestedMetadata.value } : params.persistence === "document-metadata" && params.label ? { value: { label: params.label } } : {},
50263
+ metadataPersistence: entryPersistence,
50264
+ ...requestedMetadata.storageRef !== void 0 ? { storageRef: requestedMetadata.storageRef } : {},
50265
+ ...requestedMetadata.metadataVersion !== void 0 ? { metadataVersion: requestedMetadata.metadataVersion } : {}
50266
+ };
50267
+ }
49954
50268
  function addScope(params) {
49955
50269
  const state = deps.getState();
49956
50270
  const scopeId = params.scopeId ?? `scope-${clock().replace(/[^0-9]/gu, "")}-${Math.floor(Math.random() * 1e6)}`;
@@ -49993,6 +50307,7 @@ function createWorkflowCoordinator(deps) {
49993
50307
  deps.dispatch({
49994
50308
  type: "document.replace",
49995
50309
  document: nextDocument,
50310
+ mapping: createScopeMarkerMutationMapping(),
49996
50311
  origin: { source: "api", at: clock() }
49997
50312
  });
49998
50313
  const callerAssoc = params.anchor.kind === "range" ? params.anchor.assoc : { start: -1, end: 1 };
@@ -50049,6 +50364,86 @@ function createWorkflowCoordinator(deps) {
50049
50364
  }
50050
50365
  return { scopeId, anchor: publicAnchor };
50051
50366
  }
50367
+ function addScopes(paramsList) {
50368
+ if (paramsList.length === 0) return [];
50369
+ return deps.runInRuntimeBatch("workflow.addScopes", () => {
50370
+ let nextDocument = deps.getDocument();
50371
+ let documentChanged = false;
50372
+ const results = [];
50373
+ const scopesToAdd = [];
50374
+ const metadataEntriesToAdd = [];
50375
+ const newScopeIds = /* @__PURE__ */ new Set();
50376
+ for (const params of paramsList) {
50377
+ const scopeId = params.scopeId ?? `scope-${clock().replace(/[^0-9]/gu, "")}-${Math.floor(Math.random() * 1e6)}`;
50378
+ const anchor = params.anchor.kind === "range" ? { from: params.anchor.from, to: params.anchor.to } : null;
50379
+ if (!anchor) {
50380
+ results.push({ scopeId, anchor: params.anchor });
50381
+ continue;
50382
+ }
50383
+ const callerAssoc = params.anchor.kind === "range" ? params.anchor.assoc : { start: -1, end: 1 };
50384
+ const plantResult = insertScopeMarkers(nextDocument, {
50385
+ scopeId,
50386
+ from: anchor.from,
50387
+ to: anchor.to
50388
+ });
50389
+ if (plantResult.status !== "planted") {
50390
+ results.push(
50391
+ createPlantFailureResult({
50392
+ scopeId,
50393
+ anchor,
50394
+ assoc: callerAssoc,
50395
+ plantResult
50396
+ })
50397
+ );
50398
+ continue;
50399
+ }
50400
+ nextDocument = plantResult.document;
50401
+ documentChanged = true;
50402
+ const publicAnchor = {
50403
+ kind: "range",
50404
+ from: plantResult.plantedRange.from,
50405
+ to: plantResult.plantedRange.to,
50406
+ assoc: callerAssoc
50407
+ };
50408
+ newScopeIds.add(scopeId);
50409
+ scopesToAdd.push(buildWorkflowScope({ params, scopeId, publicAnchor }));
50410
+ const entry = buildWorkflowMetadataEntry({ params, scopeId, publicAnchor });
50411
+ if (entry) metadataEntriesToAdd.push(entry);
50412
+ results.push({ scopeId, anchor: publicAnchor });
50413
+ }
50414
+ if (documentChanged) {
50415
+ deps.dispatch({
50416
+ type: "document.replace",
50417
+ document: nextDocument,
50418
+ mapping: createScopeMarkerMutationMapping(),
50419
+ origin: { source: "api", at: clock() }
50420
+ });
50421
+ }
50422
+ if (scopesToAdd.length > 0) {
50423
+ const currentOverlay = overlayStore.getOverlay() ?? {
50424
+ overlayVersion: "workflow-overlay/1",
50425
+ scopes: []
50426
+ };
50427
+ const existingScopes = currentOverlay.scopes.filter(
50428
+ (existing) => !newScopeIds.has(existing.scopeId)
50429
+ );
50430
+ deps.dispatch({
50431
+ type: "workflow.set-overlay",
50432
+ overlay: { ...currentOverlay, scopes: [...existingScopes, ...scopesToAdd] },
50433
+ origin: { source: "api", at: clock() }
50434
+ });
50435
+ }
50436
+ if (metadataEntriesToAdd.length > 0) {
50437
+ const priorEntries = overlayStore.getMetadataEntries();
50438
+ deps.dispatch({
50439
+ type: "workflow.set-metadata-entries",
50440
+ entries: [...priorEntries, ...metadataEntriesToAdd],
50441
+ origin: { source: "api", at: clock() }
50442
+ });
50443
+ }
50444
+ return results;
50445
+ });
50446
+ }
50052
50447
  function removeScope(scopeId) {
50053
50448
  const overlay = overlayStore.getOverlay();
50054
50449
  if (overlay) {
@@ -50066,6 +50461,7 @@ function createWorkflowCoordinator(deps) {
50066
50461
  deps.dispatch({
50067
50462
  type: "document.replace",
50068
50463
  document: nextDocument,
50464
+ mapping: createScopeMarkerMutationMapping(),
50069
50465
  origin: { source: "api", at: clock() }
50070
50466
  });
50071
50467
  }
@@ -50459,6 +50855,7 @@ function createWorkflowCoordinator(deps) {
50459
50855
  }
50460
50856
  return {
50461
50857
  addScope,
50858
+ addScopes,
50462
50859
  removeScope,
50463
50860
  addInvisibleScope,
50464
50861
  setScopeVisibility,
@@ -56135,6 +56532,10 @@ function createDocumentRuntime(options) {
56135
56532
  const sessionId = createSessionId(options.documentId, clock());
56136
56533
  const listeners = /* @__PURE__ */ new Set();
56137
56534
  const eventListeners = /* @__PURE__ */ new Set();
56535
+ let runtimeNotificationBatchDepth = 0;
56536
+ let pendingRuntimeListenerNotify = false;
56537
+ let pendingRenderSnapshotRefresh = false;
56538
+ const pendingRuntimeEvents = [];
56138
56539
  const loadScheduler = options.loadScheduler ?? createLoadScheduler({ backendOverride: "sync" });
56139
56540
  let effectiveMarkupModeProvider;
56140
56541
  const perfCounters = new PerfCounters();
@@ -57346,6 +57747,7 @@ function createDocumentRuntime(options) {
57346
57747
  deriveOpaqueWorkflowBlockedReason,
57347
57748
  isBlockedByProtection,
57348
57749
  dispatch: (command) => dispatchToRuntime(command),
57750
+ runInRuntimeBatch: runRuntimeNotificationBatch,
57349
57751
  emitEvent: (event) => emit(event),
57350
57752
  editorStateChannel,
57351
57753
  suggestingUnsupportedCommands: SUGGESTING_UNSUPPORTED_COMMANDS
@@ -57695,9 +58097,7 @@ function createDocumentRuntime(options) {
57695
58097
  ...cachedRenderSnapshot,
57696
58098
  surface: newSurface
57697
58099
  };
57698
- for (const listener of listeners) {
57699
- listener();
57700
- }
58100
+ notifyRuntimeListeners();
57701
58101
  }
57702
58102
  function maybeRefreshSurfaceForViewport() {
57703
58103
  const fingerprint = `${storyTargetKey(activeStory)}|${viewportRangesKey}|${String(state.selection.anchor)}:${String(state.selection.head)}`;
@@ -58299,6 +58699,17 @@ function createDocumentRuntime(options) {
58299
58699
  };
58300
58700
  resolvedReplayTextTarget = prepared.textTarget;
58301
58701
  } else {
58702
+ const listBoundaryJoinCommand = createListItemBoundaryJoinReplayCommand({
58703
+ command,
58704
+ document: replayState.document,
58705
+ selection: replayState.selection,
58706
+ surface: replaySnapshot.surface?.blocks ?? [],
58707
+ storyTarget: replayStory,
58708
+ timestamp: context.timestamp
58709
+ });
58710
+ if (listBoundaryJoinCommand) {
58711
+ executableCommand = listBoundaryJoinCommand;
58712
+ }
58302
58713
  const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58303
58714
  command,
58304
58715
  document: replayState.document,
@@ -58456,6 +58867,17 @@ function createDocumentRuntime(options) {
58456
58867
  };
58457
58868
  resolvedReplayTextTarget = prepared.textTarget;
58458
58869
  } else {
58870
+ const listBoundaryJoinCommand = createListItemBoundaryJoinReplayCommand({
58871
+ command,
58872
+ document: stateForCommand.document,
58873
+ selection: stateForCommand.selection,
58874
+ surface: snapshotForCommand.surface?.blocks ?? [],
58875
+ storyTarget: replayStory,
58876
+ timestamp: context.timestamp
58877
+ });
58878
+ if (listBoundaryJoinCommand) {
58879
+ executableCommand = listBoundaryJoinCommand;
58880
+ }
58459
58881
  const selectedListItemDeleteCommand = createSelectedListItemDeleteReplayCommand({
58460
58882
  command,
58461
58883
  document: stateForCommand.document,
@@ -59485,6 +59907,9 @@ function createDocumentRuntime(options) {
59485
59907
  addScope(params) {
59486
59908
  return workflowCoordinator.addScope(params);
59487
59909
  },
59910
+ addScopes(params) {
59911
+ return workflowCoordinator.addScopes(params);
59912
+ },
59488
59913
  getScope(scopeId) {
59489
59914
  return workflowCoordinator.getScope(scopeId);
59490
59915
  },
@@ -60468,6 +60893,9 @@ function createDocumentRuntime(options) {
60468
60893
  if (transaction.mapping.metadata?.scopeTagTouches) return false;
60469
60894
  return getLocalTextPatchMetadata(transaction.mapping) !== null;
60470
60895
  }
60896
+ function isLayoutNeutralScopeMarkerCommit(previous, next, transaction) {
60897
+ return transaction.markDirty && previous.document !== next.document && transaction.mapping.steps.length === 0 && transaction.mapping.metadata?.layoutNeutralScopeMarkers === true;
60898
+ }
60471
60899
  function applyTransactionToState(transaction, options2 = {}) {
60472
60900
  const effects = transaction.effects;
60473
60901
  const selectionUnchanged = transaction.nextState.selection === state.selection;
@@ -60496,6 +60924,11 @@ function createDocumentRuntime(options) {
60496
60924
  transaction,
60497
60925
  transaction.effects
60498
60926
  );
60927
+ const layoutNeutralScopeMarkerCommit = isLayoutNeutralScopeMarkerCommit(
60928
+ previous,
60929
+ state,
60930
+ transaction
60931
+ );
60499
60932
  perfCounters.increment("commit.refreshClassify.us", Math.round((performance.now() - tClassify0) * 1e3));
60500
60933
  const tOverlay0 = performance.now();
60501
60934
  const skipOverlaySync = useLocalTextCommitSnapshot && canSkipOverlaySyncForLocalText(transaction);
@@ -60544,9 +60977,12 @@ function createDocumentRuntime(options) {
60544
60977
  ...detachedWorkflowScopeWarnings.cleared
60545
60978
  ]
60546
60979
  };
60547
- if (!useLocalTextCommitSnapshot && transaction.markDirty && previous.document !== state.document) {
60980
+ if (!useLocalTextCommitSnapshot && !layoutNeutralScopeMarkerCommit && transaction.markDirty && previous.document !== state.document) {
60548
60981
  applyViewportRanges(getSelectionCorridorViewportRanges(cachedRenderSnapshot.surface));
60549
60982
  }
60983
+ if (layoutNeutralScopeMarkerCommit && viewportBlockRanges !== null) {
60984
+ getCachedFullSurface(state.document, activeStory);
60985
+ }
60550
60986
  const tValidation0 = performance.now();
60551
60987
  const patchSourceSurface = getReusableCachedFullSurface(previous.document, activeStory) ?? cachedRenderSnapshot.surface;
60552
60988
  const patchedFullLocalTextSurface = useLocalTextCommitSnapshot ? tryPatchLocalTextSurface(patchSourceSurface, transaction.mapping) : null;
@@ -60794,9 +61230,7 @@ function createDocumentRuntime(options) {
60794
61230
  code: cleared.code
60795
61231
  });
60796
61232
  }
60797
- for (const listener of listeners) {
60798
- listener();
60799
- }
61233
+ notifyRuntimeListeners();
60800
61234
  }
60801
61235
  function applyTextCommandInActiveStory(command, textOptions = {}) {
60802
61236
  emitStageToken(telemetryBus, "command", "command.dispatch.start", {
@@ -60915,6 +61349,39 @@ function createDocumentRuntime(options) {
60915
61349
  const preSelection = selection;
60916
61350
  const preActiveStory = activeStory;
60917
61351
  const priorDocument = state.document;
61352
+ const listBoundaryJoin = createListItemBoundaryJoinReplacement({
61353
+ command: commandForDispatch,
61354
+ document: state.document,
61355
+ editableTarget,
61356
+ selection,
61357
+ storyTarget: activeStory,
61358
+ targetResolution,
61359
+ timestamp
61360
+ });
61361
+ if (listBoundaryJoin && context.documentMode !== "suggesting") {
61362
+ const replacementCommand = {
61363
+ type: "document.replace",
61364
+ document: listBoundaryJoin.document,
61365
+ selection: listBoundaryJoin.selection,
61366
+ mapping: listBoundaryJoin.mapping,
61367
+ protectionSelection: selection,
61368
+ origin: commandForDispatch.origin
61369
+ };
61370
+ const transaction = executeEditorCommand(baseState, replacementCommand, context);
61371
+ commit(transaction);
61372
+ options.onCommandApplied?.(commandForDispatch, transaction, context, {
61373
+ preSelection,
61374
+ activeStory: preActiveStory,
61375
+ priorDocument
61376
+ });
61377
+ return completeDispatch(classifyAck({
61378
+ command: commandForDispatch,
61379
+ opId,
61380
+ priorState: baseState,
61381
+ transaction,
61382
+ newRevisionToken: state.revisionToken
61383
+ }));
61384
+ }
60918
61385
  const selectedListItemDelete = createSelectedListItemDeleteReplacement({
60919
61386
  command: commandForDispatch,
60920
61387
  document: state.document,
@@ -61356,7 +61823,7 @@ function createDocumentRuntime(options) {
61356
61823
  const warningDelta = workflowCoordinator.applyOverlayCommand(
61357
61824
  command,
61358
61825
  () => {
61359
- cachedRenderSnapshot = refreshRenderSnapshot();
61826
+ requestRuntimeRenderSnapshotRefresh();
61360
61827
  }
61361
61828
  );
61362
61829
  if (warningDelta) {
@@ -61414,11 +61881,69 @@ function createDocumentRuntime(options) {
61414
61881
  });
61415
61882
  }
61416
61883
  }
61884
+ notifyRuntimeListeners();
61885
+ }
61886
+ function runRuntimeNotificationBatch(label, fn) {
61887
+ runtimeNotificationBatchDepth += 1;
61888
+ perfCounters.increment(`runtime.notificationBatch.${label}.calls`);
61889
+ try {
61890
+ return fn();
61891
+ } finally {
61892
+ runtimeNotificationBatchDepth -= 1;
61893
+ if (runtimeNotificationBatchDepth === 0) {
61894
+ flushRuntimeNotificationBatch();
61895
+ }
61896
+ }
61897
+ }
61898
+ function requestRuntimeRenderSnapshotRefresh() {
61899
+ if (runtimeNotificationBatchDepth > 0) {
61900
+ pendingRenderSnapshotRefresh = true;
61901
+ perfCounters.increment("runtime.notificationBatch.renderRefreshDeferred");
61902
+ return;
61903
+ }
61904
+ cachedRenderSnapshot = refreshRenderSnapshot();
61905
+ }
61906
+ function notifyRuntimeListeners() {
61907
+ if (runtimeNotificationBatchDepth > 0) {
61908
+ pendingRuntimeListenerNotify = true;
61909
+ perfCounters.increment("runtime.notificationBatch.listenerNotifyDeferred");
61910
+ return;
61911
+ }
61912
+ notifyRuntimeListenersNow();
61913
+ }
61914
+ function notifyRuntimeListenersNow() {
61417
61915
  for (const listener of listeners) {
61418
61916
  listener();
61419
61917
  }
61420
61918
  }
61919
+ function flushRuntimeNotificationBatch() {
61920
+ if (pendingRenderSnapshotRefresh) {
61921
+ pendingRenderSnapshotRefresh = false;
61922
+ cachedRenderSnapshot = refreshRenderSnapshot();
61923
+ perfCounters.increment("runtime.notificationBatch.renderRefreshFlushed");
61924
+ }
61925
+ if (pendingRuntimeEvents.length > 0) {
61926
+ const queuedEvents = pendingRuntimeEvents.splice(0);
61927
+ perfCounters.increment("runtime.notificationBatch.eventsFlushed", queuedEvents.length);
61928
+ for (const event of queuedEvents) {
61929
+ emitInternalNow(event);
61930
+ }
61931
+ }
61932
+ if (pendingRuntimeListenerNotify) {
61933
+ pendingRuntimeListenerNotify = false;
61934
+ perfCounters.increment("runtime.notificationBatch.listenerNotifyFlushed");
61935
+ notifyRuntimeListenersNow();
61936
+ }
61937
+ }
61421
61938
  function emitInternal(event) {
61939
+ if (runtimeNotificationBatchDepth > 0) {
61940
+ pendingRuntimeEvents.push(event);
61941
+ perfCounters.increment("runtime.notificationBatch.eventsQueued");
61942
+ return;
61943
+ }
61944
+ emitInternalNow(event);
61945
+ }
61946
+ function emitInternalNow(event) {
61422
61947
  options.onEvent?.(event);
61423
61948
  for (const listener of eventListeners) {
61424
61949
  listener(event);
@@ -63831,6 +64356,56 @@ function stripStoryTarget(selection) {
63831
64356
  function isTopLevelMainStoryBlockPath(blockPath) {
63832
64357
  return typeof blockPath === "string" && /^main\/block\[\d+\]$/u.test(blockPath);
63833
64358
  }
64359
+ function createListItemBoundaryJoinReplacement(input) {
64360
+ const { command, document: document2, editableTarget, selection, storyTarget, targetResolution, timestamp } = input;
64361
+ const direction = listItemBoundaryJoinDirection(command, selection, targetResolution);
64362
+ if (!direction || editableTarget?.listAddress?.operationScope !== "list-text" || targetResolution?.kind !== "accepted") {
64363
+ return null;
64364
+ }
64365
+ const storyBlocks = getStoryBlocks(document2, storyTarget);
64366
+ const replacement = joinNumberedParagraphAtStoryPath(
64367
+ storyBlocks,
64368
+ editableTarget.blockPath,
64369
+ storyTarget,
64370
+ direction
64371
+ );
64372
+ if (!replacement) {
64373
+ return null;
64374
+ }
64375
+ const nextDocument = replaceStoryBlocks({
64376
+ ...document2,
64377
+ updatedAt: timestamp
64378
+ }, storyTarget, replacement.blocks);
64379
+ const deletedFrom = direction === "backward" ? Math.max(0, selection.anchor - 1) : selection.anchor;
64380
+ const deletedTo = direction === "backward" ? selection.anchor : selection.anchor + 1;
64381
+ const nextAnchor = direction === "backward" ? deletedFrom : selection.anchor;
64382
+ return {
64383
+ document: nextDocument,
64384
+ selection: createSelectionSnapshot(nextAnchor, nextAnchor),
64385
+ mapping: {
64386
+ steps: [{
64387
+ from: deletedFrom,
64388
+ to: deletedTo,
64389
+ insertSize: 0
64390
+ }],
64391
+ metadata: {
64392
+ invalidatesStructures: true
64393
+ }
64394
+ }
64395
+ };
64396
+ }
64397
+ function listItemBoundaryJoinDirection(command, selection, targetResolution) {
64398
+ if (selection.isCollapsed !== true || targetResolution?.kind !== "accepted") {
64399
+ return null;
64400
+ }
64401
+ if (command.type === "text.delete-backward" && selection.anchor === targetResolution.range.from) {
64402
+ return "backward";
64403
+ }
64404
+ if (command.type === "text.delete-forward" && selection.anchor === targetResolution.range.to) {
64405
+ return "forward";
64406
+ }
64407
+ return null;
64408
+ }
63834
64409
  function createSelectedListItemDeleteReplacement(input) {
63835
64410
  const { command, document: document2, editableTarget, selection, storyTarget, targetResolution, timestamp } = input;
63836
64411
  if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
@@ -63878,6 +64453,152 @@ function removeNumberedParagraphAtStoryPath(blocks, blockPath, storyTarget) {
63878
64453
  if (!tokens) return null;
63879
64454
  return removeNumberedParagraphFromBlocks(blocks, tokens);
63880
64455
  }
64456
+ function joinNumberedParagraphAtStoryPath(blocks, blockPath, storyTarget, direction) {
64457
+ const tokens = parseStoryBlockPathTokens(blockPath, storyTarget);
64458
+ if (!tokens) return null;
64459
+ return joinNumberedParagraphFromBlocks(blocks, tokens, direction);
64460
+ }
64461
+ function joinNumberedParagraphFromBlocks(blocks, tokens, direction) {
64462
+ const [token, ...rest] = tokens;
64463
+ if (!token || token.kind !== "block") return null;
64464
+ const block = blocks[token.index];
64465
+ if (!block) return null;
64466
+ if (rest.length === 0) {
64467
+ return joinAdjacentNumberedParagraphs(blocks, token.index, direction);
64468
+ }
64469
+ const next = rest[0];
64470
+ if (block.type === "table" && next?.kind === "row") {
64471
+ const updatedTable = joinNumberedParagraphInTable(block, rest, direction);
64472
+ if (!updatedTable) return null;
64473
+ return {
64474
+ blocks: [
64475
+ ...blocks.slice(0, token.index),
64476
+ updatedTable,
64477
+ ...blocks.slice(token.index + 1)
64478
+ ]
64479
+ };
64480
+ }
64481
+ if ((block.type === "sdt" || block.type === "custom_xml") && next?.kind === "block") {
64482
+ const updatedChildren = joinNumberedParagraphFromBlocks(block.children, rest, direction);
64483
+ if (!updatedChildren) return null;
64484
+ return {
64485
+ blocks: [
64486
+ ...blocks.slice(0, token.index),
64487
+ { ...block, children: updatedChildren.blocks },
64488
+ ...blocks.slice(token.index + 1)
64489
+ ]
64490
+ };
64491
+ }
64492
+ if (block.type === "paragraph" && next?.kind === "inline") {
64493
+ const updatedParagraph = joinNumberedParagraphInTextBoxInline(block, rest, direction);
64494
+ if (!updatedParagraph) return null;
64495
+ return {
64496
+ blocks: [
64497
+ ...blocks.slice(0, token.index),
64498
+ updatedParagraph,
64499
+ ...blocks.slice(token.index + 1)
64500
+ ]
64501
+ };
64502
+ }
64503
+ return null;
64504
+ }
64505
+ function joinAdjacentNumberedParagraphs(blocks, targetIndex, direction) {
64506
+ const target = blocks[targetIndex];
64507
+ if (target?.type !== "paragraph" || !target.numbering) {
64508
+ return null;
64509
+ }
64510
+ if (direction === "backward") {
64511
+ const previousIndex = targetIndex - 1;
64512
+ const previous = blocks[previousIndex];
64513
+ if (previous?.type !== "paragraph" || !previous.numbering) {
64514
+ return null;
64515
+ }
64516
+ const merged2 = {
64517
+ ...previous,
64518
+ children: [...previous.children, ...target.children]
64519
+ };
64520
+ return {
64521
+ blocks: [
64522
+ ...blocks.slice(0, previousIndex),
64523
+ merged2,
64524
+ ...blocks.slice(targetIndex + 1)
64525
+ ]
64526
+ };
64527
+ }
64528
+ const nextIndex = targetIndex + 1;
64529
+ const next = blocks[nextIndex];
64530
+ if (next?.type !== "paragraph" || !next.numbering) {
64531
+ return null;
64532
+ }
64533
+ const merged = {
64534
+ ...target,
64535
+ children: [...target.children, ...next.children]
64536
+ };
64537
+ return {
64538
+ blocks: [
64539
+ ...blocks.slice(0, targetIndex),
64540
+ merged,
64541
+ ...blocks.slice(nextIndex + 1)
64542
+ ]
64543
+ };
64544
+ }
64545
+ function joinNumberedParagraphInTable(table, tokens, direction) {
64546
+ const [rowToken, cellToken, ...childTokens] = tokens;
64547
+ if (rowToken?.kind !== "row" || cellToken?.kind !== "cell" || childTokens[0]?.kind !== "block") {
64548
+ return null;
64549
+ }
64550
+ const row2 = table.rows[rowToken.index];
64551
+ const cell = row2?.cells[cellToken.index];
64552
+ if (!row2 || !cell) return null;
64553
+ const updatedChildren = joinNumberedParagraphFromBlocks(cell.children, childTokens, direction);
64554
+ if (!updatedChildren) return null;
64555
+ const nextCells = [
64556
+ ...row2.cells.slice(0, cellToken.index),
64557
+ { ...cell, children: updatedChildren.blocks },
64558
+ ...row2.cells.slice(cellToken.index + 1)
64559
+ ];
64560
+ const nextRows = [
64561
+ ...table.rows.slice(0, rowToken.index),
64562
+ { ...row2, cells: nextCells },
64563
+ ...table.rows.slice(rowToken.index + 1)
64564
+ ];
64565
+ return { ...table, rows: nextRows };
64566
+ }
64567
+ function joinNumberedParagraphInTextBoxInline(paragraph, tokens, direction) {
64568
+ const [inlineToken, textBoxToken, ...childTokens] = tokens;
64569
+ if (inlineToken?.kind !== "inline" || textBoxToken?.kind !== "txbx" || childTokens[0]?.kind !== "block") {
64570
+ return null;
64571
+ }
64572
+ const inline = paragraph.children[inlineToken.index];
64573
+ if (!inline) return null;
64574
+ const updatedInline = joinNumberedParagraphInInlineTextBox(inline, childTokens, direction);
64575
+ if (!updatedInline) return null;
64576
+ return {
64577
+ ...paragraph,
64578
+ children: [
64579
+ ...paragraph.children.slice(0, inlineToken.index),
64580
+ updatedInline,
64581
+ ...paragraph.children.slice(inlineToken.index + 1)
64582
+ ]
64583
+ };
64584
+ }
64585
+ function joinNumberedParagraphInInlineTextBox(inline, tokens, direction) {
64586
+ if (inline.type === "shape" && inline.txbxBlocks) {
64587
+ const updatedBlocks = joinNumberedParagraphFromBlocks(inline.txbxBlocks, tokens, direction);
64588
+ return updatedBlocks ? { ...inline, txbxBlocks: updatedBlocks.blocks } : null;
64589
+ }
64590
+ if (inline.type === "drawing_frame" && inline.content.type === "shape" && inline.content.txbxBlocks) {
64591
+ const updatedBlocks = joinNumberedParagraphFromBlocks(inline.content.txbxBlocks, tokens, direction);
64592
+ return updatedBlocks ? {
64593
+ ...inline,
64594
+ content: {
64595
+ ...inline.content,
64596
+ txbxBlocks: updatedBlocks.blocks
64597
+ }
64598
+ } : null;
64599
+ }
64600
+ return null;
64601
+ }
63881
64602
  function removeNumberedParagraphFromBlocks(blocks, tokens) {
63882
64603
  const [token, ...rest] = tokens;
63883
64604
  if (!token || token.kind !== "block") return null;
@@ -64040,6 +64761,56 @@ function createSelectedListItemDeleteReplayCommand(input) {
64040
64761
  origin: command.origin
64041
64762
  };
64042
64763
  }
64764
+ function createListItemBoundaryJoinReplayCommand(input) {
64765
+ const { command, document: document2, selection, surface, storyTarget, timestamp } = input;
64766
+ if (command.type !== "text.delete-backward" && command.type !== "text.delete-forward") {
64767
+ return null;
64768
+ }
64769
+ const editableTarget = command.editableTarget;
64770
+ if (!editableTarget) {
64771
+ return null;
64772
+ }
64773
+ const targetResolution = resolveEditableTextTarget({
64774
+ document: document2,
64775
+ selection,
64776
+ surface,
64777
+ target: editableTarget,
64778
+ activeStoryKey: canonicalEditableTargetStoryKey(storyTarget)
64779
+ });
64780
+ const resolvedEditableTarget = targetResolution.kind === "accepted" ? editableTarget : storyTarget.kind !== "main" && blockPathBelongsToStoryTarget(editableTarget.blockPath, storyTarget) ? resolveEditableCommandTarget({
64781
+ document: document2,
64782
+ target: editableTarget,
64783
+ activeStoryKey: canonicalEditableTargetStoryKey(storyTarget),
64784
+ commandFamilies: ["text-leaf"]
64785
+ }) : null;
64786
+ const selectedRange = {
64787
+ from: Math.min(selection.anchor, selection.head),
64788
+ to: Math.max(selection.anchor, selection.head)
64789
+ };
64790
+ const replacement = createListItemBoundaryJoinReplacement({
64791
+ command,
64792
+ document: document2,
64793
+ editableTarget: resolvedEditableTarget && resolvedEditableTarget.kind === "accepted" ? resolvedEditableTarget.target : editableTarget,
64794
+ selection,
64795
+ storyTarget,
64796
+ targetResolution: resolvedEditableTarget && resolvedEditableTarget.kind === "accepted" ? {
64797
+ kind: "accepted",
64798
+ range: selectedRange
64799
+ } : targetResolution,
64800
+ timestamp
64801
+ });
64802
+ if (!replacement) {
64803
+ return null;
64804
+ }
64805
+ return {
64806
+ type: "document.replace",
64807
+ document: replacement.document,
64808
+ selection: replacement.selection,
64809
+ mapping: replacement.mapping,
64810
+ protectionSelection: selection,
64811
+ origin: command.origin
64812
+ };
64813
+ }
64043
64814
  function parseStoryBlockPathTokens(blockPath, storyTarget) {
64044
64815
  if (!blockPath) {
64045
64816
  return null;