superdoc 2.0.0-next.51 → 2.0.0-next.52

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.
@@ -1664,6 +1664,48 @@ function readEntityTarget(row) {
1664
1664
  const target = row.target;
1665
1665
  return target && typeof target === "object" ? target : null;
1666
1666
  }
1667
+ function isHostGeometryTarget(target) {
1668
+ if (!target || typeof target !== "object") return false;
1669
+ const record = target;
1670
+ const isTextPoint = (point) => {
1671
+ if (!point || typeof point !== "object") return false;
1672
+ const candidate = point;
1673
+ return candidate.kind === "text" && typeof candidate.blockId === "string" && typeof candidate.offset === "number";
1674
+ };
1675
+ const isTextSegment = (segment) => {
1676
+ if (!segment || typeof segment !== "object") return false;
1677
+ const candidate = segment;
1678
+ const range$1 = candidate.range;
1679
+ return typeof candidate.blockId === "string" && typeof range$1?.start === "number" && typeof range$1.end === "number";
1680
+ };
1681
+ if (Array.isArray(record.segments)) {
1682
+ const first = record.segments[0];
1683
+ const last = record.segments[record.segments.length - 1];
1684
+ return isTextSegment(first) && isTextSegment(last);
1685
+ }
1686
+ if (record.kind === "selection") return isTextPoint(record.start) && isTextPoint(record.end);
1687
+ if (record.kind !== "text" || typeof record.blockId !== "string") return false;
1688
+ const range = record.range;
1689
+ return typeof range?.start === "number" && typeof range.end === "number";
1690
+ }
1691
+ function readTrackedChangeNavigationTarget(row) {
1692
+ if (!row || typeof row !== "object") return null;
1693
+ const target = readEntityTarget(row);
1694
+ if (isHostGeometryTarget(target)) return target;
1695
+ const record = row;
1696
+ const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
1697
+ if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) return {
1698
+ kind: "text",
1699
+ blockId: navigationTarget.blockId,
1700
+ range: {
1701
+ start: 0,
1702
+ end: 0
1703
+ },
1704
+ ...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
1705
+ ...record.address && typeof record.address === "object" ? { address: record.address } : {}
1706
+ };
1707
+ return target;
1708
+ }
1667
1709
  function storyLocatorSignature(story) {
1668
1710
  if (!story || typeof story !== "object") return "story:body";
1669
1711
  const record = story;
@@ -2860,11 +2902,14 @@ function createSuperDocUI(options) {
2860
2902
  let explicitActiveChange = null;
2861
2903
  let explicitActiveChangeRevision = 0;
2862
2904
  let queuedTrackChangeNavigationInvalidation = 0;
2905
+ let trackChangeRevealInvalidation = 0;
2906
+ const pendingTrackChangeRevealFocuses = /* @__PURE__ */ new Set();
2863
2907
  const explicitActiveChangesEqual = (left, right) => {
2864
2908
  if (left === right) return true;
2865
2909
  if (!left || !right) return false;
2866
2910
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
2867
2911
  };
2912
+ const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
2868
2913
  const mirrorTrackedChangeFocusToHost = (next) => {
2869
2914
  const host = getHost();
2870
2915
  if (typeof host?.getHandles !== "function") return;
@@ -2893,6 +2938,7 @@ function createSuperDocUI(options) {
2893
2938
  const setExplicitActiveChange = (next, options$1) => {
2894
2939
  explicitActiveChange = next;
2895
2940
  explicitActiveChangeRevision += 1;
2941
+ trackChangeRevealInvalidation += 1;
2896
2942
  if (options$1?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
2897
2943
  };
2898
2944
  const listeners = /* @__PURE__ */ new Set();
@@ -5630,6 +5676,7 @@ function createSuperDocUI(options) {
5630
5676
  ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : {}
5631
5677
  };
5632
5678
  }
5679
+ if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
5633
5680
  if (explicitActiveChangesEqual(explicitActiveChange, next)) return false;
5634
5681
  setExplicitActiveChange(next);
5635
5682
  return true;
@@ -5900,9 +5947,13 @@ function createSuperDocUI(options) {
5900
5947
  const story = selectionStoryLocator(state.selection);
5901
5948
  const item = items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
5902
5949
  if (!item) return "unavailable";
5903
- const type = trackChangesItemPayload(item).type;
5904
- const grouping = trackChangesItemPayload(item).grouping;
5905
- if (type === "replacement" && grouping === "replacement-pair") return "range";
5950
+ const change = trackChangesItemPayload(item);
5951
+ const type = change.type;
5952
+ const grouping = change.grouping;
5953
+ if (type === "replacement" && grouping === "replacement-pair") {
5954
+ if (typeof change.insertedText === "string" && state.selection.quotedText === change.insertedText) return "id";
5955
+ return "range";
5956
+ }
5906
5957
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
5907
5958
  }
5908
5959
  function resolveTrackDecisionTarget(command, payload) {
@@ -5954,6 +6005,15 @@ function createSuperDocUI(options) {
5954
6005
  const target = trackDecisionRangeFromSelection(state.selection);
5955
6006
  return target ? executeTrackDecisionTarget(command.kind, target, null) : false;
5956
6007
  }
6008
+ if (route === "id") {
6009
+ const activeId = state.selection.activeChangeIds[0];
6010
+ const selectedStory = selectionStoryLocator(state.selection);
6011
+ const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
6012
+ return executeTrackDecision(command.kind, story ? {
6013
+ id: activeId,
6014
+ story
6015
+ } : activeId);
6016
+ }
5957
6017
  }
5958
6018
  }
5959
6019
  const resolved = resolveTrackDecisionTarget(command, payload);
@@ -7478,11 +7538,12 @@ function createSuperDocUI(options) {
7478
7538
  reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
7479
7539
  };
7480
7540
  try {
7481
- const result = await Promise.resolve(scroll.call(host, {
7541
+ const input = {
7482
7542
  target,
7483
7543
  block: options$1?.block ?? "center",
7484
7544
  behavior: options$1?.behavior ?? "smooth"
7485
- }));
7545
+ };
7546
+ const result = await Promise.resolve(options$1?.shouldContinue ? scroll.call(host, input, options$1.shouldContinue) : scroll.call(host, input));
7486
7547
  if (result && typeof result === "object" && result.success === false) return {
7487
7548
  success: false,
7488
7549
  ok: false,
@@ -7512,19 +7573,32 @@ function createSuperDocUI(options) {
7512
7573
  story: requestedStory
7513
7574
  };
7514
7575
  };
7515
- const fromList = readEntityTarget(loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory)));
7516
- if (fromList) return withRequestedStory(fromList);
7576
+ const readTarget = namespace === "trackChanges" ? readTrackedChangeNavigationTarget : readEntityTarget;
7577
+ const loadedRow = loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory));
7578
+ const loadedRecord = loadedRow && typeof loadedRow === "object" ? loadedRow : null;
7579
+ const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
7580
+ const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
7581
+ const readResolvedTarget = (row) => {
7582
+ if (!moveSide) return readTarget(row);
7583
+ const moveTarget = readEntityTarget(row);
7584
+ if (moveTarget?.kind !== "move") return null;
7585
+ const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
7586
+ return isHostGeometryTarget(sideTarget) ? sideTarget : null;
7587
+ };
7588
+ const fromList = readTarget(loadedRow);
7589
+ const listTargetIsAuthoritative = namespace !== "trackChanges" || !moveSide && isHostGeometryTarget(readEntityTarget(loadedRow));
7590
+ if (fromList && listTargetIsAuthoritative) return withRequestedStory(fromList);
7517
7591
  const api = getDoc()?.[namespace];
