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
@@ -5,7 +5,7 @@ import { t as v4_default } from "./chunks/uuid-B2Sqk-3p.es.js";
5
5
  import { a as init_dist, i as global, n as init_dist$1, r as process$1, t as require_jszip_min } from "./chunks/jszip-DzmwAHr3.es.js";
6
6
  import { t as __plugin_vue_export_helper_default } from "./chunks/_plugin-vue_export-helper-CInC0bKI.es.js";
7
7
  import { n as PDF_TO_CSS_UNITS } from "./chunks/constants-CY3R3_kF.es.js";
8
- import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, t as createSuperDocUI } from "./chunks/create-super-doc-ui-Dvl3EZsv.es.js";
8
+ import { a as createV2ReviewMutationReconciler, i as isV2EditableTextMutationEvent, o as getV2TrackedChangeMutationImpact, t as createSuperDocUI } from "./chunks/create-super-doc-ui-BKVf2uCM.es.js";
9
9
  import * as Vue from "vue";
10
10
  import { Fragment, Teleport, Transition, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineAsyncComponent, defineComponent, getCurrentInstance, h, inject, markRaw, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onActivated, onBeforeUnmount, onDeactivated, onMounted, openBlock, reactive, ref, renderList, renderSlot, resolveDirective, resolveDynamicComponent, shallowRef, toDisplayString, toRaw, toRef, unref, useAttrs, vModelText, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
11
11
  import "y-websocket";
@@ -144,19 +144,50 @@ function compareVersions(version1, version2) {
144
144
  }
145
145
  return 0;
146
146
  }
147
+ var registries = /* @__PURE__ */ new WeakMap();
148
+ function ownerDocumentFor(el) {
149
+ return el.ownerDocument ?? document;
150
+ }
151
+ function unregister(el, ownerDocument) {
152
+ const registry = registries.get(ownerDocument);
153
+ registry?.bindings.delete(el);
154
+ if (registry && registry.bindings.size === 0) {
155
+ ownerDocument.removeEventListener("click", registry.listener);
156
+ registries.delete(ownerDocument);
157
+ }
158
+ }
159
+ function register(el, binding) {
160
+ const ownerDocument = ownerDocumentFor(el);
161
+ if (el.__clickOutsideDocument && el.__clickOutsideDocument !== ownerDocument) unregister(el, el.__clickOutsideDocument);
162
+ let registry = registries.get(ownerDocument);
163
+ if (!registry) {
164
+ const bindings = /* @__PURE__ */ new Map();
165
+ const listener = (event) => {
166
+ const path = typeof event.composedPath === "function" ? event.composedPath() : null;
167
+ for (const [element, currentBinding] of bindings) if (!(path?.includes(element) ?? element.contains(event.target))) currentBinding.value?.(event);
168
+ };
169
+ registry = {
170
+ bindings,
171
+ listener
172
+ };
173
+ registries.set(ownerDocument, registry);
174
+ ownerDocument.addEventListener("click", listener);
175
+ }
176
+ registry.bindings.set(el, binding);
177
+ el.__clickOutsideHandler = registry.listener;
178
+ el.__clickOutsideDocument = ownerDocument;
179
+ }
147
180
  var v_click_outside_default = {
148
181
  mounted(el, binding) {
149
- const clickOutsideHandler = (event) => {
150
- if (!el.contains(event.target)) binding.value?.(event);
151
- };
152
- document.addEventListener("click", clickOutsideHandler);
153
- el.__clickOutsideHandler = clickOutsideHandler;
182
+ register(el, binding);
183
+ },
184
+ updated(el, binding) {
185
+ register(el, binding);
154
186
  },
155
187
  unmounted(el) {
156
- if (el.__clickOutsideHandler) {
157
- document.removeEventListener("click", el.__clickOutsideHandler);
158
- delete el.__clickOutsideHandler;
159
- }
188
+ unregister(el, el.__clickOutsideDocument ?? ownerDocumentFor(el));
189
+ delete el.__clickOutsideHandler;
190
+ delete el.__clickOutsideDocument;
160
191
  }
161
192
  };
