superdoc 2.4.0-next.3 → 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.
@@ -6319,6 +6319,7 @@ function createStubReviewHydrationController() {
6319
6319
  setContext() {},
6320
6320
  onRenderReadiness() {},
6321
6321
  hydrateNow() {},
6322
+ reconcileInBackground() {},
6322
6323
  invalidate() {},
6323
6324
  reset() {},
6324
6325
  getDiagnostics() {
@@ -6518,6 +6519,8 @@ var COMMENT_DRAFT_BLOCK_OPEN_TAG = /<(?:p|div|li|blockquote|pre|h[1-6])(?:\s[^>]
6518
6519
  var COMMENT_DRAFT_BLOCK_CLOSE_TAG = /<\/(?:p|div|li|blockquote|pre|h[1-6])\s*>/gi;
6519
6520
  var COMMENT_DRAFT_ANY_TAG = /<\/?[A-Za-z][A-Za-z0-9:-]*(?:\s[^>]*)?\s*\/?>/g;
6520
6521
  var COMMENT_DRAFT_ENTITY = /&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z][a-zA-Z0-9]+);/g;
6522
+ var TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE = 50;
6523
+ var TRACKED_CHANGE_BACKGROUND_RECONCILE_YIELD_TIMEOUT_MS = 50;
6521
6524
  var COMMENT_DRAFT_NAMED_ENTITIES = {
6522
6525
  amp: "&",
6523
6526
  apos: "'",
@@ -6534,6 +6537,13 @@ var shallowEqual = (a, b) => {
6534
6537
  if (aKeys.length !== bKeys.length) return false;
6535
6538
  return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && Object.is(a[key], b[key]));
6536
6539
  };
6540
+ var yieldForTrackedChangeBackgroundReconcile = () => new Promise((resolve) => {
6541
+ if (typeof globalThis.requestIdleCallback === "function") {
6542
+ globalThis.requestIdleCallback(resolve, { timeout: TRACKED_CHANGE_BACKGROUND_RECONCILE_YIELD_TIMEOUT_MS });
6543
+ return;
6544
+ }
6545
+ setTimeout(resolve, 0);
6546
+ });
6537
6547
  var normalizeTrackedChangeDetailLines = (lines) => {
6538
6548
  if (!Array.isArray(lines)) return null;
6539
6549
  const normalized = lines.filter((line) => line && typeof line === "object").map((line) => ({
@@ -8147,7 +8157,7 @@ const useCommentsStore = defineStore("comments", () => {
8147
8157
  if (!adapter) return false;
8148
8158
  return v2TrackedChangesAdapter.value === adapter;
8149
8159
  };
8150
- const applyReviewSnapshotFromV2 = ({ superdoc, commentsAdapter, trackedChangesAdapter, documentId, commentItems, trackedChangeItems, trackedList, patch } = {}) => withInteractionSpan("store.reviewSnapshot.apply", "store-reconciliation", {
8160
+ const applyReviewSnapshotFromV2 = ({ superdoc, commentsAdapter, trackedChangesAdapter, documentId, commentItems, trackedChangeItems, trackedList, sourceCoverageRevision, evaluatedRevision, patch } = {}) => withInteractionSpan("store.reviewSnapshot.apply", "store-reconciliation", {
8151
8161
  commentItemCount: Array.isArray(commentItems) ? commentItems.length : null,
8152
8162
  trackedItemCount: Array.isArray(trackedChangeItems) ? trackedChangeItems.length : null
8153
8163
  }, () => {
@@ -8185,11 +8195,28 @@ const useCommentsStore = defineStore("comments", () => {
8185
8195
  ok: false,
8186
8196
  reason: "adapter-mapper-missing"
8187
8197
  };
8198
+ let effectiveCommentItems = commentItems;
8199
+ let commentItemsArePartial = false;
8200
+ if (typeof commentsAdapter.seedReviewCatalog === "function") {
8201
+ const seeded = commentsAdapter.seedReviewCatalog(commentItems, {
8202
+ sourceCoverageRevision,
8203
+ evaluatedRevision
8204
+ });
8205
+ if (seeded?.ok !== true) return {
8206
+ ok: false,
8207
+ reason: seeded?.reason ?? "comment-catalog-seed-failed"
8208
+ };
8209
+ const selected = commentsAdapter.selectVisibleReviewComments?.();
8210
+ if (selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) {
8211
+ effectiveCommentItems = selected.items;
8212
+ commentItemsArePartial = true;
8213
+ }
8214
+ }
8188
8215
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
8189
8216
  let preparedInputs;
8190
8217
  let preparedParams;
8191
8218
  try {
8192
- preparedInputs = commentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8219
+ preparedInputs = effectiveCommentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8193
8220
  fileId: normalizedDocumentId,
8194
8221
  fileType
8195
8222
  })).filter(Boolean);
@@ -8218,8 +8245,9 @@ const useCommentsStore = defineStore("comments", () => {
8218
8245
  superdoc,
8219
8246
  adapter: commentsAdapter,
8220
8247
  documentId: normalizedDocumentId,
8221
- items: commentItems,
8222
- preparedInputs
8248
+ items: effectiveCommentItems,
8249
+ preparedInputs,
8250
+ pruneStale: !commentItemsArePartial
8223
8251
  });
8224
8252
  });
8225
8253
  return {
@@ -8228,12 +8256,19 @@ const useCommentsStore = defineStore("comments", () => {
8228
8256
  trackedItems: preparedParams.length
8229
8257
  };
8230
8258
  });
8231
- const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId, signal, hydrationGeneration } = {}) => withInteractionSpan("store.comments.hydrateFromV2", "comments-list", { documentId: documentId ?? null }, async () => {
8259
+ const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", isCurrent, signal, hydrationGeneration } = {}) => withInteractionSpan("store.comments.hydrateFromV2", "comments-list", { documentId: documentId ?? null }, async () => {
8232
8260
  const effectiveAdapter = adapter ?? getV2CommentsAdapter(superdoc);
8233
- if (!effectiveAdapter || typeof effectiveAdapter.refresh !== "function") return {
8261
+ const visibleWindow = trackedChangesListMode === "visible-window";
8262
+ const read = visibleWindow ? effectiveAdapter?.selectVisibleReviewComments : effectiveAdapter?.refresh;
8263
+ if (!effectiveAdapter || typeof read !== "function") return {
8234
8264
  ok: false,
8235
8265
  reason: "adapter-missing"
8236
8266
  };
8267
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2CommentsAdapter(effectiveAdapter);
8268
+ if (!hydrationIsCurrent()) return {
8269
+ ok: false,
8270
+ reason: "adapter-stale"
8271
+ };
8237
8272
  if (signal?.aborted) return {
8238
8273
  ok: false,
8239
8274
  reason: "review-hydration-superseded",
@@ -8245,7 +8280,7 @@ const useCommentsStore = defineStore("comments", () => {
8245
8280
  const refreshOptions = {};
8246
8281
  if (signal) refreshOptions.signal = signal;
8247
8282
  if (typeof hydrationGeneration === "number") refreshOptions.hydrationGeneration = hydrationGeneration;
8248
- result = Object.keys(refreshOptions).length > 0 ? await effectiveAdapter.refresh(refreshOptions) : await effectiveAdapter.refresh();
8283
+ result = Object.keys(refreshOptions).length > 0 ? await read.call(effectiveAdapter, refreshOptions) : await read.call(effectiveAdapter);
8249
8284
  } catch (err) {
8250
8285
  return {
8251
8286
  ok: false,
@@ -8253,7 +8288,7 @@ const useCommentsStore = defineStore("comments", () => {
8253
8288
  detail: err?.message ?? String(err)
8254
8289
  };
8255
8290
  }
8256
- if (!isCurrentV2CommentsAdapter(effectiveAdapter)) return {
8291
+ if (!hydrationIsCurrent()) return {
8257
8292
  ok: false,
8258
8293
  reason: "adapter-stale"
8259
8294
  };
@@ -8273,16 +8308,18 @@ const useCommentsStore = defineStore("comments", () => {
8273
8308
  adapter: effectiveAdapter,
8274
8309
  documentId,
8275
8310
  items: result.items ?? [],
8311
+ pruneStale: !visibleWindow,
8276
8312
  hydrationGeneration
8277
8313
  });
8278
8314
  return {
8279
8315
  ok: true,
8280
8316
  items: result.items ?? [],
8281
8317
  reconciled: true,
8282
- ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {}
8318
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {},
8319
+ ...result.visibleWindowSource != null ? { visibleWindowSource: result.visibleWindowSource } : {}
8283
8320
  };
8284
8321
  });
8285
- const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8322
+ const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, pruneStale = true, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8286
8323
  documentId: documentId ?? null,
8287
8324
  itemCount: Array.isArray(items) ? items.length : null,
8288
8325
  hydrationGeneration: hydrationGeneration ?? null
@@ -8400,6 +8437,7 @@ const useCommentsStore = defineStore("comments", () => {
8400
8437
  seenIncoming.add(cid);
8401
8438
  nextList.push(existing);
8402
8439
  }
8440
+ if (!input && !pruneStale) nextList.push(existing);
8403
8441
  }
8404
8442
  for (const [cid, input] of incomingByCommentId.entries()) {
8405
8443
  if (seenIncoming.has(cid)) continue;
@@ -9318,7 +9356,7 @@ const useCommentsStore = defineStore("comments", () => {
9318
9356
  success: Boolean(command(id))
9319
9357
  };
9320
9358
  };
9321
- const hydrateTrackedChangesFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", refreshReason, blocking, signal, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.hydrateFromV2", "tracked-change-list", {
9359
+ const hydrateTrackedChangesFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", refreshReason, blocking, isCurrent, targetIds, reconciliationBatchSize = TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE, signal, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.hydrateFromV2", "tracked-change-list", {
9322
9360
  documentId: documentId ?? null,
9323
9361
  trackedChangesListMode,
9324
9362
  refreshReason: refreshReason ?? null,
@@ -9329,19 +9367,49 @@ const useCommentsStore = defineStore("comments", () => {
9329
9367
  ok: false,
9330
9368
  reason: "adapter-missing"
9331
9369
  };
9370
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(effectiveAdapter);
9371
+ if (!hydrationIsCurrent()) return {
9372
+ ok: false,
9373
+ reason: "adapter-stale"
9374
+ };
9332
9375
  const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9376
+ const hydrationCanContinue = () => hydrationIsCurrent() && !signal?.aborted && trackedChangeSyncGeneration(effectiveAdapter) === syncGeneration;
9333
9377
  if (signal?.aborted) return {
9334
9378
  ok: false,
9335
9379
  reason: "review-hydration-superseded",
9336
9380
  lateResultDropped: false
9337
9381
  };
9382
+ const incrementalLiveIds = /* @__PURE__ */ new Set();
9383
+ const incrementalLiveAnchorKeys = /* @__PURE__ */ new Set();
9384
+ let incrementalPagesApplied = 0;
9385
+ let incrementalItemsApplied = 0;
9338
9386
  let result;
9339
9387
  try {
9340
9388
  const listOptions = { mode: trackedChangesListMode };
9389
+ if (Array.isArray(targetIds)) listOptions.targetIds = targetIds;
9341
9390
  if (refreshReason != null) listOptions.refreshReason = refreshReason;
9342
9391
  if (typeof blocking === "boolean") listOptions.blocking = blocking;
9343
9392
  if (signal) listOptions.signal = signal;
9344
9393
  if (typeof hydrationGeneration === "number") listOptions.hydrationGeneration = hydrationGeneration;
9394
+ if (trackedChangesListMode === "background-reconcile") {
9395
+ listOptions.shouldContinue = hydrationCanContinue;
9396
+ listOptions.onPage = async (page) => {
9397
+ if (!hydrationCanContinue()) return;
9398
+ const pageItems = Array.isArray(page?.items) ? page.items : [];
9399
+ if (!reconcileTrackedChangesFromV2({
9400
+ superdoc,
9401
+ adapter: effectiveAdapter,
9402
+ documentId,
9403
+ items: pageItems,
9404
+ pruneStale: false,
9405
+ liveIds: incrementalLiveIds,
9406
+ liveAnchorKeys: incrementalLiveAnchorKeys,
9407
+ hydrationGeneration
9408
+ }) || !hydrationCanContinue()) return;
9409
+ incrementalPagesApplied += 1;
9410
+ incrementalItemsApplied += pageItems.length;
9411
+ };
9412
+ }
9345
9413
  result = await effectiveAdapter.listTrackedChanges(listOptions);
9346
9414
  } catch (err) {
9347
9415
  return {
@@ -9350,7 +9418,7 @@ const useCommentsStore = defineStore("comments", () => {
9350
9418
  detail: err?.message ?? String(err)
9351
9419
  };
9352
9420
  }
9353
- if (!isCurrentV2TrackedChangesAdapter(effectiveAdapter)) return {
9421
+ if (!hydrationIsCurrent()) return {
9354
9422
  ok: false,
9355
9423
  reason: "adapter-stale"
9356
9424
  };
@@ -9371,7 +9439,35 @@ const useCommentsStore = defineStore("comments", () => {
9371
9439
  reason: "review-hydration-superseded",
9372
9440
  lateResultDropped: true
9373
9441
  };
9374
- reconcileTrackedChangesFromV2({
9442
+ if (trackedChangesListMode === "background-reconcile") if (incrementalPagesApplied > 0) {
9443
+ if (!hydrationCanContinue()) return {
9444
+ ok: false,
9445
+ reason: "reconciliation-stale"
9446
+ };
9447
+ const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9448
+ if (pruneStale && effectiveDocumentId) withInteractionSpan("store.trackedChanges.reconcile.finalPrune", "store-reconciliation", {
9449
+ documentId: effectiveDocumentId,
9450
+ liveIds: incrementalLiveIds.size,
9451
+ liveAnchorKeys: incrementalLiveAnchorKeys.size
9452
+ }, () => {
9453
+ if (!hydrationCanContinue()) return;
9454
+ pruneStaleTrackedChangeComments(incrementalLiveIds, incrementalLiveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9455
+ });
9456
+ } else {
9457
+ const reconcileResult = await reconcileTrackedChangesFromV2Incrementally({
9458
+ superdoc,
9459
+ adapter: effectiveAdapter,
9460
+ documentId,
9461
+ items: result.items ?? [],
9462
+ pruneStale,
9463
+ isCurrent: hydrationIsCurrent,
9464
+ batchSize: reconciliationBatchSize,
9465
+ hydrationGeneration
9466
+ });
9467
+ if (!reconcileResult.ok) return reconcileResult;
9468
+ incrementalItemsApplied = reconcileResult.appliedItems;
9469
+ }
9470
+ else reconcileTrackedChangesFromV2({
9375
9471
  superdoc,
9376
9472
  adapter: effectiveAdapter,
9377
9473
  documentId,
@@ -9391,6 +9487,12 @@ const useCommentsStore = defineStore("comments", () => {
9391
9487
  if (result.visibleWindowFallbackReason != null) hydrationResult.visibleWindowFallbackReason = result.visibleWindowFallbackReason;
9392
9488
  if (typeof result.visibleTrackedChangeExpectedRows === "number") hydrationResult.visibleTrackedChangeExpectedRows = result.visibleTrackedChangeExpectedRows;
9393
9489
  if (typeof result.visibleTrackedChangeObservedRows === "number") hydrationResult.visibleTrackedChangeObservedRows = result.visibleTrackedChangeObservedRows;
9490
+ for (const key of [
9491
+ "visibleTrackedChangeRequestedIds",
9492
+ "visibleTrackedChangeSucceededIds",
9493
+ "visibleTrackedChangeFailedIds"
9494
+ ]) if (Array.isArray(result[key])) hydrationResult[key] = [...result[key]];
9495
+ if (Array.isArray(result.visibleWindowGetFailures)) hydrationResult.visibleWindowGetFailures = result.visibleWindowGetFailures.map((failure) => ({ ...failure }));
9394
9496
  if (typeof result.refreshReason === "string") hydrationResult.refreshReason = result.refreshReason;
9395
9497
  if (typeof result.blocking === "boolean") hydrationResult.blocking = result.blocking;
9396
9498
  if (typeof result.pageCount === "number") hydrationResult.pageCount = result.pageCount;
@@ -9399,6 +9501,11 @@ const useCommentsStore = defineStore("comments", () => {
9399
9501
  if (typeof result.pagesSkippedAfterAbort === "number") hydrationResult.pagesSkippedAfterAbort = result.pagesSkippedAfterAbort;
9400
9502
  if (typeof result.totalItems === "number") hydrationResult.totalItems = result.totalItems;
9401
9503
  if (typeof result.coalesced === "boolean") hydrationResult.coalesced = result.coalesced;
9504
+ if (typeof result.evaluatedRevision === "string") hydrationResult.evaluatedRevision = result.evaluatedRevision;
9505
+ if (trackedChangesListMode === "background-reconcile") {
9506
+ hydrationResult.incrementalPagesApplied = incrementalPagesApplied;
9507
+ hydrationResult.incrementalItemsApplied = incrementalItemsApplied;
9508
+ }
9402
9509
  return hydrationResult;
9403
9510
  });
9404
9511
  const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [], allResolved } = {}) => {
@@ -9553,7 +9660,7 @@ const useCommentsStore = defineStore("comments", () => {
9553
9660
  }
9554
9661
  };
9555
9662
  };
9556
- const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9663
+ const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9557
9664
  documentId: documentId ?? null,
9558
9665
  itemCount: Array.isArray(items) ? items.length : null,
9559
9666
  pruneStale,
@@ -9562,8 +9669,9 @@ const useCommentsStore = defineStore("comments", () => {
9562
9669
  if (!adapter || !Array.isArray(items)) return;
9563
9670
  if (!isCurrentV2TrackedChangesAdapter(adapter)) return;
9564
9671
  const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9565
- const liveAnchorKeys = /* @__PURE__ */ new Set();
9566
- const liveIds = /* @__PURE__ */ new Set();
9672
+ const liveAnchorKeys = suppliedLiveAnchorKeys instanceof Set ? suppliedLiveAnchorKeys : /* @__PURE__ */ new Set();
9673
+ const liveIds = suppliedLiveIds instanceof Set ? suppliedLiveIds : /* @__PURE__ */ new Set();
9674
+ let appliedCount = 0;
9567
9675
  const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
9568
9676
  const identitySpan = startInteractionSpan("store.trackedChanges.batchIdentity", "store-reconciliation", { itemCount: items.length });
9569
9677
  const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
@@ -9583,13 +9691,95 @@ const useCommentsStore = defineStore("comments", () => {
9583
9691
  broadcastChanges: false,
9584
9692
  trackedChangeIdentityIndex
9585
9693
  });
9694
+ appliedCount += 1;
9586
9695
  }
9587
9696
  } finally {
9588
9697
  endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
9589
9698
  }
9590
- if (!effectiveDocumentId || !pruneStale) return;
9699
+ if (!effectiveDocumentId || !pruneStale) return {
9700
+ liveIds,
9701
+ liveAnchorKeys,
9702
+ appliedCount
9703
+ };
9591
9704
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9705
+ return {
9706
+ liveIds,
9707
+ liveAnchorKeys,
9708
+ appliedCount
9709
+ };
9592
9710
  });
9711
+ const reconcileTrackedChangesFromV2Incrementally = async ({ superdoc, adapter, documentId, items, pruneStale, isCurrent, batchSize = TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE, hydrationGeneration } = {}) => {
9712
+ if (!adapter || !Array.isArray(items)) return {
9713
+ ok: false,
9714
+ reason: "reconcile-input-invalid"
9715
+ };
9716
+ const current = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(adapter);
9717
+ if (!current()) return {
9718
+ ok: false,
9719
+ reason: "reconciliation-stale"
9720
+ };
9721
+ const effectiveBatchSize = Math.max(1, Math.floor(Number(batchSize) || TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE));
9722
+ const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9723
+ const liveIds = /* @__PURE__ */ new Set();
9724
+ const liveAnchorKeys = /* @__PURE__ */ new Set();
9725
+ let appliedItems = 0;
9726
+ let batchCount = 0;
9727
+ for (let offset = 0; offset < items.length; offset += effectiveBatchSize) {
9728
+ if (!current()) return {
9729
+ ok: false,
9730
+ reason: "reconciliation-stale",
9731
+ appliedItems,
9732
+ batchCount
9733
+ };
9734
+ const batch = items.slice(offset, offset + effectiveBatchSize);
9735
+ if (!reconcileTrackedChangesFromV2({
9736
+ superdoc,
9737
+ adapter,
9738
+ documentId: effectiveDocumentId,
9739
+ items: batch,
9740
+ pruneStale: false,
9741
+ liveIds,
9742
+ liveAnchorKeys,
9743
+ hydrationGeneration
9744
+ }) || !current()) return {
9745
+ ok: false,
9746
+ reason: "reconciliation-stale",
9747
+ appliedItems,
9748
+ batchCount
9749
+ };
9750
+ appliedItems += batch.length;
9751
+ batchCount += 1;
9752
+ if (offset + effectiveBatchSize < items.length) await yieldForTrackedChangeBackgroundReconcile();
9753
+ }
9754
+ if (!current()) return {
9755
+ ok: false,
9756
+ reason: "reconciliation-stale",
9757
+ appliedItems,
9758
+ batchCount
9759
+ };
9760
+ if (pruneStale && effectiveDocumentId) {
9761
+ withInteractionSpan("store.trackedChanges.reconcile.finalPrune", "store-reconciliation", {
9762
+ documentId: effectiveDocumentId,
9763
+ liveIds: liveIds.size,
9764
+ liveAnchorKeys: liveAnchorKeys.size
9765
+ }, () => {
9766
+ if (!current()) return;
9767
+ pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9768
+ });
9769
+ if (!current()) return {
9770
+ ok: false,
9771
+ reason: "reconciliation-stale",
9772
+ appliedItems,
9773
+ batchCount
9774
+ };
9775
+ }
9776
+ return {
9777
+ ok: true,
9778
+ appliedItems,
9779
+ batchCount,
9780
+ pruned: Boolean(pruneStale && effectiveDocumentId)
9781
+ };
9782
+ };
9593
9783
  const getV2TrackedChangeRowCount = (documentId = null) => {
9594
9784
  const normalizedDocumentId = documentId == null ? null : String(documentId);
9595
9785
  return commentsList.value.filter((comment) => {
@@ -18184,6 +18374,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18184
18374
  const v2ReviewHydrationController = resolvedEditorIntegration.createReviewHydrationController({
18185
18375
  hydrateComments: (ctx) => commentsStore.hydrateCommentsFromV2?.(ctx),
18186
18376
  hydrateTrackedChanges: (ctx) => commentsStore.hydrateTrackedChangesFromV2?.(ctx),
18377
+ primeTrackedChanges: (ctx) => ctx.trackedChangesAdapter?.primeVisibleTrackedChanges?.(ctx.targetIds, ctx.plan),
18187
18378
  readReviewSnapshot: (ctx) => ctx.trackedChangesAdapter?.readReviewSnapshot?.(ctx.snapshot),
18188
18379
  validateReviewSnapshot: (_ctx, result) => result?.validate?.() ?? {
18189
18380
  ok: false,
@@ -18197,6 +18388,8 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18197
18388
  commentItems: result.comments?.items,
18198
18389
  trackedChangeItems: result.trackedChanges?.items,
18199
18390
  trackedList: result.trackedChanges,
18391
+ sourceCoverageRevision: result.sourceCoverageRevision,
18392
+ evaluatedRevision: result.evaluatedRevision,
18200
18393
  patch: (callback) => commentsStore.$patch(callback)
18201
18394
  }) ?? {
18202
18395
  ok: false,
@@ -18874,6 +19067,12 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18874
19067
  v2ReviewSidebarUnlocked.value = true;
18875
19068
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
18876
19069
  }
19070
+ },
19071
+ onCommittedPagePaint: (commit) => {
19072
+ v2ReviewHydrationController.onCommittedPagePaint?.({
19073
+ ...commit,
19074
+ documentId: commit?.payload?.documentId ?? null
19075
+ });
18877
19076
  }
18878
19077
  });
18879
19078
  const collectV2TrackedChangeRestampIds = (impactOrIds) => {
@@ -19097,7 +19296,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
19097
19296
  v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
19098
19297
  };
19099
19298
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
19100
- hydrate: () => hydrateV2ReviewRowsFromHost(),
19299
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
19101
19300
  getActiveDocumentId: () => {
19102
19301
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
19103
19302
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -19350,6 +19549,12 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
19350
19549
  onCommentsUpdate: onEditorCommentsUpdate,
19351
19550
  onFontsResolved: onFontsResolvedFn,
19352
19551
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
19552
+ onReviewWindowPlanned: (payload) => {
19553
+ v2ReviewHydrationController.onReviewWindowPlanned?.({
19554
+ ...payload,
19555
+ documentId: doc.id
19556
+ });
19557
+ },
19353
19558
  fontAssets: proxy.$superdoc.config.fonts,
19354
19559
  proofing: resolvedProofingConfig.value,
19355
19560
  isNewFile,
@@ -20311,7 +20516,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
20311
20516
  ], 38);
20312
20517
  };
20313
20518
  }
20314
- }, [["__scopeId", "data-v-e489229f"]]);
20519
+ }, [["__scopeId", "data-v-c3bc8a46"]]);
20315
20520
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
20316
20521
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
20317
20522
  var piniaDevtoolsSuppressionState = {
@@ -39025,7 +39230,7 @@ var SuperDoc = class extends import_eventemitter3.default {
39025
39230
  this.config.colors = shuffleArray(this.config.colors);
39026
39231
  this.userColorMap = /* @__PURE__ */ new Map();
39027
39232
  this.colorIndex = 0;
39028
- this.version = "2.4.0-next.3";
39233
+ this.version = "2.4.0-next.4";
39029
39234
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
39030
39235
  this.superdocId = config.superdocId || v4_default();
39031
39236
  this.colors = this.config.colors ?? [];