7518
7592
  const input = namespace === "comments" ? { commentId: id } : {
7519
- id,
7593
+ id: trackedChangeLookupId,
7520
7594
  ...requestedStory ? { story: requestedStory } : {}
7521
7595
  };
7522
7596
  const get = api?.get;
7523
- if (typeof get !== "function") return null;
7597
+ if (typeof get !== "function") return withRequestedStory(fromList);
7524
7598
  try {
7525
- return withRequestedStory(readEntityTarget(await Promise.resolve(get.call(api, input))));
7599
+ return withRequestedStory(readResolvedTarget(await Promise.resolve(get.call(api, input))) ?? fromList);
7526
7600
  } catch {
7527
- return null;
7601
+ return withRequestedStory(fromList);
7528
7602
  }
7529
7603
  };
7530
7604
  function patchCommentStatus(commentId, status) {
@@ -7686,33 +7760,41 @@ function createSuperDocUI(options) {
7686
7760
  if (activeId == null) return { success: false };
7687
7761
  const optimisticActiveChange = explicitActiveChange;
7688
7762
  const optimisticRevision = explicitActiveChangeRevision;
7689
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7763
+ const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7690
7764
  const rollback = () => {
7691
7765
  if (!isCurrent()) return;
7692
7766
  setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7693
7767
  recompute();
7694
7768
  };
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;
7769
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
7702
7770
  try {
7703
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7771
+ const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7704
7772
  if (!isCurrent()) return { success: false };
7705
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7773
+ if (!target) {
7706
7774
  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));
7775
+ return { success: false };
7776
+ }
7777
+ trackedChangeNavigationInFlight += 1;
7778
+ try {
7779
+ const result = await scrollTargetIntoView(target, {
7780
+ behavior: "auto",
7781
+ shouldContinue: isCurrent
7782
+ });
7783
+ if (!isCurrent()) return { success: false };
7784
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7785
+ rollback();
7786
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
7787
+ } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
7788
+ return { success: result.ok };
7789
+ } finally {
7790
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7791
+ if (trackedChangeNavigationInFlight === 0) {
7792
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7793
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
7794
+ }
7715
7795
  }
