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
@@ -309,24 +309,36 @@ function getV2TrackedChangeMutationImpact(event) {
309
309
  if (!payload || typeof payload !== "object") return null;
310
310
  const upsertIds = /* @__PURE__ */ new Set();
311
311
  const removedIds = /* @__PURE__ */ new Set();
312
+ const remappedPairs = [];
312
313
  const collectEntity = (entry, into) => {
313
314
  if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return;
314
315
  if (typeof entry.entityId === "string" && entry.entityId.length > 0) into.add(entry.entityId);
315
316
  };
317
+ const readEntityId$1 = (entry) => {
318
+ if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return null;
319
+ return typeof entry.entityId === "string" && entry.entityId.length > 0 ? entry.entityId : null;
320
+ };
316
321
  if (Array.isArray(payload.inserted)) payload.inserted.forEach((entry) => collectEntity(entry, upsertIds));
317
322
  if (Array.isArray(payload.updated)) payload.updated.forEach((entry) => collectEntity(entry, upsertIds));
318
323
  if (Array.isArray(payload.trackedChangeRefs)) payload.trackedChangeRefs.forEach((entry) => collectEntity(entry, upsertIds));
319
324
  if (Array.isArray(payload.removed)) payload.removed.forEach((entry) => collectEntity(entry, removedIds));
320
325
  if (Array.isArray(payload.invalidatedRefs)) payload.invalidatedRefs.forEach((entry) => collectEntity(entry, removedIds));
321
326
  if (Array.isArray(payload.remappedRefs)) for (const mapping of payload.remappedRefs) {
322
- collectEntity(mapping?.from, removedIds);
323
- collectEntity(mapping?.to, upsertIds);
327
+ const fromId = readEntityId$1(mapping?.from);
328
+ const toId = readEntityId$1(mapping?.to);
329
+ if (fromId) removedIds.add(fromId);
330
+ if (toId) upsertIds.add(toId);
331
+ if (fromId && toId && fromId !== toId) remappedPairs.push({
332
+ from: fromId,
333
+ to: toId
334
+ });
324
335
  }
325
336
  for (const id of removedIds) upsertIds.delete(id);
326
337
  if (upsertIds.size === 0 && removedIds.size === 0) return null;
327
338
  return {
328
339
  upsertIds,
329
340
  removedIds,
341
+ remappedPairs,
330
342
  reconcileMode: event.origin === "history" ? "authoritative" : "targeted"
331
343
  };
332
344
  }
@@ -436,6 +448,7 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
436
448
  if (!impact) return;
437
449
  const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
438
450
  const removedIds = normalizeIds(impact.removedIds);
