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.
@@ -5693,7 +5693,7 @@ function useComment(params) {
5693
5693
  const fileType = params.fileType;
5694
5694
  const createdAtVersionNumber = params.createdAtVersionNumber;
5695
5695
  const isInternal = ref(params.isInternal !== void 0 ? params.isInternal : true);
5696
- const mentions = ref([]);
5696
+ const mentions = ref(Array.isArray(params.mentions) ? params.mentions : []);
5697
5697
  const commentElement = ref(null);
5698
5698
  const isFocused = ref(params.isFocused || false);
5699
5699
  const creatorId = ref(params.creatorId ?? null);
@@ -7253,6 +7253,7 @@ var useCommentsStore = defineStore("comments", () => {
7253
7253
  const overlappedIds = /* @__PURE__ */ new Set([]);
7254
7254
  const suppressInternalExternal = ref(true);
7255
7255
  const currentCommentText = ref("");
7256
+ const currentCommentMentions = ref([]);
7256
7257
  const commentsList = ref([]);
7257
7258
  const reviewDirectoryList = shallowRef([]);
7258
7259
  let priorTrackedChangeThreadIndex = /* @__PURE__ */ new Map();
@@ -8335,6 +8336,7 @@ var useCommentsStore = defineStore("comments", () => {
8335
8336
  const removePendingComment = (superdoc) => {
8336
8337
  const hadPending = !!pendingComment.value;
8337
8338
  currentCommentText.value = "";
8339
+ currentCommentMentions.value = [];
8338
8340
  pendingComment.value = null;
8339
8341
  pendingV2CommentTarget.value = null;
8340
8342
  superdocStore.selectionPosition = null;
@@ -8354,16 +8356,19 @@ var useCommentsStore = defineStore("comments", () => {
8354
8356
  const v2Adapter = !skipEditorUpdate && !isHydration ? getV2CommentsAdapter(superdoc) : null;
8355
8357
  if (v2Adapter && !comment.trackedChange) {
8356
8358
  const text = normalizeV2CommentDraftText(pendingComment.value ? currentCommentText.value : comment.commentText);
8359
+ const mentions = pendingComment.value ? currentCommentMentions.value : comment.mentions ?? [];
8357
8360
  const target = pendingComment.value ? pendingV2CommentTarget.value : null;
8358
8361
  const parentCommentId = comment.parentCommentId ? String(comment.parentCommentId) : pendingComment.value ? null : null;
8359
8362
  return (async () => {
8360
8363
  try {
8361
8364
  return await (parentCommentId ? v2Adapter.reply({
8362
8365
  parentCommentId,
8363
- text
8366
+ text,
8367
+ ...mentions.length ? { mentions } : {}
8364
8368
  }) : v2Adapter.commitPendingComment({
8365
8369
  text,
8366
- target
8370
+ target,
8371
+ ...mentions.length ? { mentions } : {}
8367
8372
  }));
8368
8373
  } catch (err) {
8369
8374
  return {
@@ -8824,7 +8829,7 @@ var useCommentsStore = defineStore("comments", () => {
8824
8829
  * - rejection preserves rows and emits a rejected `comments-update` event
8825
8830
  * - committed-but-refresh-failed surfaces honestly (no reconcile with `[]`)
8826
8831
  */
8827
- const replyCommentV2 = async ({ superdoc, parentCommentId, text } = {}) => {
8832
+ const replyCommentV2 = async ({ superdoc, parentCommentId, text, mentions = [] } = {}) => {
8828
8833
  if (commentsAreReadOnly()) return readOnlyMutationOutcome();
8829
8834
  const v2Adapter = getV2CommentsAdapter(superdoc);
8830
8835
  if (!v2Adapter) return {
@@ -8849,7 +8854,8 @@ var useCommentsStore = defineStore("comments", () => {
8849
8854
  fileId,
8850
8855
  operation: () => v2Adapter.reply({
8851
8856
  parentCommentId: normalizedParent,
8852
- text: plainText
8857
+ text: plainText,
8858
+ ...mentions.length ? { mentions } : {}
8853
8859
  }),
8854
8860
  eventType: COMMENT_EVENTS.ADD,
8855
8861
  rejectionFallbackReason: "v2-reply-failed",
@@ -9305,6 +9311,7 @@ var useCommentsStore = defineStore("comments", () => {
9305
9311
  };
9306
9312
  const applyUpdate = (existing, input) => {
9307
9313
  existing.commentText = input.commentText ?? existing.commentText;
9314
+ existing.mentions = Array.isArray(input.mentions) ? input.mentions : [];
9308
9315
  existing.isInternal = typeof input.isInternal === "boolean" ? input.isInternal : existing.isInternal;
9309
9316
  if (typeof input.resolvedTime === "number") {
9310
9317
  existing.resolvedTime = input.resolvedTimeWasSynthesized === true && typeof existing.resolvedTime === "number" ? existing.resolvedTime : input.resolvedTime;
@@ -9354,6 +9361,58 @@ var useCommentsStore = defineStore("comments", () => {
9354
9361
  return { added: addedComments[addedComments.length - 1] ?? null };
9355
9362
  });
9356
9363
  /**
9364
+ * Reconcile and publish a root comment created by the private V2 context menu.
9365
+ * That path owns selection capture, while this store remains the sole owner
9366
+ * of shared comment rows and public comments-update events.
9367
+ */
9368
+ const announceV2CommentCreated = async ({ superdoc, commentId } = {}) => {
9369
+ const id = normalizeCommentId(commentId);
9370
+ const adapter = getV2CommentsAdapter(superdoc);
9371
+ const commentsApi = superdoc?.activeEditor?.doc?.comments;
9372
+ if (!id) return {
9373
+ ok: false,
9374
+ reason: "comment-id-missing"
9375
+ };
9376
+ if (!adapter || !isCurrentV2CommentsAdapter(adapter)) return {
9377
+ ok: false,
9378
+ reason: "comments-adapter-stale"
9379
+ };
9380
+ if (typeof commentsApi?.get !== "function") return {
9381
+ ok: false,
9382
+ reason: "document-api-unavailable"
9383
+ };
9384
+ let item;
9385
+ try {
9386
+ item = await commentsApi.get({ commentId: id });
9387
+ } catch (err) {
9388
+ return {
9389
+ ok: false,
9390
+ reason: "comment-read-failed",
9391
+ detail: err?.message ?? String(err)
9392
+ };
9393
+ }
9394
+ if (!isCurrentV2CommentsAdapter(adapter)) return {
9395
+ ok: false,
9396
+ reason: "comments-adapter-stale"
9397
+ };
9398
+ const comment = reconcileCommentsFromV2({
9399
+ superdoc,
9400
+ adapter,
9401
+ documentId: adapter.documentId,
9402
+ items: [item],
9403
+ pruneStale: false
9404
+ })?.added ?? commentsList.value.find((candidate) => String(candidate?.commentId ?? "") === id || String(candidate?.importedId ?? "") === id) ?? null;
9405
+ const event = {
9406
+ type: COMMENT_EVENTS.ADD,
9407
+ comment: comment?.getValues?.() ?? null
9408
+ };
9409
+ superdoc?.emit?.("comments-update", event);
9410
+ return {
9411
+ ok: true,
9412
+ comment
9413
+ };
9414
+ };
9415
+ /**
9357
9416
  * Cancel the pending comment
9358
9417
  *
9359
9418
  * @returns {void}
@@ -9554,7 +9613,7 @@ var useCommentsStore = defineStore("comments", () => {
9554
9613
  });
9555
9614
  commentsList.value = commentsList.value.filter((comment) => !removedComments.includes(comment));
9556
9615
  if (removedAliasIds.size) {
9557
- const nextPositions = { ...editorCommentPositions.value || {} };
9616
+ const nextPositions = { ...editorCommentPositions.value };
9558
9617
  removedAliasIds.forEach((id) => {
9559
9618
  delete nextPositions[id];
9560
9619
  });
@@ -11075,6 +11134,7 @@ var useCommentsStore = defineStore("comments", () => {
11075
11134
  suppressInternalExternal,
11076
11135
  pendingComment,
11077
11136
  currentCommentText,
11137
+ currentCommentMentions,
11078
11138
  commentsList,
11079
11139
  reviewDirectoryList,
11080
11140
  isCommentsListVisible,
@@ -11150,6 +11210,7 @@ var useCommentsStore = defineStore("comments", () => {
11150
11210
  getV2CommentsAdapter,
11151
11211
  applyReviewWindowFromV2,
11152
11212
  reconcileCommentsFromV2,
11213
+ announceV2CommentCreated,
11153
11214
  isV2EditorActive,
11154
11215
  replyCommentV2,
11155
11216
  editCommentV2,
@@ -11298,13 +11359,13 @@ function useUiFontFamily() {
11298
11359
  //#endregion
11299
11360
  //#region src/components/CommentsLayer/CommentsDropdown.vue
11300
11361
  var _hoisted_1$31 = { class: "comments-dropdown" };
11301
- var _hoisted_2$24 = ["onClick"];
11302
- var _hoisted_3$20 = {
11362
+ var _hoisted_2$25 = ["onClick"];
11363
+ var _hoisted_3$21 = {
11303
11364
  key: 0,
11304
11365
  class: "comments-dropdown__option-icon"
11305
11366
  };
11306
- var _hoisted_4$12 = ["innerHTML"];
11307
- var _hoisted_5$8 = { class: "comments-dropdown__option-label" };
11367
+ var _hoisted_4$13 = ["innerHTML"];
11368
+ var _hoisted_5$9 = { class: "comments-dropdown__option-label" };
11308
11369
  var CommentsDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11309
11370
  __name: "CommentsDropdown",
11310
11371
  props: {
@@ -11444,13 +11505,13 @@ var CommentsDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11444
11505
  key: option.key,
11445
11506
  class: normalizeClass(["comments-dropdown__option", { "sd-disabled": option.disabled }]),
11446
11507
  onClick: ($event) => onOptionClick(option)
11447
- }, [hasIcon(option) ? (openBlock(), createElementBlock("span", _hoisted_3$20, [option.iconString ? (openBlock(), createElementBlock("span", {
11508
+ }, [hasIcon(option) ? (openBlock(), createElementBlock("span", _hoisted_3$21, [option.iconString ? (openBlock(), createElementBlock("span", {
11448
11509
  key: 0,
11449
11510
  innerHTML: option.iconString
11450
- }, null, 8, _hoisted_4$12)) : (openBlock(), createBlock(unref(OptionIcon), {
11511
+ }, null, 8, _hoisted_4$13)) : (openBlock(), createBlock(unref(OptionIcon), {
11451
11512
  key: 1,
11452
11513
  option
11453
- }, null, 8, ["option"]))])) : createCommentVNode("", true), createElementVNode("span", _hoisted_5$8, toDisplayString(option.label), 1)], 10, _hoisted_2$24);
11514
+ }, null, 8, ["option"]))])) : createCommentVNode("", true), createElementVNode("span", _hoisted_5$9, toDisplayString(option.label), 1)], 10, _hoisted_2$25);
11454
11515
  }), 128))], 4)) : createCommentVNode("", true)]))]);
11455
11516
  };
11456
11517
  }
@@ -11458,9 +11519,9 @@ var CommentsDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11458
11519
  //#endregion
11459
11520
  //#region src/components/CommentsLayer/InternalDropdown.vue
11460
11521
  var _hoisted_1$30 = { class: "sd-comment-option" };
11461
- var _hoisted_2$23 = ["innerHTML"];
11462
- var _hoisted_3$19 = { class: "sd-option-state" };
11463
- var _hoisted_4$11 = ["innerHTML"];
11522
+ var _hoisted_2$24 = ["innerHTML"];
11523
+ var _hoisted_3$20 = { class: "sd-option-state" };
11524
+ var _hoisted_4$12 = ["innerHTML"];
11464
11525
  var InternalDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11465
11526
  __name: "InternalDropdown",
11466
11527
  props: {
@@ -11532,12 +11593,12 @@ var InternalDropdown_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11532
11593
  createElementVNode("div", {
11533
11594
  class: "sd-active-icon",
11534
11595
  innerHTML: activeIcon.value
11535
- }, null, 8, _hoisted_2$23),
11536
- createElementVNode("div", _hoisted_3$19, toDisplayString(getState.value), 1),
11596
+ }, null, 8, _hoisted_2$24),
11597
+ createElementVNode("div", _hoisted_3$20, toDisplayString(getState.value), 1),
11537
11598
  createElementVNode("div", {
11538
11599
  class: "sd-dropdown-caret",
11539
11600
  innerHTML: unref(superdocIcons).caretDown
11540
- }, null, 8, _hoisted_4$11)
11601
+ }, null, 8, _hoisted_4$12)
11541
11602
  ])]),
11542
11603
  _: 1
11543
11604
  }, 8, ["disabled", "content-style"])], 4);
@@ -11687,8 +11748,8 @@ var isAllowed = (permission, role, isInternal, context = {}) => {
11687
11748
  //#endregion
11688
11749
  //#region src/components/general/Avatar.vue
11689
11750
  var _hoisted_1$29 = { class: "user-container" };
11690
- var _hoisted_2$22 = ["src"];
11691
- var _hoisted_3$18 = {
11751
+ var _hoisted_2$23 = ["src"];
11752
+ var _hoisted_3$19 = {
11692
11753
  key: 1,
11693
11754
  class: "user-bg"
11694
11755
  };
@@ -11713,21 +11774,21 @@ var Avatar_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11713
11774
  key: 0,
11714
11775
  class: "user-bg",
11715
11776
  src: __props.user.image.startsWith("http") ? __props.user.image : `data:image/png;base64,${__props.user.image}`
11716
- }, null, 8, _hoisted_2$22)) : (openBlock(), createElementBlock("span", _hoisted_3$18, toDisplayString(getInitials(__props.user.name, __props.user.email)), 1))]);
11777
+ }, null, 8, _hoisted_2$23)) : (openBlock(), createElementBlock("span", _hoisted_3$19, toDisplayString(getInitials(__props.user.name, __props.user.email)), 1))]);
11717
11778
  };
11718
11779
  }
11719
11780
  }, [["__scopeId", "data-v-c95b2073"]]);
11720
11781
  //#endregion
11721
11782
  //#region src/components/CommentsLayer/CommentHeader.vue
11722
11783
  var _hoisted_1$28 = { class: "card-section comment-header" };
11723
- var _hoisted_2$21 = { class: "comment-header-left" };
11724
- var _hoisted_3$17 = { class: "user-info" };
11725
- var _hoisted_4$10 = { class: "user-name" };
11726
- var _hoisted_5$7 = {
11784
+ var _hoisted_2$22 = { class: "comment-header-left" };
11785
+ var _hoisted_3$18 = { class: "user-info" };
11786
+ var _hoisted_4$11 = { class: "user-name" };
11787
+ var _hoisted_5$8 = {
11727
11788
  key: 0,
11728
11789
  class: "imported-tag"
11729
11790
  };
11730
- var _hoisted_6$6 = {
11791
+ var _hoisted_6$7 = {
11731
11792
  key: 0,
11732
11793
  class: "user-timestamp"
11733
11794
  };
@@ -11952,10 +12013,10 @@ var CommentHeader_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
11952
12013
  return user;
11953
12014
  });
11954
12015
  return (_ctx, _cache) => {
11955
- return openBlock(), createElementBlock("div", _hoisted_1$28, [createElementVNode("div", _hoisted_2$21, [createVNode(Avatar_default, {
12016
+ return openBlock(), createElementBlock("div", _hoisted_1$28, [createElementVNode("div", _hoisted_2$22, [createVNode(Avatar_default, {
11956
12017
  user: getCurrentUser.value,
11957
12018
  class: "avatar"
11958
- }, null, 8, ["user"]), createElementVNode("div", _hoisted_3$17, [createElementVNode("div", _hoisted_4$10, [createTextVNode(toDisplayString(getCurrentUser.value.name), 1), isImported.value ? (openBlock(), createElementBlock("span", _hoisted_5$7, "IMPORTED")) : createCommentVNode("", true)]), props.comment.createdTime ? (openBlock(), createElementBlock("div", _hoisted_6$6, toDisplayString(unref(formatDate)(props.comment.createdTime)), 1)) : createCommentVNode("", true)])]), createElementVNode("div", { class: normalizeClass(["overflow-menu", { "is-visible": props.isActive }]) }, [
12019
+ }, null, 8, ["user"]), createElementVNode("div", _hoisted_3$18, [createElementVNode("div", _hoisted_4$11, [createTextVNode(toDisplayString(getCurrentUser.value.name), 1), isImported.value ? (openBlock(), createElementBlock("span", _hoisted_5$8, "IMPORTED")) : createCommentVNode("", true)]), props.comment.createdTime ? (openBlock(), createElementBlock("div", _hoisted_6$7, toDisplayString(unref(formatDate)(props.comment.createdTime)), 1)) : createCommentVNode("", true)])]), createElementVNode("div", { class: normalizeClass(["overflow-menu", { "is-visible": props.isActive }]) }, [
11959
12020
  allowResolve.value ? (openBlock(), createElementBlock("div", {
11960
12021
  key: 0,
11961
12022
  class: normalizeClass(["overflow-menu__icon", { "sd-is-disabled": Boolean(__props.resolveDisabledReason) }]),
@@ -12004,8 +12065,24 @@ var CommentHeader_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12004
12065
  //#endregion
12005
12066
  //#region src/components/CommentsLayer/CommentInput.vue
12006
12067
  var _hoisted_1$27 = { class: "input-section" };
12007
- var TEXTAREA_MIN_HEIGHT = 28;
12008
- var TEXTAREA_MAX_HEIGHT = 132;
12068
+ var _hoisted_2$21 = { class: "comment-composer" };
12069
+ var _hoisted_3$17 = [
12070
+ "aria-expanded",
12071
+ "aria-controls",
12072
+ "aria-activedescendant"
12073
+ ];
12074
+ var _hoisted_4$10 = [
12075
+ "id",
12076
+ "aria-selected",
12077
+ "data-sd-comment-mention-option",
12078
+ "onMousedown"
12079
+ ];
12080
+ var _hoisted_5$7 = { class: "comment-mention-option__name" };
12081
+ var _hoisted_6$6 = {
12082
+ key: 0,
12083
+ class: "comment-mention-option__email"
12084
+ };
12085
+ var commentInputSequence = 0;
12009
12086
  var CommentInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12010
12087
  __name: "CommentInput",
12011
12088
  props: {
@@ -12033,15 +12110,23 @@ var CommentInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12033
12110
  },
12034
12111
  emits: ["focus"],
12035
12112
  setup(__props, { expose: __expose, emit: __emit }) {
12113
+ const TEXTAREA_MIN_HEIGHT = 28;
12114
+ const TEXTAREA_MAX_HEIGHT = 132;
12036
12115
  const emit = __emit;
12037
- const { currentCommentText } = storeToRefs(useCommentsStore());
12116
+ const props = __props;
12117
+ const { currentCommentText, currentCommentMentions } = storeToRefs(useCommentsStore());
12038
12118
  const inputRef = ref(null);
12119
+ const mentionStart = ref(null);
12120
+ const mentionQuery = ref("");
12121
+ const highlightedMentionIndex = ref(0);
12122
+ const mentionListId = `sd-comment-mention-list-${++commentInputSequence}`;
12039
12123
  const handleFocusChange = (focused) => emit("focus", focused);
12124
+ const getInputElement = () => inputRef.value;
12040
12125
  const focus = (options) => {
12041
- inputRef.value?.focus?.(options);
12126
+ getInputElement()?.focus?.(options);
12042
12127
  };
12043
12128
  const syncInputHeight = () => {
12044
- const input = inputRef.value;
12129
+ const input = getInputElement();
12045
12130
  if (!input) return;
12046
12131
  input.style.height = `${TEXTAREA_MIN_HEIGHT}px`;
12047
12132
  const scrollHeight = input.scrollHeight || TEXTAREA_MIN_HEIGHT;
@@ -12072,6 +12157,79 @@ var CommentInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12072
12157
  currentCommentText.value = textToHtml(value);
12073
12158
  }
12074
12159
  });
12160
+ const displayName = (user) => user?.name?.trim() || user?.email?.trim() || "";
12161
+ const userKey = (user) => String(user?.id ?? user?.email ?? user?.name ?? "");
12162
+ const isViewer = (user) => user?.role === "viewer" || user?.access === "viewer" || typeof user?.access === "object" && user.access?.role === "viewer";
12163
+ const mentionIdentity = (user) => ({
12164
+ ...user?.id != null ? { id: user.id } : {},
12165
+ ...user?.name != null ? { name: user.name } : {},
12166
+ ...user?.email != null ? { email: user.email } : {}
12167
+ });
12168
+ const mentionSuggestions = computed(() => {
12169
+ if (mentionStart.value == null) return [];
12170
+ const needle = mentionQuery.value.toLocaleLowerCase();
12171
+ return props.users.filter((user) => !isViewer(user) && displayName(user)).filter((user) => {
12172
+ const name = displayName(user).toLocaleLowerCase();
12173
+ const email = user?.email?.toLocaleLowerCase() ?? "";
12174
+ return !needle || name.startsWith(needle) || email.startsWith(needle);
12175
+ }).slice(0, 8);
12176
+ });
12177
+ const mentionListOpen = computed(() => mentionSuggestions.value.length > 0);
12178
+ const activeMentionOptionId = computed(() => {
12179
+ const user = mentionSuggestions.value[highlightedMentionIndex.value];
12180
+ return user ? `${mentionListId}-${userKey(user)}` : void 0;
12181
+ });
12182
+ const updateMentionQuery = (caret) => {
12183
+ const beforeCaret = commentDraft.value.slice(0, caret);
12184
+ const match = /(?:^|\s)@([^\s@]{0,40})$/.exec(beforeCaret);
12185
+ mentionStart.value = match ? caret - match[1].length - 1 : null;
12186
+ mentionQuery.value = match?.[1] ?? "";
12187
+ highlightedMentionIndex.value = 0;
12188
+ };
12189
+ const onInput = (event) => {
12190
+ currentCommentMentions.value = currentCommentMentions.value.filter((user) => commentDraft.value.includes(`@${displayName(user)}`));
12191
+ updateMentionQuery(event.target.selectionStart ?? commentDraft.value.length);
12192
+ syncInputHeight();
12193
+ };
12194
+ const selectMention = (user) => {
12195
+ if (mentionStart.value == null) return;
12196
+ const start = mentionStart.value;
12197
+ const caret = inputRef.value?.selectionStart ?? commentDraft.value.length;
12198
+ const token = `@${displayName(user)}`;
12199
+ const identity = mentionIdentity(user);
12200
+ commentDraft.value = `${commentDraft.value.slice(0, start)}${token}${commentDraft.value.slice(caret)}`;
12201
+ if (!currentCommentMentions.value.some((selected) => userKey(selected) === userKey(identity))) currentCommentMentions.value = [...currentCommentMentions.value, identity];
12202
+ mentionStart.value = null;
12203
+ mentionQuery.value = "";
12204
+ nextTick(() => {
12205
+ const nextCaret = start + token.length;
12206
+ inputRef.value?.focus();
12207
+ inputRef.value?.setSelectionRange(nextCaret, nextCaret);
12208
+ syncInputHeight();
12209
+ });
12210
+ };
12211
+ const onKeydown = (event) => {
12212
+ if (!mentionListOpen.value) return;
12213
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
12214
+ event.preventDefault();
12215
+ event.stopPropagation();
12216
+ const direction = event.key === "ArrowDown" ? 1 : -1;
12217
+ highlightedMentionIndex.value = (highlightedMentionIndex.value + direction + mentionSuggestions.value.length) % mentionSuggestions.value.length;
12218
+ return;
12219
+ }
12220
+ if (event.key === "Enter") {
12221
+ event.preventDefault();
12222
+ event.stopPropagation();
12223
+ const user = mentionSuggestions.value[highlightedMentionIndex.value];
12224
+ if (user) selectMention(user);
12225
+ return;
12226
+ }
12227
+ if (event.key === "Escape") {
12228
+ event.preventDefault();
12229
+ event.stopPropagation();
12230
+ mentionStart.value = null;
12231
+ }
12232
+ };
12075
12233
  onMounted(scheduleInputHeightSync);
12076
12234
  watch(currentCommentText, scheduleInputHeightSync);
12077
12235
  __expose({ focus });
@@ -12081,20 +12239,45 @@ var CommentInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12081
12239
  config: __props.config,
12082
12240
  comment: __props.comment,
12083
12241
  "is-pending-input": true
12084
- }, null, 8, ["config", "comment"])) : createCommentVNode("", true), createElementVNode("div", { class: normalizeClass(["comment-entry", { "sd-input-active": __props.isFocused }]) }, [withDirectives(createElementVNode("textarea", {
12242
+ }, null, 8, ["config", "comment"])) : createCommentVNode("", true), createElementVNode("div", { class: normalizeClass(["comment-entry", { "sd-input-active": __props.isFocused }]) }, [createElementVNode("div", _hoisted_2$21, [withDirectives(createElementVNode("textarea", {
12085
12243
  ref_key: "inputRef",
12086
12244
  ref: inputRef,
12245
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => commentDraft.value = $event),
12087
12246
  class: "superdoc-field",
12247
+ role: "combobox",
12248
+ "aria-autocomplete": "list",
12249
+ "aria-expanded": mentionListOpen.value,
12250
+ "aria-controls": mentionListOpen.value ? mentionListId : void 0,
12251
+ "aria-activedescendant": activeMentionOptionId.value,
12252
+ "data-sd-comment-mention-input": "",
12253
+ "data-sd-comment-text": "",
12088
12254
  placeholder: "Add a comment",
12089
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => commentDraft.value = $event),
12090
12255
  rows: "1",
12091
- onInput: syncInputHeight,
12256
+ onInput,
12257
+ onKeydown,
12092
12258
  onFocus: _cache[1] || (_cache[1] = ($event) => handleFocusChange(true)),
12093
12259
  onBlur: _cache[2] || (_cache[2] = ($event) => handleFocusChange(false))
12094
- }, null, 544), [[vModelText, commentDraft.value]])], 2)]);
12260
+ }, null, 40, _hoisted_3$17), [[vModelText, commentDraft.value]]), mentionListOpen.value ? (openBlock(), createElementBlock("div", {
12261
+ key: 0,
12262
+ id: mentionListId,
12263
+ class: "comment-mention-list",
12264
+ role: "listbox",
12265
+ "data-sd-comment-mention-list": ""
12266
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(mentionSuggestions.value, (user, index) => {
12267
+ return openBlock(), createElementBlock("button", {
12268
+ id: `${mentionListId}-${userKey(user)}`,
12269
+ key: userKey(user),
12270
+ type: "button",
12271
+ class: normalizeClass(["comment-mention-option", { "comment-mention-option--active": index === highlightedMentionIndex.value }]),
12272
+ role: "option",
12273
+ "aria-selected": index === highlightedMentionIndex.value,
12274
+ "data-sd-comment-mention-option": userKey(user),
12275
+ onMousedown: withModifiers(($event) => selectMention(user), ["prevent"])
12276
+ }, [createElementVNode("span", _hoisted_5$7, toDisplayString(displayName(user)), 1), user.email ? (openBlock(), createElementBlock("span", _hoisted_6$6, toDisplayString(user.email), 1)) : createCommentVNode("", true)], 42, _hoisted_4$10);
12277
+ }), 128))])) : createCommentVNode("", true)])], 2)]);
12095
12278
  };
12096
12279
  }
12097
- }, [["__scopeId", "data-v-e281414b"]]);
12280
+ }, [["__scopeId", "data-v-87953125"]]);
12098
12281
  //#endregion
12099
12282
  //#region src/components/CommentsLayer/CommentDialog.vue
12100
12283
  var _hoisted_1$26 = [
@@ -12249,7 +12432,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12249
12432
  const superdocStore = useSuperdocStore();
12250
12433
  const commentsStore = useCommentsStore();
12251
12434
  const { addComment, cancelComment, deleteComment, getCommentAliasIds, removePendingComment, getCommentDocumentId, requestInstantSidebarAlignment, resolveCommentPositionEntry, clearInstantSidebarAlignment, setActiveFloatingCommentInstance } = commentsStore;
12252
- const { suppressInternalExternal, getConfig, activeComment, activeFloatingCommentInstanceId, floatingCommentsOffset, pendingComment, currentCommentText, isDebugging, editingCommentId, editorCommentPositions, isCommentHighlighted } = storeToRefs(commentsStore);
12435
+ const { suppressInternalExternal, getConfig, activeComment, activeFloatingCommentInstanceId, floatingCommentsOffset, pendingComment, currentCommentText, currentCommentMentions, isDebugging, editingCommentId, editorCommentPositions, isCommentHighlighted } = storeToRefs(commentsStore);
12253
12436
  const isInternal = ref(true);
12254
12437
  const commentInput = ref(null);
12255
12438
  const editCommentInputs = ref(/* @__PURE__ */ new Map());
@@ -12493,6 +12676,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12493
12676
  if (!active) {
12494
12677
  if (isReplying.value || isEditingCommentInThisThread()) {
12495
12678
  currentCommentText.value = "";
12679
+ currentCommentMentions.value = [];
12496
12680
  editingCommentId.value = null;
12497
12681
  }
12498
12682
  textExpanded.value = false;
@@ -12673,7 +12857,8 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12673
12857
  const outcome = await commentsStore.replyCommentV2({
12674
12858
  superdoc: proxy.$superdoc,
12675
12859
  parentCommentId,
12676
- text: currentCommentText.value
12860
+ text: currentCommentText.value,
12861
+ ...currentCommentMentions.value.length ? { mentions: currentCommentMentions.value } : {}
12677
12862
  });
12678
12863
  if (!outcome?.ok) {
12679
12864
  nextTick(() => emit("resize"));
@@ -12681,6 +12866,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12681
12866
  }
12682
12867
  isReplying.value = false;
12683
12868
  currentCommentText.value = "";
12869
+ currentCommentMentions.value = [];
12684
12870
  nextTick(() => emit("resize"));
12685
12871
  return outcome;
12686
12872
  } finally {
@@ -12952,6 +13138,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
12952
13138
  switch (value) {
12953
13139
  case "edit":
12954
13140
  currentCommentText.value = comment?.commentText?.value ?? comment?.commentText ?? "";
13141
+ currentCommentMentions.value = Array.isArray(comment?.mentions) ? [...comment.mentions] : [];
12955
13142
  activeComment.value = props.comment.commentId;
12956
13143
  if (props.floatingInstanceId) setActiveFloatingCommentInstance(props.floatingInstanceId);
12957
13144
  editingCommentId.value = comment.commentId;
@@ -13011,7 +13198,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
13011
13198
  };
13012
13199
  const usersFiltered = computed(() => {
13013
13200
  const users = proxy.$superdoc.users;
13014
- if (props.comment.isInternal === true) return users.filter((user) => user.access?.role === "internal");
13201
+ if (props.comment.isInternal === true) return users.filter((user) => user.access === "internal" || user.access?.role === "internal");
13015
13202
  return users;
13016
13203
  });
13017
13204
  onMounted(() => {
@@ -13202,15 +13389,11 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
13202
13389
  }, null, 10, _hoisted_36)) : unref(isDebugging) && !isEditingThisComment.value(comment) ? (openBlock(), createElementBlock("div", _hoisted_37, toDisplayString(unref(editorCommentPositions)[comment.importedId !== void 0 ? comment.importedId : comment.commentId]?.bounds), 1)) : (openBlock(), createElementBlock("div", _hoisted_38, [createElementVNode("div", _hoisted_39, [createVNode(CommentInput_default, {
13203
13390
  ref_for: true,
13204
13391
  ref: setEditCommentInputRef(comment.commentId),
13205
- users: usersFiltered.value,
13392
+ users: [],
13206
13393
  config: unref(getConfig),
13207
13394
  "include-header": false,
13208
13395
  comment
13209
- }, null, 8, [
13210
- "users",
13211
- "config",
13212
- "comment"
13213
- ])]), createElementVNode("div", _hoisted_40, [createElementVNode("button", {
13396
+ }, null, 8, ["config", "comment"])]), createElementVNode("div", _hoisted_40, [createElementVNode("button", {
13214
13397
  class: "sd-button reply-btn-cancel",
13215
13398
  onClick: withModifiers(($event) => handleCancel(comment), ["stop", "prevent"])
13216
13399
  }, "Cancel", 8, _hoisted_41), createElementVNode("button", {
@@ -13278,7 +13461,7 @@ var CommentDialog_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
13278
13461
  ], 64))], 14, _hoisted_1$26)), [[_directive_click_outside, handleClickOutside]]);
13279
13462
  };
13280
13463
  }
13281
- }, [["__scopeId", "data-v-969c9a8f"]]);
13464
+ }, [["__scopeId", "data-v-f4c743f2"]]);
13282
13465
  //#endregion
13283
13466
  //#region src/components/CommentsLayer/commentsList/ReviewDirectoryListItem.vue
13284
13467
  var _hoisted_1$25 = [
@@ -21590,6 +21773,16 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
21590
21773
  const onV2LinkClick = (payload) => {
21591
21774
  linkPopover.handleLinkClick(payload);
21592
21775
  };
21776
+ const onV2CommentCreated = async (payload) => {
21777
+ try {
21778
+ await commentsStore.announceV2CommentCreated?.({
21779
+ superdoc: proxy.$superdoc,
21780
+ commentId: payload?.commentId
21781
+ });
21782
+ } catch (err) {
21783
+ console.warn("[SuperDoc][v2] context-menu comment reconciliation failed", err);
21784
+ }
21785
+ };
21593
21786
  const recollectV2GeometryIfActive = (options = void 0) => {
21594
21787
  if (!isV2Mode.value) return;
21595
21788
  if (!v2GeometryPublisher.getLastPayload()) return;
@@ -22692,6 +22885,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22692
22885
  onV2SelectionChanged,
22693
22886
  onV2HostEvent: (event) => onV2HostEvent(doc, event),
22694
22887
  onV2LinkClick,
22888
+ onV2CommentCreated,
22695
22889
  onV2PageMetrics
22696
22890
  }, null, 8, [
22697
22891
  "file-source",
@@ -22756,7 +22950,7 @@ var SuperDoc_default = /*#__PURE__*/ _plugin_vue_export_helper_default({
22756
22950
  ], 38);
22757
22951
  };
22758
22952
  }
22759
- }, [["__scopeId", "data-v-af3574db"]]);
22953
+ }, [["__scopeId", "data-v-85324013"]]);
22760
22954
  //#endregion
22761
22955
  //#region src/core/create-app.js
22762
22956
  var PINIA_DEVTOOLS_SETUP_EVENT = "devtools-plugin:setup";
@@ -43216,7 +43410,7 @@ var SuperDoc = class extends import_eventemitter3.default {
43216
43410
  this.config.colors = shuffleArray(this.config.colors);
43217
43411
  this.userColorMap = /* @__PURE__ */ new Map();
43218
43412
  this.colorIndex = 0;
43219
- this.version = "2.6.0-next.7";
43413
+ this.version = "2.6.0-next.8";
43220
43414
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
43221
43415
  this.superdocId = config.superdocId || v4();
43222
43416
  this.colors = this.config.colors ?? [];