7796
+ } finally {
7797
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.delete(optimisticActiveChange);
7716
7798
  }
7717
7799
  };
7718
7800
  const navigateAndScroll = (step) => {
@@ -7814,47 +7896,65 @@ function createSuperDocUI(options) {
7814
7896
  ok: false,
7815
7897
  reason: getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady
7816
7898
  };
7899
+ queuedTrackChangeNavigationInvalidation += 1;
7900
+ trackChangeRevealInvalidation += 1;
7901
+ const requestedAtInvalidation = trackChangeRevealInvalidation;
7817
7902
  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
- };
7903
+ const publicId = buildTrackedChangeIdContext(loadedItems).toPublicId(changeId) ?? changeId;
7904
+ const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === publicId));
7825
7905
  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();
7906
+ const requestedActiveChange = {
7907
+ id: publicId,
7908
+ ...story ? { story } : {},
7909
+ ...publicId !== changeId ? { paintedEntityId: changeId } : {}
7839
7910
  };
7840
- trackedChangeNavigationInFlight += 1;
7911
+ pendingTrackChangeRevealFocuses.add(requestedActiveChange);
7841
7912
  try {
7842
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7843
- if (!isCurrent()) return {
7913
+ const target = await resolveEntityTarget("trackChanges", publicId, loadedItems, story ? { story } : void 0);
7914
+ if (trackChangeRevealInvalidation !== requestedAtInvalidation) return {
7844
7915
  success: false,
7845
7916
  ok: false
7846
7917
  };
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));
7918
+ if (!target) return {
7919
+ success: false,
7920
+ ok: false,
7921
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
7922
+ };
7923
+ setExplicitActiveChange(requestedActiveChange);
7924
+ const revealInvalidation = trackChangeRevealInvalidation;
7925
+ const optimisticActiveChange = explicitActiveChange;
7926
+ const optimisticRevision = explicitActiveChangeRevision;
7927
+ const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7928
+ const rollback = () => {
7929
+ if (!isCurrent()) return;
7930
+ setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7931
+ recompute();
7932
+ };
7933
+ recompute();
7934
+ trackedChangeNavigationInFlight += 1;
7935
+ try {
7936
+ const result = await scrollTargetIntoView(target, {
7937
+ behavior: "auto",
7938
+ shouldContinue: isCurrent
7939
+ });
7940
+ if (!isCurrent()) return {
7941
+ success: false,
7942
+ ok: false
7943
+ };
7944
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7945
+ rollback();
7946
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
7947
+ } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
7948
+ return result;
7949
+ } finally {
7950
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7951
+ if (trackedChangeNavigationInFlight === 0) {
7952
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7953
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
7954
+ }
7857
7955
  }
