superdoc 2.6.0-next.7 → 2.6.0-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/superdoc.cjs CHANGED
@@ -5694,7 +5694,7 @@ function useComment(params) {
5694
5694
  const fileType = params.fileType;
5695
5695
  const createdAtVersionNumber = params.createdAtVersionNumber;
5696
5696
  const isInternal = (0, vue.ref)(params.isInternal !== void 0 ? params.isInternal : true);
5697
- const mentions = (0, vue.ref)([]);
5697
+ const mentions = (0, vue.ref)(Array.isArray(params.mentions) ? params.mentions : []);
5698
5698
  const commentElement = (0, vue.ref)(null);
5699
5699
  const isFocused = (0, vue.ref)(params.isFocused || false);
5700
5700
  const creatorId = (0, vue.ref)(params.creatorId ?? null);
@@ -7254,6 +7254,7 @@ var useCommentsStore = defineStore("comments", () => {
7254
7254
  const overlappedIds = /* @__PURE__ */ new Set([]);
7255
7255
  const suppressInternalExternal = (0, vue.ref)(true);
7256
7256
  const currentCommentText = (0, vue.ref)("");
7257
+ const currentCommentMentions = (0, vue.ref)([]);
7257
7258
  const commentsList = (0, vue.ref)([]);
7258
7259
  const reviewDirectoryList = (0, vue.shallowRef)([]);
7259
7260
  let priorTrackedChangeThreadIndex = /* @__PURE__ */ new Map();
@@ -8336,6 +8337,7 @@ var useCommentsStore = defineStore("comments", () => {
8336
8337
  const removePendingComment = (superdoc) => {
8337
8338
  const hadPending = !!pendingComment.value;
8338
8339
  currentCommentText.value = "";
8340
+ currentCommentMentions.value = [];
8339
8341
  pendingComment.value = null;
8340
8342
  pendingV2CommentTarget.value = null;
8341
8343
  superdocStore.selectionPosition = null;
@@ -8355,16 +8357,19 @@ var useCommentsStore = defineStore("comments", () => {
8355
8357
  const v2Adapter = !skipEditorUpdate && !isHydration ? getV2CommentsAdapter(superdoc) : null;
8356
8358
  if (v2Adapter && !comment.trackedChange) {
8357
8359
  const text = normalizeV2CommentDraftText(pendingComment.value ? currentCommentText.value : comment.commentText);
8360
+ const mentions = pendingComment.value ? currentCommentMentions.value : comment.mentions ?? [];
8358
8361
  const target = pendingComment.value ? pendingV2CommentTarget.value : null;
8359
8362
  const parentCommentId = comment.parentCommentId ? String(comment.parentCommentId) : pendingComment.value ? null : null;
8360
8363
  return (async () => {
8361
8364
  try {
8362
8365
  return await (parentCommentId ? v2Adapter.reply({
8363
8366
  parentCommentId,
8364
- text
8367
+ text,
8368
+ ...mentions.length ? { mentions } : {}
8365
8369
  }) : v2Adapter.commitPendingComment({
8366
8370
  text,
8367
- target
8371
+ target,
8372
+ ...mentions.length ? { mentions } : {}
8368
8373
  }));
8369
8374
  } catch (err) {
8370
8375
  return {
@@ -8825,7 +8830,7 @@ var useCommentsStore = defineStore("comments", () => {
8825
8830
  * - rejection preserves rows and emits a rejected `comments-update` event
8826
8831
  * - committed-but-refresh-failed surfaces honestly (no reconcile with `[]`)
8827
8832
  */
8828
- const replyCommentV2 = async ({ superdoc, parentCommentId, text } = {}) => {
8833
+ const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [] } = {}) => {
8829
8834
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
8830
8835
  const v2Adapter = getV2CommentsAdapter(superdoc);
8831
8836
  if (!v2Adapter) return {
@@ -8850,7 +8855,8 @@ var useCommentsStore = defineStore("comments", () => {
8850
8855
  fileId,
8851
8856
  operation: () => v2Adapter.reply({
8852
8857
  parentCommentId: normalizedParent,
8853
- text: plainText
8858
+ text: plainText,
8859
+ ...mentions.length ? { mentions } : {}
8854
8860
  }),
8855
8861
  eventType: COMMENT_EVENTS.ADD,
8856
8862
  rejectionFallbackReason: "v2-reply-failed",
@@ -9306,6 +9312,7 @@ var useCommentsStore = defineStore("comments", () => {
9306
9312
  };
9307
9313
  const applyUpdate = (existing, input) => {
9308
9314
  existing.commentText = input.commentText ?? existing.commentText;
9315
+ existing.mentions = Array.isArray(input.mentions) ? input.mentions : [];
9309
9316
  existing.isInternal = typeof input.isInternal === "boolean" ? input.isInternal : existing.isInternal;
9310
9317
  if (typeof input.resolvedTime === "number") {
9311
9318
  existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime;
@@ -9355,6 +9362,58 @@ var useCommentsStore = defineStore("comments", () => {
9355
9362
  return { added: addedComments[addedComments.length - 1] ?? null };
9356
9363
  });
9357
9364
  /**
9365
+ * Reconcile and publish a root comment created by the private V2 context menu.
9366
+ * That path owns selection capture, while this store remains the sole owner
9367
+ * of shared comment rows and public comments-update events.
9368
+ */
9369
+ const announceV2CommentCreated = async ({ superdoc, commentId } = {}) => {
9370
+ const id = normalizeCommentId(commentId);
9371
+ const adapter = getV2CommentsAdapter(superdoc);
9372
+ const commentsApi = superdoc?.activeEditor?.doc?.comments;
9373
+ if (!id) return {
9374
+ ok: false,
9375
+ reason: "comment-id-missing"
9376
+ };
9377
+ if (!adapter || !isCurrentV2CommentsAdapter(adapter)) return {
9378
+ ok: false,
9379
+ reason: "comments-adapter-stale"
9380
+ };
9381
+ if (typeof commentsApi?.get !== "function") return {
9382
+ ok: false,
9383
+ reason: "document-api-unavailable"
9384
+ };
9385
+ let item;
9386
+ try {
9387
+ item = await commentsApi.get({ commentId: id });
9388
+ } catch (err) {
9389
+ return {
9390
+ ok: false,
9391
+ reason: "comment-read-failed",
9392
+ detail: err?.message ?? String(err)
9393
+ };
9394
+ }
9395
+ if (!isCurrentV2CommentsAdapter(adapter)) return {
9396
+ ok: false,
9397
+ reason: "comments-adapter-stale"
9398
+ };
9399
+ const comment = reconcileCommentsFromV2({
9400
+ superdoc,
9401
+ adapter,
9402
+ documentId: adapter.documentId,
9403
+ items: [item],
9404
+ pruneStale: false
9405
+ })?.added ?? commentsList.value.find((candidate) => String(candidate?.commentId ?? "") === id || String(candidate?.importedId ?? "") === id) ?? null;
9406
+ const event = {
9407
+ type: COMMENT_EVENTS.ADD,
9408
+ comment: comment?.getValues?.() ?? null
9409
+ };
9410
+ superdoc?.emit?.("comments-update", event);
9411
+ return {
9412
+ ok: true,
9413
+ comment
9414
+ };
9415
+ };
9416
+ /**
9358
9417
  * Cancel the pending comment
9359
9418
  *
9360
9419
  * @returns {void}
@@ -9555,7 +9614,7 @@ var useCommentsStore = defineStore("comments", () => {
9555
9614
  });
9556
9615
  commentsList.value = commentsList.value.filter((comment) => !removedComments.includes(comment));
9557
9616
  if (removedAliasIds.size) {
9558
- const nextPositions = { ...editorCommentPositions.value || {} };
9617
+ const nextPositions = { ...editorCommentPositions.value };
9559
9618
  removedAliasIds.forEach((id) => {
9560
9619
  delete nextPositions[id];
9561
9620
  });
@@ -11076,6 +11135,7 @@ var useCommentsStore = defineStore("comments", () => {
11076
11135
  suppressInternalExternal,
11077
11136
  pendingComment,
11078
11137
  currentCommentText,
11138
+ currentCommentMentions,
11079
11139
  commentsList,
11080
11140
  reviewDirectoryList,
11081
11141
  isCommentsListVisible,
@@ -11151,6 +11211,7 @@ var useCommentsStore = defineStore("comments", () => {
11151
11211
  getV2CommentsAdapter,
11152
11212
  applyReviewWindowFromV2,
11153
11213
  reconcileCommentsFromV2,
11214
+ announceV2CommentCreated,
11154
11215
  isV2EditorActive,
11155
11216
  replyCommentV2,
11156
11217
  editCommentV2,
@@ -11299,13 +11360,13 @@ function useUiFontFamily() {
11299
11360
  //#endregion
11300
11361
  //#region src/components/CommentsLayer/CommentsDropdown.vue
11301
11362
  var _hoisted_1$31 = { class: "comments-dropdown" };
11302
- var _hoisted_2$24 = ["onClick"];
11303
- var _hoisted_3$20 = {
11363
+ var _hoisted_2$25 = ["onClick"];
11364
+ var _hoisted_3$21 = {
11304
11365
  key: 0,
11305
11366
  class: "comments-dropdown__option-icon"
11306
11367
  };
11307
- var _hoisted_4$12 = ["innerHTML"];
11308
- var _hoisted_5$8 = { class: "comments-dropdown__option-label" };
11368
+ var _hoisted_4$13 = ["innerHTML"];
11369
+ var _hoisted_5$9 = { class: "comments-dropdown__option-label" };
11309
11370
  var _sfc_main$38 = {
11310
11371
  __name: "CommentsDropdown",
11311
11372
  props: {
@@ -11445,13 +11506,13 @@ var _sfc_main$38 = {
11445
11506
  key: option.key,
11446
11507
  class: (0, vue.normalizeClass)(["comments-dropdown__option", { "sd-disabled": option.disabled }]),
11447
11508
  onClick: ($event) => onOptionClick(option)
11448
- }, [hasIcon(option) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$20, [option.iconString ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
11509
+ }, [hasIcon(option) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$21, [option.iconString ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
11449
11510
  key: 0,
11450
11511
  innerHTML: option.iconString
11451
- }, null, 8, _hoisted_4$12)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(OptionIcon), {
11512
+ }, null, 8, _hoisted_4$13)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(OptionIcon), {
11452
11513
  key: 1,
11453
11514
  option
11454
- }, null, 8, ["option"]))])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("span", _hoisted_5$8, (0, vue.toDisplayString)(option.label), 1)], 10, _hoisted_2$24);
11515
+ }, null, 8, ["option"]))])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("span", _hoisted_5$9, (0, vue.toDisplayString)(option.label), 1)], 10, _hoisted_2$25);
11455
11516
  }), 128))], 4)) : (0, vue.createCommentVNode)("", true)]))]);
11456
11517
  };
11457
11518
  }
@@ -11460,9 +11521,9 @@ var CommentsDropdown_default = /*#__PURE__*/ require__plugin_vue_export_helper._
11460
11521
  //#endregion
11461
11522
  //#region src/components/CommentsLayer/InternalDropdown.vue
11462
11523
  var _hoisted_1$30 = { class: "sd-comment-option" };
11463
- var _hoisted_2$23 = ["innerHTML"];
11464
- var _hoisted_3$19 = { class: "sd-option-state" };
11465
- var _hoisted_4$11 = ["innerHTML"];
11524
+ var _hoisted_2$24 = ["innerHTML"];
11525
+ var _hoisted_3$20 = { class: "sd-option-state" };
11526
+ var _hoisted_4$12 = ["innerHTML"];
11466
11527
  var _sfc_main$37 = {
11467
11528
  __name: "InternalDropdown",
11468
11529
  props: {
@@ -11534,12 +11595,12 @@ var _sfc_main$37 = {
11534
11595
  (0, vue.createElementVNode)("div", {
11535
11596
  class: "sd-active-icon",
11536
11597
  innerHTML: activeIcon.value
11537
- }, null, 8, _hoisted_2$23),
11538
- (0, vue.createElementVNode)("div", _hoisted_3$19, (0, vue.toDisplayString)(getState.value), 1),
11598
+ }, null, 8, _hoisted_2$24),
11599
+ (0, vue.createElementVNode)("div", _hoisted_3$20, (0, vue.toDisplayString)(getState.value), 1),
11539
11600
  (0, vue.createElementVNode)("div", {
11540
11601
  class: "sd-dropdown-caret",
11541
11602
  innerHTML: (0, vue.unref)(superdocIcons).caretDown
11542
- }, null, 8, _hoisted_4$11)
11603
+ }, null, 8, _hoisted_4$12)
11543
11604
  ])]),
11544
11605
  _: 1
11545
11606
  }, 8, ["disabled", "content-style"])], 4);
@@ -11690,8 +11751,8 @@ var isAllowed = (permission, role, isInternal, context = {}) => {
11690
11751
  //#endregion
11691
11752
  //#region src/components/general/Avatar.vue
11692
11753
  var _hoisted_1$29 = { class: "user-container" };
11693
- var _hoisted_2$22 = ["src"];
11694
- var _hoisted_3$18 = {
11754
+ var _hoisted_2$23 = ["src"];
11755
+ var _hoisted_3$19 = {
11695
11756
  key: 1,
11696
11757
  class: "user-bg"
11697
11758
  };
@@ -11716,7 +11777,7 @@ var _sfc_main$36 = {
11716
11777
  key: 0,
11717
11778
  class: "user-bg",
11718
11779
  src: __props.user.image.startsWith("http") ? __props.user.image : `data:image/png;base64,${__props.user.image}`
11719
- }, null, 8, _hoisted_2$22)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$18, (0, vue.toDisplayString)(getInitials(__props.user.name, __props.user.email)), 1))]);
11780
+ }, null, 8, _hoisted_2$23)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$19, (0, vue.toDisplayString)(getInitials(__props.user.name, __props.user.email)), 1))]);
11720
11781
  };
11721
11782
  }
11722
11783
  };
