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

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 (34) hide show
  1. package/dist/chunks/{PdfViewer-BiHh3x6b.es.js → PdfViewer-BEbvklge.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-qk_hITc5.cjs → PdfViewer-CvksDDwi.cjs} +1 -1
  3. package/dist/chunks/{index-aMnFs35I.es.js → index-B9Ad3j3k.es.js} +166 -76
  4. package/dist/chunks/{index-Di6nG2sc.cjs → index-Bf3lSVG2.cjs} +166 -76
  5. package/dist/chunks/{super-editor.es-Dz7y81Yr.es.js → super-editor.es--RpCOiR4.es.js} +157 -15
  6. package/dist/chunks/{super-editor.es-BKZvTraR.cjs → super-editor.es-Bh07I_ue.cjs} +157 -15
  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-CyBtLJFF.js} +116 -16
  18. package/dist/super-editor/chunks/{toolbar-DMobfM6u.js → toolbar-1Ejr8Exl.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.es.js +66 -21
  27. package/dist/super-editor/toolbar.es.js +2 -2
  28. package/dist/super-editor.cjs +1 -1
  29. package/dist/super-editor.es.js +1 -1
  30. package/dist/superdoc.cjs +2 -2
  31. package/dist/superdoc.es.js +2 -2
  32. package/dist/superdoc.umd.js +321 -89
  33. package/dist/superdoc.umd.js.map +1 -1
  34. 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
  /**
@@ -60261,7 +60261,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
60261
60261
  customUpdatedFiles: {},
60262
60262
  isHeaderFooterChanged: false,
60263
60263
  isCustomXmlChanged: false,
60264
- focusTarget: null
60264
+ focusTarget: null,
60265
+ permissionResolver: null
60265
60266
  });
60266
60267
  __privateMethod$1(this, _Editor_instances, initContainerElement_fn).call(this, options);
60267
60268
  __privateMethod$1(this, _Editor_instances, checkHeadless_fn).call(this, options);
@@ -61023,7 +61024,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61023
61024
  * @returns {Object | void} Migration results
61024
61025
  */
61025
61026
  processCollaborationMigrations() {
61026
- console.debug("[checkVersionMigrations] Current editor version", "0.23.0");
61027
+ console.debug("[checkVersionMigrations] Current editor version", "0.24.0");
61027
61028
  if (!this.options.ydoc) return;
61028
61029
  const metaMap = this.options.ydoc.getMap("meta");
61029
61030
  let docVersion = metaMap.get("version");
@@ -75019,11 +75020,105 @@ Please report this to https://github.com/markedjs/marked.`, e) {
75019
75020
  return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
75020
75021
  }
75021
75022
  });