7956
+ } finally {
7957
+ pendingTrackChangeRevealFocuses.delete(requestedActiveChange);
7858
7958
  }
7859
7959
  }
7860
7960
  };
@@ -1886,6 +1886,48 @@ function readEntityTarget(row) {
1886
1886
  const target = row.target;
1887
1887
  return target && typeof target === "object" ? target : null;
1888
1888
  }
1889
+ function isHostGeometryTarget(target) {
1890
+ if (!target || typeof target !== "object") return false;
1891
+ const record = target;
1892
+ const isTextPoint = (point) => {
1893
+ if (!point || typeof point !== "object") return false;
1894
+ const candidate = point;
1895
+ return candidate.kind === "text" && typeof candidate.blockId === "string" && typeof candidate.offset === "number";
1896
+ };
1897
+ const isTextSegment = (segment) => {
1898
+ if (!segment || typeof segment !== "object") return false;
1899
+ const candidate = segment;
1900
+ const range$1 = candidate.range;
1901
+ return typeof candidate.blockId === "string" && typeof range$1?.start === "number" && typeof range$1.end === "number";
1902
+ };
1903
+ if (Array.isArray(record.segments)) {
1904
+ const first = record.segments[0];
1905
+ const last = record.segments[record.segments.length - 1];
1906
+ return isTextSegment(first) && isTextSegment(last);
1907
+ }
1908
+ if (record.kind === "selection") return isTextPoint(record.start) && isTextPoint(record.end);
1909
+ if (record.kind !== "text" || typeof record.blockId !== "string") return false;
1910
+ const range = record.range;
1911
+ return typeof range?.start === "number" && typeof range.end === "number";
1912
+ }
1913
+ function readTrackedChangeNavigationTarget(row) {
1914
+ if (!row || typeof row !== "object") return null;
1915
+ const target = readEntityTarget(row);
1916
+ if (isHostGeometryTarget(target)) return target;
1917
+ const record = row;
1918
+ const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
1919
+ if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) return {
1920
+ kind: "text",
1921
+ blockId: navigationTarget.blockId,
1922
+ range: {
1923
+ start: 0,
1924
+ end: 0
1925
+ },
1926
+ ...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
1927
+ ...record.address && typeof record.address === "object" ? { address: record.address } : {}
1928
+ };
1929
+ return target;
1930
+ }
1889
1931
  function storyLocatorSignature(story) {
1890
1932
  if (!story || typeof story !== "object") return "story:body";
1891
1933
  const record = story;
@@ -3082,11 +3124,14 @@ function createSuperDocUI(options) {
3082
3124
  let explicitActiveChange = null;
3083
3125
  let explicitActiveChangeRevision = 0;
3084
3126
  let queuedTrackChangeNavigationInvalidation = 0;
3127
+ let trackChangeRevealInvalidation = 0;
3128
+ const pendingTrackChangeRevealFocuses = /* @__PURE__ */ new Set();
3085
3129
  const explicitActiveChangesEqual = (left, right) => {
3086
3130
  if (left === right) return true;
3087
3131
  if (!left || !right) return false;
3088
3132
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
3089
3133
  };
3134
+ const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
3090
3135
  const mirrorTrackedChangeFocusToHost = (next) => {
3091
3136
  const host = getHost();
3092
3137
  if (typeof host?.getHandles !== "function") return;
@@ -3115,6 +3160,7 @@ function createSuperDocUI(options) {
3115
3160
  const setExplicitActiveChange = (next, options$1) => {
3116
3161
  explicitActiveChange = next;
3117
3162
  explicitActiveChangeRevision += 1;
3163
+ trackChangeRevealInvalidation += 1;
3118
3164
  if (options$1?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
3119
3165
  };
3120
3166
  const listeners = /* @__PURE__ */ new Set();
@@ -5852,6 +5898,7 @@ function createSuperDocUI(options) {
5852
5898
  ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : {}
5853
5899
  };
5854
5900
  }
5901
+ if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
5855
5902
  if (explicitActiveChangesEqual(explicitActiveChange, next)) return false;
5856
5903
  setExplicitActiveChange(next);
5857
5904
  return true;
@@ -6122,9 +6169,13 @@ function createSuperDocUI(options) {
6122
6169
  const story = selectionStoryLocator(state.selection);
6123
6170
  const item = items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
6124
6171
  if (!item) return "unavailable";
6125
- const type = trackChangesItemPayload(item).type;
6126
- const grouping = trackChangesItemPayload(item).grouping;
6127
- if (type === "replacement" && grouping === "replacement-pair") return "range";
6172
+ const change = trackChangesItemPayload(item);
6173
+ const type = change.type;
6174
+ const grouping = change.grouping;
6175
+ if (type === "replacement" && grouping === "replacement-pair") {
6176
+ if (typeof change.insertedText === "string" && state.selection.quotedText === change.insertedText) return "id";
6177
+ return "range";
6178
+ }
6128
6179
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
6129
6180
  }
6130
6181
  function resolveTrackDecisionTarget(command, payload) {
@@ -6176,6 +6227,15 @@ function createSuperDocUI(options) {
6176
6227
  const target = trackDecisionRangeFromSelection(state.selection);
6177
6228
  return target ? executeTrackDecisionTarget(command.kind, target, null) : false;
6178
6229
  }
6230
+ if (route === "id") {
6231
+ const activeId = state.selection.activeChangeIds[0];
6232
+ const selectedStory = selectionStoryLocator(state.selection);
6233
+ const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
6234
+ return executeTrackDecision(command.kind, story ? {
6235
+ id: activeId,
6236
+ story
6237
+ } : activeId);
6238
+ }
6179
6239
  }
6180
6240
  }
6181
6241
  const resolved = resolveTrackDecisionTarget(command, payload);
@@ -7700,11 +7760,12 @@ function createSuperDocUI(options) {
7700
7760
  reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
7701
7761
  };
7702
7762
  try {
7703
- const result = await Promise.resolve(scroll.call(host, {
7763
+ const input = {
7704
7764
  target,
7705
7765
  block: options$1?.block ?? "center",
7706
7766
  behavior: options$1?.behavior ?? "smooth"
7707
- }));
7767
+ };
7768
+ const result = await Promise.resolve(options$1?.shouldContinue ? scroll.call(host, input, options$1.shouldContinue) : scroll.call(host, input));
7708
7769
  if (result && typeof result === "object" && result.success === false) return {
7709
7770
  success: false,
7710
7771
  ok: false,
@@ -7734,19 +7795,32 @@ function createSuperDocUI(options) {
7734
7795
  story: requestedStory
7735
7796
  };
7736
7797
  };
7737
- const fromList = readEntityTarget(loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory)));
7738
- if (fromList) return withRequestedStory(fromList);
7798
+ const readTarget = namespace === "trackChanges" ? readTrackedChangeNavigationTarget : readEntityTarget;
7799
+ const loadedRow = loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory));
7800
+ const loadedRecord = loadedRow && typeof loadedRow === "object" ? loadedRow : null;
7801
+ const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
7802
+ const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
7803
+ const readResolvedTarget = (row) => {
7804
+ if (!moveSide) return readTarget(row);
7805
+ const moveTarget = readEntityTarget(row);
7806
+ if (moveTarget?.kind !== "move") return null;
7807
+ const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
7808
+ return isHostGeometryTarget(sideTarget) ? sideTarget : null;
7809
+ };
7810
+ const fromList = readTarget(loadedRow);
7811
+ const listTargetIsAuthoritative = namespace !== "trackChanges" || !moveSide && isHostGeometryTarget(readEntityTarget(loadedRow));
7812
+ if (fromList && listTargetIsAuthoritative) return withRequestedStory(fromList);
7739
7813
  const api = getDoc()?.[namespace];
