superdoc 2.3.0 → 2.4.0-next.2

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 (52) hide show
  1. package/README.md +0 -1
  2. package/dist/chunks/{FindReplaceSurface-DLxnCQOB.cjs → FindReplaceSurface-Dx0llWqP.cjs} +1 -1
  3. package/dist/chunks/{PasswordPromptSurface-DMbfDvpQ.cjs → PasswordPromptSurface-BUUVC2ee.cjs} +1 -1
  4. package/dist/chunks/{PdfViewer-CoFiGAhm.cjs → PdfViewer-qpvzwYMz.cjs} +1 -1
  5. package/dist/chunks/blank-docx-DP8RUPW-.cjs +7 -0
  6. package/dist/chunks/blank-docx-XRX6Ker2.es.js +2 -0
  7. package/dist/chunks/{create-super-doc-ui-fxF2Q8Ib.es.js → create-super-doc-ui-D4kk9AjM.es.js} +331 -102
  8. package/dist/chunks/{create-super-doc-ui-CXCqHc2m.cjs → create-super-doc-ui-DXdWuTzm.cjs} +331 -102
  9. package/dist/chunks/{eventemitter3-B60Fsz-a.es.js → eventemitter3-Bt2s0X0a.es.js} +1 -1
  10. package/dist/chunks/{eventemitter3-DCfwFVNn.cjs → eventemitter3-DqY4aSMf.cjs} +1 -1
  11. package/dist/chunks/{jszip-DzmwAHr3.es.js → jszip-C8srOKAO.es.js} +2 -2
  12. package/dist/chunks/{jszip-BdUBlUeG.cjs → jszip-Cs9JBLlJ.cjs} +13 -1
  13. package/dist/chunks/{rolldown-runtime-_dR15c8t.cjs → rolldown-runtime-1Y-nnZJ3.cjs} +0 -28
  14. package/dist/chunks/{rolldown-runtime-BJVcqBbz.es.js → rolldown-runtime-D7PMmH3s.es.js} +1 -17
  15. package/dist/collaboration-upgrade-engine.cjs +1 -1
  16. package/dist/collaboration-upgrade-engine.es.js +1 -1
  17. package/dist/document-api/src/comments/comment-create-attribution.d.ts +2 -0
  18. package/dist/document-api/src/comments/comments.d.ts +13 -4
  19. package/dist/document-api/src/comments/comments.types.d.ts +39 -0
  20. package/dist/document-api/src/content-controls/content-controls.types.d.ts +2 -0
  21. package/dist/document-api/src/contract/metadata-types.d.ts +18 -0
  22. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  23. package/dist/document-api/src/index.d.ts +2 -2
  24. package/dist/layout-engine/painters/dom/src/_test-utils.d.ts +3 -2
  25. package/dist/layout-engine/painters/dom/src/index.d.ts +24 -19
  26. package/dist/layout-engine/painters/dom/src/page-content.d.ts +107 -39
  27. package/dist/layout-engine/painters/dom/src/persistent-page-surface.d.ts +144 -0
  28. package/dist/layout-engine/painters/dom/src/pm-position-validation.d.ts +1 -1
  29. package/dist/layout-engine/painters/dom/src/renderer.d.ts +50 -106
  30. package/dist/layout-engine/painters/dom/src/sdt/boundaries.d.ts +1 -1
  31. package/dist/public/ui-react.cjs +2 -2
  32. package/dist/public/ui-react.es.js +1 -1
  33. package/dist/public/ui.cjs +1 -1
  34. package/dist/public/ui.es.js +1 -1
  35. package/dist/style.css +97 -90
  36. package/dist/style.layered.css +97 -90
  37. package/dist/superdoc/src/core/types/index.d.ts +258 -11
  38. package/dist/superdoc/src/helpers/floor.d.ts +1 -1
  39. package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +4 -0
  40. package/dist/superdoc/src/internal/toolbar/built-in/constants.d.ts +0 -1
  41. package/dist/superdoc/src/public/index.d.cts +6 -0
  42. package/dist/superdoc/src/public/index.d.ts +3 -0
  43. package/dist/superdoc/src/stores/comments-store.d.ts +34 -10
  44. package/dist/superdoc/src/stores/superdoc-store.d.ts +102 -30
  45. package/dist/superdoc.cjs +4581 -608
  46. package/dist/superdoc.es.js +4581 -612
  47. package/dist-cdn/style.layered.css +1 -1
  48. package/dist-cdn/superdoc.min.css +1 -1
  49. package/dist-cdn/superdoc.min.js +37 -37
  50. package/package.json +5 -4
  51. package/dist/chunks/blank-docx-CJAXnJKp.es.js +0 -2
  52. package/dist/chunks/blank-docx-DA-G-3VB.cjs +0 -7
