@harbour-enterprises/superdoc 0.24.0 → 0.25.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/chunks/{PdfViewer-qk_hITc5.cjs → PdfViewer-CEc5ST9t.cjs} +1 -1
  2. package/dist/chunks/{PdfViewer-BiHh3x6b.es.js → PdfViewer-CoEXViCu.es.js} +1 -1
  3. package/dist/chunks/{index-Di6nG2sc.cjs → index-ChgYIPZ8.cjs} +166 -76
  4. package/dist/chunks/{index-aMnFs35I.es.js → index-DtXgSPMT.es.js} +166 -76
  5. package/dist/chunks/{super-editor.es-BKZvTraR.cjs → super-editor.es-B-E_YaLO.cjs} +163 -18
  6. package/dist/chunks/{super-editor.es-Dz7y81Yr.es.js → super-editor.es-BePxEtE8.es.js} +163 -18
  7. package/dist/core/SuperDoc.d.ts +20 -0
  8. package/dist/core/SuperDoc.d.ts.map +1 -1
  9. package/dist/core/collaboration/permissions.d.ts +7 -1
  10. package/dist/core/collaboration/permissions.d.ts.map +1 -1
  11. package/dist/core/types/index.d.ts +23 -1
  12. package/dist/core/types/index.d.ts.map +1 -1
  13. package/dist/style.css +55 -55
  14. package/dist/super-editor/ai-writer.es.js +2 -2
  15. package/dist/super-editor/chunks/{converter-CI3WqmGV.js → converter-gSy6s2VK.js} +1 -1
  16. package/dist/super-editor/chunks/{docx-zipper-D4fk50d9.js → docx-zipper-CceGxV02.js} +1 -1
  17. package/dist/super-editor/chunks/{editor-DC6pZVp1.js → editor-yaef8OIs.js} +122 -19
  18. package/dist/super-editor/chunks/{toolbar-DMobfM6u.js → toolbar-Dc5uyyIp.js} +2 -2
  19. package/dist/super-editor/converter.es.js +1 -1
  20. package/dist/super-editor/docx-zipper.es.js +2 -2
  21. package/dist/super-editor/editor.es.js +3 -3
  22. package/dist/super-editor/file-zipper.es.js +1 -1
  23. package/dist/super-editor/super-editor/src/components/slash-menu/tests/testHelpers.d.ts +1 -0
  24. package/dist/super-editor/super-editor/src/core/Editor.d.ts +18 -0
  25. package/dist/super-editor/super-editor/src/extensions/track-changes/permission-helpers.d.ts +110 -0
  26. package/dist/super-editor/super-editor/src/extensions/track-changes/trackChangesHelpers/markDeletion.d.ts +1 -1
  27. package/dist/super-editor/super-editor.es.js +66 -21
  28. package/dist/super-editor/toolbar.es.js +2 -2
  29. package/dist/super-editor.cjs +1 -1
  30. package/dist/super-editor.es.js +1 -1
  31. package/dist/superdoc.cjs +2 -2
  32. package/dist/superdoc.es.js +2 -2
  33. package/dist/superdoc.umd.js +327 -92
  34. package/dist/superdoc.umd.js.map +1 -1
  35. package/package.json +1 -1
@@ -42839,7 +42839,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
42839
42839
  static getStoredSuperdocVersion(docx) {
42840
42840
  return _SuperConverter2.getStoredCustomProperty(docx, "SuperdocVersion");
42841
42841
  }
42842
- static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "0.23.0") {
42842
+ static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "0.24.0") {
42843
42843
  return _SuperConverter2.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
42844
42844
  }
42845
42845
  /**
@@ -57052,7 +57052,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
57052
57052
  });
57053
57053
  return insertionMark;
57054
57054
  };
57055
- const markDeletion = ({ tr, from: from2, to, user, date }) => {
57055
+ const markDeletion = ({ tr, from: from2, to, user, date, id: providedId }) => {
57056
57056
  let trackedMark = findTrackedMarkBetween({
57057
57057
  tr,
57058
57058
  from: from2,
@@ -57061,7 +57061,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
57061
57061
  attrs: { authorEmail: user.email }
57062
57062
  });
57063
57063
  let id;
57064
- if (trackedMark) {
57064
+ if (providedId) {
57065
+ id = providedId;
57066
+ } else if (trackedMark) {
57065
57067
  id = trackedMark.mark.attrs.id;
57066
57068
  } else {
57067
57069
  id = v4$1();
@@ -58333,7 +58335,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
58333
58335
  from: step.from,
58334
58336
  to: step.to,
58335
58337
  user,
58336
- date
58338
+ date,
58339
+ id: meta.insertedMark?.attrs?.id
58337
58340
  });
58338
58341
  meta.deletionNodes = deletionNodes;
58339
58342
  meta.deletionMark = deletionMark2;
@@ -60261,7 +60264,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
60261
60264
  customUpdatedFiles: {},
60262
60265
  isHeaderFooterChanged: false,
60263
60266
  isCustomXmlChanged: false,
60264
- focusTarget: null
60267
+ focusTarget: null,
60268
+ permissionResolver: null
60265
60269
  });
60266
60270
  __privateMethod$1(this, _Editor_instances, initContainerElement_fn).call(this, options);
60267
60271
  __privateMethod$1(this, _Editor_instances, checkHeadless_fn).call(this, options);
@@ -61023,7 +61027,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61023
61027
  * @returns {Object | void} Migration results
61024
61028
  */
