superdoc 2.0.0-next.28 → 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.
@@ -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-BlnUCtrx.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) {
@@ -2769,6 +2800,79 @@ const trackedChangeThreadParentIdForComment = (comment) => {
2769
2800
  if (comment?.trackedChangeParentId != null && hasLegacyTrackedChangeThreadSignal(comment)) return comment.trackedChangeParentId;
2770
2801
  return null;
2771
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
+ };
2772
2876
  var Editor = class {
2773
2877
  constructor() {
2774
2878
  throw new Error("SuperDoc v2 beta: v1 editor-backed comment import paths are not available.");
@@ -2929,6 +3033,22 @@ const useCommentsStore = defineStore("comments", () => {
2929
3033
  const suppressInternalExternal = ref(true);
2930
3034
  const currentCommentText = ref("");
2931
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
+ });
2932
3052
  const isCommentsListVisible = ref(false);
2933
3053
  const editorCommentIds = ref([]);
2934
3054
  const editorCommentPositions = ref({});
@@ -3715,6 +3835,7 @@ const useCommentsStore = defineStore("comments", () => {
3715
3835
  };
3716
3836
  };
3717
3837
  const getGroupedComments = computed(() => buildGroupedComments(compareByCreatedTime));
3838
+ const hasOpenTrackedChanges = computed(() => getGroupedComments.value?.parentComments.some((comment) => comment.trackedChange && !comment.resolvedTime) ?? false);
3718
3839
  const getCommentsByPosition = computed(() => buildGroupedComments(compareByPosition));
3719
3840
  const hasOverlapId = (id) => overlappedIds.includes(id);
3720
3841
  const documentsWithConverations = computed(() => {
@@ -4900,7 +5021,7 @@ const useCommentsStore = defineStore("comments", () => {
4900
5021
  superdoc
4901
5022
  };
4902
5023
  let resolvedCount = 0;
4903
- commentsList.value.forEach((linkedComment) => {
5024
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4904
5025
  if (!linkedComment || linkedComment === comment) return;
4905
5026
  if (linkedComment.resolvedTime) return;
4906
5027
  if (![
@@ -4928,6 +5049,16 @@ const useCommentsStore = defineStore("comments", () => {
4928
5049
  }
4929
5050
  return trackedChangeIds;
4930
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
+ };
4931
5062
  const collectTrackedChangeEntityIdsFromReceipt = (receipt) => {
4932
5063
  const record = receipt && typeof receipt === "object" ? receipt : null;
4933
5064
  if (!record?.success) return /* @__PURE__ */ new Set();
@@ -4959,7 +5090,7 @@ const useCommentsStore = defineStore("comments", () => {
4959
5090
  const trackedChangeIds = collectTrackedChangeDecisionIds(comment);
4960
5091
  if (!trackedChangeIds.size) return 0;
4961
5092
  let detachedCount = 0;
4962
- commentsList.value.forEach((linkedComment) => {
5093
+ getTrackedChangeDecisionLinks(comment).forEach((linkedComment) => {
4963
5094
  if (!linkedComment || linkedComment === comment) return;
4964
5095
  const trackedChangeParentId = normalizeCommentId(linkedComment.trackedChangeParentId);
4965
5096
  const trackedChangeThreadParentId = normalizeCommentId(linkedComment.trackedChangeThreadParentId);
@@ -4975,7 +5106,7 @@ const useCommentsStore = defineStore("comments", () => {
4975
5106
  });
4976
5107
  return detachedCount;
4977
5108
  };
4978
- const pruneDecidedTrackedChangeRow = ({ superdoc, decidedId, decidedIds, documentId } = {}) => {
5109
+ const pruneDecidedTrackedChangeRow = ({ superdoc, comment: targetComment, decidedId, decidedIds, documentId } = {}) => {
4979
5110
  const normalizedDecidedIds = new Set((Array.isArray(decidedIds) || decidedIds instanceof Set ? Array.from(decidedIds) : [decidedId]).map((id) => normalizeCommentId(id)).filter(Boolean));
4980
5111
  const normalizedDocumentId = normalizeCommentId(documentId);
4981
5112
  if (!normalizedDecidedIds.size || !normalizedDocumentId) return {
@@ -4984,7 +5115,13 @@ const useCommentsStore = defineStore("comments", () => {
4984
5115
  };
4985
5116
  const removedIds = /* @__PURE__ */ new Set();
4986
5117
  let removed = false;
4987
- 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) => {
4988
5125
  if (!comment?.trackedChange) return true;
4989
5126
  if (!belongsToTrackedChangeSyncDocument(comment, normalizedDocumentId)) return true;
4990
5127
  const ids = collectTrackedChangeDecisionIds(comment);
@@ -5020,6 +5157,25 @@ const useCommentsStore = defineStore("comments", () => {
5020
5157
  adapter.clearActiveTrackedChangeTargetIfMatches(decidedId);
5021
5158
  } catch {}
5022
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
+ };
5023
5179
  const tracePostDecisionBackgroundReconcile = (superdoc, event, payload = {}) => {
5024
5180
  const record = {
5025
5181
  event,
@@ -5171,7 +5327,7 @@ const useCommentsStore = defineStore("comments", () => {
5171
5327
  adapter: v2Adapter,
5172
5328
  documentId: effectiveDocumentId,
5173
5329
  items: listResult.items ?? [],
5174
- pruneStale: listResult.complete !== false && listResult.sourceCoverageComplete !== false
5330
+ pruneStale: listResult.complete === true && listResult.sourceCoverageComplete === true
5175
5331
  });
5176
5332
  const liveIds = new Set((listResult.items ?? []).map((item) => item && item.id != null ? String(item.id) : null).filter(Boolean));
5177
5333
  if (!(decidedId != null && liveIds.has(decidedId))) {
@@ -5215,6 +5371,17 @@ const useCommentsStore = defineStore("comments", () => {
5215
5371
  };
5216
5372
  };
5217
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
+ });
5218
5385
  resolveLinkedCommentsForTrackedChangeDecision({
5219
5386
  superdoc,
5220
5387
  comment,
@@ -5226,17 +5393,11 @@ const useCommentsStore = defineStore("comments", () => {
5226
5393
  });
5227
5394
  pruneDecidedTrackedChangeRow({
5228
5395
  superdoc,
5396
+ comment,
5229
5397
  decidedIds: reliableDecidedIds,
5230
5398
  documentId: effectiveDocumentId
5231
5399
  });
5232
5400
  if (wasActiveBeforeDecide) clearActiveCommentSelection();
5233
- reliableDecidedIds.forEach((id$1) => clearV2ActiveTrackedChangeTarget(v2Adapter, id$1));
5234
- schedulePostDecisionBackgroundReconcile({
5235
- superdoc,
5236
- trackedChangesAdapter: v2Adapter,
5237
- commentsAdapter: getV2CommentsAdapter(superdoc),
5238
- documentId: effectiveDocumentId
5239
- });
5240
5401
  return {
5241
5402
  ok: true,
5242
5403
  success: true
@@ -5305,7 +5466,7 @@ const useCommentsStore = defineStore("comments", () => {
5305
5466
  reason: "adapter-stale"
5306
5467
  };
5307
5468
  if (!result?.ok) return result;
5308
- 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;
5309
5470
  reconcileTrackedChangesFromV2({
5310
5471
  superdoc,
5311
5472
  adapter: effectiveAdapter,
@@ -5342,12 +5503,12 @@ const useCommentsStore = defineStore("comments", () => {
5342
5503
  };
5343
5504
  const effectiveDocumentId = documentId ?? effectiveAdapter.documentId ?? superdoc?.activeEditor?.documentId ?? null;
5344
5505
  const normalizedRemovedIds = new Set(Array.from(removedIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean));
5345
- 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({
5346
5508
  superdoc,
5347
5509
  decidedIds: normalizedRemovedIds,
5348
5510
  documentId: effectiveDocumentId
5349
5511
  });
5350
- const normalizedUpsertIds = [...new Set(Array.from(upsertIds ?? []).map((id) => normalizeCommentId(id)).filter(Boolean))];
5351
5512
  if (normalizedUpsertIds.length === 0) return {
5352
5513
  ok: true,
5353
5514
  items: [],
@@ -5951,10 +6112,12 @@ const useCommentsStore = defineStore("comments", () => {
5951
6112
  getConfig,
5952
6113
  documentsWithConverations,
5953
6114
  getGroupedComments,
6115
+ hasOpenTrackedChanges,
5954
6116
  getCommentsByPosition,
5955
6117
  getFloatingComments,
5956
6118
  getFloatingCommentInstances,
5957
6119
  getCommentAliasIds,
6120
+ getTrackedChangeThread,
5958
6121
  getCommentPositionKey,
5959
6122
  getCommentPosition,
5960
6123
  getCommentAnchoredText,
@@ -6862,30 +7025,6 @@ var CommentInput_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
6862
7025
  };
6863
7026
  }
6864
7027
  }, [["__scopeId", "data-v-cc54b489"]]);
6865
- const collectTrackedChangeThread = (parentComment, allComments) => {
6866
- const trackedChangeId = parentComment.commentId;
6867
- const threadIds = new Set([trackedChangeId]);
6868
- const queue = [];
6869
- allComments.forEach((comment) => {
6870
- if (comment.commentId === trackedChangeId) return;
6871
- const isDirectChild = [comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(trackedChangeId);
6872
- const isTrackedChangeThreadRoot = String(trackedChangeThreadParentIdForComment(comment) ?? "") === trackedChangeId && !comment.parentCommentId;
6873
- if (isDirectChild || isTrackedChangeThreadRoot) {
6874
- threadIds.add(comment.commentId);
6875
- queue.push(comment.commentId);
6876
- }
6877
- });
6878
- for (let i = 0; i < queue.length; i += 1) {
6879
- const parentId = queue[i];
6880
- allComments.forEach((comment) => {
6881
- if ([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id)).includes(parentId) && !threadIds.has(comment.commentId)) {
6882
- threadIds.add(comment.commentId);
6883
- queue.push(comment.commentId);
6884
- }
6885
- });
6886
- }
6887
- return allComments.filter((comment) => threadIds.has(comment.commentId));
6888
- };
6889
7028
  var _hoisted_1$25 = [
6890
7029
  "data-comment-instance-id",
6891
7030
  "data-comment-thread-id",
@@ -6978,13 +7117,14 @@ var _hoisted_45 = {
6978
7117
  role: "alert"
6979
7118
  };
6980
7119
  var _hoisted_46 = { class: "tracked-change-decision-alert__message" };
6981
- var _hoisted_47 = {
7120
+ var _hoisted_47 = ["disabled", "data-disabled-reason"];
7121
+ var _hoisted_48 = {
6982
7122
  key: 1,
6983
7123
  class: "reply-expanded"
6984
7124
  };
6985
- var _hoisted_48 = { class: "reply-input-wrapper" };
6986
- var _hoisted_49 = { class: "reply-actions" };
6987
- var _hoisted_50 = ["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"];
6988
7128
  var CLICK_OUTSIDE_HIT_TOLERANCE_PX = 3;
6989
7129
  var TRACKED_CHANGE_PREVIEW_MAX_PX = 90;
6990
7130
  var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
@@ -7204,8 +7344,13 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7204
7344
  });
7205
7345
  const commentsAreReadOnly$1 = () => getConfig.value?.readOnly === true;
7206
7346
  const isReplying = ref(false);
7347
+ const isSubmittingReply = ref(false);
7207
7348
  const startReply = () => {
7208
7349
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7350
+ if (isV2WriteDisabled.value) return {
7351
+ ok: false,
7352
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7353
+ };
7209
7354
  isReplying.value = true;
7210
7355
  nextTick(() => {
7211
7356
  commentInput.value?.focus?.();
@@ -7214,12 +7359,12 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7214
7359
  };
7215
7360
  const comments = computed(() => {
7216
7361
  const parentComment = props.comment;
7217
- const allComments = commentsStore.commentsList;
7218
- return (parentComment.trackedChange ? collectTrackedChangeThread(parentComment, allComments) : allComments.filter((comment) => {
7362
+ if (parentComment.trackedChange) return commentsStore.getTrackedChangeThread(parentComment);
7363
+ return commentsStore.commentsList.filter((comment) => {
7219
7364
  const isThreadedComment = comment.parentCommentId === parentComment.commentId;
7220
7365
  const isThisComment = comment.commentId === parentComment.commentId;
7221
7366
  return isThreadedComment || isThisComment;
7222
- })).sort((a, b) => {
7367
+ }).sort((a, b) => {
7223
7368
  if (a.commentId === parentComment.commentId) return -1;
7224
7369
  if (b.commentId === parentComment.commentId) return 1;
7225
7370
  return a.createdTime - b.createdTime;
@@ -7397,20 +7542,33 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7397
7542
  };
7398
7543
  const handleAddComment = async () => {
7399
7544
  if (commentsAreReadOnly$1()) return readOnlyMutationOutcome$1();
7545
+ if (isV2WriteDisabled.value) return {
7546
+ ok: false,
7547
+ reason: v2WriteCapability.value?.reason ?? "v2-write-unavailable"
7548
+ };
7400
7549
  if (isV2Mode.value && v2CommentsAdapter.value && !isPendingNewComment.value) {
7401
- const outcome = await commentsStore.replyCommentV2({
7402
- superdoc: proxy.$superdoc,
7403
- parentCommentId: getV2ExistingReplyParentId(props.comment),
7404
- text: currentCommentText.value
7405
- });
7406
- 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 = "";
7407
7567
  nextTick(() => emit("resize"));
7408
7568
  return outcome;
7569
+ } finally {
7570
+ isSubmittingReply.value = false;
7409
7571
  }
7410
- isReplying.value = false;
7411
- currentCommentText.value = "";
7412
- nextTick(() => emit("resize"));
7413
- return outcome;
7414
7572
  }
7415
7573
  const options = {
7416
7574
  documentId: props.comment.fileId,
@@ -7436,7 +7594,14 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7436
7594
  const v2TrackedChangesAdapter = computed(() => isV2Mode.value ? proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null : null);
7437
7595
  const v2WriteCapability = computed(() => {
7438
7596
  if (!v2CommentsAdapter.value) return null;
7439
- 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;
7440
7605
  });
7441
7606
  const isV2WriteDisabled = computed(() => {
7442
7607
  if (!isV2Mode.value) return false;
@@ -7444,6 +7609,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7444
7609
  if (!cap) return false;
7445
7610
  return cap.canWrite === false;
7446
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
+ });
7447
7617
  const v2TrackedChangeCapability = computed(() => {
7448
7618
  if (!v2TrackedChangesAdapter.value) return null;
7449
7619
  const documentMode = commentsStore.viewingVisibility.documentMode;
@@ -7753,6 +7923,11 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7753
7923
  if (isPendingNewComment.value) cancelComment(proxy.$superdoc);
7754
7924
  nextTick(() => emit("resize"));
7755
7925
  }, { immediate: true });
7926
+ watch(isV2WriteDisabled, (isDisabled) => {
7927
+ if (!isDisabled) return;
7928
+ isReplying.value = false;
7929
+ nextTick(() => emit("resize"));
7930
+ });
7756
7931
  return (_ctx, _cache) => {
7757
7932
  const _directive_click_outside = resolveDirective("click-outside");
7758
7933
  return withDirectives((openBlock(), createElementBlock("div", {
@@ -7946,11 +8121,14 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7946
8121
  class: "sd-button tracked-change-decision-alert__retry",
7947
8122
  onClick: withModifiers(retryTrackedChangeDecision, ["stop", "prevent"])
7948
8123
  }, " Retry ")])) : createCommentVNode("", true),
7949
- showInputSection.value && !unref(getConfig).readOnly ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [!isReplying.value ? (openBlock(), createElementBlock("div", {
8124
+ showInputSection.value && !unref(getConfig).readOnly ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [!isReplying.value ? (openBlock(), createElementBlock("button", {
7950
8125
  key: 0,
7951
- class: "reply-pill",
7952
- onClick: withModifiers(startReply, ["stop", "prevent"])
7953
- }, "Reply or add others with @")) : (openBlock(), createElementBlock("div", _hoisted_47, [createElementVNode("div", _hoisted_48, [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, {
7954
8132
  ref_key: "commentInput",
7955
8133
  ref: commentInput,
7956
8134
  users: usersFiltered.value,
@@ -7961,19 +8139,19 @@ var CommentDialog_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
7961
8139
  "users",
7962
8140
  "config",
7963
8141
  "comment"
7964
- ])]), createElementVNode("div", _hoisted_49, [createElementVNode("button", {
8142
+ ])]), createElementVNode("div", _hoisted_50, [createElementVNode("button", {
7965
8143
  class: "sd-button reply-btn-cancel",
7966
8144
  onClick: withModifiers(handleCancel, ["stop", "prevent"])
7967
8145
  }, "Cancel"), createElementVNode("button", {
7968
- 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 }]),
7969
8147
  onClick: withModifiers(handleAddComment, ["stop", "prevent"]),
7970
- disabled: !hasTextContent.value || isV2WriteDisabled.value,
7971
- "data-disabled-reason": isV2WriteDisabled.value ? v2WriteCapability.value?.reason ?? null : null
7972
- }, " Reply ", 10, _hoisted_50)])]))], 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)
7973
8151
  ], 64))], 14, _hoisted_1$25)), [[_directive_click_outside, handleClickOutside]]);
7974
8152
  };
7975
8153
  }
7976
- }, [["__scopeId", "data-v-35236c9c"]]);
8154
+ }, [["__scopeId", "data-v-74340036"]]);
7977
8155
  var _hoisted_1$24 = { class: "comments-list" };
7978
8156
  var _hoisted_2$19 = { key: 0 };
7979
8157
  var _hoisted_3$15 = [
@@ -8316,12 +8494,12 @@ const resolvePersistentReviewCardTop = ({ comment, anchorTop, anchorBottom, card
8316
8494
  if ((Number.isFinite(anchorBottom) ? anchorBottom : anchorTop) < viewportTop) return Math.min(anchorTop, viewportTop - cardHeight - 1);
8317
8495
  return anchorTop;
8318
8496
  };
8319
- const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId, comment }) => {
8497
+ const shouldMountFloatingCommentDialog = ({ id, visibleIds, activeCommentInstanceId }) => {
8320
8498
  if (!id) return false;
8321
8499
  if (id === "pending") return true;
8322
8500
  if (activeCommentInstanceId != null && String(id) === String(activeCommentInstanceId)) return true;
8323
8501
  if (visibleIds?.has?.(id)) return true;
8324
- return isPersistentReviewSidebarItem(comment);
8502
+ return false;
8325
8503
  };
8326
8504
  const resolveRemovedReviewCardContinuityTarget = ({ previousIds, currentIds, removedId }) => {
8327
8505
  if (!Array.isArray(previousIds) || !(currentIds instanceof Set) || !removedId) return null;
@@ -8360,7 +8538,6 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8360
8538
  setup(__props) {
8361
8539
  const ESTIMATED_HEIGHT = 110;
8362
8540
  const OBSERVER_MARGIN = 600;
8363
- const FALLBACK_ANCHOR_STEP = .001;
8364
8541
  const resolveCollisions = (positions, activeIndex, gap) => {
8365
8542
  if (activeIndex >= 0) {
8366
8543
  positions[activeIndex].top = positions[activeIndex].anchorTop;
@@ -8403,6 +8580,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8403
8580
  const commentsRenderKey = ref(0);
8404
8581
  const sidebarOffsetY = ref(0);
8405
8582
  const disableInstantLayoutTransitions = ref(false);
8583
+ const viewportRevision = ref(0);
8406
8584
  const isPendingThread = (commentOrId) => {
8407
8585
  const pendingId = pendingComment.value?.commentId;
8408
8586
  if (!pendingId) return false;
@@ -8412,10 +8590,27 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8412
8590
  const getThreadId = (comment) => {
8413
8591
  return comment?.commentId ?? comment?.importedId ?? null;
8414
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
+ });
8415
8610
  const resolveFloatingInstanceId = (threadId, preferredInstanceId = null) => {
8416
8611
  if (threadId == null) return null;
8417
8612
  const normalizedThreadId = String(threadId);
8418
- const matchingInstances = floatingCommentInstances.value.filter((instance) => String(getThreadId(instance.comment)) === normalizedThreadId);
8613
+ const matchingInstances = floatingInstanceIndex.value.byThreadId.get(normalizedThreadId) ?? [];
8419
8614
  if (!matchingInstances.length) return null;
8420
8615
  if (preferredInstanceId != null) {
8421
8616
  const preferredInstance = matchingInstances.find((instance) => String(instance.id) === String(preferredInstanceId));
@@ -8438,6 +8633,14 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8438
8633
  const placeholderRefs = ref({});
8439
8634
  let observer = null;
8440
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
+ };
8441
8644
  const getAnchorTop = (instance) => {
8442
8645
  if (props.currentDocument.type === "application/pdf") {
8443
8646
  const zoom = (activeZoom.value ?? 100) / 100;
@@ -8455,6 +8658,24 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8455
8658
  const bottom = instance?.positionEntry?.bounds?.bottom;
8456
8659
  return Number.isFinite(bottom) ? bottom : anchorTop;
8457
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
+ });
8458
8679
  const getPendingAnchorTop = () => {
8459
8680
  const positionEntry = editorCommentPositions.value["pending"];
8460
8681
  if (typeof positionEntry?.bounds?.top === "number" && !isNaN(positionEntry.bounds.top)) return positionEntry.bounds.top;
@@ -8488,40 +8709,72 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8488
8709
  return resolveFloatingInstanceId(instantSidebarAlignmentThreadId.value, instantSidebarAlignmentInstanceId.value);
8489
8710
  });
8490
8711
  const allPositions = computed(() => {
8491
- const instances = floatingCommentInstances.value;
8712
+ viewportRevision.value;
8713
+ const positionedInstances = geometryBearingInstances.value;
8492
8714
  const hasPending = pendingComment.value && pendingComment.value.fileId === props.currentDocument.id;
8493
- if (!instances.length && !hasPending) return [];
8715
+ if (!positionedInstances.length && !floatingCommentInstances.value.length && !hasPending) return [];
8494
8716
  const positions = [];
8495
- let lastAnchoredTop = 0;
8496
- let fallbackAnchorCount = 0;
8497
- for (const instance of instances) {
8498
- const key = instance?.id;
8499
- const anchorTop = getAnchorTop(instance);
8500
- const threadId = getThreadId(instance?.comment);
8501
- if (!key || !threadId) continue;
8502
- const hasAnchorGeometry = typeof anchorTop === "number" && !isNaN(anchorTop);
8503
- if (!hasAnchorGeometry && !isPersistentReviewSidebarItem(instance?.comment)) continue;
8504
- let top;
8505
- if (hasAnchorGeometry) {
8506
- top = normalizeFloatingAnchorTop(anchorTop, instance.comment);
8507
- lastAnchoredTop = top;
8508
- fallbackAnchorCount = 0;
8509
- } else {
8510
- fallbackAnchorCount += 1;
8511
- top = lastAnchoredTop + fallbackAnchorCount * FALLBACK_ANCHOR_STEP;
8512
- }
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);
8513
8729
  positions.push({
8514
- id: key,
8730
+ id,
8515
8731
  threadId,
8516
8732
  pageIndex: instance?.pageIndex ?? null,
8517
8733
  anchorTop: top,
8518
- anchorBottom: hasAnchorGeometry ? getAnchorBottom(instance, top) : top,
8734
+ anchorBottom,
8519
8735
  hasAnchorGeometry,
8520
8736
  top,
8521
- height: measuredHeights.value[key] || ESTIMATED_HEIGHT,
8737
+ height: measuredHeights.value[id] || ESTIMATED_HEIGHT,
8522
8738
  commentRef: instance.comment,
8523
8739
  instanceRef: instance
8524
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
+ });
8525
8778
  }
8526
8779
  if (hasPending) {
8527
8780
  const pendingTop = getPendingAnchorTop();
@@ -8536,12 +8789,10 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8536
8789
  });
8537
8790
  }
8538
8791
  positions.sort((a, b) => a.anchorTop - b.anchorTop);
8539
- const activeKey = hasPending ? "pending" : activeCommentInstanceId.value;
8540
8792
  resolveCollisions(positions, activeKey ? positions.findIndex((p) => p.id === activeKey) : -1, 15);
8541
- const viewportRange = getFloatingViewportRange();
8542
- if (viewportRange) for (const position of positions) {
8793
+ for (const position of positions) {
8543
8794
  if (!position.hasAnchorGeometry) continue;
8544
- const persistentReviewTop = resolvePersistentReviewCardTop({
8795
+ const offscreenTop = resolvePersistentReviewCardTop({
8545
8796
  comment: position.commentRef,
8546
8797
  anchorTop: position.anchorTop,
8547
8798
  anchorBottom: position.anchorBottom,
@@ -8549,21 +8800,13 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8549
8800
  viewportTop: viewportRange.top,
8550
8801
  viewportBottom: viewportRange.bottom
8551
8802
  });
8552
- if (persistentReviewTop != null) position.top = persistentReviewTop;
8803
+ if (offscreenTop != null) position.top = offscreenTop;
8553
8804
  }
8554
8805
  return positions;
8555
8806
  });
8556
- const totalHeight = computed(() => {
8557
- if (!allPositions.value.length) return 0;
8558
- let max = 0;
8559
- for (const p of allPositions.value) {
8560
- const bottom = p.top + p.height;
8561
- if (bottom > max) max = bottom;
8562
- }
8563
- return max + 50;
8564
- });
8565
8807
  const wrapperMinHeight = computed(() => {
8566
- return totalHeight.value + Math.max(0, sidebarOffsetY.value);
8808
+ viewportRevision.value;
8809
+ return Math.max(0, props.parent?.clientHeight || 0, props.parent?.scrollHeight || 0);
8567
8810
  });
8568
8811
  const setupObserver = () => {
8569
8812
  if (observer) observer.disconnect();
@@ -8921,6 +9164,8 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8921
9164
  });
8922
9165
  onMounted(() => {
8923
9166
  setupObserver();
9167
+ props.parent?.addEventListener?.("scroll", refreshViewportWindow, { passive: true });
9168
+ window.addEventListener("resize", refreshViewportWindow, { passive: true });
8924
9169
  nextTick(observePlaceholders);
8925
9170
  });
8926
9171
  onBeforeUnmount(() => {
@@ -8931,6 +9176,12 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8931
9176
  cancelAnimationFrame(activeLayoutContinuityFrame);
8932
9177
  activeLayoutContinuityFrame = null;
8933
9178
  }
9179
+ props.parent?.removeEventListener?.("scroll", refreshViewportWindow);
9180
+ window.removeEventListener("resize", refreshViewportWindow);
9181
+ if (viewportFrame != null) {
9182
+ cancelAnimationFrame(viewportFrame);
9183
+ viewportFrame = null;
9184
+ }
8934
9185
  if (observer) {
8935
9186
  observer.disconnect();
8936
9187
  observer = null;
@@ -8991,7 +9242,7 @@ var FloatingComments_default = /* @__PURE__ */ __plugin_vue_export_helper_defaul
8991
9242
  }), 128))], 4)], 4);
8992
9243
  };
8993
9244
  }
8994
- }, [["__scopeId", "data-v-3672b976"]]);
9245
+ }, [["__scopeId", "data-v-e699ceb8"]]);
8995
9246
  var _hoisted_1$22 = {
8996
9247
  class: "superdoc__pdf-comments-layer pdf-comments-layer",
8997
9248
  "aria-hidden": "false"
@@ -11612,7 +11863,34 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11612
11863
  tick();
11613
11864
  });
11614
11865
  }
11615
- 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 } = {}) {
11616
11894
  if (typeof anchorUrl !== "string" || !anchorUrl.startsWith("#")) return;
11617
11895
  const anchorName = anchorUrl.slice(1);
11618
11896
  const escapedAnchorName = cssEscape(anchorName);
@@ -11637,6 +11915,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11637
11915
  if (story && story.storyType !== "body") return;
11638
11916
  const blockId = bookmark?.range?.from?.blockId;
11639
11917
  if (typeof blockId !== "string") return;
11918
+ if (placeCaret) await placeCaretAtHeadingStart(blockId);
11640
11919
  const mountedParagraph = findPaintedParagraphByParaId(getUi()?.viewport?.getHost?.() ?? null, blockId);
11641
11920
  if (mountedParagraph) {
11642
11921
  scrollToElement(mountedParagraph);
@@ -11746,7 +12025,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11746
12025
  function openDefaultPopover(ctx) {
11747
12026
  closeCurrentPopover("replace");
11748
12027
  if (ctx.isAnchorLink && isTocEntryLinkElement(ctx.element)) {
11749
- navigateToAnchor(ctx.href);
12028
+ navigateToAnchor(ctx.href, { placeCaret: ctx.documentMode !== "viewing" });
11750
12029
  return;
11751
12030
  }
11752
12031
  if (ctx.documentMode === "viewing") {
@@ -11762,7 +12041,7 @@ function useLinkPopover({ getSurfaceManager, getActiveEditor, getUi, getResolver
11762
12041
  const closePopover = () => closeCurrentPopover("closed");
11763
12042
  const goToAnchor = (anchorUrl) => {
11764
12043
  closePopover();
11765
- navigateToAnchor(anchorUrl);
12044
+ navigateToAnchor(anchorUrl, { placeCaret: true });
11766
12045
  };
11767
12046
  const generation = openGeneration;
11768
12047
  const openWithHyperlinkTarget = (hyperlinkTarget) => {
@@ -13521,7 +13800,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13521
13800
  __name: "SuperDoc",
13522
13801
  emits: ["selection-update"],
13523
13802
  setup(__props, { emit: __emit }) {
13524
- const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-XZg0nr8y.es.js"));
13803
+ const PdfViewer = defineAsyncComponent(() => import("./chunks/PdfViewer-CNOtyS-I.es.js"));
13525
13804
  const getDocumentLoadPassword = (doc) => doc.password ?? proxy.$superdoc.config.password;
13526
13805
  const resolveDocumentV2Collaboration = (doc) => {
13527
13806
  const rawConfig = doc?.v2Collaboration;
@@ -13566,7 +13845,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13566
13845
  });
13567
13846
  }
13568
13847
  });
13569
- 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);
13570
13849
  const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate, refreshTrackedChangeCommentsByIds, syncTrackedChangePositionsWithDocument, syncTrackedChangeComments, addComment, addHydratedComment, getComment, resolveCommentPositionEntry, belongsToDocument, COMMENT_EVENTS, requestInstantSidebarAlignment, peekInstantSidebarAlignment, clearInstantSidebarAlignment } = commentsStore;
13571
13850
  const { proxy } = getCurrentInstance();
13572
13851
  commentsStore.proxy = proxy;
@@ -13746,18 +14025,19 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
13746
14025
  if (!documentId) return;
13747
14026
  clearV2CommandShortcutBinding(documentId);
13748
14027
  if (typeof bindEditShortcuts !== "function") return;
13749
- const ui = createSuperDocUI({ superdoc: proxy.$superdoc });
14028
+ const fallbackUi = createSuperDocUI({ superdoc: proxy.$superdoc });
14029
+ const resolveUi = () => proxy.$superdoc?.toolbar?.ui ?? fallbackUi;
13750
14030
  const openLinkWorkflow = resolveV2LinkWorkflowOpener();
13751
14031
  const unbind = bindEditShortcuts({
13752
14032
  commandRoute: {
13753
- executeAsync: (commandId, payload) => ui.commands.executeAsync(commandId, payload),
13754
- getState: (commandId) => ui.commands.get(commandId).getState()
14033
+ executeAsync: (commandId, payload) => resolveUi().commands.executeAsync(commandId, payload),
14034
+ getState: (commandId) => resolveUi().commands.get(commandId).getState()
13755
14035
  },
13756
14036
  ...openLinkWorkflow ? { openLinkWorkflow } : {}
13757
14037
  });
13758
14038
  v2CommandShortcutBindings.set(documentId, {
13759
14039
  unbind,
13760
- ui
14040
+ ui: fallbackUi
13761
14041
  });
13762
14042
  };
13763
14043
  const clearV2SessionShortcutBinding = (documentId) => {
@@ -14581,13 +14861,17 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
14581
14861
  v2RulerReady.value = false;
14582
14862
  };
14583
14863
  let v2TypingReviewHydrationTimer = 0;
14584
- const hydrateV2ReviewRowsFromHost = (options = {}) => {
14864
+ const hydrateV2CommentRowsFromHost = () => {
14585
14865
  const commentsAdapter = proxy.$superdoc?.activeEditor?.v2Comments ?? null;
14586
- if (commentsAdapter) commentsStore.hydrateCommentsFromV2?.({
14866
+ if (!commentsAdapter) return;
14867
+ commentsStore.hydrateCommentsFromV2?.({
14587
14868
  superdoc: proxy.$superdoc,
14588
14869
  adapter: commentsAdapter,
14589
14870
  documentId: proxy.$superdoc?.activeEditor?.documentId ?? null
14590
14871
  });
14872
+ };
14873
+ const hydrateV2ReviewRowsFromHost = (options = {}) => {
14874
+ hydrateV2CommentRowsFromHost();
14591
14875
  const trackedChangesAdapter = proxy.$superdoc?.activeEditor?.v2TrackedChanges ?? null;
14592
14876
  if (!trackedChangesAdapter) return;
14593
14877
  commentsStore.hydrateTrackedChangesFromV2?.({
@@ -14690,6 +14974,10 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
14690
14974
  const receipt = event.origin === "history" ? null : event.receipt;
14691
14975
  const canWaitForExactPaint = reviewImpact && typeof receipt?.txId === "string" && receipt.txId.length > 0 && typeof readiness?.whenPainted === "function";
14692
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
+ }
14693
14981
  if (isV2EditableTextMutationEvent(event)) {
14694
14982
  scheduleV2TypingReviewHydration();
14695
14983
  return;
@@ -15151,7 +15439,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15151
15439
  if (isV2Mode.value && !v2GeometryAvailable.value && !hasPdfFloatingComments.value) return false;
15152
15440
  if (!shouldRenderCommentsInViewing.value) return false;
15153
15441
  if (!shouldUseSidebarComments.value) return false;
15154
- 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;
15155
15443
  });
15156
15444
  const activeCompactComment = computed(() => {
15157
15445
  if (showCommentsSidebar.value) return null;
@@ -15819,7 +16107,7 @@ var SuperDoc_default = /* @__PURE__ */ __plugin_vue_export_helper_default({
15819
16107
  ], 38);
15820
16108
  };
15821
16109
  }
15822
- }, [["__scopeId", "data-v-c4ab995e"]]);
16110
+ }, [["__scopeId", "data-v-4084bf1b"]]);
15823
16111
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
15824
16112
  var PINIA_DEVTOOLS_PLUGIN_ID = "dev.esm.pinia";
15825
16113
  var piniaDevtoolsSuppressionState = {
@@ -34365,7 +34653,7 @@ var SuperDoc = class extends import_eventemitter3.default {
34365
34653
  this.config.colors = shuffleArray(this.config.colors);
34366
34654
  this.userColorMap = /* @__PURE__ */ new Map();
34367
34655
  this.colorIndex = 0;
34368
- this.version = "2.0.0-next.28";
34656
+ this.version = "2.0.0-next.29";
34369
34657
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
34370
34658
  this.superdocId = config.superdocId || v4_default();
34371
34659
  this.colors = this.config.colors ?? [];