@@ -11724,14 +11785,14 @@ var Avatar_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue
11724
11785
  //#endregion
11725
11786
  //#region src/components/CommentsLayer/CommentHeader.vue
11726
11787
  var _hoisted_1$28 = { class: "card-section comment-header" };
11727
- var _hoisted_2$21 = { class: "comment-header-left" };
11728
- var _hoisted_3$17 = { class: "user-info" };
11729
- var _hoisted_4$10 = { class: "user-name" };
11730
- var _hoisted_5$7 = {
11788
+ var _hoisted_2$22 = { class: "comment-header-left" };
11789
+ var _hoisted_3$18 = { class: "user-info" };
11790
+ var _hoisted_4$11 = { class: "user-name" };
11791
+ var _hoisted_5$8 = {
11731
11792
  key: 0,
11732
11793
  class: "imported-tag"
11733
11794
  };
11734
- var _hoisted_6$6 = {
11795
+ var _hoisted_6$7 = {
11735
11796
  key: 0,
11736
11797
  class: "user-timestamp"
11737
11798
  };
@@ -11956,10 +12017,10 @@ var _sfc_main$35 = {
11956
12017
  return user;
11957
12018
  });
11958
12019
  return (_ctx, _cache) => {
11959
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$28, [(0, vue.createElementVNode)("div", _hoisted_2$21, [(0, vue.createVNode)(Avatar_default, {
12020
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$28, [(0, vue.createElementVNode)("div", _hoisted_2$22, [(0, vue.createVNode)(Avatar_default, {
11960
12021
  user: getCurrentUser.value,
11961
12022
  class: "avatar"
11962
- }, null, 8, ["user"]), (0, vue.createElementVNode)("div", _hoisted_3$17, [(0, vue.createElementVNode)("div", _hoisted_4$10, [(0, vue.createTextVNode)((0, vue.toDisplayString)(getCurrentUser.value.name), 1), isImported.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$7, "IMPORTED")) : (0, vue.createCommentVNode)("", true)]), props.comment.createdTime ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$6, (0, vue.toDisplayString)((0, vue.unref)(formatDate)(props.comment.createdTime)), 1)) : (0, vue.createCommentVNode)("", true)])]), (0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["overflow-menu", { "is-visible": props.isActive }]) }, [
12023
+ }, null, 8, ["user"]), (0, vue.createElementVNode)("div", _hoisted_3$18, [(0, vue.createElementVNode)("div", _hoisted_4$11, [(0, vue.createTextVNode)((0, vue.toDisplayString)(getCurrentUser.value.name), 1), isImported.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$8, "IMPORTED")) : (0, vue.createCommentVNode)("", true)]), props.comment.createdTime ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$7, (0, vue.toDisplayString)((0, vue.unref)(formatDate)(props.comment.createdTime)), 1)) : (0, vue.createCommentVNode)("", true)])]), (0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["overflow-menu", { "is-visible": props.isActive }]) }, [
11963
12024
  allowResolve.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
11964
12025
  key: 0,
11965
12026
  class: (0, vue.normalizeClass)(["overflow-menu__icon", { "sd-is-disabled": Boolean(__props.resolveDisabledReason) }]),
@@ -12009,8 +12070,24 @@ var CommentHeader_default = /*#__PURE__*/ require__plugin_vue_export_helper._plu
12009
12070
  //#endregion
12010
12071
  //#region src/components/CommentsLayer/CommentInput.vue
12011
12072
  var _hoisted_1$27 = { class: "input-section" };
12012
- var TEXTAREA_MIN_HEIGHT = 28;
12013
- var TEXTAREA_MAX_HEIGHT = 132;
12073
+ var _hoisted_2$21 = { class: "comment-composer" };
12074
+ var _hoisted_3$17 = [
12075
+ "aria-expanded",
12076
+ "aria-controls",
12077
+ "aria-activedescendant"
12078
+ ];
12079
+ var _hoisted_4$10 = [
12080
+ "id",
12081
+ "aria-selected",
12082
+ "data-sd-comment-mention-option",
12083
+ "onMousedown"
12084
+ ];
12085
+ var _hoisted_5$7 = { class: "comment-mention-option__name" };
12086
+ var _hoisted_6$6 = {
12087
+ key: 0,
12088
+ class: "comment-mention-option__email"
12089
+ };
12090
+ var commentInputSequence = 0;
12014
12091
  var _sfc_main$34 = {
12015
12092
  __name: "CommentInput",
12016
12093
  props: {
@@ -12038,15 +12115,23 @@ var _sfc_main$34 = {
12038
12115
  },
12039
12116
  emits: ["focus"],
12040
12117
  setup(__props, { expose: __expose, emit: __emit }) {
12118
+ const TEXTAREA_MIN_HEIGHT = 28;
12119
+ const TEXTAREA_MAX_HEIGHT = 132;
12041
12120
  const emit = __emit;
12042
- const { currentCommentText } = storeToRefs(useCommentsStore());
12121
+ const props = __props;
12122
+ const { currentCommentText, currentCommentMentions } = storeToRefs(useCommentsStore());
12043
12123
  const inputRef = (0, vue.ref)(null);
12124
+ const mentionStart = (0, vue.ref)(null);
12125
+ const mentionQuery = (0, vue.ref)("");
12126
+ const highlightedMentionIndex = (0, vue.ref)(0);
12127
+ const mentionListId = `sd-comment-mention-list-${++commentInputSequence}`;
12044
12128
  const handleFocusChange = (focused) => emit("focus", focused);
12129
+ const getInputElement = () => inputRef.value;
12045
12130
  const focus = (options) => {
12046
- inputRef.value?.focus?.(options);
12131
+ getInputElement()?.focus?.(options);
12047
12132
  };
12048
12133
  const syncInputHeight = () => {
12049
- const input = inputRef.value;
12134
+ const input = getInputElement();
12050
12135
  if (!input) return;
12051
12136
  input.style.height = `${TEXTAREA_MIN_HEIGHT}px`;
12052
12137
  const scrollHeight = input.scrollHeight || TEXTAREA_MIN_HEIGHT;
@@ -12077,6 +12162,79 @@ var _sfc_main$34 = {
12077
12162
  currentCommentText.value = textToHtml(value);
12078
12163
  }
12079
12164
  });
12165
+ const displayName = (user) => user?.name?.trim() || user?.email?.trim() || "";
12166
+ const userKey = (user) => String(user?.id ?? user?.email ?? user?.name ?? "");
12167
+ const isViewer = (user) => user?.role === "viewer" || user?.access === "viewer" || typeof user?.access === "object" && user.access?.role === "viewer";
12168
+ const mentionIdentity = (user) => ({
12169
+ ...user?.id != null ? { id: user.id } : {},
12170
+ ...user?.name != null ? { name: user.name } : {},
12171
+ ...user?.email != null ? { email: user.email } : {}
12172
+ });
12173
+ const mentionSuggestions = (0, vue.computed)(() => {
12174
+ if (mentionStart.value == null) return [];
12175
+ const needle = mentionQuery.value.toLocaleLowerCase();
12176
+ return props.users.filter((user) => !isViewer(user) && displayName(user)).filter((user) => {
12177
+ const name = displayName(user).toLocaleLowerCase();
12178
+ const email = user?.email?.toLocaleLowerCase() ?? "";
12179
+ return !needle || name.startsWith(needle) || email.startsWith(needle);
12180
+ }).slice(0, 8);
12181
+ });
12182
+ const mentionListOpen = (0, vue.computed)(() => mentionSuggestions.value.length > 0);
12183
+ const activeMentionOptionId = (0, vue.computed)(() => {
12184
+ const user = mentionSuggestions.value[highlightedMentionIndex.value];
12185
+ return user ? `${mentionListId}-${userKey(user)}` : void 0;
12186
+ });
12187
+ const updateMentionQuery = (caret) => {
12188
+ const beforeCaret = commentDraft.value.slice(0, caret);
12189
+ const match = /(?:^|\s)@([^\s@]{0,40})$/.exec(beforeCaret);
12190
+ mentionStart.value = match ? caret - match[1].length - 1 : null;
12191
+ mentionQuery.value = match?.[1] ?? "";
12192
+ highlightedMentionIndex.value = 0;
12193
+ };
12194
+ const onInput = (event) => {
12195
+ currentCommentMentions.value = currentCommentMentions.value.filter((user) => commentDraft.value.includes(`@${displayName(user)}`));
12196
+ updateMentionQuery(event.target.selectionStart ?? commentDraft.value.length);
12197
+ syncInputHeight();
12198
+ };
12199
+ const selectMention = (user) => {
12200
+ if (mentionStart.value == null) return;
12201
+ const start = mentionStart.value;
12202
+ const caret = inputRef.value?.selectionStart ?? commentDraft.value.length;
12203
+ const token = `@${displayName(user)}`;
12204
+ const identity = mentionIdentity(user);
12205
+ commentDraft.value = `${commentDraft.value.slice(0, start)}${token}${commentDraft.value.slice(caret)}`;
12206
+ if (!currentCommentMentions.value.some((selected) => userKey(selected) === userKey(identity))) currentCommentMentions.value = [...currentCommentMentions.value, identity];
12207
+ mentionStart.value = null;
12208
+ mentionQuery.value = "";
12209
+ (0, vue.nextTick)(() => {
12210
+ const nextCaret = start + token.length;
12211
+ inputRef.value?.focus();
12212
+ inputRef.value?.setSelectionRange(nextCaret, nextCaret);
12213
+ syncInputHeight();
12214
+ });
12215
+ };
12216
+ const onKeydown = (event) => {
12217
+ if (!mentionListOpen.value) return;
12218
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
12219
+ event.preventDefault();
12220
+ event.stopPropagation();
12221
+ const direction = event.key === "ArrowDown" ? 1 : -1;
12222
+ highlightedMentionIndex.value = (highlightedMentionIndex.value + direction + mentionSuggestions.value.length) % mentionSuggestions.value.length;
12223
+ return;
12224
+ }
12225
+ if (event.key === "Enter") {
12226
+ event.preventDefault();
12227
+ event.stopPropagation();
12228
+ const user = mentionSuggestions.value[highlightedMentionIndex.value];
12229
+ if (user) selectMention(user);
12230
+ return;
12231
+ }
12232
+ if (event.key === "Escape") {
12233
+ event.preventDefault();
12234
+ event.stopPropagation();
12235
+ mentionStart.value = null;
12236
+ }
12237
+ };
12080
12238
  (0, vue.onMounted)(scheduleInputHeightSync);
12081
12239
  (0, vue.watch)(currentCommentText, scheduleInputHeightSync);
12082
12240
  __expose({ focus });
@@ -12086,21 +12244,46 @@ var _sfc_main$34 = {
12086
12244
  config: __props.config,
12087
12245
  comment: __props.comment,
12088
12246
  "is-pending-input": true
12089
- }, null, 8, ["config", "comment"])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["comment-entry", { "sd-input-active": __props.isFocused }]) }, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
12247
+ }, null, 8, ["config", "comment"])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["comment-entry", { "sd-input-active": __props.isFocused }]) }, [(0, vue.createElementVNode)("div", _hoisted_2$21, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
12090
12248
  ref_key: "inputRef",
12091
12249
  ref: inputRef,
12250
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => commentDraft.value = $event),
12092
12251
  class: "superdoc-field",
12252
+ role: "combobox",
12253
+ "aria-autocomplete": "list",
12254
+ "aria-expanded": mentionListOpen.value,
12255
+ "aria-controls": mentionListOpen.value ? mentionListId : void 0,
12256
+ "aria-activedescendant": activeMentionOptionId.value,
12257
+ "data-sd-comment-mention-input": "",
12258
+ "data-sd-comment-text": "",
12093
12259
  placeholder: "Add a comment",
12094
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => commentDraft.value = $event),
12095
12260
  rows: "1",