61025
61029
  processCollaborationMigrations() {
61026
- console.debug("[checkVersionMigrations] Current editor version", "0.23.0");
61030
+ console.debug("[checkVersionMigrations] Current editor version", "0.24.0");
61027
61031
  if (!this.options.ydoc) return;
61028
61032
  const metaMap = this.options.ydoc.getMap("meta");
61029
61033
  let docVersion = metaMap.get("version");
@@ -75019,11 +75023,105 @@ Please report this to https://github.com/markedjs/marked.`, e) {
75019
75023
  return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
75020
75024
  }
75021
75025
  });
75026
+ const PERMISSION_MAP = {
75027
+ accept: {
75028
+ own: "RESOLVE_OWN",
75029
+ other: "RESOLVE_OTHER"
75030
+ },
75031
+ reject: {
75032
+ own: "REJECT_OWN",
75033
+ other: "REJECT_OTHER"
75034
+ }
75035
+ };
75036
+ const buildKey = (change) => {
75037
+ const id = change.mark?.attrs?.id ?? `${change.from}-${change.to}`;
75038
+ return `${id}:${change.mark?.type?.name ?? "unknown"}`;
75039
+ };
75040
+ const mergeChange = (bucket, change) => {
75041
+ const key2 = buildKey(change);
75042
+ const existing = bucket.get(key2);
75043
+ if (existing) {
75044
+ existing.from = Math.min(existing.from, change.from);
75045
+ existing.to = Math.max(existing.to, change.to);
75046
+ existing.segments.push({ from: change.from, to: change.to });
75047
+ } else {
75048
+ bucket.set(key2, {
75049
+ id: change.mark?.attrs?.id ?? null,
75050
+ type: change.mark?.type?.name ?? null,
75051
+ attrs: { ...change.mark?.attrs ?? {} },
75052
+ from: change.from,
75053
+ to: change.to,
75054
+ segments: [{ from: change.from, to: change.to }]
75055
+ });
75056
+ }
75057
+ };
75058
+ const collectTrackedChanges = ({ state: state2, from: from2, to }) => {
75059
+ if (!state2) return [];
75060
+ const collapsed = from2 === to;
75061
+ const changes = getTrackChanges(state2);
75062
+ if (!changes?.length) return [];
75063
+ const bucket = /* @__PURE__ */ new Map();
75064
+ changes.forEach((change) => {
75065
+ const overlaps = collapsed ? change.from <= from2 && change.to >= from2 : change.from < to && change.to > from2;
75066
+ if (!overlaps) return;
75067
+ mergeChange(bucket, change);
75068
+ });
75069
+ return Array.from(bucket.values());
75070
+ };
75071
+ const derivePermissionKey = ({ action, isOwn }) => {
75072
+ const mapping = PERMISSION_MAP[action];
75073
+ if (!mapping) return null;
75074
+ return isOwn ? mapping.own : mapping.other;
75075
+ };
75076
+ const resolveChanges = (editor) => {
75077
+ if (!editor) return { role: "editor", isInternal: false, currentUser: null, resolver: null };
75078
+ const role = editor.options?.role ?? "editor";
75079
+ const isInternal = Boolean(editor.options?.isInternal);
75080
+ const currentUser = editor.options?.user ?? null;
75081
+ const resolver = editor.options?.permissionResolver;
75082
+ return { role, isInternal, currentUser, resolver };
75083
+ };
75084
+ const isTrackedChangeActionAllowed = ({ editor, action, trackedChanges }) => {
75085
+ if (!trackedChanges?.length) return true;
75086
+ const { role, isInternal, currentUser, resolver } = resolveChanges(editor);
75087
+ if (typeof resolver !== "function") return true;
75088
+ const currentEmail = currentUser?.email ?? null;
75089
+ return trackedChanges.every((change) => {
75090
+ const authorEmail = change.attrs?.authorEmail ?? null;
75091
+ const isOwn = Boolean(currentEmail && authorEmail && currentEmail === authorEmail);
75092
+ const permission = derivePermissionKey({ action, isOwn });
75093
+ if (!permission) return true;
75094
+ const payload = {
75095
+ permission,
75096
+ role,
75097
+ isInternal,
75098
+ trackedChange: {
75099
+ id: change.id,
75100
+ type: change.type,
75101
+ attrs: change.attrs,
75102
+ from: change.from,
75103
+ to: change.to,
75104
+ segments: change.segments,
75105
+ commentId: change.id
75106
+ },
75107
+ comment: change.comment ?? null
75108
+ };
75109
+ return resolver(payload) !== false;
75110
+ });
75111
+ };
75112
+ const collectTrackedChangesForContext = ({ state: state2, pos, trackedChangeId }) => {
75113
+ if (pos == null) return [];
75114
+ const changes = collectTrackedChanges({ state: state2, from: pos, to: pos });
75115
+ if (!trackedChangeId) return changes;
75116
+ return changes.filter((change) => change.id === trackedChangeId);
75117
+ };
75022
75118
  const TrackChanges = Extension.create({
75023
75119
  name: "trackChanges",
75024
75120
  addCommands() {
75025
75121
  return {
75026
- acceptTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch }) => {
75122
+ acceptTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch, editor }) => {
75123
+ const trackedChanges = collectTrackedChanges({ state: state2, from: from2, to });
75124
+ if (!isTrackedChangeActionAllowed({ editor, action: "accept", trackedChanges })) return false;
75027
75125
  let { tr, doc: doc2 } = state2;
75028
75126
  tr.setMeta("acceptReject", true);
75029
75127
  const map2 = new Mapping();
@@ -75061,7 +75159,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
75061
75159
  }
75062
75160
  return true;
75063
75161
  },
