@sex-editor/slash 0.0.1 → 0.0.3-dev.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 sexandviolence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @sex-editor/slash
2
+
3
+ Hello World usage:
4
+
5
+ ```ts
6
+ import { createSexEditor } from "@sex-editor/core";
7
+ import { registerSlashPlugin } from "@sex-editor/slash";
8
+
9
+ const editor = createSexEditor("editor", {
10
+ language: "en",
11
+ });
12
+
13
+ registerSlashPlugin(editor);
14
+ ```
15
+
16
+ Install:
17
+
18
+ ```bash
19
+ npm i @sex-editor/slash
20
+ ```
21
+
22
+ ```bash
23
+ pnpm add @sex-editor/slash
24
+ ```
25
+
26
+ License: MIT
package/dist/index.d.ts CHANGED
@@ -8,8 +8,8 @@ declare class SlashMenuPlugin {
8
8
  private filteredItems;
9
9
  private active;
10
10
  private items;
11
+ private commandRange;
11
12
  constructor(editor: SexEditor);
12
- private getMenuItems;
13
13
  register(): void;
14
14
  private filterItems;
15
15
  private showMenu;
@@ -18,6 +18,27 @@ declare class SlashMenuPlugin {
18
18
  private updateMenuUI;
19
19
  private executeSelection;
20
20
  }
21
- declare function registerSlashPlugin(editor: SexEditor): SlashMenuPlugin;
22
21
 
23
- export { SlashMenuPlugin, registerSlashPlugin };
22
+ declare const registerSlashPlugin: (editor: SexEditor) => SlashMenuPlugin;
23
+
24
+ type SlashMenuGroup = "transform" | "insertInline" | "insertBlock";
25
+ interface SlashMenuItem {
26
+ key: string;
27
+ label: string;
28
+ icon: string;
29
+ keywords: string[];
30
+ group: SlashMenuGroup;
31
+ execute: (editor: SexEditor) => void;
32
+ }
33
+ type SlashCommandRange = {
34
+ startKey: string;
35
+ startOffset: number;
36
+ endKey: string;
37
+ endOffset: number;
38
+ };
39
+ type SlashMatch = {
40
+ query: string;
41
+ range: SlashCommandRange;
42
+ };
43
+
44
+ export { type SlashCommandRange, type SlashMatch, type SlashMenuGroup, type SlashMenuItem, SlashMenuPlugin, registerSlashPlugin };
package/dist/index.js CHANGED
@@ -1,32 +1,36 @@
1
- // src/index.ts
1
+ // src/plugin/SlashMenuPlugin.ts
2
2
  import {
3
- $createParagraphNode,
4
- $getSelection,
5
- $isRangeSelection,
3
+ $createRangeSelection,
4
+ $getNodeByKey,
5
+ $getSelection as $getSelection2,
6
+ $isRangeSelection as $isRangeSelection2,
7
+ $isTextNode as $isTextNode2,
8
+ $setSelection,
6
9
  COMMAND_PRIORITY_LOW,
7
10
  KEY_ARROW_DOWN_COMMAND,
8
11
  KEY_ARROW_UP_COMMAND,
9
12
  KEY_ENTER_COMMAND,
10
- KEY_ESCAPE_COMMAND,
11
- TextNode,
12
- $insertNodes
13
+ KEY_ESCAPE_COMMAND
13
14
  } from "lexical";
15
+
16
+ // src/constants/menuItems.ts
14
17
  import {
15
- $createHeadingNode,
16
- $createQuoteNode
17
- } from "@lexical/rich-text";
18
- import {
19
- INSERT_ORDERED_LIST_COMMAND,
20
- INSERT_UNORDERED_LIST_COMMAND
21
- } from "@lexical/list";
18
+ $createParagraphNode,
19
+ $getRoot,
20
+ $getSelection,
21
+ $isRangeSelection,
22
+ $insertNodes
23
+ } from "lexical";
24
+ import { $createHeadingNode, $createQuoteNode } from "@lexical/rich-text";
25
+ import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from "@lexical/list";
22
26
  import { $createCodeNode } from "@lexical/code";
23
27
  import {
28
+ $createAnchorNode,
24
29
  $createHorizontalRuleNode,
25
- $createPageBreakNode,
26
- $createTableNode,
27
30
  $createImageNode,
28
31
  $createInlineImageNode,
29
- $createAnchorNode
32
+ $createPageBreakNode,
33
+ $createTableNode
30
34
  } from "@sex-editor/core";
31
35
 
32
36
  // ../ui/dist/index.js
