@harbour-enterprises/superdoc 1.5.0-next.7 → 1.5.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.
@@ -1,6 +1,6 @@
1
1
  import { B as Buffer$2 } from "./jszip-B1fkPkPJ.es.js";
2
2
  import { t as twipsToInches, i as inchesToTwips, p as ptToTwips, l as linesToTwips, a as twipsToLines, b as pixelsToTwips, h as halfPointToPoints, c as twipsToPixels$2, d as convertSizeToCSS, e as inchesToPixels } from "./helpers-C8e9wR5l.es.js";
3
- import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-S5AfMnkn.es.js";
3
+ import { g as generateDocxRandomId, T as TextSelection$1, o as objectIncludes, w as wrapTextsInRuns, D as DOMParser$1, c as createDocFromMarkdown, a as createDocFromHTML, b as chainableEditorState, d as convertMarkdownToHTML, f as findParentNode, e as findParentNodeClosestToPos, h as generateRandom32BitHex, i as generateRandomSigned32BitIntStrId, P as PluginKey, j as Plugin, M as Mapping, N as NodeSelection, k as Selection, l as Slice, m as DOMSerializer, F as Fragment, n as Mark$1, p as dropPoint, A as AllSelection, q as Schema$1, s as canSplit, t as resolveRunProperties, u as encodeMarksFromRPr, v as liftTarget, x as canJoin, y as joinPoint, z as replaceStep$1, R as ReplaceAroundStep$1, B as htmlHandler, C as ReplaceStep, E as getResolvedParagraphProperties, G as changeListLevel, H as isList$1, I as updateNumberingProperties, L as ListHelpers, J as inputRulesPlugin, K as TrackDeleteMarkName$1, O as TrackInsertMarkName$1, Q as TrackFormatMarkName$1, U as AddMarkStep, V as RemoveMarkStep, W as CommandService, S as SuperConverter, X as EditorState, Y as unflattenListsInHtml, Z as SelectionRange, _ as Transform, $ as createOoxmlResolver, a0 as translator, a1 as translator$1, a2 as resolveDocxFontFamily, a3 as combineIndentProperties, a4 as _getReferencedTableStyles, a5 as decodeRPrFromMarks, a6 as calculateResolvedParagraphProperties, a7 as encodeCSSFromPPr, a8 as encodeCSSFromRPr, a9 as generateOrderedListIndex, aa as docxNumberingHelpers, ab as InputRule, ac as insertNewRelationship, ad as kebabCase$1, ae as getUnderlineCssString } from "./SuperConverter-CXmwt666.es.js";
4
4
  import { p as process$1, r as ref, C as global$1, c as computed, E as createElementBlock, F as Fragment$1, S as renderList, O as withModifiers, G as openBlock, P as normalizeClass, M as createCommentVNode, H as toDisplayString, K as createBaseVNode, U as createApp, f as onMounted, X as onUnmounted, R as withDirectives, v as unref, Y as vModelText, y as nextTick, L as normalizeStyle, u as watch, Z as withKeys, _ as createTextVNode, I as createVNode, h as h$1, $ as readonly, s as getCurrentInstance, o as onBeforeUnmount, j as reactive, b as onBeforeMount, i as inject, a0 as onActivated, a1 as onDeactivated, a2 as Comment, d as defineComponent, a as provide, g as Teleport, t as toRef, a3 as renderSlot, a4 as isVNode, D as shallowRef, w as watchEffect, T as Transition, a5 as mergeProps, a6 as vShow, a7 as cloneVNode, a8 as Text$2, m as markRaw, N as createBlock, J as withCtx, a9 as useCssVars, V as resolveDynamicComponent, aa as normalizeProps, ab as guardReactiveProps } from "./vue-BnBKJwCW.es.js";
5
5
  import "./jszip.min-DCl8qkFO.es.js";
6
6
  import { E as EventEmitter$1 } from "./eventemitter3-CwrdEv8r.es.js";
@@ -15507,7 +15507,7 @@ const canUseDOM = () => {
15507
15507
  return false;
15508
15508
  }
15509
15509
  };
