superdoc 2.3.0 → 2.4.0-next.10

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 (63) hide show
  1. package/README.md +0 -1
  2. package/dist/chunks/{FindReplaceSurface-DLxnCQOB.cjs → FindReplaceSurface-9VkPw_-X.cjs} +3 -4
  3. package/dist/chunks/{FindReplaceSurface-BEW-1NhM.es.js → FindReplaceSurface-BuvaIrQ5.es.js} +2 -3
  4. package/dist/chunks/{PasswordPromptSurface-DMbfDvpQ.cjs → PasswordPromptSurface-Bu5xHm91.cjs} +2 -2
  5. package/dist/chunks/{PasswordPromptSurface-BA6HHqEa.es.js → PasswordPromptSurface-Bz9_sKhX.es.js} +1 -1
  6. package/dist/chunks/{PdfViewer-CNOtyS-I.es.js → PdfViewer-CdVGB6Fe.es.js} +2 -2
  7. package/dist/chunks/{PdfViewer-CoFiGAhm.cjs → PdfViewer-D4eD2dV1.cjs} +3 -3
  8. package/dist/chunks/blank-docx-DP8RUPW-.cjs +7 -0
  9. package/dist/chunks/blank-docx-XRX6Ker2.es.js +2 -0
  10. package/dist/chunks/{create-super-doc-ui-CXCqHc2m.cjs → create-super-doc-ui-ByvmsAAG.cjs} +672 -190
  11. package/dist/chunks/{create-super-doc-ui-fxF2Q8Ib.es.js → create-super-doc-ui-DBXOhHdW.es.js} +667 -191
  12. package/dist/chunks/{eventemitter3-B60Fsz-a.es.js → eventemitter3-Bt2s0X0a.es.js} +1 -1
  13. package/dist/chunks/{eventemitter3-DCfwFVNn.cjs → eventemitter3-DqY4aSMf.cjs} +1 -1
  14. package/dist/chunks/{jszip-DzmwAHr3.es.js → jszip-C8srOKAO.es.js} +2 -2
  15. package/dist/chunks/{jszip-BdUBlUeG.cjs → jszip-Cs9JBLlJ.cjs} +13 -1
  16. package/dist/chunks/{rolldown-runtime-_dR15c8t.cjs → rolldown-runtime-1Y-nnZJ3.cjs} +0 -28
  17. package/dist/chunks/{rolldown-runtime-BJVcqBbz.es.js → rolldown-runtime-D7PMmH3s.es.js} +1 -17
  18. package/dist/collaboration-upgrade-engine.cjs +1 -1
  19. package/dist/collaboration-upgrade-engine.es.js +1 -1
  20. package/dist/document-api/src/comments/comment-create-attribution.d.ts +2 -0
  21. package/dist/document-api/src/comments/comments.d.ts +13 -4
  22. package/dist/document-api/src/comments/comments.types.d.ts +39 -0
  23. package/dist/document-api/src/content-controls/content-controls.types.d.ts +2 -0
  24. package/dist/document-api/src/contract/metadata-types.d.ts +18 -0
  25. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  26. package/dist/document-api/src/index.d.ts +2 -2
  27. package/dist/document-api/src/types/track-changes.types.d.ts +1 -3
  28. package/dist/layout-engine/painters/dom/src/_test-utils.d.ts +3 -2
  29. package/dist/layout-engine/painters/dom/src/index.d.ts +24 -19
  30. package/dist/layout-engine/painters/dom/src/page-content.d.ts +107 -39
  31. package/dist/layout-engine/painters/dom/src/persistent-page-surface.d.ts +144 -0
  32. package/dist/layout-engine/painters/dom/src/pm-position-validation.d.ts +1 -1
  33. package/dist/layout-engine/painters/dom/src/renderer.d.ts +50 -106
  34. package/dist/layout-engine/painters/dom/src/sdt/boundaries.d.ts +1 -1
  35. package/dist/public/ui-react.cjs +2 -2
  36. package/dist/public/ui-react.es.js +1 -1
  37. package/dist/public/ui.cjs +1 -1
  38. package/dist/public/ui.es.js +1 -1
  39. package/dist/style.css +138 -131
  40. package/dist/style.layered.css +138 -131
  41. package/dist/superdoc/src/core/types/index.d.ts +383 -16
  42. package/dist/superdoc/src/helpers/floor.d.ts +1 -1
  43. package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +5 -0
  44. package/dist/superdoc/src/helpers/v2-review-mutation-reconciler.d.ts +1 -1
  45. package/dist/superdoc/src/internal/toolbar/built-in/constants.d.ts +0 -1
  46. package/dist/superdoc/src/internal/toolbar/compatibility-catalog.d.ts +1 -1
  47. package/dist/superdoc/src/public/index.d.cts +14 -0
  48. package/dist/superdoc/src/public/index.d.ts +7 -0
  49. package/dist/superdoc/src/public/ui/types.d.ts +25 -1
  50. package/dist/superdoc/src/stores/comments-store.d.ts +124 -22
  51. package/dist/superdoc/src/stores/superdoc-store.d.ts +372 -66
  52. package/dist/superdoc.cjs +5160 -668
  53. package/dist/superdoc.es.js +5163 -675
  54. package/dist-cdn/style.layered.css +1 -1
  55. package/dist-cdn/superdoc.min.css +1 -1
  56. package/dist-cdn/superdoc.min.js +37 -37
  57. package/package.json +5 -4
  58. package/dist/chunks/blank-docx-CJAXnJKp.es.js +0 -2
  59. package/dist/chunks/blank-docx-DA-G-3VB.cjs +0 -7
  60. /package/dist/chunks/{_plugin-vue_export-helper-BTwbGDKw.cjs → _plugin-vue_export-helper-CZ1Nl59K.cjs} +0 -0
  61. /package/dist/chunks/{_plugin-vue_export-helper-CInC0bKI.es.js → _plugin-vue_export-helper-DSMAhhwD.es.js} +0 -0
  62. /package/dist/chunks/{constants-CY3R3_kF.es.js → constants-BfH0br5u.es.js} +0 -0
  63. /package/dist/chunks/{constants-sbCZ2O_A.cjs → constants-D7TNtyJd.cjs} +0 -0