12096
- onInput: syncInputHeight,
12261
+ onInput,
12262
+ onKeydown,
12097
12263
  onFocus: _cache[1] || (_cache[1] = ($event) => handleFocusChange(true)),
12098
12264
  onBlur: _cache[2] || (_cache[2] = ($event) => handleFocusChange(false))
12099
- }, null, 544), [[vue.vModelText, commentDraft.value]])], 2)]);
12265
+ }, null, 40, _hoisted_3$17), [[vue.vModelText, commentDraft.value]]), mentionListOpen.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
12266
+ key: 0,
12267
+ id: mentionListId,
12268
+ class: "comment-mention-list",
12269
+ role: "listbox",
12270
+ "data-sd-comment-mention-list": ""
12271
+ }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(mentionSuggestions.value, (user, index) => {
12272
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
12273
+ id: `${mentionListId}-${userKey(user)}`,
12274
+ key: userKey(user),
12275
+ type: "button",
12276
+ class: (0, vue.normalizeClass)(["comment-mention-option", { "comment-mention-option--active": index === highlightedMentionIndex.value }]),
12277
+ role: "option",
12278
+ "aria-selected": index === highlightedMentionIndex.value,
12279
+ "data-sd-comment-mention-option": userKey(user),
12280
+ onMousedown: (0, vue.withModifiers)(($event) => selectMention(user), ["prevent"])
12281
+ }, [(0, vue.createElementVNode)("span", _hoisted_5$7, (0, vue.toDisplayString)(displayName(user)), 1), user.email ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_6$6, (0, vue.toDisplayString)(user.email), 1)) : (0, vue.createCommentVNode)("", true)], 42, _hoisted_4$10);
12282
+ }), 128))])) : (0, vue.createCommentVNode)("", true)])], 2)]);
12100
12283
  };
