superdoc 2.0.0-next.27 → 2.0.0-next.29

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.
Files changed (38) hide show
  1. package/dist/chunks/{PdfViewer-XZg0nr8y.es.js → PdfViewer-CNOtyS-I.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-BtuYf2yM.cjs → PdfViewer-CoFiGAhm.cjs} +1 -1
  3. package/dist/chunks/{create-super-doc-ui-Dvl3EZsv.es.js → create-super-doc-ui-BKVf2uCM.es.js} +660 -159
  4. package/dist/chunks/{create-super-doc-ui-pYMthdDn.cjs → create-super-doc-ui-BXehf3i3.cjs} +660 -159
  5. package/dist/collaboration-upgrade-engine.cjs +1 -1
  6. package/dist/collaboration-upgrade-engine.es.js +1 -1
  7. package/dist/document-api/src/contract/operation-definitions.d.ts +1 -1
  8. package/dist/document-api/src/track-changes/track-changes.d.ts +6 -2
  9. package/dist/document-api/src/types/clipboard.d.ts +5 -1
  10. package/dist/document-api/src/types/table-operations.types.d.ts +6 -4
  11. package/dist/document-api/src/types/track-changes.types.d.ts +15 -0
  12. package/dist/layout-engine/contracts/src/incremental-dependency.d.ts +10 -0
  13. package/dist/layout-engine/contracts/src/index.d.ts +38 -0
  14. package/dist/layout-engine/layout-bridge/src/incrementalLayout.d.ts +40 -1
  15. package/dist/layout-engine/layout-engine/src/index.d.ts +17 -0
  16. package/dist/layout-engine/layout-engine/src/paginator.d.ts +16 -0
  17. package/dist/layout-engine/layout-engine/src/paragraph-layout-eligibility.d.ts +1 -4
  18. package/dist/public/ui-react.cjs +1 -1
  19. package/dist/public/ui-react.es.js +1 -1
  20. package/dist/public/ui.cjs +1 -1
  21. package/dist/public/ui.es.js +1 -1
  22. package/dist/style.css +130 -86
  23. package/dist/style.layered.css +130 -86
  24. package/dist/superdoc/src/components/CommentsLayer/floating-comment-positioning.d.ts +1 -2
  25. package/dist/superdoc/src/core/types/index.d.ts +7 -0
  26. package/dist/superdoc/src/helpers/v2-review-mutation-impact.d.ts +4 -1
  27. package/dist/superdoc/src/helpers/v2-review-mutation-reconciler.d.ts +6 -5
  28. package/dist/superdoc/src/public/ui/reasons.d.ts +8 -0
  29. package/dist/superdoc/src/stores/comments-store.d.ts +17 -11
  30. package/dist/superdoc/src/stores/helpers/floating-comment-instances.d.ts +2 -10
  31. package/dist/superdoc/src/stores/helpers/tracked-change-thread-index.d.ts +14 -0
  32. package/dist/superdoc/src/stores/superdoc-store.d.ts +51 -33
  33. package/dist/superdoc.cjs +531 -135
  34. package/dist/superdoc.es.js +531 -135
  35. package/dist-cdn/style.layered.css +1 -1
  36. package/dist-cdn/superdoc.min.css +1 -1
  37. package/dist-cdn/superdoc.min.js +37 -37
  38. package/package.json +2 -2
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-BdUBlUeG.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-pYMthdDn.cjs");
9
+ const require_create_super_doc_ui = require("./chunks/create-super-doc-ui-BXehf3i3.cjs");
10
10
  let vue = require("vue");
11
11
  vue = require_rolldown_runtime.__toESM(vue);
12
12
  require("y-websocket");
@@ -145,19 +145,50 @@ function compareVersions(version1, version2) {
145
145
  }
146
146
  return 0;
147
147
  }
148
+ var registries = /* @__PURE__ */ new WeakMap();
149
+ function ownerDocumentFor(el) {
150
+ return el.ownerDocument ?? document;
151
+ }
152
+ function unregister(el, ownerDocument) {
153
+ const registry = registries.get(ownerDocument);
154
+ registry?.bindings.delete(el);
155
+ if (registry && registry.bindings.size === 0) {
156
+ ownerDocument.removeEventListener("click", registry.listener);
157
+ registries.delete(ownerDocument);
158
+ }
159
+ }
160
+ function register(el, binding) {
161
+ const ownerDocument = ownerDocumentFor(el);
162
+ if (el.__clickOutsideDocument && el.__clickOutsideDocument !== ownerDocument) unregister(el, el.__clickOutsideDocument);
163
+ let registry = registries.get(ownerDocument);
164
+ if (!registry) {
165
+ const bindings = /* @__PURE__ */ new Map();
166
+ const listener = (event) => {
167
+ const path = typeof event.composedPath === "function" ? event.composedPath() : null;
168
+ for (const [element, currentBinding] of bindings) if (!(path?.includes(element) ?? element.contains(event.target))) currentBinding.value?.(event);
169
+ };
170
+ registry = {
171
+ bindings,
172
+ listener
173
+ };
174
+ registries.set(ownerDocument, registry);
175
+ ownerDocument.addEventListener("click", listener);
176
+ }
177
+ registry.bindings.set(el, binding);
178
+ el.__clickOutsideHandler = registry.listener;
179
+ el.__clickOutsideDocument = ownerDocument;
180
+ }
148
181
  var v_click_outside_default = {
149
182
  mounted(el, binding) {
150
- const clickOutsideHandler = (event) => {
151
- if (!el.contains(event.target)) binding.value?.(event);
152
- };
153
- document.addEventListener("click", clickOutsideHandler);
154
- el.__clickOutsideHandler = clickOutsideHandler;
183
+ register(el, binding);
184
+ },
185
+ updated(el, binding) {
186
+ register(el, binding);
155
187
  },
156
188
  unmounted(el) {
157
- if (el.__clickOutsideHandler) {
158
- document.removeEventListener("click", el.__clickOutsideHandler);
159
- delete el.__clickOutsideHandler;
160
- }
189
+ unregister(el, el.__clickOutsideDocument ?? ownerDocumentFor(el));
190
+ delete el.__clickOutsideHandler;
191
+ delete el.__clickOutsideDocument;
161
192
  }
162
193
  };