@@ -1,3 +1,64 @@
1
+ const DOM_CLASS_NAMES = {
2
+ PAGE: "superdoc-page",
3
+ FRAGMENT: "superdoc-fragment",
4
+ LINE: "superdoc-line",
5
+ INLINE_SDT_WRAPPER: "superdoc-structured-content-inline",
6
+ INLINE_SDT_LABEL: "superdoc-structured-content-inline__label",
7
+ BLOCK_SDT: "superdoc-structured-content-block",
8
+ BLOCK_SDT_LABEL: "superdoc-structured-content__label",
9
+ TABLE_FRAGMENT: "superdoc-table-fragment",
10
+ DOCUMENT_SECTION: "superdoc-document-section",
11
+ SDT_GROUP_HOVER: "sdt-group-hover",
12
+ TOC_ENTRY: "superdoc-toc-entry",
13
+ TOC_GROUP_HOVER: "toc-group-hover",
14
+ IMAGE_FRAGMENT: "superdoc-image-fragment",
15
+ INLINE_IMAGE: "superdoc-inline-image",
16
+ LIST_MARKER: "superdoc-list-marker",
17
+ INLINE_IMAGE_CLIP_WRAPPER: "superdoc-inline-image-clip-wrapper",
18
+ ANNOTATION: "annotation",
19
+ ANNOTATION_CONTENT: "annotation-content",
20
+ ANNOTATION_CARET_ANCHOR: "annotation-caret-anchor"
21
+ };
22
+ DOM_CLASS_NAMES.INLINE_SDT_LABEL, DOM_CLASS_NAMES.BLOCK_SDT_LABEL;
23
+ const DATA_ATTRS = {
24
+ PM_START: "data-pm-start",
25
+ PM_END: "data-pm-end",
26
+ LAYOUT_EPOCH: "data-layout-epoch",
27
+ TABLE_BOUNDARIES: "data-table-boundaries",
28
+ SDT_ID: "data-sdt-id",
29
+ SDT_TYPE: "data-sdt-type",
30
+ FIELD_ID: "data-field-id",
31
+ FIELD_TYPE: "data-field-type",
32
+ DRAGGABLE: "data-draggable",
33
+ DISPLAY_LABEL: "data-display-label",
34
+ VARIANT: "data-variant",
35
+ TYPE: "data-type",
36
+ LAYOUT_BOUNDARY_SCHEMA: "data-layout-boundary-schema",
37
+ LAYOUT_FRAGMENT_ID: "data-layout-fragment-id",
38
+ LAYOUT_STORY: "data-layout-story",
39
+ LAYOUT_BLOCK_REF: "data-layout-block-ref"
40
+ };
41
+ const decodeLayoutStoryDataset = (raw) => {
42
+ if (!raw) return { kind: "unknown" };
43
+ if (raw === "body") return { kind: "body" };
44
+ const idx = raw.indexOf(":");
45
+ const kind = idx === -1 ? raw : raw.slice(0, idx);
46
+ const id = idx === -1 ? void 0 : raw.slice(idx + 1);
47
+ switch (kind) {
48
+ case "body":
49
+ case "header":
50
+ case "footer":
51
+ case "footnote":
52
+ case "endnote":
53
+ case "textbox": return id ? {
54
+ kind,
55
+ id
56
+ } : { kind };
57
+ default: return { kind: "unknown" };
58
+ }
59
+ };
60
+ `${DOM_CLASS_NAMES.BLOCK_SDT}${DATA_ATTRS.SDT_ID}`;
61
+ `${DATA_ATTRS.DRAGGABLE}`;
1
62
  function getParagraphInlineDirection(attrs) {
2
63
  const fromContext = attrs?.directionContext?.inlineDirection;
3
64
  if (fromContext != null) return fromContext;
@@ -90,27 +151,46 @@ function getV2TrackedChangeMutationImpact(event) {
90
151
  if (!payload || typeof payload !== "object") return null;
91
152
  const upsertIds = /* @__PURE__ */ new Set();
92
153
  const removedIds = /* @__PURE__ */ new Set();
154
+ const remappedPairs = [];
93
155
  const collectEntity = (entry, into) => {
94
156
  if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return;
95
157
  if (typeof entry.entityId === "string" && entry.entityId.length > 0) into.add(entry.entityId);
96
158
  };
159
+ const readEntityId$1 = (entry) => {
160
+ if (entry?.kind !== "entity" || entry.entityType !== "trackedChange") return null;
161
+ return typeof entry.entityId === "string" && entry.entityId.length > 0 ? entry.entityId : null;
162
+ };
97
163
  if (Array.isArray(payload.inserted)) payload.inserted.forEach((entry) => collectEntity(entry, upsertIds));
98
164
  if (Array.isArray(payload.updated)) payload.updated.forEach((entry) => collectEntity(entry, upsertIds));
99
165
  if (Array.isArray(payload.trackedChangeRefs)) payload.trackedChangeRefs.forEach((entry) => collectEntity(entry, upsertIds));
100
166
  if (Array.isArray(payload.removed)) payload.removed.forEach((entry) => collectEntity(entry, removedIds));
101
167
  if (Array.isArray(payload.invalidatedRefs)) payload.invalidatedRefs.forEach((entry) => collectEntity(entry, removedIds));
102
168
  if (Array.isArray(payload.remappedRefs)) for (const mapping of payload.remappedRefs) {
103
- collectEntity(mapping?.from, removedIds);
104
- collectEntity(mapping?.to, upsertIds);
169
+ const fromId = readEntityId$1(mapping?.from);
170
+ const toId = readEntityId$1(mapping?.to);
171
+ if (fromId) removedIds.add(fromId);
172
+ if (toId) upsertIds.add(toId);
173
+ if (fromId && toId && fromId !== toId) remappedPairs.push({
174
+ from: fromId,
175
+ to: toId
176
+ });
105
177
  }
106
178
  for (const id of removedIds) upsertIds.delete(id);
107
- if (upsertIds.size === 0 && removedIds.size === 0) return null;
179
+ const allResolved = readAllResolvedFact(event, payload);
180
+ if (upsertIds.size === 0 && removedIds.size === 0 && !allResolved) return null;
108
181
  return {
109
182
  upsertIds,
110
183
  removedIds,
111
- reconcileMode: event.origin === "history" ? "authoritative" : "targeted"
184
+ remappedPairs,
185
+ reconcileMode: event.origin === "history" ? "authoritative" : "targeted",
186
+ ...allResolved ? { allResolved } : {}
112
187
  };
113
188
  }
189
+ function readAllResolvedFact(event, receipt) {
190
+ const fact = event?.trackedChangeAllResolved;
191
+ if (event?.origin === "history" || !fact || fact.schemaVersion !== 1 || fact.targetKind !== "all" || fact.decision !== "accept" && fact.decision !== "reject" || fact.remainingLogicalCount !== 0 || typeof fact.catalogRevision !== "string" || !fact.catalogRevision || typeof fact.sourceCoverageRevision !== "string" || !fact.sourceCoverageRevision || !Number.isSafeInteger(fact.logicalTargetCount) || fact.logicalTargetCount <= 0 || !Number.isSafeInteger(fact.physicalCarrierCount) || fact.physicalCarrierCount < fact.logicalTargetCount || typeof fact.txId !== "string" || fact.txId !== receipt.txId || typeof fact.documentEpoch !== "string" || !Number.isSafeInteger(fact.commitSequence) || typeof fact.packagePreviousRevision !== "string" || typeof fact.packageNextRevision !== "string" || fact.packagePreviousRevision === fact.packageNextRevision) return null;
192
+ return fact;
193
+ }
114
194
  var normalizeIds = (values) => new Set(Array.from(values ?? []).filter((value) => typeof value === "string" && value.length > 0));
115
195
  var itemId = (item) => {
116
196
  if (typeof item?.id === "string" && item.id.length > 0) return item.id;
@@ -121,10 +201,20 @@ var itemId = (item) => {
121
201
  var contextMatches = (left, right) => left?.adapter === right?.adapter && left?.documentId === right?.documentId && left?.editor === right?.editor && left?.reconcileToken === right?.reconcileToken;
122
202
  function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onReconciled }) {
123
203
  const pendingUpserts = /* @__PURE__ */ new Map();
204
+ let pendingAllResolved = null;
124
205
  let sequence = 0;
125
206
  let generation = 0;
126
207
  let flushInFlight = null;
127
208
  let flushRequested = false;
209
+ const settlementWaiters = [];
210
+ const resolveSettlementsThrough = (version, value) => {
211
+ for (let index = settlementWaiters.length - 1; index >= 0; index -= 1) {
212
+ const waiter = settlementWaiters[index];
213
+ if (waiter.generation !== generation || waiter.version > version) continue;
214
+ settlementWaiters.splice(index, 1);
215
+ waiter.resolve(value === true && waiter.requiresAllResolved ? pendingAllResolved == null : value);
216
+ }
217
+ };
128
218
  const isCurrent = (capturedGeneration, context) => generation === capturedGeneration && contextMatches(context, getContext());
129
219
  const clearResolved = (ids, snapshot) => {
130
220
  const clearedIds = /* @__PURE__ */ new Set();
@@ -138,6 +228,10 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
138
228
  const readySnapshot = (reconcileMode) => new Map([...pendingUpserts].filter(([, entry]) => entry.ready === true && entry.reconcileMode === reconcileMode));
139
229
  const markRenderGatedReady = () => {
140
230
  let changed = false;
231
+ if (pendingAllResolved?.gate === "render" && !pendingAllResolved.ready) {
232
+ pendingAllResolved.ready = true;
233
+ changed = true;
234
+ }
141
235
  for (const entry of pendingUpserts.values()) {
142
236
  if (entry.gate !== "render" || entry.ready) continue;
143
237
  entry.ready = true;
@@ -147,6 +241,10 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
147
241
  };
148
242
  const markEntriesReadyThrough = (maxVersion) => {
149
243
  let changed = false;
244
+ if (pendingAllResolved && pendingAllResolved.version <= maxVersion && !pendingAllResolved.ready) {
245
+ pendingAllResolved.ready = true;
246
+ changed = true;
247
+ }
150
248
  for (const entry of pendingUpserts.values()) {
151
249
  if (entry.version > maxVersion || entry.ready) continue;
152
250
  entry.ready = true;
@@ -197,14 +295,31 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
197
295
  };
198
296
  const flush = () => {
199
297
  if (flushInFlight) return flushInFlight;
298
+ const allResolvedSnapshot = pendingAllResolved?.ready ? pendingAllResolved : null;
200
299
  const targetedSnapshot = readySnapshot("targeted");
201
300
  const authoritativeSnapshot = readySnapshot("authoritative");
202
- if (targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
301
+ if (!allResolvedSnapshot && targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
203
302
  const context = getContext();
204
303
  if (!context?.adapter) return null;
205
304
  flushRequested = false;
206
305
  const capturedGeneration = generation;
207
306
  flushInFlight = Promise.resolve().then(async () => {
307
+ if (allResolvedSnapshot) {
308
+ let result = null;
309
+ try {
310
+ result = await reconcile(context, {
311
+ upsertIds: /* @__PURE__ */ new Set(),
312
+ removedIds: /* @__PURE__ */ new Set(),
313
+ allResolved: allResolvedSnapshot.fact,
314
+ reconcileMode: "targeted"
315
+ });
316
+ } catch {}
317
+ if (!isCurrent(capturedGeneration, context)) return;
318
+ if (result?.ok === true) {
319
+ if (pendingAllResolved === allResolvedSnapshot) pendingAllResolved = null;
320
+ await notifyReconciled(context, /* @__PURE__ */ new Set(), result, true);
321
+ }
322
+ }
208
323
  if (authoritativeSnapshot.size > 0) await reconcileAuthoritativeSnapshot(context, authoritativeSnapshot, capturedGeneration).catch(() => void 0);
209
324
  if (targetedSnapshot.size > 0) await reconcileTargetedSnapshot(context, targetedSnapshot, capturedGeneration).catch(() => void 0);
210
325
  }).catch(() => {}).finally(() => {
@@ -215,8 +330,18 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
215
330
  };
216
331
  const enqueueWithGate = (impact, gate) => {
217
332
  if (!impact) return;
333
+ if (impact.allResolved) {
334
+ pendingUpserts.clear();
335
+ pendingAllResolved = {
336
+ fact: impact.allResolved,
337
+ version: ++sequence,
338
+ gate,
339
+ ready: false
340
+ };
341
+ }
218
342
  const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
219
343
  const removedIds = normalizeIds(impact.removedIds);
344
+ const upsertIds = normalizeIds(impact.upsertIds);
220
345
  for (const id of removedIds) {
221
346
  const existing = pendingUpserts.get(id);
222
347
  if (existing?.reconcileMode === "authoritative") pendingUpserts.set(id, {
@@ -228,13 +353,13 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
228
353
  else pendingUpserts.delete(id);
229
354
  }
230
355
  const context = getContext();
231
- if (removedIds.size > 0 && context?.adapter) try {
356
+ if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter && !impact.allResolved) try {
232
357
  Promise.resolve(reconcile(context, {
233
358
  upsertIds: /* @__PURE__ */ new Set(),
234
359
  removedIds
235
360
  })).catch(() => void 0);
236
361
  } catch {}
237
- for (const id of normalizeIds(impact.upsertIds)) if (!removedIds.has(id)) {
362
+ for (const id of upsertIds) if (!removedIds.has(id)) {
238
363
  const existing = pendingUpserts.get(id);
239
364
  pendingUpserts.set(id, {
240
365
  version: ++sequence,
@@ -255,20 +380,34 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
255
380
  enqueueAfterPaint(impact, waitForPaint) {
256
381
  if (!impact || typeof waitForPaint !== "function") {
257
382
  enqueue(impact);
258
- return;
383
+ return Promise.resolve(false);
259
384
  }
260
385
  enqueueWithGate(impact, "exact-paint");
261
386
  const context = getContext();
262
- if (!context?.adapter) return;
263
387
  const capturedGeneration = generation;
264
388
  const capturedSequence = sequence;
389
+ if (!context?.adapter) {
390
+ markEntriesReadyThrough(capturedSequence);
391
+ return Promise.resolve(false);
392
+ }
393
+ const completion = new Promise((resolve) => {
394
+ settlementWaiters.push({
395
+ generation: capturedGeneration,
396
+ version: capturedSequence,
397
+ requiresAllResolved: Boolean(impact.allResolved),
398
+ resolve
399
+ });
400
+ });
265
401
  const impactedIds = new Set([...normalizeIds(impact.upsertIds), ...normalizeIds(impact.removedIds)]);
266
402
  const capturedEntries = new Map([...impactedIds].flatMap((id) => {
267
403
  const entry = pendingUpserts.get(id);
268
404
  return entry ? [[id, entry]] : [];
269
405
  }));
270
- Promise.resolve().then(() => waitForPaint()).then(() => {
271
- if (!isCurrent(capturedGeneration, context)) return;
406
+ Promise.resolve().then(() => waitForPaint()).then(async () => {
407
+ if (!isCurrent(capturedGeneration, context)) {
408
+ resolveSettlementsThrough(capturedSequence, false);
409
+ return;
410
+ }
272
411
  let madeReady = markRenderGatedReady();
273
412
  if (markEntriesReadyThrough(capturedSequence)) madeReady = true;
274
413
  for (const [id, entry] of capturedEntries) {
@@ -277,16 +416,35 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
277
416
  entry.ready = true;
278
417
  }
279
418
  if (flushInFlight && (madeReady || capturedSequence > 0)) flushRequested = true;
280
- flush();
419
+ const preceding = flushInFlight;
420
+ const pending = flush();
421
+ if (pending) await pending;
422
+ if (preceding) {
423
+ const followUp = flushInFlight;
424
+ if (followUp && followUp !== preceding) await followUp;
425
+ }
426
+ if (!isCurrent(capturedGeneration, context)) {
427
+ resolveSettlementsThrough(capturedSequence, false);
428
+ return;
429
+ }
430
+ resolveSettlementsThrough(capturedSequence, true);
281
431
  }).catch(() => {
282
- if (!isCurrent(capturedGeneration, context)) return;
432
+ if (!isCurrent(capturedGeneration, context)) {
433
+ resolveSettlementsThrough(capturedSequence, false);
434
+ return;
435
+ }
283
436
  for (const [id, entry] of capturedEntries) if (pendingUpserts.get(id) === entry) entry.gate = "render";
437
+ if (pendingAllResolved?.gate === "exact-paint") pendingAllResolved.gate = "render";
438
+ resolveSettlementsThrough(capturedSequence, false);
284
439
  });
440
+ return completion;
285
441
  },
286
442
  onRender,
287
443
  reset() {
444
+ for (const waiter of settlementWaiters.splice(0)) waiter.resolve(false);
288
445
  generation += 1;
289
446
  pendingUpserts.clear();
447
+ pendingAllResolved = null;
290
448
  flushRequested = false;
291
449
  },
292
450
  getPendingIds() {
@@ -1169,67 +1327,6 @@ function collectEntityHitsFromChain(start, stopAt) {
1169
1327
  }
1170
1328
  return hits;
1171
1329
  }
1172
- const DOM_CLASS_NAMES = {
1173
- PAGE: "superdoc-page",
1174
- FRAGMENT: "superdoc-fragment",
1175
- LINE: "superdoc-line",
1176
- INLINE_SDT_WRAPPER: "superdoc-structured-content-inline",
1177
- INLINE_SDT_LABEL: "superdoc-structured-content-inline__label",
1178
- BLOCK_SDT: "superdoc-structured-content-block",
1179
- BLOCK_SDT_LABEL: "superdoc-structured-content__label",
1180
- TABLE_FRAGMENT: "superdoc-table-fragment",
1181
- DOCUMENT_SECTION: "superdoc-document-section",
1182
- SDT_GROUP_HOVER: "sdt-group-hover",
1183
- TOC_ENTRY: "superdoc-toc-entry",
1184
- TOC_GROUP_HOVER: "toc-group-hover",
1185
- IMAGE_FRAGMENT: "superdoc-image-fragment",
1186
- INLINE_IMAGE: "superdoc-inline-image",
1187
- LIST_MARKER: "superdoc-list-marker",
1188
- INLINE_IMAGE_CLIP_WRAPPER: "superdoc-inline-image-clip-wrapper",
1189
- ANNOTATION: "annotation",
1190
- ANNOTATION_CONTENT: "annotation-content",
1191
- ANNOTATION_CARET_ANCHOR: "annotation-caret-anchor"
1192
- };
1193
- DOM_CLASS_NAMES.INLINE_SDT_LABEL, DOM_CLASS_NAMES.BLOCK_SDT_LABEL;
1194
- const DATA_ATTRS = {
1195
- PM_START: "data-pm-start",
1196
- PM_END: "data-pm-end",
1197
- LAYOUT_EPOCH: "data-layout-epoch",
1198
- TABLE_BOUNDARIES: "data-table-boundaries",
1199
- SDT_ID: "data-sdt-id",
1200
- SDT_TYPE: "data-sdt-type",
1201
- FIELD_ID: "data-field-id",
1202
- FIELD_TYPE: "data-field-type",
1203
- DRAGGABLE: "data-draggable",
1204
- DISPLAY_LABEL: "data-display-label",
1205
- VARIANT: "data-variant",
1206
- TYPE: "data-type",
1207
- LAYOUT_BOUNDARY_SCHEMA: "data-layout-boundary-schema",
1208
- LAYOUT_FRAGMENT_ID: "data-layout-fragment-id",
1209
- LAYOUT_STORY: "data-layout-story",
1210
- LAYOUT_BLOCK_REF: "data-layout-block-ref"
1211
- };
1212
- const decodeLayoutStoryDataset = (raw) => {
1213
- if (!raw) return { kind: "unknown" };
1214
- if (raw === "body") return { kind: "body" };
1215
- const idx = raw.indexOf(":");
1216
- const kind = idx === -1 ? raw : raw.slice(0, idx);
1217
- const id = idx === -1 ? void 0 : raw.slice(idx + 1);
1218
- switch (kind) {
1219
- case "body":
1220
- case "header":
1221
- case "footer":
1222
- case "footnote":
1223
- case "endnote":
1224
- case "textbox": return id ? {
1225
- kind,
1226
- id
1227
- } : { kind };
1228
- default: return { kind: "unknown" };
1229
- }
1230
- };
1231
- `${DOM_CLASS_NAMES.BLOCK_SDT}${DATA_ATTRS.SDT_ID}`;
1232
- `${DATA_ATTRS.DRAGGABLE}`;
1233
1330
  var schemaBooleanOrNull = () => ({ oneOf: [{ type: "boolean" }, { type: "null" }] });
1234
1331
  var schemaStringOrNull = () => ({ oneOf: [{
1235
1332
  type: "string",
@@ -1481,6 +1578,30 @@ const INLINE_PROPERTY_REGISTRY = [
1481
1578
  new Set(INLINE_PROPERTY_REGISTRY.map((entry) => entry.key));
1482
1579
  const INLINE_PROPERTY_BY_KEY = Object.fromEntries(INLINE_PROPERTY_REGISTRY.map((entry) => [entry.key, entry]));
1483
1580
  INLINE_PROPERTY_REGISTRY.filter((entry) => entry.storage === "mark").map((entry) => entry.key), INLINE_PROPERTY_REGISTRY.filter((entry) => entry.storage === "runAttribute").map((entry) => entry.key);
1581
+ var sharedUiTrackedChangesCatalogByHost = /* @__PURE__ */ new WeakMap();
1582
+ function acquireSharedUiTrackedChangesCatalog(host) {
1583
+ let state = sharedUiTrackedChangesCatalogByHost.get(host);
1584
+ if (!state) {
1585
+ state = {
1586
+ refCount: 0,
1587
+ generation: 0,
1588
+ abortController: new AbortController(),
1589
+ inFlight: null,
1590
+ activeMutationTokens: /* @__PURE__ */ new Set()
1591
+ };
1592
+ sharedUiTrackedChangesCatalogByHost.set(host, state);
1593
+ }
1594
+ state.refCount += 1;
1595
+ return state;
1596
+ }
1597
+ function releaseSharedUiTrackedChangesCatalog(host, state) {
1598
+ state.refCount = Math.max(0, state.refCount - 1);
1599
+ if (state.refCount > 0) return;
1600
+ state.abortController.abort("ui-controller-destroyed");
1601
+ state.inFlight = null;
1602
+ state.activeMutationTokens.clear();
1603
+ sharedUiTrackedChangesCatalogByHost.delete(host);
1604
+ }
1484
1605
  var SUPERDOC_UI_REASON_VALUES = new Set(Object.values(SUPERDOC_UI_REASONS));
1485
1606
  function coerceSuperDocUIReason(reason, fallback) {
1486
1607
  return typeof reason === "string" && SUPERDOC_UI_REASON_VALUES.has(reason) ? reason : fallback;
@@ -1577,12 +1698,12 @@ const HEAVY_DOC_READ_POLICY = [
1577
1698
  {
1578
1699
  key: "trackChanges",
1579
1700
  match: "exact",
1580
- note: "audited: body tracked-changes list; stale-served during load"
1701
+ note: "audited: shared all-story tracked-changes catalog; stale-served during load"
1581
1702
  },
1582
1703
  {
1583
1704
  key: "trackChanges:all",
1584
1705
  match: "exact",
1585
- note: "audited: all-story tracked-changes list; consumers already fail closed on non-ready"
1706
+ note: "audited: current-token validation over the shared all-story catalog; consumers fail closed on non-ready"
1586
1707
  },
1587
1708
  {
1588
1709
  key: "tables",
@@ -1664,6 +1785,48 @@ function readEntityTarget(row) {
1664
1785
  const target = row.target;
1665
1786
  return target && typeof target === "object" ? target : null;
1666
1787
  }
1788
+ function isHostGeometryTarget(target) {
1789
+ if (!target || typeof target !== "object") return false;
1790
+ const record = target;
1791
+ const isTextPoint = (point) => {
1792
+ if (!point || typeof point !== "object") return false;
1793
+ const candidate = point;
1794
+ return candidate.kind === "text" && typeof candidate.blockId === "string" && typeof candidate.offset === "number";
1795
+ };
1796
+ const isTextSegment = (segment) => {
1797
+ if (!segment || typeof segment !== "object") return false;
1798
+ const candidate = segment;
1799
+ const range$1 = candidate.range;
1800
+ return typeof candidate.blockId === "string" && typeof range$1?.start === "number" && typeof range$1.end === "number";
1801
+ };
1802
+ if (Array.isArray(record.segments)) {
1803
+ const first = record.segments[0];
1804
+ const last = record.segments[record.segments.length - 1];
1805
+ return isTextSegment(first) && isTextSegment(last);
1806
+ }
1807
+ if (record.kind === "selection") return isTextPoint(record.start) && isTextPoint(record.end);
1808
+ if (record.kind !== "text" || typeof record.blockId !== "string") return false;
1809
+ const range = record.range;
1810
+ return typeof range?.start === "number" && typeof range.end === "number";
1811
+ }
1812
+ function readTrackedChangeNavigationTarget(row) {
1813
+ if (!row || typeof row !== "object") return null;
1814
+ const target = readEntityTarget(row);
1815
+ if (isHostGeometryTarget(target)) return target;
1816
+ const record = row;
1817
+ const navigationTarget = record.navigationTarget && typeof record.navigationTarget === "object" ? record.navigationTarget : null;
1818
+ if (navigationTarget?.kind === "block" && typeof navigationTarget.blockId === "string" && navigationTarget.blockId.length > 0) return {
1819
+ kind: "text",
1820
+ blockId: navigationTarget.blockId,
1821
+ range: {
1822
+ start: 0,
1823
+ end: 0
1824
+ },
1825
+ ...navigationTarget.story && typeof navigationTarget.story === "object" ? { story: navigationTarget.story } : {},
1826
+ ...record.address && typeof record.address === "object" ? { address: record.address } : {}
1827
+ };
1828
+ return target;
1829
+ }
1667
1830
  function storyLocatorSignature(story) {
1668
1831
  if (!story || typeof story !== "object") return "story:body";
1669
1832
  const record = story;
@@ -1870,6 +2033,7 @@ function buildTrackedChangeIdContext(items) {
1870
2033
  return context;
1871
2034
  }
1872
2035
  var storyScopedTrackedChangeIdContextCache = /* @__PURE__ */ new WeakMap();
2036
+ var declaredBodyItemsCache = /* @__PURE__ */ new WeakMap();
1873
2037
  function buildStoryScopedTrackedChangeIdContext(items, story) {
1874
2038
  const signature = storyLocatorSignature(story);
1875
2039
  let byStory = storyScopedTrackedChangeIdContextCache.get(items);
@@ -1883,6 +2047,56 @@ function buildStoryScopedTrackedChangeIdContext(items, story) {
1883
2047
  byStory.set(signature, context);
1884
2048
  return context;
1885
2049
  }
2050
+ function isTextboxStory(story) {
2051
+ return !!story && typeof story === "object" && story.storyType === "textbox";
2052
+ }
2053
+ function resolveStorylessCanonicalTrackedChangeId(items, selectionIds) {
2054
+ const storyless = selectionIds.filter((id) => items.some((item) => readEntityId(item) === id && readEntityStory(item) == null));
2055
+ if (storyless.length === 0) return null;
2056
+ if (storyless.length === 1) return storyless[0];
2057
+ const seen = /* @__PURE__ */ new Set();
2058
+ for (const id of storyless) {
2059
+ const row = items.find((item) => readEntityId(item) === id);
2060
+ if (!row) continue;
2061
+ for (const raw of readTrackChangeProvenanceIds(row)) {
2062
+ if (seen.has(raw)) return null;
2063
+ seen.add(raw);
2064
+ }
2065
+ }
2066
+ return storyless[0];
2067
+ }
2068
+ function declaredBodyItems(items) {
2069
+ const cached = declaredBodyItemsCache.get(items);
2070
+ if (cached) return cached;
2071
+ const declared = items.filter((item) => readEntityStory(item) != null);
2072
+ const result = declared.length === items.length ? items : declared;
2073
+ declaredBodyItemsCache.set(items, result);
2074
+ return result;
2075
+ }
2076
+ function resolveSelectionActiveChangeId(options) {
2077
+ const { selectionIds, publicIds, selection, bodyItems, allStoryItems } = options;
2078
+ if (selectionIds.length === 0) return null;
2079
+ const mapIds = (context) => {
2080
+ for (const id of selectionIds) {
2081
+ const publicId = context.toPublicId(id);
2082
+ if (publicId) return publicId;
2083
+ }
2084
+ return null;
2085
+ };
2086
+ const story = selectionStory(selection);
2087
+ const isBodySelection = storyLocatorSignature(story) === storyLocatorSignature(null);
2088
+ if (allStoryItems) {
2089
+ const scopedItems = isBodySelection ? declaredBodyItems(allStoryItems) : allStoryItems;
2090
+ const fromStory = mapIds(buildStoryScopedTrackedChangeIdContext(scopedItems, story));
2091
+ if (fromStory) return fromStory;
2092
+ const fromPublic = publicIds?.find((id) => scopedItems.some((item) => entityRowMatchesRequest(item, id, story)));
2093
+ if (fromPublic) return fromPublic;
2094
+ if (!isBodySelection) return isTextboxStory(story) ? resolveStorylessCanonicalTrackedChangeId(allStoryItems, selectionIds) : null;
2095
+ return mapIds(buildTrackedChangeIdContext(bodyItems));
2096
+ }
2097
+ if (isBodySelection) return mapIds(buildTrackedChangeIdContext(bodyItems)) ?? selectionIds[0] ?? null;
2098
+ return null;
2099
+ }
1886
2100
  function resolveActiveCommentId(items, commentId) {
1887
2101
  const item = items.find((candidate) => readEntityId(candidate) === commentId || candidate.importedId === commentId);
1888
2102
  if (!item) return null;
@@ -2860,11 +3074,14 @@ function createSuperDocUI(options) {
2860
3074
  let explicitActiveChange = null;
2861
3075
  let explicitActiveChangeRevision = 0;
2862
3076
  let queuedTrackChangeNavigationInvalidation = 0;
3077
+ let trackChangeRevealInvalidation = 0;
3078
+ const pendingTrackChangeRevealFocuses = /* @__PURE__ */ new Set();
2863
3079
  const explicitActiveChangesEqual = (left, right) => {
2864
3080
  if (left === right) return true;
2865
3081
  if (!left || !right) return false;
2866
3082
  return left.id === right.id && left.paintedEntityId === right.paintedEntityId && storyLocatorSignature(left.story) === storyLocatorSignature(right.story);
2867
3083
  };
3084
+ const hasPendingTrackChangeRevealFocus = (focus) => Array.from(pendingTrackChangeRevealFocuses).some((pendingFocus) => explicitActiveChangesEqual(pendingFocus, focus));
2868
3085
  const mirrorTrackedChangeFocusToHost = (next) => {
2869
3086
  const host = getHost();
2870
3087
  if (typeof host?.getHandles !== "function") return;
@@ -2893,6 +3110,7 @@ function createSuperDocUI(options) {
2893
3110
  const setExplicitActiveChange = (next, options$1) => {
2894
3111
  explicitActiveChange = next;
2895
3112
  explicitActiveChangeRevision += 1;
3113
+ trackChangeRevealInvalidation += 1;
2896
3114
  if (options$1?.invalidateQueuedNavigation !== false) queuedTrackChangeNavigationInvalidation += 1;
2897
3115
  };
2898
3116
  const listeners = /* @__PURE__ */ new Set();
@@ -2944,6 +3162,63 @@ function createSuperDocUI(options) {
2944
3162
  };
2945
3163
  let documentMutationRevision = 0;
2946
3164
  let reviewMutationToken = {};
3165
+ const uiTrackedChangesCatalogHost = options.superdoc;
3166
+ const uiTrackedChangesCatalogState = acquireSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost);
3167
+ const supersedeUiTrackedChangesCatalogRead = () => {
3168
+ uiTrackedChangesCatalogState.abortController.abort("review-mutation-started");
3169
+ uiTrackedChangesCatalogState.inFlight = null;
3170
+ };
3171
+ const renewUiTrackedChangesCatalogRead = () => {
3172
+ uiTrackedChangesCatalogState.generation += 1;
3173
+ uiTrackedChangesCatalogState.abortController = new AbortController();
3174
+ uiTrackedChangesCatalogState.inFlight = null;
3175
+ };
3176
+ const beginUiReviewMutation = (token) => {
3177
+ const activeTokens = uiTrackedChangesCatalogState.activeMutationTokens;
3178
+ if (typeof token !== "string" || token.length === 0 || activeTokens.has(token)) return;
3179
+ const firstToken = activeTokens.size === 0;
3180
+ activeTokens.add(token);
3181
+ if (firstToken) supersedeUiTrackedChangesCatalogRead();
3182
+ };
3183
+ const settleUiReviewMutation = (token) => {
3184
+ const activeTokens = uiTrackedChangesCatalogState.activeMutationTokens;
3185
+ if (typeof token !== "string" || !activeTokens.delete(token)) return;
3186
+ if (activeTokens.size === 0) renewUiTrackedChangesCatalogRead();
3187
+ };
3188
+ const runUiTrackedChangesCatalogRead = (fallback) => {
3189
+ const v2TrackedChanges = getV2TrackedChanges();
3190
+ const listTrackedChanges = v2TrackedChanges?.listTrackedChanges;
3191
+ if (typeof listTrackedChanges !== "function") {
3192
+ if (getEditor()?.editorVersion === 2) return Promise.reject(/* @__PURE__ */ new Error("v2-tracked-changes-bridge-pending"));
3193
+ return fallback();
3194
+ }
3195
+ if (uiTrackedChangesCatalogState.activeMutationTokens.size > 0) return Promise.reject(/* @__PURE__ */ new Error("ui-review-catalog-superseded"));
3196
+ if ((getEditor()?.reviewHydration?.getDiagnostics?.())?.inFlight === true) return Promise.reject(/* @__PURE__ */ new Error("shell-review-hydration-in-flight"));
3197
+ const generation = uiTrackedChangesCatalogState.generation;
3198
+ const existing = uiTrackedChangesCatalogState.inFlight;
3199
+ if (existing?.generation === generation) return existing.promise;
3200
+ const signal = uiTrackedChangesCatalogState.abortController.signal;
3201
+ const validateResult = (result) => {
3202
+ if (signal.aborted || generation !== uiTrackedChangesCatalogState.generation || result?.reason === "review-hydration-superseded") throw new Error("ui-review-catalog-superseded");
3203
+ return result;
3204
+ };
3205
+ const rawResult = listTrackedChanges.call(v2TrackedChanges, {
3206
+ blocking: false,
3207
+ signal
3208
+ });
3209
+ if (!isPromiseLike(rawResult)) return validateResult(rawResult);
3210
+ const promise = Promise.resolve(rawResult).then(validateResult);
3211
+ uiTrackedChangesCatalogState.inFlight = {
3212
+ generation,
3213
+ promise
3214
+ };
3215
+ promise.then(() => {
3216
+ if (uiTrackedChangesCatalogState.inFlight?.promise === promise) uiTrackedChangesCatalogState.inFlight = null;
3217
+ }, () => {
3218
+ if (uiTrackedChangesCatalogState.inFlight?.promise === promise) uiTrackedChangesCatalogState.inFlight = null;
3219
+ });
3220
+ return promise;
3221
+ };
2947
3222
  let authoritativeTrackChangesPendingToken = null;
2948
3223
  let authoritativeTrackChangesRetryTimer = null;
2949
3224
  let authoritativeTrackChangesFailureCount = 0;
@@ -2955,6 +3230,7 @@ function createSuperDocUI(options) {
2955
3230
  let scheduleAuthoritativeTrackChangesRetry = (_reconcileToken) => void 0;
2956
3231
  let postDecisionTrackChangesToken = null;
2957
3232
  let postDecisionTrackChangeIds = /* @__PURE__ */ new Set();
3233
+ let allTrackedChangesResolvedToken = null;
2958
3234
  let selectionEpoch = 0;
2959
3235
  let lastCoordinatorEditor = null;
2960
3236
  const contentToken = () => `${editorIdentityId(getEditor())}|m${documentMutationRevision}`;
@@ -3033,6 +3309,23 @@ function createSuperDocUI(options) {
3033
3309
  };
3034
3310
  notifyPostDecisionTrackChanges(receipt);
3035
3311
  };
3312
+ const publishAllTrackedChangesResolved = (receipt) => {
3313
+ const previous = state.trackChanges;
3314
+ const changed = previous.items.length > 0 || previous.total !== 0 || previous.activeId !== null || previous.authors.length > 0;
3315
+ allTrackedChangesResolvedToken = contentToken();
3316
+ if (explicitActiveChange !== null) setExplicitActiveChange(null);
3317
+ state = {
3318
+ ...state,
3319
+ trackChanges: {
3320
+ ...previous,
3321
+ items: [],
3322
+ total: 0,
3323
+ activeId: null,
3324
+ authors: []
3325
+ }
3326
+ };
3327
+ if (changed) notifyPostDecisionTrackChanges(receipt);
3328
+ };
3036
3329
  const markPostDecisionTrackChanges = (ids, receipt) => {
3037
3330
  if (ids.size === 0) return;
3038
3331
  const token = contentToken();
@@ -3298,7 +3591,7 @@ function createSuperDocUI(options) {
3298
3591
  const read = host?.getDocumentLoadingSnapshot;
3299
3592
  if (typeof read !== "function") return "unknown";
3300
3593
  const stage = safeCall(() => read.call(host), null)?.sourceStage;
3301
- if (stage === "opening" || stage === "ready-window" || stage === "source-loading") return "loading";
3594
+ if (stage === "opening" || stage === "bootstrap-ready" || stage === "source-loading") return "loading";
3302
3595
  if (stage === "source-complete" || stage === "source-failed" || stage === "source-cancelled") return "complete";
3303
3596
  return "unknown";
3304
3597
  };
@@ -3711,6 +4004,7 @@ function createSuperDocUI(options) {
3711
4004
  detachSourceLoading = null;
3712
4005
  sourceLoadingSubscriptionHost = null;
3713
4006
  clearPostDecisionTrackChanges();
4007
+ allTrackedChangesResolvedToken = null;
3714
4008
  };
3715
4009
  const invalidateDocumentContent = () => {
3716
4010
  const carryAuthoritativeTrackChangesHold = authoritativeTrackChangesPendingToken !== null;
@@ -3718,6 +4012,15 @@ function createSuperDocUI(options) {
3718
4012
  if (carryAuthoritativeTrackChangesHold) authoritativeTrackChangesPendingToken = contentToken();
3719
4013
  clearPostDecisionTrackChanges();
3720
4014
  };
4015
+ const invalidateAfterCommandSettlement = () => {
4016
+ const carryAllResolvedCatalog = allTrackedChangesResolvedToken === contentToken();
4017
+ invalidateDocumentContent();
4018
+ if (carryAllResolvedCatalog) {
4019
+ replaceTrackedChangeItemsInCache([]);
4020
+ allTrackedChangesResolvedToken = contentToken();
4021
+ }
4022
+ recompute();
4023
+ };
3721
4024
  const insertText = (text) => {
3722
4025
  if (typeof text !== "string" || text.length === 0) return failedReceipt("insertText requires a non-empty string.", "INVALID_INPUT");
3723
4026
  if (readDocumentMode() === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
@@ -3921,7 +4224,17 @@ function createSuperDocUI(options) {
3921
4224
  const { value, status } = authoritativeTrackChangesPendingToken === token ? {
3922
4225
  value: heldEntry?.hasSettled ? heldEntry.value : null,
3923
4226
  status: heldEntry?.hasSettled ? "stale" : "pending"
3924
- } : readAsync("trackChanges:all", token, () => tcApi?.list ? tcApi.list({ in: "all" }) : void 0, (raw) => {
4227
+ } : readAsync("trackChanges:all", token, () => {
4228
+ if (!tcApi?.list) return void 0;
4229
+ const fallback = () => tcApi.list({ in: "all" });
4230
+ const bridgeOwnsCatalog = typeof getV2TrackedChanges()?.listTrackedChanges === "function";
4231
+ const shared = runUiTrackedChangesCatalogRead(fallback);
4232
+ if (!bridgeOwnsCatalog) return shared;
4233
+ return Promise.resolve(shared).then((raw) => {
4234
+ const record = isLooseObject(raw) ? raw : null;
4235
+ return (Array.isArray(record?.items) ? record.items : []).some((item) => readEntityStory(item) == null) ? fallback() : raw;
4236
+ });
4237
+ }, (raw) => {
3925
4238
  return (raw && Array.isArray(raw.items) ? raw.items : []).map(projectTrackChangesItem).filter((item) => item != null);
3926
4239
  });
3927
4240
  return status === "ready" ? value : null;
@@ -3937,9 +4250,9 @@ function createSuperDocUI(options) {
3937
4250
  value: heldEntry?.hasSettled ? heldEntry.value : null,
3938
4251
  status: heldEntry?.hasSettled ? "stale" : "pending"
3939
4252
  } : readAsync("trackChanges", token, () => {
3940
- 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") : []);
4253
+ if (listTrackedChanges) return runUiTrackedChangesCatalogRead(() => tcApi?.list?.());
4254
+ return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
4255
+ }, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
3943
4256
  const items = (value ?? []).map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
3944
4257
  const id = readEntityId(item);
3945
4258
  return !id || !postDecisionIds?.has(id);
@@ -3958,8 +4271,20 @@ function createSuperDocUI(options) {
3958
4271
  if (allStoryItems && !allStoryItems.some((row) => entityRowMatchesRequest(row, active.id, active.story))) setExplicitActiveChange(null);
3959
4272
  } else if (!items.some((item) => readEntityId(item) === active.id)) setExplicitActiveChange(null);
3960
4273
  }
3961
- const selectionActiveChangeIds = postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
3962
- const activeId = explicitActiveChange?.id ?? selectionActiveChangeIds[0] ?? null;
4274
+ const publicIdItems = allStoryItems ?? items;
4275
+ const selectionIdContext = buildStoryScopedTrackedChangeIdContext(publicIdItems, selectionStory(selection$1));
4276
+ const selectionActiveChangeIds = allTrackedChangesResolvedToken === token ? [] : postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
4277
+ const selectionPublicChangeIds = selectionActiveChangeIds.map((id) => selectionIdContext.toPublicId(id) ?? id);
4278
+ const explicitActiveIdContext = explicitActiveChange?.story ? buildStoryScopedTrackedChangeIdContext(publicIdItems, explicitActiveChange.story) : buildTrackedChangeIdContext(publicIdItems);
4279
+ const explicitActiveId = explicitActiveChange ? explicitActiveIdContext.toPublicId(explicitActiveChange.id) ?? explicitActiveChange.id : null;
4280
+ const selectionActiveId = resolveSelectionActiveChangeId({
4281
+ selectionIds: selectionActiveChangeIds,
4282
+ publicIds: selectionPublicChangeIds,
4283
+ selection: selection$1,
4284
+ bodyItems: items,
4285
+ allStoryItems
4286
+ });
4287
+ const activeId = explicitActiveId ?? selectionActiveId;
3963
4288
  return {
3964
4289
  status: combineStatus(listStatus, selection$1.status),
3965
4290
  items,
@@ -5623,13 +5948,18 @@ function createSuperDocUI(options) {
5623
5948
  const target = rawTarget && typeof rawTarget === "object" ? rawTarget : null;
5624
5949
  let next = null;
5625
5950
  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;
5951
+ const paintedEntityId = target.entityId;
5952
+ const targetStory = target.story && typeof target.story === "object" ? target.story : void 0;
5953
+ const allStoryItems = readAllStoryTrackChanges();
5954
+ const entityId = (allStoryItems ? targetStory ? buildStoryScopedTrackedChangeIdContext(allStoryItems, targetStory).toPublicId(paintedEntityId) : buildTrackedChangeIdContext(allStoryItems).toPublicId(paintedEntityId) : null) ?? paintedEntityId;
5955
+ const story = targetStory && storyLocatorSignature(targetStory) !== storyLocatorSignature(null) ? targetStory : void 0;
5627
5956
  next = {
5628
- id: target.entityId,
5957
+ id: entityId,
5629
5958
  ...story ? { story } : {},
5630
- ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : {}
5959
+ ...typeof target.paintedEntityId === "string" && target.paintedEntityId.length > 0 ? { paintedEntityId: target.paintedEntityId } : entityId !== paintedEntityId ? { paintedEntityId } : {}
5631
5960
  };
5632
5961
  }
5962
+ if (rawTarget == null && next == null && explicitActiveChange != null && hasPendingTrackChangeRevealFocus(explicitActiveChange)) return false;
5633
5963
  if (explicitActiveChangesEqual(explicitActiveChange, next)) return false;
5634
5964
  setExplicitActiveChange(next);
5635
5965
  return true;
@@ -5706,6 +6036,20 @@ function createSuperDocUI(options) {
5706
6036
  try {
5707
6037
  const off = next.subscribe((event) => {
5708
6038
  const type = event?.type;
6039
+ if (type === "review-mutation:started") {
6040
+ beginUiReviewMutation(event.reviewMutation?.token);
6041
+ return;
6042
+ }
6043
+ if (type === "review-mutation:aborted") {
6044
+ settleUiReviewMutation(event.reviewMutation?.token);
6045
+ scheduleAsyncRefresh();
6046
+ return;
6047
+ }
6048
+ if (type === "mutation:rejected" && event.reviewMutation) {
6049
+ settleUiReviewMutation(event.reviewMutation.token);
6050
+ scheduleAsyncRefresh();
6051
+ }
6052
+ if (type === "mutation:committed" && event.reviewMutation) settleUiReviewMutation(event.reviewMutation.token);
5709
6053
  if (type === "source:complete" || type === "source:signals-complete") {
5710
6054
  if (authoritativeTrackChangesPendingToken !== null) {
5711
6055
  clearAuthoritativeTrackChangesRetry();
@@ -5721,6 +6065,13 @@ function createSuperDocUI(options) {
5721
6065
  if (type === "mutation:committed" || type === "save:completed" || type === "collaboration:remote-changed") {
5722
6066
  const impact = type === "mutation:committed" ? getV2TrackedChangeMutationImpact(event) : null;
5723
6067
  const supersedesPendingReviewReconcile = type === "mutation:committed" && reviewMutationReconciler.getPendingIds().size > 0;
6068
+ if (impact?.allResolved) {
6069
+ reviewMutationReconciler.reset();
6070
+ replaceTrackedChangeItemsInCache([]);
6071
+ authoritativeTrackChangesPendingToken = null;
6072
+ publishAllTrackedChangesResolved(event.receipt);
6073
+ return;
6074
+ }
5724
6075
  if (impact?.removedIds.size) markPostDecisionTrackChanges(new Set(impact.removedIds), event.receipt);
5725
6076
  const authoritativeHistoryImpact = impact?.reconcileMode === "authoritative" && impact.upsertIds.size > 0;
5726
6077
  if (authoritativeHistoryImpact) {
@@ -5867,6 +6218,17 @@ function createSuperDocUI(options) {
5867
6218
  function readTrackDecisionTargetId(target) {
5868
6219
  return target && typeof target === "object" && typeof target.id === "string" ? String(target.id) : null;
5869
6220
  }
6221
+ function nonBodySelectionStoryLocator(selection$1) {
6222
+ const story = selectionStoryLocator(selection$1);
6223
+ return story && storyLocatorSignature(story) !== storyLocatorSignature(null) ? story : void 0;
6224
+ }
6225
+ function trackDecisionIdTarget(changeId, story) {
6226
+ return {
6227
+ kind: "id",
6228
+ id: changeId,
6229
+ ...story ? { story } : {}
6230
+ };
6231
+ }
5870
6232
  function trackDecisionRangeFromSelection(selection$1) {
5871
6233
  if (selection$1.empty) return null;
5872
6234
  const explicit = selection$1.selectionTarget;
@@ -5892,22 +6254,58 @@ function createSuperDocUI(options) {
5892
6254
  }
5893
6255
  };
5894
6256
  }
6257
+ function readBodyTrackChangeForDecision(activeId, story) {
6258
+ if (storyLocatorSignature(story) !== storyLocatorSignature(null)) return void 0;
6259
+ const trackChanges$1 = getDoc()?.trackChanges;
6260
+ const list = trackChanges$1?.list;
6261
+ if (typeof list !== "function") return void 0;
6262
+ const raw = safeCall(() => list.call(trackChanges$1), null);
6263
+ if (isPromiseLike(raw) || !raw || typeof raw !== "object") return void 0;
6264
+ const items = raw.items;
6265
+ if (!Array.isArray(items)) return void 0;
6266
+ return items.map(projectTrackChangesItem).find((item) => item != null && entityRowMatchesRequest(item, activeId, story));
6267
+ }
6268
+ function selectedTextCoversTrackChangeText(selection$1, item) {
6269
+ const selected = normalizeTrackDecisionText(selection$1.quotedText);
6270
+ if (!selected) return false;
6271
+ const payload = trackChangesItemPayload(item);
6272
+ return [
6273
+ payload.insertedText,
6274
+ payload.deletedText,
6275
+ payload.excerpt
6276
+ ].some((value) => normalizeTrackDecisionText(value) === selected);
6277
+ }
6278
+ function normalizeTrackDecisionText(value) {
6279
+ return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
6280
+ }
5895
6281
  function activeChangePartialDecisionRoute() {
5896
6282
  const activeId = state.selection.activeChangeIds[0];
5897
6283
  if (!activeId) return "unavailable";
5898
- const items = readAllStoryTrackChanges();
5899
- if (!items) return "unavailable";
5900
6284
  const story = selectionStoryLocator(state.selection);
5901
- const item = items.find((candidate) => entityRowMatchesRequest(candidate, activeId, story));
6285
+ 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
6286
  if (!item) return "unavailable";
5903
- const type = trackChangesItemPayload(item).type;
5904
- const grouping = trackChangesItemPayload(item).grouping;
6287
+ const change = trackChangesItemPayload(item);
6288
+ const type = change.type;
6289
+ const grouping = change.grouping;
6290
+ if (selectedTextCoversTrackChangeText(state.selection, item)) return "id";
5905
6291
  if (type === "replacement" && grouping === "replacement-pair") return "range";
5906
6292
  return (type === "insertion" || type === "deletion" || type === "insert" || type === "delete") && grouping === "standalone" ? "range" : "id";
5907
6293
  }
6294
+ function executeTrackDecisionRangeWithIdFallback(kind, rangeTarget, changeId, story) {
6295
+ const fallback = () => callTrackDecisionTarget(kind, trackDecisionIdTarget(changeId, story), changeId, story);
6296
+ try {
6297
+ const result = callTrackDecisionTarget(kind, rangeTarget, null);
6298
+ if (isPromiseLike(result)) return settleCommandExecution(Promise.resolve(result).then((settled) => {
6299
+ return commandResultSucceeded(commandResultFromOperationResult(settled)) ? settled : fallback();
6300
+ }));
6301
+ return settleCommandExecution(commandResultSucceeded(commandResultFromOperationResult(result)) ? result : fallback());
6302
+ } catch {
6303
+ return false;
6304
+ }
6305
+ }
5908
6306
  function resolveTrackDecisionTarget(command, payload) {
5909
6307
  if (command.scope === "all") return {
5910
- target: { scope: "all" },
6308
+ target: { kind: "all" },
5911
6309
  changeId: null
5912
6310
  };
5913
6311
  if (typeof payload === "string" && payload.length > 0) return {
@@ -5919,26 +6317,27 @@ function createSuperDocUI(options) {
5919
6317
  };
5920
6318
  if (payload && typeof payload === "object") {
5921
6319
  const record = payload;
5922
- if (record.target && typeof record.target === "object") return {
5923
- target: record.target,
5924
- changeId: readTrackDecisionTargetId(record.target)
5925
- };
6320
+ if (record.target && typeof record.target === "object") {
6321
+ const target = record.target;
6322
+ return {
6323
+ target,
6324
+ changeId: readTrackDecisionTargetId(target),
6325
+ story: target.story
6326
+ };
6327
+ }
5926
6328
  const id = typeof record.changeId === "string" ? record.changeId : record.id;
5927
6329
  if (typeof id === "string" && id.length > 0) return {
5928
- target: {
5929
- kind: "id",
5930
- id
5931
- },
5932
- changeId: id
6330
+ target: trackDecisionIdTarget(id, record.story),
6331
+ changeId: id,
6332
+ story: record.story
5933
6333
  };
5934
6334
  }
5935
6335
  const selectedId = state.selection.activeChangeIds[0];
6336
+ const story = nonBodySelectionStoryLocator(state.selection);
5936
6337
  return selectedId ? {
5937
- target: {
5938
- kind: "id",
5939
- id: selectedId
5940
- },
5941
- changeId: selectedId
6338
+ target: trackDecisionIdTarget(selectedId, story),
6339
+ changeId: selectedId,
6340
+ story
5942
6341
  } : null;
5943
6342
  }
5944
6343
  function executeTrackDecisionCommand(command, payload) {
@@ -5952,13 +6351,23 @@ function createSuperDocUI(options) {
5952
6351
  if (route === "unavailable") return false;
5953
6352
  if (route === "range") {
5954
6353
  const target = trackDecisionRangeFromSelection(state.selection);
5955
- return target ? executeTrackDecisionTarget(command.kind, target, null) : false;
6354
+ const story = nonBodySelectionStoryLocator(state.selection);
6355
+ return target ? executeTrackDecisionRangeWithIdFallback(command.kind, target, state.selection.activeChangeIds[0], story) : false;
6356
+ }
6357
+ if (route === "id") {
6358
+ const activeId = state.selection.activeChangeIds[0];
6359
+ const selectedStory = selectionStoryLocator(state.selection);
6360
+ const story = selectedStory && storyLocatorSignature(selectedStory) !== storyLocatorSignature(null) ? selectedStory : void 0;
6361
+ return executeTrackDecision(command.kind, story ? {
6362
+ id: activeId,
6363
+ story
6364
+ } : activeId);
5956
6365
  }
5957
6366
  }
5958
6367
  }
5959
6368
  const resolved = resolveTrackDecisionTarget(command, payload);
5960
6369
  if (!resolved) return false;
5961
- return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId);
6370
+ return executeTrackDecisionTarget(command.kind, resolved.target, resolved.changeId, resolved.story);
5962
6371
  }
5963
6372
  function executeListToggleCommand(kind, payload) {
5964
6373
  const lists = getEditCommands()?.lists;
@@ -6142,13 +6551,11 @@ function createSuperDocUI(options) {
6142
6551
  };
6143
6552
  const finalizeCommandSettlement = (promise, onSettled) => promise.then((settled) => {
6144
6553
  onSettled?.(settled);
6145
- invalidateDocumentContent();
6146
- recompute();
6554
+ invalidateAfterCommandSettlement();
6147
6555
  return settled;
6148
6556
  }, () => {
6149
6557
  onSettled?.(false);
6150
- invalidateDocumentContent();
6151
- recompute();
6558
+ invalidateAfterCommandSettlement();
6152
6559
  return false;
6153
6560
  });
6154
6561
  const settleCommandExecution = (result, onSettled) => {
@@ -6171,8 +6578,7 @@ function createSuperDocUI(options) {
6171
6578
  }
6172
6579
  onSettled?.(settled);
6173
6580
  lastCommandSettlement = Promise.resolve(settled);
6174
- invalidateDocumentContent();
6175
- recompute();
6581
+ invalidateAfterCommandSettlement();
6176
6582
  return settled;
6177
6583
  };
6178
6584
  const normalizeWorkflowReceipt = (value, fallback) => {
@@ -7334,6 +7740,14 @@ function createSuperDocUI(options) {
7334
7740
  if (typeof helper?.apply !== "function") return { reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable };
7335
7741
  return { helper };
7336
7742
  };
7743
+ const activateContentControlChrome = (id) => {
7744
+ const host = getHost();
7745
+ if (typeof host?.getHandles !== "function") return;
7746
+ try {
7747
+ const contentControls$1 = (host.getHandles()?.editing)?.contentControls;
7748
+ if (typeof contentControls$1?.activate === "function") contentControls$1.activate({ id });
7749
+ } catch {}
7750
+ };
7337
7751
  const selection = {
7338
7752
  get: selectionSnap.get,
7339
7753
  getSnapshot: selectionSnap.getSnapshot,
@@ -7478,11 +7892,12 @@ function createSuperDocUI(options) {
7478
7892
  reason: SUPERDOC_UI_REASONS.hostCapabilityUnavailable
7479
7893
  };
7480
7894
  try {
7481
- const result = await Promise.resolve(scroll.call(host, {
7895
+ const input = {
7482
7896
  target,
7483
7897
  block: options$1?.block ?? "center",
7484
7898
  behavior: options$1?.behavior ?? "smooth"
7485
- }));
7899
+ };
7900
+ const result = await Promise.resolve(options$1?.shouldContinue ? scroll.call(host, input, options$1.shouldContinue) : scroll.call(host, input));
7486
7901
  if (result && typeof result === "object" && result.success === false) return {
7487
7902
  success: false,
7488
7903
  ok: false,
@@ -7512,19 +7927,32 @@ function createSuperDocUI(options) {
7512
7927
  story: requestedStory
7513
7928
  };
7514
7929
  };
7515
- const fromList = readEntityTarget(loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory)));
7516
- if (fromList) return withRequestedStory(fromList);
7930
+ const readTarget = namespace === "trackChanges" ? readTrackedChangeNavigationTarget : readEntityTarget;
7931
+ const loadedRow = loaded.find((row) => entityRowMatchesRequest(row, id, requestedStory));
7932
+ const loadedRecord = loadedRow && typeof loadedRow === "object" ? loadedRow : null;
7933
+ const moveSide = namespace === "trackChanges" && loadedRecord?.type === "move" && (loadedRecord.subtype === "move-to" || loadedRecord.subtype === "move-from") ? loadedRecord.subtype : null;
7934
+ const trackedChangeLookupId = moveSide && typeof loadedRecord?.trackedChangeCanonicalId === "string" ? loadedRecord.trackedChangeCanonicalId : id;
7935
+ const readResolvedTarget = (row) => {
7936
+ if (!moveSide) return readTarget(row);
7937
+ const moveTarget = readEntityTarget(row);
7938
+ if (moveTarget?.kind !== "move") return null;
7939
+ const sideTarget = moveSide === "move-to" ? moveTarget.destination : moveTarget.source;
7940
+ return isHostGeometryTarget(sideTarget) ? sideTarget : null;
7941
+ };
7942
+ const fromList = readTarget(loadedRow);
7943
+ const listTargetIsAuthoritative = namespace !== "trackChanges" || !moveSide && isHostGeometryTarget(readEntityTarget(loadedRow));
7944
+ if (fromList && listTargetIsAuthoritative) return withRequestedStory(fromList);
7517
7945
  const api = getDoc()?.[namespace];
7518
7946
  const input = namespace === "comments" ? { commentId: id } : {
7519
- id,
7947
+ id: trackedChangeLookupId,
7520
7948
  ...requestedStory ? { story: requestedStory } : {}
7521
7949
  };
7522
7950
  const get = api?.get;
7523
- if (typeof get !== "function") return null;
7951
+ if (typeof get !== "function") return withRequestedStory(fromList);
7524
7952
  try {
7525
- return withRequestedStory(readEntityTarget(await Promise.resolve(get.call(api, input))));
7953
+ return withRequestedStory(readResolvedTarget(await Promise.resolve(get.call(api, input))) ?? fromList);
7526
7954
  } catch {
7527
- return null;
7955
+ return withRequestedStory(fromList);
7528
7956
  }
7529
7957
  };
7530
7958
  function patchCommentStatus(commentId, status) {
@@ -7579,10 +8007,14 @@ function createSuperDocUI(options) {
7579
8007
  const op = commentsApi?.create;
7580
8008
  if (typeof op !== "function") return failedReceipt("comments.create is unavailable.");
7581
8009
  const fallback = failedReceipt("comments.create failed.");
7582
- return safeCall(() => settleWorkflowReceipt(op.call(commentsApi, {
7583
- target: capture.target ?? capture.selectionTarget,
7584
- text: input.text
7585
- }), fallback), fallback);
8010
+ return safeCall(() => {
8011
+ const target = capture && typeof capture === "object" ? capture.target ?? capture.selectionTarget : null;
8012
+ if (target == null) return failedReceipt("A range selection is required to comment.", "NO_SELECTION");
8013
+ return settleWorkflowReceipt(op.call(commentsApi, {
8014
+ target,
8015
+ text: input.text
8016
+ }), fallback);
8017
+ }, fallback);
7586
8018
  },
7587
8019
  createFromSelection: (input) => {
7588
8020
  const refusal = commentWriteRefusal();
@@ -7614,6 +8046,18 @@ function createSuperDocUI(options) {
7614
8046
  text: input.text
7615
8047
  }), fallback), fallback);
7616
8048
  },
8049
+ edit: (commentId, input) => {
8050
+ const refusal = commentWriteRefusal();
8051
+ if (refusal) return refusal;
8052
+ const commentsApi = getDoc()?.comments;
8053
+ const op = commentsApi?.patch;
8054
+ if (typeof op !== "function") return failedReceipt("comments.patch is unavailable.");
8055
+ const fallback = failedReceipt("comments.patch failed.");
8056
+ return safeCall(() => settleWorkflowReceipt(op.call(commentsApi, {
8057
+ commentId,
8058
+ text: input.text
8059
+ }), fallback), fallback);
8060
+ },
7617
8061
  resolve: (commentId) => patchCommentStatus(commentId, "resolved"),
7618
8062
  reopen: (commentId) => patchCommentStatus(commentId, "active"),
7619
8063
  delete: (commentId) => {
@@ -7658,10 +8102,12 @@ function createSuperDocUI(options) {
7658
8102
  };
7659
8103
  const trackChangesSub = sliceHandle((s) => s.trackChanges);
7660
8104
  const navigateTrackChange = (step, options$1) => {
7661
- const rows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
7662
- if (rows.length === 0) return null;
8105
+ const allRows = trackChangesSub.get().items.filter((item) => readEntityId(item) !== null);
8106
+ if (allRows.length === 0) return null;
7663
8107
  const currentId = state.trackChanges.activeId;
7664
8108
  const currentStory = explicitActiveChange?.id === currentId ? explicitActiveChange.story : void 0;
8109
+ const bodyRows = allRows.filter((row) => readEntityRequestStory(row) == null);
8110
+ const rows = currentStory || bodyRows.length === 0 ? allRows : bodyRows;
7665
8111
  let currentIdx = currentId ? rows.findIndex((row) => entityRowMatchesRequest(row, currentId, currentStory)) : -1;
7666
8112
  if (currentIdx === -1 && currentId && currentStory) currentIdx = rows.findIndex((row) => entityRowMatchesRequest(row, currentId));
7667
8113
  const nextRow = rows[currentIdx === -1 ? step > 0 ? 0 : rows.length - 1 : (currentIdx + step + rows.length) % rows.length];
@@ -7686,33 +8132,41 @@ function createSuperDocUI(options) {
7686
8132
  if (activeId == null) return { success: false };
7687
8133
  const optimisticActiveChange = explicitActiveChange;
7688
8134
  const optimisticRevision = explicitActiveChangeRevision;
7689
- const isCurrent = () => explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8135
+ const isCurrent = () => queuedTrackChangeNavigationInvalidation === requestedAtInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
7690
8136
  const rollback = () => {
7691
8137
  if (!isCurrent()) return;
7692
8138
  setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
7693
8139
  recompute();
7694
8140
  };
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;
8141
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.add(optimisticActiveChange);
7702
8142
  try {
7703
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
8143
+ const target = await resolveEntityTarget("trackChanges", activeId, trackChangesSub.get().items, optimisticActiveChange?.story ? { story: optimisticActiveChange.story } : void 0);
7704
8144
  if (!isCurrent()) return { success: false };
7705
- if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8145
+ if (!target) {
7706
8146
  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));
8147
+ return { success: false };
7715
8148
  }
8149
+ trackedChangeNavigationInFlight += 1;
8150
+ try {
8151
+ const result = await scrollTargetIntoView(target, {
8152
+ behavior: "auto",
8153
+ shouldContinue: isCurrent
8154
+ });
8155
+ if (!isCurrent()) return { success: false };
8156
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8157
+ rollback();
8158
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8159
+ } else mirrorTrackedChangeFocusToHost(optimisticActiveChange);
8160
+ return { success: result.ok };
8161
+ } finally {
8162
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8163
+ if (trackedChangeNavigationInFlight === 0) {
8164
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8165
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8166
+ }
8167
+ }
8168
+ } finally {
8169
+ if (optimisticActiveChange) pendingTrackChangeRevealFocuses.delete(optimisticActiveChange);
7716
8170
  }
7717
8171
  };
7718
8172
  const navigateAndScroll = (step) => {
@@ -7744,8 +8198,8 @@ function createSuperDocUI(options) {
7744
8198
  },
7745
8199
  accept: (changeId) => executeTrackDecision("accept", changeId),
7746
8200
  reject: (changeId) => executeTrackDecision("reject", changeId),
7747
- acceptAll: () => executeTrackDecisionTarget("accept", { scope: "all" }, null),
7748
- rejectAll: () => executeTrackDecisionTarget("reject", { scope: "all" }, null),
8201
+ acceptAll: () => executeTrackDecisionTarget("accept", { kind: "all" }, null),
8202
+ rejectAll: () => executeTrackDecisionTarget("reject", { kind: "all" }, null),
7749
8203
  next: () => navigateTrackChange(1),
7750
8204
  previous: () => navigateTrackChange(-1),
7751
8205
  navigateNext: () => navigateAndScroll(1),
@@ -7754,7 +8208,7 @@ function createSuperDocUI(options) {
7754
8208
  if (!input || typeof input !== "object" || typeof input.x !== "number" || typeof input.y !== "number") return null;
7755
8209
  const hits = entityAt(input);
7756
8210
  if (hits.length === 0) return null;
7757
- const bodyItems = trackChangesSub.get().items;
8211
+ const items = trackChangesSub.get().items;
7758
8212
  let allStoryItems;
7759
8213
  for (const hit of hits) {
7760
8214
  if (hit.type !== "trackedChange") continue;
@@ -7763,7 +8217,7 @@ function createSuperDocUI(options) {
7763
8217
  if (allStoryItems === void 0) allStoryItems = readAllStoryTrackChanges();
7764
8218
  if (!allStoryItems) continue;
7765
8219
  candidates = allStoryItems;
7766
- } else candidates = bodyItems;
8220
+ } else candidates = items;
7767
8221
  const item = candidates.find((candidate) => entityRowMatchesRequest(candidate, hit.id, hit.story));
7768
8222
  if (item) return hit.story ? {
7769
8223
  id: item.id,
@@ -7797,10 +8251,12 @@ function createSuperDocUI(options) {
7797
8251
  if (!allStoryItems.some((row) => entityRowMatchesRequest(row, publicId, requested.story))) return false;
7798
8252
  requested.id = publicId;
7799
8253
  } 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;
8254
+ const items = trackChangesSub.get().items;
8255
+ const publicId = buildTrackedChangeIdContext(items).toPublicId(requested.id) ?? requested.id;
8256
+ const item = items.find((candidate) => readEntityId(candidate) === publicId);
8257
+ if (!item) return false;
7803
8258
  requested.id = publicId;
8259
+ requested.story = readEntityRequestStory(item);
7804
8260
  }
7805
8261
  if (paintedEntityId !== requested.id) requested.paintedEntityId = paintedEntityId;
7806
8262
  setExplicitActiveChange(requested);
@@ -7814,47 +8270,65 @@ function createSuperDocUI(options) {
7814
8270
  ok: false,
7815
8271
  reason: getEditor() ? SUPERDOC_UI_REASONS.documentApiUnavailable : SUPERDOC_UI_REASONS.notReady
7816
8272
  };
8273
+ queuedTrackChangeNavigationInvalidation += 1;
8274
+ trackChangeRevealInvalidation += 1;
8275
+ const requestedAtInvalidation = trackChangeRevealInvalidation;
7817
8276
  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
- };
8277
+ const publicId = buildTrackedChangeIdContext(loadedItems).toPublicId(changeId) ?? changeId;
8278
+ const story = readEntityRequestStory(loadedItems.find((item) => readEntityId(item) === publicId));
7825
8279
  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();
8280
+ const requestedActiveChange = {
8281
+ id: publicId,
8282
+ ...story ? { story } : {},
8283
+ ...publicId !== changeId ? { paintedEntityId: changeId } : {}
7839
8284
  };
7840
- trackedChangeNavigationInFlight += 1;
8285
+ pendingTrackChangeRevealFocuses.add(requestedActiveChange);
7841
8286
  try {
7842
- const result = await scrollTargetIntoView(target, { behavior: "auto" });
7843
- if (!isCurrent()) return {
8287
+ const target = await resolveEntityTarget("trackChanges", publicId, loadedItems, story ? { story } : void 0);
8288
+ if (trackChangeRevealInvalidation !== requestedAtInvalidation) return {
7844
8289
  success: false,
7845
8290
  ok: false
7846
8291
  };
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));
8292
+ if (!target) return {
8293
+ success: false,
8294
+ ok: false,
8295
+ reason: SUPERDOC_UI_REASONS.targetUnresolved
8296
+ };
8297
+ setExplicitActiveChange(requestedActiveChange);
8298
+ const revealInvalidation = trackChangeRevealInvalidation;
8299
+ const optimisticActiveChange = explicitActiveChange;
8300
+ const optimisticRevision = explicitActiveChangeRevision;
8301
+ const isCurrent = () => trackChangeRevealInvalidation === revealInvalidation && explicitActiveChange === optimisticActiveChange && explicitActiveChangeRevision === optimisticRevision;
8302
+ const rollback = () => {
8303
+ if (!isCurrent()) return;
8304
+ setExplicitActiveChange(previousActiveChange, { invalidateQueuedNavigation: false });
8305
+ recompute();
8306
+ };
8307
+ recompute();
8308
+ trackedChangeNavigationInFlight += 1;
8309
+ try {
8310
+ const result = await scrollTargetIntoView(target, {
8311
+ behavior: "auto",
8312
+ shouldContinue: isCurrent
8313
+ });
8314
+ if (!isCurrent()) return {
8315
+ success: false,
8316
+ ok: false
8317
+ };
8318
+ if (!result.ok && result.reason !== SUPERDOC_UI_REASONS.targetNotVisible) {
8319
+ rollback();
8320
+ mirrorTrackedChangeFocusToHost(previousActiveChange);
8321
+ } else mirrorTrackedChangeFocusToHost(requestedActiveChange);
8322
+ return result;
8323
+ } finally {
8324
+ trackedChangeNavigationInFlight = Math.max(0, trackedChangeNavigationInFlight - 1);
8325
+ if (trackedChangeNavigationInFlight === 0) {
8326
+ if (!isCurrent()) mirrorTrackedChangeFocusToHost(explicitActiveChange);
8327
+ else if (currentHostReviewSource) syncTrackedChangeFocusFromHostReviewTarget(readHostActiveReviewTarget(currentHostReviewSource));
8328
+ }
7857
8329
  }
8330
+ } finally {
8331
+ pendingTrackChangeRevealFocuses.delete(requestedActiveChange);
7858
8332
  }
7859
8333
  }
7860
8334
  };
@@ -7879,7 +8353,7 @@ function createSuperDocUI(options) {
7879
8353
  const callTrackDecisionTarget = (kind, target, changeId, story) => {
7880
8354
  if (reviewMutationsAreReadOnly()) return false;
7881
8355
  const tcApi = getDoc()?.trackChanges;
7882
- const isAllTarget = target.scope === "all";
8356
+ const isAllTarget = target.kind === "all" || target.scope === "all";
7883
8357
  const isRangeTarget = target.kind === "range";
7884
8358
  const isMultiIdTarget = target.kind === "ids";
7885
8359
  if (bulkTrackDecisionBlockedReason({
@@ -7982,6 +8456,7 @@ function createSuperDocUI(options) {
7982
8456
  success: false,
7983
8457
  reason: "not-reachable"
7984
8458
  };
8459
+ activateContentControlChrome(id);
7985
8460
  return { success: true };
7986
8461
  }
7987
8462
  };
@@ -8109,8 +8584,8 @@ function createSuperDocUI(options) {
8109
8584
  const rawHits = collectEntityHitsFromChain(startEls[0], container);
8110
8585
  for (let i = 1; i < startEls.length; i += 1) for (const hit of collectEntityHitsFromChain(startEls[i], container)) rawHits.push(hit);
8111
8586
  if (rawHits.length === 0) return rawHits;
8112
- const bodyItems = trackChangesSub.get().items;
8113
- const bodyIdContext = buildTrackedChangeIdContext(bodyItems);
8587
+ const items = trackChangesSub.get().items;
8588
+ const idContext = buildTrackedChangeIdContext(items);
8114
8589
  let allStoryItems;
8115
8590
  const seen = /* @__PURE__ */ new Set();
8116
8591
  const hits = [];
@@ -8130,7 +8605,7 @@ function createSuperDocUI(options) {
8130
8605
  publicId = buildStoryScopedTrackedChangeIdContext(allStoryItems, story).toPublicId(hit.id);
8131
8606
  if (!publicId || !allStoryItems.some((item) => entityRowMatchesRequest(item, publicId, story))) continue;
8132
8607
  } else {
8133
- publicId = bodyIdContext.toPublicId(hit.id);
8608
+ publicId = idContext.toPublicId(hit.id);
8134
8609
  if (!publicId) continue;
8135
8610
  }
8136
8611
  const key = `trackedChange:${publicId}`;
@@ -8929,6 +9404,7 @@ function createSuperDocUI(options) {
8929
9404
  const destroy = () => {
8930
9405
  if (disposed) return;
8931
9406
  disposed = true;
9407
+ releaseSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost, uiTrackedChangesCatalogState);
8932
9408
  reviewMutationReconciler.reset();
8933
9409
  clearAuthoritativeTrackChangesRetry();
8934
9410
  if (foregroundAsyncRetryTimer) {
@@ -9185,4 +9661,4 @@ var DEFAULT_FONT_SIZE_OPTIONS = [
9185
9661
  function commandIsActive(descriptor, selection) {
9186
9662
  return descriptor.activeMark ? selection.activeMarks.includes(descriptor.activeMark) : false;
9187
9663
  }
9188
- export { createV2ReviewMutationReconciler as a, isV2EditableTextMutationEvent as i, shallowEqual as n, getV2TrackedChangeMutationImpact as o, BUILT_IN_COMMAND_IDS as r, composeAuthorColorResolver as s, createSuperDocUI as t };
9664
+ export { createV2ReviewMutationReconciler as a, DOM_CLASS_NAMES as c, isV2EditableTextMutationEvent as i, shallowEqual as n, getV2TrackedChangeMutationImpact as o, BUILT_IN_COMMAND_IDS as r, composeAuthorColorResolver as s, createSuperDocUI as t };