@harbour-enterprises/superdoc 0.21.0-next.4 → 0.21.0-next.5

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.
@@ -83552,6 +83552,115 @@ runCommandWithArgumentOnly_fn = function({ item, argument, noArgumentCallback =
83552
83552
  this.updateToolbarState();
83553
83553
  }
83554
83554
  };
83555
+ const onMarginClickCursorChange = (event, editor) => {
83556
+ const y2 = event.clientY;
83557
+ const x = event.clientX;
83558
+ const { view } = editor;
83559
+ const editorRect = view.dom.getBoundingClientRect();
83560
+ let coords = {
83561
+ left: 0,
83562
+ top: y2
83563
+ };
83564
+ let isRightMargin = false;
83565
+ if (x > editorRect.right) {
83566
+ coords.left = editorRect.left + editorRect.width - 1;
83567
+ isRightMargin = true;
83568
+ } else if (x < editorRect.left) {
83569
+ coords.left = editorRect.left;
83570
+ }
83571
+ const pos = view.posAtCoords(coords)?.pos;
83572
+ if (pos) {
83573
+ let cursorPos = pos;
83574
+ if (isRightMargin) {
83575
+ const $pos = view.state.doc.resolve(pos);
83576
+ const charOffset = $pos.textOffset;
83577
+ const node = view.state.doc.nodeAt(pos);
83578
+ const text = node?.text;
83579
+ const charAtPos = text?.charAt(charOffset);
83580
+ cursorPos = node?.isText && charAtPos !== " " ? pos - 1 : pos;
83581
+ }
83582
+ const transaction = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, cursorPos));
83583
+ view.dispatch(transaction);
83584
+ view.focus();
83585
+ }
83586
+ };
83587
+ const checkNodeSpecificClicks = (editor, event, popoverControls) => {
83588
+ if (!editor) return;
83589
+ if (selectionHasNodeOrMark(editor.view.state, "link", { requireEnds: true })) {
83590
+ popoverControls.component = LinkInput;
83591
+ popoverControls.position = {
83592
+ left: `${event.clientX - editor.element.getBoundingClientRect().left}px`,
83593
+ top: `${event.clientY - editor.element.getBoundingClientRect().top + 15}px`
83594
+ };
83595
+ popoverControls.props = {
83596
+ showInput: true
83597
+ };
83598
+ popoverControls.visible = true;
83599
+ }
83600
+ };
83601
+ function selectionHasNodeOrMark(state2, name, options = {}) {
83602
+ const { requireEnds = false } = options;
83603
+ const $from = state2.selection.$from;
83604
+ const $to = state2.selection.$to;
83605
+ if (requireEnds) {
83606
+ for (let d2 = $from.depth; d2 > 0; d2--) {
83607
+ if ($from.node(d2).type.name === name) {
83608
+ return true;
83609
+ }
83610
+ }
83611
+ for (let d2 = $to.depth; d2 > 0; d2--) {
83612
+ if ($to.node(d2).type.name === name) {
83613
+ return true;
83614
+ }
83615
+ }
83616
+ } else {
83617
+ for (let d2 = $from.depth; d2 > 0; d2--) {
83618
+ if ($from.node(d2).type.name === name) {
83619
+ return true;
83620
+ }
83621
+ }
83622
+ }
83623
+ const markType = state2.schema.marks[name];
83624
+ if (markType) {
83625
+ const { from: from2, to, empty: empty2 } = state2.selection;
83626
+ if (requireEnds) {
83627
+ const fromMarks = markType.isInSet($from.marks());
83628
+ const toMarks = markType.isInSet($to.marks());
83629
+ if (fromMarks || toMarks) {
83630
+ return true;
83631
+ }
83632
+ if (empty2 && markType.isInSet(state2.storedMarks || $from.marks())) {
83633
+ return true;
83634
+ }
83635
+ } else {
83636
+ if (empty2) {
83637
+ if (markType.isInSet(state2.storedMarks || $from.marks())) {
83638
+ return true;
83639
+ }
83640
+ } else {
83641
+ let hasMark = false;
83642
+ state2.doc.nodesBetween(from2, to, (node) => {
83643
+ if (markType.isInSet(node.marks)) {
83644
+ hasMark = true;
83645
+ return false;
83646
+ }
83647
+ });
83648
+ if (hasMark) return true;
83649
+ }
83650
+ }
83651
+ }
83652
+ return false;
83653
+ }
83654
+ function moveCursorToMouseEvent(event, editor) {
83655
+ const { view } = editor;
83656
+ const coords = { left: event.clientX, top: event.clientY };
83657
+ const pos = view.posAtCoords(coords)?.pos;
83658
+ if (typeof pos === "number") {
83659
+ const tr = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, pos));
83660
+ view.dispatch(tr);
83661
+ view.focus();
83662
+ }
83663
+ }
83555
83664
  const ICONS = {
83556
83665
  addRowBefore: plusIconSvg,
83557
83666
  addRowAfter: plusIconSvg,
@@ -83751,6 +83860,30 @@ const getPropsByItemId = (itemId, props) => {
83751
83860
  return baseProps;
83752
83861
  }
83753
83862
  };