162
193
  function hasUser(entry) {
@@ -2517,7 +2548,7 @@ var aggregateRectBounds = (rects) => {
2517
2548
  };
2518
2549
  var groupRectsByPage = (rects) => {
2519
2550
  const groupedRects = /* @__PURE__ */ new Map();
2520
- rects.forEach((rect) => {
2551
+ (Array.isArray(rects) ? rects : []).forEach((rect) => {
2521
2552
  const pageIndex = toFinitePageIndex(rect?.pageIndex);
2522
2553
  if (pageIndex == null) return;
2523
2554
  const pageRects = groupedRects.get(pageIndex);
@@ -2544,31 +2575,40 @@ var resolveSelectionPageIndex = (comment) => {
2544
2575
  };
2545
2576
  var buildSingleFloatingCommentInstance = ({ id, threadId, comment, positionKey, positionEntry }) => {
2546
2577
  const geometryPageIndex = toFinitePageIndex(positionEntry?.pageIndex);
2547
- const pageIndex = geometryPageIndex != null ? geometryPageIndex : resolveSelectionPageIndex(comment);
2578
+ const fallbackPageIndex = comment?.trackedChange ? null : resolveSelectionPageIndex(comment);
2548
2579
  return {
2549
2580
  id,
2550
2581
  threadId,
2551
2582
  comment,
2552
2583
  positionKey,
2553
2584
  positionEntry: positionEntry ?? null,
2554
- pageIndex,
2585
+ pageIndex: geometryPageIndex ?? fallbackPageIndex,
2555
2586
  isPrimary: true
2556
2587
  };
2557
2588
  };
2558
- var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry }) => {
2589
+ var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry, primaryInstanceId }) => {
2559
2590
  const rectGroups = groupRectsByPage(positionEntry?.rects);
2560
2591
  if (rectGroups.length < 2) return [];
2561
- const primaryPageIndex = toFinitePageIndex(positionEntry?.pageIndex) ?? rectGroups[0]?.pageIndex ?? null;
2562
- return rectGroups.map(({ pageIndex, rects }) => {
2592
+ const renderableGroups = rectGroups.map(({ pageIndex, rects }) => {
2563
2593
  const bounds = aggregateRectBounds(rects);
2564
- if (!bounds) return null;
2594
+ return bounds ? {
2595
+ pageIndex,
2596
+ rects,
2597
+ bounds
2598
+ } : null;
2599
+ }).filter(Boolean);
2600
+ if (renderableGroups.length < 2) return [];
2601
+ const geometryPageIndex = toFinitePageIndex(positionEntry?.pageIndex);
2602
+ const primaryPageIndex = renderableGroups.some(({ pageIndex }) => pageIndex === geometryPageIndex) ? geometryPageIndex : renderableGroups[0]?.pageIndex ?? null;
2603
+ return renderableGroups.map(({ pageIndex, rects, bounds }) => {
2604
+ const isPrimary = pageIndex === primaryPageIndex;
2565
2605
  return {
2566
- id: buildPageScopedInstanceId(positionKey, pageIndex),
2606
+ id: isPrimary ? primaryInstanceId : buildPageScopedInstanceId(primaryInstanceId, pageIndex),
2567
2607
  threadId: comment?.commentId ?? positionKey,
2568
2608
  comment,
2569
2609
  positionKey,
2570
2610
  pageIndex,
2571
- isPrimary: pageIndex === primaryPageIndex,
2611
+ isPrimary,
2572
2612
  positionEntry: {
2573
2613
  ...positionEntry,
2574
2614
  pageIndex,
@@ -2576,7 +2616,7 @@ var buildRepeatedHeaderFooterInstances = ({ comment, positionKey, positionEntry
2576
2616
  bounds
2577
2617
  }
2578
2618
  };
2579
- }).filter(Boolean);
2619
+ });
2580
2620
  };
2581
2621
  const buildFloatingCommentInstances = ({ comment, positionKey, positionEntry, fallbackId }) => {
2582
2622
  const instanceId = comment?.trackedChange && comment?.trackedChangeCanonicalId && positionKey && fallbackId && String(positionKey) !== String(fallbackId) ? fallbackId : positionKey ?? fallbackId;
@@ -2585,7 +2625,8 @@ const buildFloatingCommentInstances = ({ comment, positionKey, positionEntry, fa
2585
2625
  const repeatedInstances = buildRepeatedHeaderFooterInstances({
2586
2626
  comment,
2587
2627
  positionKey,
2588
- positionEntry
2628
+ positionEntry,
2629
+ primaryInstanceId: instanceId
2589
2630
  });
2590
2631
  if (repeatedInstances.length > 0) return repeatedInstances;
2591
2632
  }
@@ -2759,6 +2800,79 @@ const trackedChangeThreadParentIdForComment = (comment) => {
2759
2800
  if (comment?.trackedChangeParentId != null && hasLegacyTrackedChangeThreadSignal(comment)) return comment.trackedChangeParentId;
2760
2801
  return null;
2761
2802
  };
2803
+ const buildTrackedChangeThreadIndex = (allComments, previous = /* @__PURE__ */ new Map()) => {
2804
+ const childrenByParentId = /* @__PURE__ */ new Map();
2805
+ const anchoredRootsByTrackedChangeId = /* @__PURE__ */ new Map();
2806
+ const commentsById = /* @__PURE__ */ new Map();
2807
+ const sourceIndex = /* @__PURE__ */ new Map();
2808
+ const append = (map, key, value) => {
2809
+ const existing = map.get(key);
2810
+ if (existing) existing.push(value);
2811
+ else map.set(key, [value]);
2812
+ };
2813
+ allComments.forEach((comment, index) => {
2814
+ sourceIndex.set(comment, index);
2815
+ append(commentsById, comment.commentId, comment);
2816
+ new Set([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id))).forEach((parentId) => append(childrenByParentId, parentId, comment));
2817
+ const trackedChangeThreadParentId = trackedChangeThreadParentIdForComment(comment);
2818
+ if (trackedChangeThreadParentId != null && !comment.parentCommentId) append(anchoredRootsByTrackedChangeId, String(trackedChangeThreadParentId), comment);
2819
+ });
2820
+ const next = /* @__PURE__ */ new Map();
2821
+ for (const parentComment of allComments) {
2822
+ if (!parentComment?.trackedChange || parentComment.commentId == null) continue;
2823
+ const trackedChangeId = parentComment.commentId;
2824
+ const threadIds = new Set([trackedChangeId]);
2825
+ const queue = [];
2826
+ const seed = typeof trackedChangeId === "string" ? [...childrenByParentId.get(trackedChangeId) ?? [], ...anchoredRootsByTrackedChangeId.get(trackedChangeId) ?? []] : [];
2827
+ for (const comment of seed) {
2828
+ const id = comment.commentId;
2829
+ if (id === trackedChangeId || threadIds.has(id)) continue;
2830
+ threadIds.add(id);
2831
+ queue.push(id);
2832
+ }
2833
+ for (let index = 0; index < queue.length; index += 1) {
2834
+ const parentId = queue[index];
2835
+ for (const comment of typeof parentId === "string" ? childrenByParentId.get(parentId) ?? [] : []) {
2836
+ const id = comment.commentId;
2837
+ if (threadIds.has(id)) continue;
2838
+ threadIds.add(id);
2839
+ queue.push(id);
2840
+ }
2841
+ }
2842
+ const members = [...threadIds].flatMap((id) => commentsById.get(id) ?? []).sort((left, right) => (sourceIndex.get(left) ?? 0) - (sourceIndex.get(right) ?? 0));
2843
+ members.sort((left, right) => {
2844
+ if (left === parentComment) return -1;
2845
+ if (right === parentComment) return 1;
2846
+ return Number(left.createdTime) - Number(right.createdTime);
2847
+ });
2848
+ const prior = previous.get(trackedChangeId);
2849
+ const stable = prior?.length === members.length && members.every((comment, index) => prior[index] === comment) ? prior : members;
2850
+ next.set(trackedChangeId, stable);
2851
+ }
2852
+ return next;
2853
+ };
2854
+ const buildTrackedChangeDecisionLinkIndex = (allComments, previous = /* @__PURE__ */ new Map()) => {
2855
+ const grouped = /* @__PURE__ */ new Map();
2856
+ for (const comment of allComments) {
2857
+ const ids = new Set([
2858
+ comment.parentCommentId,
2859
+ comment.threadingParentCommentId,
2860
+ comment.trackedChangeParentId,
2861
+ comment.trackedChangeThreadParentId
2862
+ ].filter((id) => id != null).map((id) => String(id)));
2863
+ for (const id of ids) {
2864
+ const existing = grouped.get(id);
2865
+ if (existing) existing.push(comment);
2866
+ else grouped.set(id, [comment]);
2867
+ }
2868
+ }
2869
+ const next = /* @__PURE__ */ new Map();
2870
+ for (const [id, comments] of grouped) {
2871
+ const prior = previous.get(id);
2872
+ next.set(id, prior?.length === comments.length && comments.every((comment, index) => prior[index] === comment) ? prior : comments);
2873
+ }
2874
+ return next;
2875
+ };
2762
2876
  var Editor = class {
2763
2877
  constructor() {
2764
2878
  throw new Error("SuperDoc v2 beta: v1 editor-backed comment import paths are not available.");
@@ -2919,6 +3033,22 @@ const useCommentsStore = defineStore("comments", () => {
2919
3033
  const suppressInternalExternal = ref(true);
2920
3034
  const currentCommentText = ref("");
2921
3035
  const commentsList = ref([]);
3036
+ let priorTrackedChangeThreadIndex = /* @__PURE__ */ new Map();
3037
+ const trackedChangeThreadIndex = computed(() => {
3038
+ const next = buildTrackedChangeThreadIndex(commentsList.value, priorTrackedChangeThreadIndex);
3039
+ priorTrackedChangeThreadIndex = next;
3040
+ return next;
3041
+ });
3042
+ const getTrackedChangeThread = (parentComment) => {
3043
+ const id = parentComment?.commentId ?? null;
3044
+ return (id != null ? trackedChangeThreadIndex.value.get(id) : null) ?? [parentComment].filter(Boolean);
3045
+ };
3046
+ let priorTrackedChangeDecisionLinkIndex = /* @__PURE__ */ new Map();
3047
+ const trackedChangeDecisionLinkIndex = computed(() => {
3048
+ const next = buildTrackedChangeDecisionLinkIndex(commentsList.value, priorTrackedChangeDecisionLinkIndex);
3049
+ priorTrackedChangeDecisionLinkIndex = next;
3050
+ return next;
3051
+ });
2922
3052
  const isCommentsListVisible = ref(false);
2923
3053
  const editorCommentIds = ref([]);
2924
3054
  const editorCommentPositions = ref({});
@@ -3705,6 +3835,7 @@ const useCommentsStore = defineStore("comments", () => {
3705
3835
  };
3706
3836
  };
3707
3837
  const getGroupedComments = computed(() => buildGroupedComments(compareByCreatedTime));
3838
+ const hasOpenTrackedChanges = computed(() => getGroupedComments.value?.parentComments.some((comment) => comment.trackedChange && !comment.resolvedTime) ?? false);
3708
3839
  const getCommentsByPosition = computed(() => buildGroupedComments(compareByPosition));
3709
3840
  const hasOverlapId = (id) => overlappedIds.includes(id);
3710
3841
  const documentsWithConverations = computed(() => {
@@ -4890,7 +5021,7 @@ const useCommentsStore = defineStore("comments", () => {
4890
5021
  superdoc
4891
5022
  };
4892
5023
  let resolvedCount = 0;
4893
- commentsList.value.forEach((linkedComment) => {
5024
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4894
5025
  if (!linkedComment || linkedComment === comment) return;
4895
5026
  if (linkedComment.resolvedTime) return;
4896
5027
  if (![
@@ -4918,6 +5049,16 @@ const useCommentsStore = defineStore("comments", () => {
4918
5049
  }
4919
5050
  return trackedChangeIds;
4920
5051
  };
5052
+ const getTrackedChangeDecisionLinks = (comment) => {
5053
+ const linked = [];
5054
+ const seen = /* @__PURE__ */ new Set();
5055
+ for (const id of collectTrackedChangeDecisionIds(comment)) for (const candidate of trackedChangeDecisionLinkIndex.value.get(String(id)) ?? []) {
5056
+ if (seen.has(candidate)) continue;
5057
+ seen.add(candidate);
5058
+ linked.push(candidate);
5059
+ }
5060
+ return linked;
5061
+ };
4921
5062
  const collectTrackedChangeEntityIdsFromReceipt = (receipt) => {
4922
5063
  const record = receipt && typeof receipt === "object" ? receipt : null;
4923
5064
  if (!record?.success) return /* @__PURE__ */ new Set();
@@ -4949,7 +5090,7 @@ const useCommentsStore = defineStore("comments", () => {
4949
5090
  const trackedChangeIds = collectTrackedChangeDecisionIds(comment);
4950
5091
  if (!trackedChangeIds.size) return 0;
4951
5092
  let detachedCount = 0;
4952
- commentsList.value.forEach((linkedComment) => {
5093
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4953
5094
  if (!linkedComment || linkedComment === comment) return;
4954
5095
  const trackedChangeParentId = normalizeCommentId(linkedComment.trackedChangeParentId);
4955
5096
  const trackedChangeThreadParentId = normalizeCommentId(linkedComment.trackedChangeThreadParentId);
@@ -4965,7 +5106,7 @@ const useCommentsStore = defineStore("comments", () => {
4965
5106
  });
4966
5107
  return detachedCount;
4967
5108
  };
4968
- const pruneDecidedTrackedChangeRow = ({ superdoc, decidedId, decidedIds, documentId } = {}) => {
5109
+ const pruneDecidedTrackedChangeRow = ({ superdoc, comment: targetComment, decidedId, decidedIds, documentId } = {}) => {
4969
5110
  const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(Boolean));
4970
5111
  const normalizedDocumentId = normalizeCommentId(documentId);
4971
5112
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
@@ -4974,7 +5115,13 @@ const useCommentsStore = defineStore("comments", () => {
4974
5115
  };
4975
5116
  const removedIds = /* @__PURE__ */ new Set();
4976
5117
  let removed = false;
4977
- commentsList.value = commentsList.value.filter((comment) => {
5118
+ const targetIndex = targetComment ? commentsList.value.indexOf(targetComment) : -1;
5119
+ const targetIds = targetComment?.trackedChange ? collectTrackedChangeDecisionIds(targetComment) : /* @__PURE__ */ new Set();
5120
+ if (targetIndex >= 0 && belongsToTrackedChangeSyncDocument(targetComment, normalizedDocumentId) && Array.from(normalizedDecidedIds).some((id) => targetIds.has(id))) {
5121
+ targetIds.forEach((id) => removedIds.add(id));
5122
+ commentsList.value.splice(targetIndex, 1);
5123
+ removed = true;
5124
+ } else commentsList.value = commentsList.value.filter((comment) => {
4978
5125
  if (!comment?.trackedChange) return true;
4979
5126
  if (!belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
4980
5127
  const ids = collectTrackedChangeDecisionIds(comment);
@@ -5010,6 +5157,25 @@ const useCommentsStore = defineStore("comments", () => {
5010
5157
  adapter.clearActiveTrackedChangeTargetIfMatches(decidedId);
5011
5158
  } catch {}
5012
5159
  };
5160
+ const waitForTrackedDecisionCanonicalPaint = async ({ superdoc, receipt } = {}) => {
5161
+ const readiness = superdoc?.activeEditor?.documentMutationReadiness;
5162
+ if (receipt?.success !== true || typeof receipt?.txId !== "string" || !receipt.txId || typeof readiness?.whenPainted !== "function") return;
5163
+ try {
5164
+ await readiness.whenPainted(receipt);
5165
+ const requestFrame = globalThis.requestAnimationFrame;
5166
+ if (typeof requestFrame === "function") await new Promise((resolve) => {
5167
+ let settled = false;
5168
+ const finish = () => {
5169
+ if (settled) return;
5170
+ settled = true;
5171
+ clearTimeout(fallback);
5172
+ resolve();
5173
+ };
5174
+ const fallback = setTimeout(finish, 250);
5175
+ requestFrame(() => requestFrame(() => setTimeout(finish, 0)));
5176
+ });
5177
+ } catch {}
5178
+ };
5013
5179
  const tracePostDecisionBackgroundReconcile = (superdoc, event, payload = {}) => {
5014
5180
  const record = {
5015
5181
  event,
@@ -5161,7 +5327,7 @@ const useCommentsStore = defineStore("comments", () => {
5161
5327
  adapter: v2Adapter,
5162
5328
  documentId: effectiveDocumentId,
5163
5329
  items: listResult.items ?? [],
5164
- pruneStale: listResult.complete !== false && listResult.sourceCoverageComplete !== false
5330
+ pruneStale: listResult.complete === true && listResult.sourceCoverageComplete === true
5165
5331
  });
5166
5332
  const liveIds = new Set((listResult.items ?? []).map((item) => item && item.id != null ? String(item.id) : null).filter(Boolean));
5167
5333
  if (!(decidedId != null && liveIds.has(decidedId))) {
@@ -5205,6 +5371,17 @@ const useCommentsStore = defineStore("comments", () => {
5205
5371
  };
5206
5372
  };
5207
5373
  if (!reliableDecidedIds.size || !effectiveDocumentId) return runBlockingPostDecisionReconcile();
5374
+ reliableDecidedIds.forEach((id$1) => clearV2ActiveTrackedChangeTarget(v2Adapter, id$1));
5375
+ schedulePostDecisionBackgroundReconcile({
5376
+ superdoc,
5377
+ trackedChangesAdapter: v2Adapter,
5378
+ commentsAdapter: getV2CommentsAdapter(superdoc),
5379
+ documentId: effectiveDocumentId
5380
+ });
5381
+ await waitForTrackedDecisionCanonicalPaint({
5382
+ superdoc,
5383
+ receipt: outcome.receipt
5384
+ });
5208
5385
  resolveLinkedCommentsForTrackedChangeDecision({
5209
5386
  superdoc,
5210
5387
  comment,
@@ -5216,17 +5393,11 @@ const useCommentsStore = defineStore("comments", () => {
5216
5393
  });
5217
5394
  pruneDecidedTrackedChangeRow({
5218
5395
  superdoc,
5396
+ comment,
5219
5397
  decidedIds: reliableDecidedIds,
5220
5398
  documentId: effectiveDocumentId
5221
5399
  });
5222
5400
  if (wasActiveBeforeDecide) clearActiveCommentSelection();
5223
- reliableDecidedIds.forEach((id$1) => clearV2ActiveTrackedChangeTarget(v2Adapter, id$1));
5224
- schedulePostDecisionBackgroundReconcile({
5225
- superdoc,
5226
- trackedChangesAdapter: v2Adapter,
5227
- commentsAdapter: getV2CommentsAdapter(superdoc),
5228
- documentId: effectiveDocumentId
5229
- });
5230
5401
  return {
5231
5402
  ok: true,
5232
5403
  success: true
@@ -5295,7 +5466,7 @@ const useCommentsStore = defineStore("comments", () => {
5295
5466
  reason: "adapter-stale"
5296
5467
  };
5297
5468
  if (!result?.ok) return result;
5298
- const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete !== false && result.sourceCoverageComplete !== false;
5469
+ const pruneStale = !(trackedChangesListMode === "startup-page" || trackedChangesListMode === "interaction-prime" || trackedChangesListMode === "visible-window") && result.visibleWindowSource == null && result.complete === true && result.sourceCoverageComplete === true;
5299
5470
  reconcileTrackedChangesFromV2({
5300
5471
  superdoc,
5301
5472
  adapter: effectiveAdapter,
@@ -5332,12 +5503,12 @@ const useCommentsStore = defineStore("comments", () => {
5332
5503
  };
5333
5504
  const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
5334
5505
  const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
5335
- if (normalizedRemovedIds.size > 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
5506
+ const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
5507
+ if (normalizedRemovedIds.size > 0 && normalizedUpsertIds.length === 0 && effectiveDocumentId != null) pruneDecidedTrackedChangeRow({
5336
5508
  superdoc,
5337
5509
  decidedIds: normalizedRemovedIds,
5338
5510
  documentId: effectiveDocumentId
5339
5511
  });
5340
- const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
5341
5512
  if (normalizedUpsertIds.length === 0) return {
5342
5513
  ok: true,
5343
5514
  items: [],
@@ -5808,6 +5979,7 @@ const useCommentsStore = defineStore("comments", () => {
5808
5979
  const getFloatingComments = computed(() => {
5809
5980
  return getGroupedComments.value?.parentComments.filter((c) => !c.resolvedTime).filter((c) => {
5810
5981
  if (!isEditorBackedComment(c)) return true;
5982
+ if (isTrackedChangeThread(c)) return true;
5811
5983
  return Boolean(resolveCommentPositionEntry(c).entry);
5812
5984
  }).filter((c) => !isInlineTrackedChangeInsideTrackedTable(c));
5813
5985
  });
@@ -5940,10 +6112,12 @@ const useCommentsStore = defineStore("comments", () => {
5940
6112
  getConfig,
5941
6113
  documentsWithConverations,
5942
6114
  getGroupedComments,
6115
+ hasOpenTrackedChanges,
5943
6116
  getCommentsByPosition,
5944
6117
  getFloatingComments,
5945
6118
  getFloatingCommentInstances,
5946
6119
  getCommentAliasIds,
6120
+ getTrackedChangeThread,
5947
6121
  getCommentPositionKey,
5948
6122
  getCommentPosition,
5949
6123
  getCommentAnchoredText,
@@ -6851,30 +7025,6 @@ var CommentInput_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
6851
7025
  };
6852
7026
  }
6853
7027
  }, [["__scopeId", "data-v-cc54b489"]]);
6854
- const collectTrackedChangeThread = (parentComment, allComments) => {
6855
- const trackedChangeId = parentComment.commentId;
6856
- const threadIds = new Set([trackedChangeId]);
6857
- const queue = [];
6858
- allComments.forEach((comment) => {
6859
- if (comment.commentId === trackedChangeId) return;
6860
- const isDirectChild = [comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(trackedChangeId);
6861
- const isTrackedChangeThreadRoot = String(trackedChangeThreadParentIdForComment(comment) ?? "") === trackedChangeId && !comment.parentCommentId;
6862
- if (isDirectChild || isTrackedChangeThreadRoot) {
6863
- threadIds.add(comment.commentId);
6864
- queue.push(comment.commentId);
6865
- }
6866
- });
6867
- for (let i = 0; i < queue.length; i += 1) {
6868
- const parentId = queue[i];
6869
- allComments.forEach((comment) => {
6870
- if ([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(parentId) && !threadIds.has(comment.commentId)) {
6871
- threadIds.add(comment.commentId);
6872
- queue.push(comment.commentId);
6873
- }
6874
- });
6875
- }
6876
- return allComments.filter((comment) => threadIds.has(comment.commentId));
6877
- };
6878
7028
  var _hoisted_1$25 = [
6879
7029
  "data-comment-instance-id",
6880
7030
  "data-comment-thread-id",
@@ -6962,12 +7112,19 @@ var _hoisted_44 = {
6962
7112
  class: "comment-separator"
6963
7113
  };
6964
7114
  var _hoisted_45 = {
7115
+ key: 2,
7116
+ class: "tracked-change-decision-alert",
7117
+ role: "alert"
7118
+ };
7119
+ var _hoisted_46 = { class: "tracked-change-decision-alert__message" };
7120
+ var _hoisted_47 = ["disabled", "data-disabled-reason"];
7121
+ var _hoisted_48 = {
6965
7122
  key: 1,
6966
7123
  class: "reply-expanded"
6967
7124
  };
6968
- var _hoisted_46 = { class: "reply-input-wrapper" };
6969
- var _hoisted_47 = { class: "reply-actions" };
6970
- var _hoisted_48 = ["disabled", "data-disabled-reason"];
7125
+ var _hoisted_49 = { class: "reply-input-wrapper" };
7126
+ var _hoisted_50 = { class: "reply-actions" };
7127
+ var _hoisted_51 = ["disabled", "data-disabled-reason"];
6971
7128
  var CLICK_OUTSIDE_HIT_TOLERANCE_PX = 3;
6972
7129
  var TRACKED_CHANGE_PREVIEW_MAX_PX = 90;
6973
7130
  var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
@@ -7019,6 +7176,7 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7019
7176
  const isInternal = ref(true);
7020
7177
  const commentInput = ref(null);
7021
7178
  const editCommentInputs = ref(/* @__PURE__ */ new Map());
7179
+ const trackedChangeDecisionFailure = ref(null);
7022
7180
  const CLICK_OUTSIDE_HIT_SAMPLE_OFFSETS = [
7023
7181
  [0, 0],
7024
7182
  [-CLICK_OUTSIDE_HIT_TOLERANCE_PX, 0],
@@ -7186,8 +7344,13 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7186
7344
  });
7187
7345
  const commentsAreReadOnly$1 = () => getConfig.value?.readOnly === true;
7188
7346
  const isReplying = ref(false);
7347
+ const isSubmittingReply = ref(false);
7189
7348
  const startReply = () => {
7190
7349
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7350
+ if (isV2WriteDisabled.value) return {
7351
+ ok: false,
7352
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7353
+ };
7191
7354
  isReplying.value = true;
7192
7355
  nextTick(() => {
7193
7356
  commentInput.value?.focus?.();
@@ -7196,12 +7359,12 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7196
7359
  };
7197
7360
  const comments = computed(() => {
7198
7361
  const parentComment = props.comment;
7199
- const allComments = commentsStore.commentsList;
7200
- return (parentComment.trackedChange ? collectTrackedChangeThread(parentComment, allComments) : allComments.filter((comment) => {
7362
+ if (parentComment.trackedChange) return commentsStore.getTrackedChangeThread(parentComment);
7363
+ return commentsStore.commentsList.filter((comment) => {
7201
7364
  const isThreadedComment = comment.parentCommentId === parentComment.commentId;
7202
7365
  const isThisComment = comment.commentId === parentComment.commentId;
7203
7366
  return isThreadedComment || isThisComment;
7204
- })).sort((a, b) => {
7367
+ }).sort((a, b) => {
7205
7368
  if (a.commentId === parentComment.commentId) return -1;
7206
7369
  if (b.commentId === parentComment.commentId) return 1;
7207
7370
  return a.createdTime - b.createdTime;
@@ -7379,20 +7542,33 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7379
7542
  };
7380
7543
  const handleAddComment = async () => {
7381
7544
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7545
+ if (isV2WriteDisabled.value) return {
7546
+ ok: false,
7547
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7548
+ };
7382
7549
  if (isV2Mode.value && v2CommentsAdapter.value && !isPendingNewComment.value) {
7383
- const outcome = await commentsStore.replyCommentV2({
7384
- superdoc: proxy.$superdoc,
7385
- parentCommentId: getV2ExistingReplyParentId(props.comment),
7386
- text: currentCommentText.value
7387
- });
7388
- if (!outcome?.ok) {
7550
+ if (isSubmittingReply.value) return {
7551
+ ok: false,
7552
+ reason: "reply-submit-in-flight"
7553
+ };
7554
+ isSubmittingReply.value = true;
7555
+ try {
7556
+ const outcome = await commentsStore.replyCommentV2({
7557
+ superdoc: proxy.$superdoc,
7558
+ parentCommentId: getV2ExistingReplyParentId(props.comment),
7559
+ text: currentCommentText.value
7560
+ });
7561
+ if (!outcome?.ok) {
7562
+ nextTick(() => emit("resize"));
7563
+ return outcome;
7564
+ }
7565
+ isReplying.value = false;
7566
+ currentCommentText.value = "";
7389
7567
  nextTick(() => emit("resize"));
7390
7568
  return outcome;
7569
+ } finally {
7570
+ isSubmittingReply.value = false;
7391
7571
  }
7392
- isReplying.value = false;
7393
- currentCommentText.value = "";
7394
- nextTick(() => emit("resize"));
7395
- return outcome;
7396
7572
  }
7397
7573
  const options = {
7398
7574
  documentId: props.comment.fileId,
@@ -7418,7 +7594,14 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7418
7594
  const v2TrackedChangesAdapter = computed(() => isV2Mode.value ? proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null : null);
7419
7595
  const v2WriteCapability = computed(() => {
7420
7596
  if (!v2CommentsAdapter.value) return null;
7421
- return v2CommentsAdapter.value.getCapabilityState?.() ?? null;
7597
+ const documentMode = commentsStore.viewingVisibility.documentMode;
7598
+ const capability = v2CommentsAdapter.value.getCapabilityState?.() ?? null;
7599
+ if (documentMode === "viewing") return {
7600
+ ...capability ?? {},
7601
+ canWrite: false,
7602
+ reason: "review-surface-read-only"
7603
+ };
7604
+ return capability;
7422
7605
  });
7423
7606
  const isV2WriteDisabled = computed(() => {
7424
7607
  if (!isV2Mode.value) return false;
@@ -7426,6 +7609,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7426
7609
  if (!cap) return false;
7427
7610
  return cap.canWrite === false;
7428
7611
  });
7612
+ const v2ReplyDisabledReason = computed(() => {
7613
+ if (isSubmittingReply.value) return "reply-submit-in-flight";
7614
+ if (!isV2WriteDisabled.value) return null;
7615
+ return v2WriteCapability.value?.reason ?? "v2-write-unavailable";
7616
+ });
7429
7617
  const v2TrackedChangeCapability = computed(() => {
7430
7618
  if (!v2TrackedChangesAdapter.value) return null;
7431
7619
  const documentMode = commentsStore.viewingVisibility.documentMode;
@@ -7468,6 +7656,35 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7468
7656
  if (!isV2WriteDisabled.value) return null;
7469
7657
  return v2WriteCapability.value?.reason ?? "v2-write-unavailable";
7470
7658
  });
7659
+ const reportTrackedChangeDecisionFailure = (decision, outcome) => {
7660
+ trackedChangeDecisionFailure.value = {
7661
+ decision,
7662
+ reason: outcome?.reason ?? null
7663
+ };
7664
+ nextTick(() => emit("resize"));
7665
+ };
7666
+ const clearTrackedChangeDecisionFailure = () => {
7667
+ if (!trackedChangeDecisionFailure.value) return;
7668
+ trackedChangeDecisionFailure.value = null;
7669
+ nextTick(() => emit("resize"));
7670
+ };
7671
+ const trackedChangeDecisionFailureMessage = computed(() => {
7672
+ const failure = trackedChangeDecisionFailure.value;
7673
+ if (!failure) return null;
7674
+ const action = failure.decision === "reject" ? "reject" : "accept";
7675
+ if (failure.reason === "stale-catalog") return `Couldn't ${action} this change because the review list is stale.`;
7676
+ return `Couldn't ${action} this change.`;
7677
+ });
7678
+ const retryTrackedChangeDecision = () => {
7679
+ const failure = trackedChangeDecisionFailure.value;
7680
+ if (!failure) return;
7681
+ trackedChangeDecisionFailure.value = null;
7682
+ if (failure.decision === "reject") {
7683
+ handleReject();
7684
+ return;
7685
+ }
7686
+ handleResolve();
7687
+ };
7471
7688
  const handleReject = async () => {
7472
7689
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7473
7690
  const customHandler = proxy.$superdoc.config.onTrackedChangeBubbleReject;
@@ -7478,7 +7695,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7478
7695
  comment: props.comment,
7479
7696
  decision: "reject"
7480
7697
  });
7481
- if (!outcome?.ok || outcome.success === false) return;
7698
+ if (!outcome?.ok || outcome.success === false) {
7699
+ reportTrackedChangeDecisionFailure("reject", outcome);
7700
+ return;
7701
+ }
7702
+ clearTrackedChangeDecisionFailure();
7482
7703
  if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
7483
7704
  nextTick(() => {
7484
7705
  commentsStore.lastUpdate = /* @__PURE__ */ new Date();
@@ -7534,7 +7755,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7534
7755
  comment: props.comment,
7535
7756
  decision: "accept"
7536
7757
  });
7537
- if (!outcome?.ok || outcome.success === false) return;
7758
+ if (!outcome?.ok || outcome.success === false) {
7759
+ reportTrackedChangeDecisionFailure("accept", outcome);
7760
+ return;
7761
+ }
7762
+ clearTrackedChangeDecisionFailure();
7538
7763
  if (typeof customHandler === "function") customHandler(props.comment, proxy.$superdoc.activeEditor);
7539
7764
  nextTick(() => {
7540
7765
  commentsStore.lastUpdate = /* @__PURE__ */ new Date();
@@ -7698,6 +7923,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7698
7923
  if (isPendingNewComment.value) cancelComment(proxy.$superdoc);
7699
7924
  nextTick(() => emit("resize"));
7700
7925
  }, { immediate: true });
7926
+ watch(isV2WriteDisabled, (isDisabled) => {
7927
+ if (!isDisabled) return;
7928
+ isReplying.value = false;
7929
+ nextTick(() => emit("resize"));
7930
+ });
7701
7931
  return (_ctx, _cache) => {
7702
7932
  const _directive_click_outside = resolveDirective("click-outside");
7703
7933
  return withDirectives((openBlock(), createElementBlock("div", {
@@ -7886,11 +8116,19 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7886
8116
  showSeparator.value(index) ? (openBlock(), createElementBlock("div", _hoisted_44)) : createCommentVNode("", true)
7887
8117
  ]);
7888
8118
  }), 128)),
7889
- showInputSection.value && !unref(getConfig).readOnly ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [!isReplying.value ? (openBlock(), createElementBlock("div", {
8119
+ trackedChangeDecisionFailure.value ? (openBlock(), createElementBlock("div", _hoisted_45, [createElementVNode("span", _hoisted_46, toDisplayString(trackedChangeDecisionFailureMessage.value), 1), createElementVNode("button", {
8120
+ type: "button",
8121
+ class: "sd-button tracked-change-decision-alert__retry",
8122
+ onClick: withModifiers(retryTrackedChangeDecision, ["stop", "prevent"])
8123
+ }, " Retry ")])) : createCommentVNode("", true),
8124
+ showInputSection.value && !unref(getConfig).readOnly ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [!isReplying.value ? (openBlock(), createElementBlock("button", {
7890
8125
  key: 0,
7891
- class: "reply-pill",
7892
- onClick: withModifiers(startReply, ["stop", "prevent"])
7893
- }, "Reply or add others with @")) : (openBlock(), createElementBlock("div", _hoisted_45, [createElementVNode("div", _hoisted_46, [createVNode(CommentInput_default, {
8126
+ type: "button",
8127
+ class: normalizeClass(["reply-pill", { "sd-is-disabled": isV2WriteDisabled.value }]),
8128
+ onClick: withModifiers(startReply, ["stop", "prevent"]),
8129
+ disabled: isV2WriteDisabled.value,
8130
+ "data-disabled-reason": isV2WriteDisabled.value ? v2WriteCapability.value?.reason ?? null : null
8131
+ }, " Reply or add others with @ ", 10, _hoisted_47)) : (openBlock(), createElementBlock("div", _hoisted_48, [createElementVNode("div", _hoisted_49, [createVNode(CommentInput_default, {
7894
8132
  ref_key: "commentInput",
7895
8133
  ref: commentInput,
7896
8134
  users: usersFiltered.value,
@@ -7901,19 +8139,19 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7901
8139
  "users",
7902
8140
  "config",
7903
8141
  "comment"
7904
- ])]), createElementVNode("div", _hoisted_47, [createElementVNode("button", {
8142
+ ])]), createElementVNode("div", _hoisted_50, [createElementVNode("button", {
7905
8143
  class: "sd-button reply-btn-cancel",
7906
8144
  onClick: withModifiers(handleCancel, ["stop", "prevent"])
7907
8145
  }, "Cancel"), createElementVNode("button", {
7908
- class: normalizeClass(["sd-button primary reply-btn-primary", { "sd-is-disabled": !hasTextContent.value || isV2WriteDisabled.value }]),
8146
+ class: normalizeClass(["sd-button primary reply-btn-primary", { "sd-is-disabled": !hasTextContent.value || isV2WriteDisabled.value || isSubmittingReply.value }]),
7909
8147
  onClick: withModifiers(handleAddComment, ["stop", "prevent"]),
7910
- disabled: !hasTextContent.value || isV2WriteDisabled.value,
7911
- "data-disabled-reason": isV2WriteDisabled.value ? v2WriteCapability.value?.reason ?? null : null
7912
- }, " Reply ", 10, _hoisted_48)])]))], 64)) : createCommentVNode("", true)
8148
+ disabled: !hasTextContent.value || isV2WriteDisabled.value || isSubmittingReply.value,
8149
+ "data-disabled-reason": v2ReplyDisabledReason.value
8150
+ }, " Reply ", 10, _hoisted_51)])]))], 64)) : createCommentVNode("", true)
7913
8151
  ], 64))], 14, _hoisted_1$25)), [[_directive_click_outside, handleClickOutside]]);
7914
8152
  };
7915
8153
  }
7916
- }, [["__scopeId", "data-v-67a03ee2"]]);
8154
+ }, [["__scopeId", "data-v-74340036"]]);
7917
8155
  var _hoisted_1$24 = { class: "comments-list" };
7918
8156
  var _hoisted_2$19 = { key: 0 };
7919
8157
  var _hoisted_3$15 = [
@@ -8256,12 +8494,12 @@ const resolvePersistentReviewCardTop = ({ comment, anchorTop, anchorBottom, card
8256
8494
  if ((Number.isFinite(anchorBottom) ? anchorBottom : anchorTop) < viewportTop) return Math.min(anchorTop, viewportTop - cardHeight - 1);
8257
8495
  return anchorTop;
8258
8496
  };
8259
- const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId, comment }) => {
8497
+ const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId }) => {
8260
8498
  if (!id) return false;
8261
8499
  if (id === "pending") return true;
8262
8500
  if (activeCommentInstanceId != null && String(id) === String(activeCommentInstanceId)) return true;
8263
8501
  if (visibleIds?.has?.(id)) return true;
8264
- return isPersistentReviewSidebarItem(comment);
8502
+ return false;
8265
8503
  };
8266
8504
  const resolveRemovedReviewCardContinuityTarget = ({ previousIds, currentIds, removedId }) => {
8267
8505
  if (!Array.isArray(previousIds) || !(currentIds instanceof Set) || !removedId) return null;
@@ -8342,6 +8580,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8342
8580
  const commentsRenderKey = ref(0);
8343
8581
  const sidebarOffsetY = ref(0);
8344
8582
  const disableInstantLayoutTransitions = ref(false);
8583
+ const viewportRevision = ref(0);
8345
8584
  const isPendingThread = (commentOrId) => {
8346
8585
  const pendingId = pendingComment.value?.commentId;
8347
8586
  if (!pendingId) return false;
@@ -8351,10 +8590,27 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8351
8590
  const getThreadId = (comment) => {
8352
8591
  return comment?.commentId ?? comment?.importedId ?? null;
8353
8592
  };
8593
+ const floatingInstanceIndex = computed(() => {
8594
+ const byId = /* @__PURE__ */ new Map();
8595
+ const byThreadId = /* @__PURE__ */ new Map();
8596
+ for (const instance of floatingCommentInstances.value) {
8597
+ if (instance?.id != null) byId.set(String(instance.id), instance);
8598
+ const threadId = getThreadId(instance?.comment);
8599
+ if (threadId == null) continue;
8600
+ const key = String(threadId);
8601
+ const threadInstances = byThreadId.get(key) ?? [];
8602
+ threadInstances.push(instance);
8603
+ byThreadId.set(key, threadInstances);
8604
+ }
8605
+ return {
8606
+ byId,
8607
+ byThreadId
8608
+ };
8609
+ });
8354
8610
  const resolveFloatingInstanceId = (threadId, preferredInstanceId = null) => {
8355
8611
  if (threadId == null) return null;
8356
8612
  const normalizedThreadId = String(threadId);
8357
- const matchingInstances = floatingCommentInstances.value.filter((instance) => String(getThreadId(instance.comment)) === normalizedThreadId);
8613
+ const matchingInstances = floatingInstanceIndex.value.byThreadId.get(normalizedThreadId) ?? [];
8358
8614
  if (!matchingInstances.length) return null;
8359
8615
  if (preferredInstanceId != null) {
8360
8616
  const preferredInstance = matchingInstances.find((instance) => String(instance.id) === String(preferredInstanceId));
@@ -8377,6 +8633,14 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8377
8633
  const placeholderRefs = ref({});
8378
8634
  let observer = null;
8379
8635
  const observedElements = /* @__PURE__ */ new Set();
8636
+ let viewportFrame = null;
8637
+ const refreshViewportWindow = () => {
8638
+ if (viewportFrame != null) return;
8639
+ viewportFrame = requestAnimationFrame(() => {
8640
+ viewportFrame = null;
8641
+ viewportRevision.value += 1;
8642
+ });
8643
+ };
8380
8644
  const getAnchorTop = (instance) => {
8381
8645
  if (props.currentDocument.type === "application/pdf") {
8382
8646
  const zoom = (activeZoom.value ?? 100) / 100;
@@ -8394,6 +8658,24 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8394
8658
  const bottom = instance?.positionEntry?.bounds?.bottom;
8395
8659
  return Number.isFinite(bottom) ? bottom : anchorTop;
8396
8660
  };
8661
+ const geometryBearingInstances = computed(() => {
8662
+ const positioned = [];
8663
+ for (const instance of floatingCommentInstances.value) {
8664
+ const id = instance?.id;
8665
+ const threadId = getThreadId(instance?.comment);
8666
+ const anchorTop = getAnchorTop(instance);
8667
+ if (id == null || threadId == null || !Number.isFinite(anchorTop)) continue;
8668
+ const top = normalizeFloatingAnchorTop(anchorTop, instance.comment);
8669
+ positioned.push({
8670
+ id,
8671
+ threadId,
8672
+ instance,
8673
+ top,
8674
+ anchorBottom: getAnchorBottom(instance, top)
8675
+ });
8676
+ }
8677
+ return positioned;
8678
+ });
8397
8679
  const getPendingAnchorTop = () => {
8398
8680
  const positionEntry = editorCommentPositions.value["pending"];
8399
8681
  if (typeof positionEntry?.bounds?.top === "number" && !isNaN(positionEntry.bounds.top)) return positionEntry.bounds.top;
@@ -8427,27 +8709,72 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8427
8709
  return resolveFloatingInstanceId(instantSidebarAlignmentThreadId.value, instantSidebarAlignmentInstanceId.value);
8428
8710
  });
8429
8711
  const allPositions = computed(() => {
8430
- const instances = floatingCommentInstances.value;
8712
+ viewportRevision.value;
8713
+ const positionedInstances = geometryBearingInstances.value;
8431
8714
  const hasPending = pendingComment.value && pendingComment.value.fileId === props.currentDocument.id;
8432
- if (!instances.length && !hasPending) return [];
8715
+ if (!positionedInstances.length && !floatingCommentInstances.value.length && !hasPending) return [];
8433
8716
  const positions = [];
8434
- for (const instance of instances) {
8435
- const key = instance?.id;
8436
- const anchorTop = getAnchorTop(instance);
8437
- const threadId = getThreadId(instance?.comment);
8438
- if (!key || !threadId || typeof anchorTop !== "number" || isNaN(anchorTop)) continue;
8439
- const top = normalizeFloatingAnchorTop(anchorTop, instance.comment);
8717
+ const viewportRange = getFloatingViewportRange() ?? {
8718
+ top: 0,
8719
+ bottom: Math.max(0, props.parent?.clientHeight || window.innerHeight || 0)
8720
+ };
8721
+ const renderTop = viewportRange.top - OBSERVER_MARGIN;
8722
+ const renderBottom = viewportRange.bottom + OBSERVER_MARGIN;
8723
+ const activeKey = hasPending ? "pending" : activeCommentInstanceId.value;
8724
+ const positionIds = /* @__PURE__ */ new Set();
8725
+ const pushPosition = ({ id, threadId, instance, top, anchorBottom, hasAnchorGeometry }) => {
8726
+ const key = String(id);
8727
+ if (positionIds.has(key)) return;
8728
+ positionIds.add(key);
8440
8729
  positions.push({
8441
- id: key,
8730
+ id,
8442
8731
  threadId,
8443
8732
  pageIndex: instance?.pageIndex ?? null,
8444
8733
  anchorTop: top,
8445
- anchorBottom: getAnchorBottom(instance, top),
8734
+ anchorBottom,
8735
+ hasAnchorGeometry,
8446
8736
  top,
8447
- height: measuredHeights.value[key] || ESTIMATED_HEIGHT,
8737
+ height: measuredHeights.value[id] || ESTIMATED_HEIGHT,
8448
8738
  commentRef: instance.comment,
8449
8739
  instanceRef: instance
8450
8740
  });
8741
+ };
8742
+ for (const positioned of positionedInstances) {
8743
+ const { id, threadId, instance, top, anchorBottom } = positioned;
8744
+ const isActive = activeKey != null && String(id) === String(activeKey);
8745
+ const isEditing = editingCommentId.value != null && String(threadId) === String(editingCommentId.value);
8746
+ if (!isActive && !isEditing && (anchorBottom < renderTop || top > renderBottom)) continue;
8747
+ pushPosition({
8748
+ id,
8749
+ threadId,
8750
+ instance,
8751
+ top,
8752
+ anchorBottom,
8753
+ hasAnchorGeometry: true
8754
+ });
8755
+ }
8756
+ const transitionalInstances = [];
8757
+ if (activeKey && activeKey !== "pending") {
8758
+ const activeInstance = floatingInstanceIndex.value.byId.get(String(activeKey));
8759
+ if (activeInstance) transitionalInstances.push(activeInstance);
8760
+ }
8761
+ if (editingCommentId.value != null) {
8762
+ const editingInstances = floatingInstanceIndex.value.byThreadId.get(String(editingCommentId.value)) ?? [];
8763
+ const editingInstance = editingInstances.find((instance) => instance.isPrimary) ?? editingInstances[0];
8764
+ if (editingInstance) transitionalInstances.push(editingInstance);
8765
+ }
8766
+ for (const instance of transitionalInstances) {
8767
+ const id = instance.id;
8768
+ const threadId = getThreadId(instance.comment);
8769
+ if (id == null || threadId == null || positionIds.has(String(id))) continue;
8770
+ pushPosition({
8771
+ id,
8772
+ threadId,
8773
+ instance,
8774
+ top: viewportRange.top,
8775
+ anchorBottom: viewportRange.top,
8776
+ hasAnchorGeometry: false
8777
+ });
8451
8778
  }
8452
8779
  if (hasPending) {
8453
8780
  const pendingTop = getPendingAnchorTop();
@@ -8455,17 +8782,17 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8455
8782
  id: "pending",
8456
8783
  anchorTop: pendingTop,
8457
8784
  anchorBottom: pendingTop,
8785
+ hasAnchorGeometry: true,
8458
8786
  top: pendingTop,
8459
8787
  height: measuredHeights.value["pending"] || ESTIMATED_HEIGHT,
8460
8788
  commentRef: pendingComment.value
8461
8789
  });
8462
8790
  }
8463
8791
  positions.sort((a, b) => a.anchorTop - b.anchorTop);
8464
- const activeKey = hasPending ? "pending" : activeCommentInstanceId.value;
8465
8792
  resolveCollisions(positions, activeKey ? positions.findIndex((p) => p.id === activeKey) : -1, 15);
8466
- const viewportRange = getFloatingViewportRange();
8467
- if (viewportRange) for (const position of positions) {
8468
- const persistentReviewTop = resolvePersistentReviewCardTop({
8793
+ for (const position of positions) {
8794
+ if (!position.hasAnchorGeometry) continue;
8795
+ const offscreenTop = resolvePersistentReviewCardTop({
8469
8796
  comment: position.commentRef,
8470
8797
  anchorTop: position.anchorTop,
8471
8798
  anchorBottom: position.anchorBottom,
@@ -8473,21 +8800,13 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8473
8800
  viewportTop: viewportRange.top,
8474
8801
  viewportBottom: viewportRange.bottom
8475
8802
  });
8476
- if (persistentReviewTop != null) position.top = persistentReviewTop;
8803
+ if (offscreenTop != null) position.top = offscreenTop;
8477
8804
  }
8478
8805
  return positions;
8479
8806
  });
8480
- const totalHeight = computed(() => {
8481
- if (!allPositions.value.length) return 0;
8482
- let max = 0;
8483
- for (const p of allPositions.value) {
8484
- const bottom = p.top + p.height;
8485
- if (bottom > max) max = bottom;
8486
- }
8487
- return max + 50;
8488
- });
8489
8807
  const wrapperMinHeight = computed(() => {
8490
- return totalHeight.value + Math.max(0, sidebarOffsetY.value);
8808
+ viewportRevision.value;
8809
+ return Math.max(0, props.parent?.clientHeight || 0, props.parent?.scrollHeight || 0);
8491
8810
  });
8492
8811
  const setupObserver = () => {
8493
8812
  if (observer) observer.disconnect();
@@ -8594,6 +8913,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8594
8913
  let continuityAlignmentScheduled = false;
8595
8914
  let continuityAlignmentGeneration = 0;
8596
8915
  let continuityTransitionFrame = null;
8916
+ let activeLayoutContinuityFrame = null;
8597
8917
  const clearDeferredRemeasureTimers = () => {
8598
8918
  remeasureTimers.forEach(clearTimeout);
8599
8919
  remeasureTimers = [];
@@ -8765,14 +9085,27 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8765
9085
  });
8766
9086
  let prevPositionIds = /* @__PURE__ */ new Set();
8767
9087
  let prevActiveAnchorTop = null;
9088
+ let prevActiveLayoutTop = null;
8768
9089
  watch(allPositions, (positions) => {
8769
9090
  const activePosition = activeCommentInstanceId.value ? positions.find((position) => position.id === activeCommentInstanceId.value) : null;
8770
9091
  const activeAnchorTop = typeof activePosition?.anchorTop === "number" && Number.isFinite(activePosition.anchorTop) ? activePosition.anchorTop : null;
8771
- if (activeAnchorTop != null && prevActiveAnchorTop != null && Math.abs(activeAnchorTop - prevActiveAnchorTop) > 1 && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value)) {
9092
+ const activeAnchorMoved = activeAnchorTop != null && prevActiveAnchorTop != null && Math.abs(activeAnchorTop - prevActiveAnchorTop) > 1;
9093
+ const activeLayoutTop = typeof activePosition?.top === "number" && Number.isFinite(activePosition.top) ? activePosition.top : null;
9094
+ const activeLayoutMoved = activeLayoutTop != null && prevActiveLayoutTop != null && Math.abs(activeLayoutTop - prevActiveLayoutTop) > 1;
9095
+ if (activeAnchorMoved && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value)) {
8772
9096
  sidebarOffsetY.value = 0;
8773
9097
  setInstantLayoutTransitionsDisabled(false);
9098
+ } else if (activeLayoutMoved && sidebarOffsetY.value !== 0 && !Number.isFinite(instantSidebarAlignmentTargetY.value) && !sidebarContinuityAnchor) {
9099
+ setInstantLayoutTransitionsDisabled(true);
9100
+ sidebarOffsetY.value += prevActiveLayoutTop - activeLayoutTop;
9101
+ if (activeLayoutContinuityFrame != null) cancelAnimationFrame(activeLayoutContinuityFrame);
9102
+ activeLayoutContinuityFrame = requestAnimationFrame(() => {
9103
+ activeLayoutContinuityFrame = null;
9104
+ if (!sidebarContinuityAnchor && !Number.isFinite(instantSidebarAlignmentTargetY.value)) setInstantLayoutTransitionsDisabled(false);
9105
+ });
8774
9106
  }
8775
9107
  prevActiveAnchorTop = activeAnchorTop;
9108
+ prevActiveLayoutTop = activeLayoutTop;
8776
9109
  const positionOrder = positions.map((position) => position.id);
8777
9110
  const currentIds = new Set(positionOrder);
8778
9111
  if (sidebarContinuityAnchor && !currentIds.has(sidebarContinuityAnchor.key)) {
@@ -8831,12 +9164,24 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8831
9164
  });
8832
9165
  onMounted(() => {
8833
9166
  setupObserver();
9167
+ props.parent?.addEventListener?.("scroll", refreshViewportWindow, { passive: true });
9168
+ window.addEventListener("resize", refreshViewportWindow, { passive: true });
8834
9169
  nextTick(observePlaceholders);
8835
9170
  });
8836
9171
  onBeforeUnmount(() => {
8837
9172
  clearDeferredRemeasureTimers();
8838
9173
  if (scrollTimer) clearTimeout(scrollTimer);
8839
9174
  clearSidebarContinuityAnchor();
9175
+ if (activeLayoutContinuityFrame != null) {
9176
+ cancelAnimationFrame(activeLayoutContinuityFrame);
9177
+ activeLayoutContinuityFrame = null;
9178
+ }
9179
+ props.parent?.removeEventListener?.("scroll", refreshViewportWindow);
9180
+ window.removeEventListener("resize", refreshViewportWindow);
9181
+ if (viewportFrame != null) {
9182
+ cancelAnimationFrame(viewportFrame);
9183
+ viewportFrame = null;
9184
+ }
8840
9185
  if (observer) {
8841
9186
  observer.disconnect();
8842
9187
  observer = null;
@@ -8897,7 +9242,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8897
9242
  }), 128))], 4)], 4);
8898
9243
  };
8899
9244
  }
8900
- }, [["__scopeId", "data-v-7bf85652"]]);
9245
+ }, [["__scopeId", "data-v-e699ceb8"]]);
8901
9246
  var _hoisted_1$22 = {
8902
9247
  class: "superdoc__pdf-comments-layer pdf-comments-layer",
8903
9248
  "aria-hidden": "false"
@@ -9421,6 +9766,17 @@ const DRAWING_DIAGNOSTIC_CODES = {
9421
9766
  };
9422
9767
  DRAWING_DIAGNOSTIC_CODES.missingRelationship, DRAWING_DIAGNOSTIC_CODES.unsupportedRelationshipType, DRAWING_DIAGNOSTIC_CODES.unsupportedRelationshipType, DRAWING_DIAGNOSTIC_CODES.imageTooLarge, DRAWING_DIAGNOSTIC_CODES.missingMediaPart, DRAWING_DIAGNOSTIC_CODES.unsupportedObject, DRAWING_DIAGNOSTIC_CODES.vmlUnsupported;
9423
9768
  DRAWING_DIAGNOSTIC_CODES.externalImageDeferred, DRAWING_DIAGNOSTIC_CODES.missingRelationship, DRAWING_DIAGNOSTIC_CODES.missingMediaPart, DRAWING_DIAGNOSTIC_CODES.unsupportedRelationshipType, DRAWING_DIAGNOSTIC_CODES.unsupportedMime, DRAWING_DIAGNOSTIC_CODES.imageTooLarge, DRAWING_DIAGNOSTIC_CODES.unsafeSvg, DRAWING_DIAGNOSTIC_CODES.unsupportedFormat, DRAWING_DIAGNOSTIC_CODES.embeddedObjectNotSupported, DRAWING_DIAGNOSTIC_CODES.unsupportedObject, DRAWING_DIAGNOSTIC_CODES.vmlUnsupported, DRAWING_DIAGNOSTIC_CODES.vmlImageUnsupported, DRAWING_DIAGNOSTIC_CODES.unsupportedGeometryCommand, DRAWING_DIAGNOSTIC_CODES.anchorUnsupported, DRAWING_DIAGNOSTIC_CODES.wrapUnsupported, DRAWING_DIAGNOSTIC_CODES.altContentNoSupportedChoice, DRAWING_DIAGNOSTIC_CODES.groupChildUnsupported;
9769
+ Object.freeze([
9770
+ "multiple-sections",
9771
+ "furniture-page-tokens",
9772
+ "non-balanceable-multi-column-sections",
9773
+ "body-anchored-objects",
9774
+ "footnotes",
9775
+ "page-references",
9776
+ "keep-constraints",
9777
+ "tables",
9778
+ "furniture-anchored-objects"
9779
+ ]);
9424
9780
  var _hoisted_1$20 = { class: "superdoc-html-viewer" };
9425
9781
  var _hoisted_2$17 = { class: "superdoc-html-viewer__document" };
9426
9782
  var _hoisted_3$14 = ["innerHTML"];
@@ -11257,8 +11613,11 @@ function scrollToElement(targetElement, options = {
11257
11613
  const container = getScrollableParent(targetElement);
11258
11614
  const containerRect = container.getBoundingClientRect();
11259
11615
  const offsetTop = targetElement.getBoundingClientRect().top - containerRect.top + container.scrollTop;
11616
+ const scrollPaddingTopValue = window.getComputedStyle(container).scrollPaddingTop?.trim();
11617
+ const resolvedScrollPaddingTop = scrollPaddingTopValue?.endsWith("px") ? Number(scrollPaddingTopValue.slice(0, -2)) : 0;
11618
+ const scrollPaddingTop = Number.isFinite(resolvedScrollPaddingTop) && resolvedScrollPaddingTop >= 0 ? resolvedScrollPaddingTop : 0;
11260
11619
  container.scrollTo({
11261
- top: options.block === "start" ? offsetTop : offsetTop - container.clientHeight + targetElement.offsetHeight,
11620
+ top: options.block === "start" ? offsetTop - scrollPaddingTop : offsetTop - container.clientHeight + targetElement.offsetHeight,
11262
11621
  behavior: options.behavior
11263
11622
  });
11264
11623
  }
@@ -11504,7 +11863,34 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11504
11863
  tick();
11505
11864
  });
11506
11865
  }
11507
- async function navigateToAnchor(anchorUrl) {
11866
+ async function placeCaretAtHeadingStart(blockId) {
11867
+ const setSelectionTarget = getActiveEditor()?.authoring?.setSelectionTarget;
11868
+ if (typeof setSelectionTarget !== "function") return;
11869
+ const story = {
11870
+ kind: "story",
11871
+ storyType: "body"
11872
+ };
11873
+ const anchor = {
11874
+ kind: "text",
11875
+ blockId,
11876
+ offset: 0,
11877
+ story
11878
+ };
11879
+ const target = {
11880
+ kind: "selection",
11881
+ start: anchor,
11882
+ end: anchor,
11883
+ story
11884
+ };
11885
+ try {
11886
+ await Promise.resolve(setSelectionTarget({
11887
+ target,
11888
+ collapse: "start",
11889
+ focus: true
11890
+ }));
11891
+ } catch {}
11892
+ }
11893
+ async function navigateToAnchor(anchorUrl, { placeCaret = false } = {}) {
11508
11894
  if (typeof anchorUrl !== "string" || !anchorUrl.startsWith("#")) return;
11509
11895
  const anchorName = anchorUrl.slice(1);
11510
11896
  const escapedAnchorName = cssEscape(anchorName);
@@ -11529,6 +11915,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11529
11915
  if (story && story.storyType !== "body") return;
11530
11916
  const blockId = bookmark?.range?.from?.blockId;
11531
11917
  if (typeof blockId !== "string") return;
11918
+ if (placeCaret) await placeCaretAtHeadingStart(blockId);
11532
11919
  const mountedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
11533
11920
  if (mountedParagraph) {
11534
11921
  scrollToElement(mountedParagraph);
@@ -11638,7 +12025,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11638
12025
  function openDefaultPopover(ctx) {
11639
12026
  closeCurrentPopover("replace");
11640
12027
  if (ctx.isAnchorLink && isTocEntryLinkElement(ctx.element)) {
11641
- navigateToAnchor(ctx.href);
12028
+ navigateToAnchor(ctx.href, { placeCaret: ctx.documentMode !== "viewing" });
11642
12029
  return;
11643
12030
  }
11644
12031
  if (ctx.documentMode === "viewing") {
@@ -11654,7 +12041,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11654
12041
  const closePopover = () => closeCurrentPopover("closed");
11655
12042
  const goToAnchor = (anchorUrl) => {
11656
12043
  closePopover();
11657
- navigateToAnchor(anchorUrl);
12044
+ navigateToAnchor(anchorUrl, { placeCaret: true });
11658
12045
  };
11659
12046
  const generation = openGeneration;
11660
12047
  const openWithHyperlinkTarget = (hyperlinkTarget) => {
@@ -13413,7 +13800,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13413
13800
  __name: "SuperDoc",
13414
13801
  emits: ["selection-update"],
13415
13802
  setup(__props, { emit: __emit }) {
13416
- const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-XZg0nr8y.es.js"));
13803
+ const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-CNOtyS-I.es.js"));
13417
13804
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
13418
13805
  const resolveDocumentV2Collaboration = (doc) => {
13419
13806
  const rawConfig = doc?.v2Collaboration;
@@ -13458,7 +13845,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13458
13845
  });
13459
13846
  }
13460
13847
  });
13461
- const { getConfig, documentsWithConverations, commentsList, pendingComment, activeComment, skipSelectionUpdate, commentsByDocument, isCommentsListVisible, isFloatingCommentsReady, generalCommentIds, hasSyncedCollaborationComments, editorCommentPositions, hasInitializedLocations, isCommentHighlighted } = storeToRefs(commentsStore);
13848
+ const { getConfig, documentsWithConverations, commentsList, pendingComment, activeComment, skipSelectionUpdate, commentsByDocument, isCommentsListVisible, isFloatingCommentsReady, generalCommentIds, hasSyncedCollaborationComments, editorCommentPositions, hasInitializedLocations, isCommentHighlighted, hasOpenTrackedChanges } = storeToRefs(commentsStore);
13462
13849
  const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate, refreshTrackedChangeCommentsByIds, syncTrackedChangePositionsWithDocument, syncTrackedChangeComments, addComment, addHydratedComment, getComment, resolveCommentPositionEntry, belongsToDocument, COMMENT_EVENTS, requestInstantSidebarAlignment, peekInstantSidebarAlignment, clearInstantSidebarAlignment } = commentsStore;
13463
13850
  const { proxy } = getCurrentInstance();
13464
13851
  commentsStore.proxy = proxy;
@@ -13638,18 +14025,19 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13638
14025
  if (!documentId) return;
13639
14026
  clearV2CommandShortcutBinding(documentId);
13640
14027
  if (typeof bindEditShortcuts !== "function") return;
13641
- const ui = createSuperDocUI({ superdoc: proxy.$superdoc });
14028
+ const fallbackUi = createSuperDocUI({ superdoc: proxy.$superdoc });
14029
+ const resolveUi = () => proxy.$superdoc?.toolbar?.ui ?? fallbackUi;
13642
14030
  const openLinkWorkflow = resolveV2LinkWorkflowOpener();
13643
14031
  const unbind = bindEditShortcuts({
13644
14032
  commandRoute: {
13645
- executeAsync: (commandId, payload) => ui.commands.executeAsync(commandId, payload),
13646
- getState: (commandId) => ui.commands.get(commandId).getState()
14033
+ executeAsync: (commandId, payload) => resolveUi().commands.executeAsync(commandId, payload),
14034
+ getState: (commandId) => resolveUi().commands.get(commandId).getState()
13647
14035
  },
13648
14036
  ...openLinkWorkflow ? { openLinkWorkflow } : {}
13649
14037
  });
13650
14038
  v2CommandShortcutBindings.set(documentId, {
13651
14039
  unbind,
13652
- ui
14040
+ ui: fallbackUi
13653
14041
  });
13654
14042
  };
13655
14043
  const clearV2SessionShortcutBinding = (documentId) => {
@@ -14473,13 +14861,17 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
14473
14861
  v2RulerReady.value = false;
14474
14862
  };
14475
14863
  let v2TypingReviewHydrationTimer = 0;
14476
- const hydrateV2ReviewRowsFromHost = (options = {}) => {
14864
+ const hydrateV2CommentRowsFromHost = () => {
14477
14865
  const commentsAdapter = proxy.$superdoc?.activeEditor?.v2Comments ?? null;
14478
- if (commentsAdapter) commentsStore.hydrateCommentsFromV2?.({
14866
+ if (!commentsAdapter) return;
14867
+ commentsStore.hydrateCommentsFromV2?.({
14479
14868
  superdoc: proxy.$superdoc,
14480
14869
  adapter: commentsAdapter,
14481
14870
  documentId: proxy.$superdoc?.activeEditor?.documentId ?? null
14482
14871
  });
14872
+ };
14873
+ const hydrateV2ReviewRowsFromHost = (options = {}) => {
14874
+ hydrateV2CommentRowsFromHost();
14483
14875
  const trackedChangesAdapter = proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null;
14484
14876
  if (!trackedChangesAdapter) return;
14485
14877
  commentsStore.hydrateTrackedChangesFromV2?.({
@@ -14582,6 +14974,10 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
14582
14974
  const receipt = event.origin === "history" ? null : event.receipt;
14583
14975
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
14584
14976
  v2ReviewMutationReconciler.enqueueAfterPaint(reviewImpact, canWaitForExactPaint ? () => readiness.whenPainted.call(readiness, receipt) : null);
14977
+ if (reviewImpact?.reconcileMode === "authoritative" && reviewImpact.upsertIds.size > 0) {
14978
+ hydrateV2CommentRowsFromHost();
14979
+ return;
14980
+ }
14585
14981
  if (isV2EditableTextMutationEvent(event)) {
14586
14982
  scheduleV2TypingReviewHydration();
14587
14983
  return;
@@ -15043,7 +15439,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15043
15439
  if (isV2Mode.value && !v2GeometryAvailable.value && !hasPdfFloatingComments.value) return false;
15044
15440
  if (!shouldRenderCommentsInViewing.value) return false;
15045
15441
  if (!shouldUseSidebarComments.value) return false;
15046
- return pendingComment.value || floatingComments.value.length > 0 && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
15442
+ return pendingComment.value || (floatingComments.value.length > 0 || hasOpenTrackedChanges.value) && isReady.value && layers.value && isCommentsEnabled.value && !isCommentsListVisible.value;
15047
15443
  });
15048
15444
  const activeCompactComment = computed(() => {
15049
15445
  if (showCommentsSidebar.value) return null;
@@ -15711,7 +16107,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15711
16107
  ], 38);
15712
16108
  };
15713
16109
  }
15714
- }, [["__scopeId", "data-v-c4ab995e"]]);
16110
+ }, [["__scopeId", "data-v-4084bf1b"]]);
15715
16111
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
15716
16112
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
15717
16113
  var piniaDevtoolsSuppressionState = {
@@ -34257,7 +34653,7 @@ var SuperDoc = class extends import_eventemitter3.default {
34257
34653
  this.config.colors = shuffleArray(this.config.colors);
34258
34654
  this.userColorMap = /* @__PURE__ */ new Map();
34259
34655
  this.colorIndex = 0;
34260
- this.version = "2.0.0-next.27";
34656
+ this.version = "2.0.0-next.29";
34261
34657
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
34262
34658
  this.superdocId = config.superdocId || v4_default();
34263
34659
  this.colors = this.config.colors ?? [];