451
+ const upsertIds = normalizeIds(impact.upsertIds);
439
452
  for (const id of removedIds) {
440
453
  const existing = pendingUpserts.get(id);
441
454
  if (existing?.reconcileMode === "authoritative") pendingUpserts.set(id, {
@@ -447,13 +460,13 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
447
460
  else pendingUpserts.delete(id);
448
461
  }
449
462
  const context = getContext();
450
- if (removedIds.size > 0 && context?.adapter) try {
463
+ if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter) try {
451
464
  Promise.resolve(reconcile(context, {
452
465
  upsertIds: /* @__PURE__ */ new Set(),
453
466
  removedIds
454
467
  })).catch(() => void 0);
455
468
  } catch {}
456
- for (const id of normalizeIds(impact.upsertIds)) if (!removedIds.has(id)) {
469
+ for (const id of upsertIds) if (!removedIds.has(id)) {
457
470
  const existing = pendingUpserts.get(id);
458
471
  pendingUpserts.set(id, {
459
472
  version: ++sequence,
@@ -1799,7 +1812,7 @@ const HEAVY_DOC_READ_POLICY = [
1799
1812
  {
1800
1813
  key: "trackChanges",
1801
1814
  match: "exact",
1802
- note: "audited: body tracked-changes list; stale-served during load"
1815
+ note: "audited: all-story tracked-changes list; stale-served during load"
1803
1816
  },
1804
1817
  {
1805
1818
  key: "trackChanges:all",
@@ -1886,6 +1899,48 @@ function readEntityTarget(row) {
1886
1899
  const target = row.target;
1887
1900
  return target && typeof target === "object" ? target : null;
1888
1901
  }
1902
+ function isHostGeometryTarget(target) {
1903
+ if (!target || typeof target !== "object") return false;
1904
+ const record = target;
1905
+ const isTextPoint = (point) => {
1906
+ if (!point || typeof point !== "object") return false;
1907
+ const candidate = point;
1908
+ return candidate.kind === "text" && typeof candidate.blockId === "string" && typeof candidate.offset === "number";
1909
+ };
1910
+ const isTextSegment = (segment) => {
1911
+ if (!segment || typeof segment !== "object") return false;
1912
+ const candidate = segment;
1913
+ const range$1 = candidate.range;
1914
+ return typeof candidate.blockId === "string" && typeof range$1?.start === "number" && typeof range$1.end === "number";
1915
+ };
1916
+ if (Array.isArray(record.segments)) {
1917
+ const first = record.segments[0];
1918
+ const last = record.segments[record.segments.length - 1];
1919
+ return isTextSegment(first) && isTextSegment(last);
1920
+ }
1921
+ if (record.kind === "selection") return isTextPoint(record.start) && isTextPoint(record.end);
1922
+ if (record.kind !== "text" || typeof record.blockId !== "string") return false;
1923
+ const range = record.range;
1924
+ return typeof range?.start === "number" && typeof range.end === "number";
1925
+ }
1926
+ function readTrackedChangeNavigationTarget(row) {
1927
+ if (!row || typeof row !== "object") return null;
1928
+ const target = readEntityTarget(row);
1929
+ if (isHostGeometryTarget(target)) return target;
1930
+ const record = row;
1931
+ const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
1932
+ if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) return {
1933
+ kind: "text",
1934
+ blockId: navigationTarget.blockId,
1935
+ range: {
1936
+ start: 0,
1937
+ end: 0
1938
+ },
1939
+ ...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
1940
+ ...record.address && typeof record.address === "object" ? { address: record.address } : {}
1941
+ };
1942
+ return target;
1943
+ }
1889
1944
  function storyLocatorSignature(story) {
1890
1945
  if (!story || typeof story !== "object") return "story:body";
1891
1946
  const record = story;
@@ -2092,6 +2147,7 @@ function buildTrackedChangeIdContext(items) {
2092
2147
  return context;
2093
2148
  }
2094
2149
  var storyScopedTrackedChangeIdContextCache = /* @__PURE__ */ new WeakMap();
2150
+ var declaredBodyItemsCache = /* @__PURE__ */ new WeakMap();
2095
2151
  function buildStoryScopedTrackedChangeIdContext(items, story) {
2096
2152
  const signature = storyLocatorSignature(story);
2097
2153
  let byStory = storyScopedTrackedChangeIdContextCache.get(items);
@@ -2105,6 +2161,56 @@ function buildStoryScopedTrackedChangeIdContext(items, story) {
2105
2161
  byStory.set(signature, context);
2106
2162
  return context;
2107
2163
  }
2164
+ function isTextboxStory(story) {
2165
+ return !!story && typeof story === "object" && story.storyType === "textbox";
2166
+ }
2167
+ function resolveStorylessCanonicalTrackedChangeId(items, selectionIds) {
2168
+ const storyless = selectionIds.filter((id) => items.some((item) => readEntityId(item) === id && readEntityStory(item) == null));
2169
+ if (storyless.length === 0) return null;
2170
+ if (storyless.length === 1) return storyless[0];
2171
+ const seen = /* @__PURE__ */ new Set();
2172
+ for (const id of storyless) {
2173
+ const row = items.find((item) => readEntityId(item) === id);
2174
+ if (!row) continue;
2175
+ for (const raw of readTrackChangeProvenanceIds(row)) {
2176
+ if (seen.has(raw)) return null;
2177
+ seen.add(raw);
2178
+ }
2179
+ }
2180
+ return storyless[0];
2181
+ }
2182
+ function declaredBodyItems(items) {
2183
+ const cached = declaredBodyItemsCache.get(items);
2184
+ if (cached) return cached;
2185
+ const declared = items.filter((item) => readEntityStory(item) != null);
2186
+ const result = declared.length === items.length ? items : declared;
2187
+ declaredBodyItemsCache.set(items, result);
2188
+ return result;
2189
+ }
2190
+ function resolveSelectionActiveChangeId(options) {
2191
+ const { selectionIds, publicIds, selection, bodyItems, allStoryItems } = options;
2192
+ if (selectionIds.length === 0) return null;
2193
+ const mapIds = (context) => {
2194
+ for (const id of selectionIds) {
2195
+ const publicId = context.toPublicId(id);
2196
+ if (publicId) return publicId;
2197
+ }
2198
+ return null;
2199
+ };
2200
+ const story = selectionStory(selection);
2201
+ const isBodySelection = storyLocatorSignature(story) === storyLocatorSignature(null);
2202
+ if (allStoryItems) {
2203
+ const scopedItems = isBodySelection ? declaredBodyItems(allStoryItems) : allStoryItems;
2204
+ const fromStory = mapIds(buildStoryScopedTrackedChangeIdContext(scopedItems, story));
2205
+ if (fromStory) return fromStory;
2206
+ const fromPublic = publicIds?.find((id) => scopedItems.some((item) => entityRowMatchesRequest(item, id, story)));
2207
+ if (fromPublic) return fromPublic;
2208
+ if (!isBodySelection) return isTextboxStory(story) ? resolveStorylessCanonicalTrackedChangeId(allStoryItems, selectionIds) : null;
2209
+ return mapIds(buildTrackedChangeIdContext(bodyItems));
2210
+ }
2211
+ if (isBodySelection) return mapIds(buildTrackedChangeIdContext(bodyItems)) ?? selectionIds[0] ?? null;
2212
+ return null;
2213
+ }
2108
2214
  function resolveActiveCommentId(items, commentId) {
2109
2215
  const item = items.find((candidate) => readEntityId(candidate) === commentId || candidate.importedId === commentId);
2110
2216
  if (!item) return null;
@@ -3082,11 +3188,14 @@ function createSuperDocUI(options) {
3082
3188
  let explicitActiveChange = null;
3083
3189
  let explicitActiveChangeRevision = 0;
3084
3190
  let queuedTrackChangeNavigationInvalidation = 0;
3191
+ let trackChangeRevealInvalidation = 0;
3192
+ const pendingTrackChangeRevealFocuses = /* @__PURE__ */ new Set();
3085
3193
  const explicitActiveChangesEqual = (left, right) => {
3086
3194
  if (left === right) return true;
3087
3195
  if (!left || !right) return false;
3088
3196
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
3089
3197
  };
3198
+ const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
3090
3199
  const mirrorTrackedChangeFocusToHost = (next) => {
3091
3200
  const host = getHost();
3092
3201
  if (typeof host?.getHandles !== "function") return;
@@ -3115,6 +3224,7 @@ function createSuperDocUI(options) {
3115
3224
  const setExplicitActiveChange = (next, options$1) => {
3116
3225
  explicitActiveChange = next;
3117
3226
  explicitActiveChangeRevision += 1;
3227
+ trackChangeRevealInvalidation += 1;
3118
3228
  if (options$1?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
3119
3229
  };
3120
3230
  const listeners = /* @__PURE__ */ new Set();
@@ -3520,7 +3630,7 @@ function createSuperDocUI(options) {
3520
3630
  const read = host?.getDocumentLoadingSnapshot;
3521
3631
  if (typeof read !== "function") return "unknown";
3522
3632
  const stage = safeCall(() => read.call(host), null)?.sourceStage;
3523
- if (stage === "opening" || stage === "ready-window" || stage === "source-loading") return "loading";
3633
+ if (stage === "opening" || stage === "bootstrap-ready" || stage === "source-loading") return "loading";
3524
3634
  if (stage === "source-complete" || stage === "source-failed" || stage === "source-cancelled") return "complete";
3525
3635
  return "unknown";
3526
3636
  };
@@ -4160,8 +4270,8 @@ function createSuperDocUI(options) {
4160
4270
  status: heldEntry?.hasSettled ? "stale" : "pending"
4161
4271
  } : readAsync("trackChanges", token, () => {
4162
4272
  if (listTrackedChanges) return listTrackedChanges.call(v2TrackedChanges);
4163
- return tcApi?.list ? tcApi.list() : void 0;
4164
- }, (raw) => raw && Array.isArray(raw.items) ? raw.items.filter((item) => !listTrackedChanges || storyLocatorSignature(readEntityStory(item)) === "story:body") : []);
4273
+ return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
4274
+ }, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
4165
4275
  const items = (value ?? []).map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
4166
4276
  const id = readEntityId(item);
4167
4277
  return !id || !postDecisionIds?.has(id);
@@ -4180,8 +4290,20 @@ function createSuperDocUI(options) {
4180
4290
  if (allStoryItems && !allStoryItems.some((row) => entityRowMatchesRequest(row, active.id, active.story))) setExplicitActiveChange(null);
4181
4291
  } else if (!items.some((item) => readEntityId(item) === active.id)) setExplicitActiveChange(null);
4182
4292
  }
4293
+ const publicIdItems = allStoryItems ?? items;
4294
+ const selectionIdContext = buildStoryScopedTrackedChangeIdContext(publicIdItems, selectionStory(selection$1));
4183
4295
  const selectionActiveChangeIds = postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
4184
- const activeId = explicitActiveChange?.id ?? selectionActiveChangeIds[0] ?? null;
4296
+ const selectionPublicChangeIds = selectionActiveChangeIds.map((id) => selectionIdContext.toPublicId(id) ?? id);
4297
+ const explicitActiveIdContext = explicitActiveChange?.story ? buildStoryScopedTrackedChangeIdContext(publicIdItems, explicitActiveChange.story) : buildTrackedChangeIdContext(publicIdItems);
4298
+ const explicitActiveId = explicitActiveChange ? explicitActiveIdContext.toPublicId(explicitActiveChange.id) ?? explicitActiveChange.id : null;
4299
+ const selectionActiveId = resolveSelectionActiveChangeId({
4300
+ selectionIds: selectionActiveChangeIds,
4301
+ publicIds: selectionPublicChangeIds,
4302
+ selection: selection$1,
4303
+ bodyItems: items,
4304
+ allStoryItems
4305
+ });
4306
+ const activeId = explicitActiveId ?? selectionActiveId;
4185
4307
  return {
4186
4308
  status: combineStatus(listStatus, selection$1.status),
4187
4309
  items,
@@ -5845,13 +5967,18 @@ function createSuperDocUI(options) {
5845
5967
  const target = rawTarget && typeof rawTarget === "object" ? rawTarget : null;
5846
5968
  let next = null;
5847
5969
  if (target?.entityType === "trackedChange" && typeof target.entityId === "string" && target.entityId.length > 0) {
5848
- const story = target.story && storyLocatorSignature(target.story) !== storyLocatorSignature(null) ? target.story : void 0;
5970
+ const paintedEntityId = target.entityId;
5971
+ const targetStory = target.story && typeof target.story === "object" ? target.story : void 0;
5972
+ const allStoryItems = readAllStoryTrackChanges();
5973
+ const entityId = (allStoryItems ? targetStory ? buildStoryScopedTrackedChangeIdContext(allStoryItems, targetStory).toPublicId(paintedEntityId) : buildTrackedChangeIdContext(allStoryItems).toPublicId(paintedEntityId) : null) ?? paintedEntityId;
5974
+ const story = targetStory && storyLocatorSignature(targetStory) !== storyLocatorSignature(null) ? targetStory : void 0;
5849
5975
  next = {
5850
- id: target.entityId,
5976
+ id: entityId,
5851
5977
  ...story ? { story } : {},
5852
- ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : {}
5978
+ ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : entityId !== paintedEntityId ? { paintedEntityId } : {}
5853
5979
  };
5854
5980
  }
5981
+ if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
5855
5982
  if (explicitActiveChangesEqual(explicitActiveChange, next)) return false;
5856
5983
  setExplicitActiveChange(next);
5857
5984
  return true;
@@ -6089,6 +6216,17 @@ function createSuperDocUI(options) {
6089
6216
  function readTrackDecisionTargetId(target) {
6090
6217
  return target && typeof target === "object" && typeof target.id === "string" ? String(target.id) : null;
6091
6218
  }
6219
+ function nonBodySelectionStoryLocator(selection$1) {
6220
+ const story = selectionStoryLocator(selection$1);
6221
+ return story && storyLocatorSignature(story) !== storyLocatorSignature(null) ? story : void 0;
6222
+ }
6223
+ function trackDecisionIdTarget(changeId, story) {
6224
+ return {
6225
+ kind: "id",
6226
+ id: changeId,
6227
+ ...story ? { story } : {}
6228
+ };
6229
+ }
6092
6230
  function trackDecisionRangeFromSelection(selection$1) {
6093
6231
  if (selection$1.empty) return null;
6094
6232
  const explicit = selection$1.selectionTarget;
@@ -6114,19 +6252,55 @@ function createSuperDocUI(options) {
6114
6252
  }
6115
6253
  };
6116
6254
  }
6255
+ function readBodyTrackChangeForDecision(activeId, story) {
6256
+ if (storyLocatorSignature(story) !== storyLocatorSignature(null)) return void 0;
6257
+ const trackChanges$1 = getDoc()?.trackChanges;
6258
+ const list = trackChanges$1?.list;
6259
+ if (typeof list !== "function") return void 0;
6260
+ const raw = safeCall(() => list.call(trackChanges$1), null);
6261
+ if (isPromiseLike(raw) || !raw || typeof raw !== "object") return void 0;
6262
+ const items = raw.items;
6263
+ if (!Array.isArray(items)) return void 0;
6264
+ return items.map(projectTrackChangesItem).find((item) => item != null && entityRowMatchesRequest(item, activeId, story));
6265
+ }
6266
+ function selectedTextCoversTrackChangeText(selection$1, item) {
6267
+ const selected = normalizeTrackDecisionText(selection$1.quotedText);
6268
+ if (!selected) return false;
6269
+ const payload = trackChangesItemPayload(item);
6270
+ return [
6271
+ payload.insertedText,
6272
+ payload.deletedText,
6273
+ payload.excerpt
6274
+ ].some((value) => normalizeTrackDecisionText(value) === selected);
6275
+ }
6276
+ function normalizeTrackDecisionText(value) {
6277
+ return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
6278
+ }
6117
6279
  function activeChangePartialDecisionRoute() {
6118
6280
  const activeId = state.selection.activeChangeIds[0];
6119
6281
  if (!activeId) return "unavailable";
6120
- const items = readAllStoryTrackChanges();
6121
- if (!items) return "unavailable";
6122
6282
  const story = selectionStoryLocator(state.selection);
6123
- const item = items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
6283
+ 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));
6124
6284
  if (!item) return "unavailable";
6125
- const type = trackChangesItemPayload(item).type;
6126
- const grouping = trackChangesItemPayload(item).grouping;
6285
+ const change = trackChangesItemPayload(item);
6286
+ const type = change.type;
6287
+ const grouping = change.grouping;
6288
+ if (selectedTextCoversTrackChangeText(state.selection, item)) return "id";
6127
6289
  if (type === "replacement" && grouping === "replacement-pair") return "range";
6128
6290
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
6129
6291
  }
6292
+ function executeTrackDecisionRangeWithIdFallback(kind, rangeTarget, changeId, story) {
6293
+ const fallback = () => callTrackDecisionTarget(kind, trackDecisionIdTarget(changeId, story), changeId, story);
6294
+ try {
6295
+ const result = callTrackDecisionTarget(kind, rangeTarget, null);
6296
+ if (isPromiseLike(result)) return settleCommandExecution(Promise.resolve(result).then((settled) => {
6297
+ return commandResultSucceeded(commandResultFromOperationResult(settled)) ? settled : fallback();
6298
+ }));
6299
+ return settleCommandExecution(commandResultSucceeded(commandResultFromOperationResult(result)) ? result : fallback());
6300
+ } catch {
6301
+ return false;
6302
+ }
6303
+ }
6130
6304
  function resolveTrackDecisionTarget(command, payload) {
6131
6305
  if (command.scope === "all") return {
6132
6306
  target: { scope: "all" },
@@ -6141,26 +6315,27 @@ function createSuperDocUI(options) {
6141
6315
  };
6142
6316
  if (payload && typeof payload === "object") {
6143
6317
  const record = payload;
6144
- if (record.target && typeof record.target === "object") return {
6145
- target: record.target,
6146
- changeId: readTrackDecisionTargetId(record.target)
6147
- };
6318
+ if (record.target && typeof record.target === "object") {
6319
+ const target = record.target;
6320
+ return {
6321
+ target,
6322
+ changeId: readTrackDecisionTargetId(target),
6323
+ story: target.story
6324
+ };
6325
+ }
6148
6326
  const id = typeof record.changeId === "string" ? record.changeId : record.id;
6149
6327
  if (typeof id === "string" && id.length > 0) return {
6150
- target: {
6151
- kind: "id",
6152
- id
6153
- },
6154
- changeId: id
6328
+ target: trackDecisionIdTarget(id, record.story),
6329
+ changeId: id,
6330
+ story: record.story
6155
6331
  };
6156
6332
  }
6157
6333
  const selectedId = state.selection.activeChangeIds[0];
6334
+ const story = nonBodySelectionStoryLocator(state.selection);
6158
6335
  return selectedId ? {
6159
- target: {
6160
- kind: "id",
6161
- id: selectedId
6162
- },
6163
- changeId: selectedId
6336
+ target: trackDecisionIdTarget(selectedId, story),
6337
+ changeId: selectedId,
6338
+ story
6164
6339
  } : null;
6165
6340
  }
6166
6341
  function executeTrackDecisionCommand(command, payload) {
@@ -6174,13 +6349,23 @@ function createSuperDocUI(options) {
6174
6349
  if (route === "unavailable") return false;
6175
6350
  if (route === "range") {
6176
6351
  const target = trackDecisionRangeFromSelection(state.selection);
6177
- return target ? executeTrackDecisionTarget(command.kind, target, null) : false;
6352
+ const story = nonBodySelectionStoryLocator(state.selection);
6353
+ return target ? executeTrackDecisionRangeWithIdFallback(command.kind, target, state.selection.activeChangeIds[0], story) : false;
6354
+ }
6355
+ if (route === "id") {
6356
+ const activeId = state.selection.activeChangeIds[0];
6357
+ const selectedStory = selectionStoryLocator(state.selection);
6358
+ const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
6359
+ return executeTrackDecision(command.kind, story ? {
6360
+ id: activeId,
6361
+ story
6362
+ } : activeId);
6178
6363
  }
6179
6364
  }
6180
6365
  }
6181
6366
  const resolved = resolveTrackDecisionTarget(command, payload);
6182
6367
  if (!resolved) return false;
6183
- return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId);
6368
+ return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId, resolved.story);
6184
6369
  }
6185
6370
  function executeListToggleCommand(kind, payload) {
6186
6371
  const lists = getEditCommands()?.lists;
@@ -7700,11 +7885,12 @@ function createSuperDocUI(options) {
7700
7885
  reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
7701
7886
  };
7702
7887
  try {
7703
- const result = await Promise.resolve(scroll.call(host, {
7888
+ const input = {
7704
7889
  target,
7705
7890
  block: options$1?.block ?? "center",
7706
7891
  behavior: options$1?.behavior ?? "smooth"
7707
- }));
7892
+ };
7893
+ const result = await Promise.resolve(options$1?.shouldContinue ? scroll.call(host, input, options$1.shouldContinue) : scroll.call(host, input));
7708
7894
  if (result && typeof result === "object" && result.success === false) return {
7709
7895
  success: false,
7710
7896
  ok: false,
@@ -7734,19 +7920,32 @@ function createSuperDocUI(options) {
7734
7920
  story: requestedStory
7735
7921
  };
7736
7922
  };
7737
- const fromList = readEntityTarget(loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory)));
7738
- if (fromList) return withRequestedStory(fromList);
7923
+ const readTarget = namespace === "trackChanges" ? readTrackedChangeNavigationTarget : readEntityTarget;
7924
+ const loadedRow = loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory));
7925
+ const loadedRecord = loadedRow && typeof loadedRow === "object" ? loadedRow : null;
7926
+ const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
7927
+ const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
7928
+ const readResolvedTarget = (row) => {
7929
+ if (!moveSide) return readTarget(row);
7930
+ const moveTarget = readEntityTarget(row);
7931
+ if (moveTarget?.kind !== "move") return null;
7932
+ const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
7933
+ return isHostGeometryTarget(sideTarget) ? sideTarget : null;
7934
+ };
7935
+ const fromList = readTarget(loadedRow);
7936
+ const listTargetIsAuthoritative = namespace !== "trackChanges" || !moveSide && isHostGeometryTarget(readEntityTarget(loadedRow));
7937
+ if (fromList && listTargetIsAuthoritative) return withRequestedStory(fromList);
7739
7938
  const api = getDoc()?.[namespace];
7740
7939
  const input = namespace === "comments" ? { commentId: id } : {
7741
- id,
7940
+ id: trackedChangeLookupId,
7742
7941
  ...requestedStory ? { story: requestedStory } : {}
7743
7942
  };
7744
7943
  const get = api?.get;
7745
- if (typeof get !== "function") return null;
7944
+ if (typeof get !== "function") return withRequestedStory(fromList);
7746
7945
  try {
7747
- return withRequestedStory(readEntityTarget(await Promise.resolve(get.call(api, input))));
7946
+ return withRequestedStory(readResolvedTarget(await Promise.resolve(get.call(api, input))) ?? fromList);
7748
7947
  } catch {
7749
- return null;
7948
+ return withRequestedStory(fromList);
7750
7949
  }
7751
7950
  };
7752
7951
  function patchCommentStatus(commentId, status) {
@@ -7880,10 +8079,12 @@ function createSuperDocUI(options) {
7880
8079
  };
7881
8080
  const trackChangesSub = sliceHandle((s) => s.trackChanges);
7882
8081
  const navigateTrackChange = (step, options$1) => {
7883
- const rows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
7884
- if (rows.length === 0) return null;
8082
+ const allRows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
8083
+ if (allRows.length === 0) return null;
7885
8084
  const currentId = state.trackChanges.activeId;
7886
8085
  const currentStory = explicitActiveChange?.id === currentId ? explicitActiveChange.story : void 0;
8086
+ const bodyRows = allRows.filter((row) => readEntityRequestStory(row) == null);
8087
+ const rows = currentStory || bodyRows.length === 0 ? allRows : bodyRows;
7887
8088
  let currentIdx = currentId ? rows.findIndex((row) => entityRowMatchesRequest(row, currentId, currentStory)) : -1;
7888
8089
  if (currentIdx === -1 && currentId && currentStory) currentIdx = rows.findIndex((row) => entityRowMatchesRequest(row, currentId));
7889
8090
  const nextRow = rows[currentIdx === -1 ? step > 0 ? 0 : rows.length - 1 : (currentIdx + step + rows.length) % rows.length];
@@ -7908,33 +8109,41 @@ function createSuperDocUI(options) {
7908
8109
  if (activeId == null) return { success: false };
7909
8110
  const optimisticActiveChange = explicitActiveChange;
7910
8111
  const optimisticRevision = explicitActiveChangeRevision;
7911
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8112
+ const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7912
8113
  const rollback = () => {
7913
8114
  if (!isCurrent()) return;
7914
8115
  setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7915
8116
  recompute();
7916
8117
  };
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;
8118
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
7924
8119
  try {
7925
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
8120
+ const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7926
8121
  if (!isCurrent()) return { success: false };
7927
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8122
+ if (!target) {
7928
8123
  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));
8124
+ return { success: false };
7937
8125
  }
8126
+ trackedChangeNavigationInFlight += 1;
8127
+ try {
8128
+ const result = await scrollTargetIntoView(target, {
8129
+ behavior: "auto",
8130
+ shouldContinue: isCurrent
8131
+ });
8132
+ if (!isCurrent()) return { success: false };
8133
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8134
+ rollback();
8135
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8136
+ } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
8137
+ return { success: result.ok };
8138
+ } finally {
8139
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8140
+ if (trackedChangeNavigationInFlight === 0) {
8141
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8142
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8143
+ }
8144
+ }
8145
+ } finally {
8146
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.delete(optimisticActiveChange);
7938
8147
  }
7939
8148
  };
7940
8149
  const navigateAndScroll = (step) => {
@@ -7976,7 +8185,7 @@ function createSuperDocUI(options) {
7976
8185
  if (!input || typeof input !== "object" || typeof input.x !== "number" || typeof input.y !== "number") return null;
7977
8186
  const hits = entityAt(input);
7978
8187
  if (hits.length === 0) return null;
7979
- const bodyItems = trackChangesSub.get().items;
8188
+ const items = trackChangesSub.get().items;
7980
8189
  let allStoryItems;
7981
8190
  for (const hit of hits) {
7982
8191
  if (hit.type !== "trackedChange") continue;
@@ -7985,7 +8194,7 @@ function createSuperDocUI(options) {
7985
8194
  if (allStoryItems === void 0) allStoryItems = readAllStoryTrackChanges();
7986
8195
  if (!allStoryItems) continue;
7987
8196
  candidates = allStoryItems;
7988
- } else candidates = bodyItems;
8197
+ } else candidates = items;
7989
8198
  const item = candidates.find((candidate) => entityRowMatchesRequest(candidate, hit.id, hit.story));
7990
8199
  if (item) return hit.story ? {
7991
8200
  id: item.id,
@@ -8019,10 +8228,12 @@ function createSuperDocUI(options) {
8019
8228
  if (!allStoryItems.some((row) => entityRowMatchesRequest(row, publicId, requested.story))) return false;
8020
8229
  requested.id = publicId;
8021
8230
  } else {
8022
- const bodyItems = trackChangesSub.get().items;
8023
- const publicId = buildTrackedChangeIdContext(bodyItems).toPublicId(requested.id) ?? requested.id;
8024
- if (!bodyItems.some((item) => readEntityId(item) === publicId)) return false;
8231
+ const items = trackChangesSub.get().items;
8232
+ const publicId = buildTrackedChangeIdContext(items).toPublicId(requested.id) ?? requested.id;
8233
+ const item = items.find((candidate) => readEntityId(candidate) === publicId);
8234
+ if (!item) return false;
8025
8235
  requested.id = publicId;
8236
+ requested.story = readEntityRequestStory(item);
8026
8237
  }
8027
8238
  if (paintedEntityId !== requested.id) requested.paintedEntityId = paintedEntityId;
8028
8239
  setExplicitActiveChange(requested);
@@ -8036,47 +8247,65 @@ function createSuperDocUI(options) {
8036
8247
  ok: false,
8037
8248
  reason: getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady
8038
8249
  };
8250
+ queuedTrackChangeNavigationInvalidation += 1;
8251
+ trackChangeRevealInvalidation += 1;
8252
+ const requestedAtInvalidation = trackChangeRevealInvalidation;
8039
8253
  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
- };
8254
+ const publicId = buildTrackedChangeIdContext(loadedItems).toPublicId(changeId) ?? changeId;
8255
+ const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === publicId));
8047
8256
  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();
8257
+ const requestedActiveChange = {
8258
+ id: publicId,
8259
+ ...story ? { story } : {},
8260
+ ...publicId !== changeId ? { paintedEntityId: changeId } : {}
8061
8261
  };
8062
- trackedChangeNavigationInFlight += 1;
8262
+ pendingTrackChangeRevealFocuses.add(requestedActiveChange);
8063
8263
  try {
8064
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
8065
- if (!isCurrent()) return {
8264
+ const target = await resolveEntityTarget("trackChanges", publicId, loadedItems, story ? { story } : void 0);
8265
+ if (trackChangeRevealInvalidation !== requestedAtInvalidation) return {
8066
8266
  success: false,
8067
8267
  ok: false
8068
8268
  };
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));
8269
+ if (!target) return {
8270
+ success: false,
8271
+ ok: false,
8272
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
8273
+ };
8274
+ setExplicitActiveChange(requestedActiveChange);
8275
+ const revealInvalidation = trackChangeRevealInvalidation;
8276
+ const optimisticActiveChange = explicitActiveChange;
8277
+ const optimisticRevision = explicitActiveChangeRevision;
8278
+ const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8279
+ const rollback = () => {
8280
+ if (!isCurrent()) return;
8281
+ setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
8282
+ recompute();
8283
+ };
8284
+ recompute();
8285
+ trackedChangeNavigationInFlight += 1;
8286
+ try {
8287
+ const result = await scrollTargetIntoView(target, {
8288
+ behavior: "auto",
8289
+ shouldContinue: isCurrent
8290
+ });
8291
+ if (!isCurrent()) return {
8292
+ success: false,
8293
+ ok: false
8294
+ };
8295
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8296
+ rollback();
8297
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8298
+ } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
8299
+ return result;
8300
+ } finally {
8301
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8302
+ if (trackedChangeNavigationInFlight === 0) {
8303
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8304
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8305
+ }
8079
8306
  }
8307
+ } finally {
8308
+ pendingTrackChangeRevealFocuses.delete(requestedActiveChange);
8080
8309
  }
8081
8310
  }
8082
8311
  };
@@ -8331,8 +8560,8 @@ function createSuperDocUI(options) {
8331
8560
  const rawHits = collectEntityHitsFromChain(startEls[0], container);
8332
8561
  for (let i = 1; i < startEls.length; i += 1) for (const hit of collectEntityHitsFromChain(startEls[i], container)) rawHits.push(hit);
8333
8562
  if (rawHits.length === 0) return rawHits;
8334
- const bodyItems = trackChangesSub.get().items;
8335
- const bodyIdContext = buildTrackedChangeIdContext(bodyItems);
8563
+ const items = trackChangesSub.get().items;
8564
+ const idContext = buildTrackedChangeIdContext(items);
8336
8565
  let allStoryItems;
8337
8566
  const seen = /* @__PURE__ */ new Set();
8338
8567
  const hits = [];
@@ -8352,7 +8581,7 @@ function createSuperDocUI(options) {
8352
8581
  publicId = buildStoryScopedTrackedChangeIdContext(allStoryItems, story).toPublicId(hit.id);
8353
8582
  if (!publicId || !allStoryItems.some((item) => entityRowMatchesRequest(item, publicId, story))) continue;
8354
8583
  } else {
8355
- publicId = bodyIdContext.toPublicId(hit.id);
8584
+ publicId = idContext.toPublicId(hit.id);
8356
8585
  if (!publicId) continue;
8357
8586
  }
8358
8587
  const key = `trackedChange:${publicId}`;