@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.
@@ -83569,6 +83569,115 @@ runCommandWithArgumentOnly_fn = function({ item, argument, noArgumentCallback =
83569
83569
  this.updateToolbarState();
83570
83570
  }
83571
83571
  };
83572
+ const onMarginClickCursorChange = (event, editor) => {
83573
+ const y2 = event.clientY;
83574
+ const x = event.clientX;
83575
+ const { view } = editor;
83576
+ const editorRect = view.dom.getBoundingClientRect();
83577
+ let coords = {
83578
+ left: 0,
83579
+ top: y2
83580
+ };
83581
+ let isRightMargin = false;
83582
+ if (x > editorRect.right) {
83583
+ coords.left = editorRect.left + editorRect.width - 1;
83584
+ isRightMargin = true;
83585
+ } else if (x < editorRect.left) {
83586
+ coords.left = editorRect.left;
83587
+ }
83588
+ const pos = view.posAtCoords(coords)?.pos;
83589
+ if (pos) {
83590
+ let cursorPos = pos;
83591
+ if (isRightMargin) {
83592
+ const $pos = view.state.doc.resolve(pos);
83593
+ const charOffset = $pos.textOffset;
83594
+ const node = view.state.doc.nodeAt(pos);
83595
+ const text = node?.text;
83596
+ const charAtPos = text?.charAt(charOffset);
83597
+ cursorPos = node?.isText && charAtPos !== " " ? pos - 1 : pos;
83598
+ }
83599
+ const transaction = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, cursorPos));
83600
+ view.dispatch(transaction);
83601
+ view.focus();
83602
+ }
83603
+ };
83604
+ const checkNodeSpecificClicks = (editor, event, popoverControls) => {
83605
+ if (!editor) return;
83606
+ if (selectionHasNodeOrMark(editor.view.state, "link", { requireEnds: true })) {
83607
+ popoverControls.component = LinkInput;
83608
+ popoverControls.position = {
83609
+ left: `${event.clientX - editor.element.getBoundingClientRect().left}px`,
83610
+ top: `${event.clientY - editor.element.getBoundingClientRect().top + 15}px`
83611
+ };
83612
+ popoverControls.props = {
83613
+ showInput: true
83614
+ };
83615
+ popoverControls.visible = true;
83616
+ }
83617
+ };
83618
+ function selectionHasNodeOrMark(state2, name, options = {}) {
83619
+ const { requireEnds = false } = options;
83620
+ const $from = state2.selection.$from;
83621
+ const $to = state2.selection.$to;
83622
+ if (requireEnds) {
83623
+ for (let d2 = $from.depth; d2 > 0; d2--) {
83624
+ if ($from.node(d2).type.name === name) {
83625
+ return true;
83626
+ }
83627
+ }
83628
+ for (let d2 = $to.depth; d2 > 0; d2--) {
83629
+ if ($to.node(d2).type.name === name) {
83630
+ return true;
83631
+ }
83632
+ }
83633
+ } else {
83634
+ for (let d2 = $from.depth; d2 > 0; d2--) {
83635
+ if ($from.node(d2).type.name === name) {
83636
+ return true;
83637
+ }
83638
+ }
83639
+ }
83640
+ const markType = state2.schema.marks[name];
83641
+ if (markType) {
83642
+ const { from: from2, to, empty: empty2 } = state2.selection;
83643
+ if (requireEnds) {
83644
+ const fromMarks = markType.isInSet($from.marks());
83645
+ const toMarks = markType.isInSet($to.marks());
83646
+ if (fromMarks || toMarks) {
83647
+ return true;
83648
+ }
83649
+ if (empty2 && markType.isInSet(state2.storedMarks || $from.marks())) {
83650
+ return true;
83651
+ }
83652
+ } else {
83653
+ if (empty2) {
83654
+ if (markType.isInSet(state2.storedMarks || $from.marks())) {
83655
+ return true;
83656
+ }
83657
+ } else {
83658
+ let hasMark = false;
83659
+ state2.doc.nodesBetween(from2, to, (node) => {
83660
+ if (markType.isInSet(node.marks)) {
83661
+ hasMark = true;
83662
+ return false;
83663
+ }
83664
+ });
83665
+ if (hasMark) return true;
83666
+ }
83667
+ }
83668
+ }
83669
+ return false;
83670
+ }
83671
+ function moveCursorToMouseEvent(event, editor) {
83672
+ const { view } = editor;
83673
+ const coords = { left: event.clientX, top: event.clientY };
83674
+ const pos = view.posAtCoords(coords)?.pos;
83675
+ if (typeof pos === "number") {
83676
+ const tr = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, pos));
83677
+ view.dispatch(tr);
83678
+ view.focus();
83679
+ }
83680
+ }
83572
83681
  const ICONS = {
83573
83682
  addRowBefore: plusIconSvg,
83574
83683
  addRowAfter: plusIconSvg,
@@ -83768,6 +83877,30 @@ const getPropsByItemId = (itemId, props) => {
83768
83877
  return baseProps;
83769
83878
  }
83770
83879
  };