7740
7814
  const input = namespace === "comments" ? { commentId: id } : {
7741
- id,
7815
+ id: trackedChangeLookupId,
7742
7816
  ...requestedStory ? { story: requestedStory } : {}
7743
7817
  };
7744
7818
  const get = api?.get;
7745
- if (typeof get !== "function") return null;
7819
+ if (typeof get !== "function") return withRequestedStory(fromList);
7746
7820
  try {
7747
- return withRequestedStory(readEntityTarget(await Promise.resolve(get.call(api, input))));
7821
+ return withRequestedStory(readResolvedTarget(await Promise.resolve(get.call(api, input))) ?? fromList);
7748
7822
  } catch {
7749
- return null;
7823
+ return withRequestedStory(fromList);
7750
7824
  }
7751
7825
  };
7752
7826
  function patchCommentStatus(commentId, status) {
@@ -7908,33 +7982,41 @@ function createSuperDocUI(options) {
7908
7982
  if (activeId == null) return { success: false };
7909
7983
  const optimisticActiveChange = explicitActiveChange;
7910
7984
  const optimisticRevision = explicitActiveChangeRevision;
7911
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7985
+ const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7912
7986
  const rollback = () => {
7913
7987
  if (!isCurrent()) return;
7914
7988
  setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7915
7989
  recompute();
7916
7990
  };
7917
- const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7918
- if (!isCurrent()) return { success: false };
7919
- if (!target) {
7920
- rollback();
7921
- return { success: false };
7922
- }
7923
- trackedChangeNavigationInFlight += 1;
7991
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
7924
7992
  try {
7925
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7993
+ const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7926
7994
  if (!isCurrent()) return { success: false };
7927
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
7995
+ if (!target) {
7928
7996
  rollback();
7929
- mirrorTrackedChangeFocusToHost(previousActiveChange);
7930
- } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
7931
- return { success: result.ok };
7932
- } finally {
7933
- trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
7934
- if (trackedChangeNavigationInFlight === 0) {
7935
- if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
7936
- else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
7997
+ return { success: false };
7998
+ }
7999
+ trackedChangeNavigationInFlight += 1;
8000
+ try {
8001
+ const result = await scrollTargetIntoView(target, {
8002
+ behavior: "auto",
8003
+ shouldContinue: isCurrent
8004
+ });
8005
+ if (!isCurrent()) return { success: false };
8006
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8007
+ rollback();
8008
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8009
+ } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
8010
+ return { success: result.ok };
8011
+ } finally {
8012
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8013
+ if (trackedChangeNavigationInFlight === 0) {
8014
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8015
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8016
+ }
7937
8017
  }
8018
+ } finally {
8019
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.delete(optimisticActiveChange);
7938
8020
  }
7939
8021
  };
