superdoc 2.4.0-next.2 → 2.4.0-next.4

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.
@@ -334,14 +334,21 @@ function getV2TrackedChangeMutationImpact(event) {
334
334
  });
335
335
  }
336
336
  for (const id of removedIds) upsertIds.delete(id);
337
- if (upsertIds.size === 0 && removedIds.size === 0) return null;
337
+ const allResolved = readAllResolvedFact(event, payload);
338
+ if (upsertIds.size === 0 && removedIds.size === 0 && !allResolved) return null;
338
339
  return {
339
340
  upsertIds,
340
341
  removedIds,
341
342
  remappedPairs,
342
- reconcileMode: event.origin === "history" ? "authoritative" : "targeted"
343
+ reconcileMode: event.origin === "history" ? "authoritative" : "targeted",
344
+ ...allResolved ? { allResolved } : {}
343
345
  };
344
346
  }
347
+ function readAllResolvedFact(event, receipt) {
348
+ const fact = event?.trackedChangeAllResolved;
349
+ 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;
350
+ return fact;
351
+ }
345
352
  var normalizeIds = (values) => new Set(Array.from(values ?? []).filter((value) => typeof value === "string" && value.length > 0));
346
353
  var itemId = (item) => {
347
354
  if (typeof item?.id === "string" && item.id.length > 0) return item.id;
@@ -352,10 +359,20 @@ var itemId = (item) => {
352
359
  var contextMatches = (left, right) => left?.adapter === right?.adapter && left?.documentId === right?.documentId && left?.editor === right?.editor && left?.reconcileToken === right?.reconcileToken;
353
360
  function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onReconciled }) {
354
361
  const pendingUpserts = /* @__PURE__ */ new Map();
362
+ let pendingAllResolved = null;
355
363
  let sequence = 0;
356
364
  let generation = 0;
357
365
  let flushInFlight = null;
358
366
  let flushRequested = false;
367
+ const settlementWaiters = [];
368
+ const resolveSettlementsThrough = (version, value) => {
369
+ for (let index = settlementWaiters.length - 1; index >= 0; index -= 1) {
370
+ const waiter = settlementWaiters[index];
371
+ if (waiter.generation !== generation || waiter.version > version) continue;
372
+ settlementWaiters.splice(index, 1);
373
+ waiter.resolve(value === true && waiter.requiresAllResolved ? pendingAllResolved == null : value);
374
+ }
375
+ };
359
376
  const isCurrent = (capturedGeneration, context) => generation === capturedGeneration && contextMatches(context, getContext());
360
377
  const clearResolved = (ids, snapshot) => {
361
378
  const clearedIds = /* @__PURE__ */ new Set();
@@ -369,6 +386,10 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
369
386
  const readySnapshot = (reconcileMode) => new Map([...pendingUpserts].filter(([, entry]) => entry.ready === true && entry.reconcileMode === reconcileMode));
370
387
  const markRenderGatedReady = () => {
371
388
  let changed = false;
389
+ if (pendingAllResolved?.gate === "render" && !pendingAllResolved.ready) {
390
+ pendingAllResolved.ready = true;
391
+ changed = true;
392
+ }
372
393
  for (const entry of pendingUpserts.values()) {
373
394
  if (entry.gate !== "render" || entry.ready) continue;
374
395
  entry.ready = true;
@@ -378,6 +399,10 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
378
399
  };
379
400
  const markEntriesReadyThrough = (maxVersion) => {
380
401
  let changed = false;
402
+ if (pendingAllResolved && pendingAllResolved.version <= maxVersion && !pendingAllResolved.ready) {
403
+ pendingAllResolved.ready = true;
404
+ changed = true;
405
+ }
381
406
  for (const entry of pendingUpserts.values()) {
382
407
  if (entry.version > maxVersion || entry.ready) continue;
383
408
  entry.ready = true;
@@ -428,14 +453,31 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
428
453
  };
429
454
  const flush = () => {
430
455
  if (flushInFlight) return flushInFlight;
456
+ const allResolvedSnapshot = pendingAllResolved?.ready ? pendingAllResolved : null;
431
457
  const targetedSnapshot = readySnapshot("targeted");
432
458
  const authoritativeSnapshot = readySnapshot("authoritative");
433
- if (targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
459
+ if (!allResolvedSnapshot && targetedSnapshot.size === 0 && authoritativeSnapshot.size === 0) return null;
434
460
  const context = getContext();
435
461
  if (!context?.adapter) return null;
436
462
  flushRequested = false;
437
463
  const capturedGeneration = generation;
438
464
  flushInFlight = Promise.resolve().then(async () => {
465
+ if (allResolvedSnapshot) {
466
+ let result = null;
467
+ try {
468
+ result = await reconcile(context, {
469
+ upsertIds: /* @__PURE__ */ new Set(),
470
+ removedIds: /* @__PURE__ */ new Set(),
471
+ allResolved: allResolvedSnapshot.fact,
472
+ reconcileMode: "targeted"
473
+ });
474
+ } catch {}
475
+ if (!isCurrent(capturedGeneration, context)) return;
476
+ if (result?.ok === true) {
477
+ if (pendingAllResolved === allResolvedSnapshot) pendingAllResolved = null;
478
+ await notifyReconciled(context, /* @__PURE__ */ new Set(), result, true);
479
+ }
480
+ }
439
481
  if (authoritativeSnapshot.size > 0) await reconcileAuthoritativeSnapshot(context, authoritativeSnapshot, capturedGeneration).catch(() => void 0);
440
482
  if (targetedSnapshot.size > 0) await reconcileTargetedSnapshot(context, targetedSnapshot, capturedGeneration).catch(() => void 0);
441
483
  }).catch(() => {}).finally(() => {
@@ -446,6 +488,15 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
446
488
  };
447
489
  const enqueueWithGate = (impact, gate) => {
448
490
  if (!impact) return;
491
+ if (impact.allResolved) {
492
+ pendingUpserts.clear();
493
+ pendingAllResolved = {
494
+ fact: impact.allResolved,
495
+ version: ++sequence,
496
+ gate,
497
+ ready: false
498
+ };
499
+ }
449
500
  const reconcileMode = impact.reconcileMode === "authoritative" ? "authoritative" : "targeted";
450
501
  const removedIds = normalizeIds(impact.removedIds);
451
502
  const upsertIds = normalizeIds(impact.upsertIds);
@@ -460,7 +511,7 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
460
511
  else pendingUpserts.delete(id);
461
512
  }
462
513
  const context = getContext();
463
- if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter) try {
514
+ if (removedIds.size > 0 && upsertIds.size === 0 && context?.adapter && !impact.allResolved) try {
464
515
  Promise.resolve(reconcile(context, {
465
516
  upsertIds: /* @__PURE__ */ new Set(),
466
517
  removedIds
@@ -487,20 +538,34 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
487
538
  enqueueAfterPaint(impact, waitForPaint) {
488
539
  if (!impact || typeof waitForPaint !== "function") {
489
540
  enqueue(impact);
490
- return;
541
+ return Promise.resolve(false);
491
542
  }
492
543
  enqueueWithGate(impact, "exact-paint");
493
544
  const context = getContext();
494
- if (!context?.adapter) return;
495
545
  const capturedGeneration = generation;
496
546
  const capturedSequence = sequence;
547
+ if (!context?.adapter) {
548
+ markEntriesReadyThrough(capturedSequence);
549
+ return Promise.resolve(false);
550
+ }
551
+ const completion = new Promise((resolve) => {
552
+ settlementWaiters.push({
553
+ generation: capturedGeneration,
554
+ version: capturedSequence,
555
+ requiresAllResolved: Boolean(impact.allResolved),
556
+ resolve
557
+ });
558
+ });
497
559
  const impactedIds = new Set([...normalizeIds(impact.upsertIds), ...normalizeIds(impact.removedIds)]);
498
560
  const capturedEntries = new Map([...impactedIds].flatMap((id) => {
499
561
  const entry = pendingUpserts.get(id);
500
562
  return entry ? [[id, entry]] : [];
501
563
  }));
502
- Promise.resolve().then(() => waitForPaint()).then(() => {
503
- if (!isCurrent(capturedGeneration, context)) return;
564
+ Promise.resolve().then(() => waitForPaint()).then(async () => {
565
+ if (!isCurrent(capturedGeneration, context)) {
566
+ resolveSettlementsThrough(capturedSequence, false);
567
+ return;
568
+ }
504
569
  let madeReady = markRenderGatedReady();
505
570
  if (markEntriesReadyThrough(capturedSequence)) madeReady = true;
506
571
  for (const [id, entry] of capturedEntries) {
@@ -509,16 +574,35 @@ function createV2ReviewMutationReconciler({ getContext, reconcile, hydrate, onRe
509
574
  entry.ready = true;
510
575
  }
511
576
  if (flushInFlight && (madeReady || capturedSequence > 0)) flushRequested = true;
512
- flush();
577
+ const preceding = flushInFlight;
578
+ const pending = flush();
579
+ if (pending) await pending;
580
+ if (preceding) {
581
+ const followUp = flushInFlight;
582
+ if (followUp && followUp !== preceding) await followUp;
583
+ }
584
+ if (!isCurrent(capturedGeneration, context)) {
585
+ resolveSettlementsThrough(capturedSequence, false);
586
+ return;
587
+ }
588
+ resolveSettlementsThrough(capturedSequence, true);
513
589
  }).catch(() => {
514
- if (!isCurrent(capturedGeneration, context)) return;
590
+ if (!isCurrent(capturedGeneration, context)) {
591
+ resolveSettlementsThrough(capturedSequence, false);
592
+ return;
593
+ }
515
594
  for (const [id, entry] of capturedEntries) if (pendingUpserts.get(id) === entry) entry.gate = "render";
595
+ if (pendingAllResolved?.gate === "exact-paint") pendingAllResolved.gate = "render";
596
+ resolveSettlementsThrough(capturedSequence, false);
516
597
  });
598
+ return completion;
517
599
  },
518
600
  onRender,
519
601
  reset() {
602
+ for (const waiter of settlementWaiters.splice(0)) waiter.resolve(false);
520
603
  generation += 1;
521
604
  pendingUpserts.clear();
605
+ pendingAllResolved = null;
522
606
  flushRequested = false;
523
607
  },
524
608
  getPendingIds() {
@@ -1716,6 +1800,30 @@ const INLINE_PROPERTY_KEYS_BY_STORAGE = {
1716
1800
  mark: INLINE_PROPERTY_REGISTRY.filter((entry) => entry.storage === "mark").map((entry) => entry.key),
1717
1801
  runAttribute: INLINE_PROPERTY_REGISTRY.filter((entry) => entry.storage === "runAttribute").map((entry) => entry.key)
1718
1802
  };
1803
+ var sharedUiTrackedChangesCatalogByHost = /* @__PURE__ */ new WeakMap();
1804
+ function acquireSharedUiTrackedChangesCatalog(host) {
1805
+ let state = sharedUiTrackedChangesCatalogByHost.get(host);
1806
+ if (!state) {
1807
+ state = {
1808
+ refCount: 0,
1809
+ generation: 0,
1810
+ abortController: new AbortController(),
1811
+ inFlight: null,
1812
+ activeMutationTokens: /* @__PURE__ */ new Set()
1813
+ };
1814
+ sharedUiTrackedChangesCatalogByHost.set(host, state);
1815
+ }
1816
+ state.refCount += 1;
1817
+ return state;
1818
+ }
1819
+ function releaseSharedUiTrackedChangesCatalog(host, state) {
1820
+ state.refCount = Math.max(0, state.refCount - 1);
1821
+ if (state.refCount > 0) return;
1822
+ state.abortController.abort("ui-controller-destroyed");
1823
+ state.inFlight = null;
1824
+ state.activeMutationTokens.clear();
1825
+ sharedUiTrackedChangesCatalogByHost.delete(host);
1826
+ }
1719
1827
  var SUPERDOC_UI_REASON_VALUES = new Set(Object.values(SUPERDOC_UI_REASONS));
1720
1828
  function coerceSuperDocUIReason(reason, fallback) {
1721
1829
  return typeof reason === "string" && SUPERDOC_UI_REASON_VALUES.has(reason) ? reason : fallback;
@@ -1812,12 +1920,12 @@ const HEAVY_DOC_READ_POLICY = [
1812
1920
  {
1813
1921
  key: "trackChanges",
1814
1922
  match: "exact",
1815
- note: "audited: all-story tracked-changes list; stale-served during load"
1923
+ note: "audited: shared all-story tracked-changes catalog; stale-served during load"
1816
1924
  },
1817
1925
  {
1818
1926
  key: "trackChanges:all",
1819
1927
  match: "exact",
1820
- note: "audited: all-story tracked-changes list; consumers already fail closed on non-ready"
1928
+ note: "audited: current-token validation over the shared all-story catalog; consumers fail closed on non-ready"
1821
1929
  },
1822
1930
  {
1823
1931
  key: "tables",
@@ -3276,6 +3384,63 @@ function createSuperDocUI(options) {
3276
3384
  };
3277
3385
  let documentMutationRevision = 0;
3278
3386
  let reviewMutationToken = {};
3387
+ const uiTrackedChangesCatalogHost = options.superdoc;
3388
+ const uiTrackedChangesCatalogState = acquireSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost);
3389
+ const supersedeUiTrackedChangesCatalogRead = () => {
3390
+ uiTrackedChangesCatalogState.abortController.abort("review-mutation-started");
3391
+ uiTrackedChangesCatalogState.inFlight = null;
3392
+ };
3393
+ const renewUiTrackedChangesCatalogRead = () => {
3394
+ uiTrackedChangesCatalogState.generation += 1;
3395
+ uiTrackedChangesCatalogState.abortController = new AbortController();
3396
+ uiTrackedChangesCatalogState.inFlight = null;
3397
+ };
3398
+ const beginUiReviewMutation = (token) => {
3399
+ const activeTokens = uiTrackedChangesCatalogState.activeMutationTokens;
3400
+ if (typeof token !== "string" || token.length === 0 || activeTokens.has(token)) return;
3401
+ const firstToken = activeTokens.size === 0;
3402
+ activeTokens.add(token);
3403
+ if (firstToken) supersedeUiTrackedChangesCatalogRead();
3404
+ };
3405
+ const settleUiReviewMutation = (token) => {
3406
+ const activeTokens = uiTrackedChangesCatalogState.activeMutationTokens;
3407
+ if (typeof token !== "string" || !activeTokens.delete(token)) return;
3408
+ if (activeTokens.size === 0) renewUiTrackedChangesCatalogRead();
3409
+ };
3410
+ const runUiTrackedChangesCatalogRead = (fallback) => {
3411
+ const v2TrackedChanges = getV2TrackedChanges();
3412
+ const listTrackedChanges = v2TrackedChanges?.listTrackedChanges;
3413
+ if (typeof listTrackedChanges !== "function") {
3414
+ if (getEditor()?.editorVersion === 2) return Promise.reject(/* @__PURE__ */ new Error("v2-tracked-changes-bridge-pending"));
3415
+ return fallback();
3416
+ }
3417
+ if (uiTrackedChangesCatalogState.activeMutationTokens.size > 0) return Promise.reject(/* @__PURE__ */ new Error("ui-review-catalog-superseded"));
3418
+ if ((getEditor()?.reviewHydration?.getDiagnostics?.())?.inFlight === true) return Promise.reject(/* @__PURE__ */ new Error("shell-review-hydration-in-flight"));
3419
+ const generation = uiTrackedChangesCatalogState.generation;
3420
+ const existing = uiTrackedChangesCatalogState.inFlight;
3421
+ if (existing?.generation === generation) return existing.promise;
3422
+ const signal = uiTrackedChangesCatalogState.abortController.signal;
3423
+ const validateResult = (result) => {
3424
+ if (signal.aborted || generation !== uiTrackedChangesCatalogState.generation || result?.reason === "review-hydration-superseded") throw new Error("ui-review-catalog-superseded");
3425
+ return result;
3426
+ };
3427
+ const rawResult = listTrackedChanges.call(v2TrackedChanges, {
3428
+ blocking: false,
3429
+ signal
3430
+ });
3431
+ if (!isPromiseLike(rawResult)) return validateResult(rawResult);
3432
+ const promise = Promise.resolve(rawResult).then(validateResult);
3433
+ uiTrackedChangesCatalogState.inFlight = {
3434
+ generation,
3435
+ promise
3436
+ };
3437
+ promise.then(() => {
3438
+ if (uiTrackedChangesCatalogState.inFlight?.promise === promise) uiTrackedChangesCatalogState.inFlight = null;
3439
+ }, () => {
3440
+ if (uiTrackedChangesCatalogState.inFlight?.promise === promise) uiTrackedChangesCatalogState.inFlight = null;
3441
+ });
3442
+ return promise;
3443
+ };
3279
3444
  let authoritativeTrackChangesPendingToken = null;
3280
3445
  let authoritativeTrackChangesRetryTimer = null;
3281
3446
  let authoritativeTrackChangesFailureCount = 0;
@@ -3287,6 +3452,7 @@ function createSuperDocUI(options) {
3287
3452
  let scheduleAuthoritativeTrackChangesRetry = (_reconcileToken) => void 0;
3288
3453
  let postDecisionTrackChangesToken = null;
3289
3454
  let postDecisionTrackChangeIds = /* @__PURE__ */ new Set();
3455
+ let allTrackedChangesResolvedToken = null;
3290
3456
  let selectionEpoch = 0;
3291
3457
  let lastCoordinatorEditor = null;
3292
3458
  const contentToken = () => `${editorIdentityId(getEditor())}|m${documentMutationRevision}`;
@@ -3365,6 +3531,23 @@ function createSuperDocUI(options) {
3365
3531
  };
3366
3532
  notifyPostDecisionTrackChanges(receipt);
3367
3533
  };
3534
+ const publishAllTrackedChangesResolved = (receipt) => {
3535
+ const previous = state.trackChanges;
3536
+ const changed = previous.items.length > 0 || previous.total !== 0 || previous.activeId !== null || previous.authors.length > 0;
3537
+ allTrackedChangesResolvedToken = contentToken();
3538
+ if (explicitActiveChange !== null) setExplicitActiveChange(null);
3539
+ state = {
3540
+ ...state,
3541
+ trackChanges: {
3542
+ ...previous,
3543
+ items: [],
3544
+ total: 0,
3545
+ activeId: null,
3546
+ authors: []
3547
+ }
3548
+ };
3549
+ if (changed) notifyPostDecisionTrackChanges(receipt);
3550
+ };
3368
3551
  const markPostDecisionTrackChanges = (ids, receipt) => {
3369
3552
  if (ids.size === 0) return;
3370
3553
  const token = contentToken();
@@ -4043,6 +4226,7 @@ function createSuperDocUI(options) {
4043
4226
  detachSourceLoading = null;
4044
4227
  sourceLoadingSubscriptionHost = null;
4045
4228
  clearPostDecisionTrackChanges();
4229
+ allTrackedChangesResolvedToken = null;
4046
4230
  };
4047
4231
  const invalidateDocumentContent = () => {
4048
4232
  const carryAuthoritativeTrackChangesHold = authoritativeTrackChangesPendingToken !== null;
@@ -4050,6 +4234,15 @@ function createSuperDocUI(options) {
4050
4234
  if (carryAuthoritativeTrackChangesHold) authoritativeTrackChangesPendingToken = contentToken();
4051
4235
  clearPostDecisionTrackChanges();
4052
4236
  };
4237
+ const invalidateAfterCommandSettlement = () => {
4238
+ const carryAllResolvedCatalog = allTrackedChangesResolvedToken === contentToken();
4239
+ invalidateDocumentContent();
4240
+ if (carryAllResolvedCatalog) {
4241
+ replaceTrackedChangeItemsInCache([]);
4242
+ allTrackedChangesResolvedToken = contentToken();
4243
+ }
4244
+ recompute();
4245
+ };
4053
4246
  const insertText = (text) => {
4054
4247
  if (typeof text !== "string" || text.length === 0) return failedReceipt("insertText requires a non-empty string.", "INVALID_INPUT");
4055
4248
  if (readDocumentMode() === "viewing") return failedReceipt("The document is read-only.", "DOCUMENT_READONLY");
@@ -4253,7 +4446,17 @@ function createSuperDocUI(options) {
4253
4446
  const { value, status } = authoritativeTrackChangesPendingToken === token ? {
4254
4447
  value: heldEntry?.hasSettled ? heldEntry.value : null,
4255
4448
  status: heldEntry?.hasSettled ? "stale" : "pending"
4256
- } : readAsync("trackChanges:all", token, () => tcApi?.list ? tcApi.list({ in: "all" }) : void 0, (raw) => {
4449
+ } : readAsync("trackChanges:all", token, () => {
4450
+ if (!tcApi?.list) return void 0;
4451
+ const fallback = () => tcApi.list({ in: "all" });
4452
+ const bridgeOwnsCatalog = typeof getV2TrackedChanges()?.listTrackedChanges === "function";
4453
+ const shared = runUiTrackedChangesCatalogRead(fallback);
4454
+ if (!bridgeOwnsCatalog) return shared;
4455
+ return Promise.resolve(shared).then((raw) => {
4456
+ const record = isLooseObject(raw) ? raw : null;
4457
+ return (Array.isArray(record?.items) ? record.items : []).some((item) => readEntityStory(item) == null) ? fallback() : raw;
4458
+ });
4459
+ }, (raw) => {
4257
4460
  return (raw && Array.isArray(raw.items) ? raw.items : []).map(projectTrackChangesItem).filter((item) => item != null);
4258
4461
  });
4259
4462
  return status === "ready" ? value : null;
@@ -4269,7 +4472,7 @@ function createSuperDocUI(options) {
4269
4472
  value: heldEntry?.hasSettled ? heldEntry.value : null,
4270
4473
  status: heldEntry?.hasSettled ? "stale" : "pending"
4271
4474
  } : readAsync("trackChanges", token, () => {
4272
- if (listTrackedChanges) return listTrackedChanges.call(v2TrackedChanges);
4475
+ if (listTrackedChanges) return runUiTrackedChangesCatalogRead(() => tcApi?.list?.());
4273
4476
  return tcApi?.list ? tcApi.list({ in: "all" }) : void 0;
4274
4477
  }, (raw) => raw && Array.isArray(raw.items) ? raw.items : []);
4275
4478
  const items = (value ?? []).map(projectTrackChangesItem).filter((item) => item != null).filter((item) => {
@@ -4292,7 +4495,7 @@ function createSuperDocUI(options) {
4292
4495
  }
4293
4496
  const publicIdItems = allStoryItems ?? items;
4294
4497
  const selectionIdContext = buildStoryScopedTrackedChangeIdContext(publicIdItems, selectionStory(selection$1));
4295
- const selectionActiveChangeIds = postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
4498
+ const selectionActiveChangeIds = allTrackedChangesResolvedToken === token ? [] : postDecisionIds ? selection$1.activeChangeIds.filter((id) => !postDecisionIds.has(id)) : selection$1.activeChangeIds;
4296
4499
  const selectionPublicChangeIds = selectionActiveChangeIds.map((id) => selectionIdContext.toPublicId(id) ?? id);
4297
4500
  const explicitActiveIdContext = explicitActiveChange?.story ? buildStoryScopedTrackedChangeIdContext(publicIdItems, explicitActiveChange.story) : buildTrackedChangeIdContext(publicIdItems);
4298
4501
  const explicitActiveId = explicitActiveChange ? explicitActiveIdContext.toPublicId(explicitActiveChange.id) ?? explicitActiveChange.id : null;
@@ -6055,6 +6258,20 @@ function createSuperDocUI(options) {
6055
6258
  try {
6056
6259
  const off = next.subscribe((event) => {
6057
6260
  const type = event?.type;
6261
+ if (type === "review-mutation:started") {
6262
+ beginUiReviewMutation(event.reviewMutation?.token);
6263
+ return;
6264
+ }
6265
+ if (type === "review-mutation:aborted") {
6266
+ settleUiReviewMutation(event.reviewMutation?.token);
6267
+ scheduleAsyncRefresh();
6268
+ return;
6269
+ }
6270
+ if (type === "mutation:rejected" && event.reviewMutation) {
6271
+ settleUiReviewMutation(event.reviewMutation.token);
6272
+ scheduleAsyncRefresh();
6273
+ }
6274
+ if (type === "mutation:committed" && event.reviewMutation) settleUiReviewMutation(event.reviewMutation.token);
6058
6275
  if (type === "source:complete" || type === "source:signals-complete") {
6059
6276
  if (authoritativeTrackChangesPendingToken !== null) {
6060
6277
  clearAuthoritativeTrackChangesRetry();
@@ -6070,6 +6287,13 @@ function createSuperDocUI(options) {
6070
6287
  if (type === "mutation:committed" || type === "save:completed" || type === "collaboration:remote-changed") {
6071
6288
  const impact = type === "mutation:committed" ? getV2TrackedChangeMutationImpact(event) : null;
6072
6289
  const supersedesPendingReviewReconcile = type === "mutation:committed" && reviewMutationReconciler.getPendingIds().size > 0;
6290
+ if (impact?.allResolved) {
6291
+ reviewMutationReconciler.reset();
6292
+ replaceTrackedChangeItemsInCache([]);
6293
+ authoritativeTrackChangesPendingToken = null;
6294
+ publishAllTrackedChangesResolved(event.receipt);
6295
+ return;
6296
+ }
6073
6297
  if (impact?.removedIds.size) markPostDecisionTrackChanges(new Set(impact.removedIds), event.receipt);
6074
6298
  const authoritativeHistoryImpact = impact?.reconcileMode === "authoritative" && impact.upsertIds.size > 0;
6075
6299
  if (authoritativeHistoryImpact) {
@@ -6303,7 +6527,7 @@ function createSuperDocUI(options) {
6303
6527
  }
6304
6528
  function resolveTrackDecisionTarget(command, payload) {
6305
6529
  if (command.scope === "all") return {
6306
- target: { scope: "all" },
6530
+ target: { kind: "all" },
6307
6531
  changeId: null
6308
6532
  };
6309
6533
  if (typeof payload === "string" && payload.length > 0) return {
@@ -6549,13 +6773,11 @@ function createSuperDocUI(options) {
6549
6773
  };
6550
6774
  const finalizeCommandSettlement = (promise, onSettled) => promise.then((settled) => {
6551
6775
  onSettled?.(settled);
6552
- invalidateDocumentContent();
6553
- recompute();
6776
+ invalidateAfterCommandSettlement();
6554
6777
  return settled;
6555
6778
  }, () => {
6556
6779
  onSettled?.(false);
6557
- invalidateDocumentContent();
6558
- recompute();
6780
+ invalidateAfterCommandSettlement();
6559
6781
  return false;
6560
6782
  });
6561
6783
  const settleCommandExecution = (result, onSettled) => {
@@ -6578,8 +6800,7 @@ function createSuperDocUI(options) {
6578
6800
  }
6579
6801
  onSettled?.(settled);
6580
6802
  lastCommandSettlement = Promise.resolve(settled);
6581
- invalidateDocumentContent();
6582
- recompute();
6803
+ invalidateAfterCommandSettlement();
6583
6804
  return settled;
6584
6805
  };
6585
6806
  const normalizeWorkflowReceipt = (value, fallback) => {
@@ -8175,8 +8396,8 @@ function createSuperDocUI(options) {
8175
8396
  },
8176
8397
  accept: (changeId) => executeTrackDecision("accept", changeId),
8177
8398
  reject: (changeId) => executeTrackDecision("reject", changeId),
8178
- acceptAll: () => executeTrackDecisionTarget("accept", { scope: "all" }, null),
8179
- rejectAll: () => executeTrackDecisionTarget("reject", { scope: "all" }, null),
8399
+ acceptAll: () => executeTrackDecisionTarget("accept", { kind: "all" }, null),
8400
+ rejectAll: () => executeTrackDecisionTarget("reject", { kind: "all" }, null),
8180
8401
  next: () => navigateTrackChange(1),
8181
8402
  previous: () => navigateTrackChange(-1),
8182
8403
  navigateNext: () => navigateAndScroll(1),
@@ -8330,7 +8551,7 @@ function createSuperDocUI(options) {
8330
8551
  const callTrackDecisionTarget = (kind, target, changeId, story) => {
8331
8552
  if (reviewMutationsAreReadOnly()) return false;
8332
8553
  const tcApi = getDoc()?.trackChanges;
8333
- const isAllTarget = target.scope === "all";
8554
+ const isAllTarget = target.kind === "all" || target.scope === "all";
8334
8555
  const isRangeTarget = target.kind === "range";
8335
8556
  const isMultiIdTarget = target.kind === "ids";
8336
8557
  if (bulkTrackDecisionBlockedReason({
@@ -9380,6 +9601,7 @@ function createSuperDocUI(options) {
9380
9601
  const destroy = () => {
9381
9602
  if (disposed) return;
9382
9603
  disposed = true;
9604
+ releaseSharedUiTrackedChangesCatalog(uiTrackedChangesCatalogHost, uiTrackedChangesCatalogState);
9383
9605
  reviewMutationReconciler.reset();
9384
9606
  clearAuthoritativeTrackChangesRetry();
9385
9607
  if (foregroundAsyncRetryTimer) {