75023
+ const PERMISSION_MAP = {
75024
+ accept: {
75025
+ own: "RESOLVE_OWN",
75026
+ other: "RESOLVE_OTHER"
75027
+ },
75028
+ reject: {
75029
+ own: "REJECT_OWN",
75030
+ other: "REJECT_OTHER"
75031
+ }
75032
+ };
75033
+ const buildKey = (change) => {
75034
+ const id = change.mark?.attrs?.id ?? `${change.from}-${change.to}`;
75035
+ return `${id}:${change.mark?.type?.name ?? "unknown"}`;
75036
+ };
75037
+ const mergeChange = (bucket, change) => {
75038
+ const key2 = buildKey(change);
75039
+ const existing = bucket.get(key2);
75040
+ if (existing) {
75041
+ existing.from = Math.min(existing.from, change.from);
75042
+ existing.to = Math.max(existing.to, change.to);
75043
+ existing.segments.push({ from: change.from, to: change.to });
75044
+ } else {
75045
+ bucket.set(key2, {
75046
+ id: change.mark?.attrs?.id ?? null,
75047
+ type: change.mark?.type?.name ?? null,
75048
+ attrs: { ...change.mark?.attrs ?? {} },
75049
+ from: change.from,
75050
+ to: change.to,
75051
+ segments: [{ from: change.from, to: change.to }]
75052
+ });
75053
+ }
75054
+ };
75055
+ const collectTrackedChanges = ({ state: state2, from: from2, to }) => {
75056
+ if (!state2) return [];
75057
+ const collapsed = from2 === to;
75058
+ const changes = getTrackChanges(state2);
75059
+ if (!changes?.length) return [];
75060
+ const bucket = /* @__PURE__ */ new Map();
75061
+ changes.forEach((change) => {
75062
+ const overlaps = collapsed ? change.from <= from2 && change.to >= from2 : change.from < to && change.to > from2;
75063
+ if (!overlaps) return;
75064
+ mergeChange(bucket, change);
75065
+ });
75066
+ return Array.from(bucket.values());
75067
+ };
75068
+ const derivePermissionKey = ({ action, isOwn }) => {
75069
+ const mapping = PERMISSION_MAP[action];
75070
+ if (!mapping) return null;
75071
+ return isOwn ? mapping.own : mapping.other;
75072
+ };
75073
+ const resolveChanges = (editor) => {
75074
+ if (!editor) return { role: "editor", isInternal: false, currentUser: null, resolver: null };
75075
+ const role = editor.options?.role ?? "editor";
75076
+ const isInternal = Boolean(editor.options?.isInternal);
75077
+ const currentUser = editor.options?.user ?? null;
75078
+ const resolver = editor.options?.permissionResolver;
75079
+ return { role, isInternal, currentUser, resolver };
75080
+ };
75081
+ const isTrackedChangeActionAllowed = ({ editor, action, trackedChanges }) => {
75082
+ if (!trackedChanges?.length) return true;
75083
+ const { role, isInternal, currentUser, resolver } = resolveChanges(editor);
75084
+ if (typeof resolver !== "function") return true;
75085
+ const currentEmail = currentUser?.email ?? null;
75086
+ return trackedChanges.every((change) => {
75087
+ const authorEmail = change.attrs?.authorEmail ?? null;
75088
+ const isOwn = Boolean(currentEmail && authorEmail && currentEmail === authorEmail);
75089
+ const permission = derivePermissionKey({ action, isOwn });
75090
+ if (!permission) return true;
75091
+ const payload = {
75092
+ permission,
75093
+ role,
75094
+ isInternal,
75095
+ trackedChange: {
75096
+ id: change.id,
75097
+ type: change.type,
75098
+ attrs: change.attrs,
75099
+ from: change.from,
75100
+ to: change.to,
75101
+ segments: change.segments,
75102
+ commentId: change.id
75103
+ },
75104
+ comment: change.comment ?? null
75105
+ };
75106
+ return resolver(payload) !== false;
75107
+ });
75108
+ };
75109
+ const collectTrackedChangesForContext = ({ state: state2, pos, trackedChangeId }) => {
75110
+ if (pos == null) return [];
75111
+ const changes = collectTrackedChanges({ state: state2, from: pos, to: pos });
75112
+ if (!trackedChangeId) return changes;
75113
+ return changes.filter((change) => change.id === trackedChangeId);
75114
+ };
75022
75115
  const TrackChanges = Extension.create({
75023
75116
  name: "trackChanges",
75024
75117
  addCommands() {
75025
75118
  return {
75026
- acceptTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch }) => {
75119
+ acceptTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch, editor }) => {
75120
+ const trackedChanges = collectTrackedChanges({ state: state2, from: from2, to });
75121
+ if (!isTrackedChangeActionAllowed({ editor, action: "accept", trackedChanges })) return false;
75027
75122
  let { tr, doc: doc2 } = state2;
75028
75123
  tr.setMeta("acceptReject", true);
75029
75124
  const map2 = new Mapping();
@@ -75061,7 +75156,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
75061
75156
  }
75062
75157
  return true;
75063
75158
  },