75064
- rejectTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch }) => {
75162
+ rejectTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch, editor }) => {
75163
+ const trackedChanges = collectTrackedChanges({ state: state2, from: from2, to });
75164
+ if (!isTrackedChangeActionAllowed({ editor, action: "reject", trackedChanges })) return false;
75065
75165
  const { tr, doc: doc2 } = state2;
75066
75166
  tr.setMeta("acceptReject", true);
75067
75167
  const map2 = new Mapping();
@@ -91484,7 +91584,7 @@ ${style2}
91484
91584
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
91485
91585
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
91486
91586
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
91487
- var _SuperToolbar_instances, initToolbarGroups_fn, _interceptedCommands, makeToolbarItems_fn, initDefaultFonts_fn, updateHighlightColors_fn, deactivateAll_fn, updateToolbarHistory_fn, runCommandWithArgumentOnly_fn;
91587
+ var _SuperToolbar_instances, initToolbarGroups_fn, _interceptedCommands, makeToolbarItems_fn, initDefaultFonts_fn, updateHighlightColors_fn, deactivateAll_fn, updateToolbarHistory_fn, enrichTrackedChanges_fn, runCommandWithArgumentOnly_fn;
91488
91588
  var eventemitter3$1 = { exports: {} };
91489
91589
  var hasRequiredEventemitter3$1;
91490
91590
  function requireEventemitter3$1() {
@@ -91536,7 +91636,7 @@ ${style2}
91536
91636
  var evt = prefix2 ? prefix2 + event : event, handlers2 = this._events[evt];
91537
91637
  if (!handlers2) return [];
91538
91638
  if (handlers2.fn) return [handlers2.fn];
91539
- for (var i2 = 0, l2 = handlers2.length, ee = new Array(l2); i2 < l2; i2++) {
91639
+ for (var i2 = 0, l = handlers2.length, ee = new Array(l); i2 < l; i2++) {
91540
91640
  ee[i2] = handlers2[i2].fn;
91541
91641
  }
91542
91642
  return ee;
@@ -92692,18 +92792,18 @@ ${style2}
92692
92792
  }, [
92693
92793
  (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (i2) => {
92694
92794
  return openBlock(), createElementBlock(Fragment$1, { key: i2 }, [
92695
- (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (n2) => {
92795
+ (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (n) => {
92696
92796
  return createBaseVNode("div", {
92697
92797
  class: "toolbar-table-grid__item",
92698
- key: `${i2}_${n2}`,
92699
- "data-cols": n2,
92798
+ key: `${i2}_${n}`,
92799
+ "data-cols": n,
92700
92800
  "data-rows": i2,
92701
92801
  "data-item": "true",
92702
92802
  ref_for: true,
92703
92803
  ref_key: "tableGridItems",
92704
92804
  ref: tableGridItems,
92705
- onKeydown: withModifiers((event) => handleKeyDown2(event, n2, i2), ["prevent"]),
92706
- onClick: withModifiers(($event) => handleClick2({ cols: n2, rows: i2 }), ["stop", "prevent"])
92805
+ onKeydown: withModifiers((event) => handleKeyDown2(event, n, i2), ["prevent"]),
92806
+ onClick: withModifiers(($event) => handleClick2({ cols: n, rows: i2 }), ["stop", "prevent"])
92707
92807
  }, null, 40, _hoisted_1$6$1);
92708
92808
  }), 64))
92709
92809
  ], 64);
@@ -94438,10 +94538,31 @@ ${style2}
94438
94538
  __privateMethod(this, _SuperToolbar_instances, deactivateAll_fn).call(this);
94439
94539
  return;
94440
94540
  }
94541
+ const { state: state2 } = this.activeEditor;
94542
+ const selection = state2.selection;
94543
+ const selectionTrackedChanges = __privateMethod(this, _SuperToolbar_instances, enrichTrackedChanges_fn).call(this, collectTrackedChanges({ state: state2, from: selection.from, to: selection.to }));
94544
+ const hasTrackedChanges = selectionTrackedChanges.length > 0;
94545
+ const hasValidSelection = hasTrackedChanges;
94546
+ const canAcceptTrackedChanges = hasValidSelection && isTrackedChangeActionAllowed({
94547
+ editor: this.activeEditor,
94548
+ action: "accept",
94549
+ trackedChanges: selectionTrackedChanges
94550
+ });
94551
+ const canRejectTrackedChanges = hasValidSelection && isTrackedChangeActionAllowed({
94552
+ editor: this.activeEditor,
94553
+ action: "reject",
94554
+ trackedChanges: selectionTrackedChanges
94555
+ });
94441
94556
  const marks = getActiveFormatting(this.activeEditor);
94442
94557
  const inTable = isInTable$1(this.activeEditor.state);
94443
94558
  this.toolbarItems.forEach((item) => {
94444
94559
  item.resetDisabled();
94560
+ if (item.name.value === "acceptTrackedChangeBySelection") {
94561
+ item.setDisabled(!canAcceptTrackedChanges);
94562
+ }
94563
+ if (item.name.value === "rejectTrackedChangeOnSelection") {
94564
+ item.setDisabled(!canRejectTrackedChanges);
94565
+ }
94445
94566
  if (item.name.value === "linkedStyles") {
94446
94567
  if (this.activeEditor && !getQuickFormatList(this.activeEditor).length) {
94447
94568
  return item.deactivate();
@@ -94614,6 +94735,19 @@ ${style2}
94614
94735
  this.redoDepth = redoDepth(this.activeEditor.state);
94615
94736
  }
94616
94737
  };
94738
+ enrichTrackedChanges_fn = function(trackedChanges = []) {
94739
+ if (!trackedChanges?.length) return trackedChanges;
94740
+ const store = this.superdoc?.commentsStore;
94741
+ if (!store?.getComment) return trackedChanges;
94742
+ return trackedChanges.map((change) => {
94743
+ const commentId = change.id;
94744
+ if (!commentId) return change;
94745
+ const storeComment = store.getComment(commentId);
94746
+ if (!storeComment) return change;
94747
+ const comment = typeof storeComment.getValues === "function" ? storeComment.getValues() : storeComment;
94748
+ return { ...change, comment };
94749
+ });
94750
+ };
94617
94751
  runCommandWithArgumentOnly_fn = function({ item, argument, noArgumentCallback = false }, callback) {
94618
94752
  if (!argument || !this.activeEditor) return;
94619
94753
  let command2 = item.command;
@@ -95018,6 +95152,7 @@ ${style2}
95018
95152
  state2.selection.$head.marks().forEach((mark) => activeMarks.push(mark.type.name));
95019
95153
  }
95020
95154
  const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete") || activeMarks.includes("trackFormat");
95155
+ const trackedChanges = event ? collectTrackedChangesForContext({ state: state2, pos, trackedChangeId }) : collectTrackedChanges({ state: state2, from: from2, to });
95021
95156
  const cursorCoords = pos ? view.coordsAtPos(pos) : null;
95022
95157
  const cursorPosition = cursorCoords ? {
95023
95158
  x: cursorCoords.left,
@@ -95051,7 +95186,9 @@ ${style2}
95051
95186
  event,
95052
95187
  trigger: event ? "click" : "slash",
95053
95188
  // Editor reference for advanced use cases
95054
- editor
95189
+ editor,
95190
+ // Tracked change metadata
95191
+ trackedChanges
95055
95192
  };
95056
95193
  return context;
95057
95194
  }
@@ -95148,6 +95285,14 @@ ${style2}
95148
95285
  }
95149
95286
  }
95150
95287
  };
95288
+ const canPerformTrackedChange = (context, action) => {
95289
+ if (!context?.editor) return true;
95290
+ return isTrackedChangeActionAllowed({
95291
+ editor: context.editor,
95292
+ action,
95293
+ trackedChanges: context.trackedChanges ?? []
95294
+ });
95295
+ };
95151
95296
  function getItems(context, customItems = [], includeDefaultItems = true) {
95152
95297
  const { selectedText, editor } = context;
95153
95298
  if (arguments.length === 1 && editor?.options?.slashMenuConfig) {
@@ -95205,7 +95350,7 @@ ${style2}
95205
95350
  },
95206
95351
  showWhen: (context2) => {
95207
95352
  const { trigger: trigger2, isTrackedChange } = context2;
95208
- return trigger2 === TRIGGERS.click && isTrackedChange;
95353
+ return trigger2 === TRIGGERS.click && isTrackedChange && canPerformTrackedChange(context2, "accept");
95209
95354
  }
95210
95355
  },
95211
95356
  {
@@ -95222,7 +95367,7 @@ ${style2}
95222
95367
  },
95223
95368
  showWhen: (context2) => {
95224
95369
  const { trigger: trigger2, isTrackedChange } = context2;
95225
- return trigger2 === TRIGGERS.click && isTrackedChange;
95370
+ return trigger2 === TRIGGERS.click && isTrackedChange && canPerformTrackedChange(context2, "reject");
95226
95371
  }
95227
95372
  }
95228
95373
  ]