83880
+ function normalizeClipboardContent(rawClipboardContent) {
83881
+ if (!rawClipboardContent) {
83882
+ return {
83883
+ html: null,
83884
+ text: null,
83885
+ hasContent: false,
83886
+ raw: null
83887
+ };
83888
+ }
83889
+ const html = typeof rawClipboardContent.html === "string" ? rawClipboardContent.html : null;
83890
+ const text = typeof rawClipboardContent.text === "string" ? rawClipboardContent.text : null;
83891
+ const hasHtml = !!html && html.trim().length > 0;
83892
+ const hasText = !!text && text.length > 0;
83893
+ const isObject2 = typeof rawClipboardContent === "object" && rawClipboardContent !== null;
83894
+ const fragmentSize = typeof rawClipboardContent.size === "number" ? rawClipboardContent.size : null;
83895
+ const nestedSize = isObject2 && rawClipboardContent.content && typeof rawClipboardContent.content.size === "number" ? rawClipboardContent.content.size : null;
83896
+ const hasFragmentContent = (fragmentSize ?? nestedSize ?? 0) > 0;
83897
+ return {
83898
+ html,
83899
+ text,
83900
+ hasContent: hasHtml || hasText || hasFragmentContent,
83901
+ raw: rawClipboardContent
83902
+ };
83903
+ }
83771
83904
  async function getEditorContext(editor, event) {
83772
83905
  const { view } = editor;
83773
83906
  const { state: state2 } = view;
@@ -83783,123 +83916,144 @@ async function getEditorContext(editor, event) {
83783
83916
  pos = from2;
83784
83917
  node = state2.doc.nodeAt(pos);
83785
83918
  }
83786
- const clipboardContent = await readFromClipboard(state2);
83919
+ const rawClipboardContent = await readFromClipboard(state2);
83920
+ const clipboardContent = normalizeClipboardContent(rawClipboardContent);
83921
+ const structureFromResolvedPos = pos !== null ? getStructureFromResolvedPos(state2, pos) : null;
83922
+ const isInTable2 = structureFromResolvedPos?.isInTable ?? selectionHasNodeOrMark(state2, "table", { requireEnds: true });
83923
+ const isInList = structureFromResolvedPos?.isInList ?? (selectionHasNodeOrMark(state2, "bulletList", { requireEnds: false }) || selectionHasNodeOrMark(state2, "orderedList", { requireEnds: false }));
83924
+ const isInSectionNode = structureFromResolvedPos?.isInSectionNode ?? selectionHasNodeOrMark(state2, "documentSection", { requireEnds: true });
83925
+ const currentNodeType = node?.type?.name || null;
83926
+ const activeMarks = [];
83927
+ if (event && pos !== null) {
83928
+ const $pos = state2.doc.resolve(pos);
83929
+ if ($pos.marks && typeof $pos.marks === "function") {
83930
+ $pos.marks().forEach((mark) => activeMarks.push(mark.type.name));
83931
+ }
83932
+ if (node && node.marks) {
83933
+ node.marks.forEach((mark) => activeMarks.push(mark.type.name));
83934
+ }
83935
+ } else {
83936
+ state2.storedMarks?.forEach((mark) => activeMarks.push(mark.type.name));
83937
+ state2.selection.$head.marks().forEach((mark) => activeMarks.push(mark.type.name));
83938
+ }
83939
+ const isTrackedChange = activeMarks.includes("trackInsert") || activeMarks.includes("trackDelete");
83940
+ let trackedChangeId = null;
83941
+ if (isTrackedChange && event && pos !== null) {
83942
+ const $pos = state2.doc.resolve(pos);
83943
+ const marksAtPos = $pos.marks();
83944
+ const trackedMark = marksAtPos.find((mark) => mark.type.name === "trackInsert" || mark.type.name === "trackDelete");
83945
+ if (trackedMark) {
83946
+ trackedChangeId = trackedMark.attrs.id;
83947
+ }
83948
+ }
83949
+ const cursorCoords = pos ? view.coordsAtPos(pos) : null;
83950
+ const cursorPosition = cursorCoords ? {
83951
+ x: cursorCoords.left,
83952
+ y: cursorCoords.top
83953
+ } : null;
83787
83954
  return {
83788
- editor,
83955
+ // Selection info
83789
83956
  selectedText,
83957
+ hasSelection: !empty2,
83958
+ selectionStart: from2,
83959
+ selectionEnd: to,
83960
+ // Document structure
83961
+ isInTable: isInTable2,
83962
+ isInList,
83963
+ isInSectionNode,
83964
+ currentNodeType,
83965
+ activeMarks,
83966
+ // Document state
83967
+ isTrackedChange,
83968
+ trackedChangeId,
83969
+ documentMode: editor.options?.documentMode || "editing",
83970
+ canUndo: computeCanUndo(editor, state2),
83971
+ canRedo: computeCanRedo(editor, state2),
83972
+ isEditable: editor.isEditable,
83973
+ // Clipboard
83974
+ clipboardContent,
83975
+ // Position and trigger info
83976
+ cursorPosition,
83790
83977
  pos,
83791
83978
  node,
83792
83979
  event,
83793
- clipboardContent
83980
+ // Editor reference for advanced use cases
83981
+ editor
83794
83982
  };
83795
83983
  }