75064
- rejectTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch }) => {
75159
+ rejectTrackedChangesBetween: (from2, to) => ({ state: state2, dispatch, editor }) => {
75160
+ const trackedChanges = collectTrackedChanges({ state: state2, from: from2, to });
75161
+ if (!isTrackedChangeActionAllowed({ editor, action: "reject", trackedChanges })) return false;
75065
75162
  const { tr, doc: doc2 } = state2;
75066
75163
  tr.setMeta("acceptReject", true);
75067
75164
  const map2 = new Mapping();
@@ -91484,7 +91581,7 @@ ${style2}
91484
91581
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
91485
91582
  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
91583
  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;
91584
+ var _SuperToolbar_instances, initToolbarGroups_fn, _interceptedCommands, makeToolbarItems_fn, initDefaultFonts_fn, updateHighlightColors_fn, deactivateAll_fn, updateToolbarHistory_fn, enrichTrackedChanges_fn, runCommandWithArgumentOnly_fn;
91488
91585
  var eventemitter3$1 = { exports: {} };
91489
91586
  var hasRequiredEventemitter3$1;
91490
91587
  function requireEventemitter3$1() {
@@ -91536,7 +91633,7 @@ ${style2}
91536
91633
  var evt = prefix2 ? prefix2 + event : event, handlers2 = this._events[evt];
91537
91634
  if (!handlers2) return [];
91538
91635
  if (handlers2.fn) return [handlers2.fn];
91539
- for (var i2 = 0, l2 = handlers2.length, ee = new Array(l2); i2 < l2; i2++) {
91636
+ for (var i2 = 0, l = handlers2.length, ee = new Array(l); i2 < l; i2++) {
91540
91637
  ee[i2] = handlers2[i2].fn;
91541
91638
  }
91542
91639
  return ee;
@@ -92692,18 +92789,18 @@ ${style2}
92692
92789
  }, [
92693
92790
  (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (i2) => {
92694
92791
  return openBlock(), createElementBlock(Fragment$1, { key: i2 }, [
92695
- (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (n2) => {
92792
+ (openBlock(), createElementBlock(Fragment$1, null, renderList(5, (n) => {
92696
92793
  return createBaseVNode("div", {
92697
92794
  class: "toolbar-table-grid__item",
92698
- key: `${i2}_${n2}`,
92699
- "data-cols": n2,
92795
+ key: `${i2}_${n}`,
92796
+ "data-cols": n,
92700
92797
  "data-rows": i2,
92701
92798
  "data-item": "true",
92702
92799
  ref_for: true,
92703
92800
  ref_key: "tableGridItems",
92704
92801
  ref: tableGridItems,
92705
- onKeydown: withModifiers((event) => handleKeyDown2(event, n2, i2), ["prevent"]),
92706
- onClick: withModifiers(($event) => handleClick2({ cols: n2, rows: i2 }), ["stop", "prevent"])
92802
+ onKeydown: withModifiers((event) => handleKeyDown2(event, n, i2), ["prevent"]),
92803
+ onClick: withModifiers(($event) => handleClick2({ cols: n, rows: i2 }), ["stop", "prevent"])
92707
92804
  }, null, 40, _hoisted_1$6$1);
92708
92805
  }), 64))
92709
92806
  ], 64);
@@ -94438,10 +94535,31 @@ ${style2}
94438
94535
  __privateMethod(this, _SuperToolbar_instances, deactivateAll_fn).call(this);
94439
94536
  return;
94440
94537
  }
94538
+ const { state: state2 } = this.activeEditor;
94539
+ const selection = state2.selection;
94540
+ const selectionTrackedChanges = __privateMethod(this, _SuperToolbar_instances, enrichTrackedChanges_fn).call(this, collectTrackedChanges({ state: state2, from: selection.from, to: selection.to }));
94541
+ const hasTrackedChanges = selectionTrackedChanges.length > 0;
94542
+ const hasValidSelection = hasTrackedChanges;
94543
+ const canAcceptTrackedChanges = hasValidSelection && isTrackedChangeActionAllowed({
94544
+ editor: this.activeEditor,
94545
+ action: "accept",
94546
+ trackedChanges: selectionTrackedChanges
94547
+ });
94548
+ const canRejectTrackedChanges = hasValidSelection && isTrackedChangeActionAllowed({
94549
+ editor: this.activeEditor,
94550
+ action: "reject",
94551
+ trackedChanges: selectionTrackedChanges
94552
+ });
94441
94553
  const marks = getActiveFormatting(this.activeEditor);
94442
94554
  const inTable = isInTable$1(this.activeEditor.state);
94443
94555
  this.toolbarItems.forEach((item) => {
94444
94556
  item.resetDisabled();
94557
+ if (item.name.value === "acceptTrackedChangeBySelection") {
94558
+ item.setDisabled(!canAcceptTrackedChanges);
94559
+ }
94560
+ if (item.name.value === "rejectTrackedChangeOnSelection") {
94561
+ item.setDisabled(!canRejectTrackedChanges);
94562
+ }
94445
94563
  if (item.name.value === "linkedStyles") {
94446
94564
  if (this.activeEditor && !getQuickFormatList(this.activeEditor).length) {
94447
94565
  return item.deactivate();
@@ -94614,6 +94732,19 @@ ${style2}
94614
94732
  this.redoDepth = redoDepth(this.activeEditor.state);
94615
94733
  }
94616
94734
  };
94735
+ enrichTrackedChanges_fn = function(trackedChanges = []) {
94736
+ if (!trackedChanges?.length) return trackedChanges;
94737
+ const store = this.superdoc?.commentsStore;
94738
+ if (!store?.getComment) return trackedChanges;
94739
+ return trackedChanges.map((change) => {
94740
+ const commentId = change.id;
94741
+ if (!commentId) return change;
94742
+ const storeComment = store.getComment(commentId);
94743
+ if (!storeComment) return change;
94744
+ const comment = typeof storeComment.getValues === "function" ? storeComment.getValues() : storeComment;
94745
+ return { ...change, comment };
94746
+ });
94747
+ };
94617
94748
  runCommandWithArgumentOnly_fn = function({ item, argument, noArgumentCallback = false }, callback) {
94618
94749
  if (!argument || !this.activeEditor) return;
94619
94750
  let command2 = item.command;
@@ -95018,6 +95149,7 @@ ${style2}
95018
95149
  state2.selection.$head.marks().forEach((mark) => activeMarks.push(mark.type.name));
95019
95150
  }
95020
95151
  const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete") || activeMarks.includes("trackFormat");
95152
+ const trackedChanges = event ? collectTrackedChangesForContext({ state: state2, pos, trackedChangeId }) : collectTrackedChanges({ state: state2, from: from2, to });
95021
95153
  const cursorCoords = pos ? view.coordsAtPos(pos) : null;
95022
95154
  const cursorPosition = cursorCoords ? {
95023
95155
  x: cursorCoords.left,
@@ -95051,7 +95183,9 @@ ${style2}
95051
95183
  event,
95052
95184
  trigger: event ? "click" : "slash",
95053
95185
  // Editor reference for advanced use cases
95054
- editor
95186
+ editor,
95187
+ // Tracked change metadata
95188
+ trackedChanges
95055
95189
  };
95056
95190
  return context;
95057
95191
  }
@@ -95148,6 +95282,14 @@ ${style2}
95148
95282
  }
95149
95283
  }
95150
95284
  };
95285
+ const canPerformTrackedChange = (context, action) => {
95286
+ if (!context?.editor) return true;
95287
+ return isTrackedChangeActionAllowed({
95288
+ editor: context.editor,
95289
+ action,
95290
+ trackedChanges: context.trackedChanges ?? []
95291
+ });
95292
+ };
95151
95293
  function getItems(context, customItems = [], includeDefaultItems = true) {
95152
95294
  const { selectedText, editor } = context;
95153
95295
  if (arguments.length === 1 && editor?.options?.slashMenuConfig) {
@@ -95205,7 +95347,7 @@ ${style2}
95205
95347
  },
95206
95348
  showWhen: (context2) => {
95207
95349
  const { trigger: trigger2, isTrackedChange } = context2;
95208
- return trigger2 === TRIGGERS.click && isTrackedChange;
95350
+ return trigger2 === TRIGGERS.click && isTrackedChange && canPerformTrackedChange(context2, "accept");
95209
95351
  }
95210
95352
  },
95211
95353
  {
@@ -95222,7 +95364,7 @@ ${style2}
95222
95364
  },
95223
95365
  showWhen: (context2) => {
95224
95366
  const { trigger: trigger2, isTrackedChange } = context2;
95225
- return trigger2 === TRIGGERS.click && isTrackedChange;
95367
+ return trigger2 === TRIGGERS.click && isTrackedChange && canPerformTrackedChange(context2, "reject");
95226
95368
  }
95227
95369
  }
95228
95370
  ]
@@ -111113,69 +111255,6 @@ ${style2}
111113
111255
  rejectChange: xmarkIconSvg,
111114
111256
  overflow: ellipsisVerticalSvg
111115
111257
  };
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
111258
  const _export_sfc = (sfc, props) => {
111180
111259
  const target = sfc.__vccOpts || sfc;
111181
111260
  for (const [key2, val] of props) {
@@ -111331,6 +111410,96 @@ ${style2}
111331
111410
  const formattedDate = `${formattedTime} ${month} ${day}`;
111332
111411
  return formattedDate;
111333
111412
  }
111413
+ const PERMISSIONS = Object.freeze({
111414
+ RESOLVE_OWN: "RESOLVE_OWN",
111415
+ RESOLVE_OTHER: "RESOLVE_OTHER",
111416
+ REJECT_OWN: "REJECT_OWN",
111417
+ REJECT_OTHER: "REJECT_OTHER",
111418
+ COMMENTS_OVERFLOW_OWN: "COMMENTS_OVERFLOW",
111419
+ COMMENTS_OVERFLOW_OTHER: "COMMENTS_OVERFLOW_OTHER",
111420
+ COMMENTS_DELETE_OWN: "COMMENTS_DELETE_OWN",
111421
+ COMMENTS_DELETE_OTHER: "COMMENTS_DELETE_OTHER",
111422
+ UPLOAD_VERSION: "UPLOAD_VERSION",
111423
+ VERSION_HISTORY: "VERSION_HISTORY"
111424
+ });
111425
+ const ROLES = Object.freeze({
111426
+ EDITOR: "editor",
111427
+ SUGGESTER: "suggester",
111428
+ VIEWER: "viewer"
111429
+ });
111430
+ const PERMISSION_MATRIX = Object.freeze({
111431
+ [PERMISSIONS.RESOLVE_OWN]: {
111432
+ internal: [ROLES.EDITOR],
111433
+ external: [ROLES.EDITOR]
111434
+ },
111435
+ [PERMISSIONS.RESOLVE_OTHER]: {
111436
+ internal: [ROLES.EDITOR],
111437
+ external: []
111438
+ },
111439
+ [PERMISSIONS.REJECT_OWN]: {
111440
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111441
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111442
+ },
111443
+ [PERMISSIONS.REJECT_OTHER]: {
111444
+ internal: [ROLES.EDITOR],
111445
+ external: []
111446
+ },
111447
+ [PERMISSIONS.COMMENTS_OVERFLOW_OWN]: {
111448
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111449
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111450
+ },
111451
+ [PERMISSIONS.COMMENTS_OVERFLOW_OTHER]: {
111452
+ internal: [ROLES.EDITOR],
111453
+ external: []
111454
+ },
111455
+ [PERMISSIONS.COMMENTS_DELETE_OWN]: {
111456
+ internal: [ROLES.EDITOR, ROLES.SUGGESTER],
111457
+ external: [ROLES.EDITOR, ROLES.SUGGESTER]
111458
+ },
111459
+ [PERMISSIONS.COMMENTS_DELETE_OTHER]: {
111460
+ internal: [ROLES.EDITOR],
111461
+ external: []
111462
+ },
111463
+ [PERMISSIONS.UPLOAD_VERSION]: {
111464
+ internal: [ROLES.EDITOR],
111465
+ external: []
111466
+ },
111467
+ [PERMISSIONS.VERSION_HISTORY]: {
111468
+ internal: [ROLES.EDITOR],
111469
+ external: []
111470
+ }
111471
+ });
111472
+ const pickResolver = (context = {}) => {
111473
+ if (typeof context.permissionResolver === "function") return context.permissionResolver;
111474
+ if (context.superdoc?.config?.modules?.comments?.permissionResolver) {
111475
+ const resolver = context.superdoc.config.modules.comments.permissionResolver;
111476
+ if (typeof resolver === "function") return resolver;
111477
+ }
111478
+ if (typeof context.superdoc?.config?.permissionResolver === "function") {
111479
+ return context.superdoc.config.permissionResolver;
111480
+ }
111481
+ return null;
111482
+ };
111483
+ const defaultDecisionFor = (permission, role, isInternal) => {
111484
+ const internalExternal = isInternal ? "internal" : "external";
111485
+ return PERMISSION_MATRIX[permission]?.[internalExternal]?.includes(role) ?? false;
111486
+ };
111487
+ const isAllowed = (permission, role, isInternal, context = {}) => {
111488
+ const defaultDecision = defaultDecisionFor(permission, role, isInternal);
111489
+ const resolver = pickResolver(context);
111490
+ if (typeof resolver !== "function") return defaultDecision;
111491
+ const decision = resolver({
111492
+ permission,
111493
+ role,
111494
+ isInternal,
111495
+ defaultDecision,
111496
+ comment: context.comment ?? null,
111497
+ currentUser: context.currentUser ?? context.superdoc?.config?.user ?? null,
111498
+ superdoc: context.superdoc ?? null,
111499
+ trackedChange: context.trackedChange ?? null
111500
+ });
111501
+ return typeof decision === "boolean" ? decision : defaultDecision;
111502
+ };
111334
111503
  const _hoisted_1$e = { class: "card-section comment-header" };
111335
111504
  const _hoisted_2$7 = { class: "comment-header-left" };
111336
111505
  const _hoisted_3$5 = { class: "user-info" };
@@ -111387,14 +111556,30 @@ ${style2}
111387
111556
  const allowResolve = computed(() => {
111388
111557
  if (!generallyAllowed.value) return false;
111389
111558
  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);
111559
+ const context = {
111560
+ comment: props.comment,
111561
+ currentUser: proxy.$superdoc.config.user,
111562
+ superdoc: proxy.$superdoc
111563
+ };
111564
+ if (isOwnComment || props.comment.trackedChange) {
111565
+ return isAllowed(PERMISSIONS.RESOLVE_OWN, role, isInternal, context);
111566
+ } else {
111567
+ return isAllowed(PERMISSIONS.RESOLVE_OTHER, role, isInternal, context);
111568
+ }
111392
111569
  });
111393
111570
  const allowReject = computed(() => {
111394
111571
  if (!generallyAllowed.value) return false;
111395
111572
  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);
111573
+ const context = {
111574
+ comment: props.comment,
111575
+ currentUser: proxy.$superdoc.config.user,
111576
+ superdoc: proxy.$superdoc
111577
+ };
111578
+ if (isOwnComment || props.comment.trackedChange) {
111579
+ return isAllowed(PERMISSIONS.REJECT_OWN, role, isInternal, context);
111580
+ } else {
111581
+ return isAllowed(PERMISSIONS.REJECT_OTHER, role, isInternal, context);
111582
+ }
111398
111583
  });
111399
111584
  const allowOverflow = computed(() => {
111400
111585
  if (!generallyAllowed.value) return false;
@@ -111411,9 +111596,14 @@ ${style2}
111411
111596
  options.add("edit");
111412
111597
  }
111413
111598
  const isOwnComment2 = props.comment.creatorEmail === proxy.$superdoc.config.user.email;
111414
- if (isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OWN, role, isInternal)) {
111599
+ const context = {
111600
+ comment: props.comment,
111601
+ currentUser: proxy.$superdoc.config.user,
111602
+ superdoc: proxy.$superdoc
111603
+ };
111604
+ if (isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OWN, role, isInternal, context)) {
111415
111605
  options.add("delete");
111416
- } else if (!isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OTHER, role, isInternal)) {
111606
+ } else if (!isOwnComment2 && isAllowed(PERMISSIONS.COMMENTS_DELETE_OTHER, role, isInternal, context)) {
111417
111607
  options.add("delete");
111418
111608
  }