12101
12284
  }
12102
12285
  };
12103
- var CommentInput_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$34, [["__scopeId", "data-v-e281414b"]]);
12286
+ var CommentInput_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$34, [["__scopeId", "data-v-87953125"]]);
12104
12287
  //#endregion
12105
12288
  //#region src/components/CommentsLayer/CommentDialog.vue
12106
12289
  var _hoisted_1$26 = [
@@ -12255,7 +12438,7 @@ var _sfc_main$33 = {
12255
12438
  const superdocStore = useSuperdocStore();
12256
12439
  const commentsStore = useCommentsStore();
12257
12440
  const { addComment, cancelComment, deleteComment, getCommentAliasIds, removePendingComment, getCommentDocumentId, requestInstantSidebarAlignment, resolveCommentPositionEntry, clearInstantSidebarAlignment, setActiveFloatingCommentInstance } = commentsStore;
12258
- const { suppressInternalExternal, getConfig, activeComment, activeFloatingCommentInstanceId, floatingCommentsOffset, pendingComment, currentCommentText, isDebugging, editingCommentId, editorCommentPositions, isCommentHighlighted } = storeToRefs(commentsStore);
12441
+ const { suppressInternalExternal, getConfig, activeComment, activeFloatingCommentInstanceId, floatingCommentsOffset, pendingComment, currentCommentText, currentCommentMentions, isDebugging, editingCommentId, editorCommentPositions, isCommentHighlighted } = storeToRefs(commentsStore);
12259
12442
  const isInternal = (0, vue.ref)(true);
12260
12443
  const commentInput = (0, vue.ref)(null);
12261
12444
  const editCommentInputs = (0, vue.ref)(/* @__PURE__ */ new Map());
@@ -12499,6 +12682,7 @@ var _sfc_main$33 = {
12499
12682
  if (!active) {
12500
12683
  if (isReplying.value || isEditingCommentInThisThread()) {
12501
12684
  currentCommentText.value = "";
12685
+ currentCommentMentions.value = [];
12502
12686
  editingCommentId.value = null;
12503
12687
  }
12504
12688
  textExpanded.value = false;
@@ -12679,7 +12863,8 @@ var _sfc_main$33 = {
12679
12863
  const outcome = await commentsStore.replyCommentV2({
12680
12864
  superdoc: proxy.$superdoc,
12681
12865
  parentCommentId,
12682
- text: currentCommentText.value
12866
+ text: currentCommentText.value,
12867
+ ...currentCommentMentions.value.length ? { mentions: currentCommentMentions.value } : {}
12683
12868
  });
12684
12869
  if (!outcome?.ok) {
12685
12870
  (0, vue.nextTick)(() => emit("resize"));
@@ -12687,6 +12872,7 @@ var _sfc_main$33 = {
12687
12872
  }
12688
12873
  isReplying.value = false;
12689
12874
  currentCommentText.value = "";
12875
+ currentCommentMentions.value = [];
12690
12876
  (0, vue.nextTick)(() => emit("resize"));
12691
12877
  return outcome;
12692
12878
  } finally {
@@ -12958,6 +13144,7 @@ var _sfc_main$33 = {
12958
13144
  switch (value) {
12959
13145
  case "edit":
12960
13146
  currentCommentText.value = comment?.commentText?.value ?? comment?.commentText ?? "";
13147
+ currentCommentMentions.value = Array.isArray(comment?.mentions) ? [...comment.mentions] : [];
12961
13148
  activeComment.value = props.comment.commentId;
12962
13149
  if (props.floatingInstanceId) setActiveFloatingCommentInstance(props.floatingInstanceId);
12963
13150
  editingCommentId.value = comment.commentId;
@@ -13017,7 +13204,7 @@ var _sfc_main$33 = {
13017
13204
  };
13018
13205
  const usersFiltered = (0, vue.computed)(() => {
13019
13206
  const users = proxy.$superdoc.users;
13020
- if (props.comment.isInternal === true) return users.filter((user) => user.access?.role === "internal");
13207
+ if (props.comment.isInternal === true) return users.filter((user) => user.access === "internal" || user.access?.role === "internal");
13021
13208
  return users;
13022
13209
  });
13023
13210
  (0, vue.onMounted)(() => {
@@ -13208,15 +13395,11 @@ var _sfc_main$33 = {
13208
13395
  }, null, 10, _hoisted_36)) : (0, vue.unref)(isDebugging) && !isEditingThisComment.value(comment) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_37, (0, vue.toDisplayString)((0, vue.unref)(editorCommentPositions)[comment.importedId !== void 0 ? comment.importedId : comment.commentId]?.bounds), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_38, [(0, vue.createElementVNode)("div", _hoisted_39, [(0, vue.createVNode)(CommentInput_default, {
13209
13396
  ref_for: true,
13210
13397
  ref: setEditCommentInputRef(comment.commentId),
13211
- users: usersFiltered.value,
13398
+ users: [],
13212
13399
  config: (0, vue.unref)(getConfig),
13213
13400
  "include-header": false,
13214
13401
  comment
13215
- }, null, 8, [
13216
- "users",
13217
- "config",
13218
- "comment"
13219
- ])]), (0, vue.createElementVNode)("div", _hoisted_40, [(0, vue.createElementVNode)("button", {
13402
+ }, null, 8, ["config", "comment"])]), (0, vue.createElementVNode)("div", _hoisted_40, [(0, vue.createElementVNode)("button", {
13220
13403
  class: "sd-button reply-btn-cancel",
13221
13404
  onClick: (0, vue.withModifiers)(($event) => handleCancel(comment), ["stop", "prevent"])
13222
13405
  }, "Cancel", 8, _hoisted_41), (0, vue.createElementVNode)("button", {
@@ -13285,7 +13468,7 @@ var _sfc_main$33 = {
13285
13468
  };
13286
13469
  }
13287
13470
  };
13288
- var CommentDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$33, [["__scopeId", "data-v-969c9a8f"]]);
13471
+ var CommentDialog_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_vue_export_helper_default(_sfc_main$33, [["__scopeId", "data-v-f4c743f2"]]);
13289
13472
  //#endregion
13290
13473
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue
13291
13474
  var _hoisted_1$25 = [
@@ -21630,6 +21813,16 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
21630
21813
  const onV2LinkClick = (payload) => {
21631
21814
  linkPopover.handleLinkClick(payload);
21632
21815
  };
21816
+ const onV2CommentCreated = async (payload) => {
21817
+ try {
21818
+ await commentsStore.announceV2CommentCreated?.({
21819
+ superdoc: proxy.$superdoc,
21820
+ commentId: payload?.commentId
21821
+ });
21822
+ } catch (err) {
21823
+ console.warn("[SuperDoc][v2] context-menu comment reconciliation failed", err);
21824
+ }
21825
+ };
21633
21826
  const recollectV2GeometryIfActive = (options = void 0) => {
21634
21827
  if (!isV2Mode.value) return;
21635
21828
  if (!v2GeometryPublisher.getLastPayload()) return;
@@ -22732,6 +22925,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22732
22925
  onV2SelectionChanged,
22733
22926
  onV2HostEvent: (event) => onV2HostEvent(doc, event),
22734
22927
  onV2LinkClick,
22928
+ onV2CommentCreated,
22735
22929
  onV2PageMetrics
22736
22930
  }, null, 8, [
22737
22931
  "file-source",
@@ -22796,7 +22990,7 @@ var SuperDoc_default = /*#__PURE__*/ require__plugin_vue_export_helper._plugin_v
22796
22990
  ], 38);
22797
22991
  };
22798
22992
  }
22799
- }, [["__scopeId", "data-v-af3574db"]]);
22993
+ }, [["__scopeId", "data-v-85324013"]]);
22800
22994
  //#endregion
22801
22995
  //#region src/core/create-app.js
22802
22996
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -43283,7 +43477,7 @@ var SuperDoc = class extends require_eventemitter3.import_eventemitter3.default
43283
43477
  this.config.colors = shuffleArray(this.config.colors);
43284
43478
  this.userColorMap = /* @__PURE__ */ new Map();
43285
43479
  this.colorIndex = 0;
43286
- this.version = "2.6.0-next.7";
43480
+ this.version = "2.6.0-next.8";
43287
43481
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43288
43482
  this.superdocId = config.superdocId || require_uuid.v4();
43289
43483
  this.colors = this.config.colors ?? [];