83796
- const onMarginClickCursorChange = (event, editor) => {
83797
- const y2 = event.clientY;
83798
- const x = event.clientX;
83799
- const { view } = editor;
83800
- const editorRect = view.dom.getBoundingClientRect();
83801
- let coords = {
83802
- left: 0,
83803
- top: y2
83804
- };
83805
- let isRightMargin = false;
83806
- if (x > editorRect.right) {
83807
- coords.left = editorRect.left + editorRect.width - 1;
83808
- isRightMargin = true;
83809
- } else if (x < editorRect.left) {
83810
- coords.left = editorRect.left;
83984
+ function computeCanUndo(editor, state2) {
83985
+ if (typeof editor?.can === "function") {
83986
+ try {
83987
+ const can = editor.can();
83988
+ if (can && typeof can.undo === "function") {
83989
+ return !!can.undo();
83990
+ }
83991
+ } catch (error) {
83992
+ console.warn("[SlashMenu] Unable to determine undo availability via editor.can():", error);
83993
+ }
83811
83994
  }
83812
- const pos = view.posAtCoords(coords)?.pos;
83813
- if (pos) {
83814
- let cursorPos = pos;
83815
- if (isRightMargin) {
83816
- const $pos = view.state.doc.resolve(pos);
83817
- const charOffset = $pos.textOffset;
83818
- const node = view.state.doc.nodeAt(pos);
83819
- const text = node?.text;
83820
- const charAtPos = text?.charAt(charOffset);
83821
- cursorPos = node?.isText && charAtPos !== " " ? pos - 1 : pos;
83995
+ if (isCollaborationEnabled(editor)) {
83996
+ try {
83997
+ const undoManager = yUndoPluginKey.getState(state2)?.undoManager;
83998
+ return !!undoManager && undoManager.undoStack.length > 0;
83999
+ } catch (error) {
84000
+ console.warn("[SlashMenu] Unable to determine undo availability via y-prosemirror:", error);
83822
84001
  }
83823
- const transaction = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, cursorPos));
83824
- view.dispatch(transaction);
83825
- view.focus();
83826
84002
  }
83827
- };
83828
- const checkNodeSpecificClicks = (editor, event, popoverControls) => {
83829
- if (!editor) return;
83830
- if (selectionHasNodeOrMark(editor.view.state, "link", { requireEnds: true })) {
83831
- popoverControls.component = LinkInput;
83832
- popoverControls.position = {
83833
- left: `${event.clientX - editor.element.getBoundingClientRect().left}px`,
83834
- top: `${event.clientY - editor.element.getBoundingClientRect().top + 15}px`
83835
- };
83836
- popoverControls.props = {
83837
- showInput: true
83838
- };
83839
- popoverControls.visible = true;
84003
+ try {
84004
+ return undoDepth(state2) > 0;
84005
+ } catch (error) {
84006
+ console.warn("[SlashMenu] Unable to determine undo availability via history plugin:", error);
84007
+ return false;
83840
84008
  }
83841
- };
83842
- function selectionHasNodeOrMark(state2, name, options = {}) {
83843
- const { requireEnds = false } = options;
83844
- const $from = state2.selection.$from;
83845
- const $to = state2.selection.$to;
83846
- if (requireEnds) {
83847
- for (let d2 = $from.depth; d2 > 0; d2--) {
83848
- if ($from.node(d2).type.name === name) {
83849
- return true;
83850
- }
83851
- }
83852
- for (let d2 = $to.depth; d2 > 0; d2--) {
83853
- if ($to.node(d2).type.name === name) {
83854
- return true;
83855
- }
83856
- }
83857
- } else {
83858
- for (let d2 = $from.depth; d2 > 0; d2--) {
83859
- if ($from.node(d2).type.name === name) {
83860
- return true;
84009
+ }
84010
+ function computeCanRedo(editor, state2) {
84011
+ if (typeof editor?.can === "function") {
84012
+ try {
84013
+ const can = editor.can();
84014
+ if (can && typeof can.redo === "function") {
84015
+ return !!can.redo();
83861
84016
  }
84017
+ } catch (error) {
84018
+ console.warn("[SlashMenu] Unable to determine redo availability via editor.can():", error);
83862
84019
  }
83863
84020
  }
83864
- const markType = state2.schema.marks[name];
83865
- if (markType) {
83866
- const { from: from2, to, empty: empty2 } = state2.selection;
83867
- if (requireEnds) {
83868
- const fromMarks = markType.isInSet($from.marks());
83869
- const toMarks = markType.isInSet($to.marks());
83870
- if (fromMarks || toMarks) {
83871
- return true;
83872
- }
83873
- if (empty2 && markType.isInSet(state2.storedMarks || $from.marks())) {
83874
- return true;
83875
- }
83876
- } else {
83877
- if (empty2) {
83878
- if (markType.isInSet(state2.storedMarks || $from.marks())) {
83879
- return true;
83880
- }
83881
- } else {
83882
- let hasMark = false;
83883
- state2.doc.nodesBetween(from2, to, (node) => {
83884
- if (markType.isInSet(node.marks)) {
83885
- hasMark = true;
83886
- return false;
83887
- }
83888
- });
83889
- if (hasMark) return true;
83890
- }
84021
+ if (isCollaborationEnabled(editor)) {
84022
+ try {
84023
+ const undoManager = yUndoPluginKey.getState(state2)?.undoManager;
84024
+ return !!undoManager && undoManager.redoStack.length > 0;
84025
+ } catch (error) {
84026
+ console.warn("[SlashMenu] Unable to determine redo availability via y-prosemirror:", error);
83891
84027
  }
83892
84028
  }
83893
- return false;
84029
+ try {
84030
+ return redoDepth(state2) > 0;
84031
+ } catch (error) {
84032
+ console.warn("[SlashMenu] Unable to determine redo availability via history plugin:", error);
84033
+ return false;
84034
+ }
83894
84035
  }
83895
- function moveCursorToMouseEvent(event, editor) {
83896
- const { view } = editor;
83897
- const coords = { left: event.clientX, top: event.clientY };
83898
- const pos = view.posAtCoords(coords)?.pos;
83899
- if (typeof pos === "number") {
83900
- const tr = view.state.tr.setSelection(TextSelection$1.create(view.state.doc, pos));
83901
- view.dispatch(tr);
83902
- view.focus();
84036
+ function isCollaborationEnabled(editor) {
84037
+ return Boolean(editor?.options?.collaborationProvider && editor?.options?.ydoc);
84038
+ }
84039
+ function getStructureFromResolvedPos(state2, pos) {
84040
+ try {
84041
+ const $pos = state2.doc.resolve(pos);
84042
+ const ancestors = /* @__PURE__ */ new Set();
84043
+ for (let depth = $pos.depth; depth > 0; depth--) {
84044
+ ancestors.add($pos.node(depth).type.name);
84045
+ }
84046
+ const isInList = ancestors.has("bulletList") || ancestors.has("orderedList");
84047
+ const isInTable2 = ancestors.has("table") || ancestors.has("tableRow") || ancestors.has("tableCell") || ancestors.has("tableHeader");
84048
+ const isInSectionNode = ancestors.has("documentSection");
84049
+ return {
84050
+ isInTable: isInTable2,
84051
+ isInList,
84052
+ isInSectionNode
84053
+ };
84054
+ } catch (error) {
84055
+ console.warn("[SlashMenu] Unable to resolve position for structural context:", error);
84056
+ return null;
83903
84057
  }
83904
84058
  }
83905
84059
  const isModuleEnabled = (editorOptions, moduleName) => {
@@ -83913,8 +84067,52 @@ const isModuleEnabled = (editorOptions, moduleName) => {
83913
84067
  return true;
83914
84068
  }
83915
84069
  };
84070
+ function applyCustomMenuConfiguration(defaultSections, context) {
84071
+ const { editor } = context;
84072
+ const slashMenuConfig = editor.options?.slashMenuConfig;
84073
+ if (!slashMenuConfig) {
84074
+ return defaultSections;
84075
+ }
84076
+ let sections = [];
84077
+ if (slashMenuConfig.includeDefaultItems !== false) {
84078
+ sections = [...defaultSections];
84079
+ }
84080
+ if (slashMenuConfig.customItems && Array.isArray(slashMenuConfig.customItems)) {
84081
+ sections = [...sections, ...slashMenuConfig.customItems];
84082
+ }
84083
+ if (typeof slashMenuConfig.menuProvider === "function") {
84084
+ try {
84085
+ sections = slashMenuConfig.menuProvider(context, sections) || sections;
84086
+ } catch (error) {
84087
+ console.warn("[SlashMenu] Error in custom menuProvider:", error);
84088
+ }
84089
+ }
84090
+ return sections;
84091
+ }
84092
+ function filterCustomItems(sections, context) {
84093
+ return sections.map((section) => {
84094
+ const filteredItems = section.items.filter((item) => {
84095
+ if (typeof item.showWhen === "function") {
84096
+ try {
84097
+ return item.showWhen(context);
84098
+ } catch (error) {
84099
+ console.warn(`[SlashMenu] Error in showWhen for item ${item.id}:`, error);
84100
+ return false;
84101
+ }
84102
+ }
84103
+ return true;
84104
+ });
84105
+ return {
84106
+ ...section,
84107
+ items: filteredItems
84108
+ };
84109
+ }).filter((section) => section.items.length > 0);
84110
+ }
83916
84111
  function getItems(context) {
83917
84112
  const { editor, selectedText, trigger: trigger2, clipboardContent } = context;
84113
+ const clipboardHasContent = Boolean(
84114
+ 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
84115
+ );
83918
84116
  const isInTable2 = selectionHasNodeOrMark(editor.view.state, "table", { requireEnds: true });
83919
84117
  const isInSectionNode = selectionHasNodeOrMark(editor.view.state, "documentSection", { requireEnds: true });
83920
84118
  const sections = [
@@ -84051,12 +84249,13 @@ function getItems(context) {
84051
84249
  ]
84052
84250
  }
84053
84251
  ];
84054
- const filteredSections = sections.map((section) => {
84252
+ let allSections = applyCustomMenuConfiguration(sections, context);
84253
+ const filteredSections = allSections.map((section) => {
84055
84254
  const filteredItems = section.items.filter((item) => {
84056
84255
  if (item.requiresModule && !isModuleEnabled(editor?.options, item.requiresModule)) return false;
84057
84256
  if (item.requiresSelection && !selectedText) return false;
84058
84257
  if (!item.allowedTriggers.includes(trigger2)) return false;
84059
- if (item.requiresClipboard && !clipboardContent) return false;
84258
+ if (item.requiresClipboard && !clipboardHasContent) return false;
84060
84259
  if (item.requiresTableParent && !isInTable2 || item.id === "insert-table" && isInTable2) return false;
84061
84260
  if (item.requiresSectionParent && !isInSectionNode) return false;
84062
84261
  return true;
@@ -84066,7 +84265,8 @@ function getItems(context) {
84066
84265
  items: filteredItems
84067
84266
  };
84068
84267
  }).filter((section) => section.items.length > 0);
84069
- return filteredSections;
84268
+ const finalSections = filterCustomItems(filteredSections, context);
84269
+ return finalSections;
84070
84270
  }
84071
84271
  const _hoisted_1$3 = { class: "slash-menu-items" };
84072
84272
  const _hoisted_2$1 = {
@@ -84101,6 +84301,7 @@ const _sfc_main$4 = {
84101
84301
  const menuRef = vue.ref(null);
84102
84302
  const sections = vue.ref([]);
84103
84303
  const selectedId = vue.ref(null);
84304
+ const currentContext = vue.ref(null);
84104
84305
  const handleEditorUpdate = () => {
84105
84306
  if (!props.editor?.isEditable && isOpen.value) {
84106
84307
  closeMenu({ restoreCursor: false });
@@ -84146,6 +84347,44 @@ const _sfc_main$4 = {
84146
84347
  selectedId.value = newItems[0].id;
84147
84348
  }
84148
84349
  });
84350
+ const customItemRefs = /* @__PURE__ */ new Map();
84351
+ const setCustomItemRef = (el, item) => {
84352
+ if (el && item.render) {
84353
+ customItemRefs.set(item.id, { element: el, item });
84354
+ vue.nextTick(() => {
84355
+ renderCustomItem(item.id);
84356
+ });
84357
+ }
84358
+ };
84359
+ const renderCustomItem = async (itemId) => {
84360
+ const refData = customItemRefs.get(itemId);
84361
+ if (!refData || refData.element.hasCustomContent) return;
84362
+ const { element, item } = refData;
84363
+ try {
84364
+ if (!currentContext.value) {
84365
+ currentContext.value = await getEditorContext(props.editor);
84366
+ }
84367
+ const context = currentContext.value;
84368
+ const customElement = item.render(context);
84369
+ if (customElement instanceof HTMLElement) {
84370
+ element.innerHTML = "";
84371
+ element.appendChild(customElement);
84372
+ element.hasCustomContent = true;
84373
+ }
84374
+ } catch (error) {
84375
+ console.warn(`[SlashMenu] Error rendering custom item ${itemId}:`, error);
84376
+ element.innerHTML = `<span>${item.label || "Custom Item"}</span>`;
84377
+ element.hasCustomContent = true;
84378
+ }
84379
+ };
84380
+ const cleanupCustomItems = () => {
84381
+ customItemRefs.forEach((refData) => {
84382
+ if (refData.element) {
84383
+ refData.element.hasCustomContent = false;
84384
+ }
84385
+ });
84386
+ customItemRefs.clear();
84387
+ };
84149
84388
  const handleGlobalKeyDown = (event) => {
84150
84389
  if (event.key === "Escape") {
84151
84390
  event.preventDefault();
@@ -84196,22 +84435,23 @@ const _sfc_main$4 = {
84196
84435
  return;
84197
84436
  }
84198
84437
  event.preventDefault();
84438
+ const context = await getEditorContext(props.editor, event);
84439
+ currentContext.value = context;
84440
+ sections.value = getItems({ ...context, trigger: "click" });
84441
+ selectedId.value = flattenedItems.value[0]?.id || null;
84442
+ searchQuery.value = "";
84199
84443
  props.editor.view.dispatch(
84200
84444
  props.editor.view.state.tr.setMeta(SlashMenuPluginKey, {
84201
84445
  type: "open",
84202
- pos: props.editor.view.state.selection.from,
84446
+ pos: context?.pos ?? props.editor.view.state.selection.from,
84203
84447
  clientX: event.clientX,
84204
84448
  clientY: event.clientY
84205
84449
  })
84206
84450
  );
84207
- searchQuery.value = "";
84208
- const context = await getEditorContext(props.editor, event);
84209
- sections.value = getItems({ ...context, trigger: "click" });
84210
- selectedId.value = flattenedItems.value[0]?.id || null;
84211
84451
  };
84212
84452
  const executeCommand = async (item) => {
84213
84453
  if (props.editor) {
84214
- item.action ? await item.action(props.editor) : null;
84454
+ item.action ? await item.action(props.editor, currentContext.value) : null;
84215
84455
  if (item.component) {
84216
84456
  menuRef.value;
84217
84457
  const componentProps = getPropsByItemId(item.id, props);
@@ -84229,7 +84469,7 @@ const _sfc_main$4 = {
84229
84469
  const closeMenu = (options = { restoreCursor: true }) => {
84230
84470
  if (props.editor?.view) {
84231
84471
  const pluginState = SlashMenuPluginKey.getState(props.editor.view.state);
84232
- const { anchorPos } = pluginState;
84472
+ const anchorPos = pluginState?.anchorPos;
84233
84473
  props.editor.view.dispatch(
84234
84474
  props.editor.view.state.tr.setMeta(SlashMenuPluginKey, {
84235
84475
  type: "close"
@@ -84242,6 +84482,8 @@ const _sfc_main$4 = {
84242
84482
  props.editor.view.dispatch(tr);
84243
84483
  props.editor.view.focus();
84244
84484
  }
84485
+ cleanupCustomItems();
84486
+ currentContext.value = null;
84245
84487
  isOpen.value = false;
84246
84488
  searchQuery.value = "";
84247
84489
  sections.value = [];
@@ -84258,19 +84500,29 @@ const _sfc_main$4 = {
84258
84500
  isOpen.value = true;
84259
84501
  menuPosition.value = event.menuPosition;
84260
84502
  searchQuery.value = "";
84261
- const context = await getEditorContext(props.editor);
84262
- sections.value = getItems({ ...context, trigger: "slash" });
84263
- selectedId.value = flattenedItems.value[0]?.id || null;
84503
+ if (!currentContext.value) {
84504
+ const context = await getEditorContext(props.editor);
84505
+ currentContext.value = context;
84506
+ sections.value = getItems({ ...context, trigger: "slash" });
84507
+ selectedId.value = flattenedItems.value[0]?.id || null;
84508
+ } else if (sections.value.length === 0) {
84509
+ const trigger2 = currentContext.value.event?.type === "contextmenu" ? "click" : "slash";
84510
+ sections.value = getItems({ ...currentContext.value, trigger: trigger2 });
84511
+ selectedId.value = flattenedItems.value[0]?.id || null;
84512
+ }
84264
84513
  });
84265
84514
  props.editor.view.dom.addEventListener("contextmenu", handleRightClick);
84266
84515
  props.editor.on("slashMenu:close", () => {
84516
+ cleanupCustomItems();
84267
84517
  isOpen.value = false;
84268
84518
  searchQuery.value = "";
84519
+ currentContext.value = null;
84269
84520
  });
84270
84521
  });
84271
84522
  vue.onBeforeUnmount(() => {
84272
84523
  document.removeEventListener("keydown", handleGlobalKeyDown);
84273
84524
  document.removeEventListener("mousedown", handleGlobalOutsideClick);
84525
+ cleanupCustomItems();
84274
84526
  if (props.editor) {
84275
84527
  try {
84276
84528
  props.editor.off("slashMenu:open");
@@ -84317,12 +84569,19 @@ const _sfc_main$4 = {
84317
84569
  class: vue.normalizeClass(["slash-menu-item", { "is-selected": item.id === selectedId.value }]),
84318
84570
  onClick: ($event) => executeCommand(item)
84319
84571
  }, [
84320
- item.icon ? (vue.openBlock(), vue.createElementBlock("span", {
84572
+ item.render ? (vue.openBlock(), vue.createElementBlock("div", {
84321
84573
  key: 0,
84322
- class: "slash-menu-item-icon",
84323
- innerHTML: item.icon
84324
- }, null, 8, _hoisted_4)) : vue.createCommentVNode("", true),
84325
- vue.createBaseVNode("span", null, vue.toDisplayString(item.label), 1)
84574
+ ref_for: true,
84575
+ ref: (el) => setCustomItemRef(el, item),
84576
+ class: "slash-menu-custom-item"
84577
+ }, null, 512)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
84578
+ item.icon ? (vue.openBlock(), vue.createElementBlock("span", {
84579
+ key: 0,
84580
+ class: "slash-menu-item-icon",
84581
+ innerHTML: item.icon
84582
+ }, null, 8, _hoisted_4)) : vue.createCommentVNode("", true),
84583
+ vue.createBaseVNode("span", null, vue.toDisplayString(item.label), 1)
84584
+ ], 64))
84326
84585
  ], 10, _hoisted_3$1);
84327
84586
  }), 128))
84328
84587
  ], 64);