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.
package/dist/superdoc.cjs CHANGED
@@ -6327,6 +6327,7 @@ function createStubReviewHydrationController() {
6327
6327
  setContext() {},
6328
6328
  onRenderReadiness() {},
6329
6329
  hydrateNow() {},
6330
+ reconcileInBackground() {},
6330
6331
  invalidate() {},
6331
6332
  reset() {},
6332
6333
  getDiagnostics() {
@@ -6526,6 +6527,8 @@ var COMMENT_DRAFT_BLOCK_OPEN_TAG = /<(?:p|div|li|blockquote|pre|h[1-6])(?:\s[^>]
6526
6527
  var COMMENT_DRAFT_BLOCK_CLOSE_TAG = /<\/(?:p|div|li|blockquote|pre|h[1-6])\s*>/gi;
6527
6528
  var COMMENT_DRAFT_ANY_TAG = /<\/?[A-Za-z][A-Za-z0-9:-]*(?:\s[^>]*)?\s*\/?>/g;
6528
6529
  var COMMENT_DRAFT_ENTITY = /&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z][a-zA-Z0-9]+);/g;
6530
+ var TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE = 50;
6531
+ var TRACKED_CHANGE_BACKGROUND_RECONCILE_YIELD_TIMEOUT_MS = 50;
6529
6532
  var COMMENT_DRAFT_NAMED_ENTITIES = {
6530
6533
  amp: "&",
6531
6534
  apos: "'",
@@ -6542,6 +6545,13 @@ var shallowEqual = (a, b) => {
6542
6545
  if (aKeys.length !== bKeys.length) return false;
6543
6546
  return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && Object.is(a[key], b[key]));
6544
6547
  };
6548
+ var yieldForTrackedChangeBackgroundReconcile = () => new Promise((resolve) => {
6549
+ if (typeof globalThis.requestIdleCallback === "function") {
6550
+ globalThis.requestIdleCallback(resolve, { timeout: TRACKED_CHANGE_BACKGROUND_RECONCILE_YIELD_TIMEOUT_MS });
6551
+ return;
6552
+ }
6553
+ setTimeout(resolve, 0);
6554
+ });
6545
6555
  var normalizeTrackedChangeDetailLines = (lines) => {
6546
6556
  if (!Array.isArray(lines)) return null;
6547
6557
  const normalized = lines.filter((line) => line && typeof line === "object").map((line) => ({
@@ -8155,7 +8165,7 @@ const useCommentsStore = defineStore("comments", () => {
8155
8165
  if (!adapter) return false;
8156
8166
  return v2TrackedChangesAdapter.value === adapter;
8157
8167
  };
8158
- const applyReviewSnapshotFromV2 = ({ superdoc, commentsAdapter, trackedChangesAdapter, documentId, commentItems, trackedChangeItems, trackedList, patch } = {}) => withInteractionSpan("store.reviewSnapshot.apply", "store-reconciliation", {
8168
+ const applyReviewSnapshotFromV2 = ({ superdoc, commentsAdapter, trackedChangesAdapter, documentId, commentItems, trackedChangeItems, trackedList, sourceCoverageRevision, evaluatedRevision, patch } = {}) => withInteractionSpan("store.reviewSnapshot.apply", "store-reconciliation", {
8159
8169
  commentItemCount: Array.isArray(commentItems) ? commentItems.length : null,
8160
8170
  trackedItemCount: Array.isArray(trackedChangeItems) ? trackedChangeItems.length : null
8161
8171
  }, () => {
@@ -8193,11 +8203,28 @@ const useCommentsStore = defineStore("comments", () => {
8193
8203
  ok: false,
8194
8204
  reason: "adapter-mapper-missing"
8195
8205
  };
8206
+ let effectiveCommentItems = commentItems;
8207
+ let commentItemsArePartial = false;
8208
+ if (typeof commentsAdapter.seedReviewCatalog === "function") {
8209
+ const seeded = commentsAdapter.seedReviewCatalog(commentItems, {
8210
+ sourceCoverageRevision,
8211
+ evaluatedRevision
8212
+ });
8213
+ if (seeded?.ok !== true) return {
8214
+ ok: false,
8215
+ reason: seeded?.reason ?? "comment-catalog-seed-failed"
8216
+ };
8217
+ const selected = commentsAdapter.selectVisibleReviewComments?.();
8218
+ if (selected?.ok === true && Array.isArray(selected.items) && (!Array.isArray(selected.unresolvedIds) || selected.unresolvedIds.length === 0)) {
8219
+ effectiveCommentItems = selected.items;
8220
+ commentItemsArePartial = true;
8221
+ }
8222
+ }
8196
8223
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
8197
8224
  let preparedInputs;
8198
8225
  let preparedParams;
8199
8226
  try {
8200
- preparedInputs = commentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8227
+ preparedInputs = effectiveCommentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8201
8228
  fileId: normalizedDocumentId,
8202
8229
  fileType
8203
8230
  })).filter(Boolean);
@@ -8226,8 +8253,9 @@ const useCommentsStore = defineStore("comments", () => {
8226
8253
  superdoc,
8227
8254
  adapter: commentsAdapter,
8228
8255
  documentId: normalizedDocumentId,
8229
- items: commentItems,
8230
- preparedInputs
8256
+ items: effectiveCommentItems,
8257
+ preparedInputs,
8258
+ pruneStale: !commentItemsArePartial
8231
8259
  });
8232
8260
  });
8233
8261
  return {
@@ -8236,12 +8264,19 @@ const useCommentsStore = defineStore("comments", () => {
8236
8264
  trackedItems: preparedParams.length
8237
8265
  };
8238
8266
  });
8239
- const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId, signal, hydrationGeneration } = {}) => withInteractionSpan("store.comments.hydrateFromV2", "comments-list", { documentId: documentId ?? null }, async () => {
8267
+ const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", isCurrent, signal, hydrationGeneration } = {}) => withInteractionSpan("store.comments.hydrateFromV2", "comments-list", { documentId: documentId ?? null }, async () => {
8240
8268
  const effectiveAdapter = adapter ?? getV2CommentsAdapter(superdoc);
8241
- if (!effectiveAdapter || typeof effectiveAdapter.refresh !== "function") return {
8269
+ const visibleWindow = trackedChangesListMode === "visible-window";
8270
+ const read = visibleWindow ? effectiveAdapter?.selectVisibleReviewComments : effectiveAdapter?.refresh;
8271
+ if (!effectiveAdapter || typeof read !== "function") return {
8242
8272
  ok: false,
8243
8273
  reason: "adapter-missing"
8244
8274
  };
8275
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2CommentsAdapter(effectiveAdapter);
8276
+ if (!hydrationIsCurrent()) return {
8277
+ ok: false,
8278
+ reason: "adapter-stale"
8279
+ };
8245
8280
  if (signal?.aborted) return {
8246
8281
  ok: false,
8247
8282
  reason: "review-hydration-superseded",
@@ -8253,7 +8288,7 @@ const useCommentsStore = defineStore("comments", () => {
8253
8288
  const refreshOptions = {};
8254
8289
  if (signal) refreshOptions.signal = signal;
8255
8290
  if (typeof hydrationGeneration === "number") refreshOptions.hydrationGeneration = hydrationGeneration;
8256
- result = Object.keys(refreshOptions).length > 0 ? await effectiveAdapter.refresh(refreshOptions) : await effectiveAdapter.refresh();
8291
+ result = Object.keys(refreshOptions).length > 0 ? await read.call(effectiveAdapter, refreshOptions) : await read.call(effectiveAdapter);
8257
8292
  } catch (err) {
8258
8293
  return {
8259
8294
  ok: false,
@@ -8261,7 +8296,7 @@ const useCommentsStore = defineStore("comments", () => {
8261
8296
  detail: err?.message ?? String(err)
8262
8297
  };
8263
8298
  }
8264
- if (!isCurrentV2CommentsAdapter(effectiveAdapter)) return {
8299
+ if (!hydrationIsCurrent()) return {
8265
8300
  ok: false,
8266
8301
  reason: "adapter-stale"
8267
8302
  };
@@ -8281,16 +8316,18 @@ const useCommentsStore = defineStore("comments", () => {
8281
8316
  adapter: effectiveAdapter,
8282
8317
  documentId,
8283
8318
  items: result.items ?? [],
8319
+ pruneStale: !visibleWindow,
8284
8320
  hydrationGeneration
8285
8321
  });
8286
8322
  return {
8287
8323
  ok: true,
8288
8324
  items: result.items ?? [],
8289
8325
  reconciled: true,
8290
- ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {}
8326
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {},
8327
+ ...result.visibleWindowSource != null ? { visibleWindowSource: result.visibleWindowSource } : {}
8291
8328
  };
8292
8329
  });
8293
- const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8330
+ const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, pruneStale = true, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8294
8331
  documentId: documentId ?? null,
8295
8332
  itemCount: Array.isArray(items) ? items.length : null,
8296
8333
  hydrationGeneration: hydrationGeneration ?? null
@@ -8408,6 +8445,7 @@ const useCommentsStore = defineStore("comments", () => {
8408
8445
  seenIncoming.add(cid);
8409
8446
  nextList.push(existing);
8410
8447
  }
8448
+ if (!input && !pruneStale) nextList.push(existing);
8411
8449
  }
8412
8450
  for (const [cid, input] of incomingByCommentId.entries()) {
8413
8451
  if (seenIncoming.has(cid)) continue;
@@ -9326,7 +9364,7 @@ const useCommentsStore = defineStore("comments", () => {
9326
9364
  success: Boolean(command(id))
9327
9365
  };
9328
9366
  };
9329
- const hydrateTrackedChangesFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", refreshReason, blocking, signal, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.hydrateFromV2", "tracked-change-list", {
9367
+ 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", {
9330
9368
  documentId: documentId ?? null,
9331
9369
  trackedChangesListMode,
9332
9370
  refreshReason: refreshReason ?? null,
@@ -9337,19 +9375,49 @@ const useCommentsStore = defineStore("comments", () => {
9337
9375
  ok: false,
9338
9376
  reason: "adapter-missing"
9339
9377
  };
9378
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(effectiveAdapter);
9379
+ if (!hydrationIsCurrent()) return {
9380
+ ok: false,
9381
+ reason: "adapter-stale"
9382
+ };
9340
9383
  const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9384
+ const hydrationCanContinue = () => hydrationIsCurrent() && !signal?.aborted && trackedChangeSyncGeneration(effectiveAdapter) === syncGeneration;
9341
9385
  if (signal?.aborted) return {
9342
9386
  ok: false,
9343
9387
  reason: "review-hydration-superseded",
9344
9388
  lateResultDropped: false
9345
9389
  };
9390
+ const incrementalLiveIds = /* @__PURE__ */ new Set();
9391
+ const incrementalLiveAnchorKeys = /* @__PURE__ */ new Set();
9392
+ let incrementalPagesApplied = 0;
9393
+ let incrementalItemsApplied = 0;
9346
9394
  let result;
9347
9395
  try {
9348
9396
  const listOptions = { mode: trackedChangesListMode };
9397
+ if (Array.isArray(targetIds)) listOptions.targetIds = targetIds;
9349
9398
  if (refreshReason != null) listOptions.refreshReason = refreshReason;
9350
9399
  if (typeof blocking === "boolean") listOptions.blocking = blocking;
9351
9400
  if (signal) listOptions.signal = signal;
9352
9401
  if (typeof hydrationGeneration === "number") listOptions.hydrationGeneration = hydrationGeneration;
9402
+ if (trackedChangesListMode === "background-reconcile") {
9403
+ listOptions.shouldContinue = hydrationCanContinue;
9404
+ listOptions.onPage = async (page) => {
9405
+ if (!hydrationCanContinue()) return;
9406
+ const pageItems = Array.isArray(page?.items) ? page.items : [];
9407
+ if (!reconcileTrackedChangesFromV2({
9408
+ superdoc,
9409
+ adapter: effectiveAdapter,
9410
+ documentId,
9411
+ items: pageItems,
9412
+ pruneStale: false,
9413
+ liveIds: incrementalLiveIds,
9414
+ liveAnchorKeys: incrementalLiveAnchorKeys,
9415
+ hydrationGeneration
9416
+ }) || !hydrationCanContinue()) return;
9417
+ incrementalPagesApplied += 1;
9418
+ incrementalItemsApplied += pageItems.length;
9419
+ };
9420
+ }
9353
9421
  result = await effectiveAdapter.listTrackedChanges(listOptions);
9354
9422
  } catch (err) {
9355
9423
  return {
@@ -9358,7 +9426,7 @@ const useCommentsStore = defineStore("comments", () => {
9358
9426
  detail: err?.message ?? String(err)
9359
9427
  };
9360
9428
  }
9361
- if (!isCurrentV2TrackedChangesAdapter(effectiveAdapter)) return {
9429
+ if (!hydrationIsCurrent()) return {
9362
9430
  ok: false,
9363
9431
  reason: "adapter-stale"
9364
9432
  };
@@ -9379,7 +9447,35 @@ const useCommentsStore = defineStore("comments", () => {
9379
9447
  reason: "review-hydration-superseded",
9380
9448
  lateResultDropped: true
9381
9449
  };
9382
- reconcileTrackedChangesFromV2({
9450
+ if (trackedChangesListMode === "background-reconcile") if (incrementalPagesApplied > 0) {
9451
+ if (!hydrationCanContinue()) return {
9452
+ ok: false,
9453
+ reason: "reconciliation-stale"
9454
+ };
9455
+ const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9456
+ if (pruneStale && effectiveDocumentId) withInteractionSpan("store.trackedChanges.reconcile.finalPrune", "store-reconciliation", {
9457
+ documentId: effectiveDocumentId,
9458
+ liveIds: incrementalLiveIds.size,
9459
+ liveAnchorKeys: incrementalLiveAnchorKeys.size
9460
+ }, () => {
9461
+ if (!hydrationCanContinue()) return;
9462
+ pruneStaleTrackedChangeComments(incrementalLiveIds, incrementalLiveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9463
+ });
9464
+ } else {
9465
+ const reconcileResult = await reconcileTrackedChangesFromV2Incrementally({
9466
+ superdoc,
9467
+ adapter: effectiveAdapter,
9468
+ documentId,
9469
+ items: result.items ?? [],
9470
+ pruneStale,
9471
+ isCurrent: hydrationIsCurrent,
9472
+ batchSize: reconciliationBatchSize,
9473
+ hydrationGeneration
9474
+ });
9475
+ if (!reconcileResult.ok) return reconcileResult;
9476
+ incrementalItemsApplied = reconcileResult.appliedItems;
9477
+ }
9478
+ else reconcileTrackedChangesFromV2({
9383
9479
  superdoc,
9384
9480
  adapter: effectiveAdapter,
9385
9481
  documentId,
@@ -9399,6 +9495,12 @@ const useCommentsStore = defineStore("comments", () => {
9399
9495
  if (result.visibleWindowFallbackReason != null) hydrationResult.visibleWindowFallbackReason = result.visibleWindowFallbackReason;
9400
9496
  if (typeof result.visibleTrackedChangeExpectedRows === "number") hydrationResult.visibleTrackedChangeExpectedRows = result.visibleTrackedChangeExpectedRows;
9401
9497
  if (typeof result.visibleTrackedChangeObservedRows === "number") hydrationResult.visibleTrackedChangeObservedRows = result.visibleTrackedChangeObservedRows;
9498
+ for (const key of [
9499
+ "visibleTrackedChangeRequestedIds",
9500
+ "visibleTrackedChangeSucceededIds",
9501
+ "visibleTrackedChangeFailedIds"
9502
+ ]) if (Array.isArray(result[key])) hydrationResult[key] = [...result[key]];
9503
+ if (Array.isArray(result.visibleWindowGetFailures)) hydrationResult.visibleWindowGetFailures = result.visibleWindowGetFailures.map((failure) => ({ ...failure }));
9402
9504
  if (typeof result.refreshReason === "string") hydrationResult.refreshReason = result.refreshReason;
9403
9505
  if (typeof result.blocking === "boolean") hydrationResult.blocking = result.blocking;
9404
9506
  if (typeof result.pageCount === "number") hydrationResult.pageCount = result.pageCount;
@@ -9407,6 +9509,11 @@ const useCommentsStore = defineStore("comments", () => {
9407
9509
  if (typeof result.pagesSkippedAfterAbort === "number") hydrationResult.pagesSkippedAfterAbort = result.pagesSkippedAfterAbort;
9408
9510
  if (typeof result.totalItems === "number") hydrationResult.totalItems = result.totalItems;
9409
9511
  if (typeof result.coalesced === "boolean") hydrationResult.coalesced = result.coalesced;
9512
+ if (typeof result.evaluatedRevision === "string") hydrationResult.evaluatedRevision = result.evaluatedRevision;
9513
+ if (trackedChangesListMode === "background-reconcile") {
9514
+ hydrationResult.incrementalPagesApplied = incrementalPagesApplied;
9515
+ hydrationResult.incrementalItemsApplied = incrementalItemsApplied;
9516
+ }
9410
9517
  return hydrationResult;
9411
9518
  });
9412
9519
  const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [], allResolved } = {}) => {
@@ -9561,7 +9668,7 @@ const useCommentsStore = defineStore("comments", () => {
9561
9668
  }
9562
9669
  };
9563
9670
  };
9564
- const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9671
+ const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9565
9672
  documentId: documentId ?? null,
9566
9673
  itemCount: Array.isArray(items) ? items.length : null,
9567
9674
  pruneStale,
@@ -9570,8 +9677,9 @@ const useCommentsStore = defineStore("comments", () => {
9570
9677
  if (!adapter || !Array.isArray(items)) return;
9571
9678
  if (!isCurrentV2TrackedChangesAdapter(adapter)) return;
9572
9679
  const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9573
- const liveAnchorKeys = /* @__PURE__ */ new Set();
9574
- const liveIds = /* @__PURE__ */ new Set();
9680
+ const liveAnchorKeys = suppliedLiveAnchorKeys instanceof Set ? suppliedLiveAnchorKeys : /* @__PURE__ */ new Set();
9681
+ const liveIds = suppliedLiveIds instanceof Set ? suppliedLiveIds : /* @__PURE__ */ new Set();
9682
+ let appliedCount = 0;
9575
9683
  const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
9576
9684
  const identitySpan = startInteractionSpan("store.trackedChanges.batchIdentity", "store-reconciliation", { itemCount: items.length });
9577
9685
  const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
@@ -9591,13 +9699,95 @@ const useCommentsStore = defineStore("comments", () => {
9591
9699
  broadcastChanges: false,
9592
9700
  trackedChangeIdentityIndex
9593
9701
  });
9702
+ appliedCount += 1;
9594
9703
  }
9595
9704
  } finally {
9596
9705
  endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
9597
9706
  }
9598
- if (!effectiveDocumentId || !pruneStale) return;
9707
+ if (!effectiveDocumentId || !pruneStale) return {
9708
+ liveIds,
9709
+ liveAnchorKeys,
9710
+ appliedCount
9711
+ };
9599
9712
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9713
+ return {
9714
+ liveIds,
9715
+ liveAnchorKeys,
9716
+ appliedCount
9717
+ };
9600
9718
  });
9719
+ const reconcileTrackedChangesFromV2Incrementally = async ({ superdoc, adapter, documentId, items, pruneStale, isCurrent, batchSize = TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE, hydrationGeneration } = {}) => {
9720
+ if (!adapter || !Array.isArray(items)) return {
9721
+ ok: false,
9722
+ reason: "reconcile-input-invalid"
9723
+ };
9724
+ const current = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(adapter);
9725
+ if (!current()) return {
9726
+ ok: false,
9727
+ reason: "reconciliation-stale"
9728
+ };
9729
+ const effectiveBatchSize = Math.max(1, Math.floor(Number(batchSize) || TRACKED_CHANGE_BACKGROUND_RECONCILE_BATCH_SIZE));
9730
+ const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9731
+ const liveIds = /* @__PURE__ */ new Set();
9732
+ const liveAnchorKeys = /* @__PURE__ */ new Set();
9733
+ let appliedItems = 0;
9734
+ let batchCount = 0;
9735
+ for (let offset = 0; offset < items.length; offset += effectiveBatchSize) {
9736
+ if (!current()) return {
9737
+ ok: false,
9738
+ reason: "reconciliation-stale",
9739
+ appliedItems,
9740
+ batchCount
9741
+ };
9742
+ const batch = items.slice(offset, offset + effectiveBatchSize);
9743
+ if (!reconcileTrackedChangesFromV2({
9744
+ superdoc,
9745
+ adapter,
9746
+ documentId: effectiveDocumentId,
9747
+ items: batch,
9748
+ pruneStale: false,
9749
+ liveIds,
9750
+ liveAnchorKeys,
9751
+ hydrationGeneration
9752
+ }) || !current()) return {
9753
+ ok: false,
9754
+ reason: "reconciliation-stale",
9755
+ appliedItems,
9756
+ batchCount
9757
+ };
9758
+ appliedItems += batch.length;
9759
+ batchCount += 1;
9760
+ if (offset + effectiveBatchSize < items.length) await yieldForTrackedChangeBackgroundReconcile();
9761
+ }
9762
+ if (!current()) return {
9763
+ ok: false,
9764
+ reason: "reconciliation-stale",
9765
+ appliedItems,
9766
+ batchCount
9767
+ };
9768
+ if (pruneStale && effectiveDocumentId) {
9769
+ withInteractionSpan("store.trackedChanges.reconcile.finalPrune", "store-reconciliation", {
9770
+ documentId: effectiveDocumentId,
9771
+ liveIds: liveIds.size,
9772
+ liveAnchorKeys: liveAnchorKeys.size
9773
+ }, () => {
9774
+ if (!current()) return;
9775
+ pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9776
+ });
9777
+ if (!current()) return {
9778
+ ok: false,
9779
+ reason: "reconciliation-stale",
9780
+ appliedItems,
9781
+ batchCount
9782
+ };
9783
+ }
9784
+ return {
9785
+ ok: true,
9786
+ appliedItems,
9787
+ batchCount,
9788
+ pruned: Boolean(pruneStale && effectiveDocumentId)
9789
+ };
9790
+ };
9601
9791
  const getV2TrackedChangeRowCount = (documentId = null) => {
9602
9792
  const normalizedDocumentId = documentId == null ? null : String(documentId);
9603
9793
  return commentsList.value.filter((comment) => {
@@ -18227,6 +18417,7 @@ var _sfc_main$20 = {
18227
18417
  const v2ReviewHydrationController = resolvedEditorIntegration.createReviewHydrationController({
18228
18418
  hydrateComments: (ctx) => commentsStore.hydrateCommentsFromV2?.(ctx),
18229
18419
  hydrateTrackedChanges: (ctx) => commentsStore.hydrateTrackedChangesFromV2?.(ctx),
18420
+ primeTrackedChanges: (ctx) => ctx.trackedChangesAdapter?.primeVisibleTrackedChanges?.(ctx.targetIds, ctx.plan),
18230
18421
  readReviewSnapshot: (ctx) => ctx.trackedChangesAdapter?.readReviewSnapshot?.(ctx.snapshot),
18231
18422
  validateReviewSnapshot: (_ctx, result) => result?.validate?.() ?? {
18232
18423
  ok: false,
@@ -18240,6 +18431,8 @@ var _sfc_main$20 = {
18240
18431
  commentItems: result.comments?.items,
18241
18432
  trackedChangeItems: result.trackedChanges?.items,
18242
18433
  trackedList: result.trackedChanges,
18434
+ sourceCoverageRevision: result.sourceCoverageRevision,
18435
+ evaluatedRevision: result.evaluatedRevision,
18243
18436
  patch: (callback) => commentsStore.$patch(callback)
18244
18437
  }) ?? {
18245
18438
  ok: false,
@@ -18917,6 +19110,12 @@ var _sfc_main$20 = {
18917
19110
  v2ReviewSidebarUnlocked.value = true;
18918
19111
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
18919
19112
  }
19113
+ },
19114
+ onCommittedPagePaint: (commit) => {
19115
+ v2ReviewHydrationController.onCommittedPagePaint?.({
19116
+ ...commit,
19117
+ documentId: commit?.payload?.documentId ?? null
19118
+ });
18920
19119
  }
18921
19120
  });
18922
19121
  const collectV2TrackedChangeRestampIds = (impactOrIds) => {
@@ -19140,7 +19339,7 @@ var _sfc_main$20 = {
19140
19339
  v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
19141
19340
  };
19142
19341
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
19143
- hydrate: () => hydrateV2ReviewRowsFromHost(),
19342
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
19144
19343
  getActiveDocumentId: () => {
19145
19344
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
19146
19345
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -19393,6 +19592,12 @@ var _sfc_main$20 = {
19393
19592
  onCommentsUpdate: onEditorCommentsUpdate,
19394
19593
  onFontsResolved: onFontsResolvedFn,
19395
19594
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
19595
+ onReviewWindowPlanned: (payload) => {
19596
+ v2ReviewHydrationController.onReviewWindowPlanned?.({
19597
+ ...payload,
19598
+ documentId: doc.id
19599
+ });
19600
+ },
19396
19601
  fontAssets: proxy.$superdoc.config.fonts,
19397
19602
  proofing: resolvedProofingConfig.value,
19398
19603
  isNewFile,
@@ -20355,7 +20560,7 @@ var _sfc_main$20 = {
20355
20560
  };
20356
20561
  }
20357
20562
  };
20358
- var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-e489229f"]]);
20563
+ var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-c3bc8a46"]]);
20359
20564
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
20360
20565
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
20361
20566
  var piniaDevtoolsSuppressionState = {
@@ -39092,7 +39297,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
39092
39297
  this.config.colors = shuffleArray(this.config.colors);
39093
39298
  this.userColorMap = /* @__PURE__ */ new Map();
39094
39299
  this.colorIndex = 0;
39095
- this.version = "2.4.0-next.3";
39300
+ this.version = "2.4.0-next.4";
39096
39301
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
39097
39302
  this.superdocId = config.superdocId || require_uuid.v4_default();
39098
39303
  this.colors = this.config.colors ?? [];