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.
package/dist/superdoc.cjs CHANGED
@@ -6,7 +6,7 @@ const require_uuid = require("./chunks/uuid-CFp0WGVU.cjs");
6
6
  const require_jszip = require("./chunks/jszip-Cs9JBLlJ.cjs");
7
7
  const require__plugin_vue_export_helper = require("./chunks/_plugin-vue_export-helper-BTwbGDKw.cjs");
8
8
  const require_constants = require("./chunks/constants-sbCZ2O_A.cjs");
9
- const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-DXdWuTzm.cjs");
9
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BHMQEL2y.cjs");
10
10
  let vue = require("vue");
11
11
  vue = require_rolldown_runtime.__toESM(vue);
12
12
  require("y-websocket");
@@ -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) => ({
@@ -6728,8 +6738,17 @@ const useCommentsStore = defineStore("comments", () => {
6728
6738
  return viewingVisibility.commentsVisible || viewingVisibility.trackChangesVisible;
6729
6739
  });
6730
6740
  const v2CommentsAdapter = (0, vue.shallowRef)(null);
6741
+ const v2CommentSyncGenerations = /* @__PURE__ */ new WeakMap();
6742
+ const commentSyncGeneration = (adapter) => v2CommentSyncGenerations.get(adapter) ?? 0;
6743
+ const supersedeCommentSyncGeneration = (adapter) => {
6744
+ if (!adapter || typeof adapter !== "object" && typeof adapter !== "function") return 0;
6745
+ const next = commentSyncGeneration(adapter) + 1;
6746
+ v2CommentSyncGenerations.set(adapter, next);
6747
+ return next;
6748
+ };
6731
6749
  const setV2CommentsAdapter = (adapter) => {
6732
6750
  v2CommentsAdapter.value = adapter ?? null;
6751
+ if (adapter && !v2CommentSyncGenerations.has(adapter)) v2CommentSyncGenerations.set(adapter, 0);
6733
6752
  };
6734
6753
  const getV2CommentsAdapter = (superdoc) => {
6735
6754
  const fromFacade = superdoc?.activeEditor?.v2Comments ?? null;
@@ -6738,14 +6757,27 @@ const useCommentsStore = defineStore("comments", () => {
6738
6757
  };
6739
6758
  const isV2EditorActive = (superdoc) => superdoc?.activeEditor?.editorVersion === 2 || v2CommentsAdapter.value !== null;
6740
6759
  const v2TrackedChangesAdapter = (0, vue.shallowRef)(null);
6760
+ const v2TrackedChangeSyncGenerations = /* @__PURE__ */ new WeakMap();
6761
+ const trackedChangeSyncGeneration = (adapter) => v2TrackedChangeSyncGenerations.get(adapter) ?? 0;
6762
+ const supersedeTrackedChangeSyncGeneration = (adapter) => {
6763
+ if (!adapter || typeof adapter !== "object" && typeof adapter !== "function") return 0;
6764
+ const next = trackedChangeSyncGeneration(adapter) + 1;
6765
+ v2TrackedChangeSyncGenerations.set(adapter, next);
6766
+ return next;
6767
+ };
6741
6768
  const setV2TrackedChangesAdapter = (adapter) => {
6742
6769
  v2TrackedChangesAdapter.value = adapter ?? null;
6770
+ if (adapter && !v2TrackedChangeSyncGenerations.has(adapter)) v2TrackedChangeSyncGenerations.set(adapter, 0);
6743
6771
  };
6744
6772
  const getV2TrackedChangesAdapter = (superdoc) => {
6745
6773
  const fromFacade = superdoc?.activeEditor?.v2TrackedChanges ?? null;
6746
6774
  if (fromFacade) return fromFacade;
6747
6775
  return v2TrackedChangesAdapter.value;
6748
6776
  };
6777
+ const supersedeV2ReviewHydration = ({ commentsAdapter, trackedChangesAdapter } = {}) => ({
6778
+ commentsGeneration: supersedeCommentSyncGeneration(commentsAdapter ?? v2CommentsAdapter.value),
6779
+ trackedChangesGeneration: supersedeTrackedChangeSyncGeneration(trackedChangesAdapter ?? v2TrackedChangesAdapter.value)
6780
+ });
6749
6781
  const init = (config = {}) => {
6750
6782
  const updatedConfig = {
6751
6783
  ...commentsConfig,
@@ -7201,7 +7233,7 @@ const useCommentsStore = defineStore("comments", () => {
7201
7233
  }
7202
7234
  activeEditor?.commands?.setActiveComment({ commentId: activeComment.value });
7203
7235
  };
7204
- const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, documentState = void 0 }) => {
7236
+ const handleTrackedChangeUpdate = ({ superdoc, params, broadcastChanges = true, documentState = void 0, trackedChangeIdentityIndex = null }) => {
7205
7237
  const span = startInteractionSpan("store.trackedChanges.handleUpdate", "store-reconciliation", {
7206
7238
  event: params?.event ?? null,
7207
7239
  changeId: params?.changeId ?? null,
@@ -7304,6 +7336,17 @@ const useCommentsStore = defineStore("comments", () => {
7304
7336
  if (normalizedAnchorKey && commentAnchorKey) return commentAnchorKey === normalizedAnchorKey;
7305
7337
  return false;
7306
7338
  };
7339
+ if (trackedChangeIdentityIndex) {
7340
+ const candidates = trackedChangeIdentityIndex.candidates({
7341
+ changeId: normalizedChangeId,
7342
+ importedId: normalizedImportedId,
7343
+ anchorKey: normalizedAnchorKey,
7344
+ canonicalId: normalizedTrackedChangeCanonicalId,
7345
+ positionAliases: normalizedTrackedChangePositionAliases
7346
+ });
7347
+ for (const trackedComment of candidates) if (matchesId(trackedComment) && (!normalizedDocumentId || belongsToTrackedChangeSyncDocument(trackedComment, normalizedDocumentId))) return trackedComment;
7348
+ return null;
7349
+ }
7307
7350
  if (normalizedDocumentId) return commentsList.value.find((trackedComment) => matchesId(trackedComment) && belongsToTrackedChangeSyncDocument(trackedComment, normalizedDocumentId));
7308
7351
  return commentsList.value.find(matchesId);
7309
7352
  };
@@ -7372,6 +7415,7 @@ const useCommentsStore = defineStore("comments", () => {
7372
7415
  const existing = findTrackedChangeById();
7373
7416
  if (existing) {
7374
7417
  if (!updateExistingTrackedChange(existing)) return;
7418
+ trackedChangeIdentityIndex?.add(existing);
7375
7419
  emitTrackedChangeEvent({
7376
7420
  type: COMMENT_EVENTS.UPDATE,
7377
7421
  comment: getCommentEventPayload(existing)
@@ -7383,10 +7427,12 @@ const useCommentsStore = defineStore("comments", () => {
7383
7427
  comment,
7384
7428
  broadcastChanges
7385
7429
  });
7430
+ trackedChangeIdentityIndex?.add(comment);
7386
7431
  } else if (event === "update") {
7387
7432
  const existingTrackedChange = findTrackedChangeById();
7388
7433
  if (!existingTrackedChange) return;
7389
7434
  if (!updateExistingTrackedChange(existingTrackedChange)) return;
7435
+ trackedChangeIdentityIndex?.add(existingTrackedChange);
7390
7436
  emitTrackedChangeEvent({
7391
7437
  type: COMMENT_EVENTS.UPDATE,
7392
7438
  comment: getCommentEventPayload(existingTrackedChange)
@@ -8119,7 +8165,7 @@ const useCommentsStore = defineStore("comments", () => {
8119
8165
  if (!adapter) return false;
8120
8166
  return v2TrackedChangesAdapter.value === adapter;
8121
8167
  };
8122
- 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", {
8123
8169
  commentItemCount: Array.isArray(commentItems) ? commentItems.length : null,
8124
8170
  trackedItemCount: Array.isArray(trackedChangeItems) ? trackedChangeItems.length : null
8125
8171
  }, () => {
@@ -8157,11 +8203,28 @@ const useCommentsStore = defineStore("comments", () => {
8157
8203
  ok: false,
8158
8204
  reason: "adapter-mapper-missing"
8159
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
+ }
8160
8223
  const fileType = (normalizedDocumentId ? superdocStore.getDocument(normalizedDocumentId) : null)?.type ?? null;
8161
8224
  let preparedInputs;
8162
8225
  let preparedParams;
8163
8226
  try {
8164
- preparedInputs = commentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8227
+ preparedInputs = effectiveCommentItems.filter((item) => !isSyntheticTrackedChangeCommentLaneItem(item)).map((item) => commentsAdapter.mapV2CommentToUseCommentInput(item, {
8165
8228
  fileId: normalizedDocumentId,
8166
8229
  fileType
8167
8230
  })).filter(Boolean);
@@ -8190,8 +8253,9 @@ const useCommentsStore = defineStore("comments", () => {
8190
8253
  superdoc,
8191
8254
  adapter: commentsAdapter,
8192
8255
  documentId: normalizedDocumentId,
8193
- items: commentItems,
8194
- preparedInputs
8256
+ items: effectiveCommentItems,
8257
+ preparedInputs,
8258
+ pruneStale: !commentItemsArePartial
8195
8259
  });
8196
8260
  });
8197
8261
  return {
@@ -8200,15 +8264,31 @@ const useCommentsStore = defineStore("comments", () => {
8200
8264
  trackedItems: preparedParams.length
8201
8265
  };
8202
8266
  });
8203
- const hydrateCommentsFromV2 = async ({ superdoc, adapter, documentId } = {}) => 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 () => {
8204
8268
  const effectiveAdapter = adapter ?? getV2CommentsAdapter(superdoc);
8205
- 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 {
8206
8272
  ok: false,
8207
8273
  reason: "adapter-missing"
8208
8274
  };
8275
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2CommentsAdapter(effectiveAdapter);
8276
+ if (!hydrationIsCurrent()) return {
8277
+ ok: false,
8278
+ reason: "adapter-stale"
8279
+ };
8280
+ if (signal?.aborted) return {
8281
+ ok: false,
8282
+ reason: "review-hydration-superseded",
8283
+ lateResultDropped: false
8284
+ };
8285
+ const syncGeneration = commentSyncGeneration(effectiveAdapter);
8209
8286
  let result;
8210
8287
  try {
8211
- result = await effectiveAdapter.refresh();
8288
+ const refreshOptions = {};
8289
+ if (signal) refreshOptions.signal = signal;
8290
+ if (typeof hydrationGeneration === "number") refreshOptions.hydrationGeneration = hydrationGeneration;
8291
+ result = Object.keys(refreshOptions).length > 0 ? await read.call(effectiveAdapter, refreshOptions) : await read.call(effectiveAdapter);
8212
8292
  } catch (err) {
8213
8293
  return {
8214
8294
  ok: false,
@@ -8216,25 +8296,41 @@ const useCommentsStore = defineStore("comments", () => {
8216
8296
  detail: err?.message ?? String(err)
8217
8297
  };
8218
8298
  }
8219
- if (!isCurrentV2CommentsAdapter(effectiveAdapter)) return {
8299
+ if (!hydrationIsCurrent()) return {
8220
8300
  ok: false,
8221
8301
  reason: "adapter-stale"
8222
8302
  };
8303
+ if (signal?.aborted || commentSyncGeneration(effectiveAdapter) !== syncGeneration) return {
8304
+ ok: false,
8305
+ reason: "review-hydration-superseded",
8306
+ lateResultDropped: true
8307
+ };
8223
8308
  if (!result?.ok) return result;
8309
+ if (signal?.aborted || commentSyncGeneration(effectiveAdapter) !== syncGeneration) return {
8310
+ ok: false,
8311
+ reason: "review-hydration-superseded",
8312
+ lateResultDropped: true
8313
+ };
8224
8314
  reconcileCommentsFromV2({
8225
8315
  superdoc,
8226
8316
  adapter: effectiveAdapter,
8227
8317
  documentId,
8228
- items: result.items ?? []
8318
+ items: result.items ?? [],
8319
+ pruneStale: !visibleWindow,
8320
+ hydrationGeneration
8229
8321
  });
8230
8322
  return {
8231
8323
  ok: true,
8232
- items: result.items ?? []
8324
+ items: result.items ?? [],
8325
+ reconciled: true,
8326
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {},
8327
+ ...result.visibleWindowSource != null ? { visibleWindowSource: result.visibleWindowSource } : {}
8233
8328
  };
8234
8329
  });
8235
- const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8330
+ const reconcileCommentsFromV2 = ({ superdoc, adapter, documentId, items, preparedInputs = null, pruneStale = true, hydrationGeneration } = {}) => withInteractionSpan("store.comments.reconcile", "store-reconciliation", {
8236
8331
  documentId: documentId ?? null,
8237
- itemCount: Array.isArray(items) ? items.length : null
8332
+ itemCount: Array.isArray(items) ? items.length : null,
8333
+ hydrationGeneration: hydrationGeneration ?? null
8238
8334
  }, () => {
8239
8335
  if (!adapter || !Array.isArray(items)) return { added: null };
8240
8336
  if (!isCurrentV2CommentsAdapter(adapter)) return {
@@ -8349,6 +8445,7 @@ const useCommentsStore = defineStore("comments", () => {
8349
8445
  seenIncoming.add(cid);
8350
8446
  nextList.push(existing);
8351
8447
  }
8448
+ if (!input && !pruneStale) nextList.push(existing);
8352
8449
  }
8353
8450
  for (const [cid, input] of incomingByCommentId.entries()) {
8354
8451
  if (seenIncoming.has(cid)) continue;
@@ -9008,7 +9105,7 @@ const useCommentsStore = defineStore("comments", () => {
9008
9105
  let removed = false;
9009
9106
  const targetIndex = targetComment ? commentsList.value.indexOf(targetComment) : -1;
9010
9107
  const targetIds = targetComment?.trackedChange ? collectTrackedChangeDecisionIds(targetComment) : /* @__PURE__ */ new Set();
9011
- if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(normalizedDecidedIds).some((id) => targetIds.has(id))) {
9108
+ if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(targetIds).some((id) => normalizedDecidedIds.has(id))) {
9012
9109
  targetIds.forEach((id) => removedIds.add(id));
9013
9110
  commentsList.value.splice(targetIndex, 1);
9014
9111
  removed = true;
@@ -9016,7 +9113,7 @@ const useCommentsStore = defineStore("comments", () => {
9016
9113
  if (!comment?.trackedChange) return true;
9017
9114
  if (!belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
9018
9115
  const ids = collectTrackedChangeDecisionIds(comment);
9019
- if (!Array.from(normalizedDecidedIds).some((id) => ids.has(id))) return true;
9116
+ if (!Array.from(ids).some((id) => normalizedDecidedIds.has(id))) return true;
9020
9117
  removed = true;
9021
9118
  ids.forEach((id) => removedIds.add(id));
9022
9119
  return false;
@@ -9032,6 +9129,31 @@ const useCommentsStore = defineStore("comments", () => {
9032
9129
  removedIds
9033
9130
  };
9034
9131
  };
9132
+ const clearAllResolvedTrackedChangeRows = ({ adapter, documentId } = {}) => {
9133
+ const normalizedDocumentId = normalizeCommentId(documentId);
9134
+ if (!adapter || !normalizedDocumentId) return {
9135
+ removed: 0,
9136
+ removedIds: /* @__PURE__ */ new Set()
9137
+ };
9138
+ supersedeTrackedChangeSyncGeneration(adapter);
9139
+ const removedIds = /* @__PURE__ */ new Set();
9140
+ const activeId = normalizeCommentId(activeComment.value);
9141
+ let activeWasRemoved = false;
9142
+ const next = commentsList.value.filter((comment) => {
9143
+ if (!comment?.trackedChange || !belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
9144
+ const aliases = collectTrackedChangeDecisionIds(comment);
9145
+ aliases.forEach((id) => removedIds.add(id));
9146
+ if (activeId && aliases.has(activeId)) activeWasRemoved = true;
9147
+ return false;
9148
+ });
9149
+ const removed = commentsList.value.length - next.length;
9150
+ if (removed > 0) commentsList.value = next;
9151
+ if (activeWasRemoved) clearActiveCommentSelection();
9152
+ return {
9153
+ removed,
9154
+ removedIds
9155
+ };
9156
+ };
9035
9157
  const clearV2ActiveTrackedChangeTarget = (adapter, decidedId) => {
9036
9158
  if (!decidedId || typeof adapter?.clearActiveTrackedChangeTargetIfMatches !== "function") return;
9037
9159
  try {
@@ -9242,7 +9364,7 @@ const useCommentsStore = defineStore("comments", () => {
9242
9364
  success: Boolean(command(id))
9243
9365
  };
9244
9366
  };
9245
- const hydrateTrackedChangesFromV2 = async ({ superdoc, adapter, documentId, trackedChangesListMode = "all", refreshReason, blocking } = {}) => 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", {
9246
9368
  documentId: documentId ?? null,
9247
9369
  trackedChangesListMode,
9248
9370
  refreshReason: refreshReason ?? null,
@@ -9253,11 +9375,49 @@ const useCommentsStore = defineStore("comments", () => {
9253
9375
  ok: false,
9254
9376
  reason: "adapter-missing"
9255
9377
  };
9378
+ const hydrationIsCurrent = () => isCurrent?.() !== false && isCurrentV2TrackedChangesAdapter(effectiveAdapter);
9379
+ if (!hydrationIsCurrent()) return {
9380
+ ok: false,
9381
+ reason: "adapter-stale"
9382
+ };
9383
+ const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9384
+ const hydrationCanContinue = () => hydrationIsCurrent() && !signal?.aborted && trackedChangeSyncGeneration(effectiveAdapter) === syncGeneration;
9385
+ if (signal?.aborted) return {
9386
+ ok: false,
9387
+ reason: "review-hydration-superseded",
9388
+ lateResultDropped: false
9389
+ };
9390
+ const incrementalLiveIds = /* @__PURE__ */ new Set();
9391
+ const incrementalLiveAnchorKeys = /* @__PURE__ */ new Set();
9392
+ let incrementalPagesApplied = 0;
9393
+ let incrementalItemsApplied = 0;
9256
9394
  let result;
9257
9395
  try {
9258
9396
  const listOptions = { mode: trackedChangesListMode };
9397
+ if (Array.isArray(targetIds)) listOptions.targetIds = targetIds;
9259
9398
  if (refreshReason != null) listOptions.refreshReason = refreshReason;
9260
9399
  if (typeof blocking === "boolean") listOptions.blocking = blocking;
9400
+ if (signal) listOptions.signal = signal;
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
+ }
9261
9421
  result = await effectiveAdapter.listTrackedChanges(listOptions);
9262
9422
  } catch (err) {
9263
9423
  return {
@@ -9266,22 +9426,68 @@ const useCommentsStore = defineStore("comments", () => {
9266
9426
  detail: err?.message ?? String(err)
9267
9427
  };
9268
9428
  }
9269
- if (!isCurrentV2TrackedChangesAdapter(effectiveAdapter)) return {
9429
+ if (!hydrationIsCurrent()) return {
9270
9430
  ok: false,
9271
9431
  reason: "adapter-stale"
9272
9432
  };
9433
+ if (trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9434
+ ok: false,
9435
+ reason: "review-hydration-superseded",
9436
+ lateResultDropped: true
9437
+ };
9438
+ if (signal?.aborted) return {
9439
+ ok: false,
9440
+ reason: "review-hydration-superseded",
9441
+ lateResultDropped: true
9442
+ };
9273
9443
  if (!result?.ok) return result;
9274
9444
  const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete === true && result.sourceCoverageComplete === true;
9275
- reconcileTrackedChangesFromV2({
9445
+ if (signal?.aborted || trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9446
+ ok: false,
9447
+ reason: "review-hydration-superseded",
9448
+ lateResultDropped: true
9449
+ };
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({
9276
9479
  superdoc,
9277
9480
  adapter: effectiveAdapter,
9278
9481
  documentId,
9279
9482
  items: result.items ?? [],
9280
- pruneStale
9483
+ pruneStale,
9484
+ hydrationGeneration
9281
9485
  });
9282
9486
  const hydrationResult = {
9283
9487
  ok: true,
9284
- items: result.items ?? []
9488
+ items: result.items ?? [],
9489
+ reconciled: true,
9490
+ ...typeof hydrationGeneration === "number" ? { hydrationGeneration } : {}
9285
9491
  };
9286
9492
  if (typeof result.complete === "boolean") hydrationResult.complete = result.complete;
9287
9493
  if (typeof result.sourceCoverageComplete === "boolean") hydrationResult.sourceCoverageComplete = result.sourceCoverageComplete;
@@ -9289,14 +9495,28 @@ const useCommentsStore = defineStore("comments", () => {
9289
9495
  if (result.visibleWindowFallbackReason != null) hydrationResult.visibleWindowFallbackReason = result.visibleWindowFallbackReason;
9290
9496
  if (typeof result.visibleTrackedChangeExpectedRows === "number") hydrationResult.visibleTrackedChangeExpectedRows = result.visibleTrackedChangeExpectedRows;
9291
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 }));
9292
9504
  if (typeof result.refreshReason === "string") hydrationResult.refreshReason = result.refreshReason;
9293
9505
  if (typeof result.blocking === "boolean") hydrationResult.blocking = result.blocking;
9294
9506
  if (typeof result.pageCount === "number") hydrationResult.pageCount = result.pageCount;
9507
+ if (typeof result.pagesStarted === "number") hydrationResult.pagesStarted = result.pagesStarted;
9508
+ if (typeof result.pagesCompleted === "number") hydrationResult.pagesCompleted = result.pagesCompleted;
9509
+ if (typeof result.pagesSkippedAfterAbort === "number") hydrationResult.pagesSkippedAfterAbort = result.pagesSkippedAfterAbort;
9295
9510
  if (typeof result.totalItems === "number") hydrationResult.totalItems = result.totalItems;
9296
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
+ }
9297
9517
  return hydrationResult;
9298
9518
  });
9299
- const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [] } = {}) => {
9519
+ const reconcileTrackedChangeMutationFromV2 = async ({ superdoc, adapter, documentId, upsertIds = [], removedIds = [], allResolved } = {}) => {
9300
9520
  const effectiveAdapter = adapter ?? getV2TrackedChangesAdapter(superdoc);
9301
9521
  if (!effectiveAdapter) return {
9302
9522
  ok: false,
@@ -9307,6 +9527,26 @@ const useCommentsStore = defineStore("comments", () => {
9307
9527
  reason: "adapter-stale"
9308
9528
  };
9309
9529
  const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9530
+ if (allResolved && effectiveDocumentId == null) return {
9531
+ ok: false,
9532
+ reason: "document-id-missing"
9533
+ };
9534
+ if (allResolved) return {
9535
+ ok: true,
9536
+ items: [],
9537
+ resolvedIds: [],
9538
+ unresolvedIds: [],
9539
+ allResolved: true,
9540
+ removedRows: withInteractionSpan("store.trackedChanges.allResolved", "store-reconciliation", {
9541
+ documentId: String(effectiveDocumentId),
9542
+ logicalTargetCount: allResolved.logicalTargetCount ?? null,
9543
+ physicalCarrierCount: allResolved.physicalCarrierCount ?? null
9544
+ }, () => clearAllResolvedTrackedChangeRows({
9545
+ adapter: effectiveAdapter,
9546
+ documentId: effectiveDocumentId
9547
+ })).removed
9548
+ };
9549
+ const syncGeneration = trackedChangeSyncGeneration(effectiveAdapter);
9310
9550
  const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
9311
9551
  const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
9312
9552
  if (normalizedRemovedIds.size > 0 && normalizedUpsertIds.length === 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
@@ -9345,6 +9585,10 @@ const useCommentsStore = defineStore("comments", () => {
9345
9585
  ok: false,
9346
9586
  reason: "adapter-stale"
9347
9587
  };
9588
+ if (trackedChangeSyncGeneration(effectiveAdapter) !== syncGeneration) return {
9589
+ ok: false,
9590
+ reason: "generation-stale"
9591
+ };
9348
9592
  const resolvedIds = [];
9349
9593
  const unresolvedIds = [];
9350
9594
  const items = [];
@@ -9371,35 +9615,188 @@ const useCommentsStore = defineStore("comments", () => {
9371
9615
  failures: reads.map(({ result }) => result).filter((result) => !result?.ok)
9372
9616
  };
9373
9617
  };
9374
- const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9618
+ const createTrackedChangeBatchIdentityIndex = (documentId) => {
9619
+ const byIdentityAlias = /* @__PURE__ */ new Map();
9620
+ const indexedRows = /* @__PURE__ */ new Set();
9621
+ let aliasLookups = 0;
9622
+ let candidateVisits = 0;
9623
+ const addAlias = (index, value, comment) => {
9624
+ const alias = normalizeCommentId(value);
9625
+ if (!alias) return;
9626
+ const bucket = index.get(alias) ?? /* @__PURE__ */ new Set();
9627
+ bucket.add(comment);
9628
+ index.set(alias, bucket);
9629
+ };
9630
+ const add = (comment) => {
9631
+ if (!comment?.trackedChange) return;
9632
+ if (documentId && !belongsToTrackedChangeSyncDocument(comment, documentId)) return;
9633
+ indexedRows.add(comment);
9634
+ addAlias(byIdentityAlias, comment.commentId, comment);
9635
+ addAlias(byIdentityAlias, comment.importedId, comment);
9636
+ addAlias(byIdentityAlias, comment.trackedChangeCanonicalId, comment);
9637
+ addAlias(byIdentityAlias, comment.trackedChangeAnchorKey, comment);
9638
+ getCommentAliasIds(comment).forEach((alias) => addAlias(byIdentityAlias, alias, comment));
9639
+ };
9640
+ commentsList.value.forEach(add);
9641
+ return {
9642
+ add,
9643
+ work: () => ({
9644
+ trackedRowsIndexed: indexedRows.size,
9645
+ invalidatedIdMembershipChecks: 0,
9646
+ incomingAliasLookups: aliasLookups,
9647
+ candidateVisits
9648
+ }),
9649
+ candidates({ changeId, importedId, anchorKey, canonicalId, positionAliases = [] } = {}) {
9650
+ const out = /* @__PURE__ */ new Set();
9651
+ const include = (value) => {
9652
+ const alias = normalizeCommentId(value);
9653
+ if (!alias) return;
9654
+ aliasLookups += 1;
9655
+ const bucket = byIdentityAlias.get(alias);
9656
+ if (!bucket) return;
9657
+ candidateVisits += bucket.size;
9658
+ bucket.forEach((comment) => out.add(comment));
9659
+ };
9660
+ new Set([
9661
+ changeId,
9662
+ importedId,
9663
+ canonicalId,
9664
+ anchorKey,
9665
+ ...positionAliases
9666
+ ]).forEach(include);
9667
+ return out;
9668
+ }
9669
+ };
9670
+ };
9671
+ const reconcileTrackedChangesFromV2 = ({ superdoc, adapter, documentId, items, pruneStale = true, preparedParams = null, liveIds: suppliedLiveIds = null, liveAnchorKeys: suppliedLiveAnchorKeys = null, hydrationGeneration } = {}) => withInteractionSpan("store.trackedChanges.reconcile", "store-reconciliation", {
9375
9672
  documentId: documentId ?? null,
9376
9673
  itemCount: Array.isArray(items) ? items.length : null,
9377
- pruneStale
9674
+ pruneStale,
9675
+ hydrationGeneration: hydrationGeneration ?? null
9378
9676
  }, () => {
9379
9677
  if (!adapter || !Array.isArray(items)) return;
9380
9678
  if (!isCurrentV2TrackedChangesAdapter(adapter)) return;
9381
9679
  const effectiveDocumentId = documentId ?? adapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
9382
- const liveAnchorKeys = /* @__PURE__ */ new Set();
9383
- 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;
9683
+ const trackedChangeIdentityIndex = createTrackedChangeBatchIdentityIndex(effectiveDocumentId == null ? null : String(effectiveDocumentId));
9684
+ const identitySpan = startInteractionSpan("store.trackedChanges.batchIdentity", "store-reconciliation", { itemCount: items.length });
9384
9685
  const paramsList = Array.isArray(preparedParams) ? preparedParams : items.map((item) => adapter.mapV2TrackedChangeToCommentParams(item));
9385
- for (const params of paramsList) {
9386
- if (!params) continue;
9387
- if (params.event === "omit") {
9686
+ try {
9687
+ for (const params of paramsList) {
9688
+ if (!params) continue;
9689
+ if (params.event === "omit") {
9690
+ if (params.changeId != null) liveIds.add(String(params.changeId));
9691
+ continue;
9692
+ }
9693
+ if (effectiveDocumentId && params.documentId == null) params.documentId = effectiveDocumentId;
9388
9694
  if (params.changeId != null) liveIds.add(String(params.changeId));
9389
- continue;
9695
+ if (params.trackedChangeAnchorKey != null) liveAnchorKeys.add(String(params.trackedChangeAnchorKey));
9696
+ handleTrackedChangeUpdate({
9697
+ superdoc,
9698
+ params,
9699
+ broadcastChanges: false,
9700
+ trackedChangeIdentityIndex
9701
+ });
9702
+ appliedCount += 1;
9390
9703
  }
9391
- if (effectiveDocumentId && params.documentId == null) params.documentId = effectiveDocumentId;
9392
- if (params.changeId != null) liveIds.add(String(params.changeId));
9393
- if (params.trackedChangeAnchorKey != null) liveAnchorKeys.add(String(params.trackedChangeAnchorKey));
9394
- handleTrackedChangeUpdate({
9395
- superdoc,
9396
- params,
9397
- broadcastChanges: false
9398
- });
9704
+ } finally {
9705
+ endInteractionSpan(identitySpan, trackedChangeIdentityIndex.work());
9399
9706
  }
9400
- if (!effectiveDocumentId || !pruneStale) return;
9707
+ if (!effectiveDocumentId || !pruneStale) return {
9708
+ liveIds,
9709
+ liveAnchorKeys,
9710
+ appliedCount
9711
+ };
9401
9712
  pruneStaleTrackedChangeComments(liveIds, liveAnchorKeys, String(effectiveDocumentId), superdoc, { broadcastChanges: false });
9713
+ return {
9714
+ liveIds,
9715
+ liveAnchorKeys,
9716
+ appliedCount
9717
+ };
9402
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
+ };
9791
+ const getV2TrackedChangeRowCount = (documentId = null) => {
9792
+ const normalizedDocumentId = documentId == null ? null : String(documentId);
9793
+ return commentsList.value.filter((comment) => {
9794
+ if (comment?.trackedChange !== true) return false;
9795
+ if (normalizedDocumentId == null) return true;
9796
+ const rowDocumentId = comment?.documentId ?? comment?.fileId ?? null;
9797
+ return rowDocumentId != null && String(rowDocumentId) === normalizedDocumentId;
9798
+ }).length;
9799
+ };
9403
9800
  const syncTrackedChangeComments = ({ superdoc, editor, broadcastChanges = true }) => {
9404
9801
  if (!superdoc || !editor) return;
9405
9802
  const activeDocumentId = editor?.options?.documentId != null ? String(editor.options.documentId) : null;
@@ -9966,6 +10363,7 @@ const useCommentsStore = defineStore("comments", () => {
9966
10363
  getV2CommentsAdapter,
9967
10364
  hydrateCommentsFromV2,
9968
10365
  applyReviewSnapshotFromV2,
10366
+ supersedeV2ReviewHydration,
9969
10367
  reconcileCommentsFromV2,
9970
10368
  isV2EditorActive,
9971
10369
  replyCommentV2,
@@ -9975,6 +10373,7 @@ const useCommentsStore = defineStore("comments", () => {
9975
10373
  v2TrackedChangesAdapter,
9976
10374
  setV2TrackedChangesAdapter,
9977
10375
  getV2TrackedChangesAdapter,
10376
+ getV2TrackedChangeRowCount,
9978
10377
  hydrateTrackedChangesFromV2,
9979
10378
  reconcileTrackedChangeMutationFromV2,
9980
10379
  reconcileTrackedChangesFromV2,
@@ -18018,6 +18417,7 @@ var _sfc_main$20 = {
18018
18417
  const v2ReviewHydrationController = resolvedEditorIntegration.createReviewHydrationController({
18019
18418
  hydrateComments: (ctx) => commentsStore.hydrateCommentsFromV2?.(ctx),
18020
18419
  hydrateTrackedChanges: (ctx) => commentsStore.hydrateTrackedChangesFromV2?.(ctx),
18420
+ primeTrackedChanges: (ctx) => ctx.trackedChangesAdapter?.primeVisibleTrackedChanges?.(ctx.targetIds, ctx.plan),
18021
18421
  readReviewSnapshot: (ctx) => ctx.trackedChangesAdapter?.readReviewSnapshot?.(ctx.snapshot),
18022
18422
  validateReviewSnapshot: (_ctx, result) => result?.validate?.() ?? {
18023
18423
  ok: false,
@@ -18031,6 +18431,8 @@ var _sfc_main$20 = {
18031
18431
  commentItems: result.comments?.items,
18032
18432
  trackedChangeItems: result.trackedChanges?.items,
18033
18433
  trackedList: result.trackedChanges,
18434
+ sourceCoverageRevision: result.sourceCoverageRevision,
18435
+ evaluatedRevision: result.evaluatedRevision,
18034
18436
  patch: (callback) => commentsStore.$patch(callback)
18035
18437
  }) ?? {
18036
18438
  ok: false,
@@ -18708,6 +19110,12 @@ var _sfc_main$20 = {
18708
19110
  v2ReviewSidebarUnlocked.value = true;
18709
19111
  v2GeometryEpoch.value = v2GeometryPublisher.getLastEpoch();
18710
19112
  }
19113
+ },
19114
+ onCommittedPagePaint: (commit) => {
19115
+ v2ReviewHydrationController.onCommittedPagePaint?.({
19116
+ ...commit,
19117
+ documentId: commit?.payload?.documentId ?? null
19118
+ });
18711
19119
  }
18712
19120
  });
18713
19121
  const collectV2TrackedChangeRestampIds = (impactOrIds) => {
@@ -18931,7 +19339,7 @@ var _sfc_main$20 = {
18931
19339
  v2TypingReviewHydrationTimer = setTimeout(attempt, Math.min(V2_TYPING_REVIEW_HYDRATION_IDLE_MS, ceilingRemainingMs));
18932
19340
  };
18933
19341
  const v2RemoteReviewHydrationScheduler = createV2RemoteReviewHydrationScheduler({
18934
- hydrate: () => hydrateV2ReviewRowsFromHost(),
19342
+ hydrate: () => v2ReviewHydrationController.reconcileInBackground?.("remote-review-change"),
18935
19343
  getActiveDocumentId: () => {
18936
19344
  const activeEditor = proxy.$superdoc?.activeEditor ?? null;
18937
19345
  return activeEditor?.documentId ?? activeEditor?.options?.documentId ?? null;
@@ -18940,6 +19348,23 @@ var _sfc_main$20 = {
18940
19348
  const onV2HostEvent = (document$1, event) => {
18941
19349
  if (!event) return;
18942
19350
  const documentId = document$1?.id ?? null;
19351
+ if (event.type === "review-mutation:started") {
19352
+ const activeEditor = proxy.$superdoc?.activeEditor ?? null;
19353
+ commentsStore.supersedeV2ReviewHydration?.({
19354
+ commentsAdapter: activeEditor?.v2Comments ?? null,
19355
+ trackedChangesAdapter: activeEditor?.v2TrackedChanges ?? null
19356
+ });
19357
+ v2ReviewHydrationController.beginMutation(event.reviewMutation);
19358
+ return;
19359
+ }
19360
+ if (event.type === "review-mutation:aborted") {
19361
+ v2ReviewHydrationController.settleMutation(event.reviewMutation?.token, {
19362
+ outcome: "aborted",
19363
+ resumeDomains: ["comments", "trackedChanges"],
19364
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19365
+ });
19366
+ return;
19367
+ }
18943
19368
  if (event.type === "reviewTarget:changed") {
18944
19369
  syncSidebarActiveCommentFromV2ReviewTarget(event.next);
18945
19370
  return;
@@ -18958,6 +19383,11 @@ var _sfc_main$20 = {
18958
19383
  return;
18959
19384
  }
18960
19385
  if (event.type === "mutation:rejected") {
19386
+ if (event.reviewMutation?.token) v2ReviewHydrationController.settleMutation(event.reviewMutation.token, {
19387
+ outcome: "rejected",
19388
+ resumeDomains: ["comments", "trackedChanges"],
19389
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19390
+ });
18961
19391
  maybeNotifyV2AuthorRequired(documentId, event);
18962
19392
  return;
18963
19393
  }
@@ -18973,7 +19403,22 @@ var _sfc_main$20 = {
18973
19403
  const readiness = (proxy.$superdoc?.activeEditor ?? null)?.documentMutationReadiness ?? null;
18974
19404
  const receipt = event.origin === "history" ? null : event.receipt;
18975
19405
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
18976
- v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
19406
+ const reconciliation = v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
19407
+ if (event.reviewMutation?.token) {
19408
+ const reviewMutation = event.reviewMutation;
19409
+ const allResolved = Boolean(reviewImpact?.allResolved ?? event.trackedChangeAllResolved);
19410
+ const settleReviewMutation = (reconciled) => {
19411
+ const reconciledAllResolved = allResolved && reconciled === true;
19412
+ v2ReviewHydrationController.settleMutation(reviewMutation.token, {
19413
+ outcome: "committed",
19414
+ allResolved: reconciledAllResolved,
19415
+ resumeDomains: reconciledAllResolved ? ["comments"] : ["comments", "trackedChanges"],
19416
+ trackedRowCount: commentsStore.getV2TrackedChangeRowCount?.(documentId) ?? null
19417
+ });
19418
+ };
19419
+ Promise.resolve(reconciliation).then(settleReviewMutation, () => settleReviewMutation(false));
19420
+ return;
19421
+ }
18977
19422
  if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
18978
19423
  hydrateV2CommentRowsFromHost();
18979
19424
  return;
@@ -19147,6 +19592,12 @@ var _sfc_main$20 = {
19147
19592
  onCommentsUpdate: onEditorCommentsUpdate,
19148
19593
  onFontsResolved: onFontsResolvedFn,
19149
19594
  onPageCountKnown: proxy.$superdoc.config.onPageCountKnown ?? null,
19595
+ onReviewWindowPlanned: (payload) => {
19596
+ v2ReviewHydrationController.onReviewWindowPlanned?.({
19597
+ ...payload,
19598
+ documentId: doc.id
19599
+ });
19600
+ },
19150
19601
  fontAssets: proxy.$superdoc.config.fonts,
19151
19602
  proofing: resolvedProofingConfig.value,
19152
19603
  isNewFile,
@@ -20109,7 +20560,7 @@ var _sfc_main$20 = {
20109
20560
  };
20110
20561
  }
20111
20562
  };
20112
- var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-d75089d2"]]);
20563
+ var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-c3bc8a46"]]);
20113
20564
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
20114
20565
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
20115
20566
  var piniaDevtoolsSuppressionState = {
@@ -38846,7 +39297,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
38846
39297
  this.config.colors = shuffleArray(this.config.colors);
38847
39298
  this.userColorMap = /* @__PURE__ */ new Map();
38848
39299
  this.colorIndex = 0;
38849
- this.version = "2.4.0-next.2";
39300
+ this.version = "2.4.0-next.4";
38850
39301
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
38851
39302
  this.superdocId = config.superdocId || require_uuid.v4_default();
38852
39303
  this.colors = this.config.colors ?? [];
@@ -39728,8 +40179,8 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
39728
40179
  },
39729
40180
  {
39730
40181
  feature: "shell.tracked-change-sidebar.bulk",
39731
- status: "not-shipped",
39732
- reason: "bulk-tracked-change-decisions-omitted: acceptAll/rejectAll are matrix-disabled by default in the v2 host"
40182
+ status: "supported",
40183
+ 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"
39733
40184
  },
39734
40185
  {
39735
40186
  feature: "shell.tracked-change-sidebar.non-body",