@nkzw/mdx-editor 0.1.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.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/UPSTREAM.md +21 -0
  4. package/dist/EditorIcon.js +75 -0
  5. package/dist/FormatConstants.js +20 -0
  6. package/dist/MDXEditor.js +189 -0
  7. package/dist/MarkdownEditor.js +281 -0
  8. package/dist/PersistentMarkdownEditor.js +358 -0
  9. package/dist/RealmWithPlugins.js +35 -0
  10. package/dist/core.d.ts +3232 -0
  11. package/dist/core.js +354 -0
  12. package/dist/defaultSvgIcons.js +371 -0
  13. package/dist/directive-editors/AdmonitionDirectiveDescriptor.js +28 -0
  14. package/dist/directive-editors/GenericDirectiveEditor.js +37 -0
  15. package/dist/exportMarkdownFromLexical.js +262 -0
  16. package/dist/horizontalRuleShortcut.js +37 -0
  17. package/dist/importMarkdownToLexical.js +172 -0
  18. package/dist/index.d.ts +86 -0
  19. package/dist/index.js +8 -0
  20. package/dist/jsx-editors/GenericJsxEditor.js +84 -0
  21. package/dist/mdastUtilHtmlComment.js +125 -0
  22. package/dist/persistence.d.ts +128 -0
  23. package/dist/persistence.js +4 -0
  24. package/dist/plugins/codeblock/CodeBlockNode.js +183 -0
  25. package/dist/plugins/codeblock/CodeBlockVisitor.js +14 -0
  26. package/dist/plugins/codeblock/MdastCodeVisitor.js +23 -0
  27. package/dist/plugins/codeblock/findCodeBlockDescriptor.js +8 -0
  28. package/dist/plugins/codeblock/index.js +46 -0
  29. package/dist/plugins/codemirror/CodeMirrorEditor.js +145 -0
  30. package/dist/plugins/codemirror/index.js +115 -0
  31. package/dist/plugins/codemirror/useCodeMirrorRef.js +101 -0
  32. package/dist/plugins/core/GenericHTMLNode.js +118 -0
  33. package/dist/plugins/core/LexicalGenericHTMLNodeVisitor.js +15 -0
  34. package/dist/plugins/core/LexicalLinebreakVisitor.js +10 -0
  35. package/dist/plugins/core/LexicalParagraphVisitor.js +10 -0
  36. package/dist/plugins/core/LexicalRootVisitor.js +10 -0
  37. package/dist/plugins/core/LexicalTextVisitor.js +160 -0
  38. package/dist/plugins/core/MdastBreakVisitor.js +10 -0
  39. package/dist/plugins/core/MdastFormattingVisitor.js +81 -0
  40. package/dist/plugins/core/MdastHTMLNode.js +120 -0
  41. package/dist/plugins/core/MdastHTMLVisitor.js +17 -0
  42. package/dist/plugins/core/MdastParagraphVisitor.js +23 -0
  43. package/dist/plugins/core/MdastRootVisitor.js +9 -0
  44. package/dist/plugins/core/MdastTextVisitor.js +16 -0
  45. package/dist/plugins/core/NestedLexicalEditor.js +221 -0
  46. package/dist/plugins/core/PropertyPopover.js +75 -0
  47. package/dist/plugins/core/SharedHistoryPlugin.js +10 -0
  48. package/dist/plugins/core/index.js +692 -0
  49. package/dist/plugins/core/ui/DownshiftAutoComplete.js +89 -0
  50. package/dist/plugins/core/ui/PopoverUtils.js +22 -0
  51. package/dist/plugins/diff-source/DiffSourceWrapper.js +24 -0
  52. package/dist/plugins/diff-source/DiffViewer.js +84 -0
  53. package/dist/plugins/diff-source/SourceEditor.js +60 -0
  54. package/dist/plugins/diff-source/index.js +27 -0
  55. package/dist/plugins/directives/DirectiveNode.js +107 -0
  56. package/dist/plugins/directives/DirectiveVisitor.js +10 -0
  57. package/dist/plugins/directives/MdastDirectiveVisitor.js +30 -0
  58. package/dist/plugins/directives/index.js +45 -0
  59. package/dist/plugins/frontmatter/FrontmatterEditor.js +137 -0
  60. package/dist/plugins/frontmatter/FrontmatterNode.js +70 -0
  61. package/dist/plugins/frontmatter/LexicalFrontmatterVisitor.js +10 -0
  62. package/dist/plugins/frontmatter/MdastFrontmatterVisitor.js +10 -0
  63. package/dist/plugins/frontmatter/index.js +113 -0
  64. package/dist/plugins/headings/LexicalHeadingVisitor.js +11 -0
  65. package/dist/plugins/headings/MdastHeadingVisitor.js +10 -0
  66. package/dist/plugins/headings/index.js +63 -0
  67. package/dist/plugins/image/EditImageToolbar.js +58 -0
  68. package/dist/plugins/image/ImageDialog.js +132 -0
  69. package/dist/plugins/image/ImageEditor.js +279 -0
  70. package/dist/plugins/image/ImageNode.js +187 -0
  71. package/dist/plugins/image/ImagePlaceholder.js +9 -0
  72. package/dist/plugins/image/ImageResizer.js +223 -0
  73. package/dist/plugins/image/LexicalImageVisitor.js +42 -0
  74. package/dist/plugins/image/MdastImageVisitor.js +91 -0
  75. package/dist/plugins/image/index.js +364 -0
  76. package/dist/plugins/jsx/LexicalJsxNode.js +103 -0
  77. package/dist/plugins/jsx/LexicalJsxVisitor.js +27 -0
  78. package/dist/plugins/jsx/LexicalMdxExpressionNode.js +130 -0
  79. package/dist/plugins/jsx/LexicalMdxExpressionVisitor.js +14 -0
  80. package/dist/plugins/jsx/MdastMdxExpressionVisitor.js +11 -0
  81. package/dist/plugins/jsx/MdastMdxJsEsmVisitor.js +8 -0
  82. package/dist/plugins/jsx/MdastMdxJsxElementVisitor.js +28 -0
  83. package/dist/plugins/jsx/index.js +97 -0
  84. package/dist/plugins/jsx/jsxTagName.js +7 -0
  85. package/dist/plugins/link/AutoLinkPlugin.js +18 -0
  86. package/dist/plugins/link/LexicalLinkVisitor.js +10 -0
  87. package/dist/plugins/link/MdastLinkVisitor.js +14 -0
  88. package/dist/plugins/link/index.js +34 -0
  89. package/dist/plugins/link-dialog/LinkDialog.js +262 -0
  90. package/dist/plugins/link-dialog/index.js +304 -0
  91. package/dist/plugins/lists/CheckListPlugin.js +270 -0
  92. package/dist/plugins/lists/LexicalListItemVisitor.js +41 -0
  93. package/dist/plugins/lists/LexicalListVisitor.js +13 -0
  94. package/dist/plugins/lists/MdastListItemVisitor.js +11 -0
  95. package/dist/plugins/lists/MdastListVisitor.js +19 -0
  96. package/dist/plugins/lists/NotesListItemNode.js +22 -0
  97. package/dist/plugins/lists/index.js +111 -0
  98. package/dist/plugins/markdown-shortcut/index.js +114 -0
  99. package/dist/plugins/maxlength/index.js +36 -0
  100. package/dist/plugins/quote/LexicalQuoteVisitor.js +10 -0
  101. package/dist/plugins/quote/MdastBlockQuoteVisitor.js +10 -0
  102. package/dist/plugins/quote/index.js +18 -0
  103. package/dist/plugins/remote/index.js +52 -0
  104. package/dist/plugins/search/index.js +360 -0
  105. package/dist/plugins/table/LexicalTableVisitor.js +10 -0
  106. package/dist/plugins/table/MdastTableVisitor.js +10 -0
  107. package/dist/plugins/table/TableEditor.js +527 -0
  108. package/dist/plugins/table/TableNode.js +208 -0
  109. package/dist/plugins/table/index.js +66 -0
  110. package/dist/plugins/thematic-break/LexicalThematicBreakVisitor.js +10 -0
  111. package/dist/plugins/thematic-break/MdastThematicBreakVisitor.js +10 -0
  112. package/dist/plugins/thematic-break/index.js +27 -0
  113. package/dist/plugins/toolbar/components/BlockTypeSelect.js +62 -0
  114. package/dist/plugins/toolbar/components/BoldItalicUnderlineToggles.js +98 -0
  115. package/dist/plugins/toolbar/components/ChangeAdmonitionType.js +43 -0
  116. package/dist/plugins/toolbar/components/ChangeCodeMirrorLanguage.js +42 -0
  117. package/dist/plugins/toolbar/components/CodeToggle.js +21 -0
  118. package/dist/plugins/toolbar/components/CreateLink.js +24 -0
  119. package/dist/plugins/toolbar/components/DiffSourceToggleWrapper.js +42 -0
  120. package/dist/plugins/toolbar/components/HighlightToggle.js +28 -0
  121. package/dist/plugins/toolbar/components/InsertAdmonition.js +34 -0
  122. package/dist/plugins/toolbar/components/InsertCodeBlock.js +23 -0
  123. package/dist/plugins/toolbar/components/InsertFrontmatter.js +28 -0
  124. package/dist/plugins/toolbar/components/InsertImage.js +29 -0
  125. package/dist/plugins/toolbar/components/InsertTable.js +25 -0
  126. package/dist/plugins/toolbar/components/InsertThematicBreak.js +23 -0
  127. package/dist/plugins/toolbar/components/KitchenSinkToolbar.js +82 -0
  128. package/dist/plugins/toolbar/components/ListsToggle.js +29 -0
  129. package/dist/plugins/toolbar/components/UndoRedo.js +60 -0
  130. package/dist/plugins/toolbar/index.js +32 -0
  131. package/dist/plugins/toolbar/primitives/DialogButton.js +130 -0
  132. package/dist/plugins/toolbar/primitives/TooltipWrap.js +17 -0
  133. package/dist/plugins/toolbar/primitives/select.js +76 -0
  134. package/dist/plugins/toolbar/primitives/toolbar.js +144 -0
  135. package/dist/registerCodeBoundaryEscape.js +40 -0
  136. package/dist/styles/lexical-theme.module.css.js +62 -0
  137. package/dist/styles/lexicalTheme.js +32 -0
  138. package/dist/styles/ui.module.css.js +296 -0
  139. package/dist/styles.css +2838 -0
  140. package/dist/utils/detectMac.js +16 -0
  141. package/dist/utils/fp.js +44 -0
  142. package/dist/utils/isPartOftheEditorUI.js +12 -0
  143. package/dist/utils/lexicalHelpers.js +185 -0
  144. package/dist/utils/makeHslTransparent.js +6 -0
  145. package/dist/utils/mergeStyleAttributes.js +22 -0
  146. package/dist/utils/uuid4.js +10 -0
  147. package/dist/utils/voidEmitter.js +15 -0
  148. package/package.json +133 -0