@@ -111113,69 +111258,6 @@ ${style2}
111113
111258
  rejectChange: xmarkIconSvg,
111114
111259
  overflow: ellipsisVerticalSvg
111115
111260
  };
111116
- const PERMISSIONS = Object.freeze({
111117
- RESOLVE_OWN: "RESOLVE_OWN",
111118
- RESOLVE_OTHER: "RESOLVE_OTHER",
111119
- REJECT_OWN: "REJECT_OWN",
111120
- REJECT_OTHER: "REJECT_OTHER",
111121
- COMMENTS_OVERFLOW_OWN: "COMMENTS_OVERFLOW",
111122
- COMMENTS_OVERFLOW_OTHER: "COMMENTS_OVERFLOW_OTHER",
111123
- COMMENTS_DELETE_OWN: "COMMENTS_DELETE_OWN",
111124
- COMMENTS_DELETE_OTHER: "COMMENTS_DELETE_OTHER",
111125
- UPLOAD_VERSION: "UPLOAD_VERSION",
111126
- VERSION_HISTORY: "VERSION_HISTORY"
111127
- });
111128
- const ROLES = Object.freeze({
111129
- EDITOR: "editor",
111130
- SUGGESTER: "suggester",
111131
- VIEWER: "viewer"
111132
- });
111133
- const permissions = Object.freeze({
111134
- [PERMISSIONS.RESOLVE_OWN]: {
111135
- internal: [ROLES.EDITOR],
111136
- external: [ROLES.EDITOR]
111137
- },
111138
- [PERMISSIONS.RESOLVE_OTHER]: {
111139
- internal: [ROLES.EDITOR],
111140
- external: []
111141
- },
111142
- [PERMISSIONS.REJECT_OWN]: {
111143
- internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111144
- external: [ROLES.EDITOR, ROLES.SUGGESTER]
111145
- },
111146
- [PERMISSIONS.REJECT_OTHER]: {
111147
- internal: [ROLES.EDITOR],
111148
- external: []
111149
- },
111150
- [PERMISSIONS.COMMENTS_OVERFLOW_OWN]: {
111151
- internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111152
- external: [ROLES.EDITOR, ROLES.SUGGESTER]
111153
- },
111154
- [PERMISSIONS.COMMENTS_OVERFLOW_OTHER]: {
111155
- internal: [ROLES.EDITOR],
111156
- external: []
111157
- },
111158
- [PERMISSIONS.COMMENTS_DELETE_OWN]: {
111159
- internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111160
- external: [ROLES.EDITOR, ROLES.SUGGESTER]
111161
- },
111162
- [PERMISSIONS.COMMENTS_DELETE_OTHER]: {
111163
- internal: [ROLES.EDITOR],
111164
- external: []
111165
- },
111166
- [PERMISSIONS.UPLOAD_VERSION]: {
111167
- internal: [ROLES.EDITOR],
111168
- external: []
111169
- },
111170
- [PERMISSIONS.VERSION_HISTORY]: {
111171
- internal: [ROLES.EDITOR],
111172
- external: []
111173
- }
111174
- });
111175
- const isAllowed = (permission, role, isInternal) => {
111176
- const internalExternal = isInternal ? "internal" : "external";
111177
- return permissions[permission]?.[internalExternal]?.includes(role);
111178
- };
111179
111261
  const _export_sfc = (sfc, props) => {
111180
111262
  const target = sfc.__vccOpts || sfc;
111181
111263
  for (const [key2, val] of props) {
@@ -111331,6 +111413,96 @@ ${style2}
111331
111413
  const formattedDate = `${formattedTime} ${month} ${day}`;
111332
111414
  return formattedDate;
111333
111415
  }
111416
+ const PERMISSIONS = Object.freeze({
111417
+ RESOLVE_OWN: "RESOLVE_OWN",
111418
+ RESOLVE_OTHER: "RESOLVE_OTHER",
111419
+ REJECT_OWN: "REJECT_OWN",
111420
+ REJECT_OTHER: "REJECT_OTHER",
111421
+ COMMENTS_OVERFLOW_OWN: "COMMENTS_OVERFLOW",
111422
+ COMMENTS_OVERFLOW_OTHER: "COMMENTS_OVERFLOW_OTHER",
111423
+ COMMENTS_DELETE_OWN: "COMMENTS_DELETE_OWN",
111424
+ COMMENTS_DELETE_OTHER: "COMMENTS_DELETE_OTHER",
111425
+ UPLOAD_VERSION: "UPLOAD_VERSION",
111426
+ VERSION_HISTORY: "VERSION_HISTORY"
111427
+ });
111428
+ const ROLES = Object.freeze({
111429
+ EDITOR: "editor",
111430
+ SUGGESTER: "suggester",
111431
+ VIEWER: "viewer"
111432
+ });
111433
+ const PERMISSION_MATRIX = Object.freeze({
111434
+ [PERMISSIONS.RESOLVE_OWN]: {
111435
+ internal: [ROLES.EDITOR],
111436
+ external: [ROLES.EDITOR]
111437
+ },
111438
+ [PERMISSIONS.RESOLVE_OTHER]: {
111439
+ internal: [ROLES.EDITOR],
111440
+ external: []
111441
+ },
111442
+ [PERMISSIONS.REJECT_OWN]: {
111443
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111444
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111445
+ },
111446
+ [PERMISSIONS.REJECT_OTHER]: {
111447
+ internal: [ROLES.EDITOR],
111448
+ external: []
111449
+ },
111450
+ [PERMISSIONS.COMMENTS_OVERFLOW_OWN]: {
111451
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111452
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111453
+ },
111454
+ [PERMISSIONS.COMMENTS_OVERFLOW_OTHER]: {
111455
+ internal: [ROLES.EDITOR],
111456
+ external: []
111457
+ },
111458
+ [PERMISSIONS.COMMENTS_DELETE_OWN]: {
111459
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111460
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111461
+ },
111462
+ [PERMISSIONS.COMMENTS_DELETE_OTHER]: {
111463
+ internal: [ROLES.EDITOR],
111464
+ external: []
111465
+ },
111466
+ [PERMISSIONS.UPLOAD_VERSION]: {
111467
+ internal: [ROLES.EDITOR],
111468
+ external: []
111469
+ },
111470
+ [PERMISSIONS.VERSION_HISTORY]: {
111471
+ internal: [ROLES.EDITOR],
111472
+ external: []
111473
+ }
111474
+ });
111475
+ const pickResolver = (context = {}) => {
111476
+ if (typeof context.permissionResolver === "function") return context.permissionResolver;
111477
+ if (context.superdoc?.config?.modules?.comments?.permissionResolver) {
111478
+ const resolver = context.superdoc.config.modules.comments.permissionResolver;
111479
+ if (typeof resolver === "function") return resolver;
111480
+ }
111481
+ if (typeof context.superdoc?.config?.permissionResolver === "function") {
111482
+ return context.superdoc.config.permissionResolver;
111483
+ }
111484
+ return null;
111485
+ };
111486
+ const defaultDecisionFor = (permission, role, isInternal) => {
111487
+ const internalExternal = isInternal ? "internal" : "external";
111488
+ return PERMISSION_MATRIX[permission]?.[internalExternal]?.includes(role) ?? false;
111489
+ };
111490
+ const isAllowed = (permission, role, isInternal, context = {}) => {
111491
+ const defaultDecision = defaultDecisionFor(permission, role, isInternal);
111492
+ const resolver = pickResolver(context);
111493
+ if (typeof resolver !== "function") return defaultDecision;
111494
+ const decision = resolver({
111495
+ permission,
111496
+ role,
111497
+ isInternal,
111498
+ defaultDecision,
111499
+ comment: context.comment ?? null,
111500
+ currentUser: context.currentUser ?? context.superdoc?.config?.user ?? null,
111501
+ superdoc: context.superdoc ?? null,
111502
+ trackedChange: context.trackedChange ?? null
111503
+ });
111504
+ return typeof decision === "boolean" ? decision : defaultDecision;
111505
+ };
111334
111506
  const _hoisted_1$e = { class: "card-section comment-header" };
111335
111507
  const _hoisted_2$7 = { class: "comment-header-left" };
111336
111508
  const _hoisted_3$5 = { class: "user-info" };
@@ -111387,14 +111559,30 @@ ${style2}
111387
111559
  const allowResolve = computed(() => {
111388
111560
  if (!generallyAllowed.value) return false;
111389
111561
  if (props.comment.parentCommentId) return false;
111390
- if (isOwnComment || props.comment.trackedChange) return isAllowed(PERMISSIONS.RESOLVE_OWN, role, isInternal);
111391
- else return isAllowed(PERMISSIONS.RESOLVE_OTHER, role, isInternal);
111562
+ const context = {
111563
+ comment: props.comment,
111564
+ currentUser: proxy.$superdoc.config.user,
111565
+ superdoc: proxy.$superdoc
111566
+ };
111567
+ if (isOwnComment || props.comment.trackedChange) {
111568
+ return isAllowed(PERMISSIONS.RESOLVE_OWN, role, isInternal, context);
111569
+ } else {
111570
+ return isAllowed(PERMISSIONS.RESOLVE_OTHER, role, isInternal, context);
111571
+ }
111392
111572
  });
111393
111573
  const allowReject = computed(() => {
111394
111574
  if (!generallyAllowed.value) return false;
111395
111575
  if (!props.comment.trackedChange) return false;
111396
- if (isOwnComment || props.comment.trackedChange) return isAllowed(PERMISSIONS.REJECT_OWN, role, isInternal);
111397
- else return isAllowed(PERMISSIONS.REJECT_OTHER, role, isInternal);
111576
+ const context = {
111577
+ comment: props.comment,
111578
+ currentUser: proxy.$superdoc.config.user,
111579
+ superdoc: proxy.$superdoc
111580
+ };
111581
+ if (isOwnComment || props.comment.trackedChange) {
111582
+ return isAllowed(PERMISSIONS.REJECT_OWN, role, isInternal, context);
111583
+ } else {
111584
+ return isAllowed(PERMISSIONS.REJECT_OTHER, role, isInternal, context);
111585
+ }
111398
111586
  });
111399
111587
  const allowOverflow = computed(() => {
111400
111588
  if (!generallyAllowed.value) return false;
@@ -111411,9 +111599,14 @@ ${style2}
111411
111599
  options.add("edit");
111412
111600
  }
111413
111601
  const isOwnComment2 = props.comment.creatorEmail === proxy.$superdoc.config.user.email;
111414
- if (isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OWN, role, isInternal)) {
111602
+ const context = {
111603
+ comment: props.comment,
111604
+ currentUser: proxy.$superdoc.config.user,
111605
+ superdoc: proxy.$superdoc
111606
+ };
111607
+ if (isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OWN, role, isInternal, context)) {
111415
111608
  options.add("delete");
111416
- } else if (!isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OTHER, role, isInternal)) {
111609
+ } else if (!isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OTHER, role, isInternal, context)) {
111417
111610
  options.add("delete");
111418
111611
  }