15510
- const summaryVersion = "1.5.0-next.7";
15510
+ const summaryVersion = "1.5.0-next.8";
15511
15511
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
15512
15512
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
15513
15513
  function mapAttributes(attrs) {
@@ -18164,7 +18164,7 @@ class Editor extends EventEmitter {
18164
18164
  * Process collaboration migrations
18165
18165
  */
18166
18166
  processCollaborationMigrations() {
18167
- console.debug("[checkVersionMigrations] Current editor version", "1.5.0-next.7");
18167
+ console.debug("[checkVersionMigrations] Current editor version", "1.5.0-next.8");
18168
18168
  if (!this.options.ydoc) return;
18169
18169
  const metaMap = this.options.ydoc.getMap("meta");
18170
18170
  let docVersion = metaMap.get("version");
@@ -23452,7 +23452,8 @@ const lineStyles = (lineHeight2) => ({
23452
23452
  // The primary fix uses accurate font metrics from Canvas API, but this
23453
23453
  // provides defense-in-depth against any remaining sub-pixel rendering
23454
23454
  // differences between measurement and display.
23455
- overflow: "visible"
23455
+ overflow: "visible",
23456
+ zIndex: "10"
23456
23457
  });
23457
23458
  const PRINT_STYLES = `
23458
23459
  @media print {
@@ -23758,6 +23759,7 @@ const SDT_CONTAINER_STYLES = `
23758
23759
  border: 1px solid #629be7;
23759
23760
  position: relative;
23760
23761
  display: inline;
23762
+ z-index: 10;
23761
23763
  }
23762
23764
 
23763
23765
  /* Hover effect for inline structured content */
@@ -49091,8 +49093,6 @@ class EditorOverlayManager {
49091
49093
  #activeRegion = null;
49092
49094
  /** Full-width border line element (MS Word style) */
49093
49095
  #borderLine = null;
49094
- /** Dimming overlay element (for dimming body content during editing) */
49095
- #dimmingOverlay = null;
49096
49096
  /**
49097
49097
  * Creates a new EditorOverlayManager instance.
49098
49098
  *
@@ -49386,17 +49386,6 @@ class EditorOverlayManager {
49386
49386
  }
49387
49387
  }
49388
49388
  }
49389
- /**
49390
- * Hides and removes the dimming overlay.
49391
- * @internal Reserved for future implementation of body dimming during header/footer editing.
49392
- */
49393
- // eslint-disable-next-line no-unused-private-class-members
49394
- #hideDimmingOverlay() {
49395
- if (this.#dimmingOverlay) {
49396
- this.#dimmingOverlay.remove();
49397
- this.#dimmingOverlay = null;
49398
- }
49399
- }
49400
49389
  /**
49401
49390
  * Shows a full-width border line at the bottom of the header or top of the footer.
49402
49391
  * This creates the MS Word style visual indicator spanning edge-to-edge of the page.
@@ -51569,6 +51558,7 @@ class PresentationEditor extends EventEmitter {
51569
51558
  #viewportHost;
51570
51559
  #painterHost;
51571
51560
  #selectionOverlay;
51561
+ #permissionOverlay = null;
51572
51562
  #hiddenHost;
51573
51563
  #layoutOptions;
51574
51564
  #layoutState = { blocks: [], measures: [], layout: null, bookmarks: /* @__PURE__ */ new Map() };
@@ -51728,6 +51718,16 @@ class PresentationEditor extends EventEmitter {
51728
51718
  });
51729
51719
  this.#domIndexObserverManager.setup();
51730
51720
  this.#selectionSync.on("render", () => this.#updateSelection());
51721
+ this.#selectionSync.on("render", () => this.#updatePermissionOverlay());
51722
+ this.#permissionOverlay = doc2.createElement("div");
51723
+ this.#permissionOverlay.className = "presentation-editor__permission-overlay";
51724
+ Object.assign(this.#permissionOverlay.style, {
51725
+ position: "absolute",
51726
+ inset: "0",
51727
+ pointerEvents: "none",
51728
+ zIndex: "5"
51729
+ });
51730
+ this.#viewportHost.appendChild(this.#permissionOverlay);
51731
51731
  this.#selectionOverlay = doc2.createElement("div");
51732
51732
  this.#selectionOverlay.className = "presentation-editor__selection-overlay";
51733
51733
  this.#selectionOverlay.id = `presentation-overlay-${options.documentId || "default"}`;