@@ -90,24 +90,36 @@ function getV2TrackedChangeMutationImpact(event) {
90
90
  if (!payload || typeof payload !== "object") return null;
91
91
  const upsertIds = /* @__PURE__ */ new Set();
92
92
  const removedIds = /* @__PURE__ */ new Set();
93
+ const remappedPairs = [];
93
94
  const collectEntity = (entry, into) => {
94
95
  if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return;
95
96
  if (typeof entry.entityId === "string" && entry.entityId.length > 0) into.add(entry.entityId);
96
97
  };
98
+ const readEntityId$1 = (entry) => {
99
+ if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return null;
100
+ return typeof entry.entityId === "string" && entry.entityId.length > 0 ? entry.entityId : null;
101
+ };
97
102
  if (Array.isArray(payload.inserted)) payload.inserted.forEach((entry) => collectEntity(entry, upsertIds));
98
103
  if (Array.isArray(payload.updated)) payload.updated.forEach((entry) => collectEntity(entry, upsertIds));
99
104
  if (Array.isArray(payload.trackedChangeRefs)) payload.trackedChangeRefs.forEach((entry) => collectEntity(entry, upsertIds));
100
105
  if (Array.isArray(payload.removed)) payload.removed.forEach((entry) => collectEntity(entry, removedIds));
101
106
  if (Array.isArray(payload.invalidatedRefs)) payload.invalidatedRefs.forEach((entry) => collectEntity(entry, removedIds));
102
107
  if (Array.isArray(payload.remappedRefs)) for (const mapping of payload.remappedRefs) {
103
- collectEntity(mapping?.from, removedIds);
104
- collectEntity(mapping?.to, upsertIds);
108
+ const fromId = readEntityId$1(mapping?.from);
109
+ const toId = readEntityId$1(mapping?.to);
110
+ if (fromId) removedIds.add(fromId);
111
+ if (toId) upsertIds.add(toId);
112
+ if (fromId && toId && fromId !== toId) remappedPairs.push({
113
+ from: fromId,
114
+ to: toId
115
+ });
105
116
  }
106
117
  for (const id of removedIds) upsertIds.delete(id);
107
118
  if (upsertIds.size === 0 && removedIds.size === 0) return null;
108
119
  return {
109
120
  upsertIds,
110
121
  removedIds,
122
+ remappedPairs,
111
123
  reconcileMode: event.origin === "history" ? "authoritative" : "targeted"
112
124
  };
113
125
  }
@@ -217,6 +229,7 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
217
229
  if (!impact) return;
218
230
  const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
219
231
  const removedIds = normalizeIds(impact.removedIds);