111419
111612
  options.forEach((option) => allowedOptions.push(OVERFLOW_OPTIONS[option]));
@@ -111476,7 +111669,7 @@ ${style2}
111476
111669
  };
111477
111670
  }
111478
111671
  };
111479
- const CommentHeader = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-27b7739c"]]);
111672
+ const CommentHeader = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-0f4f53ef"]]);
111480
111673
  const _hoisted_1$d = { class: "input-section" };
111481
111674
  const _sfc_main$e = {
111482
111675
  __name: "CommentInput",
@@ -111896,7 +112089,7 @@ ${style2}
111896
112089
  };
111897
112090
  }
111898
112091
  };
111899
- const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-e07f3426"]]);
112092
+ const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-36e5f63e"]]);
111900
112093
  const _hoisted_1$b = { class: "comments-list" };
111901
112094
  const _hoisted_2$5 = { key: 0 };
111902
112095
  const _hoisted_3$3 = { class: "comment-item" };
@@ -113115,7 +113308,12 @@ ${style2}
113115
113308
  externalExtensions: proxy.$superdoc.config.editorExtensions || [],
113116
113309
  suppressDefaultDocxStyles: proxy.$superdoc.config.suppressDefaultDocxStyles,
113117
113310
  disableContextMenu: proxy.$superdoc.config.disableContextMenu,