@@ -0,0 +1,125 @@
1
+ import { factorySpace } from "micromark-factory-space";
2
+ import { markdownLineEnding } from "micromark-util-character";
3
+ import { codes, types } from "micromark-util-symbol";
4
+ function commentFromMarkdown(_options) {
5
+ return {
6
+ canContainEols: ["comment"],
7
+ enter: {
8
+ comment(_) {
9
+ this.buffer();
10
+ }
11
+ },
12
+ exit: {
13
+ comment(token) {
14
+ this.resume();
15
+ }
16
+ }
17
+ };
18
+ }
19
+ const tokenize = (effects, ok, nok) => {
20
+ return start;
21
+ function start(code) {
22
+ effects.enter("comment");
23
+ effects.consume(code);
24
+ return open;
25
+ }
26
+ function open(code) {
27
+ if (code === codes.exclamationMark) {
28
+ effects.consume(code);
29
+ return declarationOpen;
30
+ }
31
+ return nok(code);
32
+ }
33
+ function declarationOpen(code) {
34
+ if (code === codes.dash) {
35
+ effects.consume(code);
36
+ return commentOpen;
37
+ }
38
+ return nok(code);
39
+ }
40
+ function commentOpen(code) {
41
+ if (code === codes.dash) {
42
+ effects.consume(code);
43
+ return commentStart;
44
+ }
45
+ return nok(code);
46
+ }
47
+ function commentStart(code) {
48
+ if (code === codes.greaterThan) {
49
+ return nok(code);
50
+ }
51
+ if (markdownLineEnding(code)) {
52
+ return atLineEnding(code);
53
+ }
54
+ effects.enter(types.data);
55
+ if (code === codes.dash) {
56
+ effects.consume(code);
57
+ return commentStartDash;
58
+ }
59
+ return comment2(code);
60
+ }
61
+ function commentStartDash(code) {
62
+ if (code === codes.greaterThan) {
63
+ return nok(code);
64
+ }
65
+ return comment2(code);
66
+ }
67
+ function comment2(code) {
68
+ if (code === codes.eof) {
69
+ return nok(code);
70
+ }
71
+ if (code === codes.dash) {
72
+ effects.consume(code);
73
+ return commentClose;
74
+ }
75
+ if (markdownLineEnding(code)) {
76
+ effects.exit(types.data);
77
+ return atLineEnding(code);
78
+ }
79
+ effects.consume(code);
80
+ return comment2;
81
+ }
82
+ function atLineEnding(code) {
83
+ effects.enter(types.lineEnding);
84
+ effects.consume(code);
85
+ effects.exit(types.lineEnding);
86
+ return factorySpace(effects, afterPrefix, types.linePrefix);
87
+ }
88
+ function afterPrefix(code) {
89
+ if (markdownLineEnding(code)) {
90
+ return atLineEnding(code);
91
+ }
92
+ effects.enter(types.data);
93
+ return comment2(code);
94
+ }
95
+ function commentClose(code) {
96
+ if (code === codes.dash) {
97
+ effects.consume(code);
98
+ return end;
99
+ }
100
+ return comment2(code);
101
+ }
102
+ function end(code) {
103
+ if (code === codes.greaterThan) {
104
+ effects.exit(types.data);
105
+ effects.enter("commentEnd");
106
+ effects.consume(code);
107
+ effects.exit("commentEnd");
108
+ effects.exit("comment");
109
+ return ok(code);
110
+ }
111
+ if (code === codes.dash) {
112
+ effects.consume(code);
113
+ return end;
114
+ }
115
+ return comment2(code);
116
+ }
117
+ };
118
+ const comment = {
119
+ flow: { [60]: { tokenize, concrete: true } },
120
+ text: { [60]: { tokenize } }
121
+ };
122
+ export {
123
+ comment,
124
+ commentFromMarkdown
125
+ };
@@ -0,0 +1,128 @@
1
+ import { FocusEvent as FocusEvent_2 } from 'react';
2
+ import { KeyboardEvent as KeyboardEvent_2 } from 'react';
3
+ import { Options } from 'mdast-util-to-markdown';
4
+ import { ReactNode } from 'react';
5
+ import { Realm } from '@mdxeditor/gurx';
6
+
7
+ export declare type MarkdownDocument = {
8
+ content: string;
9
+ id: string;
10
+ version: string;
11
+ };
12
+
13
+ declare type MarkdownEditorColorScheme = 'dark' | 'inherit' | 'light' | 'system';
14
+
15
+ declare type MarkdownEditorDensity = 'compact' | 'document';
16
+
17
+ declare type MarkdownEditorHandle = {
18
+ focus: (options?: {
19
+ defaultSelection?: 'rootStart' | 'rootEnd';
20
+ preventScroll?: boolean;
21
+ }) => void;
22
+ getMarkdown: () => string;
23
+ getSelectionMarkdown: () => string;
24
+ insertMarkdown: (markdown: string) => void;
25
+ setMarkdown: (markdown: string) => void;
26
+ };
27
+
28
+ declare type MarkdownEditorProps = {
29
+ additionalPlugins?: RealmPlugin[];
30
+ ariaLabel?: string;
31
+ autoFocus?: boolean | {
32
+ defaultSelection?: 'rootStart' | 'rootEnd';
33
+ preventScroll?: boolean;
34
+ };
35
+ className?: string;
36
+ colorScheme?: MarkdownEditorColorScheme;
37
+ contentClassName?: string;
38
+ defaultValue?: string;
39
+ density?: MarkdownEditorDensity;
40
+ minHeight?: number | string;
41
+ onBlur?: (event: FocusEvent) => void;
42
+ onChange?: (markdown: string) => void;
43
+ onError?: (error: Error) => void;
44
+ onFocus?: (event: FocusEvent_2<HTMLDivElement>) => void;
45
+ onHeightChange?: (height: number) => void;
46
+ onKeyDown?: (event: KeyboardEvent_2<HTMLDivElement>) => void;
47
+ onNavigate?: (target: string) => void;
48
+ onOpenExternalLink?: (href: string) => void;
49
+ overlayContainer?: HTMLElement | null;
50
+ placeholder?: ReactNode;
51
+ readOnly?: boolean;
52
+ resolveLink?: (href: string) => string | null;
53
+ spellCheck?: boolean;
54
+ toMarkdownOptions?: ToMarkdownOptions;
55
+ value?: string;
56
+ variant?: MarkdownEditorVariant;
57
+ };
58
+
59
+ declare type MarkdownEditorVariant = 'card' | 'embedded' | 'plain';
60
+
61
+ export declare type MarkdownPersistenceAdapter<Document extends MarkdownDocument> = {
62
+ save: (input: {
63
+ content: string;
64
+ document: Document;
65
+ keepalive: boolean;
66
+ }) => Promise<MarkdownSaveResult<Document>>;
67
+ };
68
+
69
+ export declare type MarkdownSaveResult<Document extends MarkdownDocument> = {
70
+ document: Document;
71
+ status: 'conflict';
72
+ } | {
73
+ document: Document;
74
+ status: 'saved';
75
+ };
76
+
77
+ export declare type MarkdownSaveStatus = 'conflict' | 'error' | 'saved' | 'saving' | 'unsaved';
78
+
79
+ export declare const PersistentMarkdownEditor: <Document extends MarkdownDocument>(props: PersistentMarkdownEditorProps<Document> & {
80
+ ref?: React.ForwardedRef<PersistentMarkdownEditorHandle<Document>>;
81
+ }) => React.ReactElement;
82
+
83
+ export declare type PersistentMarkdownEditorHandle<Document extends MarkdownDocument> = MarkdownEditorHandle & {
84
+ applyExternalChange: (document: Document) => void;
85
+ flush: (options?: {
86
+ keepalive?: boolean;
87
+ }) => Promise<boolean>;
88
+ hasUnsavedChanges: () => boolean;
89
+ };
90
+
91
+ export declare type PersistentMarkdownEditorProps<Document extends MarkdownDocument> = Omit<MarkdownEditorProps, 'defaultValue' | 'onBlur' | 'onChange' | 'onError' | 'value'> & {
92
+ adapter: MarkdownPersistenceAdapter<Document>;
93
+ debounceMs?: number;
94
+ document: Document;
95
+ fromStorageContent?: (content: string) => string;
96
+ lifecycleFlush?: boolean;
97
+ maxWaitMs?: number;
98
+ onBlur?: MarkdownEditorProps['onBlur'];
99
+ onDocumentChange?: (document: Document) => void;
100
+ onError?: (error: Error) => void;
101
+ onLocalChange?: (content: string) => void;
102
+ onStatusChange?: (status: MarkdownSaveStatus) => void;
103
+ toStorageContent?: (content: string) => string;
104
+ };
105
+
106
+ /**
107
+ * A plugin for the editor.
108
+ * @group Core
109
+ */
110
+ declare interface RealmPlugin {
111
+ init?: (realm: Realm) => void;
112
+ update?: (realm: Realm) => void;
113
+ postInit?: (realm: Realm) => void;
114
+ }
115
+
116
+ declare interface ToMarkdownOptions extends Options {
117
+ }
118
+
119
+ export { }
120
+
121
+
122
+ declare module 'micromark-util-types' {
123
+ interface TokenTypeMap {
124
+ comment: 'comment';
125
+ commentEnd: 'commentEnd';
126
+ data: 'data';
127
+ }
128
+ }
@@ -0,0 +1,4 @@
1
+ import { PersistentMarkdownEditor } from "./PersistentMarkdownEditor.js";
2
+ export {
3
+ PersistentMarkdownEditor
4
+ };
@@ -0,0 +1,183 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCellValue } from "@mdxeditor/gurx";
3
+ import { DecoratorNode } from "lexical";
4
+ import React from "react";
5
+ import "./index.js";
6
+ import { voidEmitter } from "../../utils/voidEmitter.js";
7
+ import { codeBlockEditorDescriptors$, defaultCodeBlockLanguage$, NESTED_EDITOR_UPDATED_COMMAND } from "../core/index.js";
8
+ import { findCodeBlockDescriptor } from "./findCodeBlockDescriptor.js";
9
+ class CodeBlockNode extends DecoratorNode {
10
+ __code;
11
+ __meta;
12
+ __language;
13
+ __focusEmitter = voidEmitter();
14
+ static getType() {
15
+ return "codeblock";
16
+ }
17
+ static clone(node) {
18
+ return new CodeBlockNode(node.__code, node.__language, node.__meta, node.__key);
19
+ }
20
+ afterCloneFrom(prevNode) {
21
+ super.afterCloneFrom(prevNode);
22
+ this.__code = prevNode.__code;
23
+ this.__meta = prevNode.__meta;
24
+ this.__language = prevNode.__language;
25
+ this.__focusEmitter = voidEmitter();
26
+ }
27
+ static importJSON(serializedNode) {
28
+ const { code, meta, language } = serializedNode;
29
+ return $createCodeBlockNode({
30
+ code,
31
+ language,
32
+ meta
33
+ });
34
+ }
35
+ static importDOM() {
36
+ return {
37
+ pre: () => {
38
+ return {
39
+ conversion: $convertPreElement,
40
+ priority: 3
41
+ };
42
+ }
43
+ };
44
+ }
45
+ constructor(code, language, meta, key) {
46
+ super(key);
47
+ this.__code = code;
48
+ this.__meta = meta;
49
+ this.__language = language;
50
+ }
51
+ exportJSON() {
52
+ return {
53
+ code: this.getCode(),
54
+ language: this.getLanguage(),
55
+ meta: this.getMeta(),
56
+ type: "codeblock",
57
+ version: 1
58
+ };
59
+ }
60
+ // View
61
+ createDOM(_config, _editor) {
62
+ return document.createElement("div");
63
+ }
64
+ updateDOM() {
65
+ return false;
66
+ }
67
+ getCode() {
68
+ return this.__code;
69
+ }
70
+ getMeta() {
71
+ return this.__meta;
72
+ }
73
+ getLanguage() {
74
+ return this.__language;
75
+ }
76
+ setCode = (code) => {
77
+ if (code !== this.__code) {
78
+ this.getWritable().__code = code;
79
+ }
80
+ };
81
+ setMeta = (meta) => {
82
+ if (meta !== this.__meta) {
83
+ this.getWritable().__meta = meta;
84
+ }
85
+ };
86
+ setLanguage = (language) => {
87
+ if (language !== this.__language) {
88
+ this.getWritable().__language = language;
89
+ }
90
+ };
91
+ select = () => {
92
+ this.__focusEmitter.publish();
93
+ };
94
+ decorate(editor) {
95
+ return /* @__PURE__ */ jsx(
96
+ CodeBlockEditorContainer,
97
+ {
98
+ parentEditor: editor,
99
+ code: this.getCode(),
100
+ meta: this.getMeta(),
101
+ language: this.getLanguage(),
102
+ codeBlockNode: this,
103
+ nodeKey: this.getKey(),
104
+ focusEmitter: this.__focusEmitter
105
+ }
106
+ );
107
+ }
108
+ isInline() {
109
+ return false;
110
+ }
111
+ }
112
+ const CodeBlockEditorContext = React.createContext(null);
113
+ const CodeBlockEditorContextProvider = ({ parentEditor, lexicalNode, children }) => {
114
+ const contextValue = React.useMemo(() => {
115
+ return {
116
+ lexicalNode,
117
+ parentEditor,
118
+ setCode: (code) => {
119
+ parentEditor.update(() => {
120
+ lexicalNode.setCode(code);
121
+ setTimeout(() => {
122
+ parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
123
+ }, 0);
124
+ });
125
+ },
126
+ setLanguage: (language) => {
127
+ parentEditor.update(() => {
128
+ lexicalNode.setLanguage(language);
129
+ });
130
+ },
131
+ setMeta: (meta) => {
132
+ parentEditor.update(() => {
133
+ lexicalNode.setMeta(meta);
134
+ });
135
+ }
136
+ };
137
+ }, [lexicalNode, parentEditor]);
138
+ return /* @__PURE__ */ jsx(CodeBlockEditorContext.Provider, { value: contextValue, children });
139
+ };
140
+ function useCodeBlockEditorContext() {
141
+ const context = React.useContext(CodeBlockEditorContext);
142
+ if (!context) {
143
+ throw new Error("useCodeBlockEditor must be used within a CodeBlockEditor");
144
+ }
145
+ return context;
146
+ }
147
+ const CodeBlockEditorContainer = (props) => {
148
+ const codeBlockEditorDescriptors = useCellValue(codeBlockEditorDescriptors$);
149
+ const defaultCodeBlockLanguage = useCellValue(defaultCodeBlockLanguage$);
150
+ const descriptor = findCodeBlockDescriptor(codeBlockEditorDescriptors, props.language, props.meta, defaultCodeBlockLanguage);
151
+ if (!descriptor) {
152
+ throw new Error(`No CodeBlockEditor registered for language=${props.language} meta=${props.meta}`);
153
+ }
154
+ const Editor = descriptor.Editor;
155
+ const { codeBlockNode: _, parentEditor: __, ...restProps } = props;
156
+ return /* @__PURE__ */ jsx(CodeBlockEditorContextProvider, { parentEditor: props.parentEditor, lexicalNode: props.codeBlockNode, children: /* @__PURE__ */ jsx(Editor, { ...restProps }) });
157
+ };
158
+ function $createCodeBlockNode(options) {
159
+ const { code = "", language = "", meta = "" } = options;
160
+ return new CodeBlockNode(code, language, meta);
161
+ }
162
+ function $isCodeBlockNode(node) {
163
+ return node instanceof CodeBlockNode;
164
+ }
165
+ function $convertPreElement(element) {
166
+ const preElement = element;
167
+ const code = preElement.textContent;
168
+ const classAttribute = element.getAttribute("class") ?? "";
169
+ const dataLanguageAttribute = element.getAttribute("data-language") ?? "";
170
+ const languageMatch = /language-(\w+)/.exec(classAttribute);
171
+ const language = languageMatch ? languageMatch[1] : dataLanguageAttribute;
172
+ const meta = preElement.getAttribute("data-meta") ?? "";
173
+ return {
174
+ node: $createCodeBlockNode({ code, language, meta })
175
+ };
176
+ }
177
+ export {
178
+ $convertPreElement,
179
+ $createCodeBlockNode,
180
+ $isCodeBlockNode,
181
+ CodeBlockNode,
182
+ useCodeBlockEditorContext
183
+ };
@@ -0,0 +1,14 @@
1
+ import { $isCodeBlockNode } from "./CodeBlockNode.js";
2
+ const CodeBlockVisitor = {
3
+ testLexicalNode: $isCodeBlockNode,
4
+ visitLexicalNode: ({ lexicalNode, actions }) => {
5
+ actions.addAndStepInto("code", {
6
+ value: lexicalNode.getCode(),
7
+ lang: lexicalNode.getLanguage(),
8
+ meta: lexicalNode.getMeta()
9
+ });
10
+ }
11
+ };
12
+ export {
13
+ CodeBlockVisitor
14
+ };
@@ -0,0 +1,23 @@
1
+ import { $createCodeBlockNode } from "./CodeBlockNode.js";
2
+ import { findCodeBlockDescriptor } from "./findCodeBlockDescriptor.js";
3
+ const MdastCodeVisitor = {
4
+ testNode: (node, { codeBlockEditorDescriptors, defaultCodeBlockLanguage }) => {
5
+ if (node.type === "code") {
6
+ const descriptor = findCodeBlockDescriptor(codeBlockEditorDescriptors, node.lang, node.meta, defaultCodeBlockLanguage);
7
+ return descriptor !== void 0;
8
+ }
9
+ return false;
10
+ },
11
+ visitNode({ mdastNode, actions }) {
12
+ actions.addAndStepInto(
13
+ $createCodeBlockNode({
14
+ code: mdastNode.value,
15
+ language: mdastNode.lang ?? "",
16
+ meta: mdastNode.meta ?? ""
17
+ })
18
+ );
19
+ }
20
+ };
21
+ export {
22
+ MdastCodeVisitor
23
+ };
@@ -0,0 +1,8 @@
1
+ function findCodeBlockDescriptor(descriptors, language, meta, defaultLanguage) {
2
+ const sortedDescriptors = [...descriptors].sort((a, b) => b.priority - a.priority);
3
+ const resolvedMeta = meta ?? "";
4
+ return sortedDescriptors.find((descriptor) => descriptor.match(language ?? "", resolvedMeta)) ?? sortedDescriptors.find((descriptor) => descriptor.match(defaultLanguage ?? "", resolvedMeta));
5
+ }
6
+ export {
7
+ findCodeBlockDescriptor
8
+ };
@@ -0,0 +1,46 @@
1
+ import { CodeBlockVisitor } from "./CodeBlockVisitor.js";
2
+ import { MdastCodeVisitor } from "./MdastCodeVisitor.js";
3
+ import { Appender, codeBlockEditorDescriptors$, defaultCodeBlockLanguage$, insertDecoratorNode$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addActivePlugin$ } from "../core/index.js";
4
+ import { $createCodeBlockNode, CodeBlockNode } from "./CodeBlockNode.js";
5
+ import { $convertPreElement, $isCodeBlockNode, useCodeBlockEditorContext } from "./CodeBlockNode.js";
6
+ import { Signal, withLatestFrom, map } from "@mdxeditor/gurx";
7
+ import { realmPlugin } from "../../RealmWithPlugins.js";
8
+ const insertCodeBlock$ = Signal((r) => {
9
+ r.link(
10
+ r.pipe(
11
+ insertCodeBlock$,
12
+ withLatestFrom(defaultCodeBlockLanguage$),
13
+ map(
14
+ ([payload, defaultCodeBlockLanguage]) => () => $createCodeBlockNode({ language: defaultCodeBlockLanguage, ...payload })
15
+ )
16
+ ),
17
+ insertDecoratorNode$
18
+ );
19
+ });
20
+ const appendCodeBlockEditorDescriptor$ = Appender(codeBlockEditorDescriptors$);
21
+ const codeBlockPlugin = realmPlugin({
22
+ update(realm, params) {
23
+ realm.pub(defaultCodeBlockLanguage$, params?.defaultCodeBlockLanguage ?? "");
24
+ },
25
+ init(realm, params) {
26
+ realm.pubIn({
27
+ [addActivePlugin$]: "codeblock",
28
+ [defaultCodeBlockLanguage$]: params?.defaultCodeBlockLanguage ?? "",
29
+ [codeBlockEditorDescriptors$]: params?.codeBlockEditorDescriptors ?? [],
30
+ [addImportVisitor$]: MdastCodeVisitor,
31
+ [addLexicalNode$]: CodeBlockNode,
32
+ [addExportVisitor$]: CodeBlockVisitor
33
+ });
34
+ }
35
+ });
36
+ export {
37
+ $convertPreElement,
38
+ $createCodeBlockNode,
39
+ $isCodeBlockNode,
40
+ CodeBlockNode,
41
+ appendCodeBlockEditorDescriptor$,
42
+ codeBlockPlugin,
43
+ defaultCodeBlockLanguage$,
44
+ insertCodeBlock$,
45
+ useCodeBlockEditorContext
46
+ };
@@ -0,0 +1,145 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useCellValues } from "@mdxeditor/gurx";
3
+ import React from "react";
4
+ import { EditorIcon } from "../../EditorIcon.js";
5
+ import styles from "../../styles/ui.module.css.js";
6
+ import { useCodeBlockEditorContext } from "../codeblock/CodeBlockNode.js";
7
+ import { useTranslation, readOnly$ } from "../core/index.js";
8
+ import { languages } from "@codemirror/language-data";
9
+ import { EditorState } from "@codemirror/state";
10
+ import { EditorView, lineNumbers, keymap } from "@codemirror/view";
11
+ import { indentWithTab } from "@codemirror/commands";
12
+ import { basicLight } from "cm6-theme-basic-light";
13
+ import { basicSetup } from "codemirror";
14
+ import { $setSelection } from "lexical";
15
+ import { codeMirrorExtensions$, codeMirrorAutoLoadLanguageSupport$, codeBlockLanguages$, getCodeBlockLanguageSelectData, EMPTY_VALUE } from "./index.js";
16
+ import { useCodeMirrorRef } from "./useCodeMirrorRef.js";
17
+ import { Select } from "../toolbar/primitives/select.js";
18
+ const COMMON_STATE_CONFIG_EXTENSIONS = [];
19
+ const CodeMirrorEditor = ({ language, nodeKey, code, focusEmitter }) => {
20
+ const t = useTranslation();
21
+ const { parentEditor, lexicalNode } = useCodeBlockEditorContext();
22
+ const [readOnly, codeMirrorExtensions, autoLoadLanguageSupport, codeBlockLanguages] = useCellValues(
23
+ readOnly$,
24
+ codeMirrorExtensions$,
25
+ codeMirrorAutoLoadLanguageSupport$,
26
+ codeBlockLanguages$
27
+ );
28
+ const codeMirrorRef = useCodeMirrorRef(nodeKey, language, focusEmitter);
29
+ const { setCode } = useCodeBlockEditorContext();
30
+ const editorViewRef = React.useRef(null);
31
+ const elRef = React.useRef(null);
32
+ const { value: selectValue, items: selectItems } = React.useMemo(
33
+ () => getCodeBlockLanguageSelectData(codeBlockLanguages, language),
34
+ [codeBlockLanguages, language]
35
+ );
36
+ const setCodeRef = React.useRef(setCode);
37
+ setCodeRef.current = setCode;
38
+ codeMirrorRef.current = {
39
+ getCodemirror: () => editorViewRef.current
40
+ };
41
+ React.useEffect(() => {
42
+ const el = elRef.current;
43
+ void (async () => {
44
+ const extensions = [
45
+ ...codeMirrorExtensions,
46
+ basicSetup,
47
+ basicLight,
48
+ lineNumbers(),
49
+ keymap.of([indentWithTab]),
50
+ EditorView.lineWrapping,
51
+ EditorView.updateListener.of(({ state }) => {
52
+ setCodeRef.current(state.doc.toString());
53
+ }),
54
+ EditorView.domEventHandlers({
55
+ focus: () => {
56
+ parentEditor.update(() => {
57
+ $setSelection(null);
58
+ });
59
+ }
60
+ })
61
+ ];
62
+ if (readOnly) {
63
+ extensions.push(EditorState.readOnly.of(true));
64
+ }
65
+ if (language !== "") {
66
+ const canonical = codeBlockLanguages.keyMap[language] ?? language;
67
+ const providedSupport = codeBlockLanguages.supportMap[canonical];
68
+ if (providedSupport) {
69
+ extensions.push(providedSupport.extension);
70
+ } else if (autoLoadLanguageSupport) {
71
+ const languageData = languages.find((l) => {
72
+ return l.name === language || l.alias.includes(language) || l.extensions.includes(language);
73
+ });
74
+ if (languageData) {
75
+ try {
76
+ const languageSupport = await languageData.load();
77
+ extensions.push(languageSupport.extension);
78
+ } catch (_e) {
79
+ console.warn("failed to load language support for", language);
80
+ }
81
+ }
82
+ }
83
+ }
84
+ el.innerHTML = "";
85
+ editorViewRef.current = new EditorView({
86
+ parent: el,
87
+ state: EditorState.create({ doc: code, extensions })
88
+ });
89
+ el.addEventListener("keydown", stopPropagationHandler);
90
+ })();
91
+ return () => {
92
+ editorViewRef.current?.destroy();
93
+ editorViewRef.current = null;
94
+ el.removeEventListener("keydown", stopPropagationHandler);
95
+ };
96
+ }, [readOnly, language, ...codeMirrorExtensions]);
97
+ return /* @__PURE__ */ jsxs("div", { className: styles.codeMirrorWrapper, children: [
98
+ /* @__PURE__ */ jsxs("div", { className: styles.codeMirrorToolbar, children: [
99
+ /* @__PURE__ */ jsx(
100
+ Select,
101
+ {
102
+ disabled: readOnly,
103
+ value: selectValue,
104
+ onChange: (language2) => {
105
+ parentEditor.update(() => {
106
+ lexicalNode.setLanguage(language2 === EMPTY_VALUE ? "" : language2);
107
+ setTimeout(() => {
108
+ parentEditor.update(() => {
109
+ lexicalNode.getLatest().select();
110
+ });
111
+ });
112
+ });
113
+ },
114
+ triggerTitle: t("codeBlock.selectLanguage", "Select code block language"),
115
+ placeholder: t("codeBlock.inlineLanguage", "Language"),
116
+ items: selectItems
117
+ }
118
+ ),
119
+ /* @__PURE__ */ jsx(
120
+ "button",
121
+ {
122
+ className: styles.iconButton,
123
+ type: "button",
124
+ disabled: readOnly,
125
+ title: t("codeblock.delete", "Delete code block"),
126
+ onClick: (e) => {
127
+ e.preventDefault();
128
+ parentEditor.update(() => {
129
+ lexicalNode.remove();
130
+ });
131
+ },
132
+ children: /* @__PURE__ */ jsx(EditorIcon, { name: "delete" })
133
+ }
134
+ )
135
+ ] }),
136
+ /* @__PURE__ */ jsx("div", { ref: elRef })
137
+ ] });
138
+ };
139
+ function stopPropagationHandler(ev) {
140
+ ev.stopPropagation();
141
+ }
142
+ export {
143
+ COMMON_STATE_CONFIG_EXTENSIONS,
144
+ CodeMirrorEditor
145
+ };