163
194
  function hasUser(entry) {
@@ -2520,7 +2551,7 @@ var aggregateRectBounds = (rects) => {
2520
2551
  };
2521
2552
  var groupRectsByPage = (rects) => {
2522
2553
  const groupedRects = /* @__PURE__ */ new Map();
2523
- rects.forEach((rect) => {
2554
+ (Array.isArray(rects) ? rects : []).forEach((rect) => {
2524
2555
  const pageIndex = toFinitePageIndex(rect?.pageIndex);
2525
2556
  if (pageIndex == null) return;
2526
2557
  const pageRects = groupedRects.get(pageIndex);
@@ -2547,31 +2578,40 @@ var resolveSelectionPageIndex = (comment) => {
2547
2578
  };
2548
2579
  var buildSingleFloatingCommentInstance = ({ id, threadId, comment, positionKey, positionEntry }) => {
2549
2580
  const geometryPageIndex = toFinitePageIndex(positionEntry?.pageIndex);
2550
- const pageIndex = geometryPageIndex != null ? geometryPageIndex : resolveSelectionPageIndex(comment);
2581
+ const fallbackPageIndex = comment?.trackedChange ? null : resolveSelectionPageIndex(comment);
2551
2582
  return {
2552
2583
  id,
2553
2584
  threadId,
2554
2585
  comment,
2555
2586
  positionKey,
2556
2587
  positionEntry: positionEntry ?? null,
2557
- pageIndex,
2588
+ pageIndex: geometryPageIndex ?? fallbackPageIndex,
2558
2589
  isPrimary: true
2559
2590
  };
2560
2591
  };
2561
- var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry }) => {
2592
+ var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry, primaryInstanceId }) => {
2562
2593
  const rectGroups = groupRectsByPage(positionEntry?.rects);
2563
2594
  if (rectGroups.length < 2) return [];
2564
- const primaryPageIndex = toFinitePageIndex(positionEntry?.pageIndex) ?? rectGroups[0]?.pageIndex ?? null;
2565
- return rectGroups.map(({ pageIndex, rects }) => {
2595
+ const renderableGroups = rectGroups.map(({ pageIndex, rects }) => {
2566
2596
  const bounds = aggregateRectBounds(rects);
2567
- if (!bounds) return null;
2597
+ return bounds ? {
2598
+ pageIndex,
2599
+ rects,
2600
+ bounds
2601
+ } : null;
2602
+ }).filter(Boolean);
2603
+ if (renderableGroups.length < 2) return [];
2604
+ const geometryPageIndex = toFinitePageIndex(positionEntry?.pageIndex);
2605
+ const primaryPageIndex = renderableGroups.some(({ pageIndex }) => pageIndex === geometryPageIndex) ? geometryPageIndex : renderableGroups[0]?.pageIndex ?? null;
2606
+ return renderableGroups.map(({ pageIndex, rects, bounds }) => {
2607
+ const isPrimary = pageIndex === primaryPageIndex;
2568
2608
  return {
2569
- id: buildPageScopedInstanceId(positionKey, pageIndex),
2609
+ id: isPrimary ? primaryInstanceId : buildPageScopedInstanceId(primaryInstanceId, pageIndex),
2570
2610
  threadId: comment?.commentId ?? positionKey,
2571
2611
  comment,
2572
2612
  positionKey,
2573
2613
  pageIndex,
2574
- isPrimary: pageIndex === primaryPageIndex,
2614
+ isPrimary,
2575
2615
  positionEntry: {
2576
2616
  ...positionEntry,
2577
2617
  pageIndex,
@@ -2579,7 +2619,7 @@ var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry
2579
2619
  bounds
2580
2620
  }
2581
2621
  };
2582
- }).filter(Boolean);
2622
+ });
2583
2623
  };
2584
2624
  const buildFloatingCommentInstances = ({ comment, positionKey, positionEntry, fallbackId }) => {
2585
2625
  const instanceId = comment?.trackedChange && comment?.trackedChangeCanonicalId && positionKey && fallbackId && String(positionKey) !== String(fallbackId) ? fallbackId : positionKey ?? fallbackId;
@@ -2588,7 +2628,8 @@ const buildFloatingCommentInstances = ({ comment, positionKey, positionEntry, fa
2588
2628
  const repeatedInstances = buildRepeatedHeaderFooterInstances({
2589
2629
  comment,
2590
2630
  positionKey,
2591
- positionEntry
2631
+ positionEntry,
2632
+ primaryInstanceId: instanceId
2592
2633
  });
2593
2634
  if (repeatedInstances.length > 0) return repeatedInstances;
2594
2635
  }
@@ -2763,6 +2804,79 @@ const trackedChangeThreadParentIdForComment = (comment) => {
2763
2804
  if (comment?.trackedChangeParentId != null && hasLegacyTrackedChangeThreadSignal(comment)) return comment.trackedChangeParentId;
2764
2805
  return null;
2765
2806
  };
2807
+ const buildTrackedChangeThreadIndex = (allComments, previous = /* @__PURE__ */ new Map()) => {
2808
+ const childrenByParentId = /* @__PURE__ */ new Map();
2809
+ const anchoredRootsByTrackedChangeId = /* @__PURE__ */ new Map();
2810
+ const commentsById = /* @__PURE__ */ new Map();
2811
+ const sourceIndex = /* @__PURE__ */ new Map();
2812
+ const append = (map, key, value) => {
2813
+ const existing = map.get(key);
2814
+ if (existing) existing.push(value);
2815
+ else map.set(key, [value]);
2816
+ };
2817
+ allComments.forEach((comment, index) => {
2818
+ sourceIndex.set(comment, index);
2819
+ append(commentsById, comment.commentId, comment);
2820
+ new Set([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id))).forEach((parentId) => append(childrenByParentId, parentId, comment));
2821
+ const trackedChangeThreadParentId = trackedChangeThreadParentIdForComment(comment);
2822
+ if (trackedChangeThreadParentId != null && !comment.parentCommentId) append(anchoredRootsByTrackedChangeId, String(trackedChangeThreadParentId), comment);
2823
+ });
2824
+ const next = /* @__PURE__ */ new Map();
2825
+ for (const parentComment of allComments) {
2826
+ if (!parentComment?.trackedChange || parentComment.commentId == null) continue;
2827
+ const trackedChangeId = parentComment.commentId;
2828
+ const threadIds = new Set([trackedChangeId]);
2829
+ const queue = [];
2830
+ const seed = typeof trackedChangeId === "string" ? [...childrenByParentId.get(trackedChangeId) ?? [], ...anchoredRootsByTrackedChangeId.get(trackedChangeId) ?? []] : [];
2831
+ for (const comment of seed) {
2832
+ const id = comment.commentId;
2833
+ if (id === trackedChangeId || threadIds.has(id)) continue;
2834
+ threadIds.add(id);
2835
+ queue.push(id);
2836
+ }
2837
+ for (let index = 0; index < queue.length; index += 1) {
2838
+ const parentId = queue[index];
2839
+ for (const comment of typeof parentId === "string" ? childrenByParentId.get(parentId) ?? [] : []) {
2840
+ const id = comment.commentId;
2841
+ if (threadIds.has(id)) continue;
2842
+ threadIds.add(id);
2843
+ queue.push(id);
2844
+ }
2845
+ }
2846
+ const members = [...threadIds].flatMap((id) => commentsById.get(id) ?? []).sort((left, right) => (sourceIndex.get(left) ?? 0) - (sourceIndex.get(right) ?? 0));
2847
+ members.sort((left, right) => {
2848
+ if (left === parentComment) return -1;
2849
+ if (right === parentComment) return 1;
2850
+ return Number(left.createdTime) - Number(right.createdTime);
2851
+ });
2852
+ const prior = previous.get(trackedChangeId);
2853
+ const stable = prior?.length === members.length && members.every((comment, index) => prior[index] === comment) ? prior : members;
2854
+ next.set(trackedChangeId, stable);
2855
+ }
2856
+ return next;
2857
+ };
2858
+ const buildTrackedChangeDecisionLinkIndex = (allComments, previous = /* @__PURE__ */ new Map()) => {
2859
+ const grouped = /* @__PURE__ */ new Map();
2860
+ for (const comment of allComments) {
2861
+ const ids = new Set([
2862
+ comment.parentCommentId,
2863
+ comment.threadingParentCommentId,
2864
+ comment.trackedChangeParentId,
2865
+ comment.trackedChangeThreadParentId
2866
+ ].filter((id) => id != null).map((id) => String(id)));
2867
+ for (const id of ids) {
2868
+ const existing = grouped.get(id);
2869
+ if (existing) existing.push(comment);
2870
+ else grouped.set(id, [comment]);
2871
+ }
2872
+ }
2873
+ const next = /* @__PURE__ */ new Map();
2874
+ for (const [id, comments] of grouped) {
2875
+ const prior = previous.get(id);
2876
+ next.set(id, prior?.length === comments.length && comments.every((comment, index) => prior[index] === comment) ? prior : comments);
2877
+ }
2878
+ return next;
2879
+ };
2766
2880
  var Editor = class {
2767
2881
  constructor() {
2768
2882
  throw new Error("SuperDoc v2 beta: v1 editor-backed comment import paths are not available.");
@@ -2923,6 +3037,22 @@ const useCommentsStore = defineStore("comments", () => {
2923
3037
  const suppressInternalExternal = (0, vue.ref)(true);
2924
3038
  const currentCommentText = (0, vue.ref)("");
2925
3039
  const commentsList = (0, vue.ref)([]);
3040
+ let priorTrackedChangeThreadIndex = /* @__PURE__ */ new Map();
3041
+ const trackedChangeThreadIndex = (0, vue.computed)(() => {
3042
+ const next = buildTrackedChangeThreadIndex(commentsList.value, priorTrackedChangeThreadIndex);
3043
+ priorTrackedChangeThreadIndex = next;
3044
+ return next;
3045
+ });
3046
+ const getTrackedChangeThread = (parentComment) => {
3047
+ const id = parentComment?.commentId ?? null;
3048
+ return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? [parentComment].filter(Boolean);
3049
+ };
3050
+ let priorTrackedChangeDecisionLinkIndex = /* @__PURE__ */ new Map();
3051
+ const trackedChangeDecisionLinkIndex = (0, vue.computed)(() => {
3052
+ const next = buildTrackedChangeDecisionLinkIndex(commentsList.value, priorTrackedChangeDecisionLinkIndex);
3053
+ priorTrackedChangeDecisionLinkIndex = next;
3054
+ return next;
3055
+ });
2926
3056
  const isCommentsListVisible = (0, vue.ref)(false);
2927
3057
  const editorCommentIds = (0, vue.ref)([]);
2928
3058
  const editorCommentPositions = (0, vue.ref)({});
@@ -3709,6 +3839,7 @@ const useCommentsStore = defineStore("comments", () => {
3709
3839
  };
3710
3840
  };
3711
3841
  const getGroupedComments = (0, vue.computed)(() => buildGroupedComments(compareByCreatedTime));
3842
+ const hasOpenTrackedChanges = (0, vue.computed)(() => getGroupedComments.value?.parentComments.some((comment) => comment.trackedChange && !comment.resolvedTime) ?? false);
3712
3843
  const getCommentsByPosition = (0, vue.computed)(() => buildGroupedComments(compareByPosition));
3713
3844
  const hasOverlapId = (id) => overlappedIds.includes(id);
3714
3845
  const documentsWithConverations = (0, vue.computed)(() => {
@@ -4894,7 +5025,7 @@ const useCommentsStore = defineStore("comments", () => {
4894
5025
  superdoc
4895
5026
  };
4896
5027
  let resolvedCount = 0;
4897
- commentsList.value.forEach((linkedComment) => {
5028
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4898
5029
  if (!linkedComment || linkedComment === comment) return;
4899
5030
  if (linkedComment.resolvedTime) return;
4900
5031
  if (![
@@ -4922,6 +5053,16 @@ const useCommentsStore = defineStore("comments", () => {
4922
5053
  }
4923
5054
  return trackedChangeIds;
4924
5055
  };
5056
+ const getTrackedChangeDecisionLinks = (comment) => {
5057
+ const linked = [];
5058
+ const seen = /* @__PURE__ */ new Set();
5059
+ for (const id of collectTrackedChangeDecisionIds(comment)) for (const candidate of trackedChangeDecisionLinkIndex.value.get(String(id)) ?? []) {
5060
+ if (seen.has(candidate)) continue;
5061
+ seen.add(candidate);
5062
+ linked.push(candidate);
5063
+ }
5064
+ return linked;
5065
+ };
4925
5066
  const collectTrackedChangeEntityIdsFromReceipt = (receipt) => {
4926
5067
  const record = receipt && typeof receipt === "object" ? receipt : null;
4927
5068
  if (!record?.success) return /* @__PURE__ */ new Set();
@@ -4953,7 +5094,7 @@ const useCommentsStore = defineStore("comments", () => {
4953
5094
  const trackedChangeIds = collectTrackedChangeDecisionIds(comment);
4954
5095
  if (!trackedChangeIds.size) return 0;
4955
5096
  let detachedCount = 0;
4956
- commentsList.value.forEach((linkedComment) => {
5097
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4957
5098
  if (!linkedComment || linkedComment === comment) return;
4958
5099
  const trackedChangeParentId = normalizeCommentId(linkedComment.trackedChangeParentId);
4959
5100
  const trackedChangeThreadParentId = normalizeCommentId(linkedComment.trackedChangeThreadParentId);
@@ -4969,7 +5110,7 @@ const useCommentsStore = defineStore("comments", () => {
4969
5110
  });
4970
5111
  return detachedCount;
4971
5112
  };
4972
- const pruneDecidedTrackedChangeRow = ({ superdoc, decidedId, decidedIds, documentId } = {}) => {
5113
+ const pruneDecidedTrackedChangeRow = ({ superdoc, comment: targetComment, decidedId, decidedIds, documentId } = {}) => {
4973
5114
  const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(Boolean));
4974
5115
  const normalizedDocumentId = normalizeCommentId(documentId);
4975
5116
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
@@ -4978,7 +5119,13 @@ const useCommentsStore = defineStore("comments", () => {
4978
5119
  };
4979
5120
  const removedIds = /* @__PURE__ */ new Set();
4980
5121
  let removed = false;
4981
- commentsList.value = commentsList.value.filter((comment) => {
5122
+ const targetIndex = targetComment ? commentsList.value.indexOf(targetComment) : -1;
5123
+ const targetIds = targetComment?.trackedChange ? collectTrackedChangeDecisionIds(targetComment) : /* @__PURE__ */ new Set();
5124
+ if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(normalizedDecidedIds).some((id) => targetIds.has(id))) {
5125
+ targetIds.forEach((id) => removedIds.add(id));
5126
+ commentsList.value.splice(targetIndex, 1);
5127
+ removed = true;
5128
+ } else commentsList.value = commentsList.value.filter((comment) => {
4982
5129
  if (!comment?.trackedChange) return true;
4983
5130
  if (!belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
4984
5131
  const ids = collectTrackedChangeDecisionIds(comment);
@@ -5014,6 +5161,25 @@ const useCommentsStore = defineStore("comments", () => {
5014
5161
  adapter.clearActiveTrackedChangeTargetIfMatches(decidedId);
5015
5162
  } catch {}
5016
5163
  };
5164
+ const waitForTrackedDecisionCanonicalPaint = async ({ superdoc, receipt } = {}) => {
5165
+ const readiness = superdoc?.activeEditor?.documentMutationReadiness;
5166
+ if (receipt?.success !== true || typeof receipt?.txId !== "string" || !receipt.txId || typeof readiness?.whenPainted !== "function") return;
5167
+ try {
5168
+ await readiness.whenPainted(receipt);
5169
+ const requestFrame = globalThis.requestAnimationFrame;
5170
+ if (typeof requestFrame === "function") await new Promise((resolve) => {
5171
+ let settled = false;
5172
+ const finish = () => {
5173
+ if (settled) return;
5174
+ settled = true;
5175
+ clearTimeout(fallback);
5176
+ resolve();
5177
+ };
5178
+ const fallback = setTimeout(finish, 250);
5179
+ requestFrame(() => requestFrame(() => setTimeout(finish, 0)));
5180
+ });
5181
+ } catch {}
5182
+ };
5017
5183
  const tracePostDecisionBackgroundReconcile = (superdoc, event, payload = {}) => {
5018
5184
  const record = {
5019
5185
  event,
@@ -5165,7 +5331,7 @@ const useCommentsStore = defineStore("comments", () => {
5165
5331
  adapter: v2Adapter,
5166
5332
  documentId: effectiveDocumentId,
5167
5333
  items: listResult.items ?? [],
5168
- pruneStale: listResult.complete !== false && listResult.sourceCoverageComplete !== false
5334
+ pruneStale: listResult.complete === true && listResult.sourceCoverageComplete === true
5169
5335
  });
5170
5336
  const liveIds = new Set((listResult.items ?? []).map((item) => item && item.id != null ? String(item.id) : null).filter(Boolean));
5171
5337
  if (!(decidedId != null && liveIds.has(decidedId))) {
@@ -5209,6 +5375,17 @@ const useCommentsStore = defineStore("comments", () => {
5209
5375
  };
5210
5376
  };
5211
5377
  if (!reliableDecidedIds.size || !effectiveDocumentId) return runBlockingPostDecisionReconcile();
5378
+ reliableDecidedIds.forEach((id$1) => clearV2ActiveTrackedChangeTarget(v2Adapter, id$1));
5379
+ schedulePostDecisionBackgroundReconcile({
5380
+ superdoc,
5381
+ trackedChangesAdapter: v2Adapter,
5382
+ commentsAdapter: getV2CommentsAdapter(superdoc),
5383
+ documentId: effectiveDocumentId
5384
+ });
5385
+ await waitForTrackedDecisionCanonicalPaint({
5386
+ superdoc,
5387
+ receipt: outcome.receipt
5388
+ });
5212
5389
  resolveLinkedCommentsForTrackedChangeDecision({
5213
5390
  superdoc,
5214
5391
  comment,
@@ -5220,17 +5397,11 @@ const useCommentsStore = defineStore("comments", () => {
5220
5397
  });
5221
5398
  pruneDecidedTrackedChangeRow({
5222
5399
  superdoc,
5400
+ comment,
5223
5401
  decidedIds: reliableDecidedIds,
5224
5402
  documentId: effectiveDocumentId
5225
5403
  });
5226
5404
  if (wasActiveBeforeDecide) clearActiveCommentSelection();
5227
- reliableDecidedIds.forEach((id$1) => clearV2ActiveTrackedChangeTarget(v2Adapter, id$1));
5228
- schedulePostDecisionBackgroundReconcile({
5229
- superdoc,
5230
- trackedChangesAdapter: v2Adapter,
5231
- commentsAdapter: getV2CommentsAdapter(superdoc),
5232
- documentId: effectiveDocumentId
5233
- });
5234
5405
  return {
5235
5406
  ok: true,
5236
5407
  success: true
@@ -5299,7 +5470,7 @@ const useCommentsStore = defineStore("comments", () => {
5299
5470
  reason: "adapter-stale"
5300
5471
  };
5301
5472
  if (!result?.ok) return result;
5302
- const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete !== false && result.sourceCoverageComplete !== false;
5473
+ const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete === true && result.sourceCoverageComplete === true;
5303
5474
  reconcileTrackedChangesFromV2({
5304
5475
  superdoc,
5305
5476
  adapter: effectiveAdapter,
@@ -5336,12 +5507,12 @@ const useCommentsStore = defineStore("comments", () => {
5336
5507
  };
5337
5508
  const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
5338
5509
  const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
5339
- if (normalizedRemovedIds.size > 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
5510
+ const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
5511
+ if (normalizedRemovedIds.size > 0 && normalizedUpsertIds.length === 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
5340
5512
  superdoc,
5341
5513
  decidedIds: normalizedRemovedIds,
5342
5514
  documentId: effectiveDocumentId
5343
5515
  });
5344
- const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
5345
5516
  if (normalizedUpsertIds.length === 0) return {
5346
5517
  ok: true,
5347
5518
  items: [],
@@ -5812,6 +5983,7 @@ const useCommentsStore = defineStore("comments", () => {
5812
5983
  const getFloatingComments = (0, vue.computed)(() => {
5813
5984
  return getGroupedComments.value?.parentComments.filter((c) => !c.resolvedTime).filter((c) => {
5814
5985
  if (!isEditorBackedComment(c)) return true;
5986
+ if (isTrackedChangeThread(c)) return true;
5815
5987
  return Boolean(resolveCommentPositionEntry(c).entry);
5816
5988
  }).filter((c) => !isInlineTrackedChangeInsideTrackedTable(c));
5817
5989
  });
@@ -5944,10 +6116,12 @@ const useCommentsStore = defineStore("comments", () => {
5944
6116
  getConfig,
5945
6117
  documentsWithConverations,
5946
6118
  getGroupedComments,
6119
+ hasOpenTrackedChanges,
5947
6120
  getCommentsByPosition,
5948
6121
  getFloatingComments,
5949
6122
  getFloatingCommentInstances,
5950
6123
  getCommentAliasIds,
6124
+ getTrackedChangeThread,
5951
6125
  getCommentPositionKey,
5952
6126
  getCommentPosition,
5953
6127
  getCommentAnchoredText,
@@ -6860,30 +7034,6 @@ var _sfc_main$33 = {
6860
7034
  }
6861
7035
  };
6862
7036
  var CommentInput_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$33, [["__scopeId", "data-v-cc54b489"]]);
6863
- const collectTrackedChangeThread = (parentComment, allComments) => {
6864
- const trackedChangeId = parentComment.commentId;
6865
- const threadIds = new Set([trackedChangeId]);
6866
- const queue = [];
6867
- allComments.forEach((comment) => {
6868
- if (comment.commentId === trackedChangeId) return;
6869
- const isDirectChild = [comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(trackedChangeId);
6870
- const isTrackedChangeThreadRoot = String(trackedChangeThreadParentIdForComment(comment) ?? "") === trackedChangeId && !comment.parentCommentId;
6871
- if (isDirectChild || isTrackedChangeThreadRoot) {
6872
- threadIds.add(comment.commentId);
6873
- queue.push(comment.commentId);
6874
- }
6875
- });
6876
- for (let i = 0; i < queue.length; i += 1) {
6877
- const parentId = queue[i];
6878
- allComments.forEach((comment) => {
6879
- if ([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(parentId) && !threadIds.has(comment.commentId)) {
6880
- threadIds.add(comment.commentId);
6881
- queue.push(comment.commentId);
6882
- }
6883
- });
6884
- }
6885
- return allComments.filter((comment) => threadIds.has(comment.commentId));
6886
- };
6887
7037
  var _hoisted_1$25 = [
6888
7038
  "data-comment-instance-id",
6889
7039
  "data-comment-thread-id",
@@ -6971,12 +7121,19 @@ var _hoisted_44 = {
6971
7121
  class: "comment-separator"
6972
7122
  };
6973
7123
  var _hoisted_45 = {
7124
+ key: 2,
7125
+ class: "tracked-change-decision-alert",
7126
+ role: "alert"
7127
+ };
7128
+ var _hoisted_46 = { class: "tracked-change-decision-alert__message" };
7129
+ var _hoisted_47 = ["disabled", "data-disabled-reason"];
7130
+ var _hoisted_48 = {
6974
7131
  key: 1,
6975
7132
  class: "reply-expanded"
6976
7133
  };
6977
- var _hoisted_46 = { class: "reply-input-wrapper" };
6978
- var _hoisted_47 = { class: "reply-actions" };
6979
- var _hoisted_48 = ["disabled", "data-disabled-reason"];
7134
+ var _hoisted_49 = { class: "reply-input-wrapper" };
7135
+ var _hoisted_50 = { class: "reply-actions" };
7136
+ var _hoisted_51 = ["disabled", "data-disabled-reason"];
6980
7137
  var CLICK_OUTSIDE_HIT_TOLERANCE_PX = 3;
6981
7138
  var TRACKED_CHANGE_PREVIEW_MAX_PX = 90;
6982
7139
  var _sfc_main$32 = {
@@ -7028,6 +7185,7 @@ var _sfc_main$32 = {
7028
7185
  const isInternal = (0, vue.ref)(true);
7029
7186
  const commentInput = (0, vue.ref)(null);
7030
7187
  const editCommentInputs = (0, vue.ref)(/* @__PURE__ */ new Map());
7188
+ const trackedChangeDecisionFailure = (0, vue.ref)(null);
7031
7189
  const CLICK_OUTSIDE_HIT_SAMPLE_OFFSETS = [
7032
7190
  [0, 0],
7033
7191
  [-CLICK_OUTSIDE_HIT_TOLERANCE_PX, 0],
@@ -7195,8 +7353,13 @@ var _sfc_main$32 = {
7195
7353
  });
7196
7354
  const commentsAreReadOnly$1 = () => getConfig.value?.readOnly === true;
7197
7355
  const isReplying = (0, vue.ref)(false);
7356
+ const isSubmittingReply = (0, vue.ref)(false);
7198
7357
  const startReply = () => {
7199
7358
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7359
+ if (isV2WriteDisabled.value) return {
7360
+ ok: false,
7361
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7362
+ };
7200
7363
  isReplying.value = true;
7201
7364
  (0, vue.nextTick)(() => {
7202
7365
  commentInput.value?.focus?.();
@@ -7205,12 +7368,12 @@ var _sfc_main$32 = {
7205
7368
  };
7206
7369
  const comments = (0, vue.computed)(() => {
7207
7370
  const parentComment = props.comment;
7208
- const allComments = commentsStore.commentsList;
7209
- return (parentComment.trackedChange ? collectTrackedChangeThread(parentComment, allComments) : allComments.filter((comment) => {
7371
+ if (parentComment.trackedChange) return commentsStore.getTrackedChangeThread(parentComment);
7372
+ return commentsStore.commentsList.filter((comment) => {
7210
7373
  const isThreadedComment = comment.parentCommentId === parentComment.commentId;
7211
7374
  const isThisComment = comment.commentId === parentComment.commentId;
7212
7375
  return isThreadedComment || isThisComment;
7213
- })).sort((a, b) => {
7376
+ }).sort((a, b) => {
7214
7377
  if (a.commentId === parentComment.commentId) return -1;
7215
7378
  if (b.commentId === parentComment.commentId) return 1;
7216
7379
  return a.createdTime - b.createdTime;
@@ -7388,20 +7551,33 @@ var _sfc_main$32 = {
7388
7551
  };
7389
7552
  const handleAddComment = async () => {
7390
7553
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7554
+ if (isV2WriteDisabled.value) return {
7555
+ ok: false,
7556
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7557
+ };
7391
7558
  if (isV2Mode.value && v2CommentsAdapter.value && !isPendingNewComment.value) {
7392
- const outcome = await commentsStore.replyCommentV2({
7393
- superdoc: proxy.$superdoc,
7394
- parentCommentId: getV2ExistingReplyParentId(props.comment),
7395
- text: currentCommentText.value
7396
- });
7397
- if (!outcome?.ok) {
7559
+ if (isSubmittingReply.value) return {
7560
+ ok: false,
7561
+ reason: "reply-submit-in-flight"
7562
+ };
7563
+ isSubmittingReply.value = true;
7564
+ try {
7565
+ const outcome = await commentsStore.replyCommentV2({
7566
+ superdoc: proxy.$superdoc,
7567
+ parentCommentId: getV2ExistingReplyParentId(props.comment),
7568
+ text: currentCommentText.value
7569
+ });
7570
+ if (!outcome?.ok) {
7571
+ (0, vue.nextTick)(() => emit("resize"));
7572
+ return outcome;
7573
+ }
7574
+ isReplying.value = false;
7575
+ currentCommentText.value = "";
7398
7576
  (0, vue.nextTick)(() => emit("resize"));
7399
7577
  return outcome;
7578
+ } finally {
7579
+ isSubmittingReply.value = false;
7400
7580
  }
7401
- isReplying.value = false;
7402
- currentCommentText.value = "";
7403
- (0, vue.nextTick)(() => emit("resize"));
7404
- return outcome;
7405
7581
  }
7406
7582
  const options = {
7407
7583
  documentId: props.comment.fileId,
@@ -7427,7 +7603,14 @@ var _sfc_main$32 = {
7427
7603
  const v2TrackedChangesAdapter = (0, vue.computed)(() => isV2Mode.value ? proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null : null);
7428
7604
  const v2WriteCapability = (0, vue.computed)(() => {
7429
7605
  if (!v2CommentsAdapter.value) return null;
7430
- return v2CommentsAdapter.value.getCapabilityState?.() ?? null;
7606
+ const documentMode = commentsStore.viewingVisibility.documentMode;
7607
+ const capability = v2CommentsAdapter.value.getCapabilityState?.() ?? null;
7608
+ if (documentMode === "viewing") return {
7609
+ ...capability ?? {},
7610
+ canWrite: false,
7611
+ reason: "review-surface-read-only"
7612
+ };
7613
+ return capability;
7431
7614
  });
7432
7615
  const isV2WriteDisabled = (0, vue.computed)(() => {
7433
7616
  if (!isV2Mode.value) return false;
@@ -7435,6 +7618,11 @@ var _sfc_main$32 = {
7435
7618
  if (!cap) return false;
7436
7619
  return cap.canWrite === false;
7437
7620
  });
7621
+ const v2ReplyDisabledReason = (0, vue.computed)(() => {
7622
+ if (isSubmittingReply.value) return "reply-submit-in-flight";
7623
+ if (!isV2WriteDisabled.value) return null;
7624
+ return v2WriteCapability.value?.reason ?? "v2-write-unavailable";
7625
+ });
7438
7626
  const v2TrackedChangeCapability = (0, vue.computed)(() => {
7439
7627
  if (!v2TrackedChangesAdapter.value) return null;
7440
7628
  const documentMode = commentsStore.viewingVisibility.documentMode;
@@ -7477,6 +7665,35 @@ var _sfc_main$32 = {
7477
7665
  if (!isV2WriteDisabled.value) return null;
7478
7666
  return v2WriteCapability.value?.reason ?? "v2-write-unavailable";
7479
7667
  });
7668
+ const reportTrackedChangeDecisionFailure = (decision, outcome) => {
7669
+ trackedChangeDecisionFailure.value = {
7670
+ decision,
7671
+ reason: outcome?.reason ?? null
7672
+ };
7673
+ (0, vue.nextTick)(() => emit("resize"));
7674
+ };
7675
+ const clearTrackedChangeDecisionFailure = () => {
7676
+ if (!trackedChangeDecisionFailure.value) return;
7677
+ trackedChangeDecisionFailure.value = null;
7678
+ (0, vue.nextTick)(() => emit("resize"));
7679
+ };
7680
+ const trackedChangeDecisionFailureMessage = (0, vue.computed)(() => {
7681
+ const failure = trackedChangeDecisionFailure.value;
7682
+ if (!failure) return null;
7683
+ const action = failure.decision === "reject" ? "reject" : "accept";
7684
+ if (failure.reason === "stale-catalog") return `Couldn't ${action} this change because the review list is stale.`;
7685
+ return `Couldn't ${action} this change.`;
7686
+ });
7687
+ const retryTrackedChangeDecision = () => {
7688
+ const failure = trackedChangeDecisionFailure.value;
7689
+ if (!failure) return;
7690
+ trackedChangeDecisionFailure.value = null;
7691
+ if (failure.decision === "reject") {
7692
+ handleReject();
7693
+ return;
7694
+ }
7695
+ handleResolve();
7696
+ };
7480
7697
  const handleReject = async () => {
7481
7698
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7482
7699
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleReject;
@@ -7487,7 +7704,11 @@ var _sfc_main$32 = {
7487
7704
  comment: props.comment,
7488
7705
  decision: "reject"
7489
7706
  });
7490
- if (!outcome?.ok || outcome.success === false) return;
7707
+ if (!outcome?.ok || outcome.success === false) {
7708
+ reportTrackedChangeDecisionFailure("reject", outcome);
7709
+ return;
7710
+ }
7711
+ clearTrackedChangeDecisionFailure();
7491
7712
  if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
7492
7713
  (0, vue.nextTick)(() => {
7493
7714
  commentsStore.lastUpdate = /* @__PURE__ */ new Date();
@@ -7543,7 +7764,11 @@ var _sfc_main$32 = {
7543
7764
  comment: props.comment,
7544
7765
  decision: "accept"
7545
7766
  });
7546
- if (!outcome?.ok || outcome.success === false) return;
7767
+ if (!outcome?.ok || outcome.success === false) {
7768
+ reportTrackedChangeDecisionFailure("accept", outcome);
7769
+ return;
7770
+ }
7771
+ clearTrackedChangeDecisionFailure();
7547
7772
  if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
7548
7773
  (0, vue.nextTick)(() => {
7549
7774
  commentsStore.lastUpdate = /* @__PURE__ */ new Date();
@@ -7707,6 +7932,11 @@ var _sfc_main$32 = {
7707
7932
  if (isPendingNewComment.value) cancelComment(proxy.$superdoc);
7708
7933
  (0, vue.nextTick)(() => emit("resize"));
7709
7934
  }, { immediate: true });
7935
+ (0, vue.watch)(isV2WriteDisabled, (isDisabled) => {
7936
+ if (!isDisabled) return;
7937
+ isReplying.value = false;
7938
+ (0, vue.nextTick)(() => emit("resize"));
7939
+ });
7710
7940
  return (_ctx, _cache) => {
7711
7941
  const _directive_click_outside = (0, vue.resolveDirective)("click-outside");
7712
7942
  return (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
@@ -7895,11 +8125,19 @@ var _sfc_main$32 = {
7895
8125
  showSeparator.value(index) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_44)) : (0, vue.createCommentVNode)("", true)
7896
8126
  ]);
7897
8127
  }), 128)),
7898
- showInputSection.value && !(0, vue.unref)(getConfig).readOnly ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [!isReplying.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
8128
+ trackedChangeDecisionFailure.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_45, [(0, vue.createElementVNode)("span", _hoisted_46, (0, vue.toDisplayString)(trackedChangeDecisionFailureMessage.value), 1), (0, vue.createElementVNode)("button", {
8129
+ type: "button",
8130
+ class: "sd-button tracked-change-decision-alert__retry",
8131
+ onClick: (0, vue.withModifiers)(retryTrackedChangeDecision, ["stop", "prevent"])
8132
+ }, " Retry ")])) : (0, vue.createCommentVNode)("", true),
8133
+ showInputSection.value && !(0, vue.unref)(getConfig).readOnly ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 3 }, [!isReplying.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
7899
8134
  key: 0,
7900
- class: "reply-pill",
7901
- onClick: (0, vue.withModifiers)(startReply, ["stop", "prevent"])
7902
- }, "Reply or add others with @")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_45, [(0, vue.createElementVNode)("div", _hoisted_46, [(0, vue.createVNode)(CommentInput_default, {
8135
+ type: "button",
8136
+ class: (0, vue.normalizeClass)(["reply-pill", { "sd-is-disabled": isV2WriteDisabled.value }]),
8137
+ onClick: (0, vue.withModifiers)(startReply, ["stop", "prevent"]),
8138
+ disabled: isV2WriteDisabled.value,
8139
+ "data-disabled-reason": isV2WriteDisabled.value ? v2WriteCapability.value?.reason ?? null : null
8140
+ }, " Reply or add others with @ ", 10, _hoisted_47)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [(0, vue.createElementVNode)("div", _hoisted_49, [(0, vue.createVNode)(CommentInput_default, {
7903
8141
  ref_key: "commentInput",
7904
8142
  ref: commentInput,
7905
8143
  users: usersFiltered.value,
@@ -7910,20 +8148,20 @@ var _sfc_main$32 = {
7910
8148
  "users",
7911
8149
  "config",
7912
8150
  "comment"
7913
- ])]), (0, vue.createElementVNode)("div", _hoisted_47, [(0, vue.createElementVNode)("button", {
8151
+ ])]), (0, vue.createElementVNode)("div", _hoisted_50, [(0, vue.createElementVNode)("button", {
7914
8152
  class: "sd-button reply-btn-cancel",
7915
8153
  onClick: (0, vue.withModifiers)(handleCancel, ["stop", "prevent"])
7916
8154
  }, "Cancel"), (0, vue.createElementVNode)("button", {
7917
- class: (0, vue.normalizeClass)(["sd-button primary reply-btn-primary", { "sd-is-disabled": !hasTextContent.value || isV2WriteDisabled.value }]),
8155
+ class: (0, vue.normalizeClass)(["sd-button primary reply-btn-primary", { "sd-is-disabled": !hasTextContent.value || isV2WriteDisabled.value || isSubmittingReply.value }]),
7918
8156
  onClick: (0, vue.withModifiers)(handleAddComment, ["stop", "prevent"]),
7919
- disabled: !hasTextContent.value || isV2WriteDisabled.value,
7920
- "data-disabled-reason": isV2WriteDisabled.value ? v2WriteCapability.value?.reason ?? null : null
7921
- }, " Reply ", 10, _hoisted_48)])]))], 64)) : (0, vue.createCommentVNode)("", true)
8157
+ disabled: !hasTextContent.value || isV2WriteDisabled.value || isSubmittingReply.value,
8158
+ "data-disabled-reason": v2ReplyDisabledReason.value
8159
+ }, " Reply ", 10, _hoisted_51)])]))], 64)) : (0, vue.createCommentVNode)("", true)
7922
8160
  ], 64))], 14, _hoisted_1$25)), [[_directive_click_outside, handleClickOutside]]);
7923
8161
  };
7924
8162
  }
7925
8163
  };
7926
- var CommentDialog_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$32, [["__scopeId", "data-v-67a03ee2"]]);
8164
+ var CommentDialog_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$32, [["__scopeId", "data-v-74340036"]]);
7927
8165
  var _hoisted_1$24 = { class: "comments-list" };
7928
8166
  var _hoisted_2$19 = { key: 0 };
7929
8167
  var _hoisted_3$15 = [
@@ -8267,12 +8505,12 @@ const resolvePersistentReviewCardTop = ({ comment, anchorTop, anchorBottom, card
8267
8505
  if ((Number.isFinite(anchorBottom) ? anchorBottom : anchorTop) < viewportTop) return Math.min(anchorTop, viewportTop - cardHeight - 1);
8268
8506
  return anchorTop;
8269
8507
  };
8270
- const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId, comment }) => {
8508
+ const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId }) => {
8271
8509
  if (!id) return false;
8272
8510
  if (id === "pending") return true;
8273
8511
  if (activeCommentInstanceId != null && String(id) === String(activeCommentInstanceId)) return true;
8274
8512
  if (visibleIds?.has?.(id)) return true;
8275
- return isPersistentReviewSidebarItem(comment);
8513
+ return false;
8276
8514
  };
8277
8515
  const resolveRemovedReviewCardContinuityTarget = ({ previousIds, currentIds, removedId }) => {
8278
8516
  if (!Array.isArray(previousIds) || !(currentIds instanceof Set) || !removedId) return null;
@@ -8353,6 +8591,7 @@ var _sfc_main$30 = {
8353
8591
  const commentsRenderKey = (0, vue.ref)(0);
8354
8592
  const sidebarOffsetY = (0, vue.ref)(0);
8355
8593
  const disableInstantLayoutTransitions = (0, vue.ref)(false);
8594
+ const viewportRevision = (0, vue.ref)(0);
8356
8595
  const isPendingThread = (commentOrId) => {
8357
8596
  const pendingId = pendingComment.value?.commentId;
8358
8597
  if (!pendingId) return false;
@@ -8362,10 +8601,27 @@ var _sfc_main$30 = {
8362
8601
  const getThreadId = (comment) => {
8363
8602
  return comment?.commentId ?? comment?.importedId ?? null;
8364
8603
  };
8604
+ const floatingInstanceIndex = (0, vue.computed)(() => {
8605
+ const byId = /* @__PURE__ */ new Map();
8606
+ const byThreadId = /* @__PURE__ */ new Map();
8607
+ for (const instance of floatingCommentInstances.value) {
8608
+ if (instance?.id != null) byId.set(String(instance.id), instance);
8609
+ const threadId = getThreadId(instance?.comment);
8610
+ if (threadId == null) continue;
8611
+ const key = String(threadId);
8612
+ const threadInstances = byThreadId.get(key) ?? [];
8613
+ threadInstances.push(instance);
8614
+ byThreadId.set(key, threadInstances);
8615
+ }
8616
+ return {
8617
+ byId,
8618
+ byThreadId
8619
+ };
8620
+ });
8365
8621
  const resolveFloatingInstanceId = (threadId, preferredInstanceId = null) => {
8366
8622
  if (threadId == null) return null;
8367
8623
  const normalizedThreadId = String(threadId);
8368
- const matchingInstances = floatingCommentInstances.value.filter((instance) => String(getThreadId(instance.comment)) === normalizedThreadId);
8624
+ const matchingInstances = floatingInstanceIndex.value.byThreadId.get(normalizedThreadId) ?? [];
8369
8625
  if (!matchingInstances.length) return null;
8370
8626
  if (preferredInstanceId != null) {
8371
8627
  const preferredInstance = matchingInstances.find((instance) => String(instance.id) === String(preferredInstanceId));
@@ -8388,6 +8644,14 @@ var _sfc_main$30 = {
8388
8644
  const placeholderRefs = (0, vue.ref)({});
8389
8645
  let observer = null;
8390
8646
  const observedElements = /* @__PURE__ */ new Set();
8647
+ let viewportFrame = null;
8648
+ const refreshViewportWindow = () => {
8649
+ if (viewportFrame != null) return;
8650
+ viewportFrame = requestAnimationFrame(() => {
8651
+ viewportFrame = null;
8652
+ viewportRevision.value += 1;
8653
+ });
8654
+ };
8391
8655
  const getAnchorTop = (instance) => {
8392
8656
  if (props.currentDocument.type === "application/pdf") {
8393
8657
  const zoom = (activeZoom.value ?? 100) / 100;
@@ -8405,6 +8669,24 @@ var _sfc_main$30 = {
8405
8669
  const bottom = instance?.positionEntry?.bounds?.bottom;
8406
8670
  return Number.isFinite(bottom) ? bottom : anchorTop;
8407
8671
  };
8672
+ const geometryBearingInstances = (0, vue.computed)(() => {
8673
+ const positioned = [];
8674
+ for (const instance of floatingCommentInstances.value) {
8675
+ const id = instance?.id;
8676
+ const threadId = getThreadId(instance?.comment);
8677
+ const anchorTop = getAnchorTop(instance);
8678
+ if (id == null || threadId == null || !Number.isFinite(anchorTop)) continue;
8679
+ const top = normalizeFloatingAnchorTop(anchorTop, instance.comment);
8680
+ positioned.push({
8681
+ id,
8682
+ threadId,
8683
+ instance,
8684
+ top,
8685
+ anchorBottom: getAnchorBottom(instance, top)
8686
+ });
8687
+ }
8688
+ return positioned;
8689
+ });
8408
8690
  const getPendingAnchorTop = () => {
8409
8691
  const positionEntry = editorCommentPositions.value["pending"];
8410
8692
  if (typeof positionEntry?.bounds?.top === "number" && !isNaN(positionEntry.bounds.top)) return positionEntry.bounds.top;
@@ -8438,27 +8720,72 @@ var _sfc_main$30 = {
8438
8720
  return resolveFloatingInstanceId(instantSidebarAlignmentThreadId.value, instantSidebarAlignmentInstanceId.value);
8439
8721
  });
8440
8722
  const allPositions = (0, vue.computed)(() => {
8441
- const instances = floatingCommentInstances.value;
8723
+ viewportRevision.value;
8724
+ const positionedInstances = geometryBearingInstances.value;
8442
8725
  const hasPending = pendingComment.value && pendingComment.value.fileId === props.currentDocument.id;
8443
- if (!instances.length && !hasPending) return [];
8726
+ if (!positionedInstances.length && !floatingCommentInstances.value.length && !hasPending) return [];
8444
8727
  const positions = [];
8445
- for (const instance of instances) {
8446
- const key = instance?.id;
8447
- const anchorTop = getAnchorTop(instance);
8448
- const threadId = getThreadId(instance?.comment);
8449
- if (!key || !threadId || typeof anchorTop !== "number" || isNaN(anchorTop)) continue;
8450
- const top = normalizeFloatingAnchorTop(anchorTop, instance.comment);
8728
+ const viewportRange = getFloatingViewportRange() ?? {
8729
+ top: 0,
8730
+ bottom: Math.max(0, props.parent?.clientHeight || window.innerHeight || 0)
8731
+ };
8732
+ const renderTop = viewportRange.top - OBSERVER_MARGIN;
8733
+ const renderBottom = viewportRange.bottom + OBSERVER_MARGIN;
8734
+ const activeKey = hasPending ? "pending" : activeCommentInstanceId.value;
8735
+ const positionIds = /* @__PURE__ */ new Set();
8736
+ const pushPosition = ({ id, threadId, instance, top, anchorBottom, hasAnchorGeometry }) => {
8737
+ const key = String(id);
8738
+ if (positionIds.has(key)) return;
8739
+ positionIds.add(key);
8451
8740
  positions.push({
8452
- id: key,
8741
+ id,
8453
8742
  threadId,
8454
8743
  pageIndex: instance?.pageIndex ?? null,
8455
8744
  anchorTop: top,
8456
- anchorBottom: getAnchorBottom(instance, top),
8745
+ anchorBottom,
8746
+ hasAnchorGeometry,
8457
8747
  top,
8458
- height: measuredHeights.value[key] || ESTIMATED_HEIGHT,
8748
+ height: measuredHeights.value[id] || ESTIMATED_HEIGHT,
8459
8749
  commentRef: instance.comment,
8460
8750
  instanceRef: instance
8461
8751
  });
8752
+ };
8753
+ for (const positioned of positionedInstances) {
8754
+ const { id, threadId, instance, top, anchorBottom } = positioned;
8755
+ const isActive = activeKey != null && String(id) === String(activeKey);
8756
+ const isEditing = editingCommentId.value != null && String(threadId) === String(editingCommentId.value);
8757
+ if (!isActive && !isEditing && (anchorBottom < renderTop || top > renderBottom)) continue;
8758
+ pushPosition({
8759
+ id,
8760
+ threadId,
8761
+ instance,
8762
+ top,
8763
+ anchorBottom,
8764
+ hasAnchorGeometry: true
8765
+ });
8766
+ }
8767
+ const transitionalInstances = [];
8768
+ if (activeKey && activeKey !== "pending") {
8769
+ const activeInstance = floatingInstanceIndex.value.byId.get(String(activeKey));
8770
+ if (activeInstance) transitionalInstances.push(activeInstance);
8771
+ }
8772
+ if (editingCommentId.value != null) {
8773
+ const editingInstances = floatingInstanceIndex.value.byThreadId.get(String(editingCommentId.value)) ?? [];
8774
+ const editingInstance = editingInstances.find((instance) => instance.isPrimary) ?? editingInstances[0];
8775
+ if (editingInstance) transitionalInstances.push(editingInstance);
8776
+ }
8777
+ for (const instance of transitionalInstances) {
8778
+ const id = instance.id;
8779
+ const threadId = getThreadId(instance.comment);
8780
+ if (id == null || threadId == null || positionIds.has(String(id))) continue;
8781
+ pushPosition({
8782
+ id,
8783
+ threadId,
8784
+ instance,
8785
+ top: viewportRange.top,
8786
+ anchorBottom: viewportRange.top,
8787
+ hasAnchorGeometry: false
8788
+ });
8462
8789
  }
8463
8790
  if (hasPending) {
8464
8791
  const pendingTop = getPendingAnchorTop();
@@ -8466,17 +8793,17 @@ var _sfc_main$30 = {
8466
8793
  id: "pending",
8467
8794
  anchorTop: pendingTop,
8468
8795
  anchorBottom: pendingTop,
8796
+ hasAnchorGeometry: true,
8469
8797
  top: pendingTop,
8470
8798
  height: measuredHeights.value["pending"] || ESTIMATED_HEIGHT,
8471
8799
  commentRef: pendingComment.value
8472
8800
  });
8473
8801
  }
8474
8802
  positions.sort((a, b) => a.anchorTop - b.anchorTop);
8475
- const activeKey = hasPending ? "pending" : activeCommentInstanceId.value;
8476
8803
  resolveCollisions(positions, activeKey ? positions.findIndex((p) => p.id === activeKey) : -1, 15);
8477
- const viewportRange = getFloatingViewportRange();
8478
- if (viewportRange) for (const position of positions) {
8479
- const persistentReviewTop = resolvePersistentReviewCardTop({
8804
+ for (const position of positions) {
8805
+ if (!position.hasAnchorGeometry) continue;
8806
+ const offscreenTop = resolvePersistentReviewCardTop({
8480
8807
  comment: position.commentRef,
8481
8808
  anchorTop: position.anchorTop,
8482
8809
  anchorBottom: position.anchorBottom,
@@ -8484,21 +8811,13 @@ var _sfc_main$30 = {
8484
8811
  viewportTop: viewportRange.top,
8485
8812
  viewportBottom: viewportRange.bottom
8486
8813
  });
8487
- if (persistentReviewTop != null) position.top = persistentReviewTop;
8814
+ if (offscreenTop != null) position.top = offscreenTop;
8488
8815
  }
8489
8816
  return positions;
8490
8817
  });
8491
- const totalHeight = (0, vue.computed)(() => {
8492
- if (!allPositions.value.length) return 0;
8493
- let max = 0;
8494
- for (const p of allPositions.value) {
8495
- const bottom = p.top + p.height;
8496
- if (bottom > max) max = bottom;
8497
- }
8498
- return max + 50;
8499
- });
8500
8818
  const wrapperMinHeight = (0, vue.computed)(() => {
8501
- return totalHeight.value + Math.max(0, sidebarOffsetY.value);
8819
+ viewportRevision.value;
8820
+ return Math.max(0, props.parent?.clientHeight || 0, props.parent?.scrollHeight || 0);
8502
8821
  });
8503
8822
  const setupObserver = () => {
8504
8823
  if (observer) observer.disconnect();
@@ -8605,6 +8924,7 @@ var _sfc_main$30 = {
8605
8924
  let continuityAlignmentScheduled = false;
8606
8925
  let continuityAlignmentGeneration = 0;
8607
8926
  let continuityTransitionFrame = null;
8927
+ let activeLayoutContinuityFrame = null;
8608
8928
  const clearDeferredRemeasureTimers = () => {
8609
8929
  remeasureTimers.forEach(clearTimeout);
8610
8930
  remeasureTimers = [];
@@ -8776,14 +9096,27 @@ var _sfc_main$30 = {
8776
9096
  });
8777
9097
  let prevPositionIds = /* @__PURE__ */ new Set();
8778
9098
  let prevActiveAnchorTop = null;
9099
+ let prevActiveLayoutTop = null;
8779
9100
  (0, vue.watch)(allPositions, (positions) => {
8780
9101
  const activePosition = activeCommentInstanceId.value ? positions.find((position) => position.id === activeCommentInstanceId.value) : null;
8781
9102
  const activeAnchorTop = typeof activePosition?.anchorTop === "number" && Number.isFinite(activePosition.anchorTop) ? activePosition.anchorTop : null;
8782
- if (activeAnchorTop != null && prevActiveAnchorTop != null && Math.abs(activeAnchorTop - prevActiveAnchorTop) > 1 && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value)) {
9103
+ const activeAnchorMoved = activeAnchorTop != null && prevActiveAnchorTop != null && Math.abs(activeAnchorTop - prevActiveAnchorTop) > 1;
9104
+ const activeLayoutTop = typeof activePosition?.top === "number" && Number.isFinite(activePosition.top) ? activePosition.top : null;
9105
+ const activeLayoutMoved = activeLayoutTop != null && prevActiveLayoutTop != null && Math.abs(activeLayoutTop - prevActiveLayoutTop) > 1;
9106
+ if (activeAnchorMoved && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value)) {
8783
9107
  sidebarOffsetY.value = 0;
8784
9108
  setInstantLayoutTransitionsDisabled(false);
9109
+ } else if (activeLayoutMoved && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value) && !sidebarContinuityAnchor) {
9110
+ setInstantLayoutTransitionsDisabled(true);
9111
+ sidebarOffsetY.value += prevActiveLayoutTop - activeLayoutTop;
9112
+ if (activeLayoutContinuityFrame != null) cancelAnimationFrame(activeLayoutContinuityFrame);
9113
+ activeLayoutContinuityFrame = requestAnimationFrame(() => {
9114
+ activeLayoutContinuityFrame = null;
9115
+ if (!sidebarContinuityAnchor && !Number.isFinite(instantSidebarAlignmentTargetY.value)) setInstantLayoutTransitionsDisabled(false);
9116
+ });
8785
9117
  }
8786
9118
  prevActiveAnchorTop = activeAnchorTop;
9119
+ prevActiveLayoutTop = activeLayoutTop;
8787
9120
  const positionOrder = positions.map((position) => position.id);
8788
9121
  const currentIds = new Set(positionOrder);
8789
9122
  if (sidebarContinuityAnchor && !currentIds.has(sidebarContinuityAnchor.key)) {
@@ -8842,12 +9175,24 @@ var _sfc_main$30 = {
8842
9175
  });
8843
9176
  (0, vue.onMounted)(() => {
8844
9177
  setupObserver();
9178
+ props.parent?.addEventListener?.("scroll", refreshViewportWindow, { passive: true });
9179
+ window.addEventListener("resize", refreshViewportWindow, { passive: true });
8845
9180
  (0, vue.nextTick)(observePlaceholders);
8846
9181
  });
8847
9182
  (0, vue.onBeforeUnmount)(() => {
8848
9183
  clearDeferredRemeasureTimers();
8849
9184
  if (scrollTimer) clearTimeout(scrollTimer);
8850
9185
  clearSidebarContinuityAnchor();
9186
+ if (activeLayoutContinuityFrame != null) {
9187
+ cancelAnimationFrame(activeLayoutContinuityFrame);
9188
+ activeLayoutContinuityFrame = null;
9189
+ }
9190
+ props.parent?.removeEventListener?.("scroll", refreshViewportWindow);
9191
+ window.removeEventListener("resize", refreshViewportWindow);
9192
+ if (viewportFrame != null) {
9193
+ cancelAnimationFrame(viewportFrame);
9194
+ viewportFrame = null;
9195
+ }
8851
9196
  if (observer) {
8852
9197
  observer.disconnect();
8853
9198
  observer = null;
@@ -8909,7 +9254,7 @@ var _sfc_main$30 = {
8909
9254
  };
8910
9255
  }
8911
9256
  };
8912
- var FloatingComments_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$30, [["__scopeId", "data-v-7bf85652"]]);
9257
+ var FloatingComments_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$30, [["__scopeId", "data-v-e699ceb8"]]);
8913
9258
  var _hoisted_1$22 = {
8914
9259
  class: "superdoc__pdf-comments-layer pdf-comments-layer",
8915
9260
  "aria-hidden": "false"
@@ -9655,6 +10000,17 @@ const DRAWING_SUPPORT_TAXONOMY = {
9655
10000
  }
9656
10001
  };
9657
10002
  const DRAWING_FAMILIES = Object.keys(DRAWING_SUPPORT_TAXONOMY);
10003
+ const PAGE_CHECKPOINT_DEPENDENCY_CLASSES = Object.freeze([
10004
+ "multiple-sections",
10005
+ "furniture-page-tokens",
10006
+ "non-balanceable-multi-column-sections",
10007
+ "body-anchored-objects",
10008
+ "footnotes",
10009
+ "page-references",
10010
+ "keep-constraints",
10011
+ "tables",
10012
+ "furniture-anchored-objects"
10013
+ ]);
9658
10014
  var _hoisted_1$20 = { class: "superdoc-html-viewer" };
9659
10015
  var _hoisted_2$17 = { class: "superdoc-html-viewer__document" };
9660
10016
  var _hoisted_3$14 = ["innerHTML"];
@@ -11511,8 +11867,11 @@ function scrollToElement(targetElement, options = {
11511
11867
  const container = getScrollableParent(targetElement);
11512
11868
  const containerRect = container.getBoundingClientRect();
11513
11869
  const offsetTop = targetElement.getBoundingClientRect().top - containerRect.top + container.scrollTop;
11870
+ const scrollPaddingTopValue = window.getComputedStyle(container).scrollPaddingTop?.trim();
11871
+ const resolvedScrollPaddingTop = scrollPaddingTopValue?.endsWith("px") ? Number(scrollPaddingTopValue.slice(0, -2)) : 0;
11872
+ const scrollPaddingTop = Number.isFinite(resolvedScrollPaddingTop) && resolvedScrollPaddingTop >= 0 ? resolvedScrollPaddingTop : 0;
11514
11873
  container.scrollTo({
11515
- top: options.block === "start" ? offsetTop : offsetTop - container.clientHeight + targetElement.offsetHeight,
11874
+ top: options.block === "start" ? offsetTop - scrollPaddingTop : offsetTop - container.clientHeight + targetElement.offsetHeight,
11516
11875
  behavior: options.behavior
11517
11876
  });
11518
11877
  }
@@ -11758,7 +12117,34 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11758
12117
  tick();
11759
12118
  });
11760
12119
  }
11761
- async function navigateToAnchor(anchorUrl) {
12120
+ async function placeCaretAtHeadingStart(blockId) {
12121
+ const setSelectionTarget = getActiveEditor()?.authoring?.setSelectionTarget;
12122
+ if (typeof setSelectionTarget !== "function") return;
12123
+ const story = {
12124
+ kind: "story",
12125
+ storyType: "body"
12126
+ };
12127
+ const anchor = {
12128
+ kind: "text",
12129
+ blockId,
12130
+ offset: 0,
12131
+ story
12132
+ };
12133
+ const target = {
12134
+ kind: "selection",
12135
+ start: anchor,
12136
+ end: anchor,
12137
+ story
12138
+ };
12139
+ try {
12140
+ await Promise.resolve(setSelectionTarget({
12141
+ target,
12142
+ collapse: "start",
12143
+ focus: true
12144
+ }));
12145
+ } catch {}
12146
+ }
12147
+ async function navigateToAnchor(anchorUrl, { placeCaret = false } = {}) {
11762
12148
  if (typeof anchorUrl !== "string" || !anchorUrl.startsWith("#")) return;
11763
12149
  const anchorName = anchorUrl.slice(1);
11764
12150
  const escapedAnchorName = cssEscape(anchorName);
@@ -11783,6 +12169,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11783
12169
  if (story && story.storyType !== "body") return;
11784
12170
  const blockId = bookmark?.range?.from?.blockId;
11785
12171
  if (typeof blockId !== "string") return;
12172
+ if (placeCaret) await placeCaretAtHeadingStart(blockId);
11786
12173
  const mountedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
11787
12174
  if (mountedParagraph) {
11788
12175
  scrollToElement(mountedParagraph);
@@ -11892,7 +12279,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11892
12279
  function openDefaultPopover(ctx) {
11893
12280
  closeCurrentPopover("replace");
11894
12281
  if (ctx.isAnchorLink && isTocEntryLinkElement(ctx.element)) {
11895
- navigateToAnchor(ctx.href);
12282
+ navigateToAnchor(ctx.href, { placeCaret: ctx.documentMode !== "viewing" });
11896
12283
  return;
11897
12284
  }
11898
12285
  if (ctx.documentMode === "viewing") {
@@ -11908,7 +12295,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11908
12295
  const closePopover = () => closeCurrentPopover("closed");
11909
12296
  const goToAnchor = (anchorUrl) => {
11910
12297
  closePopover();
11911
- navigateToAnchor(anchorUrl);
12298
+ navigateToAnchor(anchorUrl, { placeCaret: true });
11912
12299
  };
11913
12300
  const generation = openGeneration;
11914
12301
  const openWithHyperlinkTarget = (hyperlinkTarget) => {
@@ -13671,7 +14058,7 @@ var _sfc_main$20 = {
13671
14058
  __name: "SuperDoc",
13672
14059
  emits: ["selection-update"],
13673
14060
  setup(__props, { emit: __emit }) {
13674
- const PdfViewer = (0, vue.defineAsyncComponent)(() => Promise.resolve().then(() => require("./chunks/PdfViewer-BtuYf2yM.cjs")));
14061
+ const PdfViewer = (0, vue.defineAsyncComponent)(() => Promise.resolve().then(() => require("./chunks/PdfViewer-CoFiGAhm.cjs")));
13675
14062
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
13676
14063
  const resolveDocumentV2Collaboration = (doc) => {
13677
14064
  const rawConfig = doc?.v2Collaboration;
@@ -13716,7 +14103,7 @@ var _sfc_main$20 = {
13716
14103
  });
13717
14104
  }
13718
14105
  });
13719
- const { getConfig, documentsWithConverations, commentsList, pendingComment, activeComment, skipSelectionUpdate, commentsByDocument, isCommentsListVisible, isFloatingCommentsReady, generalCommentIds, hasSyncedCollaborationComments, editorCommentPositions, hasInitializedLocations, isCommentHighlighted } = storeToRefs(commentsStore);
14106
+ const { getConfig, documentsWithConverations, commentsList, pendingComment, activeComment, skipSelectionUpdate, commentsByDocument, isCommentsListVisible, isFloatingCommentsReady, generalCommentIds, hasSyncedCollaborationComments, editorCommentPositions, hasInitializedLocations, isCommentHighlighted, hasOpenTrackedChanges } = storeToRefs(commentsStore);
13720
14107
  const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate, refreshTrackedChangeCommentsByIds, syncTrackedChangePositionsWithDocument, syncTrackedChangeComments, addComment, addHydratedComment, getComment, resolveCommentPositionEntry, belongsToDocument, COMMENT_EVENTS, requestInstantSidebarAlignment, peekInstantSidebarAlignment, clearInstantSidebarAlignment } = commentsStore;
13721
14108
  const { proxy } = (0, vue.getCurrentInstance)();
13722
14109
  commentsStore.proxy = proxy;
@@ -13896,18 +14283,19 @@ var _sfc_main$20 = {
13896
14283
  if (!documentId) return;
13897
14284
  clearV2CommandShortcutBinding(documentId);
13898
14285
  if (typeof bindEditShortcuts !== "function") return;
13899
- const ui = require_create_super_doc_ui.createSuperDocUI({ superdoc: proxy.$superdoc });
14286
+ const fallbackUi = require_create_super_doc_ui.createSuperDocUI({ superdoc: proxy.$superdoc });
14287
+ const resolveUi = () => proxy.$superdoc?.toolbar?.ui ?? fallbackUi;
13900
14288
  const openLinkWorkflow = resolveV2LinkWorkflowOpener();
13901
14289
  const unbind = bindEditShortcuts({
13902
14290
  commandRoute: {
13903
- executeAsync: (commandId, payload) => ui.commands.executeAsync(commandId, payload),
13904
- getState: (commandId) => ui.commands.get(commandId).getState()
14291
+ executeAsync: (commandId, payload) => resolveUi().commands.executeAsync(commandId, payload),
14292
+ getState: (commandId) => resolveUi().commands.get(commandId).getState()
13905
14293
  },
13906
14294
  ...openLinkWorkflow ? { openLinkWorkflow } : {}
13907
14295
  });
13908
14296
  v2CommandShortcutBindings.set(documentId, {
13909
14297
  unbind,
13910
- ui
14298
+ ui: fallbackUi
13911
14299
  });
13912
14300
  };
13913
14301
  const clearV2SessionShortcutBinding = (documentId) => {
@@ -14731,13 +15119,17 @@ var _sfc_main$20 = {
14731
15119
  v2RulerReady.value = false;
14732
15120
  };
14733
15121
  let v2TypingReviewHydrationTimer = 0;
14734
- const hydrateV2ReviewRowsFromHost = (options = {}) => {
15122
+ const hydrateV2CommentRowsFromHost = () => {
14735
15123
  const commentsAdapter = proxy.$superdoc?.activeEditor?.v2Comments ?? null;
14736
- if (commentsAdapter) commentsStore.hydrateCommentsFromV2?.({
15124
+ if (!commentsAdapter) return;
15125
+ commentsStore.hydrateCommentsFromV2?.({
14737
15126
  superdoc: proxy.$superdoc,
14738
15127
  adapter: commentsAdapter,
14739
15128
  documentId: proxy.$superdoc?.activeEditor?.documentId ?? null
14740
15129
  });
15130
+ };
15131
+ const hydrateV2ReviewRowsFromHost = (options = {}) => {
15132
+ hydrateV2CommentRowsFromHost();
14741
15133
  const trackedChangesAdapter = proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null;
14742
15134
  if (!trackedChangesAdapter) return;
14743
15135
  commentsStore.hydrateTrackedChangesFromV2?.({
@@ -14840,6 +15232,10 @@ var _sfc_main$20 = {
14840
15232
  const receipt = event.origin === "history" ? null : event.receipt;
14841
15233
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
14842
15234
  v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
15235
+ if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
15236
+ hydrateV2CommentRowsFromHost();
15237
+ return;
15238
+ }
14843
15239
  if (require_create_super_doc_ui.isV2EditableTextMutationEvent(event)) {
14844
15240
  scheduleV2TypingReviewHydration();
14845
15241
  return;
@@ -15301,7 +15697,7 @@ var _sfc_main$20 = {
15301
15697
  if (isV2Mode.value && !v2GeometryAvailable.value && !hasPdfFloatingComments.value) return false;
15302
15698
  if (!shouldRenderCommentsInViewing.value) return false;
15303
15699
  if (!shouldUseSidebarComments.value) return false;
15304
- return pendingComment.value || floatingComments.value.length > 0 && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
15700
+ return pendingComment.value || (floatingComments.value.length > 0 || hasOpenTrackedChanges.value) && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
15305
15701
  });
15306
15702
  const activeCompactComment = (0, vue.computed)(() => {
15307
15703
  if (showCommentsSidebar.value) return null;
@@ -15970,7 +16366,7 @@ var _sfc_main$20 = {
15970
16366
  };
15971
16367
  }
15972
16368
  };
15973
- var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-c4ab995e"]]);
16369
+ var SuperDoc_default = /* @__PURE__ */ require__plugin_vue_export_helper.__plugin_vue_export_helper_default(_sfc_main$20, [["__scopeId", "data-v-4084bf1b"]]);
15974
16370
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
15975
16371
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
15976
16372
  var piniaDevtoolsSuppressionState = {
@@ -34539,7 +34935,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
34539
34935
  this.config.colors = shuffleArray(this.config.colors);
34540
34936
  this.userColorMap = /* @__PURE__ */ new Map();
34541
34937
  this.colorIndex = 0;
34542
- this.version = "2.0.0-next.27";
34938
+ this.version = "2.0.0-next.29";
34543
34939
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
34544
34940
  this.superdocId = config.superdocId || require_uuid.v4_default();
34545
34941
  this.colors = this.config.colors ?? [];