113118
- jsonOverride: proxy.$superdoc.config.jsonOverride
113311
+ jsonOverride: proxy.$superdoc.config.jsonOverride,
113312
+ permissionResolver: (payload = {}) => proxy.$superdoc.canPerformPermission({
113313
+ role: proxy.$superdoc.config.role,
113314
+ isInternal: proxy.$superdoc.config.isInternal,
113315
+ ...payload
113316
+ })
113119
113317
  };
113120
113318
  return options;
113121
113319
  };
@@ -113484,7 +113682,7 @@ ${style2}
113484
113682
  };
113485
113683
  }
113486
113684
  };
113487
- const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-6de7ff3a"]]);
113685
+ const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-ac4ea6eb"]]);
113488
113686
  const createSuperdocVueApp = () => {
113489
113687
  const app = createApp(App);
113490
113688
  const pinia = createPinia();
@@ -113579,6 +113777,8 @@ ${style2}
113579
113777
  users: [],
113580
113778
  modules: {},
113581
113779
  // Optional: Modules to load. Use modules.ai.{your_key} to pass in your key
113780
+ permissionResolver: null,
113781
+ // Optional: Override for permission checks
113582
113782
  title: "SuperDoc",
113583
113783
  conversations: [],
113584
113784
  pagination: false,
@@ -113636,7 +113836,7 @@ ${style2}
113636
113836
  this.config.colors = shuffleArray(this.config.colors);
113637
113837
  this.userColorMap = /* @__PURE__ */ new Map();
113638
113838
  this.colorIndex = 0;
113639
- this.version = "0.23.0";
113839
+ this.version = "0.24.0";
113640
113840
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
113641
113841
  this.superdocId = config2.superdocId || v4();
113642
113842
  this.colors = this.config.colors;
@@ -113942,6 +114142,41 @@ ${style2}
113942
114142
  }