111419
111609
  options.forEach((option) => allowedOptions.push(OVERFLOW_OPTIONS[option]));
@@ -111476,7 +111666,7 @@ ${style2}
111476
111666
  };
111477
111667
  }
111478
111668
  };
111479
- const CommentHeader = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-27b7739c"]]);
111669
+ const CommentHeader = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-0f4f53ef"]]);
111480
111670
  const _hoisted_1$d = { class: "input-section" };
111481
111671
  const _sfc_main$e = {
111482
111672
  __name: "CommentInput",
@@ -111896,7 +112086,7 @@ ${style2}
111896
112086
  };
111897
112087
  }
111898
112088
  };
111899
- const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-e07f3426"]]);
112089
+ const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-36e5f63e"]]);
111900
112090
  const _hoisted_1$b = { class: "comments-list" };
111901
112091
  const _hoisted_2$5 = { key: 0 };
111902
112092
  const _hoisted_3$3 = { class: "comment-item" };
@@ -113115,7 +113305,12 @@ ${style2}
113115
113305
  externalExtensions: proxy.$superdoc.config.editorExtensions || [],
113116
113306
  suppressDefaultDocxStyles: proxy.$superdoc.config.suppressDefaultDocxStyles,
113117
113307
  disableContextMenu: proxy.$superdoc.config.disableContextMenu,
