superdoc 2.6.0-next.8 → 2.6.0

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.
@@ -5689,6 +5689,7 @@ function useComment(params) {
5689
5689
  let parentCommentId = params.parentCommentId;
5690
5690
  let trackedChangeParentId = params.trackedChangeParentId;
5691
5691
  const trackedChangeThreadParentId = ref(params.trackedChangeThreadParentId);
5692
+ const trackedChangeSide = ref(params.trackedChangeSide);
5692
5693
  const fileId = params.fileId;
5693
5694
  const fileType = params.fileType;
5694
5695
  const createdAtVersionNumber = params.createdAtVersionNumber;
@@ -5939,6 +5940,7 @@ function useComment(params) {
5939
5940
  parentCommentId,
5940
5941
  trackedChangeParentId,
5941
5942
  trackedChangeThreadParentId: trackedChangeThreadParentId.value,
5943
+ trackedChangeSide: trackedChangeSide.value,
5942
5944
  fileId,
5943
5945
  fileType,
5944
5946
  mentions: mentions.value.map((u) => {
@@ -5994,6 +5996,7 @@ function useComment(params) {
5994
5996
  parentCommentId,
5995
5997
  trackedChangeParentId,
5996
5998
  trackedChangeThreadParentId,
5999
+ trackedChangeSide,
5997
6000
  fileId,
5998
6001
  fileType,
5999
6002
  mentions,
@@ -6913,6 +6916,78 @@ var trackedChangeThreadParentIdForComment = (comment) => {
6913
6916
  //#endregion
6914
6917
  //#region src/stores/helpers/tracked-change-thread-index.js
6915
6918
  /**
6919
+ * @param {string|null|undefined} side
6920
+ * @returns {'source'|'destination'|null}
6921
+ */
6922
+ var trackedChangeSideGroup = (side) => {
6923
+ if (side === "source" || side === "deleted") return "source";
6924
+ if (side === "destination" || side === "inserted") return "destination";
6925
+ return null;
6926
+ };
6927
+ /**
6928
+ * @param {{ semanticColorKey?: string|null }} comment
6929
+ * @returns {'source'|'destination'|null}
6930
+ */
6931
+ var trackedChangeRowSideGroup = (comment) => {
6932
+ if (comment?.semanticColorKey === "move-from") return "source";
6933
+ if (comment?.semanticColorKey === "move-to") return "destination";
6934
+ return null;
6935
+ };
6936
+ /**
6937
+ * Resolve explicit conversation provenance to the visible tracked-change row.
6938
+ * Canonical move ids are shared, so their persisted side selects the source or
6939
+ * destination card. Other ambiguous canonical ids remain unresolved.
6940
+ *
6941
+ * @template {{ commentId: string|number, trackedChange?: boolean, trackedChangeCanonicalId?: string|number|null, trackedChangeSide?: string|null, semanticColorKey?: string|null, parentCommentId?: string|number|null, threadingParentCommentId?: string|number|null, trackedChangeParentId?: string|number|null, trackedChangeThreadParentId?: string|number|null }} Comment
6942
+ * @param {ReadonlyArray<Comment>} allComments
6943
+ * @returns {Map<Comment, Comment>}
6944
+ */
6945
+ var buildTrackedChangeThreadOwnerIndex = (allComments) => {
6946
+ /** @type {Map<string, Comment[]>} */
6947
+ const trackedRowsById = /* @__PURE__ */ new Map();
6948
+ /** @type {Map<string, Comment[]>} */
6949
+ const trackedRowsByCanonicalId = /* @__PURE__ */ new Map();
6950
+ /**
6951
+ * @template Key, Value
6952
+ * @param {Map<Key, Value[]>} map
6953
+ * @param {Key} key
6954
+ * @param {Value} value
6955
+ */
6956
+ const append = (map, key, value) => {
6957
+ const existing = map.get(key);
6958
+ if (existing) existing.push(value);
6959
+ else map.set(key, [value]);
6960
+ };
6961
+ for (const comment of allComments) {
6962
+ if (comment?.trackedChange !== true) continue;
6963
+ if (typeof comment.commentId === "string") append(trackedRowsById, comment.commentId, comment);
6964
+ if (comment.trackedChangeCanonicalId != null) append(trackedRowsByCanonicalId, String(comment.trackedChangeCanonicalId), comment);
6965
+ }
6966
+ /** @type {Map<Comment, Comment>} */
6967
+ const ownerByComment = /* @__PURE__ */ new Map();
6968
+ for (const comment of allComments) {
6969
+ if (comment?.trackedChange === true) continue;
6970
+ const aliases = new Set([comment.threadingParentCommentId, trackedChangeThreadParentIdForComment(comment)].filter((id) => id != null).map((id) => String(id)));
6971
+ if (aliases.size === 0) continue;
6972
+ const directOwners = new Set([...aliases].flatMap((alias) => trackedRowsById.get(alias) ?? []));
6973
+ if (directOwners.size === 1) {
6974
+ ownerByComment.set(comment, [...directOwners][0]);
6975
+ continue;
6976
+ }
6977
+ if (directOwners.size > 1) continue;
6978
+ const canonicalOwners = new Set([...aliases].flatMap((alias) => trackedRowsByCanonicalId.get(alias) ?? []));
6979
+ if (canonicalOwners.size === 1) {
6980
+ ownerByComment.set(comment, [...canonicalOwners][0]);
6981
+ continue;
6982
+ }
6983
+ const sideGroup = trackedChangeSideGroup(comment.trackedChangeSide);
6984
+ if (!sideGroup) continue;
6985
+ const sideOwners = [...canonicalOwners].filter((owner) => trackedChangeRowSideGroup(owner) === sideGroup);
6986
+ if (sideOwners.length === 1) ownerByComment.set(comment, sideOwners[0]);
6987
+ }
6988
+ return ownerByComment;
6989
+ };
6990
+ /**
6916
6991
  * Build every tracked-change conversation in one graph pass.
6917
6992
  *
6918
6993
  * `CommentDialog` instances are intentionally mounted for every tracked-change
@@ -6927,7 +7002,7 @@ var trackedChangeThreadParentIdForComment = (comment) => {
6927
7002
  * asymmetry here so numeric tracked-change row ids do not acquire new thread
6928
7003
  * membership.
6929
7004
  *
6930
- * @template {{ commentId: string|number, trackedChange?: boolean, createdTime?: number, parentCommentId?: string|number|null, threadingParentCommentId?: string|number|null, trackedChangeParentId?: string|number|null }} Comment
7005
+ * @template {{ commentId: string|number, trackedChange?: boolean, trackedChangeCanonicalId?: string|number|null, trackedChangeSide?: string|null, semanticColorKey?: string|null, createdTime?: number, parentCommentId?: string|number|null, threadingParentCommentId?: string|number|null, trackedChangeParentId?: string|number|null }} Comment
6931
7006
  * @param {ReadonlyArray<Comment>} allComments
6932
7007
  * @param {ReadonlyMap<string|number, ReadonlyArray<Comment>>} [previous]
6933
7008
  * @returns {Map<string|number, ReadonlyArray<Comment>>}
@@ -6935,12 +7010,13 @@ var trackedChangeThreadParentIdForComment = (comment) => {
6935
7010
  var buildTrackedChangeThreadIndex = (allComments, previous = /* @__PURE__ */ new Map()) => {
6936
7011
  /** @type {Map<string, Comment[]>} Parent links are normalized exactly as in the legacy collector. */
6937
7012
  const childrenByParentId = /* @__PURE__ */ new Map();
6938
- /** @type {Map<string, Comment[]>} Explicit tracked-change conversation roots, normalized like the legacy collector. */
6939
- const anchoredRootsByTrackedChangeId = /* @__PURE__ */ new Map();
7013
+ /** @type {Map<string, Comment[]>} Explicit conversation members keyed by their visible tracked-change row. */
7014
+ const anchoredCommentsByTrackedChangeId = /* @__PURE__ */ new Map();
6940
7015
  /** @type {Map<string|number, Comment[]>} */
6941
7016
  const commentsById = /* @__PURE__ */ new Map();
6942
7017
  /** @type {Map<Comment, number>} */
6943
7018
  const sourceIndex = /* @__PURE__ */ new Map();
7019
+ const trackedChangeOwnerByComment = buildTrackedChangeThreadOwnerIndex(allComments);
6944
7020
  /**
6945
7021
  * @template Key, Value
6946
7022
  * @param {Map<Key, Value[]>} map
@@ -6956,8 +7032,8 @@ var buildTrackedChangeThreadIndex = (allComments, previous = /* @__PURE__ */ new
6956
7032
  sourceIndex.set(comment, index);
6957
7033
  append(commentsById, comment.commentId, comment);
6958
7034
  new Set([comment.parentCommentId, comment.threadingParentCommentId].filter((id) => id != null).map((id) => String(id))).forEach((parentId) => append(childrenByParentId, parentId, comment));
6959
- const trackedChangeThreadParentId = isV2SyntheticTrackedChangeRow(comment) ? null : trackedChangeThreadParentIdForComment(comment);
6960
- if (trackedChangeThreadParentId != null && !comment.parentCommentId) append(anchoredRootsByTrackedChangeId, String(trackedChangeThreadParentId), comment);
7035
+ const owner = trackedChangeOwnerByComment.get(comment);
7036
+ if (owner?.commentId != null) append(anchoredCommentsByTrackedChangeId, String(owner.commentId), comment);
6961
7037
  });
6962
7038
  /** @type {Map<string|number, ReadonlyArray<Comment>>} */
6963
7039
  const next = /* @__PURE__ */ new Map();
@@ -6967,7 +7043,10 @@ var buildTrackedChangeThreadIndex = (allComments, previous = /* @__PURE__ */ new
6967
7043
  const threadIds = /* @__PURE__ */ new Set([trackedChangeId]);
6968
7044
  /** @type {Array<string|number>} */
6969
7045
  const queue = [];
6970
- const seed = typeof trackedChangeId === "string" ? [...childrenByParentId.get(trackedChangeId) ?? [], ...anchoredRootsByTrackedChangeId.get(trackedChangeId) ?? []] : [];
7046
+ /** @type {string[]} */
7047
+ const threadParentIds = [];
7048
+ if (typeof trackedChangeId === "string") threadParentIds.push(trackedChangeId);
7049
+ const seed = threadParentIds.flatMap((parentId) => [...childrenByParentId.get(parentId) ?? [], ...anchoredCommentsByTrackedChangeId.get(parentId) ?? []]);
6971
7050
  for (const comment of seed) {
6972
7051
  const id = comment.commentId;
6973
7052
  if (id === trackedChangeId || threadIds.has(id)) continue;
@@ -7144,6 +7223,14 @@ var normalizeV2CommentDraftText = (value) => {
7144
7223
  if (!html || html === "<p></p>") return "";
7145
7224
  return decodeCommentDraftEntities(html.replace(COMMENT_DRAFT_BR_TAG, "\n").replace(COMMENT_DRAFT_BLOCK_CLOSE_TAG, "\n").replace(COMMENT_DRAFT_BLOCK_OPEN_TAG, "").replace(COMMENT_DRAFT_ANY_TAG, "")).replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "").replace(/\n+$/, "");
7146
7225
  };
7226
+ var cloneV2TrackedChangeStory = (story) => {
7227
+ if (!story || typeof story !== "object") return null;
7228
+ const rawStory = toRaw(story);
7229
+ return {
7230
+ ...rawStory,
7231
+ ...rawStory.section && typeof rawStory.section === "object" ? { section: { ...toRaw(rawStory.section) } } : {}
7232
+ };
7233
+ };
7147
7234
  var useCommentsStore = defineStore("comments", () => {
7148
7235
  const BODY_TRACKED_CHANGE_STORY = {
7149
7236
  kind: "story",
@@ -7457,12 +7544,6 @@ var useCommentsStore = defineStore("comments", () => {
7457
7544
  if (isV2SyntheticTrackedChangeRow(comment) && comment?.trackedChange === true) return null;
7458
7545
  return trackedChangeThreadParentIdForComment(comment);
7459
7546
  };
7460
- const shouldThreadWithTrackedChange = (comment) => {
7461
- const trackedChangeParentId = trackedChangeThreadParentIdForComment$1(comment);
7462
- if (!trackedChangeParentId) return false;
7463
- const trackedChange = getComment(trackedChangeParentId);
7464
- return Boolean(trackedChange?.trackedChange);
7465
- };
7466
7547
  /**
7467
7548
  * Extract the position lookup key from a comment or comment ID.
7468
7549
  * Prefers whichever key currently exists in editorCommentPositions.
@@ -8247,9 +8328,10 @@ var useCommentsStore = defineStore("comments", () => {
8247
8328
  const parentComments = [];
8248
8329
  const resolvedComments = [];
8249
8330
  const childCommentMap = /* @__PURE__ */ new Map();
8331
+ const trackedChangeThreadOwnerIndex = buildTrackedChangeThreadOwnerIndex(source);
8250
8332
  source.forEach((comment) => {
8251
8333
  if (!isThreadVisible(comment)) return;
8252
- const trackedChangeThreadParentId = shouldThreadWithTrackedChange(comment) ? trackedChangeThreadParentIdForComment$1(comment) : null;
8334
+ const trackedChangeThreadParentId = trackedChangeThreadOwnerIndex.get(comment)?.commentId ?? null;
8253
8335
  const parentId = comment.trackedChange ? null : comment.parentCommentId || trackedChangeThreadParentId;
8254
8336
  if (comment.resolvedTime) resolvedComments.push(comment);
8255
8337
  else if (!parentId && !comment.resolvedTime) parentComments.push({ ...comment });
@@ -8820,16 +8902,18 @@ var useCommentsStore = defineStore("comments", () => {
8820
8902
  };
8821
8903
  };
8822
8904
  /**
8823
- * Reply to an existing comment through the v2 adapter.
8905
+ * Reply to an existing comment or tracked-change review row through the v2 adapter.
8824
8906
  *
8825
8907
  * Plan §4.1 rules:
8826
8908
  * - empty text rejects before mutation with `comment-text-empty`
8827
8909
  * - missing parent rejects before mutation with `parent-comment-id-missing`
8828
- * - successful reply refreshes from v2 list and reconciles
8910
+ * - a tracked-change row creates its first anchored root only after a
8911
+ * canonical-id reply reports `TARGET_NOT_FOUND`
8912
+ * - successful mutation refreshes from v2 list and reconciles
8829
8913
  * - rejection preserves rows and emits a rejected `comments-update` event
8830
8914
  * - committed-but-refresh-failed surfaces honestly (no reconcile with `[]`)
8831
8915
  */
8832
- const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [] } = {}) => {
8916
+ const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [], trackedChangeTarget } = {}) => {
8833
8917
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
8834
8918
  const v2Adapter = getV2CommentsAdapter(superdoc);
8835
8919
  if (!v2Adapter) return {
@@ -8846,17 +8930,44 @@ var useCommentsStore = defineStore("comments", () => {
8846
8930
  ok: false,
8847
8931
  reason: "comment-text-empty"
8848
8932
  };
8849
- const parent = commentsList.value.find((c) => String(c.commentId) === normalizedParent);
8933
+ let normalizedTrackedChangeTarget = null;
8934
+ if (trackedChangeTarget != null) {
8935
+ const trackedChangeId = trackedChangeTarget?.trackedChangeId != null ? String(trackedChangeTarget.trackedChangeId) : null;
8936
+ if (!trackedChangeId) return {
8937
+ ok: false,
8938
+ reason: "tracked-change-id-missing"
8939
+ };
8940
+ if (trackedChangeId !== normalizedParent) return {
8941
+ ok: false,
8942
+ reason: "tracked-change-target-mismatch"
8943
+ };
8944
+ const story = cloneV2TrackedChangeStory(trackedChangeTarget.story);
8945
+ normalizedTrackedChangeTarget = {
8946
+ kind: "trackedChange",
8947
+ trackedChangeId,
8948
+ ...trackedChangeTarget.side != null ? { side: trackedChangeTarget.side } : {},
8949
+ ...story ? { story } : {}
8950
+ };
8951
+ }
8952
+ const parent = commentsList.value.find((c) => String(c.commentId) === normalizedParent || c.trackedChange === true && String(c.trackedChangeCanonicalId) === normalizedParent);
8850
8953
  const fileId = parent?.fileId ?? v2Adapter.documentId ?? null;
8851
8954
  return runV2CommentMutation({
8852
8955
  superdoc,
8853
8956
  adapter: v2Adapter,
8854
8957
  fileId,
8855
- operation: () => v2Adapter.reply({
8856
- parentCommentId: normalizedParent,
8857
- text: plainText,
8858
- ...mentions.length ? { mentions } : {}
8859
- }),
8958
+ operation: async () => {
8959
+ const replyOutcome = await v2Adapter.reply({
8960
+ parentCommentId: normalizedParent,
8961
+ text: plainText,
8962
+ ...mentions.length ? { mentions } : {}
8963
+ });
8964
+ if (replyOutcome?.ok || replyOutcome?.code !== "TARGET_NOT_FOUND" || !normalizedTrackedChangeTarget) return replyOutcome;
8965
+ return v2Adapter.commitPendingComment({
8966
+ text: plainText,
8967
+ target: normalizedTrackedChangeTarget,
8968
+ ...mentions.length ? { mentions } : {}
8969
+ });
8970
+ },
8860
8971
  eventType: COMMENT_EVENTS.ADD,
8861
8972
  rejectionFallbackReason: "v2-reply-failed",
8862
8973
  rejectionEventExtras: parent ? { comment: getCommentEventPayload(parent) } : {},
@@ -9329,7 +9440,7 @@ var useCommentsStore = defineStore("comments", () => {
9329
9440
  if (input.trackedChangeThreadParentId !== void 0) existing.trackedChangeThreadParentId = input.trackedChangeThreadParentId;
9330
9441
  else existing.trackedChangeThreadParentId = void 0;
9331
9442
  if (input.trackedChangeSide !== void 0) existing.trackedChangeSide = input.trackedChangeSide;
9332
- else delete existing.trackedChangeSide;
9443
+ else existing.trackedChangeSide = void 0;
9333
9444
  if (input.threadingParentCommentId !== void 0) existing.threadingParentCommentId = input.threadingParentCommentId;
9334
9445
  else delete existing.threadingParentCommentId;
9335
9446
  };
@@ -10071,7 +10182,7 @@ var useCommentsStore = defineStore("comments", () => {
10071
10182
  if (!(trackedChangeParentId && trackedChangeIds.has(trackedChangeParentId) || trackedChangeThreadParentId && trackedChangeIds.has(trackedChangeThreadParentId) || threadingParentCommentId && trackedChangeIds.has(threadingParentCommentId) || parentCommentId && trackedChangeIds.has(parentCommentId))) return;
10072
10183
  delete linkedComment.trackedChangeParentId;
10073
10184
  linkedComment.trackedChangeThreadParentId = void 0;
10074
- delete linkedComment.trackedChangeSide;
10185
+ linkedComment.trackedChangeSide = void 0;
10075
10186
  delete linkedComment.threadingParentCommentId;
10076
10187
  if (parentCommentId && trackedChangeIds.has(parentCommentId)) delete linkedComment.parentCommentId;
10077
10188
  detachedCount += 1;
@@ -12510,10 +12621,23 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12510
12621
  };
12511
12622
  const getV2ExistingReplyParentId = (comment) => {
12512
12623
  if (!comment) return null;
12513
- if (comment.trackedChange && comment.importedId != null) return String(comment.importedId);
12624
+ if (comment.trackedChange && comment.trackedChangeCanonicalId != null) return String(comment.trackedChangeCanonicalId);
12514
12625
  if (comment.commentId != null) return String(comment.commentId);
12515
12626
  return null;
12516
12627
  };
12628
+ const getV2TrackedChangeCommentTarget = (comment) => {
12629
+ if (!comment?.trackedChange) return null;
12630
+ const trackedChangeId = getV2ExistingReplyParentId(comment);
12631
+ if (!trackedChangeId) return null;
12632
+ const target = {
12633
+ kind: "trackedChange",
12634
+ trackedChangeId
12635
+ };
12636
+ if (comment.trackedChangeStory) target.story = comment.trackedChangeStory;
12637
+ if (comment.semanticColorKey === "move-from") target.side = "source";
12638
+ if (comment.semanticColorKey === "move-to") target.side = "destination";
12639
+ return target;
12640
+ };
12517
12641
  const getEntryBoundsCoordinate = (entry, coordinate) => {
12518
12642
  const value = entry?.bounds?.[coordinate];
12519
12643
  return Number.isFinite(value) ? value : null;
@@ -12854,11 +12978,13 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12854
12978
  isSubmittingReply.value = true;
12855
12979
  try {
12856
12980
  const parentCommentId = getV2ExistingReplyParentId(props.comment);
12981
+ const trackedChangeTarget = getV2TrackedChangeCommentTarget(props.comment);
12857
12982
  const outcome = await commentsStore.replyCommentV2({
12858
12983
  superdoc: proxy.$superdoc,
12859
12984
  parentCommentId,
12860
12985
  text: currentCommentText.value,
12861
- ...currentCommentMentions.value.length ? { mentions: currentCommentMentions.value } : {}
12986
+ ...currentCommentMentions.value.length ? { mentions: currentCommentMentions.value } : {},
12987
+ ...trackedChangeTarget ? { trackedChangeTarget } : {}
12862
12988
  });
12863
12989
  if (!outcome?.ok) {
12864
12990
  nextTick(() => emit("resize"));
@@ -13461,7 +13587,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
13461
13587
  ], 64))], 14, _hoisted_1$26)), [[_directive_click_outside, handleClickOutside]]);
13462
13588
  };
13463
13589
  }
13464
- }, [["__scopeId", "data-v-f4c743f2"]]);
13590
+ }, [["__scopeId", "data-v-50d8ea52"]]);
13465
13591
  //#endregion
13466
13592
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue
13467
13593
  var _hoisted_1$25 = [
@@ -17946,7 +18072,9 @@ function scrollToElement(targetElement, options = {
17946
18072
  if (!targetElement) return;
17947
18073
  const container = getScrollableParent(targetElement);
17948
18074
  const containerRect = container.getBoundingClientRect();
17949
- const offsetTop = targetElement.getBoundingClientRect().top - containerRect.top + container.scrollTop;
18075
+ const targetRect = targetElement.getBoundingClientRect();
18076
+ const containerTop = container === (document.scrollingElement || document.documentElement) ? 0 : containerRect.top;
18077
+ const offsetTop = targetRect.top - containerTop + container.scrollTop;
17950
18078
  const scrollPaddingTopValue = window.getComputedStyle(container).scrollPaddingTop?.trim();
17951
18079
  const resolvedScrollPaddingTop = scrollPaddingTopValue?.endsWith("px") ? Number(scrollPaddingTopValue.slice(0, -2)) : 0;
17952
18080
  const scrollPaddingTop = Number.isFinite(resolvedScrollPaddingTop) && resolvedScrollPaddingTop >= 0 ? resolvedScrollPaddingTop : 0;
@@ -43410,7 +43538,7 @@ var SuperDoc = class extends import_eventemitter3.default {
43410
43538
  this.config.colors = shuffleArray(this.config.colors);
43411
43539
  this.userColorMap = /* @__PURE__ */ new Map();
43412
43540
  this.colorIndex = 0;
43413
- this.version = "2.6.0-next.8";
43541
+ this.version = "2.6.0";
43414
43542
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43415
43543
  this.superdocId = config.superdocId || v4();
43416
43544
  this.colors = this.config.colors ?? [];