113943
114143
  });
113944
114144
  }
114145
+ /**
114146
+ * Determine whether the current configuration allows a given permission.
114147
+ * Used by downstream consumers (toolbar, context menu, commands) to keep
114148
+ * tracked-change affordances consistent with customer overrides.
114149
+ *
114150
+ * @param {Object} params
114151
+ * @param {string} params.permission Permission key to evaluate
114152
+ * @param {string} [params.role=this.config.role] Role to evaluate against
114153
+ * @param {boolean} [params.isInternal=this.config.isInternal] Internal/external flag
114154
+ * @param {Object|null} [params.comment] Comment object (if already resolved)
114155
+ * @param {Object|null} [params.trackedChange] Tracked change metadata (id, attrs, etc.)
114156
+ * @returns {boolean}
114157
+ */
114158
+ canPerformPermission({
114159
+ permission,
114160
+ role = this.config.role,
114161
+ isInternal = this.config.isInternal,
114162
+ comment = null,
114163
+ trackedChange = null
114164
+ } = {}) {
114165
+ if (!permission) return false;
114166
+ let resolvedComment = comment ?? trackedChange?.comment ?? null;
114167
+ const commentId = trackedChange?.commentId || trackedChange?.id;
114168
+ if (!resolvedComment && commentId && this.commentsStore?.getComment) {
114169
+ const storeComment = this.commentsStore.getComment(commentId);
114170
+ resolvedComment = storeComment?.getValues ? storeComment.getValues() : storeComment;
114171
+ }
114172
+ const context = {
114173
+ superdoc: this,
114174
+ currentUser: this.config.user,
114175
+ comment: resolvedComment ?? null,
114176
+ trackedChange: trackedChange ?? null
114177
+ };
114178
+ return isAllowed(permission, role, isInternal, context);
114179
+ }
113945
114180
  #addToolbar() {
113946
114181
  const moduleConfig = this.config.modules?.toolbar || {};
113947
114182
  this.toolbarElement = this.config.modules?.toolbar?.selector || this.config.toolbar;