83863
+ function normalizeClipboardContent(rawClipboardContent) {
83864
+ if (!rawClipboardContent) {
83865
+ return {
83866
+ html: null,
83867
+ text: null,
83868
+ hasContent: false,
83869
+ raw: null
83870
+ };
83871
+ }
83872
+ const html = typeof rawClipboardContent.html === "string" ? rawClipboardContent.html : null;
83873
+ const text = typeof rawClipboardContent.text === "string" ? rawClipboardContent.text : null;
83874
+ const hasHtml = !!html && html.trim().length > 0;
83875
+ const hasText = !!text && text.length > 0;
83876
+ const isObject2 = typeof rawClipboardContent === "object" && rawClipboardContent !== null;
83877
+ const fragmentSize = typeof rawClipboardContent.size === "number" ? rawClipboardContent.size : null;
83878
+ const nestedSize = isObject2 && rawClipboardContent.content && typeof rawClipboardContent.content.size === "number" ? rawClipboardContent.content.size : null;
83879
+ const hasFragmentContent = (fragmentSize ?? nestedSize ?? 0) > 0;
83880
+ return {
83881
+ html,
83882
+ text,
83883
+ hasContent: hasHtml || hasText || hasFragmentContent,
83884
+ raw: rawClipboardContent
83885
+ };
83886
+ }
83754
83887
  async function getEditorContext(editor, event) {
83755
83888
  const { view } = editor;
83756
83889
  const { state: state2 } = view;
@@ -83766,123 +83899,144 @@ async function getEditorContext(editor, event) {
83766
83899
  pos = from2;
83767
83900
  node = state2.doc.nodeAt(pos);
83768
83901
  }
83769
- const clipboardContent = await readFromClipboard(state2);
83902
+ const rawClipboardContent = await readFromClipboard(state2);
83903
+ const clipboardContent = normalizeClipboardContent(rawClipboardContent);
83904
+ const structureFromResolvedPos = pos !== null ? getStructureFromResolvedPos(state2, pos) : null;
83905
+ const isInTable2 = structureFromResolvedPos?.isInTable ?? selectionHasNodeOrMark(state2, "table", { requireEnds: true });
83906
+ const isInList = structureFromResolvedPos?.isInList ?? (selectionHasNodeOrMark(state2, "bulletList", { requireEnds: false }) || selectionHasNodeOrMark(state2, "orderedList", { requireEnds: false }));
83907
+ const isInSectionNode = structureFromResolvedPos?.isInSectionNode ?? selectionHasNodeOrMark(state2, "documentSection", { requireEnds: true });
83908
+ const currentNodeType = node?.type?.name || null;
83909
+ const activeMarks = [];
83910
+ if (event && pos !== null) {
83911
+ const $pos = state2.doc.resolve(pos);
83912
+ if ($pos.marks && typeof $pos.marks === "function") {
83913
+ $pos.marks().forEach((mark) => activeMarks.push(mark.type.name));
83914
+ }
83915
+ if (node && node.marks) {
83916
+ node.marks.forEach((mark) => activeMarks.push(mark.type.name));
83917
+ }
83918
+ } else {
83919
+ state2.storedMarks?.forEach((mark) => activeMarks.push(mark.type.name));
83920
+ state2.selection.$head.marks().forEach((mark) => activeMarks.push(mark.type.name));
83921
+ }
83922
+ const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete");
83923
+ let trackedChangeId = null;
83924
+ if (isTrackedChange && event && pos !== null) {
83925
+ const $pos = state2.doc.resolve(pos);
83926
+ const marksAtPos = $pos.marks();
83927
+ const trackedMark = marksAtPos.find((mark) => mark.type.name === "trackInsert" || mark.type.name === "trackDelete");
83928
+ if (trackedMark) {
83929
+ trackedChangeId = trackedMark.attrs.id;
83930
+ }
83931
+ }
83932
+ const cursorCoords = pos ? view.coordsAtPos(pos) : null;
83933
+ const cursorPosition = cursorCoords ? {
83934
+ x: cursorCoords.left,
83935
+ y: cursorCoords.top
83936
+ } : null;
83770
83937
  return {
83771
- editor,
83938
+ // Selection info
83772
83939
  selectedText,
83940
+ hasSelection: !empty2,
83941
+ selectionStart: from2,
83942
+ selectionEnd: to,
83943
+ // Document structure
83944
+ isInTable: isInTable2,
83945
+ isInList,
83946
+ isInSectionNode,
83947
+ currentNodeType,
83948
+ activeMarks,
83949
+ // Document state
83950
+ isTrackedChange,
83951
+ trackedChangeId,
83952
+ documentMode: editor.options?.documentMode || "editing",
83953
+ canUndo: computeCanUndo(editor, state2),
83954
+ canRedo: computeCanRedo(editor, state2),
83955
+ isEditable: editor.isEditable,
83956
+ // Clipboard
83957
+ clipboardContent,
83958
+ // Position and trigger info
83959
+ cursorPosition,
83773
83960
  pos,
83774
83961
  node,
83775
83962
  event,
83776
- clipboardContent
83963
+ // Editor reference for advanced use cases
83964
+ editor
83777
83965
  };
83778
83966
  }