113118
- jsonOverride: proxy.$superdoc.config.jsonOverride
113308
+ jsonOverride: proxy.$superdoc.config.jsonOverride,
113309
+ permissionResolver: (payload = {}) => proxy.$superdoc.canPerformPermission({
113310
+ role: proxy.$superdoc.config.role,
113311
+ isInternal: proxy.$superdoc.config.isInternal,
113312
+ ...payload
113313
+ })
113119
113314
  };
113120
113315
  return options;
113121
113316
  };
@@ -113484,7 +113679,7 @@ ${style2}
113484
113679
  };
113485
113680
  }
113486
113681
  };
113487
- const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-6de7ff3a"]]);
113682
+ const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-ac4ea6eb"]]);
113488
113683
  const createSuperdocVueApp = () => {
113489
113684
  const app = createApp(App);
113490
113685
  const pinia = createPinia();
@@ -113579,6 +113774,8 @@ ${style2}
113579
113774
  users: [],
113580
113775
  modules: {},
113581
113776
  // Optional: Modules to load. Use modules.ai.{your_key} to pass in your key
113777
+ permissionResolver: null,
113778
+ // Optional: Override for permission checks
113582
113779
  title: "SuperDoc",
113583
113780
  conversations: [],
113584
113781
  pagination: false,
@@ -113636,7 +113833,7 @@ ${style2}
113636
113833
  this.config.colors = shuffleArray(this.config.colors);
113637
113834
  this.userColorMap = /* @__PURE__ */ new Map();
113638
113835
  this.colorIndex = 0;
113639
- this.version = "0.23.0";
113836
+ this.version = "0.24.0";
113640
113837
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
113641
113838
  this.superdocId = config2.superdocId || v4();
113642
113839
  this.colors = this.config.colors;
@@ -113942,6 +114139,41 @@ ${style2}
113942
114139
  }
