@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,89 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCombobox } from "downshift";
3
+ import React from "react";
4
+ import { Controller } from "react-hook-form";
5
+ import { EditorIcon } from "../../../EditorIcon.js";
6
+ import styles from "../../../styles/ui.module.css.js";
7
+ const MAX_SUGGESTIONS = 20;
8
+ const DownshiftAutoComplete = (props) => {
9
+ if (props.suggestions.length > 0) {
10
+ return /* @__PURE__ */ jsx(DownshiftAutoCompleteWithSuggestions, { ...props });
11
+ } else {
12
+ return /* @__PURE__ */ jsx("input", { className: styles.textInput, size: 40, autoFocus: true, ...props.register(props.inputName) });
13
+ }
14
+ };
15
+ const DownshiftAutoCompleteWithSuggestions = ({
16
+ autofocus,
17
+ suggestions,
18
+ control,
19
+ inputName,
20
+ placeholder,
21
+ initialInputValue,
22
+ setValue
23
+ }) => {
24
+ const [items, setItems] = React.useState(suggestions.slice(0, MAX_SUGGESTIONS));
25
+ const enableAutoComplete = suggestions.length > 0;
26
+ const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, highlightedIndex, getItemProps, selectedItem } = useCombobox({
27
+ initialInputValue,
28
+ onInputValueChange({ inputValue = "" }) {
29
+ setValue(inputName, inputValue);
30
+ inputValue = inputValue.toLowerCase() || "";
31
+ const matchingItems = [];
32
+ for (const suggestion of suggestions) {
33
+ if (suggestion.toLowerCase().includes(inputValue)) {
34
+ matchingItems.push(suggestion);
35
+ if (matchingItems.length >= MAX_SUGGESTIONS) {
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ setItems(matchingItems);
41
+ },
42
+ items,
43
+ itemToString(item) {
44
+ return item ?? "";
45
+ }
46
+ });
47
+ const dropdownIsVisible = isOpen && items.length > 0;
48
+ return /* @__PURE__ */ jsxs("div", { className: styles.downshiftAutocompleteContainer, children: [
49
+ /* @__PURE__ */ jsxs("div", { "data-visible-dropdown": dropdownIsVisible, className: styles.downshiftInputWrapper, children: [
50
+ /* @__PURE__ */ jsx(
51
+ Controller,
52
+ {
53
+ name: inputName,
54
+ control,
55
+ render: ({ field }) => {
56
+ const downshiftSrcProps = getInputProps();
57
+ return /* @__PURE__ */ jsx(
58
+ "input",
59
+ {
60
+ ...downshiftSrcProps,
61
+ name: field.name,
62
+ placeholder,
63
+ className: styles.downshiftInput,
64
+ size: 30,
65
+ "data-editor-dialog": true,
66
+ autoFocus: autofocus
67
+ }
68
+ );
69
+ }
70
+ }
71
+ ),
72
+ enableAutoComplete && /* @__PURE__ */ jsx("button", { "aria-label": "toggle menu", type: "button", ...getToggleButtonProps(), children: /* @__PURE__ */ jsx(EditorIcon, { name: "chevron-down" }) })
73
+ ] }),
74
+ /* @__PURE__ */ jsx("div", { className: styles.downshiftAutocompleteContainer, children: /* @__PURE__ */ jsx("ul", { ...getMenuProps(), "data-visible": dropdownIsVisible, children: items.map((item, index) => /* @__PURE__ */ jsx(
75
+ "li",
76
+ {
77
+ "data-selected": selectedItem === item,
78
+ "data-highlighted": highlightedIndex === index,
79
+ ...getItemProps({ item, index }),
80
+ children: item
81
+ },
82
+ `${item}${index}`
83
+ )) }) })
84
+ ] });
85
+ };
86
+ export {
87
+ DownshiftAutoComplete,
88
+ DownshiftAutoCompleteWithSuggestions
89
+ };
@@ -0,0 +1,22 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import * as RadixPopover from "@radix-ui/react-popover";
4
+ import { editorRootElementRef$ } from "../index.js";
5
+ import styles from "../../../styles/ui.module.css.js";
6
+ import { useCellValue } from "@mdxeditor/gurx";
7
+ const PopoverPortal = (props) => {
8
+ const editorRootElementRef = useCellValue(editorRootElementRef$);
9
+ return /* @__PURE__ */ jsx(RadixPopover.Portal, { ...props, container: editorRootElementRef?.current });
10
+ };
11
+ const PopoverContent = React.forwardRef(
12
+ (props, ref) => {
13
+ return /* @__PURE__ */ jsxs(RadixPopover.Content, { ...props, className: styles.popoverContent, sideOffset: 5, side: "top", ref, children: [
14
+ /* @__PURE__ */ jsx("span", { className: styles.popoverArrow, children: /* @__PURE__ */ jsx(RadixPopover.Arrow, {}) }),
15
+ props.children
16
+ ] });
17
+ }
18
+ );
19
+ export {
20
+ PopoverContent,
21
+ PopoverPortal
22
+ };
@@ -0,0 +1,24 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { DiffViewer } from "./DiffViewer.js";
3
+ import { SourceEditor } from "./SourceEditor.js";
4
+ import { markdownProcessingError$, viewMode$ } from "../core/index.js";
5
+ import styles from "../../styles/ui.module.css.js";
6
+ import { useCellValues } from "@mdxeditor/gurx";
7
+ const DiffSourceWrapper = ({ children }) => {
8
+ const [error, viewMode] = useCellValues(markdownProcessingError$, viewMode$);
9
+ return /* @__PURE__ */ jsxs("div", { className: "mdxeditor-diff-source-wrapper", children: [
10
+ error ? /* @__PURE__ */ jsxs("div", { className: styles.markdownParseError, children: [
11
+ /* @__PURE__ */ jsxs("p", { children: [
12
+ error.error,
13
+ "."
14
+ ] }),
15
+ /* @__PURE__ */ jsx("p", { children: "You can fix the errors in source mode and switch to rich text mode when you are ready." })
16
+ ] }) : null,
17
+ /* @__PURE__ */ jsx("div", { className: "mdxeditor-rich-text-editor", style: { display: viewMode === "rich-text" && error == null ? "block" : "none" }, children }),
18
+ viewMode === "diff" ? /* @__PURE__ */ jsx(DiffViewer, {}) : null,
19
+ viewMode === "source" ? /* @__PURE__ */ jsx(SourceEditor, {}) : null
20
+ ] });
21
+ };
22
+ export {
23
+ DiffSourceWrapper
24
+ };
@@ -0,0 +1,84 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { diffMarkdown$, readOnlyDiff$, cmExtensions$ } from "./index.js";
4
+ import { markdown$, readOnly$, markdownSourceEditorValue$, onBlur$ } from "../core/index.js";
5
+ import { MergeView } from "@codemirror/merge";
6
+ import { EditorState } from "@codemirror/state";
7
+ import { EditorView } from "@codemirror/view";
8
+ import { COMMON_STATE_CONFIG_EXTENSIONS } from "./SourceEditor.js";
9
+ import { useRealm, useCellValues, usePublisher, useCellValue } from "@mdxeditor/gurx";
10
+ function setContent(view, content) {
11
+ if (view !== void 0) {
12
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: content } });
13
+ }
14
+ }
15
+ const DiffViewer = () => {
16
+ const realm = useRealm();
17
+ const [newMarkdown, oldMarkdown, readOnly, readOnlyDiff] = useCellValues(markdown$, diffMarkdown$, readOnly$, readOnlyDiff$);
18
+ const onUpdate = usePublisher(markdownSourceEditorValue$);
19
+ const elRef = React.useRef(null);
20
+ const cmMergeViewRef = React.useRef(null);
21
+ const cmExtensions = useCellValue(cmExtensions$);
22
+ const triggerOnBlur = usePublisher(onBlur$);
23
+ React.useEffect(() => {
24
+ return realm.sub(diffMarkdown$, (newDiffMarkdown) => {
25
+ setContent(cmMergeViewRef.current?.a, newDiffMarkdown);
26
+ });
27
+ }, [realm]);
28
+ React.useEffect(() => {
29
+ return realm.sub(markdown$, (newMarkdown2) => {
30
+ setContent(cmMergeViewRef.current?.b, newMarkdown2);
31
+ });
32
+ }, [realm]);
33
+ React.useEffect(() => {
34
+ const isReadOnly = readOnly || readOnlyDiff;
35
+ const revertParams = isReadOnly ? {
36
+ renderRevertControl: void 0,
37
+ revertControls: void 0
38
+ } : {
39
+ renderRevertControl: () => {
40
+ const el = document.createElement("button");
41
+ el.classList.add("cm-merge-revert");
42
+ el.appendChild(document.createTextNode("⮕"));
43
+ return el;
44
+ },
45
+ revertControls: "a-to-b"
46
+ };
47
+ cmMergeViewRef.current = new MergeView({
48
+ ...revertParams,
49
+ parent: elRef.current,
50
+ orientation: "a-b",
51
+ gutter: true,
52
+ a: {
53
+ doc: oldMarkdown,
54
+ extensions: [...cmExtensions, ...COMMON_STATE_CONFIG_EXTENSIONS, EditorState.readOnly.of(true)]
55
+ },
56
+ b: {
57
+ doc: newMarkdown,
58
+ extensions: [
59
+ ...cmExtensions,
60
+ ...COMMON_STATE_CONFIG_EXTENSIONS,
61
+ EditorState.readOnly.of(isReadOnly),
62
+ EditorView.updateListener.of(({ state }) => {
63
+ const md = state.doc.toString();
64
+ onUpdate(md);
65
+ }),
66
+ EditorView.focusChangeEffect.of((_, focused) => {
67
+ if (!focused) {
68
+ triggerOnBlur(new FocusEvent("blur"));
69
+ }
70
+ return null;
71
+ })
72
+ ]
73
+ }
74
+ });
75
+ return () => {
76
+ cmMergeViewRef.current?.destroy();
77
+ cmMergeViewRef.current = null;
78
+ };
79
+ }, [onUpdate, cmExtensions]);
80
+ return /* @__PURE__ */ jsx("div", { ref: elRef, className: "mdxeditor-diff-editor" });
81
+ };
82
+ export {
83
+ DiffViewer
84
+ };
@@ -0,0 +1,60 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { markdown } from "@codemirror/lang-markdown";
3
+ import { EditorState } from "@codemirror/state";
4
+ import { EditorView, lineNumbers } from "@codemirror/view";
5
+ import { basicLight } from "cm6-theme-basic-light";
6
+ import { basicSetup } from "codemirror";
7
+ import React from "react";
8
+ import { cmExtensions$ } from "./index.js";
9
+ import { markdown$, readOnly$, markdownSourceEditorValue$, onBlur$ } from "../core/index.js";
10
+ import { useCellValues, usePublisher } from "@mdxeditor/gurx";
11
+ const COMMON_STATE_CONFIG_EXTENSIONS = [
12
+ basicSetup,
13
+ basicLight,
14
+ markdown(),
15
+ lineNumbers(),
16
+ EditorView.lineWrapping
17
+ ];
18
+ const SourceEditor = () => {
19
+ const [markdown2, readOnly, cmExtensions] = useCellValues(markdown$, readOnly$, cmExtensions$);
20
+ const updateMarkdown = usePublisher(markdownSourceEditorValue$);
21
+ const triggerOnBlur = usePublisher(onBlur$);
22
+ const editorViewRef = React.useRef(null);
23
+ const ref = React.useCallback(
24
+ (el) => {
25
+ if (el !== null) {
26
+ const extensions = [
27
+ // custom extensions should come first so that you can override the default extensions
28
+ ...cmExtensions,
29
+ ...COMMON_STATE_CONFIG_EXTENSIONS,
30
+ EditorView.updateListener.of(({ state }) => {
31
+ updateMarkdown(state.doc.toString());
32
+ }),
33
+ EditorView.focusChangeEffect.of((_, focused) => {
34
+ if (!focused) {
35
+ triggerOnBlur(new FocusEvent("blur"));
36
+ }
37
+ return null;
38
+ })
39
+ ];
40
+ if (readOnly) {
41
+ extensions.push(EditorState.readOnly.of(true));
42
+ }
43
+ el.innerHTML = "";
44
+ editorViewRef.current = new EditorView({
45
+ parent: el,
46
+ state: EditorState.create({ doc: markdown2, extensions })
47
+ });
48
+ } else {
49
+ editorViewRef.current?.destroy();
50
+ editorViewRef.current = null;
51
+ }
52
+ },
53
+ [markdown2, readOnly, updateMarkdown, cmExtensions, triggerOnBlur]
54
+ );
55
+ return /* @__PURE__ */ jsx("div", { ref, className: "cm-sourceView mdxeditor-source-editor" });
56
+ };
57
+ export {
58
+ COMMON_STATE_CONFIG_EXTENSIONS,
59
+ SourceEditor
60
+ };
@@ -0,0 +1,27 @@
1
+ import { viewMode$, addEditorWrapper$ } from "../core/index.js";
2
+ import { DiffSourceWrapper } from "./DiffSourceWrapper.js";
3
+ import { Cell } from "@mdxeditor/gurx";
4
+ import { realmPlugin } from "../../RealmWithPlugins.js";
5
+ const diffMarkdown$ = Cell("");
6
+ const cmExtensions$ = Cell([]);
7
+ const readOnlyDiff$ = Cell(false);
8
+ const diffSourcePlugin = realmPlugin({
9
+ update: (r, params) => {
10
+ r.pub(diffMarkdown$, params?.diffMarkdown ?? "");
11
+ },
12
+ init(r, params) {
13
+ r.pubIn({
14
+ [diffMarkdown$]: params?.diffMarkdown ?? "",
15
+ [cmExtensions$]: params?.codeMirrorExtensions ?? [],
16
+ [addEditorWrapper$]: DiffSourceWrapper,
17
+ [readOnlyDiff$]: params?.readOnlyDiff ?? false,
18
+ [viewMode$]: params?.viewMode ?? "rich-text"
19
+ });
20
+ }
21
+ });
22
+ export {
23
+ cmExtensions$,
24
+ diffMarkdown$,
25
+ diffSourcePlugin,
26
+ readOnlyDiff$
27
+ };
@@ -0,0 +1,107 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { DecoratorNode } from "lexical";
3
+ import { NestedEditorsContext } from "../core/NestedLexicalEditor.js";
4
+ import { voidEmitter } from "../../utils/voidEmitter.js";
5
+ import { useCellValues } from "@mdxeditor/gurx";
6
+ import { directiveDescriptors$ } from "../core/index.js";
7
+ class DirectiveNode extends DecoratorNode {
8
+ /** @internal */
9
+ __mdastNode;
10
+ /** @internal */
11
+ __focusEmitter = voidEmitter();
12
+ /** @internal */
13
+ static getType() {
14
+ return "directive";
15
+ }
16
+ /** @internal */
17
+ static clone(node) {
18
+ return new DirectiveNode(structuredClone(node.__mdastNode), node.__key);
19
+ }
20
+ /** @internal */
21
+ static importJSON(serializedNode) {
22
+ return $createDirectiveNode(serializedNode.mdastNode);
23
+ }
24
+ /**
25
+ * Constructs a new {@link DirectiveNode} with the specified MDAST directive node as the object to edit.
26
+ */
27
+ constructor(mdastNode, key) {
28
+ super(key);
29
+ this.__mdastNode = mdastNode;
30
+ }
31
+ /**
32
+ * Returns the MDAST node that is being edited.
33
+ */
34
+ getMdastNode() {
35
+ return this.__mdastNode;
36
+ }
37
+ /** @internal */
38
+ exportJSON() {
39
+ return {
40
+ mdastNode: structuredClone(this.__mdastNode),
41
+ type: "directive",
42
+ version: 1
43
+ };
44
+ }
45
+ /** @internal */
46
+ createDOM() {
47
+ return document.createElement(this.__mdastNode.type === "textDirective" ? "span" : "div");
48
+ }
49
+ /** @internal */
50
+ updateDOM() {
51
+ return false;
52
+ }
53
+ /**
54
+ * Sets a new MDAST node to edit.
55
+ */
56
+ setMdastNode(mdastNode) {
57
+ this.getWritable().__mdastNode = mdastNode;
58
+ }
59
+ /**
60
+ * Focuses the direcitive editor.
61
+ */
62
+ select = () => {
63
+ this.__focusEmitter.publish();
64
+ };
65
+ /** @internal */
66
+ decorate(parentEditor, config) {
67
+ return /* @__PURE__ */ jsx(
68
+ DirectiveEditorContainer,
69
+ {
70
+ lexicalNode: this,
71
+ mdastNode: this.getMdastNode(),
72
+ parentEditor,
73
+ config,
74
+ focusEmitter: this.__focusEmitter
75
+ }
76
+ );
77
+ }
78
+ /** @internal */
79
+ isInline() {
80
+ return this.__mdastNode.type === "textDirective";
81
+ }
82
+ /** @internal */
83
+ isKeyboardSelectable() {
84
+ return true;
85
+ }
86
+ }
87
+ const DirectiveEditorContainer = (props) => {
88
+ const { mdastNode } = props;
89
+ const [directiveDescriptors] = useCellValues(directiveDescriptors$);
90
+ const descriptor = directiveDescriptors.find((descriptor2) => descriptor2.testNode(mdastNode));
91
+ if (!descriptor) {
92
+ throw new Error(`No descriptor found for directive ${mdastNode.name}`);
93
+ }
94
+ const Editor = descriptor.Editor;
95
+ return /* @__PURE__ */ jsx(NestedEditorsContext.Provider, { value: props, children: /* @__PURE__ */ jsx(Editor, { descriptor, mdastNode, lexicalNode: props.lexicalNode, parentEditor: props.parentEditor }) });
96
+ };
97
+ function $createDirectiveNode(mdastNode, key) {
98
+ return new DirectiveNode(mdastNode, key);
99
+ }
100
+ function $isDirectiveNode(node) {
101
+ return node instanceof DirectiveNode;
102
+ }
103
+ export {
104
+ $createDirectiveNode,
105
+ $isDirectiveNode,
106
+ DirectiveNode
107
+ };
@@ -0,0 +1,10 @@
1
+ import { $isDirectiveNode } from "./DirectiveNode.js";
2
+ const DirectiveVisitor = {
3
+ testLexicalNode: $isDirectiveNode,
4
+ visitLexicalNode({ actions, mdastParent, lexicalNode }) {
5
+ actions.appendToParent(mdastParent, lexicalNode.getMdastNode());
6
+ }
7
+ };
8
+ export {
9
+ DirectiveVisitor
10
+ };
@@ -0,0 +1,30 @@
1
+ import { $createTextNode } from "lexical";
2
+ import { $createDirectiveNode } from "./DirectiveNode.js";
3
+ const DIRECTIVE_TYPES = ["leafDirective", "containerDirective", "textDirective"];
4
+ function isMdastDirectivesNode(node) {
5
+ return DIRECTIVE_TYPES.includes(node.type);
6
+ }
7
+ const MdastDirectiveVisitor = (escapeUnknownTextDirectives) => ({
8
+ testNode: (node, { directiveDescriptors }) => {
9
+ if (isMdastDirectivesNode(node)) {
10
+ const descriptor = directiveDescriptors.find((descriptor2) => descriptor2.testNode(node));
11
+ if (escapeUnknownTextDirectives && !descriptor && node.type === "textDirective") {
12
+ return true;
13
+ }
14
+ return descriptor !== void 0;
15
+ }
16
+ return false;
17
+ },
18
+ visitNode({ lexicalParent, mdastNode, descriptors }) {
19
+ const isKnown = !escapeUnknownTextDirectives || descriptors.directiveDescriptors.some((d) => d.testNode(mdastNode));
20
+ if (isKnown) {
21
+ lexicalParent.append($createDirectiveNode(mdastNode));
22
+ } else {
23
+ lexicalParent.append($createTextNode(`:${mdastNode.name}`));
24
+ }
25
+ }
26
+ });
27
+ export {
28
+ MdastDirectiveVisitor,
29
+ isMdastDirectivesNode
30
+ };
@@ -0,0 +1,45 @@
1
+ import { realmPlugin } from "../../RealmWithPlugins.js";
2
+ import { insertDecoratorNode$, addToMarkdownExtension$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addSyntaxExtension$, addMdastExtension$, directiveDescriptors$ } from "../core/index.js";
3
+ import { Signal, map } from "@mdxeditor/gurx";
4
+ import { directiveToMarkdown, directiveFromMarkdown } from "mdast-util-directive";
5
+ import { directive } from "micromark-extension-directive";
6
+ import { $createDirectiveNode, DirectiveNode } from "./DirectiveNode.js";
7
+ import { $isDirectiveNode } from "./DirectiveNode.js";
8
+ import { DirectiveVisitor } from "./DirectiveVisitor.js";
9
+ import { MdastDirectiveVisitor } from "./MdastDirectiveVisitor.js";
10
+ const insertDirective$ = Signal((r) => {
11
+ r.link(
12
+ r.pipe(
13
+ insertDirective$,
14
+ map((payload) => {
15
+ return () => $createDirectiveNode({ children: [], ...payload });
16
+ })
17
+ ),
18
+ insertDecoratorNode$
19
+ );
20
+ });
21
+ const directivesPlugin = realmPlugin({
22
+ update: (realm, params) => {
23
+ realm.pub(directiveDescriptors$, params?.directiveDescriptors ?? []);
24
+ },
25
+ init: (realm, params) => {
26
+ realm.pubIn({
27
+ [directiveDescriptors$]: params?.directiveDescriptors ?? [],
28
+ // import
29
+ [addMdastExtension$]: directiveFromMarkdown(),
30
+ [addSyntaxExtension$]: directive(),
31
+ [addImportVisitor$]: MdastDirectiveVisitor(params?.escapeUnknownTextDirectives),
32
+ // export
33
+ [addLexicalNode$]: DirectiveNode,
34
+ [addExportVisitor$]: DirectiveVisitor,
35
+ [addToMarkdownExtension$]: directiveToMarkdown()
36
+ });
37
+ }
38
+ });
39
+ export {
40
+ $createDirectiveNode,
41
+ $isDirectiveNode,
42
+ DirectiveNode,
43
+ directivesPlugin,
44
+ insertDirective$
45
+ };
@@ -0,0 +1,137 @@
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
+ import * as Dialog from "@radix-ui/react-dialog";
3
+ import classNames from "classnames";
4
+ import YamlParser from "js-yaml";
5
+ import React from "react";
6
+ import { useForm, useFieldArray } from "react-hook-form";
7
+ import { frontmatterDialogOpen$, removeFrontmatter$ } from "./index.js";
8
+ import styles from "../../styles/ui.module.css.js";
9
+ import { readOnly$, editorRootElementRef$, iconComponentFor$, useTranslation } from "../core/index.js";
10
+ import { useCellValues, usePublisher } from "@mdxeditor/gurx";
11
+ const FrontmatterEditor = ({ yaml, onChange }) => {
12
+ const [readOnly, editorRootElementRef, iconComponentFor, frontmatterDialogOpen] = useCellValues(
13
+ readOnly$,
14
+ editorRootElementRef$,
15
+ iconComponentFor$,
16
+ frontmatterDialogOpen$
17
+ );
18
+ const t = useTranslation();
19
+ const setFrontmatterDialogOpen = usePublisher(frontmatterDialogOpen$);
20
+ const removeFrontmatter = usePublisher(removeFrontmatter$);
21
+ const yamlConfig = React.useMemo(() => {
22
+ if (!yaml) {
23
+ return [];
24
+ }
25
+ return Object.entries(YamlParser.load(yaml)).map(([key, value]) => ({ key, value }));
26
+ }, [yaml]);
27
+ const { register, control, handleSubmit } = useForm({
28
+ defaultValues: {
29
+ yamlConfig
30
+ }
31
+ });
32
+ const { fields, append, remove } = useFieldArray({
33
+ control,
34
+ name: "yamlConfig"
35
+ });
36
+ const onSubmit = React.useCallback(
37
+ ({ yamlConfig: yamlConfig2 }) => {
38
+ if (yamlConfig2.length === 0) {
39
+ removeFrontmatter();
40
+ setFrontmatterDialogOpen(false);
41
+ return;
42
+ }
43
+ const yaml2 = yamlConfig2.reduce((acc, { key, value }) => {
44
+ if (key && value) {
45
+ acc[key] = value;
46
+ }
47
+ return acc;
48
+ }, {});
49
+ onChange(YamlParser.dump(yaml2).trim());
50
+ setFrontmatterDialogOpen(false);
51
+ },
52
+ [onChange, setFrontmatterDialogOpen, removeFrontmatter]
53
+ );
54
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
55
+ Dialog.Root,
56
+ {
57
+ open: frontmatterDialogOpen,
58
+ onOpenChange: (open) => {
59
+ setFrontmatterDialogOpen(open);
60
+ },
61
+ children: /* @__PURE__ */ jsxs(Dialog.Portal, { container: editorRootElementRef?.current, children: [
62
+ /* @__PURE__ */ jsx(Dialog.Overlay, { className: styles.dialogOverlay }),
63
+ /* @__PURE__ */ jsxs(Dialog.Content, { className: styles.largeDialogContent, "data-editor-type": "frontmatter", children: [
64
+ /* @__PURE__ */ jsx(Dialog.Title, { className: styles.dialogTitle, children: t("frontmatterEditor.title", "Edit document frontmatter") }),
65
+ /* @__PURE__ */ jsxs(
66
+ "form",
67
+ {
68
+ onSubmit: (e) => {
69
+ void handleSubmit(onSubmit)(e);
70
+ e.stopPropagation();
71
+ },
72
+ onReset: (e) => {
73
+ e.stopPropagation();
74
+ setFrontmatterDialogOpen(false);
75
+ },
76
+ children: [
77
+ /* @__PURE__ */ jsxs("table", { className: styles.propertyEditorTable, children: [
78
+ /* @__PURE__ */ jsxs("colgroup", { children: [
79
+ /* @__PURE__ */ jsx("col", {}),
80
+ /* @__PURE__ */ jsx("col", {}),
81
+ /* @__PURE__ */ jsx("col", {})
82
+ ] }),
83
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
84
+ /* @__PURE__ */ jsx("th", { children: t("frontmatterEditor.key", "Key") }),
85
+ /* @__PURE__ */ jsx("th", { children: t("frontmatterEditor.value", "Value") }),
86
+ /* @__PURE__ */ jsx("th", {})
87
+ ] }) }),
88
+ /* @__PURE__ */ jsx("tbody", { children: fields.map((item, index) => {
89
+ return /* @__PURE__ */ jsxs("tr", { children: [
90
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(TableInput, { ...register(`yamlConfig.${index}.key`, { required: true }), autofocusIfEmpty: true, readOnly }) }),
91
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(TableInput, { ...register(`yamlConfig.${index}.value`, { required: true }), readOnly }) }),
92
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ onClick: () => {
97
+ remove(index);
98
+ },
99
+ className: styles.iconButton,
100
+ disabled: readOnly,
101
+ children: iconComponentFor("delete_big")
102
+ }
103
+ ) })
104
+ ] }, item.id);
105
+ }) }),
106
+ /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
107
+ "button",
108
+ {
109
+ disabled: readOnly,
110
+ className: classNames(styles.primaryButton, styles.smallButton),
111
+ type: "button",
112
+ onClick: () => {
113
+ append({ key: "", value: "" });
114
+ },
115
+ children: t("frontmatterEditor.addEntry", "Add entry")
116
+ }
117
+ ) }) }) })
118
+ ] }),
119
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" }, children: [
120
+ /* @__PURE__ */ jsx("button", { type: "submit", className: styles.primaryButton, children: t("dialogControls.save", "Save") }),
121
+ /* @__PURE__ */ jsx("button", { type: "reset", className: styles.secondaryButton, children: t("dialogControls.cancel", "Cancel") })
122
+ ] })
123
+ ]
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx(Dialog.Close, { asChild: true, children: /* @__PURE__ */ jsx("button", { className: styles.dialogCloseButton, "aria-label": t("dialogControls.cancel", "Cancel"), children: iconComponentFor("close") }) })
127
+ ] })
128
+ ] })
129
+ }
130
+ ) });
131
+ };
132
+ const TableInput = React.forwardRef(({ className, autofocusIfEmpty: _, ...props }, ref) => {
133
+ return /* @__PURE__ */ jsx("input", { className: classNames(styles.propertyEditorInput, className), ...props, ref });
134
+ });
135
+ export {
136
+ FrontmatterEditor
137
+ };