7940
8022
  const navigateAndScroll = (step) => {
@@ -8036,47 +8118,65 @@ function createSuperDocUI(options) {
8036
8118
  ok: false,
8037
8119
  reason: getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady
8038
8120
  };
8121
+ queuedTrackChangeNavigationInvalidation += 1;
8122
+ trackChangeRevealInvalidation += 1;
8123
+ const requestedAtInvalidation = trackChangeRevealInvalidation;
8039
8124
  const loadedItems = trackChangesSub.get().items;
8040
- const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === changeId));
8041
- const target = await resolveEntityTarget("trackChanges", changeId, loadedItems, story ? { story } : void 0);
8042
- if (!target) return {
8043
- success: false,
8044
- ok: false,
8045
- reason: SUPERDOC_UI_REASONS.targetUnresolved
8046
- };
8125
+ const publicId = buildTrackedChangeIdContext(loadedItems).toPublicId(changeId) ?? changeId;
8126
+ const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === publicId));
8047
8127
  const previousActiveChange = explicitActiveChange;
8048
- const requestedActiveChange = story ? {
8049
- id: changeId,
8050
- story
8051
- } : { id: changeId };
8052
- setExplicitActiveChange(requestedActiveChange);
8053
- recompute();
8054
- const optimisticActiveChange = explicitActiveChange;
8055
- const optimisticRevision = explicitActiveChangeRevision;
8056
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8057
- const rollback = () => {
8058
- if (!isCurrent()) return;
8059
- setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
8060
- recompute();
8128
+ const requestedActiveChange = {
8129
+ id: publicId,
8130
+ ...story ? { story } : {},
8131
+ ...publicId !== changeId ? { paintedEntityId: changeId } : {}
8061
8132
  };
8062
- trackedChangeNavigationInFlight += 1;
8133
+ pendingTrackChangeRevealFocuses.add(requestedActiveChange);
8063
8134
  try {
8064
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
8065
- if (!isCurrent()) return {
8135
+ const target = await resolveEntityTarget("trackChanges", publicId, loadedItems, story ? { story } : void 0);
8136
+ if (trackChangeRevealInvalidation !== requestedAtInvalidation) return {
8066
8137
  success: false,
8067
8138
  ok: false
8068
8139
  };
8069
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8070
- rollback();
8071
- mirrorTrackedChangeFocusToHost(previousActiveChange);
8072
- } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
8073
- return result;
8074
- } finally {
8075
- trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8076
- if (trackedChangeNavigationInFlight === 0) {
8077
- if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8078
- else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8140
+ if (!target) return {
8141
+ success: false,
8142
+ ok: false,
8143
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
8144
+ };
8145
+ setExplicitActiveChange(requestedActiveChange);
8146
+ const revealInvalidation = trackChangeRevealInvalidation;
8147
+ const optimisticActiveChange = explicitActiveChange;
8148
+ const optimisticRevision = explicitActiveChangeRevision;
8149
+ const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8150
+ const rollback = () => {
8151
+ if (!isCurrent()) return;
8152
+ setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
8153
+ recompute();
8154
+ };
8155
+ recompute();
8156
+ trackedChangeNavigationInFlight += 1;
8157
+ try {
8158
+ const result = await scrollTargetIntoView(target, {
8159
+ behavior: "auto",
8160
+ shouldContinue: isCurrent
8161
+ });
8162
+ if (!isCurrent()) return {
8163
+ success: false,
8164
+ ok: false
8165
+ };
8166
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8167
+ rollback();
8168
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8169
+ } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
8170
+ return result;
8171
+ } finally {
8172
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8173
+ if (trackedChangeNavigationInFlight === 0) {
8174
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8175
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8176
+ }
8079
8177
  }
8178
+ } finally {
8179
+ pendingTrackChangeRevealFocuses.delete(requestedActiveChange);
8080
8180
  }
8081
8181
  }
8082
8182
  };
@@ -7,7 +7,7 @@ const COLLABORATION_UPGRADE_ENGINE_MINIMUM_NODE_MAJOR = 20;
7
7
  var PRIVATE_ENGINE_INFO = (0, __superdoc_docx_engine_collaboration_upgrade_engine.getCollaborationUpgradeEngineInfo)();
8
8
  var ENGINE_INFO = Object.freeze({
9
9
  ...PRIVATE_ENGINE_INFO,
10
- superdocVersion: "2.0.0-next.51",
10
+ superdocVersion: "2.0.0-next.52",
11
11
  roomSchemaVersion: Object.freeze({ ...PRIVATE_ENGINE_INFO.roomSchemaVersion }),
12
12
  supportedBundleVersions: SUPPORTED_COLLABORATION_UPGRADE_BUNDLE_VERSIONS,
13
13
  supportedV1ReaderContractVersions: SUPPORTED_V1_READER_CONTRACT_VERSIONS