@@ -51813,7 +51813,7 @@ class PresentationEditor extends EventEmitter {
51813
51813
  const normalizedEditorProps = {
51814
51814
  ...editorOptions.editorProps ?? {},
51815
51815
  editable: () => {
51816
- return this.#documentMode !== "viewing";
51816
+ return !this.#isViewLocked();
51817
51817
  }
51818
51818
  };
51819
51819
  try {
@@ -52255,6 +52255,7 @@ class PresentationEditor extends EventEmitter {
52255
52255
  this.#pendingDocChange = true;
52256
52256
  this.#scheduleRerender();
52257
52257
  }
52258
+ this.#updatePermissionOverlay();
52258
52259
  }
52259
52260
  #syncDocumentModeClass() {
52260
52261
  if (!this.#visibleHost) return;
@@ -53644,7 +53645,7 @@ class PresentationEditor extends EventEmitter {
53644
53645
  win,
53645
53646
  this.#visibleHost,
53646
53647
  () => this.#getActiveDomTarget(),
53647
- () => this.#documentMode !== "viewing"
53648
+ () => !this.#isViewLocked()
53648
53649
  );
53649
53650
  this.#inputBridge.bind();
53650
53651
  }
@@ -54569,7 +54570,7 @@ class PresentationEditor extends EventEmitter {
54569
54570
  this.#dragUsedPageNotMountedFallback = false;
54570
54571
  return;
54571
54572
  }
54572
- if (this.#session.mode !== "body" || this.#documentMode === "viewing") {
54573
+ if (this.#session.mode !== "body" || this.#isViewLocked()) {
54573
54574
  this.#dragLastPointer = null;
54574
54575
  this.#dragLastRawHit = null;
54575
54576
  this.#dragUsedPageNotMountedFallback = false;
@@ -54919,6 +54920,7 @@ class PresentationEditor extends EventEmitter {
54919
54920
  this.#epochMapper.onLayoutComplete(layoutEpoch);
54920
54921
  this.#selectionSync.onLayoutComplete(layoutEpoch);
54921
54922
  layoutCompleted = true;
54923
+ this.#updatePermissionOverlay();
54922
54924
  this.#layoutError = null;
54923
54925
  this.#layoutErrorState = "healthy";
54924
54926
  this.#dismissErrorBanner();
@@ -55007,7 +55009,7 @@ class PresentationEditor extends EventEmitter {
55007
55009
  if (!this.#localSelectionLayer) {
55008
55010
  return;
55009
55011
  }
55010
- if (this.#documentMode === "viewing") {
55012
+ if (this.#isViewLocked()) {
55011
55013
  try {
55012
55014
  this.#localSelectionLayer.innerHTML = "";
55013
55015
  } catch (error) {
@@ -55089,6 +55091,76 @@ class PresentationEditor extends EventEmitter {
55089
55091
  }
55090
55092
  }
55091
55093
  }
55094
+ /**
55095
+ * Updates the permission overlay (w:permStart/w:permEnd) to match the current editor permission ranges.
55096
+ *
55097
+ * This method is called after layout completes to ensure permission overlay
55098
+ * is based on stable permission ranges data.
55099
+ */
55100
+ #updatePermissionOverlay() {
55101
+ const overlay2 = this.#permissionOverlay;
55102
+ if (!overlay2) {
55103
+ return;
55104
+ }
55105
+ if (this.#session.mode !== "body") {
55106
+ overlay2.innerHTML = "";
55107
+ return;
55108
+ }
55109
+ const permissionStorage = this.#editor?.storage?.permissionRanges;
55110
+ const ranges = permissionStorage?.ranges ?? [];
55111
+ const shouldRender = ranges.length > 0;
55112
+ if (!shouldRender) {
55113
+ overlay2.innerHTML = "";
55114
+ return;
55115
+ }
55116
+ const layout = this.#layoutState.layout;
55117
+ if (!layout) {
55118
+ overlay2.innerHTML = "";
55119
+ return;
55120
+ }
55121
+ const docEpoch = this.#epochMapper.getCurrentEpoch();
55122
+ if (this.#layoutEpoch < docEpoch) {
55123
+ return;
55124
+ }
55125
+ const pageHeight = this.#getBodyPageHeight();
55126
+ const pageGap = layout.pageGap ?? this.#getEffectivePageGap();
55127
+ const fragment = overlay2.ownerDocument?.createDocumentFragment();
55128
+ if (!fragment) {
55129
+ overlay2.innerHTML = "";
55130
+ return;
55131
+ }
55132
+ ranges.forEach(({ from: from3, to }) => {
55133
+ const rects = this.#computeSelectionRectsFromDom(from3, to);
55134
+ if (!rects?.length) {
55135
+ return;
55136
+ }
55137
+ rects.forEach((rect) => {
55138
+ const pageLocalY = rect.y - rect.pageIndex * (pageHeight + pageGap);
55139
+ const coords = this.#convertPageLocalToOverlayCoords(rect.pageIndex, rect.x, pageLocalY);
55140
+ if (!coords) {
55141
+ return;
55142
+ }
55143
+ const highlight = overlay2.ownerDocument?.createElement("div");
55144
+ if (!highlight) {
55145
+ return;
55146
+ }
55147
+ highlight.className = "presentation-editor__permission-highlight";
55148
+ Object.assign(highlight.style, {
55149
+ position: "absolute",
55150
+ left: `${coords.x}px`,
55151
+ top: `${coords.y}px`,
55152
+ width: `${Math.max(1, rect.width)}px`,
55153
+ height: `${Math.max(1, rect.height)}px`,
55154
+ borderRadius: "2px",
55155
+ pointerEvents: "none",
55156
+ zIndex: 1
55157
+ });
55158
+ fragment.appendChild(highlight);
55159
+ });
55160
+ });
55161
+ overlay2.innerHTML = "";
55162
+ overlay2.appendChild(fragment);
55163
+ }
55092
55164
  #resolveLayoutOptions(blocks, sectionMetadata) {
55093
55165
  const defaults = this.#computeDefaultLayoutDefaults();
55094
55166
  const firstSection = blocks?.find(
@@ -55834,7 +55906,7 @@ class PresentationEditor extends EventEmitter {
55834
55906
  this.#announce(announcement.message);
55835
55907
  }
55836
55908
  #validateHeaderFooterEditPermission() {
55837
- if (this.#documentMode === "viewing") {
55909
+ if (this.#isViewLocked()) {
55838
55910
  return { allowed: false, reason: "documentMode" };
55839
55911
  }
55840
55912
  if (!this.#editor.isEditable) {
@@ -56697,6 +56769,17 @@ class PresentationEditor extends EventEmitter {
56697
56769
  this.#errorBanner = null;
56698
56770
  this.#errorBannerMessage = null;
56699
56771
  }
56772
+ /**
56773
+ * Determines whether the current viewing mode should block edits.
56774
+ * When documentMode is viewing but the active editor has been toggled
56775
+ * back to editable (e.g. permission ranges), we treat the view as editable.
56776
+ */
56777
+ #isViewLocked() {
56778
+ if (this.#documentMode !== "viewing") return false;
56779
+ const hasPermissionOverride = !!this.#editor?.storage?.permissionRanges?.hasAllowedRanges;
56780
+ if (hasPermissionOverride) return false;
56781
+ return this.#documentMode === "viewing";
56782
+ }
56700
56783
  /**
56701
56784
  * Applies vertical alignment and font scaling to layout DOM elements for subscript/superscript rendering.
56702
56785
  *
@@ -58576,6 +58659,11 @@ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ O
58576
58659
  parseTagObject
58577
58660
  }, Symbol.toStringTag, { value: "Module" }));
58578
58661
  const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
58662
+ function isValidIntegerId(id) {
58663
+ if (id === null || id === void 0) return true;
58664
+ const str = String(id);
58665
+ return /^-?\d+$/.test(str);
58666
+ }
58579
58667
  const findFirstTextNode = (node) => {
58580
58668
  let firstTextNode = null;
58581
58669
  node.descendants((child) => {
@@ -58617,6 +58705,9 @@ const StructuredContentCommands = Extension.create({
58617
58705
  * });
58618
58706
  */
58619
58707
  insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state, tr }) => {
58708
+ if (options.attrs?.id !== void 0 && !isValidIntegerId(options.attrs.id)) {
58709
+ throw new Error("Invalid structured content id - must be an integer, got: " + options.attrs.id);
58710
+ }
58620
58711
  const { schema } = editor;
58621
58712
  let { from: from3, to } = state.selection;
58622
58713
  if (dispatch) {
@@ -58679,6 +58770,9 @@ const StructuredContentCommands = Extension.create({
58679
58770
  * });
58680
58771
  */
58681
58772
  insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state, tr }) => {
58773
+ if (options.attrs?.id !== void 0 && !isValidIntegerId(options.attrs.id)) {
58774
+ throw new Error("Invalid structured content id - must be an integer, got: " + options.attrs.id);
58775
+ }
58682
58776
  const { schema } = editor;
58683
58777
  let { from: from3, to } = state.selection;
58684
58778
  if (dispatch) {
@@ -58736,6 +58830,9 @@ const StructuredContentCommands = Extension.create({
58736
58830
  * });
58737
58831
  */
58738
58832
  updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state, tr }) => {
58833
+ if (options.attrs?.id !== void 0 && !isValidIntegerId(options.attrs.id)) {
58834
+ throw new Error("Invalid structured content id - must be an integer, got: " + options.attrs.id);
58835
+ }
58739
58836
  const structuredContentTags = getStructuredContentTagsById(id, state);
58740
58837
  if (!structuredContentTags.length) {
58741
58838
  return true;
@@ -58863,6 +58960,9 @@ const StructuredContentCommands = Extension.create({
58863
58960
  * });
58864
58961
  */
58865
58962
  updateStructuredContentByGroup: (group, options = {}) => ({ editor, dispatch, state, tr }) => {
58963
+ if (options.attrs?.id !== void 0 && !isValidIntegerId(options.attrs.id)) {
58964
+ throw new Error("Invalid structured content id - must be an integer, got: " + options.attrs.id);
58965
+ }
58866
58966
  const structuredContentTags = getStructuredContentByGroup(group, state);
58867
58967
  if (!structuredContentTags.length) {
58868
58968
  return true;
@@ -59540,7 +59640,7 @@ const mergeRanges$2 = (ranges, docSize) => {
59540
59640
  }
59541
59641
  return merged;
59542
59642
  };
59543
- const collectChangedRanges = (trs, docSize) => {
59643
+ const collectChangedRanges$1 = (trs, docSize) => {
59544
59644
  const ranges = [];
59545
59645
  trs.forEach((tr) => {
59546
59646
  if (!tr.docChanged) return;
@@ -59743,7 +59843,7 @@ const wrapTextInRunsPlugin = (editor) => {
59743
59843
  const runType = newState.schema.nodes.run;
59744
59844
  if (!runType) return null;
59745
59845
  pendingRanges = mapRangesThroughTransactions(pendingRanges, transactions, docSize);
59746
- const changedRanges = collectChangedRanges(transactions, docSize);
59846
+ const changedRanges = collectChangedRanges$1(transactions, docSize);
59747
59847
  pendingRanges = mergeRanges$2([...pendingRanges, ...changedRanges], docSize);
59748
59848
  if (view?.composing) {
59749
59849
  return null;
@@ -74618,6 +74718,283 @@ const NodeResizer = Extension.create({
74618
74718
  return [nodeResizer(["image"], this.editor)];
74619
74719
  }
74620
74720
  });
74721
+ const PERMISSION_PLUGIN_KEY = new PluginKey("permissionRanges");
74722
+ const EVERYONE_GROUP = "everyone";
74723
+ const EMPTY_IDENTIFIER_SET = Object.freeze(/* @__PURE__ */ new Set());
74724
+ const normalizeIdentifier = (value) => typeof value === "string" ? value.trim().toLowerCase() : "";
74725
+ const buildAllowedIdentifierSet = (editor) => {
74726
+ const email = normalizeIdentifier(editor?.options?.user?.email);
74727
+ if (!email) {
74728
+ return EMPTY_IDENTIFIER_SET;
74729
+ }
74730
+ const [localPart, domain] = email.split("@");
74731
+ if (!localPart || !domain) {
74732
+ return EMPTY_IDENTIFIER_SET;
74733
+ }
74734
+ const formatted = `${domain}\\${localPart}`;
74735
+ return formatted ? /* @__PURE__ */ new Set([formatted]) : EMPTY_IDENTIFIER_SET;
74736
+ };
74737
+ const isEveryoneGroup = (value) => normalizeIdentifier(value) === EVERYONE_GROUP;
74738
+ const isRangeAllowedForUser = (attrs, allowedIdentifiers) => {
74739
+ if (!attrs) return false;
74740
+ if (isEveryoneGroup(attrs.edGrp)) {
74741
+ return true;
74742
+ }
74743
+ if (!allowedIdentifiers?.size) {
74744
+ return false;
74745
+ }
74746
+ const normalizedEd = normalizeIdentifier(attrs.ed);
74747
+ return normalizedEd && allowedIdentifiers.has(normalizedEd);
74748
+ };
74749
+ const getPermissionNodeId = (node, pos, fallbackPrefix) => String(node.attrs?.id ?? `${fallbackPrefix}-${pos}`);
74750
+ const buildPermissionState = (doc2, allowedIdentifiers = EMPTY_IDENTIFIER_SET) => {
74751
+ const ranges = [];
74752
+ const openRanges = /* @__PURE__ */ new Map();
74753
+ doc2.descendants((node, pos) => {
74754
+ if (node.type?.name === "permStart") {
74755
+ const id = getPermissionNodeId(node, pos, "permStart");
74756
+ openRanges.set(id, {
74757
+ from: pos + node.nodeSize,
74758
+ attrs: node.attrs ?? {}
74759
+ });
74760
+ return false;
74761
+ }
74762
+ if (node.type?.name === "permEnd") {
74763
+ const id = getPermissionNodeId(node, pos, "permEnd");
74764
+ const start2 = openRanges.get(id);
74765
+ if (start2 && isRangeAllowedForUser(start2.attrs, allowedIdentifiers)) {
74766
+ const to = Math.max(pos, start2.from);
74767
+ if (to > start2.from) {
74768
+ ranges.push({
74769
+ id,
74770
+ from: start2.from,
74771
+ to
74772
+ });
74773
+ }
74774
+ }
74775
+ if (start2) {
74776
+ openRanges.delete(id);
74777
+ }
74778
+ return false;
74779
+ }
74780
+ });
74781
+ return {
74782
+ ranges,
74783
+ hasAllowedRanges: ranges.length > 0
74784
+ };
74785
+ };
74786
+ const collectPermissionTags = (doc2, permStartType, permEndType) => {
74787
+ const tags = /* @__PURE__ */ new Map();
74788
+ doc2.descendants((node, pos) => {
74789
+ if (node.type !== permStartType && node.type !== permEndType) {
74790
+ return;
74791
+ }
74792
+ const id = node.attrs?.id;
74793
+ if (!id) {
74794
+ return;
74795
+ }
74796
+ const entry = tags.get(id) ?? {};
74797
+ if (node.type === permStartType) {
74798
+ entry.start = { pos, attrs: node.attrs ?? {} };
74799
+ } else if (node.type === permEndType) {
74800
+ entry.end = { pos, attrs: node.attrs ?? {} };
74801
+ }
74802
+ tags.set(id, entry);
74803
+ });
74804
+ return tags;
74805
+ };
74806
+ const clampPosition = (pos, size2) => {
74807
+ if (Number.isNaN(pos) || !Number.isFinite(pos)) {
74808
+ return 0;
74809
+ }
74810
+ return Math.max(0, Math.min(pos, size2));
74811
+ };
74812
+ const trimPermissionTagsFromRange = (doc2, range, permStartType, permEndType) => {
74813
+ let from3 = range.from;
74814
+ let to = range.to;
74815
+ while (from3 < to) {
74816
+ const node = doc2.nodeAt(from3);
74817
+ if (!node || node.type !== permStartType && node.type !== permEndType) {
74818
+ break;
74819
+ }
74820
+ from3 += node.nodeSize;
74821
+ }
74822
+ while (to > from3) {
74823
+ const $pos = doc2.resolve(to);
74824
+ const nodeBefore = $pos.nodeBefore;
74825
+ if (!nodeBefore || nodeBefore.type !== permStartType && nodeBefore.type !== permEndType) {
74826
+ break;
74827
+ }
74828
+ to -= nodeBefore.nodeSize;
74829
+ }
74830
+ return { from: from3, to };
74831
+ };
74832
+ const collectChangedRanges = (tr) => {
74833
+ const ranges = [];
74834
+ tr.mapping.maps.forEach((map3) => {
74835
+ map3.forEach((oldStart, oldEnd) => {
74836
+ const from3 = Math.min(oldStart, oldEnd);
74837
+ const to = Math.max(oldStart, oldEnd);
74838
+ ranges.push({ from: from3, to });
74839
+ });
74840
+ });
74841
+ return ranges;
74842
+ };
74843
+ const isRangeAllowed = (range, allowedRanges) => {
74844
+ if (!allowedRanges?.length) return false;
74845
+ return allowedRanges.some((allowed) => range.from >= allowed.from && range.to <= allowed.to);
74846
+ };
74847
+ const PermissionRanges = Extension.create({
74848
+ name: "permissionRanges",
74849
+ addStorage() {
74850
+ return {
74851
+ ranges: [],
74852
+ hasAllowedRanges: false
74853
+ };
74854
+ },
74855
+ addPmPlugins() {
74856
+ const editor = this.editor;
74857
+ const storage = this.storage;
74858
+ let originalSetDocumentMode = null;
74859
+ const getAllowedIdentifiers = () => buildAllowedIdentifierSet(editor);
74860
+ const toggleEditableIfAllowed = (hasAllowedRanges) => {
74861
+ if (!editor || editor.isDestroyed) return;
74862
+ if (editor.options.documentMode !== "viewing") return;
74863
+ if (hasAllowedRanges && !editor.isEditable) {
74864
+ editor.setEditable(true, false);
74865
+ } else if (!hasAllowedRanges && editor.isEditable) {
74866
+ editor.setEditable(false, false);
74867
+ }
74868
+ };
74869
+ const updateEditableState = (hasAllowedRanges) => {
74870
+ const nextValue = Boolean(hasAllowedRanges);
74871
+ const previousValue = storage.hasAllowedRanges;
74872
+ storage.hasAllowedRanges = nextValue;
74873
+ if (previousValue === nextValue) {
74874
+ return;
74875
+ }
74876
+ toggleEditableIfAllowed(nextValue);
74877
+ };
74878
+ if (editor && typeof editor.setDocumentMode === "function") {
74879
+ originalSetDocumentMode = editor.setDocumentMode.bind(editor);
74880
+ editor.setDocumentMode = (mode, caller) => {
74881
+ originalSetDocumentMode(mode, caller);
74882
+ const state = editor.state;
74883
+ if (!state) return;
74884
+ const pluginState = PERMISSION_PLUGIN_KEY.getState(state);
74885
+ if (pluginState) {
74886
+ toggleEditableIfAllowed(pluginState.hasAllowedRanges);
74887
+ }
74888
+ };
74889
+ }
74890
+ return [
74891
+ new Plugin({
74892
+ key: PERMISSION_PLUGIN_KEY,
74893
+ state: {
74894
+ init(_2, state) {
74895
+ const permissionState = buildPermissionState(state.doc, getAllowedIdentifiers());
74896
+ storage.ranges = permissionState.ranges;
74897
+ updateEditableState(permissionState.hasAllowedRanges);
74898
+ return permissionState;
74899
+ },
74900
+ apply(tr, value, _oldState, newState) {
74901
+ let permissionState = value;
74902
+ if (tr.docChanged) {
74903
+ permissionState = buildPermissionState(newState.doc, getAllowedIdentifiers());
74904
+ storage.ranges = permissionState.ranges;
74905
+ updateEditableState(permissionState.hasAllowedRanges);
74906
+ }
74907
+ return permissionState;
74908
+ }
74909
+ },
74910
+ view() {
74911
+ return {
74912
+ destroy() {
74913
+ if (editor && originalSetDocumentMode) {
74914
+ editor.setDocumentMode = originalSetDocumentMode;
74915
+ }
74916
+ }
74917
+ };
74918
+ },
74919
+ // Appends transactions to the document to ensure permission ranges are updated.
74920
+ appendTransaction(transactions, oldState, newState) {
74921
+ if (!transactions.some((tr2) => tr2.docChanged)) return null;
74922
+ const permStartType = newState.schema.nodes["permStart"];
74923
+ const permEndType = newState.schema.nodes["permEnd"];
74924
+ if (!permStartType || !permEndType) return null;
74925
+ const oldTags = collectPermissionTags(oldState.doc, permStartType, permEndType);
74926
+ if (!oldTags.size) {
74927
+ return null;
74928
+ }
74929
+ const newTags = collectPermissionTags(newState.doc, permStartType, permEndType);
74930
+ const mappingToNew = new Mapping();
74931
+ transactions.forEach((tr2) => {
74932
+ mappingToNew.appendMapping(tr2.mapping);
74933
+ });
74934
+ const pendingInsertions = [];
74935
+ oldTags.forEach((tag, id) => {
74936
+ const current = newTags.get(id);
74937
+ if (tag.start && !current?.start) {
74938
+ const mapped = mappingToNew.mapResult(tag.start.pos, -1);
74939
+ pendingInsertions.push({
74940
+ pos: mapped.pos,
74941
+ nodeType: permStartType,
74942
+ attrs: tag.start.attrs,
74943
+ priority: 0
74944
+ });
74945
+ }
74946
+ if (tag.end && !current?.end) {
74947
+ const mapped = mappingToNew.mapResult(tag.end.pos, 1);
74948
+ pendingInsertions.push({
74949
+ pos: mapped.pos,
74950
+ nodeType: permEndType,
74951
+ attrs: tag.end.attrs,
74952
+ priority: 1
74953
+ });
74954
+ }
74955
+ });
74956
+ if (!pendingInsertions.length) {
74957
+ return null;
74958
+ }
74959
+ pendingInsertions.sort((a, b2) => {
74960
+ if (a.pos === b2.pos) {
74961
+ return a.priority - b2.priority;
74962
+ }
74963
+ return a.pos - b2.pos;
74964
+ });
74965
+ const tr = newState.tr;
74966
+ let offset2 = 0;
74967
+ pendingInsertions.forEach((item) => {
74968
+ const node = item.nodeType.create(item.attrs);
74969
+ const insertPos = clampPosition(item.pos + offset2, tr.doc.content.size);
74970
+ tr.insert(insertPos, node);
74971
+ offset2 += node.nodeSize;
74972
+ });
74973
+ return tr.docChanged ? tr : null;
74974
+ },
74975
+ // Filters transactions to ensure only allowed edits are applied.
74976
+ filterTransaction(tr, state) {
74977
+ if (!tr.docChanged) return true;
74978
+ if (!editor || editor.options.documentMode !== "viewing") return true;
74979
+ const pluginState = PERMISSION_PLUGIN_KEY.getState(state);
74980
+ if (!pluginState?.hasAllowedRanges) {
74981
+ return true;
74982
+ }
74983
+ const changedRanges = collectChangedRanges(tr);
74984
+ if (!changedRanges.length) return true;
74985
+ const permStartType = state.schema.nodes["permStart"];
74986
+ const permEndType = state.schema.nodes["permEnd"];
74987
+ if (!permStartType || !permEndType) return true;
74988
+ const allRangesAllowed = changedRanges.every((range) => {
74989
+ const trimmed = trimPermissionTagsFromRange(state.doc, range, permStartType, permEndType);
74990
+ return isRangeAllowed(trimmed, pluginState.ranges);
74991
+ });
74992
+ return allRangesAllowed;
74993
+ }
74994
+ })
74995
+ ];
74996
+ }
74997
+ });
74621
74998
  const PermStart = Node$1.create({
74622
74999
  name: "permStart",
74623
75000
  group: "inline",
@@ -74777,6 +75154,7 @@ const getStarterExtensions = () => {
74777
75154
  ShapeGroup,
74778
75155
  PermStart,
74779
75156
  PermEnd,
75157
+ PermissionRanges,
74780
75158
  PassthroughInline,
74781
75159
  PassthroughBlock
74782
75160
  ];
package/dist/style.css CHANGED
@@ -53,6 +53,13 @@ a {
53
53
  .presentation-editor__selection-caret {
54
54
  animation: superdoc-caret-blink 1.2s steps(2, start) infinite;
55
55
  }
56
+ .presentation-editor__permission-overlay {
57
+ pointer-events: none;
58
+ }
59
+ .presentation-editor__permission-highlight {
60
+ border-radius: 2px;
61
+ background-color: rgba(199, 200, 217, 0.55);
62
+ }
56
63
  @keyframes superdoc-caret-blink {
57
64
  0%,
58
65
  45% {
@@ -2,6 +2,6 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  require("../chunks/jszip-C8_CqJxM.cjs");
4
4
  require("../chunks/helpers-nOdwpmwb.cjs");
5
- const superEditor_converter = require("../chunks/SuperConverter-CIjArITT.cjs");
5
+ const superEditor_converter = require("../chunks/SuperConverter-iLb7JzYI.cjs");
6
6
  require("../chunks/uuid-R7L08bOx.cjs");
7
7
  exports.SuperConverter = superEditor_converter.SuperConverter;
@@ -1,6 +1,6 @@
1
1
  import "../chunks/jszip-B1fkPkPJ.es.js";
2
2
  import "../chunks/helpers-C8e9wR5l.es.js";
3
- import { S } from "../chunks/SuperConverter-S5AfMnkn.es.js";
3
+ import { S } from "../chunks/SuperConverter-CXmwt666.es.js";
4
4
  import "../chunks/uuid-CjlX8hrF.es.js";
5
5
  export {
6
6
  S as SuperConverter
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./chunks/index-LpMObyDy.cjs");
3
+ const index = require("./chunks/index-CJcGqc_k.cjs");
4
4
  const superEditor_docxZipper = require("./super-editor/docx-zipper.cjs");
5
5
  const superEditor_fileZipper = require("./super-editor/file-zipper.cjs");
6
6
  const vue = require("./chunks/vue-De9wkgLl.cjs");
7
- const superEditor_converter = require("./chunks/SuperConverter-CIjArITT.cjs");
7
+ const superEditor_converter = require("./chunks/SuperConverter-iLb7JzYI.cjs");
8
8
  function isNodeType(node, name) {
9
9
  return node.type.name === name;
10
10
  }
@@ -1,9 +1,9 @@
1
- import { ax as Node, ay as Mark } from "./chunks/index-7ebfCUN0.es.js";
2
- import { ao, au, a9, ab, aw, am, av, aA, an, ak, aq, az, aa, as, aC, aE, aB, ac, aD, ar, at } from "./chunks/index-7ebfCUN0.es.js";
1
+ import { ax as Node, ay as Mark } from "./chunks/index-DIb368iJ.es.js";
2
+ import { ao, au, a9, ab, aw, am, av, aA, an, ak, aq, az, aa, as, aC, aE, aB, ac, aD, ar, at } from "./chunks/index-DIb368iJ.es.js";
3
3
  import { default as default2 } from "./super-editor/docx-zipper.es.js";
4
4
  import { createZip } from "./super-editor/file-zipper.es.js";
5
5
  import { d as defineComponent, E as createElementBlock, G as openBlock, K as createBaseVNode } from "./chunks/vue-BnBKJwCW.es.js";
6
- import { S, r } from "./chunks/SuperConverter-S5AfMnkn.es.js";
6
+ import { S, r } from "./chunks/SuperConverter-CXmwt666.es.js";
7
7
  function isNodeType(node, name) {
8
8
  return node.type.name === name;
9
9
  }
package/dist/superdoc.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./chunks/index-LpMObyDy.cjs");
4
- const superdoc = require("./chunks/index-CbbEjpJ0.cjs");
5
- const superEditor_converter = require("./chunks/SuperConverter-CIjArITT.cjs");
3
+ const index = require("./chunks/index-CJcGqc_k.cjs");
4
+ const superdoc = require("./chunks/index-Cv7KI1Ex.cjs");
5
+ const superEditor_converter = require("./chunks/SuperConverter-iLb7JzYI.cjs");
6
6
  const blankDocx = require("./chunks/blank-docx-DfW3Eeh2.cjs");
7
7
  require("./chunks/jszip-C8_CqJxM.cjs");
8
8
  require("./chunks/helpers-nOdwpmwb.cjs");