83779
- const onMarginClickCursorChange = (event, editor) => {
83780
- const y2 = event.clientY;
83781
- const x = event.clientX;
83782
- const { view } = editor;
83783
- const editorRect = view.dom.getBoundingClientRect();
83784
- let coords = {
83785
- left: 0,
83786
- top: y2
83787
- };
83788
- let isRightMargin = false;
83789
- if (x > editorRect.right) {
83790
- coords.left = editorRect.left + editorRect.width - 1;
83791
- isRightMargin = true;
83792
- } else if (x < editorRect.left) {
83793
- coords.left = editorRect.left;
83967
+ function computeCanUndo(editor, state2) {
83968
+ if (typeof editor?.can === "function") {
83969
+ try {
83970
+ const can = editor.can();
83971
+ if (can && typeof can.undo === "function") {
83972
+ return !!can.undo();
83973
+ }
83974
+ } catch (error) {
83975
+ console.warn("[SlashMenu] Unable to determine undo availability via editor.can():", error);
83976
+ }
83794
83977
  }
83795
- const pos = view.posAtCoords(coords)?.pos;
83796
- if (pos) {
83797
- let cursorPos = pos;
83798
- if (isRightMargin) {
83799
- const $pos = view.state.doc.resolve(pos);
83800
- const charOffset = $pos.textOffset;
83801
- const node = view.state.doc.nodeAt(pos);
83802
- const text = node?.text;
83803
- const charAtPos = text?.charAt(charOffset);
83804
- cursorPos = node?.isText && charAtPos !== " " ? pos - 1 : pos;
83978
+ if (isCollaborationEnabled(editor)) {
83979
+ try {
83980
+ const undoManager = yUndoPluginKey.getState(state2)?.undoManager;
83981
+ return !!undoManager && undoManager.undoStack.length > 0;
83982
+ } catch (error) {
83983
+ console.warn("[SlashMenu] Unable to determine undo availability via y-prosemirror:", error);
83805
83984
  }
83806
- const transaction = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, cursorPos));
83807
- view.dispatch(transaction);
83808
- view.focus();
83809
83985
  }
83810
- };
83811
- const checkNodeSpecificClicks = (editor, event, popoverControls) => {
83812
- if (!editor) return;
83813
- if (selectionHasNodeOrMark(editor.view.state, "link", { requireEnds: true })) {
83814
- popoverControls.component = LinkInput;
83815
- popoverControls.position = {
83816
- left: `${event.clientX - editor.element.getBoundingClientRect().left}px`,
83817
- top: `${event.clientY - editor.element.getBoundingClientRect().top + 15}px`
83818
- };
83819
- popoverControls.props = {
83820
- showInput: true
83821
- };
83822
- popoverControls.visible = true;
83986
+ try {
83987
+ return undoDepth(state2) > 0;
83988
+ } catch (error) {
83989
+ console.warn("[SlashMenu] Unable to determine undo availability via history plugin:", error);
83990
+ return false;
83823
83991
  }
83824
- };
83825
- function selectionHasNodeOrMark(state2, name, options = {}) {
83826
- const { requireEnds = false } = options;
83827
- const $from = state2.selection.$from;
83828
- const $to = state2.selection.$to;
83829
- if (requireEnds) {
83830
- for (let d2 = $from.depth; d2 > 0; d2--) {
83831
- if ($from.node(d2).type.name === name) {
83832
- return true;
83833
- }
83834
- }
83835
- for (let d2 = $to.depth; d2 > 0; d2--) {
83836
- if ($to.node(d2).type.name === name) {
83837
- return true;
83838
- }
83839
- }
83840
- } else {
83841
- for (let d2 = $from.depth; d2 > 0; d2--) {
83842
- if ($from.node(d2).type.name === name) {
83843
- return true;
83992
+ }
83993
+ function computeCanRedo(editor, state2) {
83994
+ if (typeof editor?.can === "function") {
83995
+ try {
83996
+ const can = editor.can();
83997
+ if (can && typeof can.redo === "function") {
83998
+ return !!can.redo();
83844
83999
  }
84000
+ } catch (error) {
84001
+ console.warn("[SlashMenu] Unable to determine redo availability via editor.can():", error);
83845
84002
  }
83846
84003
  }
83847
- const markType = state2.schema.marks[name];
83848
- if (markType) {
83849
- const { from: from2, to, empty: empty2 } = state2.selection;
83850
- if (requireEnds) {
83851
- const fromMarks = markType.isInSet($from.marks());
83852
- const toMarks = markType.isInSet($to.marks());
83853
- if (fromMarks || toMarks) {
83854
- return true;
83855
- }
83856
- if (empty2 && markType.isInSet(state2.storedMarks || $from.marks())) {
83857
- return true;
83858
- }
83859
- } else {
83860
- if (empty2) {
83861
- if (markType.isInSet(state2.storedMarks || $from.marks())) {
83862
- return true;
83863
- }
83864
- } else {
83865
- let hasMark = false;
83866
- state2.doc.nodesBetween(from2, to, (node) => {
83867
- if (markType.isInSet(node.marks)) {
83868
- hasMark = true;
83869
- return false;
83870
- }
83871
- });
83872
- if (hasMark) return true;
83873
- }
84004
+ if (isCollaborationEnabled(editor)) {
84005
+ try {
84006
+ const undoManager = yUndoPluginKey.getState(state2)?.undoManager;
84007
+ return !!undoManager && undoManager.redoStack.length > 0;
84008
+ } catch (error) {
84009
+ console.warn("[SlashMenu] Unable to determine redo availability via y-prosemirror:", error);
83874
84010
  }
83875
84011
  }
83876
- return false;
84012
+ try {
84013
+ return redoDepth(state2) > 0;
84014
+ } catch (error) {
84015
+ console.warn("[SlashMenu] Unable to determine redo availability via history plugin:", error);
84016
+ return false;
84017
+ }
83877
84018
  }
83878
- function moveCursorToMouseEvent(event, editor) {
83879
- const { view } = editor;
83880
- const coords = { left: event.clientX, top: event.clientY };
83881
- const pos = view.posAtCoords(coords)?.pos;
83882
- if (typeof pos === "number") {
83883
- const tr = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, pos));
83884
- view.dispatch(tr);
83885
- view.focus();
84019
+ function isCollaborationEnabled(editor) {
84020
+ return Boolean(editor?.options?.collaborationProvider && editor?.options?.ydoc);
84021
+ }
84022
+ function getStructureFromResolvedPos(state2, pos) {
84023
+ try {
84024
+ const $pos = state2.doc.resolve(pos);
84025
+ const ancestors = /* @__PURE__ */ new Set();
84026
+ for (let depth = $pos.depth; depth > 0; depth--) {
84027
+ ancestors.add($pos.node(depth).type.name);
84028
+ }
84029
+ const isInList = ancestors.has("bulletList") || ancestors.has("orderedList");
84030
+ const isInTable2 = ancestors.has("table") || ancestors.has("tableRow") || ancestors.has("tableCell") || ancestors.has("tableHeader");
84031
+ const isInSectionNode = ancestors.has("documentSection");
84032
+ return {
84033
+ isInTable: isInTable2,
84034
+ isInList,
84035
+ isInSectionNode
84036
+ };
84037
+ } catch (error) {
84038
+ console.warn("[SlashMenu] Unable to resolve position for structural context:", error);
84039
+ return null;
83886
84040
  }
83887
84041
  }
83888
84042
  const isModuleEnabled = (editorOptions, moduleName) => {
@@ -83896,8 +84050,52 @@ const isModuleEnabled = (editorOptions, moduleName) => {
83896
84050
  return true;
83897
84051
  }
83898
84052
  };
84053
+ function applyCustomMenuConfiguration(defaultSections, context) {
84054
+ const { editor } = context;
84055
+ const slashMenuConfig = editor.options?.slashMenuConfig;
84056
+ if (!slashMenuConfig) {
84057
+ return defaultSections;
84058
+ }
84059
+ let sections = [];
84060
+ if (slashMenuConfig.includeDefaultItems !== false) {
84061
+ sections = [...defaultSections];
84062
+ }
84063
+ if (slashMenuConfig.customItems && Array.isArray(slashMenuConfig.customItems)) {
84064
+ sections = [...sections, ...slashMenuConfig.customItems];
84065
+ }
84066
+ if (typeof slashMenuConfig.menuProvider === "function") {
84067
+ try {
84068
+ sections = slashMenuConfig.menuProvider(context, sections) || sections;
84069
+ } catch (error) {
84070
+ console.warn("[SlashMenu] Error in custom menuProvider:", error);
84071
+ }
84072
+ }
84073
+ return sections;
84074
+ }
84075
+ function filterCustomItems(sections, context) {
84076
+ return sections.map((section) => {
84077
+ const filteredItems = section.items.filter((item) => {
84078
+ if (typeof item.showWhen === "function") {
84079
+ try {
84080
+ return item.showWhen(context);
84081
+ } catch (error) {
84082
+ console.warn(`[SlashMenu] Error in showWhen for item ${item.id}:`, error);
84083
+ return false;
84084
+ }
84085
+ }
84086
+ return true;
84087
+ });
84088
+ return {
84089
+ ...section,
84090
+ items: filteredItems
84091
+ };
84092
+ }).filter((section) => section.items.length > 0);
84093
+ }
83899
84094
  function getItems(context) {
83900
84095
  const { editor, selectedText, trigger: trigger2, clipboardContent } = context;
84096
+ const clipboardHasContent = Boolean(
84097
+ clipboardContent?.hasContent || clipboardContent?.html || clipboardContent?.text || typeof clipboardContent?.size === "number" && clipboardContent.size > 0 || clipboardContent && typeof clipboardContent?.content?.size === "number" && clipboardContent.content.size > 0 || clipboardContent?.raw && typeof clipboardContent.raw.size === "number" && clipboardContent.raw.size > 0 || clipboardContent?.raw && typeof clipboardContent.raw?.content?.size === "number" && clipboardContent.raw.content.size > 0
84098
+ );
83901
84099
  const isInTable2 = selectionHasNodeOrMark(editor.view.state, "table", { requireEnds: true });
83902
84100
  const isInSectionNode = selectionHasNodeOrMark(editor.view.state, "documentSection", { requireEnds: true });
83903
84101
  const sections = [
@@ -84034,12 +84232,13 @@ function getItems(context) {
84034
84232
  ]
84035
84233
  }
84036
84234
  ];
84037
- const filteredSections = sections.map((section) => {
84235
+ let allSections = applyCustomMenuConfiguration(sections, context);
84236
+ const filteredSections = allSections.map((section) => {
84038
84237
  const filteredItems = section.items.filter((item) => {
84039
84238
  if (item.requiresModule && !isModuleEnabled(editor?.options, item.requiresModule)) return false;
84040
84239
  if (item.requiresSelection && !selectedText) return false;
84041
84240
  if (!item.allowedTriggers.includes(trigger2)) return false;
84042
- if (item.requiresClipboard && !clipboardContent) return false;
84241
+ if (item.requiresClipboard && !clipboardHasContent) return false;
84043
84242
  if (item.requiresTableParent && !isInTable2 || item.id === "insert-table" && isInTable2) return false;
84044
84243
  if (item.requiresSectionParent && !isInSectionNode) return false;
84045
84244
  return true;
@@ -84049,7 +84248,8 @@ function getItems(context) {
84049
84248
  items: filteredItems
84050
84249
  };
84051
84250
  }).filter((section) => section.items.length > 0);
84052
- return filteredSections;
84251
+ const finalSections = filterCustomItems(filteredSections, context);
84252
+ return finalSections;
84053
84253
  }
84054
84254
  const _hoisted_1$3 = { class: "slash-menu-items" };
84055
84255
  const _hoisted_2$1 = {
@@ -84084,6 +84284,7 @@ const _sfc_main$4 = {
84084
84284
  const menuRef = ref$1(null);
84085
84285
  const sections = ref$1([]);
84086
84286
  const selectedId = ref$1(null);
84287
+ const currentContext = ref$1(null);
84087
84288
  const handleEditorUpdate = () => {
84088
84289
  if (!props.editor?.isEditable && isOpen.value) {
84089
84290
  closeMenu({ restoreCursor: false });
@@ -84129,6 +84330,44 @@ const _sfc_main$4 = {
84129
84330
  selectedId.value = newItems[0].id;
84130
84331
  }
84131
84332
  });
84333
+ const customItemRefs = /* @__PURE__ */ new Map();
84334
+ const setCustomItemRef = (el, item) => {
84335
+ if (el && item.render) {
84336
+ customItemRefs.set(item.id, { element: el, item });
84337
+ nextTick(() => {
84338
+ renderCustomItem(item.id);
84339
+ });
84340
+ }
84341
+ };
84342
+ const renderCustomItem = async (itemId) => {
84343
+ const refData = customItemRefs.get(itemId);
84344
+ if (!refData || refData.element.hasCustomContent) return;
84345
+ const { element, item } = refData;
84346
+ try {
84347
+ if (!currentContext.value) {
84348
+ currentContext.value = await getEditorContext(props.editor);
84349
+ }
84350
+ const context = currentContext.value;
84351
+ const customElement = item.render(context);
84352
+ if (customElement instanceof HTMLElement) {
84353
+ element.innerHTML = "";
84354
+ element.appendChild(customElement);
84355
+ element.hasCustomContent = true;
84356
+ }
84357
+ } catch (error) {
84358
+ console.warn(`[SlashMenu] Error rendering custom item ${itemId}:`, error);
84359
+ element.innerHTML = `<span>${item.label || "Custom Item"}</span>`;
84360
+ element.hasCustomContent = true;
84361
+ }
84362
+ };
84363
+ const cleanupCustomItems = () => {
84364
+ customItemRefs.forEach((refData) => {
84365
+ if (refData.element) {
84366
+ refData.element.hasCustomContent = false;
84367
+ }
84368
+ });
84369
+ customItemRefs.clear();
84370
+ };
84132
84371
  const handleGlobalKeyDown = (event) => {
84133
84372
  if (event.key === "Escape") {
84134
84373
  event.preventDefault();
@@ -84179,22 +84418,23 @@ const _sfc_main$4 = {
84179
84418
  return;
84180
84419
  }
84181
84420
  event.preventDefault();
84421
+ const context = await getEditorContext(props.editor, event);
84422
+ currentContext.value = context;
84423
+ sections.value = getItems({ ...context, trigger: "click" });
84424
+ selectedId.value = flattenedItems.value[0]?.id || null;
84425
+ searchQuery.value = "";
84182
84426
  props.editor.view.dispatch(
84183
84427
  props.editor.view.state.tr.setMeta(SlashMenuPluginKey, {
84184
84428
  type: "open",
84185
- pos: props.editor.view.state.selection.from,
84429
+ pos: context?.pos ?? props.editor.view.state.selection.from,
84186
84430
  clientX: event.clientX,
84187
84431
  clientY: event.clientY
84188
84432
  })
84189
84433
  );
84190
- searchQuery.value = "";
84191
- const context = await getEditorContext(props.editor, event);
84192
- sections.value = getItems({ ...context, trigger: "click" });
84193
- selectedId.value = flattenedItems.value[0]?.id || null;
84194
84434
  };
84195
84435
  const executeCommand = async (item) => {
84196
84436
  if (props.editor) {
84197
- item.action ? await item.action(props.editor) : null;
84437
+ item.action ? await item.action(props.editor, currentContext.value) : null;
84198
84438
  if (item.component) {
84199
84439
  menuRef.value;
84200
84440
  const componentProps = getPropsByItemId(item.id, props);
@@ -84212,7 +84452,7 @@ const _sfc_main$4 = {
84212
84452
  const closeMenu = (options = { restoreCursor: true }) => {
84213
84453
  if (props.editor?.view) {
84214
84454
  const pluginState = SlashMenuPluginKey.getState(props.editor.view.state);
84215
- const { anchorPos } = pluginState;
84455
+ const anchorPos = pluginState?.anchorPos;
84216
84456
  props.editor.view.dispatch(
84217
84457
  props.editor.view.state.tr.setMeta(SlashMenuPluginKey, {
84218
84458
  type: "close"
@@ -84225,6 +84465,8 @@ const _sfc_main$4 = {
84225
84465
  props.editor.view.dispatch(tr);
84226
84466
  props.editor.view.focus();
84227
84467
  }
84468
+ cleanupCustomItems();
84469
+ currentContext.value = null;
84228
84470
  isOpen.value = false;
84229
84471
  searchQuery.value = "";
84230
84472
  sections.value = [];
@@ -84241,19 +84483,29 @@ const _sfc_main$4 = {
84241
84483
  isOpen.value = true;
84242
84484
  menuPosition.value = event.menuPosition;
84243
84485
  searchQuery.value = "";
84244
- const context = await getEditorContext(props.editor);
84245
- sections.value = getItems({ ...context, trigger: "slash" });
84246
- selectedId.value = flattenedItems.value[0]?.id || null;
84486
+ if (!currentContext.value) {
84487
+ const context = await getEditorContext(props.editor);
84488
+ currentContext.value = context;
84489
+ sections.value = getItems({ ...context, trigger: "slash" });
84490
+ selectedId.value = flattenedItems.value[0]?.id || null;
84491
+ } else if (sections.value.length === 0) {
84492
+ const trigger2 = currentContext.value.event?.type === "contextmenu" ? "click" : "slash";
84493
+ sections.value = getItems({ ...currentContext.value, trigger: trigger2 });
84494
+ selectedId.value = flattenedItems.value[0]?.id || null;
84495
+ }
84247
84496
  });
84248
84497
  props.editor.view.dom.addEventListener("contextmenu", handleRightClick);
84249
84498
  props.editor.on("slashMenu:close", () => {
84499
+ cleanupCustomItems();
84250
84500
  isOpen.value = false;
84251
84501
  searchQuery.value = "";
84502
+ currentContext.value = null;
84252
84503
  });
84253
84504
  });
84254
84505
  onBeforeUnmount(() => {
84255
84506
  document.removeEventListener("keydown", handleGlobalKeyDown);
84256
84507
  document.removeEventListener("mousedown", handleGlobalOutsideClick);
84508
+ cleanupCustomItems();
84257
84509
  if (props.editor) {
84258
84510
  try {
84259
84511
  props.editor.off("slashMenu:open");
@@ -84300,12 +84552,19 @@ const _sfc_main$4 = {
84300
84552
  class: normalizeClass(["slash-menu-item", { "is-selected": item.id === selectedId.value }]),
84301
84553
  onClick: ($event) => executeCommand(item)
84302
84554
  }, [
84303
- item.icon ? (openBlock(), createElementBlock("span", {
84555
+ item.render ? (openBlock(), createElementBlock("div", {
84304
84556
  key: 0,
84305
- class: "slash-menu-item-icon",
84306
- innerHTML: item.icon
84307
- }, null, 8, _hoisted_4)) : createCommentVNode("", true),
84308
- createBaseVNode("span", null, toDisplayString(item.label), 1)
84557
+ ref_for: true,
84558
+ ref: (el) => setCustomItemRef(el, item),
84559
+ class: "slash-menu-custom-item"
84560
+ }, null, 512)) : (openBlock(), createElementBlock(Fragment$1, { key: 1 }, [
84561
+ item.icon ? (openBlock(), createElementBlock("span", {
84562
+ key: 0,
84563
+ class: "slash-menu-item-icon",
84564
+ innerHTML: item.icon
84565
+ }, null, 8, _hoisted_4)) : createCommentVNode("", true),
84566
+ createBaseVNode("span", null, toDisplayString(item.label), 1)
84567
+ ], 64))
84309
84568
  ], 10, _hoisted_3$1);
84310
84569
  }), 128))
84311
84570
  ], 64);
@@ -90,6 +90,14 @@ export type Modules = {
90
90
  * Toolbar module configuration
91
91
  */
92
92
  toolbar?: any;
93
+ /**
94
+ * Slash menu module configuration
95
+ */
96
+ slashMenu?: {
97
+ customItems?: any[];
98
+ menuProvider?: Function;
99
+ includeDefaultItems?: boolean;
100
+ };
93
101
  };
94
102
  export type Editor = {
95
103
  setHighContrastMode: (value: any) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/types/index.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/types/index.js"],"names":[],"mappings":"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"}