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.
@@ -5,7 +5,7 @@ import { t as v4_default } from "./chunks/uuid-B2Sqk-3p.es.js";
5
5
  import { a as init_dist$1, i as global, n as init_dist$2, o as Buffer, r as process$1, s as init_dist, t as require_jszip_min } from "./chunks/jszip-C8srOKAO.es.js";
6
6
  import { t as __plugin_vue_export_helper_default } from "./chunks/_plugin-vue_export-helper-CInC0bKI.es.js";
7
7
  import { n as PDF_TO_CSS_UNITS } from "./chunks/constants-CY3R3_kF.es.js";
8
- import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as composeAuthorColorResolver, t as createSuperDocUI } from "./chunks/create-super-doc-ui-D4kk9AjM.es.js";
8
+ import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, s as composeAuthorColorResolver, t as createSuperDocUI } from "./chunks/create-super-doc-ui-Dli6Wq70.es.js";
9
9
  import * as Vue from "vue";
10
10
  import { Fragment, Teleport, Transition, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineAsyncComponent, defineComponent, effectScope, getCurrentInstance, getCurrentScope, h, hasInjectionContext, inject, isReactive, isRef, markRaw, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onActivated, onBeforeUnmount, onDeactivated, onMounted, onScopeDispose, openBlock, reactive, ref, renderList, renderSlot, resolveDirective, resolveDynamicComponent, shallowRef, toDisplayString, toRaw, toRef, toRefs, unref, useAttrs, vModelText, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
11
11
  import "y-websocket";
@@ -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) => ({
@@ -6720,8 +6730,17 @@ const useCommentsStore = defineStore("comments", () => {
6720
6730
  return viewingVisibility.commentsVisible || viewingVisibility.trackChangesVisible;
6721
6731
  });
6722
6732
  const v2CommentsAdapter = shallowRef(null);
6733
+ const v2CommentSyncGenerations = /* @__PURE__ */ new WeakMap();
6734
+ const commentSyncGeneration = (adapter) => v2CommentSyncGenerations.get(adapter) ?? 0;
6735
+ const supersedeCommentSyncGeneration = (adapter) => {
6736
+ if (!adapter || typeof adapter !== "object" && typeof adapter !== "function") return 0;
6737
+ const next = commentSyncGeneration(adapter) + 1;
6738
+ v2CommentSyncGenerations.set(adapter, next);
6739
+ return next;
6740
+ };
6723
6741
  const setV2CommentsAdapter = (adapter) => {
6724
6742
  v2CommentsAdapter.value = adapter ?? null;
6743
+ if (adapter && !v2CommentSyncGenerations.has(adapter)) v2CommentSyncGenerations.set(adapter, 0);
6725
6744
  };
6726
6745
  const getV2CommentsAdapter = (superdoc) => {
6727
6746
  const fromFacade = superdoc?.activeEditor?.v2Comments ?? null;
@@ -6730,14 +6749,27 @@ const useCommentsStore = defineStore("comments", () => {
6730
6749
  };
6731
6750
  const isV2EditorActive = (superdoc) => superdoc?.activeEditor?.editorVersion === 2 || v2CommentsAdapter.value !== null;
6732
6751
  const v2TrackedChangesAdapter = shallowRef(null);
6752
+ const v2TrackedChangeSyncGenerations = /* @__PURE__ */ new WeakMap();
6753
+ const trackedChangeSyncGeneration = (adapter) => v2TrackedChangeSyncGenerations.get(adapter) ?? 0;
6754
+ const supersedeTrackedChangeSyncGeneration = (adapter) => {
6755
+ if (!adapter || typeof adapter !== "object" && typeof adapter !== "function") return 0;
6756
+ const next = trackedChangeSyncGeneration(adapter) + 1;
6757
+ v2TrackedChangeSyncGenerations.set(adapter, next);
6758
+ return next;
6759
+ };
6733
6760
  const setV2TrackedChangesAdapter = (adapter) => {
6734
6761
  v2TrackedChangesAdapter.value = adapter ?? null;
6762
+ if (adapter && !v2TrackedChangeSyncGenerations.has(adapter)) v2TrackedChangeSyncGenerations.set(adapter, 0);
6735
6763
  };
6736
6764
  const getV2TrackedChangesAdapter = (superdoc) => {
6737
6765
  const fromFacade = superdoc?.activeEditor?.v2TrackedChanges ?? null;
6738
6766
  if (fromFacade) return fromFacade;
6739
6767
  return v2TrackedChangesAdapter.value;
6740
6768
  };
6769
+ const supersedeV2ReviewHydration = ({ commentsAdapter, trackedChangesAdapter } = {}) => ({
6770
+ commentsGeneration: supersedeCommentSyncGeneration(commentsAdapter ?? v2CommentsAdapter.value),
6771
+ trackedChangesGeneration: supersedeTrackedChangeSyncGeneration(trackedChangesAdapter ?? v2TrackedChangesAdapter.value)
6772
+ });
6741
6773
  const init = (config = {}) => {
6742
6774
  const updatedConfig = {
6743
6775
  ...commentsConfig,
@@ -7193,7 +7225,7 @@ const useCommentsStore = defineStore("comments", () => {
7193
7225
  }
7194
7226
  activeEditor?.commands?.setActiveComment({ commentId: activeComment.value });
7195
7227
  };
7196
- const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, documentState = void 0 }) => {
7228
+ const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, documentState = void 0, trackedChangeIdentityIndex = null }) => {
7197
7229
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
7198
7230
  event: params?.event ?? null,
7199
7231
  changeId: params?.changeId ?? null,
@@ -7296,6 +7328,17 @@ const useCommentsStore = defineStore("comments", () => {
7296
7328
  if (normalizedAnchorKey && commentAnchorKey) return commentAnchorKey === normalizedAnchorKey;
7297
7329
  return false;
7298
7330
  };
7331
+ if (trackedChangeIdentityIndex) {
7332
+ const candidates = trackedChangeIdentityIndex.candidates({
7333
+ changeId: normalizedChangeId,
7334
+ importedId: normalizedImportedId,
7335
+ anchorKey: normalizedAnchorKey,
7336
+ canonicalId: normalizedTrackedChangeCanonicalId,
7337
+ positionAliases: normalizedTrackedChangePositionAliases
7338
+ });
7339
+ for (const trackedComment of candidates) if (matchesId(trackedComment) && (!normalizedDocumentId || belongsToTrackedChangeSyncDocument(trackedComment, normalizedDocumentId))) return trackedComment;
7340
+ return null;
7341
+ }
7299
7342
  if (normalizedDocumentId) return commentsList.value.find((trackedComment) => matchesId(trackedComment) && belongsToTrackedChangeSyncDocument(trackedComment, normalizedDocumentId));
7300
7343
  return commentsList.value.find(matchesId);
7301
7344
  };
@@ -7364,6 +7407,7 @@ const useCommentsStore = defineStore("comments", () => {
7364
7407
  const existing = findTrackedChangeById();
7365
7408
  if (existing) {
7366
7409
  if (!updateExistingTrackedChange(existing)) return;
7410
+ trackedChangeIdentityIndex?.add(existing);
7367
7411
  emitTrackedChangeEvent({
7368
7412
  type: COMMENT_EVENTS.UPDATE,
7369
7413
  comment: getCommentEventPayload(existing)
@@ -7375,10 +7419,12 @@ const useCommentsStore = defineStore("comments", () => {
7375
7419
  comment,
7376
7420
  broadcastChanges
7377
7421
  });
7422
+ trackedChangeIdentityIndex?.add(comment);
7378
7423
  } else if (event === "update") {
7379
7424
  const existingTrackedChange = findTrackedChangeById();
7380
7425
  if (!existingTrackedChange) return;
7381
7426
  if (!updateExistingTrackedChange(existingTrackedChange)) return;
7427
+ trackedChangeIdentityIndex?.add(existingTrackedChange);
7382
7428
  emitTrackedChangeEvent({
7383
7429
  type: COMMENT_EVENTS.UPDATE,
7384
7430
  comment: getCommentEventPayload(existingTrackedChange)
@@ -8111,7 +8157,7 @@ const useCommentsStore = defineStore("comments", () => {
8111
8157
  if (!adapter) return false;
8112
8158
  return v2TrackedChangesAdapter.value === adapter;
8113
8159
  };
8114
- 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", {
8115
8161
  commentItemCount: Array.isArray(commentItems) ? commentItems.length : null,
8116
8162
  trackedItemCount: Array.isArray(trackedChangeItems) ? trackedChangeItems.length : null
8117
8163
  }, () => {
@@ -8149,11 +8195,28 @@ const useCommentsStore = defineStore("comments", () => {
8149
8195
  ok: false,
8150
8196
  reason: "adapter-mapper-missing"
8151
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
+ }
8152
8215
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
8153
8216
  let preparedInputs;
8154
8217
  let preparedParams;
8155
8218
  try {
8156
- preparedInputs = commentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8219
+ preparedInputs = effectiveCommentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8157
8220
  fileId: normalizedDocumentId,
8158
8221
  fileType
8159
8222
  })).filter(Boolean);
@@ -8182,8 +8245,9 @@ const useCommentsStore = defineStore("comments", () => {
8182
8245
  superdoc,
8183
8246
  adapter: commentsAdapter,
8184
8247
  documentId: normalizedDocumentId,
8185
- items: commentItems,
8186
- preparedInputs
8248
+ items: effectiveCommentItems,
8249
+ preparedInputs,
8250
+ pruneStale: !commentItemsArePartial
8187
8251
  });
8188
8252
  });
8189
8253
  return {
@@ -8192,15 +8256,31 @@ const useCommentsStore = defineStore("comments", () => {
8192
8256
  trackedItems: preparedParams.length
8193
8257
  };
8194
8258
  });
8195
- const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId } = {}) => 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 () => {
8196
8260
  const effectiveAdapter = adapter ?? getV2CommentsAdapter(superdoc);
8197
- 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 {
8198
8264
  ok: false,
8199
8265
  reason: "adapter-missing"
8200
8266
  };
8267
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2CommentsAdapter(effectiveAdapter);
8268
+ if (!hydrationIsCurrent()) return {
8269
+ ok: false,
8270
+ reason: "adapter-stale"
8271
+ };
8272
+ if (signal?.aborted) return {
8273
+ ok: false,
8274
+ reason: "review-hydration-superseded",
8275
+ lateResultDropped: false
8276
+ };
8277
+ const syncGeneration = commentSyncGeneration(effectiveAdapter);
8201
8278
  let result;
8202
8279
  try {
8203
- result = await effectiveAdapter.refresh();
8280
+ const refreshOptions = {};
8281
+ if (signal) refreshOptions.signal = signal;
8282
+ if (typeof hydrationGeneration === "number") refreshOptions.hydrationGeneration = hydrationGeneration;
8283
+ result = Object.keys(refreshOptions).length > 0 ? await read.call(effectiveAdapter, refreshOptions) : await read.call(effectiveAdapter);
8204
8284
  } catch (err) {
8205
8285
  return {
8206
8286
  ok: false,
@@ -8208,25 +8288,41 @@ const useCommentsStore = defineStore("comments", () => {
8208
8288
  detail: err?.message ?? String(err)
8209
8289
  };
8210
8290
  }
8211
- if (!isCurrentV2CommentsAdapter(effectiveAdapter)) return {
8291
+ if (!hydrationIsCurrent()) return {
8212
8292
  ok: false,
8213
8293
  reason: "adapter-stale"
8214
8294
  };
8295
+ if (signal?.aborted || commentSyncGeneration(effectiveAdapter) !== syncGeneration) return {
8296
+ ok: false,
8297
+ reason: "review-hydration-superseded",
8298
+ lateResultDropped: true
8299
+ };
8215
8300
  if (!result?.ok) return result;
8301
+ if (signal?.aborted || commentSyncGeneration(effectiveAdapter) !== syncGeneration) return {
8302
+ ok: false,
8303
+ reason: "review-hydration-superseded",
8304
+ lateResultDropped: true
8305
+ };
8216
8306
  reconcileCommentsFromV2({
8217
8307
  superdoc,
8218
8308
  adapter: effectiveAdapter,
8219
8309
  documentId,
8220
- items: result.items ?? []
8310
+ items: result.items ?? [],
8311
+ pruneStale: !visibleWindow,
8312
+ hydrationGeneration
8221
8313
  });
8222
8314
  return {
8223
8315
  ok: true,
8224
- items: result.items ?? []
8316
+ items: result.items ?? [],
8317
+ reconciled: true,
8318
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {},
8319
+ ...result.visibleWindowSource != null ? { visibleWindowSource: result.visibleWindowSource } : {}
8225
8320
  };
8226
8321
  });
8227
- const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8322
+ const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, pruneStale = true, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8228
8323
  documentId: documentId ?? null,
8229
- itemCount: Array.isArray(items) ? items.length : null
8324
+ itemCount: Array.isArray(items) ? items.length : null,
8325
+ hydrationGeneration: hydrationGeneration ?? null
8230
8326
  }, () => {
8231
8327
  if (!adapter || !Array.isArray(items)) return { added: null };
8232
8328
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -8341,6 +8437,7 @@ const useCommentsStore = defineStore("comments", () => {
8341
8437
  seenIncoming.add(cid);
8342
8438
  nextList.push(existing);
8343
8439
  }
8440
+ if (!input && !pruneStale) nextList.push(existing);
8344
8441
  }
8345
8442
  for (const [cid, input] of incomingByCommentId.entries()) {
8346
8443
  if (seenIncoming.has(cid)) continue;
@@ -9000,7 +9097,7 @@ const useCommentsStore = defineStore("comments", () => {
9000
9097
  let removed = false;
9001
9098
  const targetIndex = targetComment ? commentsList.value.indexOf(targetComment) : -1;
9002
9099
  const targetIds = targetComment?.trackedChange ? collectTrackedChangeDecisionIds(targetComment) : /* @__PURE__ */ new Set();
9003
- if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(normalizedDecidedIds).some((id) => targetIds.has(id))) {
9100
+ if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(targetIds).some((id) => normalizedDecidedIds.has(id))) {
9004
9101
  targetIds.forEach((id) => removedIds.add(id));
9005
9102
  commentsList.value.splice(targetIndex, 1);
9006
9103
  removed = true;
@@ -9008,7 +9105,7 @@ const useCommentsStore = defineStore("comments", () => {
9008
9105
  if (!comment?.trackedChange) return true;
9009
9106
  if (!belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
9010
9107
  const ids = collectTrackedChangeDecisionIds(comment);
9011
- if (!Array.from(normalizedDecidedIds).some((id) => ids.has(id))) return true;
9108
+ if (!Array.from(ids).some((id) => normalizedDecidedIds.has(id))) return true;
9012
9109
  removed = true;
9013
9110
  ids.forEach((id) => removedIds.add(id));
9014
9111
  return false;
@@ -9024,6 +9121,31 @@ const useCommentsStore = defineStore("comments", () => {
9024
9121
  removedIds
9025
9122
  };
9026
9123
  };
9124
+ const clearAllResolvedTrackedChangeRows = ({ adapter, documentId } = {}) => {
9125
+ const normalizedDocumentId = normalizeCommentId(documentId);
9126
+ if (!adapter || !normalizedDocumentId) return {
9127
+ removed: 0,
9128
+ removedIds: /* @__PURE__ */ new Set()
9129
+ };
9130
+ supersedeTrackedChangeSyncGeneration(adapter);
9131
+ const removedIds = /* @__PURE__ */ new Set();
9132
+ const activeId = normalizeCommentId(activeComment.value);
9133
+ let activeWasRemoved = false;
9134
+ const next = commentsList.value.filter((comment) => {
9135
+ if (!comment?.trackedChange || !belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
9136
+ const aliases = collectTrackedChangeDecisionIds(comment);
9137
+ aliases.forEach((id) => removedIds.add(id));
9138
+ if (activeId && aliases.has(activeId)) activeWasRemoved = true;
9139
+ return false;
9140
+ });
9141
+ const removed = commentsList.value.length - next.length;
9142
+ if (removed > 0) commentsList.value = next;
9143
+ if (activeWasRemoved) clearActiveCommentSelection();
9144
+ return {
9145
+ removed,
9146
+ removedIds
9147
+ };
9148
+ };
9027
9149
  const clearV2ActiveTrackedChangeTarget = (adapter, decidedId) => {
9028
9150
  if (!decidedId || typeof adapter?.clearActiveTrackedChangeTargetIfMatches !== "function") return;
9029
9151
  try {
@@ -9234,7 +9356,7 @@ const useCommentsStore = defineStore("comments", () => {
9234
9356
  success: Boolean(command(id))
9235
9357
  };
9236
9358
  };
9237
- const hydrateTrackedChangesFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", refreshReason, blocking } = {}) => 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", {
9238
9360
  documentId: documentId ?? null,
9239
9361
  trackedChangesListMode,
9240
9362
  refreshReason: refreshReason ?? null,
@@ -9245,11 +9367,49 @@ const useCommentsStore = defineStore("comments", () => {
9245
9367
  ok: false,
9246
9368
  reason: "adapter-missing"
9247
9369
  };
9370
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(effectiveAdapter);
9371
+ if (!hydrationIsCurrent()) return {
9372
+ ok: false,
9373
+ reason: "adapter-stale"
9374
+ };
9375
+ const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9376
+ const hydrationCanContinue = () => hydrationIsCurrent() && !signal?.aborted && trackedChangeSyncGeneration(effectiveAdapter) === syncGeneration;
9377
+ if (signal?.aborted) return {
9378
+ ok: false,
9379
+ reason: "review-hydration-superseded",
9380
+ lateResultDropped: false
9381
+ };
9382
+ const incrementalLiveIds = /* @__PURE__ */ new Set();
9383
+ const incrementalLiveAnchorKeys = /* @__PURE__ */ new Set();
9384
+ let incrementalPagesApplied = 0;
9385
+ let incrementalItemsApplied = 0;
9248
9386
  let result;
9249
9387
  try {
9250
9388
  const listOptions = { mode: trackedChangesListMode };
9389
+ if (Array.isArray(targetIds)) listOptions.targetIds = targetIds;
9251
9390
  if (refreshReason != null) listOptions.refreshReason = refreshReason;
9252
9391
  if (typeof blocking === "boolean") listOptions.blocking = blocking;
9392
+ if (signal) listOptions.signal = signal;
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
+ }
9253
9413
  result = await effectiveAdapter.listTrackedChanges(listOptions);
9254
9414
  } catch (err) {
9255
9415
  return {
@@ -9258,22 +9418,68 @@ const useCommentsStore = defineStore("comments", () => {
9258
9418
  detail: err?.message ?? String(err)
9259
9419
  };
9260
9420
  }
9261
- if (!isCurrentV2TrackedChangesAdapter(effectiveAdapter)) return {
9421
+ if (!hydrationIsCurrent()) return {
9262
9422
  ok: false,
9263
9423
  reason: "adapter-stale"
9264
9424
  };
9425
+ if (trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9426
+ ok: false,
9427
+ reason: "review-hydration-superseded",
9428
+ lateResultDropped: true
9429
+ };
9430
+ if (signal?.aborted) return {
9431
+ ok: false,
9432
+ reason: "review-hydration-superseded",
9433
+ lateResultDropped: true
9434
+ };
9265
9435
  if (!result?.ok) return result;
9266
9436
  const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete === true && result.sourceCoverageComplete === true;
9267
- reconcileTrackedChangesFromV2({
9437
+ if (signal?.aborted || trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9438
+ ok: false,
9439
+ reason: "review-hydration-superseded",
9440
+ lateResultDropped: true
9441
+ };
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({
9268
9471
  superdoc,
9269
9472
  adapter: effectiveAdapter,
9270
9473
  documentId,
9271
9474
  items: result.items ?? [],
9272
- pruneStale
9475
+ pruneStale,
9476
+ hydrationGeneration
9273
9477
  });
9274
9478
  const hydrationResult = {
9275
9479
  ok: true,
9276
- items: result.items ?? []
9480
+ items: result.items ?? [],
9481
+ reconciled: true,
9482
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {}
9277
9483
  };
9278
9484
  if (typeof result.complete === "boolean") hydrationResult.complete = result.complete;
9279
9485
  if (typeof result.sourceCoverageComplete === "boolean") hydrationResult.sourceCoverageComplete = result.sourceCoverageComplete;
@@ -9281,14 +9487,28 @@ const useCommentsStore = defineStore("comments", () => {
9281
9487
  if (result.visibleWindowFallbackReason != null) hydrationResult.visibleWindowFallbackReason = result.visibleWindowFallbackReason;
9282
9488
  if (typeof result.visibleTrackedChangeExpectedRows === "number") hydrationResult.visibleTrackedChangeExpectedRows = result.visibleTrackedChangeExpectedRows;
9283
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 }));
9284
9496
  if (typeof result.refreshReason === "string") hydrationResult.refreshReason = result.refreshReason;
9285
9497
  if (typeof result.blocking === "boolean") hydrationResult.blocking = result.blocking;
9286
9498
  if (typeof result.pageCount === "number") hydrationResult.pageCount = result.pageCount;
9499
+ if (typeof result.pagesStarted === "number") hydrationResult.pagesStarted = result.pagesStarted;
9500
+ if (typeof result.pagesCompleted === "number") hydrationResult.pagesCompleted = result.pagesCompleted;
9501
+ if (typeof result.pagesSkippedAfterAbort === "number") hydrationResult.pagesSkippedAfterAbort = result.pagesSkippedAfterAbort;
9287
9502
  if (typeof result.totalItems === "number") hydrationResult.totalItems = result.totalItems;
9288
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
+ }
9289
9509
  return hydrationResult;
9290
9510
  });
9291
- const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [] } = {}) => {
9511
+ const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [], allResolved } = {}) => {
9292
9512
  const effectiveAdapter = adapter ?? getV2TrackedChangesAdapter(superdoc);
9293
9513
  if (!effectiveAdapter) return {
9294
9514
  ok: false,
@@ -9299,6 +9519,26 @@ const useCommentsStore = defineStore("comments", () => {
9299
9519
  reason: "adapter-stale"
9300
9520
  };
9301
9521
  const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9522
+ if (allResolved && effectiveDocumentId == null) return {
9523
+ ok: false,
9524
+ reason: "document-id-missing"
9525
+ };
9526
+ if (allResolved) return {
9527
+ ok: true,
9528
+ items: [],
9529
+ resolvedIds: [],
9530
+ unresolvedIds: [],
9531
+ allResolved: true,
9532
+ removedRows: withInteractionSpan("store.trackedChanges.allResolved", "store-reconciliation", {
9533
+ documentId: String(effectiveDocumentId),
9534
+ logicalTargetCount: allResolved.logicalTargetCount ?? null,
9535
+ physicalCarrierCount: allResolved.physicalCarrierCount ?? null
9536
+ }, () => clearAllResolvedTrackedChangeRows({
9537
+ adapter: effectiveAdapter,
9538
+ documentId: effectiveDocumentId
9539
+ })).removed
9540
+ };
9541
+ const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9302
9542
  const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
9303
9543
  const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
9304
9544
  if (normalizedRemovedIds.size > 0 && normalizedUpsertIds.length === 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
@@ -9337,6 +9577,10 @@ const useCommentsStore = defineStore("comments", () => {
9337
9577
  ok: false,
9338
9578
  reason: "adapter-stale"
9339
9579
  };
9580
+ if (trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9581
+ ok: false,
9582
+ reason: "generation-stale"
9583
+ };
9340
9584
  const resolvedIds = [];
9341
9585
  const unresolvedIds = [];
9342
9586
  const items = [];
@@ -9363,35 +9607,188 @@ const useCommentsStore = defineStore("comments", () => {
9363
9607
  failures: reads.map(({ result }) => result).filter((result) => !result?.ok)
9364
9608
  };
9365
9609
  };
9366
- const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9610
+ const createTrackedChangeBatchIdentityIndex = (documentId) => {
9611
+ const byIdentityAlias = /* @__PURE__ */ new Map();
9612
+ const indexedRows = /* @__PURE__ */ new Set();
9613
+ let aliasLookups = 0;
9614
+ let candidateVisits = 0;
9615
+ const addAlias = (index, value, comment) => {
9616
+ const alias = normalizeCommentId(value);
9617
+ if (!alias) return;
9618
+ const bucket = index.get(alias) ?? /* @__PURE__ */ new Set();
9619
+ bucket.add(comment);
9620
+ index.set(alias, bucket);
9621
+ };
9622
+ const add = (comment) => {
9623
+ if (!comment?.trackedChange) return;
9624
+ if (documentId && !belongsToTrackedChangeSyncDocument(comment, documentId)) return;
9625
+ indexedRows.add(comment);
9626
+ addAlias(byIdentityAlias, comment.commentId, comment);
9627
+ addAlias(byIdentityAlias, comment.importedId, comment);
9628
+ addAlias(byIdentityAlias, comment.trackedChangeCanonicalId, comment);
9629
+ addAlias(byIdentityAlias, comment.trackedChangeAnchorKey, comment);
9630
+ getCommentAliasIds(comment).forEach((alias) => addAlias(byIdentityAlias, alias, comment));
9631
+ };
9632
+ commentsList.value.forEach(add);
9633
+ return {
9634
+ add,
9635
+ work: () => ({
9636
+ trackedRowsIndexed: indexedRows.size,
9637
+ invalidatedIdMembershipChecks: 0,
9638
+ incomingAliasLookups: aliasLookups,
9639
+ candidateVisits
9640
+ }),
9641
+ candidates({ changeId, importedId, anchorKey, canonicalId, positionAliases = [] } = {}) {
9642
+ const out = /* @__PURE__ */ new Set();
9643
+ const include = (value) => {
9644
+ const alias = normalizeCommentId(value);
9645
+ if (!alias) return;
9646
+ aliasLookups += 1;
9647
+ const bucket = byIdentityAlias.get(alias);
9648
+ if (!bucket) return;
9649
+ candidateVisits += bucket.size;
9650
+ bucket.forEach((comment) => out.add(comment));
9651
+ };
9652
+ new Set([
9653
+ changeId,
9654
+ importedId,
9655
+ canonicalId,
9656
+ anchorKey,
9657
+ ...positionAliases
9658
+ ]).forEach(include);
9659
+ return out;
9660
+ }
9661
+ };
9662
+ };
9663
+ const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9367
9664
  documentId: documentId ?? null,
9368
9665
  itemCount: Array.isArray(items) ? items.length : null,
9369
- pruneStale
9666
+ pruneStale,
9667
+ hydrationGeneration: hydrationGeneration ?? null
9370
9668
  }, () => {
9371
9669
  if (!adapter || !Array.isArray(items)) return;
9372
9670
  if (!isCurrentV2TrackedChangesAdapter(adapter)) return;
9373
9671
  const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9374
- const liveAnchorKeys = /* @__PURE__ */ new Set();
9375
- 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;
9675
+ const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
9676
+ const identitySpan = startInteractionSpan("store.trackedChanges.batchIdentity", "store-reconciliation", { itemCount: items.length });
9376
9677
  const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
9377
- for (const params of paramsList) {
9378
- if (!params) continue;
9379
- if (params.event === "omit") {
9678
+ try {
9679
+ for (const params of paramsList) {
9680
+ if (!params) continue;
9681
+ if (params.event === "omit") {
9682
+ if (params.changeId != null) liveIds.add(String(params.changeId));
9683
+ continue;
9684
+ }
9685
+ if (effectiveDocumentId && params.documentId == null) params.documentId = effectiveDocumentId;
9380
9686
  if (params.changeId != null) liveIds.add(String(params.changeId));
9381
- continue;
9687
+ if (params.trackedChangeAnchorKey != null) liveAnchorKeys.add(String(params.trackedChangeAnchorKey));
9688
+ handleTrackedChangeUpdate({
9689
+ superdoc,
9690
+ params,
9691
+ broadcastChanges: false,
9692
+ trackedChangeIdentityIndex
9693
+ });
9694
+ appliedCount += 1;
9382
9695
  }
9383
- if (effectiveDocumentId && params.documentId == null) params.documentId = effectiveDocumentId;
9384
- if (params.changeId != null) liveIds.add(String(params.changeId));
9385
- if (params.trackedChangeAnchorKey != null) liveAnchorKeys.add(String(params.trackedChangeAnchorKey));
9386
- handleTrackedChangeUpdate({
9387
- superdoc,
9388
- params,
9389
- broadcastChanges: false
9390
- });
9696
+ } finally {
9697
+ endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
9391
9698
  }
9392
- if (!effectiveDocumentId || !pruneStale) return;
9699
+ if (!effectiveDocumentId || !pruneStale) return {
9700
+ liveIds,
9701
+ liveAnchorKeys,
9702
+ appliedCount
9703
+ };
9393
9704
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9705
+ return {
9706
+ liveIds,
9707
+ liveAnchorKeys,
9708
+ appliedCount
9709
+ };
9394
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
+ };
9783
+ const getV2TrackedChangeRowCount = (documentId = null) => {
9784
+ const normalizedDocumentId = documentId == null ? null : String(documentId);
9785
+ return commentsList.value.filter((comment) => {
9786
+ if (comment?.trackedChange !== true) return false;
9787
+ if (normalizedDocumentId == null) return true;
9788
+ const rowDocumentId = comment?.documentId ?? comment?.fileId ?? null;
9789
+ return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
9790
+ }).length;
9791
+ };
9395
9792
  const syncTrackedChangeComments = ({ superdoc, editor, broadcastChanges = true }) => {
9396
9793
  if (!superdoc || !editor) return;
9397
9794
  const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
@@ -9958,6 +10355,7 @@ const useCommentsStore = defineStore("comments", () => {
9958
10355
  getV2CommentsAdapter,
9959
10356
  hydrateCommentsFromV2,
9960
10357
  applyReviewSnapshotFromV2,
10358
+ supersedeV2ReviewHydration,
9961
10359
  reconcileCommentsFromV2,
9962
10360
  isV2EditorActive,
9963
10361
  replyCommentV2,
@@ -9967,6 +10365,7 @@ const useCommentsStore = defineStore("comments", () => {
9967
10365
  v2TrackedChangesAdapter,
9968
10366
  setV2TrackedChangesAdapter,
9969
10367
  getV2TrackedChangesAdapter,
10368
+ getV2TrackedChangeRowCount,
9970
10369
  hydrateTrackedChangesFromV2,
9971
10370
  reconcileTrackedChangeMutationFromV2,
9972
10371
  reconcileTrackedChangesFromV2,
@@ -17975,6 +18374,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
17975
18374
  const v2ReviewHydrationController = resolvedEditorIntegration.createReviewHydrationController({
17976
18375
  hydrateComments: (ctx) => commentsStore.hydrateCommentsFromV2?.(ctx),
17977
18376
  hydrateTrackedChanges: (ctx) => commentsStore.hydrateTrackedChangesFromV2?.(ctx),
18377
+ primeTrackedChanges: (ctx) => ctx.trackedChangesAdapter?.primeVisibleTrackedChanges?.(ctx.targetIds, ctx.plan),
17978
18378
  readReviewSnapshot: (ctx) => ctx.trackedChangesAdapter?.readReviewSnapshot?.(ctx.snapshot),
17979
18379
  validateReviewSnapshot: (_ctx, result) => result?.validate?.() ?? {
17980
18380
  ok: false,
@@ -17988,6 +18388,8 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
17988
18388
  commentItems: result.comments?.items,
17989
18389
  trackedChangeItems: result.trackedChanges?.items,
17990
18390
  trackedList: result.trackedChanges,
18391
+ sourceCoverageRevision: result.sourceCoverageRevision,
18392
+ evaluatedRevision: result.evaluatedRevision,
17991
18393
  patch: (callback) => commentsStore.$patch(callback)
17992
18394
  }) ?? {
17993
18395
  ok: false,
@@ -18665,6 +19067,12 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18665
19067
  v2ReviewSidebarUnlocked.value = true;
18666
19068
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
18667
19069
  }
19070
+ },
19071
+ onCommittedPagePaint: (commit) => {
19072
+ v2ReviewHydrationController.onCommittedPagePaint?.({
19073
+ ...commit,
19074
+ documentId: commit?.payload?.documentId ?? null
19075
+ });
18668
19076
  }
18669
19077
  });
18670
19078
  const collectV2TrackedChangeRestampIds = (impactOrIds) => {
@@ -18888,7 +19296,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18888
19296
  v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
18889
19297
  };
18890
19298
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
18891
- hydrate: () => hydrateV2ReviewRowsFromHost(),
19299
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
18892
19300
  getActiveDocumentId: () => {
18893
19301
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
18894
19302
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -18897,6 +19305,23 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18897
19305
  const onV2HostEvent = (document$1, event) => {
18898
19306
  if (!event) return;
18899
19307
  const documentId = document$1?.id ?? null;
19308
+ if (event.type === "review-mutation:started") {
19309
+ const activeEditor = proxy.$superdoc?.activeEditor ?? null;
19310
+ commentsStore.supersedeV2ReviewHydration?.({
19311
+ commentsAdapter: activeEditor?.v2Comments ?? null,
19312
+ trackedChangesAdapter: activeEditor?.v2TrackedChanges ?? null
19313
+ });
19314
+ v2ReviewHydrationController.beginMutation(event.reviewMutation);
19315
+ return;
19316
+ }
19317
+ if (event.type === "review-mutation:aborted") {
19318
+ v2ReviewHydrationController.settleMutation(event.reviewMutation?.token, {
19319
+ outcome: "aborted",
19320
+ resumeDomains: ["comments", "trackedChanges"],
19321
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19322
+ });
19323
+ return;
19324
+ }
18900
19325
  if (event.type === "reviewTarget:changed") {
18901
19326
  syncSidebarActiveCommentFromV2ReviewTarget(event.next);
18902
19327
  return;
@@ -18915,6 +19340,11 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18915
19340
  return;
18916
19341
  }
18917
19342
  if (event.type === "mutation:rejected") {
19343
+ if (event.reviewMutation?.token) v2ReviewHydrationController.settleMutation(event.reviewMutation.token, {
19344
+ outcome: "rejected",
19345
+ resumeDomains: ["comments", "trackedChanges"],
19346
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19347
+ });
18918
19348
  maybeNotifyV2AuthorRequired(documentId, event);
18919
19349
  return;
18920
19350
  }
@@ -18930,7 +19360,22 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
18930
19360
  const readiness = (proxy.$superdoc?.activeEditor ?? null)?.documentMutationReadiness ?? null;
18931
19361
  const receipt = event.origin === "history" ? null : event.receipt;
18932
19362
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
18933
- v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
19363
+ const reconciliation = v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
19364
+ if (event.reviewMutation?.token) {
19365
+ const reviewMutation = event.reviewMutation;
19366
+ const allResolved = Boolean(reviewImpact?.allResolved ?? event.trackedChangeAllResolved);
19367
+ const settleReviewMutation = (reconciled) => {
19368
+ const reconciledAllResolved = allResolved && reconciled === true;
19369
+ v2ReviewHydrationController.settleMutation(reviewMutation.token, {
19370
+ outcome: "committed",
19371
+ allResolved: reconciledAllResolved,
19372
+ resumeDomains: reconciledAllResolved ? ["comments"] : ["comments", "trackedChanges"],
19373
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19374
+ });
19375
+ };
19376
+ Promise.resolve(reconciliation).then(settleReviewMutation, () => settleReviewMutation(false));
19377
+ return;
19378
+ }
18934
19379
  if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
18935
19380
  hydrateV2CommentRowsFromHost();
18936
19381
  return;
@@ -19104,6 +19549,12 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
19104
19549
  onCommentsUpdate: onEditorCommentsUpdate,
19105
19550
  onFontsResolved: onFontsResolvedFn,
19106
19551
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
19552
+ onReviewWindowPlanned: (payload) => {
19553
+ v2ReviewHydrationController.onReviewWindowPlanned?.({
19554
+ ...payload,
19555
+ documentId: doc.id
19556
+ });
19557
+ },
19107
19558
  fontAssets: proxy.$superdoc.config.fonts,
19108
19559
  proofing: resolvedProofingConfig.value,
19109
19560
  isNewFile,
@@ -20065,7 +20516,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
20065
20516
  ], 38);
20066
20517
  };
20067
20518
  }
20068
- }, [["__scopeId", "data-v-d75089d2"]]);
20519
+ }, [["__scopeId", "data-v-c3bc8a46"]]);
20069
20520
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
20070
20521
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
20071
20522
  var piniaDevtoolsSuppressionState = {
@@ -38779,7 +39230,7 @@ var SuperDoc = class extends import_eventemitter3.default {
38779
39230
  this.config.colors = shuffleArray(this.config.colors);
38780
39231
  this.userColorMap = /* @__PURE__ */ new Map();
38781
39232
  this.colorIndex = 0;
38782
- this.version = "2.4.0-next.2";
39233
+ this.version = "2.4.0-next.4";
38783
39234
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
38784
39235
  this.superdocId = config.superdocId || v4_default();
38785
39236
  this.colors = this.config.colors ?? [];
@@ -39661,8 +40112,8 @@ var SuperDoc = class extends import_eventemitter3.default {
39661
40112
  },
39662
40113
  {
39663
40114
  feature: "shell.tracked-change-sidebar.bulk",
39664
- status: "not-shipped",
39665
- reason: "bulk-tracked-change-decisions-omitted: acceptAll/rejectAll are matrix-disabled by default in the v2 host"
40115
+ status: "supported",
40116
+ reason: "SD-4039/SD-4040: the shipped v2 command posture exposes all-story Accept All and Reject All through the canonical doc.trackChanges.decide({ target: { kind: 'all' } }) mutation"
39666
40117
  },
39667
40118
  {
39668
40119
  feature: "shell.tracked-change-sidebar.non-body",