@@ -731,408 +735,371 @@ ensureStyle(
731
735
  `
732
736
  );
733
737
 
734
- // src/index.ts
735
- var ensureStyle2 = (id, cssText) => {
736
- if (typeof document === "undefined") return;
737
- if (document.getElementById(id)) return;
738
- const style = document.createElement("style");
739
- style.id = id;
740
- style.textContent = cssText;
741
- (document.head ?? document.documentElement).appendChild(style);
738
+ // src/constants/menuItems.ts
739
+ var transformCurrentBlock = (createNode) => (editor) => {
740
+ editor.update(() => {
741
+ const selection = $getSelection();
742
+ if (!$isRangeSelection(selection)) return;
743
+ const anchor = selection.anchor.getNode();
744
+ const topLevel = anchor.getTopLevelElement();
745
+ const newNode = createNode();
746
+ if (!topLevel) {
747
+ $getRoot().append(newNode);
748
+ } else {
749
+ topLevel.replace(newNode);
750
+ newNode.append(...topLevel.getChildren());
751
+ }
752
+ newNode.select();
753
+ });
742
754
  };
743
- ensureStyle2(
744
- "sex-slash-styles",
745
- `.sex-slash-menu {
746
- position: absolute;
747
- z-index: 100;
748
- background: white;
749
- border: 1px solid #e5e7eb;
750
- border-radius: 8px;
751
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
752
- width: 280px;
753
- max-height: 300px;
754
- overflow-y: auto;
755
- padding: 4px;
756
- }
757
-
758
- .sex-slash-menu-item {
759
- display: flex;
760
- align-items: center;
761
- padding: 8px 12px;
762
- cursor: pointer;
763
- border-radius: 4px;
764
- font-size: 14px;
765
- color: #374151;
766
- transition: background-color 0.2s;
767
- }
768
-
769
- .sex-slash-menu-item:hover,
770
- .sex-slash-menu-item.selected {
771
- background-color: #f3f4f6;
772
- }
773
-
774
- .sex-slash-menu-icon {
775
- margin-right: 12px;
776
- width: 20px;
777
- height: 20px;
778
- display: flex;
779
- align-items: center;
780
- justify-content: center;
781
- color: #6b7280;
782
- }
783
-
784
- .sex-slash-menu-label {
785
- flex: 1;
786
- }
787
-
788
- .sex-slash-menu-hidden {
789
- display: none;
790
- }
791
- `
792
- );
793
- var SlashMenuPlugin = class {
794
- constructor(editor) {
795
- this.menuElement = null;
796
- this.selectedIndex = 0;
797
- this.queryString = null;
798
- this.filteredItems = [];
799
- this.active = false;
800
- this.editor = editor;
801
- this.items = this.getMenuItems();
802
- }
803
- getMenuItems() {
804
- const t = this.editor.t;
805
- return [
806
- {
807
- key: "paragraph",
808
- label: t("toolbar.block.paragraph") || "Paragraph",
809
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M14 17H4v2h10v-2zm0-8H4v2h10V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"/></svg>`,
810
- keywords: ["p", "paragraph", "text"],
811
- execute: (editor) => {
812
- editor.update(() => {
813
- const selection = $getSelection();
814
- if ($isRangeSelection(selection)) {
815
- const paragraph = $createParagraphNode();
816
- $insertNodes([paragraph]);
817
- }
818
- });
819
- }
820
- },
821
- {
822
- key: "anchor",
823
- label: t("toolbar.anchor") || "Anchor",
824
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>`,
825
- keywords: ["anchor", "bookmark", "id"],
826
- execute: (editor) => {
827
- const dialog = new AnchorInsertDialog({
828
- t: (key) => editor.t ? editor.t(key) : key,
829
- onSubmit: (data) => {
830
- editor.update(() => {
831
- const node = $createAnchorNode(data.id);
832
- $insertNodes([node]);
833
- });
834
- }
835
- });
836
- dialog.show();
837
- }
838
- },
839
- {
840
- key: "h1",
841
- label: t("toolbar.block.h1") || "Heading 1",
842
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
843
- keywords: ["h1", "heading1", "title"],
844
- execute: (editor) => {
845
- editor.update(() => {
846
- const selection = $getSelection();
847
- if ($isRangeSelection(selection)) {
848
- $insertNodes([$createHeadingNode("h1")]);
849
- }
850
- });
851
- }
852
- },
853
- {
854
- key: "h2",
855
- label: t("toolbar.block.h2") || "Heading 2",
856
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
857
- keywords: ["h2", "heading2", "subtitle"],
858
- execute: (editor) => {
859
- editor.update(() => {
860
- const selection = $getSelection();
861
- if ($isRangeSelection(selection)) {
862
- $insertNodes([$createHeadingNode("h2")]);
863
- }
864
- });
865
- }
866
- },
867
- {
868
- key: "h3",
869
- label: t("toolbar.block.h3") || "Heading 3",
870
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
871
- keywords: ["h3", "heading3"],
872
- execute: (editor) => {
873
- editor.update(() => {
874
- const selection = $getSelection();
875
- if ($isRangeSelection(selection)) {
876
- $insertNodes([$createHeadingNode("h3")]);
877
- }
878
- });
879
- }
880
- },
881
- {
882
- key: "h4",
883
- label: t("toolbar.block.h4") || "Heading 4",
884
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
885
- keywords: ["h4", "heading4"],
886
- execute: (editor) => {
887
- editor.update(() => {
888
- const selection = $getSelection();
889
- if ($isRangeSelection(selection)) {
890
- $insertNodes([$createHeadingNode("h4")]);
891
- }
892
- });
893
- }
894
- },
895
- {
896
- key: "h5",
897
- label: t("toolbar.block.h5") || "Heading 5",
898
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
899
- keywords: ["h5", "heading5"],
900
- execute: (editor) => {
901
- editor.update(() => {
902
- const selection = $getSelection();
903
- if ($isRangeSelection(selection)) {
904
- $insertNodes([$createHeadingNode("h5")]);
905
- }
906
- });
907
- }
908
- },
909
- {
910
- key: "h6",
911
- label: t("toolbar.block.h6") || "Heading 6",
912
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
913
- keywords: ["h6", "heading6"],
914
- execute: (editor) => {
915
- editor.update(() => {
916
- const selection = $getSelection();
917
- if ($isRangeSelection(selection)) {
918
- $insertNodes([$createHeadingNode("h6")]);
919
- }
920
- });
921
- }
922
- },
923
- {
924
- key: "ul",
925
- label: t("toolbar.block.bullet") || "Bullet List",
926
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`,
927
- keywords: ["ul", "list", "bullet"],
928
- execute: (editor) => {
929
- editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0);
930
- }
931
- },
932
- {
933
- key: "ol",
934
- label: t("toolbar.block.number") || "Numbered List",
935
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>`,
936
- keywords: ["ol", "list", "number", "ordered"],
937
- execute: (editor) => {
938
- editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0);
939
- }
940
- },
941
- {
942
- key: "code",
943
- label: t("toolbar.block.code") || "Code Block",
944
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>`,
945
- keywords: ["code", "block", "javascript", "ts"],
946
- execute: (editor) => {
947
- editor.update(() => {
948
- const selection = $getSelection();
949
- if ($isRangeSelection(selection)) {
950
- $insertNodes([$createCodeNode()]);
951
- }
952
- });
953
- }
954
- },
955
- {
956
- key: "quote",
957
- label: t("toolbar.block.quote") || "Quote",
958
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>`,
959
- keywords: ["quote", "blockquote"],
960
- execute: (editor) => {
961
- editor.update(() => {
962
- const selection = $getSelection();
963
- if ($isRangeSelection(selection)) {
964
- $insertNodes([$createQuoteNode()]);
965
- }
966
- });
967
- }
968
- },
969
- {
970
- key: "image",
971
- label: t("toolbar.image") || "Image",
972
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>`,
973
- keywords: ["image", "photo", "picture"],
974
- execute: (editor) => {
975
- const dialog = new ImageInsertDialog({
976
- t: editor.t,
977
- onSubmit: async (data) => {
978
- try {
979
- let src = data.src || "";
980
- const meta = data.type === "file" && data.file ? await Promise.resolve(editor._imageMetaResolver?.(data.file)).catch(() => void 0) : void 0;
981
- if (data.type === "file" && data.file) {
982
- const file = data.file;
983
- const strategy = editor._imageStorageStrategy ?? "auto";
984
- const shouldBase64 = strategy === "base64" || strategy === "auto" && !editor._imageUploadHandler;
985
- if (!shouldBase64) {
986
- if (!editor._imageUploadHandler) {
987
- throw new Error("imageUploadHandler is required when imageStorage is 'upload'");
988
- }
989
- src = await editor._imageUploadHandler(file);
990
- } else {
991
- src = await new Promise((resolve) => {
992
- const reader = new FileReader();
993
- reader.onload = () => resolve(reader.result);
994
- reader.readAsDataURL(file);
995
- });
755
+ var createSlashMenuItems = (editor) => {
756
+ const t = editor.t;
757
+ return [
758
+ {
759
+ key: "paragraph",
760
+ label: t("toolbar.block.paragraph") || "Paragraph",
761
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M14 17H4v2h10v-2zm0-8H4v2h10V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z"/></svg>`,
762
+ keywords: ["p", "paragraph", "text"],
763
+ group: "transform",
764
+ execute: transformCurrentBlock(() => $createParagraphNode())
765
+ },
766
+ {
767
+ key: "anchor",
768
+ label: t("toolbar.anchor") || "Anchor",
769
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>`,
770
+ keywords: ["anchor", "bookmark", "id"],
771
+ group: "insertInline",
772
+ execute: (editor2) => {
773
+ const dialog = new AnchorInsertDialog({
774
+ t: (key) => editor2.t ? editor2.t(key) : key,
775
+ onSubmit: (data) => {
776
+ editor2.update(() => {
777
+ const node = $createAnchorNode(data.id);
778
+ $insertNodes([node]);
779
+ });
780
+ }
781
+ });
782
+ dialog.show();
783
+ }
784
+ },
785
+ {
786
+ key: "h1",
787
+ label: t("toolbar.block.h1") || "Heading 1",
788
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
789
+ keywords: ["h1", "heading1", "title"],
790
+ group: "transform",
791
+ execute: transformCurrentBlock(() => $createHeadingNode("h1"))
792
+ },
793
+ {
794
+ key: "h2",
795
+ label: t("toolbar.block.h2") || "Heading 2",
796
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
797
+ keywords: ["h2", "heading2", "subtitle"],
798
+ group: "transform",
799
+ execute: transformCurrentBlock(() => $createHeadingNode("h2"))
800
+ },
801
+ {
802
+ key: "h3",
803
+ label: t("toolbar.block.h3") || "Heading 3",
804
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
805
+ keywords: ["h3", "heading3"],
806
+ group: "transform",
807
+ execute: transformCurrentBlock(() => $createHeadingNode("h3"))
808
+ },
809
+ {
810
+ key: "h4",
811
+ label: t("toolbar.block.h4") || "Heading 4",
812
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
813
+ keywords: ["h4", "heading4"],
814
+ group: "transform",
815
+ execute: transformCurrentBlock(() => $createHeadingNode("h4"))
816
+ },
817
+ {
818
+ key: "h5",
819
+ label: t("toolbar.block.h5") || "Heading 5",
820
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
821
+ keywords: ["h5", "heading5"],
822
+ group: "transform",
823
+ execute: transformCurrentBlock(() => $createHeadingNode("h5"))
824
+ },
825
+ {
826
+ key: "h6",
827
+ label: t("toolbar.block.h6") || "Heading 6",
828
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>`,
829
+ keywords: ["h6", "heading6"],
830
+ group: "transform",
831
+ execute: transformCurrentBlock(() => $createHeadingNode("h6"))
832
+ },
833
+ {
834
+ key: "ul",
835
+ label: t("toolbar.block.bullet") || "Bullet List",
836
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>`,
837
+ keywords: ["ul", "list", "bullet"],
838
+ group: "transform",
839
+ execute: (editor2) => editor2.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0)
840
+ },
841
+ {
842
+ key: "ol",
843
+ label: t("toolbar.block.number") || "Numbered List",
844
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>`,
845
+ keywords: ["ol", "list", "number", "ordered"],
846
+ group: "transform",
847
+ execute: (editor2) => editor2.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0)
848
+ },
849
+ {
850
+ key: "code",
851
+ label: t("toolbar.block.code") || "Code Block",
852
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>`,
853
+ keywords: ["code", "block", "javascript", "ts"],
854
+ group: "transform",
855
+ execute: transformCurrentBlock(() => $createCodeNode())
856
+ },
857
+ {
858
+ key: "quote",
859
+ label: t("toolbar.block.quote") || "Quote",
860
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg>`,
861
+ keywords: ["quote", "blockquote"],
862
+ group: "transform",
863
+ execute: transformCurrentBlock(() => $createQuoteNode())
864
+ },
865
+ {
866
+ key: "image",
867
+ label: t("toolbar.image") || "Image",
868
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>`,
869
+ keywords: ["image", "photo", "picture"],
870
+ group: "insertBlock",
871
+ execute: (editor2) => {
872
+ const dialog = new ImageInsertDialog({
873
+ t: editor2.t,
874
+ onSubmit: async (data) => {
875
+ try {
876
+ let src = data.src || "";
877
+ const meta = data.type === "file" && data.file ? await Promise.resolve(editor2._imageMetaResolver?.(data.file)).catch(() => void 0) : void 0;
878
+ if (data.type === "file" && data.file) {
879
+ const file = data.file;
880
+ const strategy = editor2._imageStorageStrategy ?? "auto";
881
+ const shouldBase64 = strategy === "base64" || strategy === "auto" && !editor2._imageUploadHandler;
882
+ if (!shouldBase64) {
883
+ if (!editor2._imageUploadHandler) {
884
+ throw new Error("imageUploadHandler is required when imageStorage is 'upload'");
996
885
  }
997
- }
998
- if (src) {
999
- editor.update(() => {
1000
- const node = $createImageNode({
1001
- src,
1002
- altText: data.alt,
1003
- maxWidth: 500,
1004
- meta: meta || void 0
1005
- });
1006
- $insertNodes([node]);
886
+ src = await editor2._imageUploadHandler(file);
887
+ } else {
888
+ src = await new Promise((resolve) => {
889
+ const reader = new FileReader();
890
+ reader.onload = () => resolve(reader.result);
891
+ reader.readAsDataURL(file);
1007
892
  });
1008
893
  }
1009
- } catch (e) {
1010
- console.error("Image insert failed", e);
1011
894
  }
895
+ if (src) {
896
+ editor2.update(() => {
897
+ const node = $createImageNode({
898
+ src,
899
+ altText: data.alt,
900
+ maxWidth: 500,
901
+ meta: meta || void 0
902
+ });
903
+ $insertNodes([node]);
904
+ });
905
+ }
906
+ } catch (e) {
907
+ console.error("Image insert failed", e);
1012
908
  }
1013
- });
1014
- dialog.show();
1015
- }
1016
- },
1017
- {
1018
- key: "inline-image",
1019
- label: t("toolbar.inlineImage") || "Inline Image",
1020
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>`,
1021
- keywords: ["inline-image", "inline", "image", "img"],
1022
- execute: (editor) => {
1023
- const dialog = new ImageInsertDialog({
1024
- t: editor.t,
1025
- onSubmit: async (data) => {
1026
- try {
1027
- let src = data.src || "";
1028
- const meta = data.type === "file" && data.file ? await Promise.resolve(editor._imageMetaResolver?.(data.file)).catch(() => void 0) : void 0;
1029
- if (data.type === "file" && data.file) {
1030
- const file = data.file;
1031
- const strategy = editor._imageStorageStrategy ?? "auto";
1032
- const shouldBase64 = strategy === "base64" || strategy === "auto" && !editor._imageUploadHandler;
1033
- if (!shouldBase64) {
1034
- if (!editor._imageUploadHandler) {
1035
- throw new Error("imageUploadHandler is required when imageStorage is 'upload'");
1036
- }
1037
- src = await editor._imageUploadHandler(file);
1038
- } else {
1039
- src = await new Promise((resolve) => {
1040
- const reader = new FileReader();
1041
- reader.onload = () => resolve(reader.result);
1042
- reader.readAsDataURL(file);
1043
- });
909
+ }
910
+ });
911
+ dialog.show();
912
+ }
913
+ },
914
+ {
915
+ key: "inline-image",
916
+ label: t("toolbar.inlineImage") || "Inline Image",
917
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>`,
918
+ keywords: ["inline-image", "inline", "image", "img"],
919
+ group: "insertInline",
920
+ execute: (editor2) => {
921
+ const dialog = new ImageInsertDialog({
922
+ t: editor2.t,
923
+ onSubmit: async (data) => {
924
+ try {
925
+ let src = data.src || "";
926
+ const meta = data.type === "file" && data.file ? await Promise.resolve(editor2._imageMetaResolver?.(data.file)).catch(() => void 0) : void 0;
927
+ if (data.type === "file" && data.file) {
928
+ const file = data.file;
929
+ const strategy = editor2._imageStorageStrategy ?? "auto";
930
+ const shouldBase64 = strategy === "base64" || strategy === "auto" && !editor2._imageUploadHandler;
931
+ if (!shouldBase64) {
932
+ if (!editor2._imageUploadHandler) {
933
+ throw new Error("imageUploadHandler is required when imageStorage is 'upload'");
1044
934
  }
1045
- }
1046
- if (src) {
1047
- editor.update(() => {
1048
- const node = $createInlineImageNode({
1049
- src,
1050
- altText: data.alt,
1051
- maxWidth: 500,
1052
- width: 100,
1053
- meta: meta || void 0
1054
- });
1055
- $insertNodes([node]);
935
+ src = await editor2._imageUploadHandler(file);
936
+ } else {
937
+ src = await new Promise((resolve) => {
938
+ const reader = new FileReader();
939
+ reader.onload = () => resolve(reader.result);
940
+ reader.readAsDataURL(file);
1056
941
  });
1057
942
  }
1058
- } catch (e) {
1059
- console.error("Inline image insert failed", e);
1060
943
  }
944
+ if (src) {
945
+ editor2.update(() => {
946
+ const node = $createInlineImageNode({
947
+ src,
948
+ altText: data.alt,
949
+ maxWidth: 500,
950
+ width: 100,
951
+ meta: meta || void 0
952
+ });
953
+ $insertNodes([node]);
954
+ });
955
+ }
956
+ } catch (e) {
957
+ console.error("Inline image insert failed", e);
1061
958
  }
1062
- });
1063
- dialog.show();
1064
- }
1065
- },
1066
- {
1067
- key: "table",
1068
- label: t("toolbar.insertTable") || "Table",
1069
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"/></svg>`,
1070
- keywords: ["table", "grid"],
1071
- execute: (editor) => {
1072
- const dialog = new TableInsertDialog({
1073
- t,
1074
- onSubmit: (rows, cols) => {
1075
- editor.update(() => {
1076
- const node = $createTableNode(rows, cols);
1077
- $insertNodes([node]);
1078
- });
1079
- }
1080
- });
1081
- dialog.show();
1082
- }
1083
- },
1084
- {
1085
- key: "hr",
1086
- label: t("toolbar.horizontalRule") || "Horizontal Rule",
1087
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M4 11h16v2H4z"/></svg>`,
1088
- keywords: ["hr", "line", "separator"],
1089
- execute: (editor) => {
1090
- editor.update(() => {
1091
- const selection = $getSelection();
1092
- if ($isRangeSelection(selection)) {
1093
- $insertNodes([$createHorizontalRuleNode()]);
1094
- }
1095
- });
1096
- }
1097
- },
1098
- {
1099
- key: "page-break",
1100
- label: t("toolbar.pageBreak") || "Page Break",
1101
- icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M9 13h6v-2h-6v2zm-2 2H5v-2h2v2zm-2-4H3v2h2v-2zm0 8h2v-2H5v2zm14-2v-2h-2v2h2zm-2 4h2v-2h-2v2zM6 7H4v2h2V7zm14 0v2h-2V7h2zm-8-2h6v2h-6V5zM8 7H6v2h2V7zm8 2v2h2V9h-2zm-4 4h2v-2h-2v2zm-4 0h2v-2H8v2zm4 4h2v-2h-2v2z"/></svg>`,
1102
- keywords: ["page", "break", "split"],
1103
- execute: (editor) => {
1104
- editor.update(() => {
1105
- const selection = $getSelection();
1106
- if ($isRangeSelection(selection)) {
1107
- $insertNodes([$createPageBreakNode()]);
1108
- }
1109
- });
1110
- }
959
+ }
960
+ });
961
+ dialog.show();
962
+ }
963
+ },
964
+ {
965
+ key: "table",
966
+ label: t("toolbar.insertTable") || "Table",
967
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"/></svg>`,
968
+ keywords: ["table", "grid"],
969
+ group: "insertBlock",
970
+ execute: (editor2) => {
971
+ const dialog = new TableInsertDialog({
972
+ t,
973
+ onSubmit: (rows, cols) => {
974
+ editor2.update(() => {
975
+ const node = $createTableNode(rows, cols);
976
+ $insertNodes([node]);
977
+ });
978
+ }
979
+ });
980
+ dialog.show();
981
+ }
982
+ },
983
+ {
984
+ key: "hr",
985
+ label: t("toolbar.horizontalRule") || "Horizontal Rule",
986
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M4 11h16v2H4z"/></svg>`,
987
+ keywords: ["hr", "line", "separator"],
988
+ group: "insertBlock",
989
+ execute: (editor2) => {
990
+ editor2.update(() => {
991
+ const selection = $getSelection();
992
+ if ($isRangeSelection(selection)) $insertNodes([$createHorizontalRuleNode()]);
993
+ });
1111
994
  }
1112
- ];
995
+ },
996
+ {
997
+ key: "page-break",
998
+ label: t("toolbar.pageBreak") || "Page Break",
999
+ icon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M9 13h6v-2h-6v2zm-2 2H5v-2h2v2zm-2-4H3v2h2v-2zm0 8h2v-2H5v2zm14-2v-2h-2v2h2zm-2 4h2v-2h-2v2zM6 7H4v2h2V7zm14 0v2h-2V7h2zm-8-2h6v2h-6V5zM8 7H6v2h2V7zm8 2v2h2V9h-2zm-4 4h2v-2h-2v2zm-4 0h2v-2H8v2zm4 4h2v-2h-2v2z"/></svg>`,
1000
+ keywords: ["page", "break", "split"],
1001
+ group: "insertBlock",
1002
+ execute: (editor2) => {
1003
+ editor2.update(() => {
1004
+ const selection = $getSelection();
1005
+ if ($isRangeSelection(selection)) $insertNodes([$createPageBreakNode()]);
1006
+ });
1007
+ }
1008
+ }
1009
+ ];
1010
+ };
1011
+
1012
+ // src/utils/getSlashMatch.ts
1013
+ import {
1014
+ $isElementNode,
1015
+ $isTextNode
1016
+ } from "lexical";
1017
+ var collectTextNodes = (node, out) => {
1018
+ if ($isTextNode(node)) {
1019
+ out.push(node);
1020
+ return;
1021
+ }
1022
+ if ($isElementNode(node)) {
1023
+ const children = node.getChildren();
1024
+ for (const child of children) collectTextNodes(child, out);
1025
+ }
1026
+ };
1027
+ var getSlashMatch = (selection) => {
1028
+ const anchorNode = selection.anchor.getNode();
1029
+ if (!$isTextNode(anchorNode)) return null;
1030
+ const block = anchorNode.getTopLevelElement();
1031
+ if (!block) return null;
1032
+ const textNodes = [];
1033
+ collectTextNodes(block, textNodes);
1034
+ if (textNodes.length === 0) return null;
1035
+ let cursorIndex = null;
1036
+ let running = 0;
1037
+ for (const n of textNodes) {
1038
+ if (n.getKey() === anchorNode.getKey()) {
1039
+ cursorIndex = running + selection.anchor.offset;
1040
+ break;
1041
+ }
1042
+ running += n.getTextContentSize();
1043
+ }
1044
+ if (cursorIndex === null) return null;
1045
+ const blockText = block.getTextContent();
1046
+ const beforeCursor = blockText.slice(0, cursorIndex);
1047
+ const slashIndex = beforeCursor.lastIndexOf("/");
1048
+ if (slashIndex === -1) return null;
1049
+ const query = beforeCursor.slice(slashIndex + 1);
1050
+ if (/\s/.test(query)) return null;
1051
+ const indexToPoint = (index) => {
1052
+ let remaining = index;
1053
+ for (const n of textNodes) {
1054
+ const size = n.getTextContentSize();
1055
+ if (remaining <= size) return { key: n.getKey(), offset: remaining };
1056
+ remaining -= size;
1057
+ }
1058
+ const last = textNodes[textNodes.length - 1];
1059
+ return { key: last.getKey(), offset: last.getTextContentSize() };
1060
+ };
1061
+ const start = indexToPoint(slashIndex);
1062
+ const end = indexToPoint(cursorIndex);
1063
+ if (!start || !end) return null;
1064
+ return {
1065
+ query,
1066
+ range: {
1067
+ startKey: start.key,
1068
+ startOffset: start.offset,
1069
+ endKey: end.key,
1070
+ endOffset: end.offset
1071
+ }
1072
+ };
1073
+ };
1074
+
1075
+ // src/plugin/SlashMenuPlugin.ts
1076
+ var SlashMenuPlugin = class {
1077
+ constructor(editor) {
1078
+ this.menuElement = null;
1079
+ this.selectedIndex = 0;
1080
+ this.queryString = null;
1081
+ this.filteredItems = [];
1082
+ this.active = false;
1083
+ this.commandRange = null;
1084
+ this.editor = editor;
1085
+ this.items = createSlashMenuItems(editor);
1113
1086
  }
1114
1087
  register() {
1115
1088
  this.editor.registerUpdateListener(({ editorState }) => {
1116
1089
  editorState.read(() => {
1117
- const selection = $getSelection();
1118
- if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
1090
+ const selection = $getSelection2();
1091
+ if (!$isRangeSelection2(selection) || !selection.isCollapsed()) {
1119
1092
  this.hideMenu();
1120
1093
  return;
1121
1094
  }
1122
- const anchor = selection.anchor;
1123
- const node = anchor.getNode();
1124
- if (node instanceof TextNode) {
1125
- const textContent = node.getTextContent();
1126
- const block = node.getTopLevelElementOrThrow();
1127
- const blockText = block.getTextContent();
1128
- if (blockText.startsWith("/") && selection.anchor.offset <= blockText.length) {
1129
- const query = blockText.substring(1);
1130
- this.queryString = query;
1131
- this.showMenu(selection);
1132
- return;
1133
- }
1095
+ const match = getSlashMatch(selection);
1096
+ if (!match) {
1097
+ this.hideMenu();
1098
+ return;
1134
1099
  }
1135
- this.hideMenu();
1100
+ this.queryString = match.query;
1101
+ this.commandRange = match.range;
1102
+ this.showMenu(selection);
1136
1103
  });
1137
1104
  });
1138
1105
  this.editor.registerCommand(
@@ -1193,6 +1160,16 @@ var SlashMenuPlugin = class {
1193
1160
  this.filteredItems = this.items.filter(
1194
1161
  (item) => item.label.toLowerCase().includes(q) || item.keywords.some((k) => k.toLowerCase().includes(q))
1195
1162
  );
1163
+ const groupOrder = ["transform", "insertInline", "insertBlock"];
1164
+ const groupIndex = (g) => groupOrder.indexOf(g);
1165
+ const keyOrder = /* @__PURE__ */ new Map();
1166
+ this.items.forEach((it, idx) => keyOrder.set(it.key, idx));
1167
+ this.filteredItems.sort((a, b) => {
1168
+ const ga = groupIndex(a.group);
1169
+ const gb = groupIndex(b.group);
1170
+ if (ga !== gb) return ga - gb;
1171
+ return (keyOrder.get(a.key) ?? 0) - (keyOrder.get(b.key) ?? 0);
1172
+ });
1196
1173
  }
1197
1174
  showMenu(selection) {
1198
1175
  this.active = true;
@@ -1201,6 +1178,9 @@ var SlashMenuPlugin = class {
1201
1178
  this.hideMenu();
1202
1179
  return;
1203
1180
  }
1181
+ if (this.selectedIndex >= this.filteredItems.length) {
1182
+ this.selectedIndex = 0;
1183
+ }
1204
1184
  if (!this.menuElement) {
1205
1185
  this.menuElement = document.createElement("div");
1206
1186
  this.menuElement.className = "sex-slash-menu";
@@ -1208,7 +1188,21 @@ var SlashMenuPlugin = class {
1208
1188
  }
1209
1189
  this.menuElement.innerHTML = "";
1210
1190
  this.menuElement.style.display = "block";
1191
+ const translate = (key) => this.editor.t?.(key) ?? key;
1192
+ const groupLabels = {
1193
+ transform: translate("slash.group.transform"),
1194
+ insertInline: translate("slash.group.insertInline"),
1195
+ insertBlock: translate("slash.group.insertBlock")
1196
+ };
1197
+ let lastGroup = null;
1211
1198
  this.filteredItems.forEach((item, index) => {
1199
+ if (item.group !== lastGroup) {
1200
+ lastGroup = item.group;
1201
+ const groupEl = document.createElement("div");
1202
+ groupEl.className = "sex-slash-menu-group";
1203
+ groupEl.textContent = groupLabels[item.group];
1204
+ this.menuElement?.appendChild(groupEl);
1205
+ }
1212
1206
  const el = document.createElement("div");
1213
1207
  el.className = `sex-slash-menu-item ${index === this.selectedIndex ? "selected" : ""}`;
1214
1208
  el.innerHTML = `
@@ -1235,9 +1229,8 @@ var SlashMenuPlugin = class {
1235
1229
  this.active = false;
1236
1230
  this.queryString = null;
1237
1231
  this.selectedIndex = 0;
1238
- if (this.menuElement) {
1239
- this.menuElement.style.display = "none";
1240
- }
1232
+ this.commandRange = null;
1233
+ if (this.menuElement) this.menuElement.style.display = "none";
1241
1234
  }
1242
1235
  moveSelection(delta) {
1243
1236
  this.selectedIndex += delta;
@@ -1262,31 +1255,108 @@ var SlashMenuPlugin = class {
1262
1255
  }
1263
1256
  executeSelection() {
1264
1257
  const item = this.filteredItems[this.selectedIndex];
1265
- if (item) {
1266
- const deleteCount = (this.queryString?.length || 0) + 1;
1267
- this.hideMenu();
1268
- this.editor.update(() => {
1269
- const selection = $getSelection();
1270
- if ($isRangeSelection(selection)) {
1271
- const anchor = selection.anchor;
1272
- const node = anchor.getNode();
1273
- if (node instanceof TextNode) {
1274
- node.spliceText(0, deleteCount, "", true);
1275
- }
1276
- }
1277
- }, {
1258
+ if (!item) return;
1259
+ const commandRange = this.commandRange;
1260
+ this.hideMenu();
1261
+ this.editor.update(
1262
+ () => {
1263
+ if (!commandRange) return;
1264
+ const startNode = $getNodeByKey(commandRange.startKey);
1265
+ const endNode = $getNodeByKey(commandRange.endKey);
1266
+ if (!startNode || !endNode) return;
1267
+ if (!$isTextNode2(startNode) || !$isTextNode2(endNode)) return;
1268
+ const rangeSelection = $createRangeSelection();
1269
+ rangeSelection.setTextNodeRange(
1270
+ startNode,
1271
+ commandRange.startOffset,
1272
+ endNode,
1273
+ commandRange.endOffset
1274
+ );
1275
+ $setSelection(rangeSelection);
1276
+ rangeSelection.removeText();
1277
+ },
1278
+ {
1278
1279
  onUpdate: () => {
1279
1280
  item.execute(this.editor);
1280
1281
  }
1281
- });
1282
- }
1282
+ }
1283
+ );
1283
1284
  }
1284
1285
  };
1285
- function registerSlashPlugin(editor) {
1286
+
1287
+ // src/plugin/registerSlashPlugin.ts
1288
+ var registerSlashPlugin = (editor) => {
1286
1289
  const plugin = new SlashMenuPlugin(editor);
1287
1290
  plugin.register();
1288
1291
  return plugin;
1292
+ };
1293
+
1294
+ // src/index.ts
1295
+ var ensureStyle2 = (id, cssText) => {
1296
+ if (typeof document === "undefined") return;
1297
+ if (document.getElementById(id)) return;
1298
+ const style = document.createElement("style");
1299
+ style.id = id;
1300
+ style.textContent = cssText;
1301
+ (document.head ?? document.documentElement).appendChild(style);
1302
+ };
1303
+ ensureStyle2(
1304
+ "sex-slash-styles",
1305
+ `.sex-slash-menu {
1306
+ position: absolute;
1307
+ z-index: 100;
1308
+ background: white;
1309
+ border: 1px solid #e5e7eb;
1310
+ border-radius: 8px;
1311
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
1312
+ width: 280px;
1313
+ max-height: 300px;
1314
+ overflow-y: auto;
1315
+ padding: 4px;
1316
+ }
1317
+
1318
+ .sex-slash-menu-item {
1319
+ display: flex;
1320
+ align-items: center;
1321
+ padding: 8px 12px;
1322
+ cursor: pointer;
1323
+ border-radius: 4px;
1324
+ font-size: 14px;
1325
+ color: #374151;
1326
+ transition: background-color 0.2s;
1327
+ }
1328
+
1329
+ .sex-slash-menu-item:hover,
1330
+ .sex-slash-menu-item.selected {
1331
+ background-color: #f3f4f6;
1289
1332
  }
1333
+
1334
+ .sex-slash-menu-icon {
1335
+ margin-right: 12px;
1336
+ width: 20px;
1337
+ height: 20px;
1338
+ display: flex;
1339
+ align-items: center;
1340
+ justify-content: center;
1341
+ color: #6b7280;
1342
+ }
1343
+
1344
+ .sex-slash-menu-label {
1345
+ flex: 1;
1346
+ }
1347
+
1348
+ .sex-slash-menu-group {
1349
+ padding: 6px 12px;
1350
+ font-size: 12px;
1351
+ color: #6b7280;
1352
+ user-select: none;
1353
+ }
1354
+
1355
+ .sex-slash-menu-hidden {
1356
+ display: none;
1357
+ }
1358
+ `
1359
+ );
1290
1360
  export {
1291
1361
  SlashMenuPlugin,
1292
1362
  registerSlashPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sex-editor/slash",
3
- "version": "0.0.1",
3
+ "version": "0.0.3-dev.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "devDependencies": {
16
16
  "tsup": "8.3.5",
17
17
  "typescript": "^5.6.3",
18
- "@sex-editor/core": "0.0.2",
19
- "@sex-editor/ui": "0.0.1"
18
+ "@sex-editor/ui": "0.0.1",
19
+ "@sex-editor/core": "0.0.3-dev.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "@sex-editor/core": ">=0.0.2 <0.0.3",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "keywords": [],
37
37
  "author": "",
38
- "license": "ISC",
38
+ "license": "MIT",
39
39
  "scripts": {
40
40
  "dev": "tsup --watch",
41
41
  "build": "tsup"