113943
114140
  });
113944
114141
  }
114142
+ /**
114143
+ * Determine whether the current configuration allows a given permission.
114144
+ * Used by downstream consumers (toolbar, context menu, commands) to keep
114145
+ * tracked-change affordances consistent with customer overrides.
114146
+ *
114147
+ * @param {Object} params
114148
+ * @param {string} params.permission Permission key to evaluate
114149
+ * @param {string} [params.role=this.config.role] Role to evaluate against
114150
+ * @param {boolean} [params.isInternal=this.config.isInternal] Internal/external flag
114151
+ * @param {Object|null} [params.comment] Comment object (if already resolved)
114152
+ * @param {Object|null} [params.trackedChange] Tracked change metadata (id, attrs, etc.)
114153
+ * @returns {boolean}
114154
+ */
114155
+ canPerformPermission({
114156
+ permission,
114157
+ role = this.config.role,
114158
+ isInternal = this.config.isInternal,
114159
+ comment = null,
114160
+ trackedChange = null
114161
+ } = {}) {
114162
+ if (!permission) return false;
114163
+ let resolvedComment = comment ?? trackedChange?.comment ?? null;
114164
+ const commentId = trackedChange?.commentId || trackedChange?.id;
114165
+ if (!resolvedComment && commentId && this.commentsStore?.getComment) {
114166
+ const storeComment = this.commentsStore.getComment(commentId);
114167
+ resolvedComment = storeComment?.getValues ? storeComment.getValues() : storeComment;
114168
+ }
114169
+ const context = {
114170
+ superdoc: this,
114171
+ currentUser: this.config.user,
114172
+ comment: resolvedComment ?? null,
114173
+ trackedChange: trackedChange ?? null
114174
+ };
114175
+ return isAllowed(permission, role, isInternal, context);
114176
+ }
113945
114177
  #addToolbar() {
113946
114178
  const moduleConfig = this.config.modules?.toolbar || {};
113947
114179
  this.toolbarElement = this.config.modules?.toolbar?.selector || this.config.toolbar;