232
+ const upsertIds = normalizeIds(impact.upsertIds);
220
233
  for (const id of removedIds) {
221
234
  const existing = pendingUpserts.get(id);
222
235
  if (existing?.reconcileMode === "authoritative") pendingUpserts.set(id, {
@@ -228,13 +241,13 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
228
241
  else pendingUpserts.delete(id);
229
242
  }
230
243
  const context = getContext();
231
- if (removedIds.size > 0 && context?.adapter) try {
244
+ if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter) try {
232
245
  Promise.resolve(reconcile(context, {
233
246
  upsertIds: /* @__PURE__ */ new Set(),
234
247
  removedIds
235
248
  })).catch(() => void 0);
236
249
  } catch {}
237
- for (const id of normalizeIds(impact.upsertIds)) if (!removedIds.has(id)) {
250
+ for (const id of upsertIds) if (!removedIds.has(id)) {
238
251
  const existing = pendingUpserts.get(id);
239
252
  pendingUpserts.set(id, {
240
253
  version: ++sequence,
@@ -1577,7 +1590,7 @@ const HEAVY_DOC_READ_POLICY = [
1577
1590
  {
1578
1591
  key: "trackChanges",
1579
1592
  match: "exact",
1580
- note: "audited: body tracked-changes list; stale-served during load"
1593
+ note: "audited: all-story tracked-changes list; stale-served during load"
1581
1594
  },
1582
1595
  {
1583
1596
  key: "trackChanges:all",
@@ -1664,6 +1677,48 @@ function readEntityTarget(row) {
1664
1677
  const target = row.target;
1665
1678
  return target && typeof target === "object" ? target : null;
1666
1679
  }
1680
+ function isHostGeometryTarget(target) {
1681
+ if (!target || typeof target !== "object") return false;
1682
+ const record = target;
1683
+ const isTextPoint = (point) => {
1684
+ if (!point || typeof point !== "object") return false;
1685
+ const candidate = point;
1686
+ return candidate.kind === "text" && typeof candidate.blockId === "string" && typeof candidate.offset === "number";
1687
+ };
1688
+ const isTextSegment = (segment) => {
1689
+ if (!segment || typeof segment !== "object") return false;
1690
+ const candidate = segment;
1691
+ const range$1 = candidate.range;
1692
+ return typeof candidate.blockId === "string" && typeof range$1?.start === "number" && typeof range$1.end === "number";
1693
+ };
1694
+ if (Array.isArray(record.segments)) {
1695
+ const first = record.segments[0];
1696
+ const last = record.segments[record.segments.length - 1];
1697
+ return isTextSegment(first) && isTextSegment(last);
1698
+ }
1699
+ if (record.kind === "selection") return isTextPoint(record.start) && isTextPoint(record.end);
1700
+ if (record.kind !== "text" || typeof record.blockId !== "string") return false;
1701
+ const range = record.range;
1702
+ return typeof range?.start === "number" && typeof range.end === "number";
1703
+ }
1704
+ function readTrackedChangeNavigationTarget(row) {
1705
+ if (!row || typeof row !== "object") return null;
1706
+ const target = readEntityTarget(row);
1707
+ if (isHostGeometryTarget(target)) return target;
1708
+ const record = row;
1709
+ const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
1710
+ if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) return {
1711
+ kind: "text",
1712
+ blockId: navigationTarget.blockId,
1713
+ range: {
1714
+ start: 0,
1715
+ end: 0
1716
+ },
1717
+ ...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
1718
+ ...record.address && typeof record.address === "object" ? { address: record.address } : {}
1719
+ };
1720
+ return target;
1721
+ }
1667
1722
  function storyLocatorSignature(story) {
1668
1723
  if (!story || typeof story !== "object") return "story:body";
1669
1724
  const record = story;
@@ -1870,6 +1925,7 @@ function buildTrackedChangeIdContext(items) {
1870
1925
  return context;
1871
1926
  }
1872
1927
  var storyScopedTrackedChangeIdContextCache = /* @__PURE__ */ new WeakMap();
1928
+ var declaredBodyItemsCache = /* @__PURE__ */ new WeakMap();
1873
1929
  function buildStoryScopedTrackedChangeIdContext(items, story) {
1874
1930
  const signature = storyLocatorSignature(story);
1875
1931
  let byStory = storyScopedTrackedChangeIdContextCache.get(items);
@@ -1883,6 +1939,56 @@ function buildStoryScopedTrackedChangeIdContext(items, story) {
1883
1939
  byStory.set(signature, context);
1884
1940
  return context;
1885
1941
  }
1942
+ function isTextboxStory(story) {
1943
+ return !!story && typeof story === "object" && story.storyType === "textbox";
1944
+ }
1945
+ function resolveStorylessCanonicalTrackedChangeId(items, selectionIds) {
1946
+ const storyless = selectionIds.filter((id) => items.some((item) => readEntityId(item) === id && readEntityStory(item) == null));
1947
+ if (storyless.length === 0) return null;
1948
+ if (storyless.length === 1) return storyless[0];
1949
+ const seen = /* @__PURE__ */ new Set();
1950
+ for (const id of storyless) {
1951
+ const row = items.find((item) => readEntityId(item) === id);
1952
+ if (!row) continue;
1953
+ for (const raw of readTrackChangeProvenanceIds(row)) {
1954
+ if (seen.has(raw)) return null;
1955
+ seen.add(raw);
1956
+ }
1957
+ }
1958
+ return storyless[0];
1959
+ }
1960
+ function declaredBodyItems(items) {
1961
+ const cached = declaredBodyItemsCache.get(items);
1962
+ if (cached) return cached;
1963
+ const declared = items.filter((item) => readEntityStory(item) != null);
1964
+ const result = declared.length === items.length ? items : declared;
1965
+ declaredBodyItemsCache.set(items, result);
1966
+ return result;
1967
+ }
1968
+ function resolveSelectionActiveChangeId(options) {
1969
+ const { selectionIds, publicIds, selection, bodyItems, allStoryItems } = options;
1970
+ if (selectionIds.length === 0) return null;
1971
+ const mapIds = (context) => {
1972
+ for (const id of selectionIds) {
1973
+ const publicId = context.toPublicId(id);
1974
+ if (publicId) return publicId;
1975
+ }
1976
+ return null;
1977
+ };
1978
+ const story = selectionStory(selection);
1979
+ const isBodySelection = storyLocatorSignature(story) === storyLocatorSignature(null);
1980
+ if (allStoryItems) {
1981
+ const scopedItems = isBodySelection ? declaredBodyItems(allStoryItems) : allStoryItems;
1982
+ const fromStory = mapIds(buildStoryScopedTrackedChangeIdContext(scopedItems, story));
1983
+ if (fromStory) return fromStory;
1984
+ const fromPublic = publicIds?.find((id) => scopedItems.some((item) => entityRowMatchesRequest(item, id, story)));
1985
+ if (fromPublic) return fromPublic;
1986
+ if (!isBodySelection) return isTextboxStory(story) ? resolveStorylessCanonicalTrackedChangeId(allStoryItems, selectionIds) : null;
1987
+ return mapIds(buildTrackedChangeIdContext(bodyItems));
1988
+ }
1989
+ if (isBodySelection) return mapIds(buildTrackedChangeIdContext(bodyItems)) ?? selectionIds[0] ?? null;
1990
+ return null;
1991
+ }
1886
1992
  function resolveActiveCommentId(items, commentId) {
1887
1993
  const item = items.find((candidate) => readEntityId(candidate) === commentId || candidate.importedId === commentId);
1888
1994
  if (!item) return null;
@@ -2860,11 +2966,14 @@ function createSuperDocUI(options) {
2860
2966
  let explicitActiveChange = null;
2861
2967
  let explicitActiveChangeRevision = 0;
2862
2968
  let queuedTrackChangeNavigationInvalidation = 0;
2969
+ let trackChangeRevealInvalidation = 0;
2970
+ const pendingTrackChangeRevealFocuses = /* @__PURE__ */ new Set();
2863
2971
  const explicitActiveChangesEqual = (left, right) => {
2864
2972
  if (left === right) return true;
2865
2973
  if (!left || !right) return false;
2866
2974
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
2867
2975
  };
2976
+ const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
2868
2977
  const mirrorTrackedChangeFocusToHost = (next) => {
2869
2978
  const host = getHost();
2870
2979
  if (typeof host?.getHandles !== "function") return;
@@ -2893,6 +3002,7 @@ function createSuperDocUI(options) {
2893
3002
  const setExplicitActiveChange = (next, options$1) => {
2894
3003
  explicitActiveChange = next;
2895
3004
  explicitActiveChangeRevision += 1;
3005
+ trackChangeRevealInvalidation += 1;
2896
3006
  if (options$1?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
2897
3007
  };
2898
3008
  const listeners = /* @__PURE__ */ new Set();
@@ -3298,7 +3408,7 @@ function createSuperDocUI(options) {
3298
3408
  const read = host?.getDocumentLoadingSnapshot;
3299
3409
  if (typeof read !== "function") return "unknown";
3300
3410
  const stage = safeCall(() => read.call(host), null)?.sourceStage;
3301
- if (stage === "opening" || stage === "ready-window" || stage === "source-loading") return "loading";
3411
+ if (stage === "opening" || stage === "bootstrap-ready" || stage === "source-loading") return "loading";
3302
3412
  if (stage === "source-complete" || stage === "source-failed" || stage === "source-cancelled") return "complete";
3303
3413
  return "unknown";
3304
3414
  };
@@ -3938,8 +4048,8 @@ function createSuperDocUI(options) {
3938
4048
  status: heldEntry?.hasSettled ? "stale" : "pending"
3939
4049
  } : readAsync("trackChanges", token, () => {
3940
4050
  if (listTrackedChanges) return listTrackedChanges.call(v2TrackedChanges);
3941
- return tcApi?.list ? tcApi.list() : void 0;
3942
- }, (raw) => raw && Array.isArray(raw.items) ? raw.items.filter((item) => !listTrackedChanges || storyLocatorSignature(readEntityStory(item)) === "story:body") : []);
4051
+ return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
4052
+ }, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
3943
4053
  const items = (value ?? []).map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
3944
4054
  const id = readEntityId(item);
3945
4055
  return !id || !postDecisionIds?.has(id);
@@ -3958,8 +4068,20 @@ function createSuperDocUI(options) {
3958
4068
  if (allStoryItems && !allStoryItems.some((row) => entityRowMatchesRequest(row, active.id, active.story))) setExplicitActiveChange(null);
3959
4069
  } else if (!items.some((item) => readEntityId(item) === active.id)) setExplicitActiveChange(null);
3960
4070
  }
4071
+ const publicIdItems = allStoryItems ?? items;
4072
+ const selectionIdContext = buildStoryScopedTrackedChangeIdContext(publicIdItems, selectionStory(selection$1));
3961
4073
  const selectionActiveChangeIds = postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
3962
- const activeId = explicitActiveChange?.id ?? selectionActiveChangeIds[0] ?? null;
4074
+ const selectionPublicChangeIds = selectionActiveChangeIds.map((id) => selectionIdContext.toPublicId(id) ?? id);
4075
+ const explicitActiveIdContext = explicitActiveChange?.story ? buildStoryScopedTrackedChangeIdContext(publicIdItems, explicitActiveChange.story) : buildTrackedChangeIdContext(publicIdItems);
4076
+ const explicitActiveId = explicitActiveChange ? explicitActiveIdContext.toPublicId(explicitActiveChange.id) ?? explicitActiveChange.id : null;
4077
+ const selectionActiveId = resolveSelectionActiveChangeId({
4078
+ selectionIds: selectionActiveChangeIds,
4079
+ publicIds: selectionPublicChangeIds,
4080
+ selection: selection$1,
4081
+ bodyItems: items,
4082
+ allStoryItems
4083
+ });
4084
+ const activeId = explicitActiveId ?? selectionActiveId;
3963
4085
  return {
3964
4086
  status: combineStatus(listStatus, selection$1.status),
3965
4087
  items,
@@ -5623,13 +5745,18 @@ function createSuperDocUI(options) {
5623
5745
  const target = rawTarget && typeof rawTarget === "object" ? rawTarget : null;
5624
5746
  let next = null;
5625
5747
  if (target?.entityType === "trackedChange" && typeof target.entityId === "string" && target.entityId.length > 0) {
5626
- const story = target.story && storyLocatorSignature(target.story) !== storyLocatorSignature(null) ? target.story : void 0;
5748
+ const paintedEntityId = target.entityId;
5749
+ const targetStory = target.story && typeof target.story === "object" ? target.story : void 0;
5750
+ const allStoryItems = readAllStoryTrackChanges();
5751
+ const entityId = (allStoryItems ? targetStory ? buildStoryScopedTrackedChangeIdContext(allStoryItems, targetStory).toPublicId(paintedEntityId) : buildTrackedChangeIdContext(allStoryItems).toPublicId(paintedEntityId) : null) ?? paintedEntityId;
5752
+ const story = targetStory && storyLocatorSignature(targetStory) !== storyLocatorSignature(null) ? targetStory : void 0;
5627
5753
  next = {
5628
- id: target.entityId,
5754
+ id: entityId,
5629
5755
  ...story ? { story } : {},
5630
- ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : {}
5756
+ ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : entityId !== paintedEntityId ? { paintedEntityId } : {}
5631
5757
  };
5632
5758
  }
5759
+ if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
5633
5760
  if (explicitActiveChangesEqual(explicitActiveChange, next)) return false;
5634
5761
  setExplicitActiveChange(next);
5635
5762
  return true;
@@ -5867,6 +5994,17 @@ function createSuperDocUI(options) {
5867
5994
  function readTrackDecisionTargetId(target) {
5868
5995
  return target && typeof target === "object" && typeof target.id === "string" ? String(target.id) : null;
5869
5996
  }
5997
+ function nonBodySelectionStoryLocator(selection$1) {
5998
+ const story = selectionStoryLocator(selection$1);
5999
+ return story && storyLocatorSignature(story) !== storyLocatorSignature(null) ? story : void 0;
6000
+ }
6001
+ function trackDecisionIdTarget(changeId, story) {
6002
+ return {
6003
+ kind: "id",
6004
+ id: changeId,
6005
+ ...story ? { story } : {}
6006
+ };
6007
+ }
5870
6008
  function trackDecisionRangeFromSelection(selection$1) {
5871
6009
  if (selection$1.empty) return null;
5872
6010
  const explicit = selection$1.selectionTarget;
@@ -5892,19 +6030,55 @@ function createSuperDocUI(options) {
5892
6030
  }
5893
6031
  };
5894
6032
  }
6033
+ function readBodyTrackChangeForDecision(activeId, story) {
6034
+ if (storyLocatorSignature(story) !== storyLocatorSignature(null)) return void 0;
6035
+ const trackChanges$1 = getDoc()?.trackChanges;
6036
+ const list = trackChanges$1?.list;
6037
+ if (typeof list !== "function") return void 0;
6038
+ const raw = safeCall(() => list.call(trackChanges$1), null);
6039
+ if (isPromiseLike(raw) || !raw || typeof raw !== "object") return void 0;
6040
+ const items = raw.items;
6041
+ if (!Array.isArray(items)) return void 0;
6042
+ return items.map(projectTrackChangesItem).find((item) => item != null && entityRowMatchesRequest(item, activeId, story));
6043
+ }
6044
+ function selectedTextCoversTrackChangeText(selection$1, item) {
6045
+ const selected = normalizeTrackDecisionText(selection$1.quotedText);
6046
+ if (!selected) return false;
6047
+ const payload = trackChangesItemPayload(item);
6048
+ return [
6049
+ payload.insertedText,
6050
+ payload.deletedText,
6051
+ payload.excerpt
6052
+ ].some((value) => normalizeTrackDecisionText(value) === selected);
6053
+ }
6054
+ function normalizeTrackDecisionText(value) {
6055
+ return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
6056
+ }
5895
6057
  function activeChangePartialDecisionRoute() {
5896
6058
  const activeId = state.selection.activeChangeIds[0];
5897
6059
  if (!activeId) return "unavailable";
5898
- const items = readAllStoryTrackChanges();
5899
- if (!items) return "unavailable";
5900
6060
  const story = selectionStoryLocator(state.selection);
5901
- const item = items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
6061
+ const item = (state.trackChanges.status === "ready" ? state.trackChanges.items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story)) : void 0) ?? readBodyTrackChangeForDecision(activeId, story) ?? readAllStoryTrackChanges()?.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
5902
6062
  if (!item) return "unavailable";
5903
- const type = trackChangesItemPayload(item).type;
5904
- const grouping = trackChangesItemPayload(item).grouping;
6063
+ const change = trackChangesItemPayload(item);
6064
+ const type = change.type;
6065
+ const grouping = change.grouping;
6066
+ if (selectedTextCoversTrackChangeText(state.selection, item)) return "id";
5905
6067
  if (type === "replacement" && grouping === "replacement-pair") return "range";
5906
6068
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
5907
6069
  }
6070
+ function executeTrackDecisionRangeWithIdFallback(kind, rangeTarget, changeId, story) {
6071
+ const fallback = () => callTrackDecisionTarget(kind, trackDecisionIdTarget(changeId, story), changeId, story);
6072
+ try {
6073
+ const result = callTrackDecisionTarget(kind, rangeTarget, null);
6074
+ if (isPromiseLike(result)) return settleCommandExecution(Promise.resolve(result).then((settled) => {
6075
+ return commandResultSucceeded(commandResultFromOperationResult(settled)) ? settled : fallback();
6076
+ }));
6077
+ return settleCommandExecution(commandResultSucceeded(commandResultFromOperationResult(result)) ? result : fallback());
6078
+ } catch {
6079
+ return false;
6080
+ }
6081
+ }
5908
6082
  function resolveTrackDecisionTarget(command, payload) {
5909
6083
  if (command.scope === "all") return {
5910
6084
  target: { scope: "all" },
@@ -5919,26 +6093,27 @@ function createSuperDocUI(options) {
5919
6093
  };
5920
6094
  if (payload && typeof payload === "object") {
5921
6095
  const record = payload;
5922
- if (record.target && typeof record.target === "object") return {
5923
- target: record.target,
5924
- changeId: readTrackDecisionTargetId(record.target)
5925
- };
6096
+ if (record.target && typeof record.target === "object") {
6097
+ const target = record.target;
6098
+ return {
6099
+ target,
6100
+ changeId: readTrackDecisionTargetId(target),
6101
+ story: target.story
6102
+ };
6103
+ }
5926
6104
  const id = typeof record.changeId === "string" ? record.changeId : record.id;
5927
6105
  if (typeof id === "string" && id.length > 0) return {
5928
- target: {
5929
- kind: "id",
5930
- id
5931
- },
5932
- changeId: id
6106
+ target: trackDecisionIdTarget(id, record.story),
6107
+ changeId: id,
6108
+ story: record.story
5933
6109
  };
5934
6110
  }
5935
6111
  const selectedId = state.selection.activeChangeIds[0];
6112
+ const story = nonBodySelectionStoryLocator(state.selection);
5936
6113
  return selectedId ? {
5937
- target: {
5938
- kind: "id",
5939
- id: selectedId
5940
- },
5941
- changeId: selectedId
6114
+ target: trackDecisionIdTarget(selectedId, story),
6115
+ changeId: selectedId,
6116
+ story
5942
6117
  } : null;
5943
6118
  }
5944
6119
  function executeTrackDecisionCommand(command, payload) {
@@ -5952,13 +6127,23 @@ function createSuperDocUI(options) {
5952
6127
  if (route === "unavailable") return false;
5953
6128
  if (route === "range") {
5954
6129
  const target = trackDecisionRangeFromSelection(state.selection);
5955
- return target ? executeTrackDecisionTarget(command.kind, target, null) : false;
6130
+ const story = nonBodySelectionStoryLocator(state.selection);
6131
+ return target ? executeTrackDecisionRangeWithIdFallback(command.kind, target, state.selection.activeChangeIds[0], story) : false;
6132
+ }
6133
+ if (route === "id") {
6134
+ const activeId = state.selection.activeChangeIds[0];
6135
+ const selectedStory = selectionStoryLocator(state.selection);
6136
+ const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
6137
+ return executeTrackDecision(command.kind, story ? {
6138
+ id: activeId,
6139
+ story
6140
+ } : activeId);
5956
6141
  }
5957
6142
  }
5958
6143
  }
5959
6144
  const resolved = resolveTrackDecisionTarget(command, payload);
5960
6145
  if (!resolved) return false;
5961
- return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId);
6146
+ return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId, resolved.story);
5962
6147
  }
5963
6148
  function executeListToggleCommand(kind, payload) {
5964
6149
  const lists = getEditCommands()?.lists;
@@ -7478,11 +7663,12 @@ function createSuperDocUI(options) {
7478
7663
  reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
7479
7664
  };
7480
7665
  try {
7481
- const result = await Promise.resolve(scroll.call(host, {
7666
+ const input = {
7482
7667
  target,
7483
7668
  block: options$1?.block ?? "center",
7484
7669
  behavior: options$1?.behavior ?? "smooth"
7485
- }));
7670
+ };
7671
+ const result = await Promise.resolve(options$1?.shouldContinue ? scroll.call(host, input, options$1.shouldContinue) : scroll.call(host, input));
7486
7672
  if (result && typeof result === "object" && result.success === false) return {
7487
7673
  success: false,
7488
7674
  ok: false,
@@ -7512,19 +7698,32 @@ function createSuperDocUI(options) {
7512
7698
  story: requestedStory
7513
7699
  };
7514
7700
  };
7515
- const fromList = readEntityTarget(loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory)));
7516
- if (fromList) return withRequestedStory(fromList);
7701
+ const readTarget = namespace === "trackChanges" ? readTrackedChangeNavigationTarget : readEntityTarget;
7702
+ const loadedRow = loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory));
7703
+ const loadedRecord = loadedRow && typeof loadedRow === "object" ? loadedRow : null;
7704
+ const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
7705
+ const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
7706
+ const readResolvedTarget = (row) => {
7707
+ if (!moveSide) return readTarget(row);
7708
+ const moveTarget = readEntityTarget(row);
7709
+ if (moveTarget?.kind !== "move") return null;
7710
+ const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
7711
+ return isHostGeometryTarget(sideTarget) ? sideTarget : null;
7712
+ };
7713
+ const fromList = readTarget(loadedRow);
7714
+ const listTargetIsAuthoritative = namespace !== "trackChanges" || !moveSide && isHostGeometryTarget(readEntityTarget(loadedRow));
7715
+ if (fromList && listTargetIsAuthoritative) return withRequestedStory(fromList);
7517
7716
  const api = getDoc()?.[namespace];
7518
7717
  const input = namespace === "comments" ? { commentId: id } : {
7519
- id,
7718
+ id: trackedChangeLookupId,
7520
7719
  ...requestedStory ? { story: requestedStory } : {}
7521
7720
  };
7522
7721
  const get = api?.get;
7523
- if (typeof get !== "function") return null;
7722
+ if (typeof get !== "function") return withRequestedStory(fromList);
7524
7723
  try {
7525
- return withRequestedStory(readEntityTarget(await Promise.resolve(get.call(api, input))));
7724
+ return withRequestedStory(readResolvedTarget(await Promise.resolve(get.call(api, input))) ?? fromList);
7526
7725
  } catch {
7527
- return null;
7726
+ return withRequestedStory(fromList);
7528
7727
  }
7529
7728
  };
7530
7729
  function patchCommentStatus(commentId, status) {
@@ -7658,10 +7857,12 @@ function createSuperDocUI(options) {
7658
7857
  };
7659
7858
  const trackChangesSub = sliceHandle((s) => s.trackChanges);
7660
7859
  const navigateTrackChange = (step, options$1) => {
7661
- const rows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
7662
- if (rows.length === 0) return null;
7860
+ const allRows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
7861
+ if (allRows.length === 0) return null;
7663
7862
  const currentId = state.trackChanges.activeId;
7664
7863
  const currentStory = explicitActiveChange?.id === currentId ? explicitActiveChange.story : void 0;
7864
+ const bodyRows = allRows.filter((row) => readEntityRequestStory(row) == null);
7865
+ const rows = currentStory || bodyRows.length === 0 ? allRows : bodyRows;
7665
7866
  let currentIdx = currentId ? rows.findIndex((row) => entityRowMatchesRequest(row, currentId, currentStory)) : -1;
7666
7867
  if (currentIdx === -1 && currentId && currentStory) currentIdx = rows.findIndex((row) => entityRowMatchesRequest(row, currentId));
7667
7868
  const nextRow = rows[currentIdx === -1 ? step > 0 ? 0 : rows.length - 1 : (currentIdx + step + rows.length) % rows.length];
@@ -7686,33 +7887,41 @@ function createSuperDocUI(options) {
7686
7887
  if (activeId == null) return { success: false };
7687
7888
  const optimisticActiveChange = explicitActiveChange;
7688
7889
  const optimisticRevision = explicitActiveChangeRevision;
7689
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7890
+ const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7690
7891
  const rollback = () => {
7691
7892
  if (!isCurrent()) return;
7692
7893
  setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7693
7894
  recompute();
7694
7895
  };
7695
- const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7696
- if (!isCurrent()) return { success: false };
7697
- if (!target) {
7698
- rollback();
7699
- return { success: false };
7700
- }
7701
- trackedChangeNavigationInFlight += 1;
7896
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
7702
7897
  try {
7703
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7898
+ const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7704
7899
  if (!isCurrent()) return { success: false };
7705
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7900
+ if (!target) {
7706
7901
  rollback();
7707
- mirrorTrackedChangeFocusToHost(previousActiveChange);
7708
- } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
7709
- return { success: result.ok };
7710
- } finally {
7711
- trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7712
- if (trackedChangeNavigationInFlight === 0) {
7713
- if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7714
- else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
7902
+ return { success: false };
7715
7903
  }
7904
+ trackedChangeNavigationInFlight += 1;
7905
+ try {
7906
+ const result = await scrollTargetIntoView(target, {
7907
+ behavior: "auto",
7908
+ shouldContinue: isCurrent
7909
+ });
7910
+ if (!isCurrent()) return { success: false };
7911
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7912
+ rollback();
7913
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
7914
+ } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
7915
+ return { success: result.ok };
7916
+ } finally {
7917
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7918
+ if (trackedChangeNavigationInFlight === 0) {
7919
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7920
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
7921
+ }
7922
+ }
7923
+ } finally {
7924
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.delete(optimisticActiveChange);
7716
7925
  }
7717
7926
  };
7718
7927
  const navigateAndScroll = (step) => {
@@ -7754,7 +7963,7 @@ function createSuperDocUI(options) {
7754
7963
  if (!input || typeof input !== "object" || typeof input.x !== "number" || typeof input.y !== "number") return null;
7755
7964
  const hits = entityAt(input);
7756
7965
  if (hits.length === 0) return null;
7757
- const bodyItems = trackChangesSub.get().items;
7966
+ const items = trackChangesSub.get().items;
7758
7967
  let allStoryItems;
7759
7968
  for (const hit of hits) {
7760
7969
  if (hit.type !== "trackedChange") continue;
@@ -7763,7 +7972,7 @@ function createSuperDocUI(options) {
7763
7972
  if (allStoryItems === void 0) allStoryItems = readAllStoryTrackChanges();
7764
7973
  if (!allStoryItems) continue;
7765
7974
  candidates = allStoryItems;
7766
- } else candidates = bodyItems;
7975
+ } else candidates = items;
7767
7976
  const item = candidates.find((candidate) => entityRowMatchesRequest(candidate, hit.id, hit.story));
7768
7977
  if (item) return hit.story ? {
7769
7978
  id: item.id,
@@ -7797,10 +8006,12 @@ function createSuperDocUI(options) {
7797
8006
  if (!allStoryItems.some((row) => entityRowMatchesRequest(row, publicId, requested.story))) return false;
7798
8007
  requested.id = publicId;
7799
8008
  } else {
7800
- const bodyItems = trackChangesSub.get().items;
7801
- const publicId = buildTrackedChangeIdContext(bodyItems).toPublicId(requested.id) ?? requested.id;
7802
- if (!bodyItems.some((item) => readEntityId(item) === publicId)) return false;
8009
+ const items = trackChangesSub.get().items;
8010
+ const publicId = buildTrackedChangeIdContext(items).toPublicId(requested.id) ?? requested.id;
8011
+ const item = items.find((candidate) => readEntityId(candidate) === publicId);
8012
+ if (!item) return false;
7803
8013
  requested.id = publicId;
8014
+ requested.story = readEntityRequestStory(item);
7804
8015
  }
7805
8016
  if (paintedEntityId !== requested.id) requested.paintedEntityId = paintedEntityId;
7806
8017
  setExplicitActiveChange(requested);
@@ -7814,47 +8025,65 @@ function createSuperDocUI(options) {
7814
8025
  ok: false,
7815
8026
  reason: getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady
7816
8027
  };
8028
+ queuedTrackChangeNavigationInvalidation += 1;
8029
+ trackChangeRevealInvalidation += 1;
8030
+ const requestedAtInvalidation = trackChangeRevealInvalidation;
7817
8031
  const loadedItems = trackChangesSub.get().items;
7818
- const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === changeId));
7819
- const target = await resolveEntityTarget("trackChanges", changeId, loadedItems, story ? { story } : void 0);
7820
- if (!target) return {
7821
- success: false,
7822
- ok: false,
7823
- reason: SUPERDOC_UI_REASONS.targetUnresolved
7824
- };
8032
+ const publicId = buildTrackedChangeIdContext(loadedItems).toPublicId(changeId) ?? changeId;
8033
+ const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === publicId));
7825
8034
  const previousActiveChange = explicitActiveChange;
7826
- const requestedActiveChange = story ? {
7827
- id: changeId,
7828
- story
7829
- } : { id: changeId };
7830
- setExplicitActiveChange(requestedActiveChange);
7831
- recompute();
7832
- const optimisticActiveChange = explicitActiveChange;
7833
- const optimisticRevision = explicitActiveChangeRevision;
7834
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7835
- const rollback = () => {
7836
- if (!isCurrent()) return;
7837
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7838
- recompute();
8035
+ const requestedActiveChange = {
8036
+ id: publicId,
8037
+ ...story ? { story } : {},
8038
+ ...publicId !== changeId ? { paintedEntityId: changeId } : {}
7839
8039
  };
7840
- trackedChangeNavigationInFlight += 1;
8040
+ pendingTrackChangeRevealFocuses.add(requestedActiveChange);
7841
8041
  try {
7842
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7843
- if (!isCurrent()) return {
8042
+ const target = await resolveEntityTarget("trackChanges", publicId, loadedItems, story ? { story } : void 0);
8043
+ if (trackChangeRevealInvalidation !== requestedAtInvalidation) return {
7844
8044
  success: false,
7845
8045
  ok: false
7846
8046
  };
7847
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7848
- rollback();
7849
- mirrorTrackedChangeFocusToHost(previousActiveChange);
7850
- } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
7851
- return result;
7852
- } finally {
7853
- trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7854
- if (trackedChangeNavigationInFlight === 0) {
7855
- if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7856
- else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8047
+ if (!target) return {
8048
+ success: false,
8049
+ ok: false,
8050
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
8051
+ };
8052
+ setExplicitActiveChange(requestedActiveChange);
8053
+ const revealInvalidation = trackChangeRevealInvalidation;
8054
+ const optimisticActiveChange = explicitActiveChange;
8055
+ const optimisticRevision = explicitActiveChangeRevision;
8056
+ const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8057
+ const rollback = () => {
8058
+ if (!isCurrent()) return;
8059
+ setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
8060
+ recompute();
8061
+ };
8062
+ recompute();
8063
+ trackedChangeNavigationInFlight += 1;
8064
+ try {
8065
+ const result = await scrollTargetIntoView(target, {
8066
+ behavior: "auto",
8067
+ shouldContinue: isCurrent
8068
+ });
8069
+ if (!isCurrent()) return {
8070
+ success: false,
8071
+ ok: false
8072
+ };
8073
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8074
+ rollback();
8075
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8076
+ } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
8077
+ return result;
8078
+ } finally {
8079
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8080
+ if (trackedChangeNavigationInFlight === 0) {
8081
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8082
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8083
+ }
7857
8084
  }
8085
+ } finally {
8086
+ pendingTrackChangeRevealFocuses.delete(requestedActiveChange);
7858
8087
  }
7859
8088
  }
7860
8089
  };
@@ -8109,8 +8338,8 @@ function createSuperDocUI(options) {
8109
8338
  const rawHits = collectEntityHitsFromChain(startEls[0], container);
8110
8339
  for (let i = 1; i < startEls.length; i += 1) for (const hit of collectEntityHitsFromChain(startEls[i], container)) rawHits.push(hit);
8111
8340
  if (rawHits.length === 0) return rawHits;
8112
- const bodyItems = trackChangesSub.get().items;
8113
- const bodyIdContext = buildTrackedChangeIdContext(bodyItems);
8341
+ const items = trackChangesSub.get().items;
8342
+ const idContext = buildTrackedChangeIdContext(items);
8114
8343
  let allStoryItems;
8115
8344
  const seen = /* @__PURE__ */ new Set();
8116
8345
  const hits = [];
@@ -8130,7 +8359,7 @@ function createSuperDocUI(options) {
8130
8359
  publicId = buildStoryScopedTrackedChangeIdContext(allStoryItems, story).toPublicId(hit.id);
8131
8360
  if (!publicId || !allStoryItems.some((item) => entityRowMatchesRequest(item, publicId, story))) continue;
8132
8361
  } else {
8133
- publicId = bodyIdContext.toPublicId(hit.id);
8362
+ publicId = idContext.toPublicId(hit.id);
8134
8363
  if (!publicId) continue;
8135
8364
  }
8136
8365
  const key = `trackedChange:${publicId}`;