deploy: v0.12 fix 存档key名

This commit is contained in:
2026-06-24 03:09:02 +00:00
parent 7a08d49fe3
commit e0a5281119
58743 changed files with 7297269 additions and 39505 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Petyo Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+33
View File
@@ -0,0 +1,33 @@
# MDXEditor
![npm](https://img.shields.io/npm/v/@mdxeditor/editor)
![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@mdxeditor/editor)
> Because markdown editing can be even more delightful.
MDXEditor is an open-source React component that allows users to author markdown documents naturally. Just like in Google docs or Notion. [See the live demo](https://mdxeditor.dev/editor/demo) that has all features turned on.
The component supports the core markdown syntax and certain extensions, including tables, images, code blocks, etc. It also allows users to edit JSX components with a built-in JSX editor or a custom one.
```jsx
import {MDXEditor, headingsPlugin} from '@mdxeditor/editor';
import '@mdxeditor/editor/style.css';
export default function App() {
return <MDXEditor markdown={'# Hello World'} plugins={[headingsPlugin()]} />;
}
```
## Get Started
The best place to get started using the component is the [documentation](https://mdxeditor.dev/editor/docs/getting-started).
## Help and support
If you find a bug, check if something similar is not reported already in the [issues](https://github.com/mdx-editor/editor/issues). If not, [create a new issue](https://github.com/mdx-editor/editor/issues/new?assignees=&labels=bug&projects=&template=1.bug.md&title=%5BBUG%5D).
If you're integrating the component in your commercial project and need dedicated assistance with your issues in exchange of sponsorship, [contact me over email](mailto:petyo@mdxeditor.dev).
If you want to discuss ideas start a discussion in the [Discussions](https://github.com/mdx-editor/editor/discussions) section.
## License
MIT &copy; Petyo Ivanov.
+20
View File
@@ -0,0 +1,20 @@
const DEFAULT_FORMAT = 0;
const IS_BOLD = 1;
const IS_ITALIC = 2;
const IS_STRIKETHROUGH = 4;
const IS_UNDERLINE = 8;
const IS_CODE = 16;
const IS_SUBSCRIPT = 32;
const IS_SUPERSCRIPT = 64;
const IS_HIGHLIGHT = 128;
export {
DEFAULT_FORMAT,
IS_BOLD,
IS_CODE,
IS_HIGHLIGHT,
IS_ITALIC,
IS_STRIKETHROUGH,
IS_SUBSCRIPT,
IS_SUPERSCRIPT,
IS_UNDERLINE
};
+183
View File
@@ -0,0 +1,183 @@
import { usePublisher, useCellValue, useCellValues, useRealm } from "@mdxeditor/gurx";
import React__default from "react";
import { corePlugin, editorRootElementRef$, editorWrapperElementRef$, rootEditor$, useTranslation, contentEditableRef$, contentEditableWrapperElement$, contentEditableClassName$, spellCheck$, composerChildren$, topAreaChildren$, editorWrappers$, placeholder$, bottomAreaChildren$, viewMode$, activeEditor$, exportVisitors$, toMarkdownExtensions$, toMarkdownOptions$, jsxComponentDescriptors$, jsxIsAvailable$, insertMarkdown$, setMarkdown$, markdownSourceEditorValue$, markdown$ } from "./plugins/core/index.js";
import { RealmWithPlugins } from "./RealmWithPlugins.js";
import { createLexicalComposerContext, LexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import classNames from "classnames";
import { defaultSvgIcons } from "./defaultSvgIcons.js";
import { lexicalTheme } from "./styles/lexicalTheme.js";
import styles from "./styles/ui.module.css.js";
import { noop } from "./utils/fp.js";
import { getSelectionAsMarkdown } from "./utils/lexicalHelpers.js";
const LexicalProvider = ({ children }) => {
const rootEditor = useCellValue(rootEditor$);
const composerContextValue = React__default.useMemo(() => {
return [rootEditor, createLexicalComposerContext(null, lexicalTheme)];
}, [rootEditor]);
return /* @__PURE__ */ React__default.createElement(LexicalComposerContext.Provider, { value: composerContextValue }, children);
};
const RichTextEditor = () => {
const t = useTranslation();
const setContentEditableRef = usePublisher(contentEditableRef$);
const setEditorRootWrapperElement = usePublisher(contentEditableWrapperElement$);
const onRef = (el) => {
setEditorRootWrapperElement(el);
setContentEditableRef(el ? { current: el } : null);
};
const [contentEditableClassName, spellCheck, composerChildren, topAreaChildren, editorWrappers, placeholder, bottomAreaChildren] = useCellValues(
contentEditableClassName$,
spellCheck$,
composerChildren$,
topAreaChildren$,
editorWrappers$,
placeholder$,
bottomAreaChildren$
);
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, topAreaChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })), /* @__PURE__ */ React__default.createElement(RenderRecursiveWrappers, { wrappers: editorWrappers }, /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.rootContentEditableWrapper, "mdxeditor-root-contenteditable") }, /* @__PURE__ */ React__default.createElement(
RichTextPlugin,
{
contentEditable: /* @__PURE__ */ React__default.createElement("div", { ref: onRef }, /* @__PURE__ */ React__default.createElement(
ContentEditable,
{
className: classNames(styles.contentEditable, contentEditableClassName),
ariaLabel: t("contentArea.editableMarkdown", "editable markdown"),
spellCheck
}
)),
placeholder: /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.contentEditable, styles.placeholder, contentEditableClassName) }, /* @__PURE__ */ React__default.createElement("p", null, placeholder)),
ErrorBoundary: LexicalErrorBoundary
}
))), composerChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })), bottomAreaChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })));
};
const DEFAULT_MARKDOWN_OPTIONS = {
listItemIndent: "one"
};
const defaultIconComponentFor = (name) => {
return defaultSvgIcons[name];
};
function defaultTranslation(key, defaultValue, interpolations = {}) {
let value = defaultValue;
for (const [k, v] of Object.entries(interpolations)) {
value = value.replaceAll(`{{${k}}}`, String(v));
}
return value;
}
const RenderRecursiveWrappers = ({ wrappers, children }) => {
if (wrappers.length === 0) {
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children);
}
const Wrapper = wrappers[0];
return /* @__PURE__ */ React__default.createElement(Wrapper, null, /* @__PURE__ */ React__default.createElement(RenderRecursiveWrappers, { wrappers: wrappers.slice(1) }, children));
};
const EditorRootElement = ({ children, className, overlayContainer }) => {
const editorRootElementRef = React__default.useRef(null);
const wrapperElementRef = React__default.useRef(null);
const setEditorRootElementRef = usePublisher(editorRootElementRef$);
const setEditorWrapperElementRef = usePublisher(editorWrapperElementRef$);
React__default.useEffect(() => {
const popupContainer = document.createElement("div");
popupContainer.classList.add(
"mdxeditor-popup-container",
styles.editorRoot,
styles.popupContainer,
...(className ?? "").trim().split(" ").filter(Boolean)
);
const container = overlayContainer ?? document.body;
container.appendChild(popupContainer);
editorRootElementRef.current = popupContainer;
setEditorRootElementRef(editorRootElementRef);
setEditorWrapperElementRef(wrapperElementRef);
return () => {
popupContainer.remove();
};
}, [className, editorRootElementRef, overlayContainer, setEditorRootElementRef, setEditorWrapperElementRef]);
return /* @__PURE__ */ React__default.createElement("div", { className: classNames("mdxeditor", styles.editorRoot, styles.editorWrapper, className), ref: wrapperElementRef }, children);
};
const Methods = ({ mdxRef }) => {
const realm = useRealm();
React__default.useImperativeHandle(
mdxRef,
() => {
return {
getMarkdown: () => {
const viewMode = realm.getValue(viewMode$);
if (viewMode === "source" || viewMode === "diff") {
return realm.getValue(markdownSourceEditorValue$);
}
return realm.getValue(markdown$);
},
setMarkdown: (markdown) => {
realm.pub(setMarkdown$, markdown);
},
insertMarkdown: (markdown) => {
realm.pub(insertMarkdown$, markdown);
},
focus: (callbackFn, opts) => {
var _a;
(_a = realm.getValue(rootEditor$)) == null ? void 0 : _a.focus(callbackFn, opts);
},
getContentEditableHTML: () => {
var _a;
return ((_a = realm.getValue(contentEditableRef$)) == null ? void 0 : _a.current.innerHTML) ?? "";
},
getSelectionMarkdown: () => {
const viewMode = realm.getValue(viewMode$);
if (viewMode === "source" || viewMode === "diff") {
return "";
}
const activeEditor = realm.getValue(activeEditor$);
if (!activeEditor) {
return "";
}
realm.getValue(exportVisitors$);
realm.getValue(toMarkdownExtensions$);
realm.getValue(toMarkdownOptions$);
realm.getValue(jsxComponentDescriptors$);
realm.getValue(jsxIsAvailable$);
return getSelectionAsMarkdown(activeEditor);
}
};
},
[realm]
);
return null;
};
const MDXEditor = React__default.forwardRef((props, ref) => {
return /* @__PURE__ */ React__default.createElement(
RealmWithPlugins,
{
plugins: [
corePlugin({
contentEditableClassName: props.contentEditableClassName ?? "",
spellCheck: props.spellCheck ?? true,
initialMarkdown: props.markdown,
onChange: props.onChange ?? noop,
onBlur: props.onBlur ?? noop,
toMarkdownOptions: props.toMarkdownOptions ?? DEFAULT_MARKDOWN_OPTIONS,
autoFocus: props.autoFocus ?? false,
placeholder: props.placeholder ?? "",
readOnly: Boolean(props.readOnly),
iconComponentFor: props.iconComponentFor ?? defaultIconComponentFor,
suppressHtmlProcessing: props.suppressHtmlProcessing ?? false,
onError: props.onError ?? noop,
translation: props.translation ?? defaultTranslation,
trim: props.trim ?? true,
lexicalTheme: props.lexicalTheme,
..."editorState" in props ? { editorState: props.editorState } : {},
suppressSharedHistory: props.suppressSharedHistory ?? false,
additionalLexicalNodes: props.additionalLexicalNodes ?? [],
lexicalEditorNamespace: props.lexicalEditorNamespace ?? "MDXEditor"
}),
...props.plugins ?? []
]
},
/* @__PURE__ */ React__default.createElement(EditorRootElement, { className: props.className, overlayContainer: props.overlayContainer }, /* @__PURE__ */ React__default.createElement(LexicalProvider, null, /* @__PURE__ */ React__default.createElement(RichTextEditor, null))),
/* @__PURE__ */ React__default.createElement(Methods, { mdxRef: ref })
);
});
export {
MDXEditor
};
+45
View File
@@ -0,0 +1,45 @@
import React__default from "react";
import { Realm, RealmContext } from "@mdxeditor/gurx";
import { tap } from "./utils/fp.js";
function realmPlugin(plugin) {
return function(params) {
return {
init: (realm) => {
var _a;
return (_a = plugin.init) == null ? void 0 : _a.call(plugin, realm, params);
},
postInit: (realm) => {
var _a;
return (_a = plugin.postInit) == null ? void 0 : _a.call(plugin, realm, params);
},
update: (realm) => {
var _a;
return (_a = plugin.update) == null ? void 0 : _a.call(plugin, realm, params);
}
};
};
}
function RealmWithPlugins({ children, plugins }) {
const theRealm = React__default.useMemo(() => {
return tap(new Realm(), (r) => {
var _a, _b;
for (const plugin of plugins) {
(_a = plugin.init) == null ? void 0 : _a.call(plugin, r);
}
for (const plugin of plugins) {
(_b = plugin.postInit) == null ? void 0 : _b.call(plugin, r);
}
});
}, []);
React__default.useEffect(() => {
var _a;
for (const plugin of plugins) {
(_a = plugin.update) == null ? void 0 : _a.call(plugin, theRealm);
}
});
return /* @__PURE__ */ React__default.createElement(RealmContext.Provider, { value: theRealm }, children);
}
export {
RealmWithPlugins,
realmPlugin
};
+349
View File
@@ -0,0 +1,349 @@
import React__default from "react";
const defaultSvgIcons = {
undo: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M7.202 18.5V17H14.3788C15.4224 17 16.3205 16.6554 17.073 15.9663C17.8257 15.2773 18.202 14.4263 18.202 13.4135C18.202 12.4007 17.8257 11.5512 17.073 10.8652C16.3205 10.1794 15.4224 9.8365 14.3788 9.8365H7.35775L10.1402 12.6193L9.0865 13.673L4.5 9.0865L9.0865 4.5L10.1402 5.55375L7.35775 8.3365H14.3788C15.8416 8.3365 17.0945 8.82467 18.1375 9.801C19.1805 10.7773 19.702 11.9815 19.702 13.4135C19.702 14.8455 19.1805 16.0513 18.1375 17.0308C17.0945 18.0103 15.8416 18.5 14.3788 18.5H7.202Z",
fill: "currentColor"
}
)),
redo: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.6211 18.5C8.15827 18.5 6.90535 18.0103 5.86235 17.0308C4.81935 16.0513 4.29785 14.8455 4.29785 13.4135C4.29785 11.9815 4.81935 10.7773 5.86235 9.801C6.90535 8.82467 8.15827 8.3365 9.6211 8.3365H16.6421L13.8596 5.55375L14.9134 4.5L19.4999 9.0865L14.9134 13.673L13.8596 12.6193L16.6421 9.8365H9.6211C8.57744 9.8365 7.67935 10.1794 6.92685 10.8652C6.17418 11.5512 5.79785 12.4007 5.79785 13.4135C5.79785 14.4263 6.17418 15.2773 6.92685 15.9663C7.67935 16.6554 8.57744 17 9.6211 17H16.7979V18.5H9.6211Z",
fill: "currentColor"
}
)),
format_bold: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M7.33838 18.625V5.375H12.1999C13.219 5.375 14.1405 5.69233 14.9644 6.327C15.788 6.9615 16.1999 7.816 16.1999 8.8905C16.1999 9.63783 16.0194 10.2471 15.6584 10.7182C15.2975 11.1894 14.9088 11.5314 14.4921 11.7442C15.005 11.9211 15.4947 12.2708 15.9614 12.7933C16.428 13.3158 16.6614 14.0193 16.6614 14.9038C16.6614 16.1819 16.1902 17.1217 15.2479 17.723C14.3055 18.3243 13.3562 18.625 12.3999 18.625H7.33838ZM9.48838 16.6328H12.3191C13.1063 16.6328 13.6627 16.4142 13.9884 15.977C14.314 15.5398 14.4769 15.1206 14.4769 14.7192C14.4769 14.3179 14.314 13.8987 13.9884 13.4615C13.6627 13.0243 13.0909 12.8058 12.2729 12.8058H9.48838V16.6328ZM9.48838 10.875H12.0826C12.6903 10.875 13.172 10.7013 13.5279 10.3538C13.8835 10.0064 14.0614 9.59042 14.0614 9.10575C14.0614 8.59042 13.8733 8.16925 13.4971 7.84225C13.1208 7.51542 12.6595 7.352 12.1134 7.352H9.48838V10.875Z",
fill: "currentColor"
}
)),
format_italic: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.39404 18.625V16.8173H9.21129L12.4518 7.18275H8.63454V5.375H17.7883V7.18275H14.2785L11.0383 16.8173H14.5478V18.625H5.39404Z",
fill: "currentColor"
}
)),
format_underlined: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.34619 22.125V20.625H18.6537V22.125H5.34619ZM11.9999 18.5287C10.4448 18.5287 9.23102 18.0566 8.35869 17.1125C7.48619 16.1683 7.04994 14.9032 7.04994 13.3172V5.41345H8.90369V13.4095C8.90369 14.4198 9.17228 15.2295 9.70944 15.8385C10.2466 16.4475 11.0101 16.752 11.9999 16.752C12.9898 16.752 13.7533 16.4475 14.2904 15.8385C14.8276 15.2295 15.0962 14.4198 15.0962 13.4095V5.41345H16.9499V13.3172C16.9499 14.9032 16.5137 16.1683 15.6412 17.1125C14.7689 18.0566 13.5551 18.5287 11.9999 18.5287Z",
fill: "currentColor"
}
)),
format_highlight: (
/* taken from from https://lucide.dev/icons/highlighter */
/* @__PURE__ */ React__default.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: "1.75",
strokeLinecap: "round",
strokeLinejoin: "round"
},
/* @__PURE__ */ React__default.createElement("path", { d: "m9 11-6 6v3h9l3-3" }),
/* @__PURE__ */ React__default.createElement("path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
)
),
code: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M7.99994 17.6537L2.34619 11.9999L7.99994 6.34619L9.06919 7.41544L4.46919 12.0154L9.05369 16.5999L7.99994 17.6537ZM15.9999 17.6537L14.9307 16.5844L19.5307 11.9844L14.9462 7.39994L15.9999 6.34619L21.6537 11.9999L15.9999 17.6537Z",
fill: "currentColor"
}
)),
strikeThrough: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M12.15 19.6923C10.9732 19.6923 9.9315 19.359 9.025 18.6923C8.11867 18.0256 7.45525 17.1128 7.03475 15.9538L8.6385 15.2635C8.91667 16.0444 9.3545 16.6867 9.952 17.1905C10.5493 17.6944 11.2923 17.9463 12.1808 17.9463C12.9578 17.9463 13.6744 17.7508 14.3307 17.3598C14.9872 16.9686 15.3155 16.3487 15.3155 15.5C15.3155 15.123 15.2555 14.7968 15.1355 14.5213C15.0157 14.2456 14.8462 13.9885 14.627 13.75H16.677C16.7987 13.9705 16.8932 14.2256 16.9605 14.5153C17.0278 14.8051 17.0615 15.1334 17.0615 15.5C17.0615 16.8372 16.5747 17.8702 15.601 18.599C14.6272 19.3279 13.4768 19.6923 12.15 19.6923ZM2.25 11.75V10.25H21.75V11.75H2.25ZM12.05 4.19629C13.0475 4.19629 13.9123 4.42704 14.6443 4.88854C15.3763 5.35004 15.9673 6.05896 16.4173 7.01529L14.823 7.73079C14.6218 7.26029 14.2978 6.84462 13.851 6.48379C13.4042 6.12279 12.8141 5.94229 12.0808 5.94229C11.2244 5.94229 10.5392 6.16221 10.025 6.60204C9.51083 7.04171 9.264 7.59104 9.2845 8.25004H7.5385C7.50133 7.16421 7.89783 6.21712 8.728 5.40879C9.558 4.60046 10.6653 4.19629 12.05 4.19629Z",
fill: "currentColor"
}
)),
superscript: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M16.6924 8V6.2115C16.6924 5.95383 16.7786 5.73875 16.9511 5.56625C17.1235 5.39392 17.3385 5.30775 17.5961 5.30775H19.5001V4.38475H16.6924V3.5H19.4809C19.7385 3.5 19.9536 3.58625 20.1261 3.75875C20.2985 3.93108 20.3846 4.14617 20.3846 4.404V5.2885C20.3846 5.54617 20.2985 5.76125 20.1261 5.93375C19.9536 6.10608 19.7385 6.19225 19.4809 6.19225H17.5771V7.1155H20.3846V8H16.6924ZM4.44238 18.5L8.84638 11.6287L4.77713 5.30775H6.74438L9.95963 10.404H10.0214L13.2501 5.30775H15.2329L11.1194 11.6287L15.5579 18.5H13.5751L10.0214 12.9308H9.95963L6.42513 18.5H4.44238Z",
fill: "currentColor"
}
)),
subscript: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M16.6924 20.5V18.7115C16.6924 18.4538 16.7786 18.2387 16.9511 18.0662C17.1235 17.8939 17.3385 17.8077 17.5961 17.8077H19.5001V16.8845H16.6924V16H19.4809C19.7385 16 19.9536 16.0863 20.1261 16.2587C20.2985 16.4311 20.3846 16.6461 20.3846 16.9038V17.7885C20.3846 18.0462 20.2985 18.2612 20.1261 18.4337C19.9536 18.6061 19.7385 18.6923 19.4809 18.6923H17.5771V19.6152H20.3846V20.5H16.6924ZM4.44238 18.6923L8.84638 11.8212L4.77713 5.5H6.74438L9.95963 10.5962H10.0214L13.2501 5.5H15.2329L11.1194 11.8212L15.5579 18.6923H13.5751L10.0214 13.123H9.95963L6.42513 18.6923H4.44238Z",
fill: "currentColor"
}
)),
format_list_bulleted: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.30775 18.75V17.25H20.5V18.75H9.30775ZM9.30775 12.75V11.25H20.5V12.75H9.30775ZM9.30775 6.75005V5.25005H20.5V6.75005H9.30775ZM5.1635 19.6635C4.706 19.6635 4.31442 19.5006 3.98875 19.1748C3.66292 18.8491 3.5 18.4575 3.5 18C3.5 17.5425 3.66292 17.151 3.98875 16.8253C4.31442 16.4995 4.706 16.3365 5.1635 16.3365C5.621 16.3365 6.01258 16.4995 6.33825 16.8253C6.66408 17.151 6.827 17.5425 6.827 18C6.827 18.4575 6.66408 18.8491 6.33825 19.1748C6.01258 19.5006 5.621 19.6635 5.1635 19.6635ZM5.1635 13.6635C4.706 13.6635 4.31442 13.5006 3.98875 13.1748C3.66292 12.8491 3.5 12.4575 3.5 12C3.5 11.5425 3.66292 11.151 3.98875 10.8253C4.31442 10.4995 4.706 10.3365 5.1635 10.3365C5.621 10.3365 6.01258 10.4995 6.33825 10.8253C6.66408 11.151 6.827 11.5425 6.827 12C6.827 12.4575 6.66408 12.8491 6.33825 13.1748C6.01258 13.5006 5.621 13.6635 5.1635 13.6635ZM5.1635 7.66355C4.706 7.66355 4.31442 7.50063 3.98875 7.1748C3.66292 6.84913 3.5 6.45755 3.5 6.00005C3.5 5.54255 3.66292 5.15096 3.98875 4.8253C4.31442 4.49946 4.706 4.33655 5.1635 4.33655C5.621 4.33655 6.01258 4.49946 6.33825 4.8253C6.66408 5.15096 6.827 5.54255 6.827 6.00005C6.827 6.45755 6.66408 6.84913 6.33825 7.1748C6.01258 7.50063 5.621 7.66355 5.1635 7.66355Z",
fill: "currentColor"
}
)),
format_list_numbered: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M3.5 21.5V20.3078H6V19.25H4.5V18.0577H6V17H3.5V15.8077H6.34625C6.58592 15.8077 6.78683 15.8888 6.949 16.051C7.11117 16.2132 7.19225 16.4141 7.19225 16.6538V17.8462C7.19225 18.0859 7.11117 18.2868 6.949 18.449C6.78683 18.6112 6.58592 18.6923 6.34625 18.6923C6.58592 18.6923 6.78683 18.7733 6.949 18.9355C7.11117 19.0977 7.19225 19.2987 7.19225 19.5385V20.6538C7.19225 20.8936 7.11117 21.0946 6.949 21.2568C6.78683 21.4189 6.58592 21.5 6.34625 21.5H3.5ZM3.5 14.8463V12.25C3.5 12.0103 3.58108 11.8093 3.74325 11.647C3.90542 11.4848 4.10642 11.4038 4.34625 11.4038H6V10.3462H3.5V9.15375H6.34625C6.58592 9.15375 6.78683 9.23483 6.949 9.397C7.11117 9.55933 7.19225 9.76033 7.19225 10V11.75C7.19225 11.9897 7.11117 12.1907 6.949 12.353C6.78683 12.5152 6.58592 12.5963 6.34625 12.5963H4.69225V13.6538H7.19225V14.8463H3.5ZM5 8.19225V3.69225H3.5V2.5H6.19225V8.19225H5ZM9.30775 18.75V17.25H20.5V18.75H9.30775ZM9.30775 12.75V11.25H20.5V12.75H9.30775ZM9.30775 6.75V5.25H20.5V6.75H9.30775Z",
fill: "currentColor"
}
)),
format_list_checked: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.69425 18.452L2.5 15.2578L3.54425 14.2135L5.66925 16.3385L9.91925 12.0885L10.9635 13.1578L5.69425 18.452ZM5.69425 10.8365L2.5 7.64227L3.54425 6.59802L5.66925 8.72302L9.91925 4.47302L10.9635 5.54227L5.69425 10.8365ZM13.0095 16.5578V15.0578H21.5095V16.5578H13.0095ZM13.0095 8.94227V7.44227H21.5095V8.94227H13.0095Z",
fill: "currentColor"
}
)),
link: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M10.8077 16.5385H7.0385C5.78283 16.5385 4.7125 16.096 3.8275 15.211C2.9425 14.3262 2.5 13.256 2.5 12.0005C2.5 10.745 2.9425 9.67471 3.8275 8.78955C4.7125 7.90421 5.78283 7.46155 7.0385 7.46155H10.8077V8.96155H7.0385C6.19867 8.96155 5.48233 9.25805 4.8895 9.85105C4.2965 10.444 4 11.1604 4 12C4 12.8397 4.2965 13.556 4.8895 14.149C5.48233 14.742 6.19867 15.0385 7.0385 15.0385H10.8077V16.5385ZM8.25 12.75V11.25H15.75V12.75H8.25ZM13.1923 16.5385V15.0385H16.9615C17.8013 15.0385 18.5177 14.742 19.1105 14.149C19.7035 13.556 20 12.8397 20 12C20 11.1604 19.7035 10.444 19.1105 9.85105C18.5177 9.25805 17.8013 8.96155 16.9615 8.96155H13.1923V7.46155H16.9615C18.2172 7.46155 19.2875 7.90405 20.1725 8.78905C21.0575 9.67388 21.5 10.744 21.5 11.9995C21.5 13.255 21.0575 14.3254 20.1725 15.2105C19.2875 16.0959 18.2172 16.5385 16.9615 16.5385H13.1923Z",
fill: "currentColor"
}
)),
add_photo: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.11537 20.5C4.6182 20.5 4.19262 20.323 3.83862 19.969C3.48462 19.615 3.30762 19.1894 3.30762 18.6922V5.30773C3.30762 4.81056 3.48462 4.38498 3.83862 4.03098C4.19262 3.67698 4.6182 3.49998 5.11537 3.49998H13.8076V4.99998H5.11537C5.02553 4.99998 4.95178 5.02881 4.89412 5.08648C4.83645 5.14415 4.80762 5.2179 4.80762 5.30773V18.6922C4.80762 18.7821 4.83645 18.8558 4.89412 18.9135C4.95178 18.9711 5.02553 19 5.11537 19H18.4999C18.5895 19 18.6633 18.9711 18.7211 18.9135C18.7788 18.8558 18.8076 18.7821 18.8076 18.6922V9.99998H20.3076V18.6922C20.3076 19.1894 20.1306 19.615 19.7766 19.969C19.4226 20.323 18.997 20.5 18.4999 20.5H5.11537ZM17.1921 8.61523V6.61523H15.1921V5.11548H17.1921V3.11548H18.6921V5.11548H20.6921V6.61523H18.6921V8.61523H17.1921ZM6.55762 16.75H17.1344L13.8459 12.3655L11.0384 16.0192L9.03837 13.4615L6.55762 16.75Z",
fill: "currentColor"
}
)),
table: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M3.5 18.6923V5.30775C3.5 4.81058 3.677 4.385 4.031 4.031C4.385 3.677 4.81058 3.5 5.30775 3.5H18.6923C19.1894 3.5 19.615 3.677 19.969 4.031C20.323 4.385 20.5 4.81058 20.5 5.30775V18.6923C20.5 19.1894 20.323 19.615 19.969 19.969C19.615 20.323 19.1894 20.5 18.6923 20.5H5.30775C4.81058 20.5 4.385 20.323 4.031 19.969C3.677 19.615 3.5 19.1894 3.5 18.6923ZM5 9.077H19V5.30775C19 5.21792 18.9712 5.14417 18.9135 5.0865C18.8558 5.02883 18.7821 5 18.6923 5H5.30775C5.21792 5 5.14417 5.02883 5.0865 5.0865C5.02883 5.14417 5 5.21792 5 5.30775V9.077ZM10.1615 14.0385H13.8385V10.577H10.1615V14.0385ZM10.1615 19H13.8385V15.5385H10.1615V19ZM5 14.0385H8.6615V10.577H5V14.0385ZM15.3385 14.0385H19V10.577H15.3385V14.0385ZM5.30775 19H8.6615V15.5385H5V18.6923C5 18.7821 5.02883 18.8558 5.0865 18.9135C5.14417 18.9712 5.21792 19 5.30775 19ZM15.3385 19H18.6923C18.7821 19 18.8558 18.9712 18.9135 18.9135C18.9712 18.8558 19 18.7821 19 18.6923V15.5385H15.3385V19Z",
fill: "currentColor"
}
)),
horizontal_rule: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement("path", { d: "M4.5 12.75V11.25H19.5V12.75H4.5Z", fill: "currentColor" })),
frontmatter: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement("path", { d: "M4.5 8.75V7.25H8.5V8.75H4.5Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M4.5 14.75V13.25H8.5V14.75H4.5Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M9.5 8.75V7.25H13.5V8.75H9.5Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M9.5 14.75V13.25H13.5V14.75H9.5Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M14.5 8.75V7.25H18.5V8.75H14.5Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M14.5 14.75V13.25H18.5V14.75H14.5Z", fill: "currentColor" })),
frame_source: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M8.6 15.2443L5.35575 12L8.6 8.75575L9.64425 9.825L7.46925 12L9.64425 14.175L8.6 15.2443ZM15.4 15.2443L14.3558 14.175L16.5307 12L14.3558 9.825L15.4 8.75575L18.6443 12L15.4 15.2443ZM5.30775 20.5C4.80258 20.5 4.375 20.325 4.025 19.975C3.675 19.625 3.5 19.1974 3.5 18.6923V15H5V18.6923C5 18.7692 5.03208 18.8398 5.09625 18.9038C5.16025 18.9679 5.23075 19 5.30775 19H9V20.5H5.30775ZM15 20.5V19H18.6923C18.7692 19 18.8398 18.9679 18.9038 18.9038C18.9679 18.8398 19 18.7692 19 18.6923V15H20.5V18.6923C20.5 19.1974 20.325 19.625 19.975 19.975C19.625 20.325 19.1974 20.5 18.6923 20.5H15ZM3.5 9V5.30775C3.5 4.80258 3.675 4.375 4.025 4.025C4.375 3.675 4.80258 3.5 5.30775 3.5H9V5H5.30775C5.23075 5 5.16025 5.03208 5.09625 5.09625C5.03208 5.16025 5 5.23075 5 5.30775V9H3.5ZM19 9V5.30775C19 5.23075 18.9679 5.16025 18.9038 5.09625C18.8398 5.03208 18.7692 5 18.6923 5H15V3.5H18.6923C19.1974 3.5 19.625 3.675 19.975 4.025C20.325 4.375 20.5 4.80258 20.5 5.30775V9H19Z",
fill: "currentColor"
}
)),
arrow_drop_down: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement("path", { d: "M11.9999 14.6537L7.59619 10.25H16.4037L11.9999 14.6537Z", fill: "currentColor" })),
admonition: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M12.0001 21.4C11.7654 21.4 11.5385 21.3548 11.3193 21.2645C11.1002 21.174 10.9002 21.0435 10.7193 20.873L3.1271 13.2807C2.9566 13.0999 2.8261 12.8999 2.7356 12.6807C2.64526 12.4616 2.6001 12.2346 2.6001 12C2.6001 11.7653 2.64526 11.5358 2.7356 11.3115C2.8261 11.0871 2.9566 10.8897 3.1271 10.7192L10.7193 3.12698C10.9002 2.94614 11.1002 2.81314 11.3193 2.72798C11.5385 2.64264 11.7654 2.59998 12.0001 2.59998C12.2348 2.59998 12.4643 2.64264 12.6886 2.72798C12.9129 2.81314 13.1103 2.94614 13.2808 3.12698L20.8731 10.7192C21.0539 10.8897 21.1869 11.0871 21.2721 11.3115C21.3574 11.5358 21.4001 11.7653 21.4001 12C21.4001 12.2346 21.3574 12.4616 21.2721 12.6807C21.1869 12.8999 21.0539 13.0999 20.8731 13.2807L13.2808 20.873C13.1103 21.0435 12.9129 21.174 12.6886 21.2645C12.4643 21.3548 12.2348 21.4 12.0001 21.4ZM12.2213 19.8037L19.8039 12.2212C19.8552 12.1699 19.8808 12.0961 19.8808 12C19.8808 11.9038 19.8552 11.8301 19.8039 11.7787L12.2213 4.19623C12.17 4.14489 12.0963 4.11923 12.0001 4.11923C11.9039 4.11923 11.8302 4.14489 11.7788 4.19623L4.19635 11.7787C4.14501 11.8301 4.11935 11.9038 4.11935 12C4.11935 12.0961 4.14501 12.1699 4.19635 12.2212L11.7788 19.8037C11.8302 19.8551 11.9039 19.8807 12.0001 19.8807C12.0963 19.8807 12.17 19.8551 12.2213 19.8037ZM11.2501 13.0865H12.7501V7.47123H11.2501V13.0865ZM12.0001 15.702C12.2258 15.702 12.4168 15.6237 12.5731 15.4672C12.7296 15.3109 12.8078 15.1199 12.8078 14.8942C12.8078 14.6686 12.7296 14.4776 12.5731 14.3212C12.4168 14.1647 12.2258 14.0865 12.0001 14.0865C11.7744 14.0865 11.5834 14.1647 11.4271 14.3212C11.2706 14.4776 11.1923 14.6686 11.1923 14.8942C11.1923 15.1199 11.2706 15.3109 11.4271 15.4672C11.5834 15.6237 11.7744 15.702 12.0001 15.702Z",
fill: "currentColor"
}
)),
sandpack: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M12.9999 22.5C12.4947 22.5 12.0671 22.325 11.7171 21.975C11.3671 21.625 11.1921 21.1974 11.1921 20.6922V7.30775C11.1921 6.80258 11.3671 6.375 11.7171 6.025C12.0671 5.675 12.4947 5.5 12.9999 5.5H16.3844C16.8895 5.5 17.3171 5.675 17.6671 6.025C18.0171 6.375 18.1921 6.80258 18.1921 7.30775V20.6922C18.1921 21.1974 18.0171 21.625 17.6671 21.975C17.3171 22.325 16.8895 22.5 16.3844 22.5H12.9999ZM12.6921 7.30775V20.6922C12.6921 20.7692 12.7242 20.8398 12.7884 20.9038C12.8524 20.9679 12.9229 21 12.9999 21H16.3844C16.4614 21 16.5319 20.9679 16.5959 20.9038C16.66 20.8398 16.6921 20.7692 16.6921 20.6922V7.30775C16.6921 7.23075 16.66 7.16025 16.5959 7.09625C16.5319 7.03208 16.4614 7 16.3844 7H12.9999C12.9229 7 12.8524 7.03208 12.7884 7.09625C12.7242 7.16025 12.6921 7.23075 12.6921 7.30775ZM6.61537 18.5C6.1102 18.5 5.68262 18.325 5.33262 17.975C4.98262 17.625 4.80762 17.1974 4.80762 16.6923V3.30775C4.80762 2.80258 4.98262 2.375 5.33262 2.025C5.68262 1.675 6.1102 1.5 6.61537 1.5H9.99987C10.505 1.5 10.9326 1.675 11.2826 2.025C11.6326 2.375 11.8076 2.80258 11.8076 3.30775V16.6923C11.8076 17.1974 11.6326 17.625 11.2826 17.975C10.9326 18.325 10.505 18.5 9.99987 18.5H6.61537ZM6.30762 3.30775V16.6923C6.30762 16.7692 6.3397 16.8398 6.40387 16.9038C6.46787 16.9679 6.53837 17 6.61537 17H9.99987C10.0769 17 10.1474 16.9679 10.2114 16.9038C10.2755 16.8398 10.3076 16.7692 10.3076 16.6923V3.30775C10.3076 3.23075 10.2755 3.16025 10.2114 3.09625C10.1474 3.03208 10.0769 3 9.99987 3H6.61537C6.53837 3 6.46787 3.03208 6.40387 3.09625C6.3397 3.16025 6.30762 3.23075 6.30762 3.30775Z",
fill: "currentColor"
}
)),
rich_text: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.30775 20.5C4.80258 20.5 4.375 20.325 4.025 19.975C3.675 19.625 3.5 19.1974 3.5 18.6923V5.30775C3.5 4.80258 3.675 4.375 4.025 4.025C4.375 3.675 4.80258 3.5 5.30775 3.5H18.6923C19.1974 3.5 19.625 3.675 19.975 4.025C20.325 4.375 20.5 4.80258 20.5 5.30775V18.6923C20.5 19.1974 20.325 19.625 19.975 19.975C19.625 20.325 19.1974 20.5 18.6923 20.5H5.30775ZM5.30775 19H18.6923C18.7692 19 18.8398 18.9679 18.9038 18.9038C18.9679 18.8398 19 18.7692 19 18.6923V5.30775C19 5.23075 18.9679 5.16025 18.9038 5.09625C18.8398 5.03208 18.7692 5 18.6923 5H5.30775C5.23075 5 5.16025 5.03208 5.09625 5.09625C5.03208 5.16025 5 5.23075 5 5.30775V18.6923C5 18.7692 5.03208 18.8398 5.09625 18.9038C5.16025 18.9679 5.23075 19 5.30775 19ZM6.75 17H17.3268L14 12.3655L11.2308 16.0192L9 13.4615L6.75 17Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement(
"path",
{
fillRule: "evenodd",
clipRule: "evenodd",
d: "M6 12V6.5H9.1925C9.5925 6.5 9.9425 6.65 10.2425 6.95C10.5425 7.25 10.6925 7.6 10.6925 8V8.6925C10.6925 9.03733 10.605 9.32675 10.43 9.56075C10.255 9.79458 10.0168 9.97817 9.7155 10.1115L10.6155 12H9.404L8.504 10.1925H7.1925V12H6ZM9.1925 9H7.1925V7.6925H9.1925C9.26933 7.6925 9.33983 7.7245 9.404 7.7885C9.468 7.85267 9.5 7.92317 9.5 8V8.6925C9.5 8.76933 9.468 8.83983 9.404 8.904C9.33983 8.968 9.26933 9 9.1925 9Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement("path", { d: "M12.5 7H17.5V8.25H12.5V7Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("path", { d: "M12.5 9.25H17.5V10.5H12.5V9.25Z", fill: "currentColor" })),
difference: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
fillRule: "evenodd",
clipRule: "evenodd",
d: "M5.30775 20.5C4.80258 20.5 4.375 20.325 4.025 19.975C3.675 19.625 3.5 19.1974 3.5 18.6923V5.30775C3.5 4.80258 3.675 4.375 4.025 4.025C4.375 3.675 4.80258 3.5 5.30775 3.5H18.6923C19.1974 3.5 19.625 3.675 19.975 4.025C20.325 4.375 20.5 4.80258 20.5 5.30775V18.6923C20.5 19.1974 20.325 19.625 19.975 19.975C19.625 20.325 19.1974 20.5 18.6923 20.5H5.30775ZM18.6923 19H5.30775C5.23075 19 5.16025 18.9679 5.09625 18.9038C5.03208 18.8398 5 18.7692 5 18.6923V5.30775C5 5.23075 5.03208 5.16025 5.09625 5.09625C5.16025 5.03208 5.23075 5 5.30775 5H18.6923C18.7692 5 18.8398 5.03208 18.9038 5.09625C18.9679 5.16025 19 5.23075 19 5.30775V18.6923C19 18.7692 18.9679 18.8398 18.9038 18.9038C18.8398 18.9679 18.7692 19 18.6923 19Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement("rect", { x: "9", y: "10", width: "5.5", height: "1.5", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("rect", { x: "9", y: "15", width: "5.5", height: "1.5", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement("rect", { x: "11", y: "8", width: "1.5", height: "5.5", fill: "currentColor" })),
markdown: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
fillRule: "evenodd",
clipRule: "evenodd",
d: "M5.30775 20.5C4.80258 20.5 4.375 20.325 4.025 19.975C3.675 19.625 3.5 19.1974 3.5 18.6923V5.30775C3.5 4.80258 3.675 4.375 4.025 4.025C4.375 3.675 4.80258 3.5 5.30775 3.5H18.6923C19.1974 3.5 19.625 3.675 19.975 4.025C20.325 4.375 20.5 4.80258 20.5 5.30775V18.6923C20.5 19.1974 20.325 19.625 19.975 19.975C19.625 20.325 19.1974 20.5 18.6923 20.5H5.30775ZM18.6923 19H5.30775C5.23075 19 5.16025 18.9679 5.09625 18.9038C5.03208 18.8398 5 18.7692 5 18.6923V5.30775C5 5.23075 5.03208 5.16025 5.09625 5.09625C5.16025 5.03208 5.23075 5 5.30775 5H18.6923C18.7692 5 18.8398 5.03208 18.9038 5.09625C18.9679 5.16025 19 5.23075 19 5.30775V18.6923C19 18.7692 18.9679 18.8398 18.9038 18.9038C18.8398 18.9679 18.7692 19 18.6923 19Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement("path", { d: "M13 15H11.5V11L9.5 14L7.5 11V15H6V8H7.5L9.5 11L11.5 8H13V15Z", fill: "currentColor" }), /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M15.9921 15.5833L13.2886 12.8797L14.0546 12.1137L15.4504 13.5016V7.99597L16.5336 7.99993V13.5016L17.9296 12.1137L18.6954 12.8797L15.9921 15.5833Z",
fill: "currentColor"
}
)),
open_in_new: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5.30775 20.5C4.80258 20.5 4.375 20.325 4.025 19.975C3.675 19.625 3.5 19.1974 3.5 18.6923V5.30775C3.5 4.80258 3.675 4.375 4.025 4.025C4.375 3.675 4.80258 3.5 5.30775 3.5H11.6152V5H5.30775C5.23075 5 5.16025 5.03208 5.09625 5.09625C5.03208 5.16025 5 5.23075 5 5.30775V18.6923C5 18.7692 5.03208 18.8398 5.09625 18.9038C5.16025 18.9679 5.23075 19 5.30775 19H18.6923C18.7692 19 18.8398 18.9679 18.9038 18.9038C18.9679 18.8398 19 18.7692 19 18.6923V12.3848H20.5V18.6923C20.5 19.1974 20.325 19.625 19.975 19.975C19.625 20.325 19.1974 20.5 18.6923 20.5H5.30775ZM9.71925 15.3345L8.6655 14.2808L17.9462 5H14V3.5H20.5V10H19V6.05375L9.71925 15.3345Z",
fill: "currentColor"
}
)),
link_off: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M18.8843 16.1038L17.7498 14.9385C18.4228 14.7552 18.9661 14.3946 19.3796 13.8568C19.7931 13.319 19.9998 12.7 19.9998 12C19.9998 11.1604 19.705 10.444 19.1153 9.85105C18.5255 9.25805 17.8139 8.96155 16.9806 8.96155H13.1728V7.46155H16.9806C18.2293 7.46155 19.2947 7.90422 20.1768 8.78955C21.0588 9.67472 21.4998 10.7449 21.4998 12C21.4998 12.8859 21.262 13.6951 20.7863 14.4278C20.3107 15.1606 19.6767 15.7193 18.8843 16.1038ZM15.5806 12.75L14.0806 11.25H15.7306V12.75H15.5806ZM20.1461 22.2538L1.74609 3.8538L2.79984 2.80005L21.1998 21.2L20.1461 22.2538ZM10.8268 16.5386H7.03834C5.78318 16.5386 4.71301 16.0959 3.82784 15.2105C2.94251 14.3254 2.49984 13.2552 2.49984 12C2.49984 10.8885 2.85784 9.91322 3.57384 9.07405C4.28984 8.23505 5.18826 7.72838 6.26909 7.55405H6.49984L7.90759 8.96155H7.03834C6.19851 8.96155 5.48218 9.25805 4.88934 9.85105C4.29634 10.444 3.99984 11.1604 3.99984 12C3.99984 12.8397 4.29634 13.556 4.88934 14.149C5.48218 14.742 6.19851 15.0385 7.03834 15.0385H10.8268V16.5386ZM8.26909 12.75V11.25H10.2113L11.6863 12.75H8.26909Z",
fill: "currentColor"
}
)),
edit: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M5 19H6.2615L16.498 8.7635L15.2365 7.502L5 17.7385V19ZM3.5 20.5V17.1155L16.6905 3.93075C16.8417 3.79342 17.0086 3.68733 17.1913 3.6125C17.3741 3.5375 17.5658 3.5 17.7663 3.5C17.9668 3.5 18.1609 3.53558 18.3488 3.60675C18.5367 3.67792 18.7032 3.79108 18.848 3.94625L20.0693 5.18275C20.2244 5.32758 20.335 5.49425 20.401 5.68275C20.467 5.87125 20.5 6.05975 20.5 6.24825C20.5 6.44942 20.4657 6.64133 20.397 6.824C20.3283 7.00683 20.2191 7.17383 20.0693 7.325L6.8845 20.5H3.5ZM15.8562 8.14375L15.2365 7.502L16.498 8.7635L15.8562 8.14375Z",
fill: "currentColor"
}
)),
content_copy: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.05775 17.5C8.55258 17.5 8.125 17.325 7.775 16.975C7.425 16.625 7.25 16.1974 7.25 15.6923V4.30775C7.25 3.80258 7.425 3.375 7.775 3.025C8.125 2.675 8.55258 2.5 9.05775 2.5H17.4423C17.9474 2.5 18.375 2.675 18.725 3.025C19.075 3.375 19.25 3.80258 19.25 4.30775V15.6923C19.25 16.1974 19.075 16.625 18.725 16.975C18.375 17.325 17.9474 17.5 17.4423 17.5H9.05775ZM9.05775 16H17.4423C17.5192 16 17.5898 15.9679 17.6538 15.9038C17.7179 15.8398 17.75 15.7692 17.75 15.6923V4.30775C17.75 4.23075 17.7179 4.16025 17.6538 4.09625C17.5898 4.03208 17.5192 4 17.4423 4H9.05775C8.98075 4 8.91025 4.03208 8.84625 4.09625C8.78208 4.16025 8.75 4.23075 8.75 4.30775V15.6923C8.75 15.7692 8.78208 15.8398 8.84625 15.9038C8.91025 15.9679 8.98075 16 9.05775 16ZM5.55775 21C5.05258 21 4.625 20.825 4.275 20.475C3.925 20.125 3.75 19.6974 3.75 19.1923V6.30775H5.25V19.1923C5.25 19.2693 5.28208 19.3398 5.34625 19.4038C5.41025 19.4679 5.48075 19.5 5.55775 19.5H15.4423V21H5.55775Z",
fill: "currentColor"
}
)),
more_horiz: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M6.23096 13.5C5.81846 13.5 5.46537 13.3531 5.17171 13.0592C4.87787 12.7656 4.73096 12.4125 4.73096 12C4.73096 11.5875 4.87787 11.2344 5.17171 10.9408C5.46537 10.6469 5.81846 10.5 6.23096 10.5C6.64346 10.5 6.99662 10.6469 7.29046 10.9408C7.58412 11.2344 7.73096 11.5875 7.73096 12C7.73096 12.4125 7.58412 12.7656 7.29046 13.0592C6.99662 13.3531 6.64346 13.5 6.23096 13.5ZM12.0002 13.5C11.5877 13.5 11.2346 13.3531 10.941 13.0592C10.6471 12.7656 10.5002 12.4125 10.5002 12C10.5002 11.5875 10.6471 11.2344 10.941 10.9408C11.2346 10.6469 11.5877 10.5 12.0002 10.5C12.4127 10.5 12.7658 10.6469 13.0595 10.9408C13.3533 11.2344 13.5002 11.5875 13.5002 12C13.5002 12.4125 13.3533 12.7656 13.0595 13.0592C12.7658 13.3531 12.4127 13.5 12.0002 13.5ZM17.7695 13.5C17.357 13.5 17.0038 13.3531 16.71 13.0592C16.4163 12.7656 16.2695 12.4125 16.2695 12C16.2695 11.5875 16.4163 11.2344 16.71 10.9408C17.0038 10.6469 17.357 10.5 17.7695 10.5C18.182 10.5 18.535 10.6469 18.8287 10.9408C19.1225 11.2344 19.2695 11.5875 19.2695 12C19.2695 12.4125 19.1225 12.7656 18.8287 13.0592C18.535 13.3531 18.182 13.5 17.7695 13.5Z",
fill: "currentColor"
}
)),
more_vert: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M12 19.2692C11.5875 19.2692 11.2344 19.1223 10.9408 18.8285C10.6469 18.5348 10.5 18.1817 10.5 17.7692C10.5 17.3567 10.6469 17.0035 10.9408 16.7097C11.2344 16.416 11.5875 16.2692 12 16.2692C12.4125 16.2692 12.7656 16.416 13.0592 16.7097C13.3531 17.0035 13.5 17.3567 13.5 17.7692C13.5 18.1817 13.3531 18.5348 13.0592 18.8285C12.7656 19.1223 12.4125 19.2692 12 19.2692ZM12 13.5C11.5875 13.5 11.2344 13.353 10.9408 13.0592C10.6469 12.7655 10.5 12.4125 10.5 12C10.5 11.5875 10.6469 11.2344 10.9408 10.9407C11.2344 10.6469 11.5875 10.5 12 10.5C12.4125 10.5 12.7656 10.6469 13.0592 10.9407C13.3531 11.2344 13.5 11.5875 13.5 12C13.5 12.4125 13.3531 12.7655 13.0592 13.0592C12.7656 13.353 12.4125 13.5 12 13.5ZM12 7.73071C11.5875 7.73071 11.2344 7.58388 10.9408 7.29021C10.6469 6.99638 10.5 6.64321 10.5 6.23071C10.5 5.81821 10.6469 5.46513 10.9408 5.17146C11.2344 4.87763 11.5875 4.73071 12 4.73071C12.4125 4.73071 12.7656 4.87763 13.0592 5.17146C13.3531 5.46513 13.5 5.81821 13.5 6.23071C13.5 6.64321 13.3531 6.99638 13.0592 7.29021C12.7656 7.58388 12.4125 7.73071 12 7.73071Z",
fill: "currentColor"
}
)),
close: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M6.39994 18.6538L5.34619 17.6L10.9462 12L5.34619 6.4L6.39994 5.34625L11.9999 10.9463L17.5999 5.34625L18.6537 6.4L13.0537 12L18.6537 17.6L17.5999 18.6538L11.9999 13.0538L6.39994 18.6538Z",
fill: "currentColor"
}
)),
settings: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.69225 21.5L9.3115 18.4538C9.04367 18.3641 8.769 18.2385 8.4875 18.077C8.20617 17.9153 7.95459 17.7422 7.73275 17.5577L4.9115 18.75L2.604 14.75L5.04425 12.9058C5.02125 12.7571 5.00492 12.6077 4.99525 12.4578C4.98559 12.3078 4.98075 12.1583 4.98075 12.0095C4.98075 11.8673 4.98559 11.7228 4.99525 11.576C5.00492 11.4292 5.02125 11.2686 5.04425 11.0943L2.604 9.25L4.9115 5.26925L7.723 6.452C7.96417 6.261 8.22159 6.08633 8.49525 5.928C8.76892 5.76967 9.03784 5.64242 9.302 5.54625L9.69225 2.5H14.3078L14.6885 5.55575C14.9885 5.66475 15.2599 5.792 15.5028 5.9375C15.7458 6.083 15.991 6.2545 16.2385 6.452L19.0885 5.26925L21.396 9.25L18.9173 11.123C18.9531 11.2845 18.9727 11.4355 18.976 11.576C18.9792 11.7163 18.9808 11.8577 18.9808 12C18.9808 12.1358 18.9775 12.274 18.971 12.4145C18.9647 12.5548 18.9417 12.7154 18.902 12.8963L21.3615 14.75L19.0538 18.75L16.2385 17.548C15.991 17.7455 15.7384 17.9202 15.4808 18.072C15.2231 18.224 14.959 18.3481 14.6885 18.4443L14.3078 21.5H9.69225ZM11 20H12.9655L13.325 17.3212C13.8353 17.1879 14.3017 16.9985 14.724 16.753C15.1465 16.5073 15.5539 16.1916 15.9463 15.8057L18.4308 16.85L19.4155 15.15L17.2463 13.5155C17.3296 13.2565 17.3863 13.0026 17.4163 12.7537C17.4464 12.5051 17.4615 12.2538 17.4615 12C17.4615 11.7397 17.4464 11.4884 17.4163 11.2463C17.3863 11.0039 17.3296 10.7564 17.2463 10.5038L19.4345 8.85L18.45 7.15L15.9365 8.2095C15.6018 7.85183 15.2009 7.53583 14.7338 7.2615C14.2664 6.98717 13.7937 6.79292 13.3155 6.67875L13 4H11.0155L10.6845 6.66925C10.1743 6.78975 9.70325 6.97433 9.27125 7.223C8.83909 7.47183 8.42684 7.79233 8.0345 8.1845L5.55 7.15L4.5655 8.85L6.725 10.4595C6.64167 10.6968 6.58334 10.9437 6.55 11.2C6.51667 11.4563 6.5 11.7262 6.5 12.0095C6.5 12.2698 6.51667 12.525 6.55 12.775C6.58334 13.025 6.6385 13.2718 6.7155 13.5155L4.5655 15.15L5.55 16.85L8.025 15.8C8.4045 16.1897 8.81025 16.5089 9.24225 16.7578C9.67442 17.0064 10.152 17.1974 10.675 17.3307L11 20ZM12.0115 15C12.8435 15 13.5515 14.708 14.1355 14.124C14.7195 13.54 15.0115 12.832 15.0115 12C15.0115 11.168 14.7195 10.46 14.1355 9.876C13.5515 9.292 12.8435 9 12.0115 9C11.1692 9 10.4586 9.292 9.87975 9.876C9.30092 10.46 9.0115 11.168 9.0115 12C9.0115 12.832 9.30092 13.54 9.87975 14.124C10.4586 14.708 11.1692 15 12.0115 15Z",
fill: "currentColor"
}
)),
delete_big: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M7.30775 20.5C6.80908 20.5 6.38308 20.3234 6.02975 19.9702C5.67658 19.6169 5.5 19.1909 5.5 18.6922V5.99998H4.5V4.49998H9V3.61548H15V4.49998H19.5V5.99998H18.5V18.6922C18.5 19.1974 18.325 19.625 17.975 19.975C17.625 20.325 17.1974 20.5 16.6923 20.5H7.30775ZM17 5.99998H7V18.6922C7 18.7821 7.02883 18.8558 7.0865 18.9135C7.14417 18.9711 7.21792 19 7.30775 19H16.6923C16.7692 19 16.8398 18.9679 16.9038 18.9037C16.9679 18.8397 17 18.7692 17 18.6922V5.99998ZM9.404 17H10.9037V7.99998H9.404V17ZM13.0962 17H14.596V7.99998H13.0962V17Z",
fill: "currentColor"
}
)),
delete_small: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M7.30775 20.5C6.80908 20.5 6.38308 20.3234 6.02975 19.9702C5.67658 19.6169 5.5 19.1909 5.5 18.6922V5.99998H4.5V4.49998H9V3.61548H15V4.49998H19.5V5.99998H18.5V18.6922C18.5 19.1974 18.325 19.625 17.975 19.975C17.625 20.325 17.1974 20.5 16.6923 20.5H7.30775ZM17 5.99998H7V18.6922C7 18.7821 7.02883 18.8558 7.0865 18.9135C7.14417 18.9711 7.21792 19 7.30775 19H16.6923C16.7692 19 16.8398 18.9679 16.9038 18.9037C16.9679 18.8397 17 18.7692 17 18.6922V5.99998ZM9.404 17H10.9037V7.99998H9.404V17ZM13.0962 17H14.596V7.99998H13.0962V17Z",
fill: "currentColor"
}
)),
format_align_center: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M3.5 20.5V19H20.5V20.5H3.5ZM7.5 16.625V15.125H16.5V16.625H7.5ZM3.5 12.75V11.25H20.5V12.75H3.5ZM7.5 8.875V7.375H16.5V8.875H7.5ZM3.5 5V3.5H20.5V5H3.5Z",
fill: "currentColor"
}
)),
format_align_left: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M3.5 20.5V19H20.5V20.5H3.5ZM3.5 16.625V15.125H14.5V16.625H3.5ZM3.5 12.75V11.25H20.5V12.75H3.5ZM3.5 8.875V7.375H14.5V8.875H3.5ZM3.5 5V3.5H20.5V5H3.5Z",
fill: "currentColor"
}
)),
format_align_right: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M3.5 5V3.5H20.5V5H3.5ZM9.5 8.875V7.375H20.5V8.875H9.5ZM3.5 12.75V11.25H20.5V12.75H3.5ZM9.5 16.625V15.125H20.5V16.625H9.5ZM3.5 20.5V19H20.5V20.5H3.5Z",
fill: "currentColor"
}
)),
add_row: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
fillRule: "evenodd",
clipRule: "evenodd",
d: "M4.025 10.2077C4.375 9.85775 4.80258 9.68275 5.30775 9.68275H6.5V11.1827H5.30775C5.21792 11.1827 5.14417 11.2116 5.0865 11.2693C5.02883 11.3269 5 11.4007 5 11.4905V16.1923C5 16.2821 5.02883 16.3558 5.0865 16.4135C5.14417 16.4712 5.21792 16.5 5.30775 16.5H18.6923C18.7821 16.5 18.8558 16.4712 18.9135 16.4135C18.9712 16.3558 19 16.2821 19 16.1923V11.4905C19 11.4007 18.9712 11.3269 18.9135 11.2693C18.8558 11.2116 18.7821 11.1827 18.6923 11.1827H17.6923V9.68275H18.6923C19.1974 9.68275 19.625 9.85775 19.975 10.2077C20.325 10.5577 20.5 10.9853 20.5 11.4905V16.1923C20.5 16.6974 20.325 17.125 19.975 17.475C19.625 17.825 19.1974 18 18.6923 18H5.30775C4.80258 18 4.375 17.825 4.025 17.475C3.675 17.125 3.5 16.6974 3.5 16.1923V11.4905C3.5 10.9853 3.675 10.5577 4.025 10.2077Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M11.3848 9.68275V7.5H12.8848V9.68275H15V11.1827H12.8848V13.2307H11.3848V11.1827H9.26925V9.68275H11.3848Z",
fill: "currentColor"
}
)),
add_column: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
fillRule: "evenodd",
clipRule: "evenodd",
d: "M9.70775 4.025C9.35775 4.375 9.18275 4.80258 9.18275 5.30775V6.5H10.6827V5.30775C10.6827 5.21792 10.7116 5.14417 10.7693 5.0865C10.8269 5.02883 10.9007 5 10.9905 5H15.6923C15.7821 5 15.8558 5.02883 15.9135 5.0865C15.9712 5.14417 16 5.21792 16 5.30775V18.6923C16 18.7821 15.9712 18.8558 15.9135 18.9135C15.8558 18.9712 15.7821 19 15.6923 19H10.9905C10.9007 19 10.8269 18.9712 10.7693 18.9135C10.7116 18.8558 10.6827 18.7821 10.6827 18.6923V17.6923H9.18275V18.6923C9.18275 19.1974 9.35775 19.625 9.70775 19.975C10.0577 20.325 10.4853 20.5 10.9905 20.5H15.6923C16.1974 20.5 16.625 20.325 16.975 19.975C17.325 19.625 17.5 19.1974 17.5 18.6923V5.30775C17.5 4.80258 17.325 4.375 16.975 4.025C16.625 3.675 16.1974 3.5 15.6923 3.5H10.9905C10.4853 3.5 10.0577 3.675 9.70775 4.025Z",
fill: "currentColor"
}
), /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.18275 11.3848H7V12.8848H9.18275V15H10.6827V12.8848H12.7307V11.3848H10.6827V9.26925H9.18275V11.3848Z",
fill: "currentColor"
}
)),
insert_col_left: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M13.0001 20.1152H8.67713H8.75388H8.69238H13.0001ZM7.19238 19.8075C7.19238 20.3127 7.36738 20.7402 7.71738 21.0902C8.06738 21.4402 8.49497 21.6152 9.00013 21.6152H12.6924C13.1975 21.6152 13.6251 21.4402 13.9751 21.0902C14.3251 20.7402 14.5001 20.3127 14.5001 19.8075V10.423H13.0001V19.8075C13.0001 19.8973 12.9713 19.9711 12.9136 20.0287C12.856 20.0864 12.7822 20.1152 12.6924 20.1152H9.00013C8.9103 20.1152 8.83655 20.0864 8.77888 20.0287C8.72122 19.9711 8.69238 19.8973 8.69238 19.8075V4.69223C8.26422 4.74357 7.90722 4.93299 7.62138 5.26048C7.33538 5.58798 7.19238 5.97548 7.19238 6.42298V19.8075ZM13.0001 2.49998V4.61523H10.8846V6.11523H13.0001V8.23073H14.5001V6.11523H16.6154V4.61523H14.5001V2.49998H13.0001Z",
fill: "currentColor"
}
)),
insert_row_above: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M20.1152 9.99987V14.3229V14.2461V14.3076V9.99987ZM19.8075 15.8076C20.3127 15.8076 20.7402 15.6326 21.0902 15.2826C21.4402 14.9326 21.6152 14.505 21.6152 13.9999V10.3076C21.6152 9.80245 21.4402 9.37487 21.0902 9.02487C20.7402 8.67487 20.3127 8.49987 19.8075 8.49987H10.423V9.99987H19.8075C19.8973 9.99987 19.9711 10.0287 20.0287 10.0864C20.0864 10.144 20.1152 10.2178 20.1152 10.3076V13.9999C20.1152 14.0897 20.0864 14.1635 20.0287 14.2211C19.9711 14.2788 19.8973 14.3076 19.8075 14.3076H4.69223C4.74357 14.7358 4.93299 15.0928 5.26048 15.3786C5.58798 15.6646 5.97548 15.8076 6.42298 15.8076H19.8075ZM2.49998 9.99987H4.61523V12.1154H6.11523V9.99987H8.23073V8.49987H6.11523V6.38462H4.61523V8.49987H2.49998V9.99987Z",
fill: "currentColor"
}
)),
insert_row_below: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M20.1152 14.1924V9.86939V9.94614V9.88464V14.1924ZM19.8075 8.38464C20.3127 8.38464 20.7402 8.55964 21.0902 8.90964C21.4402 9.25964 21.6152 9.68723 21.6152 10.1924V13.8846C21.6152 14.3898 21.4402 14.8174 21.0902 15.1674C20.7402 15.5174 20.3127 15.6924 19.8075 15.6924H10.423V14.1924H19.8075C19.8973 14.1924 19.9711 14.1636 20.0287 14.1059C20.0864 14.0482 20.1152 13.9745 20.1152 13.8846V10.1924C20.1152 10.1026 20.0864 10.0288 20.0287 9.97114C19.9711 9.91348 19.8973 9.88464 19.8075 9.88464H4.69223C4.74357 9.45648 4.93299 9.09948 5.26048 8.81364C5.58798 8.52764 5.97548 8.38464 6.42298 8.38464H19.8075ZM2.49998 14.1924H4.61523V12.0769H6.11523V14.1924H8.23073V15.6924H6.11523V17.8076H4.61523V15.6924H2.49998V14.1924Z",
fill: "currentColor"
}
)),
insert_col_right: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M10.8075 20.1152H15.1305H15.0537H15.1152H10.8075ZM16.6152 19.8075C16.6152 20.3127 16.4402 20.7402 16.0902 21.0902C15.7402 21.4402 15.3127 21.6152 14.8075 21.6152H11.1152C10.6101 21.6152 10.1825 21.4402 9.83248 21.0902C9.48248 20.7402 9.30748 20.3127 9.30748 19.8075V10.423H10.8075V19.8075C10.8075 19.8973 10.8363 19.9711 10.894 20.0287C10.9517 20.0864 11.0254 20.1152 11.1152 20.1152H14.8075C14.8973 20.1152 14.9711 20.0864 15.0287 20.0287C15.0864 19.9711 15.1152 19.8973 15.1152 19.8075V4.69223C15.5434 4.74357 15.9004 4.93299 16.1862 5.26048C16.4722 5.58798 16.6152 5.97548 16.6152 6.42298V19.8075ZM10.8075 2.49998V4.61523H12.923V6.11523H10.8075V8.23073H9.30748V6.11523H7.19223V4.61523H9.30748V2.49998H10.8075Z",
fill: "currentColor"
}
)),
check: /* @__PURE__ */ React__default.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React__default.createElement(
"path",
{
d: "M9.54983 17.6537L4.21533 12.3192L5.28433 11.25L9.54983 15.5155L18.7153 6.34998L19.7843 7.41923L9.54983 17.6537Z",
fill: "currentColor"
}
))
};
export {
defaultSvgIcons
};
@@ -0,0 +1,29 @@
import React__default from "react";
import { useNestedEditorContext, NestedLexicalEditor } from "../plugins/core/NestedLexicalEditor.js";
const ADMONITION_TYPES = ["note", "tip", "danger", "info", "caution"];
const AdmonitionDirectiveDescriptor = {
name: "admonition",
attributes: [],
hasChildren: true,
testNode(node) {
return ADMONITION_TYPES.includes(node.name);
},
Editor({ mdastNode }) {
var _a;
const {
config: { theme }
} = useNestedEditorContext();
return /* @__PURE__ */ React__default.createElement("div", { className: (_a = theme.admonition) == null ? void 0 : _a[mdastNode.name] }, /* @__PURE__ */ React__default.createElement(
NestedLexicalEditor,
{
block: true,
getContent: (node) => node.children,
getUpdatedMdastNode: (mdastNode2, children) => ({ ...mdastNode2, children })
}
));
}
};
export {
ADMONITION_TYPES,
AdmonitionDirectiveDescriptor
};
@@ -0,0 +1,33 @@
import React__default from "react";
import { useMdastNodeUpdater, NestedLexicalEditor } from "../plugins/core/NestedLexicalEditor.js";
import { PropertyPopover } from "../plugins/core/PropertyPopover.js";
import styles from "../styles/ui.module.css.js";
const GenericDirectiveEditor = ({ mdastNode, descriptor }) => {
const updateMdastNode = useMdastNodeUpdater();
const properties = React__default.useMemo(() => {
return descriptor.attributes.reduce((acc, attributeName) => {
var _a;
acc[attributeName] = ((_a = mdastNode.attributes) == null ? void 0 : _a[attributeName]) ?? "";
return acc;
}, {});
}, [mdastNode, descriptor]);
const onChange = React__default.useCallback(
(values) => {
updateMdastNode({ attributes: Object.fromEntries(Object.entries(values).filter(([, value]) => value !== "")) });
},
[updateMdastNode]
);
return /* @__PURE__ */ React__default.createElement("div", { className: mdastNode.type === "textDirective" ? styles.inlineEditor : styles.blockEditor }, descriptor.attributes.length == 0 && descriptor.hasChildren && mdastNode.type !== "textDirective" ? /* @__PURE__ */ React__default.createElement("span", { className: styles.genericComponentName }, mdastNode.name) : null, descriptor.attributes.length > 0 ? /* @__PURE__ */ React__default.createElement(PropertyPopover, { properties, title: mdastNode.name || "", onChange }) : null, descriptor.hasChildren ? /* @__PURE__ */ React__default.createElement(
NestedLexicalEditor,
{
block: mdastNode.type === "containerDirective",
getContent: (node) => node.children,
getUpdatedMdastNode: (mdastNode2, children) => {
return { ...mdastNode2, children };
}
}
) : /* @__PURE__ */ React__default.createElement("span", { className: styles.genericComponentName }, mdastNode.name));
};
export {
GenericDirectiveEditor
};
+267
View File
@@ -0,0 +1,267 @@
import { $isElementNode } from "lexical";
import { toMarkdown } from "mdast-util-to-markdown";
import { isMdastHTMLNode } from "./plugins/core/MdastHTMLNode.js";
import { mergeStyleAttributes } from "./utils/mergeStyleAttributes.js";
function isParent(node) {
return node.children instanceof Array;
}
function exportLexicalTreeToMdast({
root,
visitors,
jsxComponentDescriptors,
jsxIsAvailable,
addImportStatements = true
}) {
let unistRoot = null;
const referredComponents = /* @__PURE__ */ new Set();
const knownImportSources = /* @__PURE__ */ new Map();
visitors = visitors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
visit(root, null);
function registerReferredComponent(componentName, importStatement) {
referredComponents.add(componentName);
if (importStatement) {
knownImportSources.set(componentName, { ...importStatement });
}
}
function appendToParent(parentNode, node) {
if (unistRoot === null) {
unistRoot = node;
return unistRoot;
}
if (!isParent(parentNode)) {
throw new Error("Attempting to append children to a non-parent");
}
const siblings = parentNode.children;
const prevSibling = siblings.at(-1);
if (prevSibling) {
const joinVisitor = visitors.find((visitor) => {
var _a;
return (_a = visitor.shouldJoin) == null ? void 0 : _a.call(visitor, prevSibling, node);
});
if (joinVisitor) {
const joinedNode = joinVisitor.join(prevSibling, node);
siblings.splice(siblings.length - 1, 1, joinedNode);
return joinedNode;
}
}
siblings.push(node);
return node;
}
function visitChildren(lexicalNode, parentNode) {
lexicalNode.getChildren().forEach((lexicalChild) => {
visit(lexicalChild, parentNode);
});
}
function visit(lexicalNode, mdastParent, usedVisitors = null) {
var _a;
const visitor = visitors.find((visitor2, index) => {
var _a2;
if (usedVisitors == null ? void 0 : usedVisitors.has(index)) {
return false;
}
return (_a2 = visitor2.testLexicalNode) == null ? void 0 : _a2.call(visitor2, lexicalNode);
});
if (!visitor) {
throw new Error(`no lexical visitor found for ${lexicalNode.getType()}`, {
cause: lexicalNode
});
}
(_a = visitor.visitLexicalNode) == null ? void 0 : _a.call(visitor, {
lexicalNode,
mdastParent,
actions: {
addAndStepInto(type, props = {}, hasChildren = true) {
const newNode = {
type,
...props,
...hasChildren ? { children: [] } : {}
};
appendToParent(mdastParent, newNode);
if ($isElementNode(lexicalNode) && hasChildren) {
visitChildren(lexicalNode, newNode);
}
},
appendToParent,
visitChildren,
visit,
registerReferredComponent,
nextVisitor() {
visit(lexicalNode, mdastParent, (usedVisitors ?? /* @__PURE__ */ new Set()).add(visitors.indexOf(visitor)));
return;
}
}
});
}
if (unistRoot === null) {
throw new Error("traversal ended with no root element");
}
const importsMap = /* @__PURE__ */ new Map();
const defaultImportsMap = /* @__PURE__ */ new Map();
for (const componentName of referredComponents) {
const descriptor = jsxComponentDescriptors.find((descriptor2) => descriptor2.name === componentName) ?? knownImportSources.get(componentName) ?? jsxComponentDescriptors.find((descriptor2) => descriptor2.name === "*");
if (!descriptor) {
throw new Error(`Component ${componentName} is used but not imported`);
}
if (!descriptor.source) {
continue;
}
if (descriptor.defaultExport) {
defaultImportsMap.set(componentName, descriptor.source);
} else {
const { source } = descriptor;
const existing = importsMap.get(source);
if (existing) {
existing.push(componentName);
} else {
importsMap.set(source, [componentName]);
}
}
}
if (!addImportStatements) {
for (const [path, names] of importsMap.entries()) {
const cleaned = names.filter((n) => knownImportSources.has(n));
if (cleaned.length > 0) {
importsMap.set(path, cleaned);
} else {
importsMap.delete(path);
}
}
for (const key of defaultImportsMap.keys()) {
if (!knownImportSources.has(key)) {
defaultImportsMap.delete(key);
}
}
}
const imports = Array.from(importsMap).map(([source, componentNames]) => {
return {
type: "mdxjsEsm",
value: `import { ${componentNames.join(", ")} } from '${source}'`
};
});
imports.push(
...Array.from(defaultImportsMap).map(([componentName, source]) => {
return {
type: "mdxjsEsm",
value: `import ${componentName} from '${source}'`
};
})
);
const typedRoot = unistRoot;
const frontmatter = typedRoot.children.find((child) => child.type === "yaml");
if (frontmatter) {
typedRoot.children.splice(typedRoot.children.indexOf(frontmatter) + 1, 0, ...imports);
} else {
typedRoot.children.unshift(...imports);
}
fixWrappingWhitespace(typedRoot, []);
collapseNestedHtmlTags(typedRoot);
if (!jsxIsAvailable) {
convertUnderlineJsxToHtml(typedRoot);
}
return typedRoot;
}
function collapseNestedHtmlTags(node) {
if ("children" in node && node.children.length > 0) {
if (isMdastHTMLNode(node) && node.children.length === 1) {
const onlyChild = node.children[0];
if (onlyChild.type === "mdxJsxTextElement" && onlyChild.name === "span") {
onlyChild.attributes.forEach((attribute) => {
if (attribute.type === "mdxJsxAttribute") {
const parentAttribute = node.attributes.find((attr) => attr.type === "mdxJsxAttribute" && attr.name === attribute.name);
if (parentAttribute) {
if (attribute.name === "className") {
const mergedClassesSet = /* @__PURE__ */ new Set([
...parentAttribute.value.split(" "),
...attribute.value.split(" ")
]);
parentAttribute.value = Array.from(mergedClassesSet).join(" ");
} else if (attribute.name === "style") {
parentAttribute.value = mergeStyleAttributes(parentAttribute.value, attribute.value);
}
} else {
node.attributes.push(attribute);
}
}
});
node.children = onlyChild.children;
}
}
node.children.forEach((child) => {
collapseNestedHtmlTags(child);
});
}
}
function convertUnderlineJsxToHtml(node) {
if (Object.hasOwn(node, "children")) {
const nodeAsParent = node;
const newChildren = [];
nodeAsParent.children.forEach((child) => {
if (child.type === "mdxJsxTextElement" && child.name === "u") {
newChildren.push(...[{ type: "html", value: "<u>" }, ...child.children, { type: "html", value: "</u>" }]);
} else {
newChildren.push(child);
convertUnderlineJsxToHtml(child);
}
});
nodeAsParent.children = newChildren;
}
}
const TRAILING_WHITESPACE_REGEXP = /\s+$/;
const LEADING_WHITESPACE_REGEXP = /^\s+/;
function fixWrappingWhitespace(node, parentChain) {
if (node.type === "strong" || node.type === "emphasis") {
const lastChild = node.children.at(-1);
if ((lastChild == null ? void 0 : lastChild.type) === "text") {
const trailingWhitespace = TRAILING_WHITESPACE_REGEXP.exec(lastChild.value);
if (trailingWhitespace) {
lastChild.value = lastChild.value.replace(TRAILING_WHITESPACE_REGEXP, "");
const parent = parentChain.at(-1);
if (parent) {
parent.children.splice(parent.children.indexOf(node) + 1, 0, {
type: "text",
value: trailingWhitespace[0]
});
fixWrappingWhitespace(parent, parentChain.slice(0, -1));
}
}
}
const firstChild = node.children.at(0);
if ((firstChild == null ? void 0 : firstChild.type) === "text") {
const leadingWhitespace = LEADING_WHITESPACE_REGEXP.exec(firstChild.value);
if (leadingWhitespace) {
firstChild.value = firstChild.value.replace(LEADING_WHITESPACE_REGEXP, "");
const parent = parentChain.at(-1);
if (parent) {
parent.children.splice(parent.children.indexOf(node), 0, {
type: "text",
value: leadingWhitespace[0]
});
fixWrappingWhitespace(parent, parentChain.slice(0, -1));
}
}
}
}
if ("children" in node && node.children.length > 0) {
const nodeAsParent = node;
nodeAsParent.children.forEach((child) => {
fixWrappingWhitespace(child, [...parentChain, nodeAsParent]);
});
}
}
function exportMarkdownFromLexical({
root,
toMarkdownOptions,
toMarkdownExtensions,
visitors,
jsxComponentDescriptors,
jsxIsAvailable
}) {
return toMarkdown(exportLexicalTreeToMdast({ root, visitors, jsxComponentDescriptors, jsxIsAvailable }), {
extensions: toMarkdownExtensions,
...toMarkdownOptions
}) + "\n";
}
export {
exportLexicalTreeToMdast,
exportMarkdownFromLexical
};
+174
View File
@@ -0,0 +1,174 @@
import { fromMarkdown } from "mdast-util-from-markdown";
import { toMarkdown } from "mdast-util-to-markdown";
function isParent(node) {
return node.children instanceof Array;
}
class MarkdownParseError extends Error {
constructor(message, cause) {
super(message);
this.name = "MarkdownParseError";
this.cause = cause;
}
}
class UnrecognizedMarkdownConstructError extends Error {
constructor(message) {
super(message);
this.name = "UnrecognizedMarkdownConstructError";
}
}
function gatherMetadata(mdastNode) {
const importsMap = /* @__PURE__ */ new Map();
if (mdastNode.type !== "root") {
return {
importDeclarations: {}
};
}
const importStatements = mdastNode.children.filter((n) => n.type === "mdxjsEsm").filter((n) => n.value.startsWith("import "));
importStatements.forEach((imp) => {
var _a, _b;
(((_b = (_a = imp.data) == null ? void 0 : _a.estree) == null ? void 0 : _b.body) ?? []).forEach((declaration) => {
if (declaration.type !== "ImportDeclaration") {
return;
}
declaration.specifiers.forEach((specifier) => {
importsMap.set(specifier.local.name, {
source: `${declaration.source.value}`,
defaultExport: specifier.type === "ImportDefaultSpecifier"
});
});
});
});
return {
importDeclarations: Object.fromEntries(importsMap.entries())
};
}
function importMarkdownToLexical({
root,
markdown,
visitors,
syntaxExtensions,
mdastExtensions,
...descriptors
}) {
var _a;
let mdastRoot;
try {
mdastRoot = fromMarkdown(markdown, {
extensions: syntaxExtensions,
mdastExtensions
});
} catch (e) {
if (e instanceof Error) {
throw new MarkdownParseError(`Error parsing markdown: ${e.message}`, e);
} else {
throw new MarkdownParseError(`Error parsing markdown: ${e}`, e);
}
}
if (mdastRoot.children.length === 0) {
mdastRoot.children.push({ type: "paragraph", children: [] });
}
if (((_a = mdastRoot.children.at(-1)) == null ? void 0 : _a.type) !== "paragraph") {
mdastRoot.children.push({ type: "paragraph", children: [] });
}
importMdastTreeToLexical({ root, mdastRoot, visitors, ...descriptors });
}
function importMdastTreeToLexical({ root, mdastRoot, visitors, ...descriptors }) {
const formattingMap = /* @__PURE__ */ new WeakMap();
const styleMap = /* @__PURE__ */ new WeakMap();
const metaData = gatherMetadata(mdastRoot);
visitors = visitors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
function visitChildren(mdastNode, lexicalParent) {
if (!isParent(mdastNode)) {
throw new Error("Attempting to visit children of a non-parent");
}
mdastNode.children.forEach((child) => {
visit(child, lexicalParent, mdastNode);
});
}
function visit(mdastNode, lexicalParent, mdastParent, skipVisitors = null) {
const visitor = visitors.find((visitor2, index) => {
if (skipVisitors == null ? void 0 : skipVisitors.has(index)) {
return false;
}
if (typeof visitor2.testNode === "string") {
return visitor2.testNode === mdastNode.type;
}
return visitor2.testNode(mdastNode, descriptors);
});
if (!visitor) {
try {
throw new UnrecognizedMarkdownConstructError(`Unsupported markdown syntax: ${toMarkdown(mdastNode)}`);
} catch (_e) {
throw new UnrecognizedMarkdownConstructError(
`Parsing of the following markdown structure failed: ${JSON.stringify({
type: mdastNode.type,
name: "name" in mdastNode ? mdastNode.name : "N/A"
})}`
);
}
}
visitor.visitNode({
//@ts-expect-error root type is glitching
mdastNode,
lexicalParent,
mdastParent,
descriptors,
metaData,
actions: {
visitChildren,
nextVisitor() {
visit(mdastNode, lexicalParent, mdastParent, (skipVisitors ?? /* @__PURE__ */ new Set()).add(visitors.indexOf(visitor)));
},
addAndStepInto(lexicalNode) {
lexicalParent.append(lexicalNode);
if (isParent(mdastNode)) {
visitChildren(mdastNode, lexicalNode);
}
},
addFormatting(format, node) {
if (!node) {
if (isParent(mdastNode)) {
node = mdastNode;
}
}
if (node) {
formattingMap.set(node, format | (formattingMap.get(mdastParent) ?? 0));
}
},
removeFormatting(format, node) {
if (!node) {
if (isParent(mdastNode)) {
node = mdastNode;
}
}
if (node) {
formattingMap.set(node, format ^ (formattingMap.get(mdastParent) ?? 0));
}
},
getParentFormatting() {
return formattingMap.get(mdastParent) ?? 0;
},
addStyle(style, node) {
if (!node) {
if (isParent(mdastNode)) {
node = mdastNode;
}
}
if (node) {
styleMap.set(node, style);
}
},
getParentStyle() {
return styleMap.get(mdastParent) ?? "";
}
}
});
}
visit(mdastRoot, root, null);
}
export {
MarkdownParseError,
UnrecognizedMarkdownConstructError,
importMarkdownToLexical,
importMdastTreeToLexical
};
+3208
View File
File diff suppressed because it is too large Load Diff
+359
View File
@@ -0,0 +1,359 @@
import "./styles/globals.css.js";
export * from "@mdxeditor/gurx";
import { MDXEditor } from "./MDXEditor.js";
import { defaultSvgIcons } from "./defaultSvgIcons.js";
import { MarkdownParseError, UnrecognizedMarkdownConstructError, importMarkdownToLexical, importMdastTreeToLexical } from "./importMarkdownToLexical.js";
import { exportLexicalTreeToMdast, exportMarkdownFromLexical } from "./exportMarkdownFromLexical.js";
import { Appender, NESTED_EDITOR_UPDATED_COMMAND, activeEditor$, activeEditorSubscriptions$, activePlugins$, addActivePlugin$, addBottomAreaChild$, addComposerChild$, addEditorWrapper$, addExportVisitor$, addImportVisitor$, addLexicalNode$, addMdastExtension$, addNestedEditorChild$, addSyntaxExtension$, addToMarkdownExtension$, addTopAreaChild$, applyBlockType$, applyFormat$, autoFocus$, bottomAreaChildren$, codeBlockEditorDescriptors$, composerChildren$, contentEditableClassName$, contentEditableRef$, contentEditableWrapperElement$, convertSelectionToNode$, corePlugin, createActiveEditorSubscription$, createRootEditorSubscription$, currentBlockType$, currentFormat$, currentSelection$, directiveDescriptors$, editorInFocus$, editorRootElementRef$, editorWrapperElementRef$, editorWrappers$, exportVisitors$, historyState$, iconComponentFor$, importVisitors$, inFocus$, initialMarkdown$, initialMarkdownNormalize$, insertDecoratorNode$, insertMarkdown$, jsxComponentDescriptors$, jsxIsAvailable$, lexicalTheme$, markdown$, markdownErrorSignal$, markdownProcessingError$, markdownSourceEditorValue$, mdastExtensions$, muteChange$, nestedEditorChildren$, onBlur$, placeholder$, readOnly$, rootEditor$, rootEditorSubscriptions$, setMarkdown$, spellCheck$, syntaxExtensions$, toMarkdownExtensions$, toMarkdownOptions$, topAreaChildren$, translation$, useTranslation, usedLexicalNodes$, viewMode$ } from "./plugins/core/index.js";
import { ALL_HEADING_LEVELS, allowedHeadingLevels$, headingsPlugin } from "./plugins/headings/index.js";
import { insertThematicBreak$, thematicBreakPlugin } from "./plugins/thematic-break/index.js";
import { applyListType$, currentListType$, listsPlugin } from "./plugins/lists/index.js";
import { insertTable$, tablePlugin } from "./plugins/table/index.js";
import { disableAutoLink$, linkPlugin } from "./plugins/link/index.js";
import { INSERT_IMAGE_COMMAND, allowSetImageDimensions$, closeImageDialog$, disableImageResize$, disableImageSettingsButton$, editImageToolbarComponent$, imageAutocompleteSuggestions$, imageDialogState$, imagePlaceholder$, imagePlugin, imagePreviewHandler$, imageUploadHandler$, insertImage$, openEditImageDialog$, openNewImageDialog$, parseImageDimension, saveImage$ } from "./plugins/image/index.js";
import { frontmatterDialogOpen$, frontmatterPlugin, hasFrontmatter$, insertFrontmatter$, removeFrontmatter$ } from "./plugins/frontmatter/index.js";
import { quotePlugin } from "./plugins/quote/index.js";
import { maxLengthPlugin } from "./plugins/maxlength/index.js";
import { insertJsx$, isMdastJsxNode, jsxPlugin } from "./plugins/jsx/index.js";
import { GenericJsxEditor } from "./jsx-editors/GenericJsxEditor.js";
import { insertSandpack$, sandpackConfig$, sandpackPlugin } from "./plugins/sandpack/index.js";
import { SandpackEditor } from "./plugins/sandpack/SandpackEditor.js";
import { codeBlockLanguages$, codeMirrorAutoLoadLanguageSupport$, codeMirrorExtensions$, codeMirrorPlugin, insertCodeMirror$ } from "./plugins/codemirror/index.js";
import { COMMON_STATE_CONFIG_EXTENSIONS, CodeMirrorEditor } from "./plugins/codemirror/CodeMirrorEditor.js";
import { appendCodeBlockEditorDescriptor$, codeBlockPlugin, defaultCodeBlockLanguage$, insertCodeBlock$ } from "./plugins/codeblock/index.js";
import { directivesPlugin, insertDirective$ } from "./plugins/directives/index.js";
import { ADMONITION_TYPES, AdmonitionDirectiveDescriptor } from "./directive-editors/AdmonitionDirectiveDescriptor.js";
import { GenericDirectiveEditor } from "./directive-editors/GenericDirectiveEditor.js";
import { applyLinkChanges$, cancelLinkEdit$, linkAutocompleteSuggestions$, linkDialogPlugin, linkDialogState$, onClickLinkCallback$, onReadOnlyClickLinkCallback$, onWindowChange$, openLinkEditDialog$, removeLink$, showLinkTitleField$, switchFromPreviewToLinkEdit$, updateLink$ } from "./plugins/link-dialog/index.js";
import { toolbarClassName$, toolbarContents$, toolbarPlugin } from "./plugins/toolbar/index.js";
import { cmExtensions$, diffMarkdown$, diffSourcePlugin, readOnlyDiff$ } from "./plugins/diff-source/index.js";
import { markdownShortcutPlugin } from "./plugins/markdown-shortcut/index.js";
import { EmptyTextNodeIndex, MDX_FOCUS_SEARCH_NAME, MDX_SEARCH_NAME, debouncedIndexer$, editorSearchCursor$, editorSearchRanges$, editorSearchScrollableContent$, editorSearchTerm$, editorSearchTermDebounced$, editorSearchTextNodeIndex$, rangeSearchScan, searchOpen$, searchPlugin, useEditorSearch } from "./plugins/search/index.js";
import { BlockTypeSelect } from "./plugins/toolbar/components/BlockTypeSelect.js";
import { BoldItalicUnderlineToggles, StrikeThroughSupSubToggles } from "./plugins/toolbar/components/BoldItalicUnderlineToggles.js";
import { ChangeAdmonitionType, admonitionLabelsMap } from "./plugins/toolbar/components/ChangeAdmonitionType.js";
import { ChangeCodeMirrorLanguage } from "./plugins/toolbar/components/ChangeCodeMirrorLanguage.js";
import { CodeToggle } from "./plugins/toolbar/components/CodeToggle.js";
import { HighlightToggle } from "./plugins/toolbar/components/HighlightToggle.js";
import { CreateLink } from "./plugins/toolbar/components/CreateLink.js";
import { DiffSourceToggleWrapper } from "./plugins/toolbar/components/DiffSourceToggleWrapper.js";
import { InsertAdmonition } from "./plugins/toolbar/components/InsertAdmonition.js";
import { InsertCodeBlock } from "./plugins/toolbar/components/InsertCodeBlock.js";
import { InsertFrontmatter } from "./plugins/toolbar/components/InsertFrontmatter.js";
import { InsertImage } from "./plugins/toolbar/components/InsertImage.js";
import { InsertSandpack } from "./plugins/toolbar/components/InsertSandpack.js";
import { InsertTable } from "./plugins/toolbar/components/InsertTable.js";
import { InsertThematicBreak } from "./plugins/toolbar/components/InsertThematicBreak.js";
import { ListsToggle } from "./plugins/toolbar/components/ListsToggle.js";
import { ShowSandpackInfo } from "./plugins/toolbar/components/ShowSandpackInfo.js";
import { UndoRedo } from "./plugins/toolbar/components/UndoRedo.js";
import { KitchenSinkToolbar } from "./plugins/toolbar/components/KitchenSinkToolbar.js";
import { Button, ButtonOrDropdownButton, ButtonWithTooltip, ConditionalContents, MultipleChoiceToggleGroup, Root, Separator, SingleChoiceToggleGroup, SingleToggleGroup, ToggleSingleGroupWithItem, ToolbarToggleItem } from "./plugins/toolbar/primitives/toolbar.js";
import { DialogButton } from "./plugins/toolbar/primitives/DialogButton.js";
import { TooltipWrap } from "./plugins/toolbar/primitives/TooltipWrap.js";
import { Select, SelectButtonTrigger, SelectContent, SelectItem, SelectTrigger } from "./plugins/toolbar/primitives/select.js";
import { NestedEditorsContext, NestedLexicalEditor, useLexicalNodeRemove, useMdastNodeUpdater, useNestedEditorContext } from "./plugins/core/NestedLexicalEditor.js";
import { PropertyPopover } from "./plugins/core/PropertyPopover.js";
import { RemoteMDXEditorRealmProvider, remoteRealmPlugin, useRemoteMDXEditorRealm } from "./plugins/remote/index.js";
import { CAN_USE_DOM, IS_APPLE, controlOrMeta } from "./utils/detectMac.js";
import { always, call, compose, curry1to0, curry2to1, joinProc, noop, prop, tap, thrush } from "./utils/fp.js";
import { isPartOftheEditorUI } from "./utils/isPartOftheEditorUI.js";
import { fromWithinEditorRead, getSelectedNode, getSelectionAsMarkdown, getSelectionRectangle, getStateAsMarkdown } from "./utils/lexicalHelpers.js";
import { makeHslTransparent } from "./utils/makeHslTransparent.js";
import { uuidv4 } from "./utils/uuid4.js";
import { voidEmitter } from "./utils/voidEmitter.js";
import { RealmWithPlugins, realmPlugin } from "./RealmWithPlugins.js";
import { DEFAULT_FORMAT, IS_BOLD, IS_CODE, IS_HIGHLIGHT, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE } from "./FormatConstants.js";
import { lexicalTheme } from "./styles/lexicalTheme.js";
import * as lexical from "lexical";
import { htmlTags, isMdastHTMLNode } from "./plugins/core/MdastHTMLNode.js";
import { $createGenericHTMLNode, $isGenericHTMLNode, GenericHTMLNode, TYPE_NAME } from "./plugins/core/GenericHTMLNode.js";
import { $convertTableElement, $createTableNode, $isTableNode, TableNode } from "./plugins/table/TableNode.js";
import { $createImageNode, $isImageNode, ImageNode } from "./plugins/image/ImageNode.js";
import { $createFrontmatterNode, $isFrontmatterNode, FrontmatterNode } from "./plugins/frontmatter/FrontmatterNode.js";
import { $convertPreElement, $createCodeBlockNode, $isCodeBlockNode, CodeBlockNode, useCodeBlockEditorContext } from "./plugins/codeblock/CodeBlockNode.js";
import { $createDirectiveNode, $isDirectiveNode, DirectiveNode } from "./plugins/directives/DirectiveNode.js";
export {
$convertPreElement,
$convertTableElement,
$createCodeBlockNode,
$createDirectiveNode,
$createFrontmatterNode,
$createGenericHTMLNode,
$createImageNode,
$createTableNode,
$isCodeBlockNode,
$isDirectiveNode,
$isFrontmatterNode,
$isGenericHTMLNode,
$isImageNode,
$isTableNode,
ADMONITION_TYPES,
ALL_HEADING_LEVELS,
AdmonitionDirectiveDescriptor,
Appender,
BlockTypeSelect,
BoldItalicUnderlineToggles,
Button,
ButtonOrDropdownButton,
ButtonWithTooltip,
CAN_USE_DOM,
COMMON_STATE_CONFIG_EXTENSIONS,
ChangeAdmonitionType,
ChangeCodeMirrorLanguage,
CodeBlockNode,
CodeMirrorEditor,
CodeToggle,
ConditionalContents,
CreateLink,
DEFAULT_FORMAT,
DialogButton,
DiffSourceToggleWrapper,
DirectiveNode,
EmptyTextNodeIndex,
FrontmatterNode,
GenericDirectiveEditor,
GenericHTMLNode,
GenericJsxEditor,
HighlightToggle,
INSERT_IMAGE_COMMAND,
IS_APPLE,
IS_BOLD,
IS_CODE,
IS_HIGHLIGHT,
IS_ITALIC,
IS_STRIKETHROUGH,
IS_SUBSCRIPT,
IS_SUPERSCRIPT,
IS_UNDERLINE,
ImageNode,
InsertAdmonition,
InsertCodeBlock,
InsertFrontmatter,
InsertImage,
InsertSandpack,
InsertTable,
InsertThematicBreak,
KitchenSinkToolbar,
ListsToggle,
MDXEditor,
MDX_FOCUS_SEARCH_NAME,
MDX_SEARCH_NAME,
MarkdownParseError,
MultipleChoiceToggleGroup,
NESTED_EDITOR_UPDATED_COMMAND,
NestedEditorsContext,
NestedLexicalEditor,
PropertyPopover,
RealmWithPlugins,
RemoteMDXEditorRealmProvider,
Root,
SandpackEditor,
Select,
SelectButtonTrigger,
SelectContent,
SelectItem,
SelectTrigger,
Separator,
ShowSandpackInfo,
SingleChoiceToggleGroup,
SingleToggleGroup,
StrikeThroughSupSubToggles,
TYPE_NAME,
TableNode,
ToggleSingleGroupWithItem,
ToolbarToggleItem,
TooltipWrap,
UndoRedo,
UnrecognizedMarkdownConstructError,
activeEditor$,
activeEditorSubscriptions$,
activePlugins$,
addActivePlugin$,
addBottomAreaChild$,
addComposerChild$,
addEditorWrapper$,
addExportVisitor$,
addImportVisitor$,
addLexicalNode$,
addMdastExtension$,
addNestedEditorChild$,
addSyntaxExtension$,
addToMarkdownExtension$,
addTopAreaChild$,
admonitionLabelsMap,
allowSetImageDimensions$,
allowedHeadingLevels$,
always,
appendCodeBlockEditorDescriptor$,
applyBlockType$,
applyFormat$,
applyLinkChanges$,
applyListType$,
autoFocus$,
bottomAreaChildren$,
call,
cancelLinkEdit$,
closeImageDialog$,
cmExtensions$,
codeBlockEditorDescriptors$,
codeBlockLanguages$,
codeBlockPlugin,
codeMirrorAutoLoadLanguageSupport$,
codeMirrorExtensions$,
codeMirrorPlugin,
compose,
composerChildren$,
contentEditableClassName$,
contentEditableRef$,
contentEditableWrapperElement$,
controlOrMeta,
convertSelectionToNode$,
corePlugin,
createActiveEditorSubscription$,
createRootEditorSubscription$,
currentBlockType$,
currentFormat$,
currentListType$,
currentSelection$,
curry1to0,
curry2to1,
debouncedIndexer$,
defaultCodeBlockLanguage$,
defaultSvgIcons,
diffMarkdown$,
diffSourcePlugin,
directiveDescriptors$,
directivesPlugin,
disableAutoLink$,
disableImageResize$,
disableImageSettingsButton$,
editImageToolbarComponent$,
editorInFocus$,
editorRootElementRef$,
editorSearchCursor$,
editorSearchRanges$,
editorSearchScrollableContent$,
editorSearchTerm$,
editorSearchTermDebounced$,
editorSearchTextNodeIndex$,
editorWrapperElementRef$,
editorWrappers$,
exportLexicalTreeToMdast,
exportMarkdownFromLexical,
exportVisitors$,
fromWithinEditorRead,
frontmatterDialogOpen$,
frontmatterPlugin,
getSelectedNode,
getSelectionAsMarkdown,
getSelectionRectangle,
getStateAsMarkdown,
hasFrontmatter$,
headingsPlugin,
historyState$,
htmlTags,
iconComponentFor$,
imageAutocompleteSuggestions$,
imageDialogState$,
imagePlaceholder$,
imagePlugin,
imagePreviewHandler$,
imageUploadHandler$,
importMarkdownToLexical,
importMdastTreeToLexical,
importVisitors$,
inFocus$,
initialMarkdown$,
initialMarkdownNormalize$,
insertCodeBlock$,
insertCodeMirror$,
insertDecoratorNode$,
insertDirective$,
insertFrontmatter$,
insertImage$,
insertJsx$,
insertMarkdown$,
insertSandpack$,
insertTable$,
insertThematicBreak$,
isMdastHTMLNode,
isMdastJsxNode,
isPartOftheEditorUI,
joinProc,
jsxComponentDescriptors$,
jsxIsAvailable$,
jsxPlugin,
lexical,
lexicalTheme,
lexicalTheme$,
linkAutocompleteSuggestions$,
linkDialogPlugin,
linkDialogState$,
linkPlugin,
listsPlugin,
makeHslTransparent,
markdown$,
markdownErrorSignal$,
markdownProcessingError$,
markdownShortcutPlugin,
markdownSourceEditorValue$,
maxLengthPlugin,
mdastExtensions$,
muteChange$,
nestedEditorChildren$,
noop,
onBlur$,
onClickLinkCallback$,
onReadOnlyClickLinkCallback$,
onWindowChange$,
openEditImageDialog$,
openLinkEditDialog$,
openNewImageDialog$,
parseImageDimension,
placeholder$,
prop,
quotePlugin,
rangeSearchScan,
readOnly$,
readOnlyDiff$,
realmPlugin,
remoteRealmPlugin,
removeFrontmatter$,
removeLink$,
rootEditor$,
rootEditorSubscriptions$,
sandpackConfig$,
sandpackPlugin,
saveImage$,
searchOpen$,
searchPlugin,
setMarkdown$,
showLinkTitleField$,
spellCheck$,
switchFromPreviewToLinkEdit$,
syntaxExtensions$,
tablePlugin,
tap,
thematicBreakPlugin,
thrush,
toMarkdownExtensions$,
toMarkdownOptions$,
toolbarClassName$,
toolbarContents$,
toolbarPlugin,
topAreaChildren$,
translation$,
updateLink$,
useCodeBlockEditorContext,
useEditorSearch,
useLexicalNodeRemove,
useMdastNodeUpdater,
useNestedEditorContext,
useRemoteMDXEditorRealm,
useTranslation,
usedLexicalNodes$,
uuidv4,
viewMode$,
voidEmitter
};
+79
View File
@@ -0,0 +1,79 @@
import React__default from "react";
import { useMdastNodeUpdater, NestedLexicalEditor } from "../plugins/core/NestedLexicalEditor.js";
import { PropertyPopover } from "../plugins/core/PropertyPopover.js";
import styles from "../styles/ui.module.css.js";
const isExpressionValue = (value) => {
if (value !== null && typeof value === "object" && "type" in value && "value" in value && typeof value.value === "string") {
return true;
}
return false;
};
const isStringValue = (value) => typeof value === "string";
const isMdxJsxAttribute = (value) => {
if (value.type === "mdxJsxAttribute" && typeof value.name === "string") {
return true;
}
return false;
};
const GenericJsxEditor = ({ mdastNode, descriptor, PropertyEditor }) => {
const updateMdastNode = useMdastNodeUpdater();
const properties = React__default.useMemo(
() => descriptor.props.reduce((acc, { name }) => {
const attribute = mdastNode.attributes.find((attr) => isMdxJsxAttribute(attr) ? attr.name === name : false);
if (attribute) {
if (isExpressionValue(attribute.value)) {
acc[name] = attribute.value.value;
return acc;
}
if (isStringValue(attribute.value)) {
acc[name] = attribute.value;
return acc;
}
}
acc[name] = "";
return acc;
}, {}),
[mdastNode, descriptor]
);
const onChange = React__default.useCallback(
(values) => {
const updatedAttributes = Object.entries(values).reduce((acc, [name, value]) => {
if (value === "") {
return acc;
}
const property = descriptor.props.find((prop) => prop.name === name);
if ((property == null ? void 0 : property.type) === "expression") {
acc.push({
type: "mdxJsxAttribute",
name,
value: { type: "mdxJsxAttributeValueExpression", value }
});
return acc;
}
acc.push({
type: "mdxJsxAttribute",
name,
value
});
return acc;
}, []);
updateMdastNode({ attributes: updatedAttributes });
},
[mdastNode, updateMdastNode, descriptor]
);
const PropertyEditorComponent = PropertyEditor ?? PropertyPopover;
const shouldRenderComponentName = descriptor.props.length == 0 && descriptor.hasChildren && descriptor.kind === "flow";
return /* @__PURE__ */ React__default.createElement("div", { className: descriptor.kind === "text" ? styles.inlineEditor : styles.blockEditor }, shouldRenderComponentName ? /* @__PURE__ */ React__default.createElement("span", { className: styles.genericComponentName }, mdastNode.name ?? "Fragment") : null, descriptor.props.length > 0 ? /* @__PURE__ */ React__default.createElement(PropertyEditorComponent, { properties, title: mdastNode.name ?? "", onChange }) : null, descriptor.hasChildren ? /* @__PURE__ */ React__default.createElement(
NestedLexicalEditor,
{
block: descriptor.kind === "flow",
getContent: (node) => node.children,
getUpdatedMdastNode: (mdastNode2, children) => {
return { ...mdastNode2, children };
}
}
) : /* @__PURE__ */ React__default.createElement("span", { className: styles.genericComponentName }, mdastNode.name));
};
export {
GenericJsxEditor
};
+125
View File
@@ -0,0 +1,125 @@
import { factorySpace } from "micromark-factory-space";
import { markdownLineEnding } from "micromark-util-character";
import { codes, types } from "micromark-util-symbol";
function commentFromMarkdown(_options) {
return {
canContainEols: ["comment"],
enter: {
comment(_) {
this.buffer();
}
},
exit: {
comment(token) {
this.resume();
}
}
};
}
const tokenize = (effects, ok, nok) => {
return start;
function start(code) {
effects.enter("comment");
effects.consume(code);
return open;
}
function open(code) {
if (code === codes.exclamationMark) {
effects.consume(code);
return declarationOpen;
}
return nok(code);
}
function declarationOpen(code) {
if (code === codes.dash) {
effects.consume(code);
return commentOpen;
}
return nok(code);
}
function commentOpen(code) {
if (code === codes.dash) {
effects.consume(code);
return commentStart;
}
return nok(code);
}
function commentStart(code) {
if (code === codes.greaterThan) {
return nok(code);
}
if (markdownLineEnding(code)) {
return atLineEnding(code);
}
effects.enter(types.data);
if (code === codes.dash) {
effects.consume(code);
return commentStartDash;
}
return comment2(code);
}
function commentStartDash(code) {
if (code === codes.greaterThan) {
return nok(code);
}
return comment2(code);
}
function comment2(code) {
if (code === codes.eof) {
return nok(code);
}
if (code === codes.dash) {
effects.consume(code);
return commentClose;
}
if (markdownLineEnding(code)) {
effects.exit(types.data);
return atLineEnding(code);
}
effects.consume(code);
return comment2;
}
function atLineEnding(code) {
effects.enter(types.lineEnding);
effects.consume(code);
effects.exit(types.lineEnding);
return factorySpace(effects, afterPrefix, types.linePrefix);
}
function afterPrefix(code) {
if (markdownLineEnding(code)) {
return atLineEnding(code);
}
effects.enter(types.data);
return comment2(code);
}
function commentClose(code) {
if (code === codes.dash) {
effects.consume(code);
return end;
}
return comment2(code);
}
function end(code) {
if (code === codes.greaterThan) {
effects.exit(types.data);
effects.enter("commentEnd");
effects.consume(code);
effects.exit("commentEnd");
effects.exit("comment");
return ok(code);
}
if (code === codes.dash) {
effects.consume(code);
return end;
}
return comment2(code);
}
};
const comment = {
flow: { [60]: { tokenize, concrete: true } },
text: { [60]: { tokenize } }
};
export {
comment,
commentFromMarkdown
};
+188
View File
@@ -0,0 +1,188 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { useCellValue } from "@mdxeditor/gurx";
import { DecoratorNode } from "lexical";
import React__default from "react";
import { defaultCodeBlockLanguage$ } from "./index.js";
import { voidEmitter } from "../../utils/voidEmitter.js";
import { codeBlockEditorDescriptors$, NESTED_EDITOR_UPDATED_COMMAND } from "../core/index.js";
class CodeBlockNode extends DecoratorNode {
constructor(code, language, meta, key) {
super(key);
__publicField(this, "__code");
__publicField(this, "__meta");
__publicField(this, "__language");
__publicField(this, "__focusEmitter", voidEmitter());
__publicField(this, "setCode", (code) => {
if (code !== this.__code) {
this.getWritable().__code = code;
}
});
__publicField(this, "setMeta", (meta) => {
if (meta !== this.__meta) {
this.getWritable().__meta = meta;
}
});
__publicField(this, "setLanguage", (language) => {
if (language !== this.__language) {
this.getWritable().__language = language;
}
});
__publicField(this, "select", () => {
this.__focusEmitter.publish();
});
this.__code = code;
this.__meta = meta;
this.__language = language;
}
static getType() {
return "codeblock";
}
static clone(node) {
return new CodeBlockNode(node.__code, node.__language, node.__meta, node.__key);
}
afterCloneFrom(prevNode) {
super.afterCloneFrom(prevNode);
this.__code = prevNode.__code;
this.__meta = prevNode.__meta;
this.__language = prevNode.__language;
this.__focusEmitter = voidEmitter();
}
static importJSON(serializedNode) {
const { code, meta, language } = serializedNode;
return $createCodeBlockNode({
code,
language,
meta
});
}
static importDOM() {
return {
pre: () => {
return {
conversion: $convertPreElement,
priority: 3
};
}
};
}
exportJSON() {
return {
code: this.getCode(),
language: this.getLanguage(),
meta: this.getMeta(),
type: "codeblock",
version: 1
};
}
// View
createDOM(_config, _editor) {
return document.createElement("div");
}
updateDOM() {
return false;
}
getCode() {
return this.__code;
}
getMeta() {
return this.__meta;
}
getLanguage() {
return this.__language;
}
decorate(editor) {
return /* @__PURE__ */ React__default.createElement(
CodeBlockEditorContainer,
{
parentEditor: editor,
code: this.getCode(),
meta: this.getMeta(),
language: this.getLanguage(),
codeBlockNode: this,
nodeKey: this.getKey(),
focusEmitter: this.__focusEmitter
}
);
}
isInline() {
return false;
}
}
const CodeBlockEditorContext = React__default.createContext(null);
const CodeBlockEditorContextProvider = ({ parentEditor, lexicalNode, children }) => {
const contextValue = React__default.useMemo(() => {
return {
lexicalNode,
parentEditor,
setCode: (code) => {
parentEditor.update(() => {
lexicalNode.setCode(code);
setTimeout(() => {
parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
}, 0);
});
},
setLanguage: (language) => {
parentEditor.update(() => {
lexicalNode.setLanguage(language);
});
},
setMeta: (meta) => {
parentEditor.update(() => {
lexicalNode.setMeta(meta);
});
}
};
}, [lexicalNode, parentEditor]);
return /* @__PURE__ */ React__default.createElement(CodeBlockEditorContext.Provider, { value: contextValue }, children);
};
function useCodeBlockEditorContext() {
const context = React__default.useContext(CodeBlockEditorContext);
if (!context) {
throw new Error("useCodeBlockEditor must be used within a CodeBlockEditor");
}
return context;
}
const CodeBlockEditorContainer = (props) => {
const codeBlockEditorDescriptors = useCellValue(codeBlockEditorDescriptors$);
const defaultCodeBlockLanguage = useCellValue(defaultCodeBlockLanguage$);
let descriptor = codeBlockEditorDescriptors.sort((a, b) => b.priority - a.priority).find((descriptor2) => descriptor2.match(props.language || "", props.meta || ""));
descriptor ?? (descriptor = codeBlockEditorDescriptors.find((descriptor2) => descriptor2.match(defaultCodeBlockLanguage || "", props.meta || "")));
if (!descriptor) {
throw new Error(`No CodeBlockEditor registered for language=${props.language} meta=${props.meta}`);
}
const Editor = descriptor.Editor;
const { codeBlockNode: _, parentEditor: __, ...restProps } = props;
return /* @__PURE__ */ React__default.createElement(CodeBlockEditorContextProvider, { parentEditor: props.parentEditor, lexicalNode: props.codeBlockNode }, /* @__PURE__ */ React__default.createElement(Editor, { ...restProps }));
};
function $createCodeBlockNode(options) {
const { code = "", language = "", meta = "" } = options;
return new CodeBlockNode(code, language, meta);
}
function $isCodeBlockNode(node) {
return node instanceof CodeBlockNode;
}
function $convertPreElement(element) {
const preElement = element;
const code = preElement.textContent;
const classAttribute = element.getAttribute("class") ?? "";
const dataLanguageAttribute = element.getAttribute("data-language") ?? "";
const languageMatch = /language-(\w+)/.exec(classAttribute);
const language = languageMatch ? languageMatch[1] : dataLanguageAttribute;
const meta = preElement.getAttribute("data-meta") ?? "";
return {
node: $createCodeBlockNode({ code, language, meta })
};
}
export {
$convertPreElement,
$createCodeBlockNode,
$isCodeBlockNode,
CodeBlockNode,
useCodeBlockEditorContext
};
@@ -0,0 +1,14 @@
import { $isCodeBlockNode } from "./CodeBlockNode.js";
const CodeBlockVisitor = {
testLexicalNode: $isCodeBlockNode,
visitLexicalNode: ({ lexicalNode, actions }) => {
actions.addAndStepInto("code", {
value: lexicalNode.getCode(),
lang: lexicalNode.getLanguage(),
meta: lexicalNode.getMeta()
});
}
};
export {
CodeBlockVisitor
};
@@ -0,0 +1,22 @@
import { $createCodeBlockNode } from "./CodeBlockNode.js";
const MdastCodeVisitor = {
testNode: (node, { codeBlockEditorDescriptors }) => {
if (node.type === "code") {
const descriptor = codeBlockEditorDescriptors.find((descriptor2) => descriptor2.match(node.lang, node.meta));
return descriptor !== void 0;
}
return false;
},
visitNode({ mdastNode, actions }) {
actions.addAndStepInto(
$createCodeBlockNode({
code: mdastNode.value,
language: mdastNode.lang,
meta: mdastNode.meta
})
);
}
};
export {
MdastCodeVisitor
};
+46
View File
@@ -0,0 +1,46 @@
import { CodeBlockVisitor } from "./CodeBlockVisitor.js";
import { MdastCodeVisitor } from "./MdastCodeVisitor.js";
import { Appender, insertDecoratorNode$, codeBlockEditorDescriptors$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addActivePlugin$ } from "../core/index.js";
import { $createCodeBlockNode, CodeBlockNode } from "./CodeBlockNode.js";
import { $convertPreElement, $isCodeBlockNode, useCodeBlockEditorContext } from "./CodeBlockNode.js";
import { Cell, Signal, withLatestFrom, map } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
const defaultCodeBlockLanguage$ = Cell("");
const insertCodeBlock$ = Signal((r) => {
r.link(
r.pipe(
insertCodeBlock$,
withLatestFrom(defaultCodeBlockLanguage$),
map(
([payload, defaultCodeBlockLanguage]) => () => $createCodeBlockNode({ language: defaultCodeBlockLanguage, ...payload })
)
),
insertDecoratorNode$
);
});
const appendCodeBlockEditorDescriptor$ = Appender(codeBlockEditorDescriptors$);
const codeBlockPlugin = realmPlugin({
update(realm, params) {
realm.pub(defaultCodeBlockLanguage$, (params == null ? void 0 : params.defaultCodeBlockLanguage) ?? "");
},
init(realm, params) {
realm.pubIn({
[addActivePlugin$]: "codeblock",
[codeBlockEditorDescriptors$]: (params == null ? void 0 : params.codeBlockEditorDescriptors) ?? [],
[addImportVisitor$]: MdastCodeVisitor,
[addLexicalNode$]: CodeBlockNode,
[addExportVisitor$]: CodeBlockVisitor
});
}
});
export {
$convertPreElement,
$createCodeBlockNode,
$isCodeBlockNode,
CodeBlockNode,
appendCodeBlockEditorDescriptor$,
codeBlockPlugin,
defaultCodeBlockLanguage$,
insertCodeBlock$,
useCodeBlockEditorContext
};
@@ -0,0 +1,130 @@
import { useCellValues } from "@mdxeditor/gurx";
import React__default from "react";
import styles from "../../styles/ui.module.css.js";
import { useCodeBlockEditorContext } from "../codeblock/CodeBlockNode.js";
import { useTranslation, readOnly$, iconComponentFor$ } from "../core/index.js";
import { languages } from "@codemirror/language-data";
import { EditorState } from "@codemirror/state";
import { lineNumbers, keymap, EditorView } from "@codemirror/view";
import { indentWithTab } from "@codemirror/commands";
import { basicLight } from "cm6-theme-basic-light";
import { basicSetup } from "codemirror";
import { $setSelection } from "lexical";
import { codeMirrorExtensions$, codeMirrorAutoLoadLanguageSupport$, codeBlockLanguages$ } from "./index.js";
import { useCodeMirrorRef } from "../sandpack/useCodeMirrorRef.js";
import { Select } from "../toolbar/primitives/select.js";
const COMMON_STATE_CONFIG_EXTENSIONS = [];
const EMPTY_VALUE = "__EMPTY_VALUE__";
const CodeMirrorEditor = ({ language, nodeKey, code, focusEmitter }) => {
const t = useTranslation();
const { parentEditor, lexicalNode } = useCodeBlockEditorContext();
const [readOnly, codeMirrorExtensions, autoLoadLanguageSupport, iconComponentFor, codeBlockLanguages] = useCellValues(
readOnly$,
codeMirrorExtensions$,
codeMirrorAutoLoadLanguageSupport$,
iconComponentFor$,
codeBlockLanguages$
);
const codeMirrorRef = useCodeMirrorRef(nodeKey, "codeblock", language, focusEmitter);
const { setCode } = useCodeBlockEditorContext();
const editorViewRef = React__default.useRef(null);
const elRef = React__default.useRef(null);
const setCodeRef = React__default.useRef(setCode);
setCodeRef.current = setCode;
codeMirrorRef.current = {
getCodemirror: () => editorViewRef.current
};
React__default.useEffect(() => {
const el = elRef.current;
void (async () => {
const extensions = [
...codeMirrorExtensions,
basicSetup,
basicLight,
lineNumbers(),
keymap.of([indentWithTab]),
EditorView.lineWrapping,
EditorView.updateListener.of(({ state }) => {
setCodeRef.current(state.doc.toString());
}),
EditorView.domEventHandlers({
focus: () => {
parentEditor.update(() => {
$setSelection(null);
});
}
})
];
if (readOnly) {
extensions.push(EditorState.readOnly.of(true));
}
if (language !== "" && autoLoadLanguageSupport) {
const languageData = languages.find((l) => {
return l.name === language || l.alias.includes(language) || l.extensions.includes(language);
});
if (languageData) {
try {
const languageSupport = await languageData.load();
extensions.push(languageSupport.extension);
} catch (_e) {
console.warn("failed to load language support for", language);
}
}
}
el.innerHTML = "";
editorViewRef.current = new EditorView({
parent: el,
state: EditorState.create({ doc: code, extensions })
});
el.addEventListener("keydown", stopPropagationHandler);
})();
return () => {
var _a;
(_a = editorViewRef.current) == null ? void 0 : _a.destroy();
editorViewRef.current = null;
el.removeEventListener("keydown", stopPropagationHandler);
};
}, [readOnly, language, ...codeMirrorExtensions]);
return /* @__PURE__ */ React__default.createElement("div", { className: styles.codeMirrorWrapper }, /* @__PURE__ */ React__default.createElement("div", { className: styles.codeMirrorToolbar }, /* @__PURE__ */ React__default.createElement(
Select,
{
disabled: readOnly,
value: language,
onChange: (language2) => {
parentEditor.update(() => {
lexicalNode.setLanguage(language2 === EMPTY_VALUE ? "" : language2);
setTimeout(() => {
parentEditor.update(() => {
lexicalNode.getLatest().select();
});
});
});
},
triggerTitle: t("codeBlock.selectLanguage", "Select code block language"),
placeholder: t("codeBlock.inlineLanguage", "Language"),
items: Object.entries(codeBlockLanguages).map(([value, label]) => ({ value: value ? value : EMPTY_VALUE, label }))
}
), /* @__PURE__ */ React__default.createElement(
"button",
{
className: styles.iconButton,
type: "button",
disabled: readOnly,
title: t("codeblock.delete", "Delete code block"),
onClick: (e) => {
e.preventDefault();
parentEditor.update(() => {
lexicalNode.remove();
});
}
},
iconComponentFor("delete_small")
)), /* @__PURE__ */ React__default.createElement("div", { ref: elRef }));
};
function stopPropagationHandler(ev) {
ev.stopPropagation();
}
export {
COMMON_STATE_CONFIG_EXTENSIONS,
CodeMirrorEditor
};
+61
View File
@@ -0,0 +1,61 @@
import { realmPlugin } from "../../RealmWithPlugins.js";
import { Cell, Signal, map } from "@mdxeditor/gurx";
import { insertCodeBlock$, appendCodeBlockEditorDescriptor$ } from "../codeblock/index.js";
import { CodeMirrorEditor } from "./CodeMirrorEditor.js";
const codeBlockLanguages$ = Cell({
js: "JavaScript",
ts: "TypeScript",
tsx: "TypeScript (React)",
jsx: "JavaScript (React)",
css: "CSS"
});
const insertCodeMirror$ = Signal((r) => {
r.link(
r.pipe(
insertCodeMirror$,
map(({ language, code }) => {
return {
code,
language,
meta: ""
};
})
),
insertCodeBlock$
);
});
const codeMirrorExtensions$ = Cell([]);
const codeMirrorAutoLoadLanguageSupport$ = Cell(true);
const codeMirrorPlugin = realmPlugin({
update(r, params) {
r.pubIn({
[codeBlockLanguages$]: params == null ? void 0 : params.codeBlockLanguages,
[codeMirrorExtensions$]: (params == null ? void 0 : params.codeMirrorExtensions) ?? [],
[codeMirrorAutoLoadLanguageSupport$]: (params == null ? void 0 : params.autoLoadLanguageSupport) ?? true
});
},
init(r, params) {
r.pubIn({
[codeBlockLanguages$]: params == null ? void 0 : params.codeBlockLanguages,
[codeMirrorExtensions$]: (params == null ? void 0 : params.codeMirrorExtensions) ?? [],
[appendCodeBlockEditorDescriptor$]: buildCodeBlockDescriptor((params == null ? void 0 : params.codeBlockLanguages) ?? {}),
[codeMirrorAutoLoadLanguageSupport$]: (params == null ? void 0 : params.autoLoadLanguageSupport) ?? true
});
}
});
function buildCodeBlockDescriptor(codeBlockLanguages) {
return {
match(language, meta) {
return Object.hasOwn(codeBlockLanguages, language ?? "") && !meta;
},
priority: 1,
Editor: CodeMirrorEditor
};
}
export {
codeBlockLanguages$,
codeMirrorAutoLoadLanguageSupport$,
codeMirrorExtensions$,
codeMirrorPlugin,
insertCodeMirror$
};
+125
View File
@@ -0,0 +1,125 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { ElementNode, $applyNodeReplacement } from "lexical";
const TYPE_NAME = "generic-html";
class GenericHTMLNode extends ElementNode {
/**
* Constructs a new {@link GenericHTMLNode} with the specified MDAST HTML node as the object to edit.
*/
constructor(tag, type, attributes, key) {
super(key);
/** @internal */
__publicField(this, "__tag");
/** @internal */
__publicField(this, "__nodeType");
/** @internal */
__publicField(this, "__attributes");
this.__tag = tag;
this.__nodeType = type;
this.__attributes = attributes;
}
/** @internal */
static getType() {
return TYPE_NAME;
}
/** @internal */
static clone(node) {
return new GenericHTMLNode(node.__tag, node.__nodeType, node.__attributes, node.__key);
}
getTag() {
return this.__tag;
}
getNodeType() {
return this.__nodeType;
}
getAttributes() {
return this.__attributes;
}
updateAttributes(attributes) {
const self = this.getWritable();
self.__attributes = attributes;
}
getStyle() {
var _a;
return (_a = this.__attributes.find((attribute) => attribute.name === "style")) == null ? void 0 : _a.value;
}
// View
createDOM() {
const tag = this.__tag;
const element = document.createElement(tag);
this.__attributes.forEach((attribute) => {
element.setAttribute(attribute.name, attribute.value);
});
return element;
}
updateDOM() {
return false;
}
static importDOM() {
return {};
}
exportDOM(editor) {
const { element } = super.exportDOM(editor);
return {
element
};
}
static importJSON(serializedNode) {
const node = $createGenericHTMLNode(serializedNode.tag, serializedNode.mdxType, serializedNode.attributes);
node.setFormat(serializedNode.format);
node.setIndent(serializedNode.indent);
node.setDirection(serializedNode.direction);
return node;
}
exportJSON() {
return {
...super.exportJSON(),
tag: this.getTag(),
attributes: this.__attributes,
mdxType: this.__nodeType,
type: TYPE_NAME,
version: 1
};
}
/*
// Mutation
insertNewAfter(selection?: RangeSelection, restoreSelection = true): ParagraphNode | GenericHTMLNode {
const anchorOffset = selection ? selection.anchor.offset : 0
const newElement =
anchorOffset > 0 && anchorOffset < this.getTextContentSize() ? $createHeadingNode(this.getTag()) : $createParagraphNode()
const direction = this.getDirection()
newElement.setDirection(direction)
this.insertAfter(newElement, restoreSelection)
return newElement
}
collapseAtStart(): true {
const newElement = !this.isEmpty() ? $createHeadingNode(this.getTag()) : $createParagraphNode()
const children = this.getChildren()
children.forEach((child) => newElement.append(child))
this.replace(newElement)
return true
}*/
extractWithChild() {
return true;
}
isInline() {
return this.__nodeType === "mdxJsxTextElement";
}
}
function $createGenericHTMLNode(tag, type, attributes) {
return $applyNodeReplacement(new GenericHTMLNode(tag, type, attributes));
}
function $isGenericHTMLNode(node) {
return node instanceof GenericHTMLNode;
}
export {
$createGenericHTMLNode,
$isGenericHTMLNode,
GenericHTMLNode,
TYPE_NAME
};
@@ -0,0 +1,15 @@
import { $isGenericHTMLNode } from "./GenericHTMLNode.js";
const LexicalGenericHTMLVisitor = {
testLexicalNode: $isGenericHTMLNode,
visitLexicalNode({ actions, lexicalNode }) {
actions.addAndStepInto("mdxJsxTextElement", {
name: lexicalNode.getTag(),
type: lexicalNode.getNodeType(),
attributes: lexicalNode.getAttributes()
});
},
priority: -100
};
export {
LexicalGenericHTMLVisitor
};
@@ -0,0 +1,10 @@
import { $isLineBreakNode } from "lexical";
const LexicalLinebreakVisitor = {
testLexicalNode: $isLineBreakNode,
visitLexicalNode: ({ mdastParent, actions }) => {
actions.appendToParent(mdastParent, { type: "text", value: "\n" });
}
};
export {
LexicalLinebreakVisitor
};
@@ -0,0 +1,10 @@
import { $isParagraphNode } from "lexical";
const LexicalParagraphVisitor = {
testLexicalNode: $isParagraphNode,
visitLexicalNode: ({ actions }) => {
actions.addAndStepInto("paragraph");
}
};
export {
LexicalParagraphVisitor
};
+10
View File
@@ -0,0 +1,10 @@
import { $isRootNode } from "lexical";
const LexicalRootVisitor = {
testLexicalNode: $isRootNode,
visitLexicalNode: ({ actions }) => {
actions.addAndStepInto("root");
}
};
export {
LexicalRootVisitor
};
+160
View File
@@ -0,0 +1,160 @@
import { $isTextNode } from "lexical";
import { IS_UNDERLINE, IS_SUPERSCRIPT, IS_SUBSCRIPT, IS_ITALIC, IS_BOLD, IS_STRIKETHROUGH, IS_HIGHLIGHT, IS_CODE } from "../../FormatConstants.js";
function isMdastText(mdastNode) {
return mdastNode.type === "text";
}
const JOINABLE_TAGS = ["u", "span", "sub", "sup"];
const LexicalTextVisitor = {
shouldJoin: (prevNode, currentNode) => {
if (["text", "emphasis", "strong", "highlight"].includes(prevNode.type)) {
return prevNode.type === currentNode.type;
}
if (prevNode.type === "mdxJsxTextElement" && // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
currentNode.type === "mdxJsxTextElement" && JOINABLE_TAGS.includes(currentNode.name)) {
const currentMdxNode = currentNode;
return prevNode.name === currentMdxNode.name && JSON.stringify(prevNode.attributes) === JSON.stringify(currentMdxNode.attributes);
}
return false;
},
join(prevNode, currentNode) {
if (isMdastText(prevNode) && isMdastText(currentNode)) {
return {
type: "text",
value: prevNode.value + currentNode.value
};
} else {
return {
...prevNode,
children: [...prevNode.children, ...currentNode.children]
};
}
},
testLexicalNode: $isTextNode,
visitLexicalNode: ({ lexicalNode, mdastParent, actions }) => {
const previousSibling = lexicalNode.getPreviousSibling();
const prevFormat = $isTextNode(previousSibling) ? previousSibling.getFormat() : 0;
const textContent = lexicalNode.getTextContent();
const format = lexicalNode.getFormat();
const style = lexicalNode.getStyle();
let localParentNode = mdastParent;
if (style) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "span",
children: [],
attributes: [{ type: "mdxJsxAttribute", name: "style", value: style }]
});
}
if (prevFormat & format & IS_UNDERLINE) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "u",
children: [],
attributes: []
});
}
if (prevFormat & format & IS_SUPERSCRIPT) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "sup",
children: [],
attributes: []
});
}
if (prevFormat & format & IS_SUBSCRIPT) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "sub",
children: [],
attributes: []
});
}
if (prevFormat & format & IS_ITALIC) {
localParentNode = actions.appendToParent(localParentNode, {
type: "emphasis",
children: []
});
}
if (prevFormat & format & IS_BOLD) {
localParentNode = actions.appendToParent(localParentNode, {
type: "strong",
children: []
});
}
if (prevFormat & format & IS_STRIKETHROUGH) {
localParentNode = actions.appendToParent(localParentNode, {
type: "delete",
children: []
});
}
if (prevFormat & format & IS_HIGHLIGHT) {
localParentNode = actions.appendToParent(localParentNode, {
type: "highlight",
children: []
});
}
if (format & IS_UNDERLINE && !(prevFormat & IS_UNDERLINE)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "u",
children: [],
attributes: []
});
}
if (format & IS_SUPERSCRIPT && !(prevFormat & IS_SUPERSCRIPT)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "sup",
children: [],
attributes: []
});
}
if (format & IS_SUBSCRIPT && !(prevFormat & IS_SUBSCRIPT)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "mdxJsxTextElement",
name: "sub",
children: [],
attributes: []
});
}
if (format & IS_ITALIC && !(prevFormat & IS_ITALIC)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "emphasis",
children: []
});
}
if (format & IS_BOLD && !(prevFormat & IS_BOLD)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "strong",
children: []
});
}
if (format & IS_STRIKETHROUGH && !(prevFormat & IS_STRIKETHROUGH)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "delete",
children: []
});
}
if (format & IS_HIGHLIGHT && !(prevFormat & IS_HIGHLIGHT)) {
localParentNode = actions.appendToParent(localParentNode, {
type: "highlight",
children: []
});
}
if (format & IS_CODE) {
actions.appendToParent(localParentNode, {
type: "inlineCode",
value: textContent
});
return;
}
actions.appendToParent(localParentNode, {
type: "text",
value: textContent
});
}
};
export {
LexicalTextVisitor,
isMdastText
};
+10
View File
@@ -0,0 +1,10 @@
import { $createLineBreakNode } from "lexical";
const MdastBreakVisitor = {
testNode: "break",
visitNode: function({ lexicalParent }) {
lexicalParent.append($createLineBreakNode());
}
};
export {
MdastBreakVisitor
};
@@ -0,0 +1,81 @@
import { IS_ITALIC, IS_BOLD, IS_CODE, IS_STRIKETHROUGH, IS_HIGHLIGHT, IS_UNDERLINE, IS_SUPERSCRIPT, IS_SUBSCRIPT } from "../../FormatConstants.js";
import { $createTextNode } from "lexical";
function buildFormattingVisitors(tag, format) {
return [
{
testNode: (node) => node.type === "mdxJsxTextElement" && node.name === tag,
visitNode({ actions, mdastNode, lexicalParent }) {
actions.addFormatting(format);
actions.visitChildren(mdastNode, lexicalParent);
}
},
{
testNode: (node) => node.type === "html" && node.value === `<${tag}>`,
visitNode({ actions, mdastParent }) {
actions.addFormatting(format, mdastParent);
}
},
{
testNode: (node) => node.type === "html" && node.value === `</${tag}>`,
visitNode({ actions, mdastParent }) {
actions.removeFormatting(format, mdastParent);
}
}
];
}
const StrikeThroughVisitor = {
testNode: "delete",
visitNode({ mdastNode, actions, lexicalParent }) {
actions.addFormatting(IS_STRIKETHROUGH);
actions.visitChildren(mdastNode, lexicalParent);
}
};
const HighlightVisitor = {
testNode: "highlight",
visitNode({ mdastNode, actions, lexicalParent }) {
actions.addFormatting(IS_HIGHLIGHT);
actions.visitChildren(mdastNode, lexicalParent);
}
};
const MdCodeVisitor = {
testNode: "inlineCode",
visitNode({ mdastNode, actions }) {
actions.addAndStepInto($createTextNode(mdastNode.value).setFormat(actions.getParentFormatting() | IS_CODE));
}
};
const MdEmphasisVisitor = {
testNode: "emphasis",
visitNode({ mdastNode, actions, lexicalParent }) {
actions.addFormatting(IS_ITALIC);
actions.visitChildren(mdastNode, lexicalParent);
}
};
const MdStrongVisitor = {
testNode: "strong",
visitNode({ mdastNode, actions, lexicalParent }) {
actions.addFormatting(IS_BOLD);
actions.visitChildren(mdastNode, lexicalParent);
}
};
const formattingVisitors = [
// emphasis
MdEmphasisVisitor,
// strong
MdStrongVisitor,
// underline
...buildFormattingVisitors("u", IS_UNDERLINE),
// code
...buildFormattingVisitors("code", IS_CODE),
MdCodeVisitor,
// strikethrough
StrikeThroughVisitor,
// highlight
HighlightVisitor,
// superscript
...buildFormattingVisitors("sup", IS_SUPERSCRIPT),
// subscript
...buildFormattingVisitors("sub", IS_SUBSCRIPT)
];
export {
formattingVisitors
};
+120
View File
@@ -0,0 +1,120 @@
const MDX_NODE_TYPES = ["mdxJsxTextElement", "mdxJsxFlowElement"];
function isMdastHTMLNode(node) {
return MDX_NODE_TYPES.includes(node.type) && htmlTags.includes(node.name.toLowerCase());
}
const htmlTags = [
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hgroup",
"hr",
"html",
"i",
"iframe",
// 'img',
"input",
"ins",
"kbd",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr"
];
export {
htmlTags,
isMdastHTMLNode
};
+17
View File
@@ -0,0 +1,17 @@
import { $createGenericHTMLNode } from "./GenericHTMLNode.js";
import { isMdastHTMLNode } from "./MdastHTMLNode.js";
const MdastHTMLVisitor = {
testNode: isMdastHTMLNode,
visitNode: function({ mdastNode, actions, lexicalParent }) {
if (mdastNode.name === "span" && mdastNode.attributes.length === 1 && mdastNode.attributes[0].type === "mdxJsxAttribute" && mdastNode.attributes[0].name === "style") {
actions.addStyle(mdastNode.attributes[0].value, mdastNode);
actions.visitChildren(mdastNode, lexicalParent);
} else {
actions.addAndStepInto($createGenericHTMLNode(mdastNode.name, mdastNode.type, mdastNode.attributes));
}
},
priority: -100
};
export {
MdastHTMLVisitor
};
@@ -0,0 +1,15 @@
import { $createParagraphNode } from "lexical";
const lexicalTypesThatShouldSkipParagraphs = ["listitem", "quote", "admonition"];
const MdastParagraphVisitor = {
testNode: "paragraph",
visitNode: function({ mdastNode, lexicalParent, actions }) {
if (lexicalTypesThatShouldSkipParagraphs.includes(lexicalParent.getType())) {
actions.visitChildren(mdastNode, lexicalParent);
} else {
actions.addAndStepInto($createParagraphNode());
}
}
};
export {
MdastParagraphVisitor
};
+9
View File
@@ -0,0 +1,9 @@
const MdastRootVisitor = {
testNode: "root",
visitNode({ actions, mdastNode, lexicalParent }) {
actions.visitChildren(mdastNode, lexicalParent);
}
};
export {
MdastRootVisitor
};
+16
View File
@@ -0,0 +1,16 @@
import { $createTextNode } from "lexical";
const MdastTextVisitor = {
testNode: "text",
visitNode({ mdastNode, actions }) {
const node = $createTextNode(mdastNode.value);
node.setFormat(actions.getParentFormatting());
const style = actions.getParentStyle();
if (style !== "") {
node.setStyle(style);
}
actions.addAndStepInto(node);
}
};
export {
MdastTextVisitor
};
+213
View File
@@ -0,0 +1,213 @@
import { $addUpdateTag, $getNodeByKey, createEditor, $getRoot, FOCUS_COMMAND, COMMAND_PRIORITY_LOW, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_HIGH, KEY_BACKSPACE_COMMAND, COMMAND_PRIORITY_CRITICAL } from "lexical";
import React__default from "react";
import { NESTED_EDITOR_UPDATED_COMMAND, rootEditor$, importVisitors$, exportVisitors$, usedLexicalNodes$, jsxComponentDescriptors$, directiveDescriptors$, codeBlockEditorDescriptors$, jsxIsAvailable$, nestedEditorChildren$, lexicalTheme$, editorInFocus$ } from "./index.js";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import classNames from "classnames";
import { exportLexicalTreeToMdast } from "../../exportMarkdownFromLexical.js";
import { importMdastTreeToLexical } from "../../importMarkdownToLexical.js";
import styles from "../../styles/ui.module.css.js";
import { SharedHistoryPlugin } from "./SharedHistoryPlugin.js";
import { mergeRegister } from "@lexical/utils";
import { isPartOftheEditorUI } from "../../utils/isPartOftheEditorUI.js";
import { useRealm, useCellValues, usePublisher } from "@mdxeditor/gurx";
const NestedEditorsContext = React__default.createContext(void 0);
function useNestedEditorContext() {
const context = React__default.useContext(NestedEditorsContext);
if (!context) {
throw new Error("useNestedEditor must be used within a NestedEditorsProvider");
}
return context;
}
function useMdastNodeUpdater() {
const { parentEditor, mdastNode, lexicalNode } = useNestedEditorContext();
return function updateMdastNode(node) {
parentEditor.update(
() => {
$addUpdateTag("history-push");
const currentNode = $getNodeByKey(lexicalNode.getKey());
if (currentNode) {
currentNode.setMdastNode({ ...mdastNode, ...node });
}
},
{ discrete: true }
);
parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
};
}
function useLexicalNodeRemove() {
const { parentEditor, lexicalNode } = useNestedEditorContext();
return () => {
parentEditor.update(() => {
const node = $getNodeByKey(lexicalNode.getKey());
node.selectNext();
node.remove();
});
};
}
const NestedLexicalEditor = function(props) {
const { getContent, getUpdatedMdastNode, contentEditableProps, block = false } = props;
const { mdastNode, lexicalNode, focusEmitter } = useNestedEditorContext();
const updateMdastNode = useMdastNodeUpdater();
const removeNode = useLexicalNodeRemove();
const content = getContent(mdastNode);
const realm = useRealm();
const [
rootEditor,
importVisitors,
exportVisitors,
usedLexicalNodes,
jsxComponentDescriptors,
directiveDescriptors,
codeBlockEditorDescriptors,
jsxIsAvailable,
nestedEditorChildren,
lexicalTheme
] = useCellValues(
rootEditor$,
importVisitors$,
exportVisitors$,
usedLexicalNodes$,
jsxComponentDescriptors$,
directiveDescriptors$,
codeBlockEditorDescriptors$,
jsxIsAvailable$,
nestedEditorChildren$,
lexicalTheme$
);
const setEditorInFocus = usePublisher(editorInFocus$);
const [editor] = React__default.useState(() => {
const editor2 = createEditor({
nodes: usedLexicalNodes,
theme: realm.getValue(lexicalTheme$),
namespace: "NestedEditor"
});
return editor2;
});
React__default.useEffect(() => {
focusEmitter.subscribe(() => {
editor.focus();
});
}, [editor, focusEmitter]);
React__default.useEffect(() => {
editor.update(() => {
$getRoot().clear();
let theContent = content;
if (block) {
if (theContent.length === 0) {
theContent = [{ type: "paragraph", children: [] }];
}
} else {
theContent = [{ type: "paragraph", children: content }];
}
importMdastTreeToLexical({
root: $getRoot(),
mdastRoot: {
type: "root",
children: theContent
},
visitors: importVisitors,
directiveDescriptors,
codeBlockEditorDescriptors,
jsxComponentDescriptors
});
});
}, [editor, block, importVisitors]);
React__default.useEffect(() => {
function updateParentNode() {
editor.getEditorState().read(() => {
const mdast = exportLexicalTreeToMdast({
root: $getRoot(),
visitors: exportVisitors,
jsxComponentDescriptors,
jsxIsAvailable,
addImportStatements: false
});
const content2 = block ? mdast.children : mdast.children[0].children;
updateMdastNode(getUpdatedMdastNode(structuredClone(mdastNode), content2));
});
}
return mergeRegister(
editor.registerCommand(
FOCUS_COMMAND,
() => {
setEditorInFocus({ editorType: "lexical", rootNode: lexicalNode, editorRef: editor });
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
BLUR_COMMAND,
(payload) => {
const relatedTarget = payload.relatedTarget;
if (isPartOftheEditorUI(relatedTarget, rootEditor.getRootElement())) {
return false;
}
updateParentNode();
setEditorInFocus(null);
return true;
},
COMMAND_PRIORITY_EDITOR
),
// triggered by codemirror
editor.registerCommand(
NESTED_EDITOR_UPDATED_COMMAND,
() => {
updateParentNode();
return true;
},
COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
SELECTION_CHANGE_COMMAND,
() => {
setEditorInFocus({ editorType: "lexical", rootNode: lexicalNode, editorRef: editor });
return false;
},
COMMAND_PRIORITY_HIGH
),
editor.registerCommand(
KEY_BACKSPACE_COMMAND,
(_, editor2) => {
const editorElement = editor2.getRootElement();
if ((editorElement == null ? void 0 : editorElement.innerText) === "\n") {
removeNode();
return true;
}
return false;
},
COMMAND_PRIORITY_CRITICAL
)
);
}, [
block,
editor,
exportVisitors,
getUpdatedMdastNode,
jsxComponentDescriptors,
jsxIsAvailable,
lexicalNode,
mdastNode,
removeNode,
setEditorInFocus,
updateMdastNode,
rootEditor
]);
return /* @__PURE__ */ React__default.createElement(LexicalNestedComposer, { initialEditor: editor, initialTheme: lexicalTheme }, /* @__PURE__ */ React__default.createElement(
RichTextPlugin,
{
contentEditable: /* @__PURE__ */ React__default.createElement(ContentEditable, { ...contentEditableProps, className: classNames(styles.nestedEditor, contentEditableProps == null ? void 0 : contentEditableProps.className) }),
placeholder: null,
ErrorBoundary: LexicalErrorBoundary
}
), /* @__PURE__ */ React__default.createElement(SharedHistoryPlugin, null), nestedEditorChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })));
};
export {
NestedEditorsContext,
NestedLexicalEditor,
useLexicalNodeRemove,
useMdastNodeUpdater,
useNestedEditorContext
};
+50
View File
@@ -0,0 +1,50 @@
import * as RadixPopover from "@radix-ui/react-popover";
import React__default from "react";
import { useForm } from "react-hook-form";
import styles from "../../styles/ui.module.css.js";
import { useCellValue } from "@mdxeditor/gurx";
import { iconComponentFor$ } from "./index.js";
import { PopoverPortal, PopoverContent } from "./ui/PopoverUtils.js";
const PropertyPopover = ({ title, properties, onChange }) => {
const [open, setOpen] = React__default.useState(false);
const iconComponentFor = useCellValue(iconComponentFor$);
const { register, handleSubmit, reset } = useForm({ defaultValues: properties });
return /* @__PURE__ */ React__default.createElement(
RadixPopover.Root,
{
open,
onOpenChange: (v) => {
setOpen(v);
}
},
/* @__PURE__ */ React__default.createElement(RadixPopover.Trigger, { className: styles.iconButton }, /* @__PURE__ */ React__default.createElement("div", null, iconComponentFor("settings"))),
/* @__PURE__ */ React__default.createElement(PopoverPortal, null, /* @__PURE__ */ React__default.createElement(PopoverContent, null, /* @__PURE__ */ React__default.createElement(
"form",
{
onSubmit: (e) => {
void handleSubmit(onChange)(e);
setOpen(false);
e.preventDefault();
e.stopPropagation();
}
},
/* @__PURE__ */ React__default.createElement("h3", { className: styles.propertyPanelTitle }, title, " Attributes"),
/* @__PURE__ */ React__default.createElement("table", { className: styles.propertyEditorTable }, /* @__PURE__ */ React__default.createElement("thead", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", { className: styles.readOnlyColumnCell }, "Attribute"), /* @__PURE__ */ React__default.createElement("th", null, "Value"))), /* @__PURE__ */ React__default.createElement("tbody", null, Object.keys(properties).map((propName) => /* @__PURE__ */ React__default.createElement("tr", { key: propName }, /* @__PURE__ */ React__default.createElement("th", { className: styles.readOnlyColumnCell }, " ", propName, " "), /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement("input", { ...register(propName), className: styles.propertyEditorInput }))))), /* @__PURE__ */ React__default.createElement("tfoot", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("td", { colSpan: 2 }, /* @__PURE__ */ React__default.createElement("div", { className: styles.buttonsFooter }, /* @__PURE__ */ React__default.createElement("button", { type: "submit", className: styles.primaryButton }, "Save"), /* @__PURE__ */ React__default.createElement(
"button",
{
type: "reset",
className: styles.secondaryButton,
onClick: (e) => {
e.preventDefault();
reset(properties);
setOpen(false);
}
},
"Cancel"
))))))
)))
);
};
export {
PropertyPopover
};
@@ -0,0 +1,10 @@
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import React__default from "react";
import { historyState$ } from "./index.js";
import { useCellValue } from "@mdxeditor/gurx";
const SharedHistoryPlugin = () => {
return /* @__PURE__ */ React__default.createElement(HistoryPlugin, { externalHistoryState: useCellValue(historyState$) });
};
export {
SharedHistoryPlugin
};
+674
View File
@@ -0,0 +1,674 @@
import { realmPlugin } from "../../RealmWithPlugins.js";
import { createEmptyHistoryState } from "@lexical/react/LexicalHistoryPlugin.js";
import { $isHeadingNode } from "@lexical/rich-text";
import { $setBlocksType } from "@lexical/selection";
import { $findMatchingParent, $wrapNodeInElement, $insertNodeToNearestRoot } from "@lexical/utils";
import { Cell, Signal, withLatestFrom, filter, useCellValue, scan, map } from "@mdxeditor/gurx";
import { createCommand, FORMAT_TEXT_COMMAND, $isRootOrShadowRoot, $getRoot, $setSelection, $getSelection, $insertNodes, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_CRITICAL, DecoratorNode, $createParagraphNode, FOCUS_COMMAND, BLUR_COMMAND, $isRangeSelection, createEditor, ParagraphNode, TextNode } from "lexical";
import { gfmStrikethrough } from "micromark-extension-gfm-strikethrough";
import { gfmStrikethroughToMarkdown, gfmStrikethroughFromMarkdown } from "mdast-util-gfm-strikethrough";
import { highlightMarkToMarkdown, highlightMarkFromMarkdown } from "mdast-util-highlight-mark";
import { mdxJsxToMarkdown, mdxJsxFromMarkdown } from "mdast-util-mdx-jsx";
import { highlightMark } from "micromark-extension-highlight-mark";
import { mdxJsx } from "micromark-extension-mdx-jsx";
import { mdxMd } from "micromark-extension-mdx-md";
import { exportMarkdownFromLexical } from "../../exportMarkdownFromLexical.js";
import { importMarkdownToLexical, MarkdownParseError, UnrecognizedMarkdownConstructError } from "../../importMarkdownToLexical.js";
import { noop } from "../../utils/fp.js";
import { GenericHTMLNode } from "./GenericHTMLNode.js";
import { $createGenericHTMLNode, $isGenericHTMLNode, TYPE_NAME } from "./GenericHTMLNode.js";
import { LexicalGenericHTMLVisitor } from "./LexicalGenericHTMLNodeVisitor.js";
import { LexicalLinebreakVisitor } from "./LexicalLinebreakVisitor.js";
import { LexicalParagraphVisitor } from "./LexicalParagraphVisitor.js";
import { LexicalRootVisitor } from "./LexicalRootVisitor.js";
import { LexicalTextVisitor } from "./LexicalTextVisitor.js";
import { MdastBreakVisitor } from "./MdastBreakVisitor.js";
import { formattingVisitors } from "./MdastFormattingVisitor.js";
import { MdastHTMLVisitor } from "./MdastHTMLVisitor.js";
import { MdastParagraphVisitor } from "./MdastParagraphVisitor.js";
import { MdastRootVisitor } from "./MdastRootVisitor.js";
import { MdastTextVisitor } from "./MdastTextVisitor.js";
import { SharedHistoryPlugin } from "./SharedHistoryPlugin.js";
import { comment, commentFromMarkdown } from "../../mdastUtilHtmlComment.js";
import { lexicalTheme } from "../../styles/lexicalTheme.js";
const NESTED_EDITOR_UPDATED_COMMAND = createCommand("NESTED_EDITOR_UPDATED_COMMAND");
const rootEditor$ = Cell(null);
const activeEditor$ = Cell(null);
const contentEditableClassName$ = Cell("");
const spellCheck$ = Cell(true);
const readOnly$ = Cell(false, (r) => {
r.sub(r.pipe(readOnly$, withLatestFrom(rootEditor$)), ([readOnly, rootEditor]) => {
rootEditor == null ? void 0 : rootEditor.setEditable(!readOnly);
});
});
const placeholder$ = Cell("");
const autoFocus$ = Cell(false);
const inFocus$ = Cell(false);
const currentFormat$ = Cell(0);
const markdownProcessingError$ = Cell(null);
const markdownErrorSignal$ = Signal((r) => {
r.link(
r.pipe(
markdownProcessingError$,
filter((e) => e !== null)
),
markdownErrorSignal$
);
});
const applyFormat$ = Signal((r) => {
r.sub(r.pipe(applyFormat$, withLatestFrom(activeEditor$)), ([format, theEditor]) => {
theEditor == null ? void 0 : theEditor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
});
});
const currentSelection$ = Cell(null, (r) => {
r.sub(r.pipe(currentSelection$, withLatestFrom(activeEditor$)), ([selection, theEditor]) => {
if (!selection || !theEditor) {
return;
}
const anchorNode = selection.anchor.getNode();
let element = anchorNode.getKey() === "root" ? anchorNode : $findMatchingParent(anchorNode, (e) => {
const parent = e.getParent();
return parent !== null && $isRootOrShadowRoot(parent);
});
element ?? (element = anchorNode.getTopLevelElementOrThrow());
const elementKey = element.getKey();
const elementDOM = theEditor.getElementByKey(elementKey);
if (elementDOM !== null) {
const blockType = $isHeadingNode(element) ? element.getTag() : element.getType();
r.pub(currentBlockType$, blockType);
}
});
});
const initialMarkdown$ = Cell("");
const markdown$ = Cell("");
const initialMarkdownNormalize$ = Cell(false);
const markdownSignal$ = Signal((r) => {
r.link(markdown$, markdownSignal$);
r.sub(initialMarkdown$, (md) => {
r.pubIn({
[initialMarkdownNormalize$]: true,
[markdown$]: md
});
});
});
const mutableMarkdownSignal$ = Signal((r) => {
r.link(
r.pipe(
markdownSignal$,
withLatestFrom(muteChange$),
filter(([, muted]) => !muted),
map(([value]) => value)
),
mutableMarkdownSignal$
);
}, true);
const importVisitors$ = Cell([]);
const usedLexicalNodes$ = Cell([]);
const syntaxExtensions$ = Cell([]);
const mdastExtensions$ = Cell([]);
const exportVisitors$ = Cell([]);
const toMarkdownExtensions$ = Cell([]);
const toMarkdownOptions$ = Cell({});
const jsxIsAvailable$ = Cell(false);
const jsxComponentDescriptors$ = Cell([]);
const directiveDescriptors$ = Cell([]);
const codeBlockEditorDescriptors$ = Cell([]);
const editorRootElementRef$ = Cell(null);
const editorWrapperElementRef$ = Cell(null);
const contentEditableRef$ = Cell(null);
const addLexicalNode$ = Appender(usedLexicalNodes$);
const addImportVisitor$ = Appender(importVisitors$);
const addSyntaxExtension$ = Appender(syntaxExtensions$);
const addMdastExtension$ = Appender(mdastExtensions$);
const addExportVisitor$ = Appender(exportVisitors$);
const addToMarkdownExtension$ = Appender(toMarkdownExtensions$);
const muteChange$ = Cell(false);
const setMarkdown$ = Signal((r) => {
r.sub(
r.pipe(
setMarkdown$,
withLatestFrom(markdown$, rootEditor$, inFocus$),
filter(([newMarkdown, oldMarkdown]) => {
return newMarkdown.trim() !== oldMarkdown.trim();
})
),
([theNewMarkdownValue, , editor, inFocus]) => {
r.pub(muteChange$, true);
editor == null ? void 0 : editor.update(
() => {
$getRoot().clear();
tryImportingMarkdown(r, $getRoot(), theNewMarkdownValue);
if (!inFocus) {
$setSelection(null);
} else {
editor.focus();
}
},
{
onUpdate: () => {
r.pub(muteChange$, false);
}
}
);
}
);
});
const insertMarkdown$ = Signal((r) => {
r.sub(r.pipe(insertMarkdown$, withLatestFrom(activeEditor$, inFocus$)), ([markdownToInsert, editor, inFocus]) => {
editor == null ? void 0 : editor.update(() => {
const selection = $getSelection();
if (selection !== null) {
const importPoint = {
children: [],
append(node) {
this.children.push(node);
},
getType() {
return selection.getNodes()[0].getType();
}
};
tryImportingMarkdown(r, importPoint, markdownToInsert);
$insertNodes(importPoint.children);
}
if (!inFocus) {
$setSelection(null);
} else {
editor.focus();
}
});
});
});
function rebind() {
return scan((teardowns, [subs, activeEditorValue]) => {
teardowns.forEach((teardown) => {
if (!teardown) {
throw new Error("You have a subscription that does not return a teardown");
}
teardown();
});
return activeEditorValue ? subs.map((s) => s(activeEditorValue)) : [];
}, []);
}
const contentEditableWrapperElement$ = Cell(null);
const activeEditorSubscriptions$ = Cell([], (r) => {
r.pipe(r.combine(activeEditorSubscriptions$, activeEditor$), rebind());
});
const rootEditorSubscriptions$ = Cell([], (r) => {
r.pipe(r.combine(rootEditorSubscriptions$, rootEditor$), rebind());
});
const editorInFocus$ = Cell(null, noop, (prev, next) => {
return Boolean(prev && next && prev.editorRef === next.editorRef);
});
const onBlur$ = Signal();
const iconComponentFor$ = Cell((name) => {
throw new Error(`No icon component for ${name}`);
});
function Appender(cell$, init) {
return Signal((r, sig$) => {
r.changeWith(cell$, sig$, (values, newValue) => {
if (!Array.isArray(newValue)) {
newValue = [newValue];
}
let result = values;
for (const v of newValue) {
if (!values.includes(v)) {
result = [...result, v];
}
}
return result;
});
init == null ? void 0 : init(r, sig$);
});
}
function handleSelectionChange(r) {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
r.pubIn({
[currentSelection$]: selection,
[currentFormat$]: selection.format
});
}
}
const createRootEditorSubscription$ = Appender(rootEditorSubscriptions$, (r, sig$) => {
r.pub(sig$, [
(rootEditor) => {
return rootEditor.registerCommand(
SELECTION_CHANGE_COMMAND,
(_, theActiveEditor) => {
r.pubIn({
[activeEditor$]: theActiveEditor,
[inFocus$]: true
});
if (theActiveEditor._parentEditor === null) {
theActiveEditor.getEditorState().read(() => {
r.pub(editorInFocus$, {
rootNode: $getRoot(),
editorType: "lexical",
editorRef: theActiveEditor
});
});
}
handleSelectionChange(r);
return false;
},
COMMAND_PRIORITY_CRITICAL
);
},
// Export handler
(rootEditor) => {
return rootEditor.registerUpdateListener(({ dirtyElements, dirtyLeaves, editorState }) => {
const err = r.getValue(markdownProcessingError$);
if (err !== null) {
return;
}
if (dirtyElements.size === 0 && dirtyLeaves.size === 0) {
return;
}
let theNewMarkdownValue;
editorState.read(() => {
const lastChild = $getRoot().getLastChild();
if (lastChild instanceof DecoratorNode) {
rootEditor.update(
() => {
$getRoot().append($createParagraphNode());
},
{ discrete: true }
);
}
theNewMarkdownValue = exportMarkdownFromLexical({
root: $getRoot(),
visitors: r.getValue(exportVisitors$),
jsxComponentDescriptors: r.getValue(jsxComponentDescriptors$),
toMarkdownExtensions: r.getValue(toMarkdownExtensions$),
toMarkdownOptions: r.getValue(toMarkdownOptions$),
jsxIsAvailable: r.getValue(jsxIsAvailable$)
});
});
r.pub(markdown$, theNewMarkdownValue.trim());
r.pub(initialMarkdownNormalize$, false);
});
},
(rootEditor) => {
return rootEditor.registerCommand(
FOCUS_COMMAND,
() => {
r.pub(inFocus$, true);
return false;
},
COMMAND_PRIORITY_CRITICAL
);
}
/*
// Fixes select all when frontmatter is present
(rootEditor) => {
return rootEditor.registerCommand<KeyboardEvent>(
KEY_DOWN_COMMAND,
(event) => {
const { keyCode, ctrlKey, metaKey } = event
if (keyCode === 65 && controlOrMeta(metaKey, ctrlKey)) {
let shouldOverride = false
rootEditor.getEditorState().read(() => {
shouldOverride = $isDecoratorNode($getRoot().getFirstChild()) || $isDecoratorNode($getRoot().getLastChild())
})
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (shouldOverride) {
event.preventDefault()
event.stopImmediatePropagation()
rootEditor.update(() => {
const rootElement = rootEditor.getRootElement() as HTMLDivElement
window.getSelection()?.selectAllChildren(rootElement)
rootElement.focus({
preventScroll: true
})
})
return true
}
}
return false
},
COMMAND_PRIORITY_CRITICAL
)
}*/
]);
});
const createActiveEditorSubscription$ = Appender(activeEditorSubscriptions$, (r, sig$) => {
r.pub(sig$, [
(editor) => {
return editor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
handleSelectionChange(r);
});
});
},
(editor) => {
return editor.registerCommand(
BLUR_COMMAND,
(payload) => {
var _a;
const theRootEditor = r.getValue(rootEditor$);
if (theRootEditor) {
const movingOutside = !((_a = theRootEditor.getRootElement()) == null ? void 0 : _a.contains(payload.relatedTarget));
if (movingOutside) {
r.pubIn({
[inFocus$]: false,
[onBlur$]: payload
});
}
}
return false;
},
COMMAND_PRIORITY_CRITICAL
);
}
]);
});
function tryImportingMarkdown(r, node, markdownValue) {
try {
importMarkdownToLexical({
root: node,
visitors: r.getValue(importVisitors$),
mdastExtensions: r.getValue(mdastExtensions$),
markdown: markdownValue,
syntaxExtensions: r.getValue(syntaxExtensions$),
jsxComponentDescriptors: r.getValue(jsxComponentDescriptors$),
directiveDescriptors: r.getValue(directiveDescriptors$),
codeBlockEditorDescriptors: r.getValue(codeBlockEditorDescriptors$)
});
r.pub(markdownProcessingError$, null);
} catch (e) {
if (e instanceof MarkdownParseError || e instanceof UnrecognizedMarkdownConstructError) {
r.pubIn({
[markdown$]: markdownValue,
[markdownProcessingError$]: {
error: e.message,
source: markdownValue
}
});
} else {
throw e;
}
}
}
const composerChildren$ = Cell([]);
const addComposerChild$ = Appender(composerChildren$);
const topAreaChildren$ = Cell([]);
const addTopAreaChild$ = Appender(topAreaChildren$);
const bottomAreaChildren$ = Cell([]);
const addBottomAreaChild$ = Appender(bottomAreaChildren$);
const editorWrappers$ = Cell([]);
const addEditorWrapper$ = Appender(editorWrappers$);
const nestedEditorChildren$ = Cell([]);
const addNestedEditorChild$ = Appender(nestedEditorChildren$);
const historyState$ = Cell(createEmptyHistoryState());
const currentBlockType$ = Cell("");
const applyBlockType$ = Signal();
const convertSelectionToNode$ = Signal((r) => {
r.sub(r.pipe(convertSelectionToNode$, withLatestFrom(activeEditor$)), ([factory, editor]) => {
editor == null ? void 0 : editor.update(() => {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
$setBlocksType(selection, factory);
setTimeout(() => {
editor.focus();
});
}
});
});
});
const insertDecoratorNode$ = Signal((r) => {
r.sub(r.pipe(insertDecoratorNode$, withLatestFrom(activeEditor$)), ([nodeFactory, theEditor]) => {
theEditor == null ? void 0 : theEditor.focus(
() => {
theEditor.getEditorState().read(() => {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
theEditor.update(() => {
const node = nodeFactory();
if (node.isInline()) {
$insertNodes([node]);
if ($isRootOrShadowRoot(node.getParentOrThrow())) {
$wrapNodeInElement(node, $createParagraphNode).selectEnd();
}
} else {
$insertNodeToNearestRoot(node);
}
setTimeout(() => {
if ("select" in node && typeof node.select === "function") {
node.select();
}
});
});
setTimeout(() => {
theEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
});
}
});
},
{ defaultSelection: "rootEnd" }
);
});
});
const viewMode$ = Cell("rich-text", (r) => {
function currentNextViewMode() {
return scan(
(prev, next) => {
return {
current: prev.next,
next
};
},
{ current: "rich-text", next: "rich-text" }
);
}
r.sub(r.pipe(viewMode$, currentNextViewMode(), withLatestFrom(markdownSourceEditorValue$)), ([{ current }, markdownSourceFromEditor]) => {
if (current === "source" || current === "diff") {
r.pub(setMarkdown$, markdownSourceFromEditor);
}
});
r.sub(
r.pipe(
viewMode$,
currentNextViewMode(),
filter((mode) => mode.current === "rich-text"),
withLatestFrom(activeEditor$)
),
([, editor]) => {
editor == null ? void 0 : editor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
}
);
});
const markdownSourceEditorValue$ = Cell(
"",
(r) => {
r.link(markdown$, markdownSourceEditorValue$);
r.link(markdownSourceEditorValue$, markdownSignal$);
},
true
);
const activePlugins$ = Cell([]);
const addActivePlugin$ = Appender(activePlugins$);
const translation$ = Cell(() => {
throw new Error("No translation function provided");
});
const lexicalTheme$ = Cell(lexicalTheme);
const corePlugin = realmPlugin({
init(r, params) {
const initialMarkdown = (params == null ? void 0 : params.initialMarkdown) ?? "";
r.register(createRootEditorSubscription$);
r.register(createActiveEditorSubscription$);
r.register(markdownSignal$);
r.register(markdownSourceEditorValue$);
r.pubIn({
[initialMarkdown$]: (params == null ? void 0 : params.trim) ? initialMarkdown.trim() : initialMarkdown,
[iconComponentFor$]: params == null ? void 0 : params.iconComponentFor,
[addImportVisitor$]: [MdastRootVisitor, MdastParagraphVisitor, MdastTextVisitor, MdastBreakVisitor, ...formattingVisitors],
[addLexicalNode$]: [ParagraphNode, TextNode, GenericHTMLNode],
[addExportVisitor$]: [
LexicalRootVisitor,
LexicalParagraphVisitor,
LexicalTextVisitor,
LexicalLinebreakVisitor,
LexicalGenericHTMLVisitor
],
[contentEditableClassName$]: params == null ? void 0 : params.contentEditableClassName,
[spellCheck$]: params == null ? void 0 : params.spellCheck,
[toMarkdownOptions$]: params == null ? void 0 : params.toMarkdownOptions,
[autoFocus$]: params == null ? void 0 : params.autoFocus,
[placeholder$]: params == null ? void 0 : params.placeholder,
[readOnly$]: params == null ? void 0 : params.readOnly,
[translation$]: params == null ? void 0 : params.translation,
[addMdastExtension$]: [gfmStrikethroughFromMarkdown(), highlightMarkFromMarkdown],
[addSyntaxExtension$]: [gfmStrikethrough(), highlightMark()],
[addToMarkdownExtension$]: [mdxJsxToMarkdown(), gfmStrikethroughToMarkdown(), highlightMarkToMarkdown],
[lexicalTheme$]: (params == null ? void 0 : params.lexicalTheme) ?? lexicalTheme
});
r.singletonSub(markdownErrorSignal$, params == null ? void 0 : params.onError);
r.singletonSub(mutableMarkdownSignal$, (value) => {
params == null ? void 0 : params.onChange(value, r.getValue(initialMarkdownNormalize$));
});
r.singletonSub(onBlur$, params == null ? void 0 : params.onBlur);
if (!(params == null ? void 0 : params.suppressHtmlProcessing)) {
r.pubIn({
[addMdastExtension$]: [mdxJsxFromMarkdown(), commentFromMarkdown()],
[addSyntaxExtension$]: [mdxJsx(), mdxMd(), comment],
[addImportVisitor$]: MdastHTMLVisitor
});
}
if (!(params == null ? void 0 : params.suppressSharedHistory)) {
r.pub(addComposerChild$, SharedHistoryPlugin);
}
},
postInit(r, params) {
const newEditor = createEditor({
// ...(params?.editorState ? { editorState: params.editorState } : {}),
editable: (params == null ? void 0 : params.readOnly) !== true,
namespace: (params == null ? void 0 : params.lexicalEditorNamespace) ?? "MDXEditor",
nodes: [...r.getValue(usedLexicalNodes$), ...(params == null ? void 0 : params.additionalLexicalNodes) ?? []],
onError: (error) => {
throw error;
},
theme: r.getValue(lexicalTheme$)
});
if ((params == null ? void 0 : params.editorState) !== null) {
newEditor.update(() => {
const markdown = (params == null ? void 0 : params.initialMarkdown.trim()) ?? "";
tryImportingMarkdown(r, $getRoot(), markdown);
const autoFocusValue = params == null ? void 0 : params.autoFocus;
if (autoFocusValue) {
if (autoFocusValue === true) {
setTimeout(() => {
newEditor.focus(noop, { defaultSelection: "rootStart" });
});
return;
}
setTimeout(() => {
newEditor.focus(noop, {
defaultSelection: autoFocusValue.defaultSelection ?? "rootStart"
});
});
}
});
}
r.pub(rootEditor$, newEditor);
r.pub(activeEditor$, newEditor);
},
update(realm, params) {
realm.pubIn({
[contentEditableClassName$]: params == null ? void 0 : params.contentEditableClassName,
[spellCheck$]: params == null ? void 0 : params.spellCheck,
[toMarkdownOptions$]: params == null ? void 0 : params.toMarkdownOptions,
[autoFocus$]: params == null ? void 0 : params.autoFocus,
[placeholder$]: params == null ? void 0 : params.placeholder,
[readOnly$]: params == null ? void 0 : params.readOnly
});
realm.singletonSub(mutableMarkdownSignal$, (value) => {
params == null ? void 0 : params.onChange(value, realm.getValue(initialMarkdownNormalize$));
});
realm.singletonSub(onBlur$, params == null ? void 0 : params.onBlur);
realm.singletonSub(markdownErrorSignal$, params == null ? void 0 : params.onError);
}
});
function useTranslation() {
return useCellValue(translation$);
}
export {
$createGenericHTMLNode,
$isGenericHTMLNode,
Appender,
GenericHTMLNode,
NESTED_EDITOR_UPDATED_COMMAND,
TYPE_NAME,
activeEditor$,
activeEditorSubscriptions$,
activePlugins$,
addActivePlugin$,
addBottomAreaChild$,
addComposerChild$,
addEditorWrapper$,
addExportVisitor$,
addImportVisitor$,
addLexicalNode$,
addMdastExtension$,
addNestedEditorChild$,
addSyntaxExtension$,
addToMarkdownExtension$,
addTopAreaChild$,
applyBlockType$,
applyFormat$,
autoFocus$,
bottomAreaChildren$,
codeBlockEditorDescriptors$,
composerChildren$,
contentEditableClassName$,
contentEditableRef$,
contentEditableWrapperElement$,
convertSelectionToNode$,
corePlugin,
createActiveEditorSubscription$,
createRootEditorSubscription$,
currentBlockType$,
currentFormat$,
currentSelection$,
directiveDescriptors$,
editorInFocus$,
editorRootElementRef$,
editorWrapperElementRef$,
editorWrappers$,
exportVisitors$,
historyState$,
iconComponentFor$,
importVisitors$,
inFocus$,
initialMarkdown$,
initialMarkdownNormalize$,
insertDecoratorNode$,
insertMarkdown$,
jsxComponentDescriptors$,
jsxIsAvailable$,
lexicalTheme$,
markdown$,
markdownErrorSignal$,
markdownProcessingError$,
markdownSourceEditorValue$,
mdastExtensions$,
muteChange$,
nestedEditorChildren$,
onBlur$,
placeholder$,
readOnly$,
rootEditor$,
rootEditorSubscriptions$,
setMarkdown$,
spellCheck$,
syntaxExtensions$,
toMarkdownExtensions$,
toMarkdownOptions$,
topAreaChildren$,
translation$,
useTranslation,
usedLexicalNodes$,
viewMode$
};
@@ -0,0 +1,84 @@
import { useCombobox } from "downshift";
import React__default from "react";
import { Controller } from "react-hook-form";
import styles from "../../../styles/ui.module.css.js";
import { iconComponentFor$ } from "../index.js";
import { useCellValue } from "@mdxeditor/gurx";
const MAX_SUGGESTIONS = 20;
const DownshiftAutoComplete = (props) => {
if (props.suggestions.length > 0) {
return /* @__PURE__ */ React__default.createElement(DownshiftAutoCompleteWithSuggestions, { ...props });
} else {
return /* @__PURE__ */ React__default.createElement("input", { className: styles.textInput, size: 40, autoFocus: true, ...props.register(props.inputName) });
}
};
const DownshiftAutoCompleteWithSuggestions = ({
autofocus,
suggestions,
control,
inputName,
placeholder,
initialInputValue,
setValue
}) => {
const [items, setItems] = React__default.useState(suggestions.slice(0, MAX_SUGGESTIONS));
const iconComponentFor = useCellValue(iconComponentFor$);
const enableAutoComplete = suggestions.length > 0;
const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, highlightedIndex, getItemProps, selectedItem } = useCombobox({
initialInputValue,
onInputValueChange({ inputValue = "" }) {
setValue(inputName, inputValue);
inputValue = inputValue.toLowerCase() || "";
const matchingItems = [];
for (const suggestion of suggestions) {
if (suggestion.toLowerCase().includes(inputValue)) {
matchingItems.push(suggestion);
if (matchingItems.length >= MAX_SUGGESTIONS) {
break;
}
}
}
setItems(matchingItems);
},
items,
itemToString(item) {
return item ?? "";
}
});
const dropdownIsVisible = isOpen && items.length > 0;
return /* @__PURE__ */ React__default.createElement("div", { className: styles.downshiftAutocompleteContainer }, /* @__PURE__ */ React__default.createElement("div", { "data-visible-dropdown": dropdownIsVisible, className: styles.downshiftInputWrapper }, /* @__PURE__ */ React__default.createElement(
Controller,
{
name: inputName,
control,
render: ({ field }) => {
const downshiftSrcProps = getInputProps();
return /* @__PURE__ */ React__default.createElement(
"input",
{
...downshiftSrcProps,
name: field.name,
placeholder,
className: styles.downshiftInput,
size: 30,
"data-editor-dialog": true,
autoFocus: autofocus
}
);
}
}
), enableAutoComplete && /* @__PURE__ */ React__default.createElement("button", { "aria-label": "toggle menu", type: "button", ...getToggleButtonProps() }, iconComponentFor("arrow_drop_down"))), /* @__PURE__ */ React__default.createElement("div", { className: styles.downshiftAutocompleteContainer }, /* @__PURE__ */ React__default.createElement("ul", { ...getMenuProps(), "data-visible": dropdownIsVisible }, items.map((item, index) => /* @__PURE__ */ React__default.createElement(
"li",
{
"data-selected": selectedItem === item,
"data-highlighted": highlightedIndex === index,
key: `${item}${index}`,
...getItemProps({ item, index })
},
item
)))));
};
export {
DownshiftAutoComplete,
DownshiftAutoCompleteWithSuggestions
};
+18
View File
@@ -0,0 +1,18 @@
import React__default from "react";
import * as RadixPopover from "@radix-ui/react-popover";
import { editorRootElementRef$ } from "../index.js";
import styles from "../../../styles/ui.module.css.js";
import { useCellValue } from "@mdxeditor/gurx";
const PopoverPortal = (props) => {
const editorRootElementRef = useCellValue(editorRootElementRef$);
return /* @__PURE__ */ React__default.createElement(RadixPopover.Portal, { ...props, container: editorRootElementRef == null ? void 0 : editorRootElementRef.current });
};
const PopoverContent = React__default.forwardRef(
(props, ref) => {
return /* @__PURE__ */ React__default.createElement(RadixPopover.Content, { ...props, className: styles.popoverContent, sideOffset: 5, side: "top", ref }, /* @__PURE__ */ React__default.createElement("span", { className: styles.popoverArrow }, /* @__PURE__ */ React__default.createElement(RadixPopover.Arrow, null)), props.children);
}
);
export {
PopoverContent,
PopoverPortal
};
@@ -0,0 +1,13 @@
import React__default from "react";
import { DiffViewer } from "./DiffViewer.js";
import { SourceEditor } from "./SourceEditor.js";
import { markdownProcessingError$, viewMode$ } from "../core/index.js";
import styles from "../../styles/ui.module.css.js";
import { useCellValues } from "@mdxeditor/gurx";
const DiffSourceWrapper = ({ children }) => {
const [error, viewMode] = useCellValues(markdownProcessingError$, viewMode$);
return /* @__PURE__ */ React__default.createElement("div", { className: "mdxeditor-diff-source-wrapper" }, error ? /* @__PURE__ */ React__default.createElement("div", { className: styles.markdownParseError }, /* @__PURE__ */ React__default.createElement("p", null, error.error, "."), /* @__PURE__ */ React__default.createElement("p", null, "You can fix the errors in source mode and switch to rich text mode when you are ready.")) : null, /* @__PURE__ */ React__default.createElement("div", { className: "mdxeditor-rich-text-editor", style: { display: viewMode === "rich-text" && error == null ? "block" : "none" } }, children), viewMode === "diff" ? /* @__PURE__ */ React__default.createElement(DiffViewer, null) : null, viewMode === "source" ? /* @__PURE__ */ React__default.createElement(SourceEditor, null) : null);
};
export {
DiffSourceWrapper
};
+86
View File
@@ -0,0 +1,86 @@
import React__default from "react";
import { diffMarkdown$, readOnlyDiff$, cmExtensions$ } from "./index.js";
import { markdown$, readOnly$, markdownSourceEditorValue$, onBlur$ } from "../core/index.js";
import { MergeView } from "@codemirror/merge";
import { EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
import { COMMON_STATE_CONFIG_EXTENSIONS } from "./SourceEditor.js";
import { useRealm, useCellValues, usePublisher, useCellValue } from "@mdxeditor/gurx";
function setContent(view, content) {
if (view !== void 0) {
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: content } });
}
}
const DiffViewer = () => {
const realm = useRealm();
const [newMarkdown, oldMarkdown, readOnly, readOnlyDiff] = useCellValues(markdown$, diffMarkdown$, readOnly$, readOnlyDiff$);
const onUpdate = usePublisher(markdownSourceEditorValue$);
const elRef = React__default.useRef(null);
const cmMergeViewRef = React__default.useRef(null);
const cmExtensions = useCellValue(cmExtensions$);
const triggerOnBlur = usePublisher(onBlur$);
React__default.useEffect(() => {
return realm.sub(diffMarkdown$, (newDiffMarkdown) => {
var _a;
setContent((_a = cmMergeViewRef.current) == null ? void 0 : _a.a, newDiffMarkdown);
});
}, [realm]);
React__default.useEffect(() => {
return realm.sub(markdown$, (newMarkdown2) => {
var _a;
setContent((_a = cmMergeViewRef.current) == null ? void 0 : _a.b, newMarkdown2);
});
}, [realm]);
React__default.useEffect(() => {
const isReadOnly = readOnly || readOnlyDiff;
const revertParams = isReadOnly ? {
renderRevertControl: void 0,
revertControls: void 0
} : {
renderRevertControl: () => {
const el = document.createElement("button");
el.classList.add("cm-merge-revert");
el.appendChild(document.createTextNode("⮕"));
return el;
},
revertControls: "a-to-b"
};
cmMergeViewRef.current = new MergeView({
...revertParams,
parent: elRef.current,
orientation: "a-b",
gutter: true,
a: {
doc: oldMarkdown,
extensions: [...cmExtensions, ...COMMON_STATE_CONFIG_EXTENSIONS, EditorState.readOnly.of(true)]
},
b: {
doc: newMarkdown,
extensions: [
...cmExtensions,
...COMMON_STATE_CONFIG_EXTENSIONS,
EditorState.readOnly.of(isReadOnly),
EditorView.updateListener.of(({ state }) => {
const md = state.doc.toString();
onUpdate(md);
}),
EditorView.focusChangeEffect.of((_, focused) => {
if (!focused) {
triggerOnBlur(new FocusEvent("blur"));
}
return null;
})
]
}
});
return () => {
var _a;
(_a = cmMergeViewRef.current) == null ? void 0 : _a.destroy();
cmMergeViewRef.current = null;
};
}, [onUpdate, cmExtensions]);
return /* @__PURE__ */ React__default.createElement("div", { ref: elRef, className: "mdxeditor-diff-editor" });
};
export {
DiffViewer
};
@@ -0,0 +1,60 @@
import { markdown } from "@codemirror/lang-markdown";
import { EditorState } from "@codemirror/state";
import { lineNumbers, EditorView } from "@codemirror/view";
import { basicLight } from "cm6-theme-basic-light";
import { basicSetup } from "codemirror";
import React__default from "react";
import { cmExtensions$ } from "./index.js";
import { markdown$, readOnly$, markdownSourceEditorValue$, onBlur$ } from "../core/index.js";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
const COMMON_STATE_CONFIG_EXTENSIONS = [
basicSetup,
basicLight,
markdown(),
lineNumbers(),
EditorView.lineWrapping
];
const SourceEditor = () => {
const [markdown2, readOnly, cmExtensions] = useCellValues(markdown$, readOnly$, cmExtensions$);
const updateMarkdown = usePublisher(markdownSourceEditorValue$);
const triggerOnBlur = usePublisher(onBlur$);
const editorViewRef = React__default.useRef(null);
const ref = React__default.useCallback(
(el) => {
var _a;
if (el !== null) {
const extensions = [
// custom extensions should come first so that you can override the default extensions
...cmExtensions,
...COMMON_STATE_CONFIG_EXTENSIONS,
EditorView.updateListener.of(({ state }) => {
updateMarkdown(state.doc.toString());
}),
EditorView.focusChangeEffect.of((_, focused) => {
if (!focused) {
triggerOnBlur(new FocusEvent("blur"));
}
return null;
})
];
if (readOnly) {
extensions.push(EditorState.readOnly.of(true));
}
el.innerHTML = "";
editorViewRef.current = new EditorView({
parent: el,
state: EditorState.create({ doc: markdown2, extensions })
});
} else {
(_a = editorViewRef.current) == null ? void 0 : _a.destroy();
editorViewRef.current = null;
}
},
[markdown2, readOnly, updateMarkdown, cmExtensions, triggerOnBlur]
);
return /* @__PURE__ */ React__default.createElement("div", { ref, className: "cm-sourceView mdxeditor-source-editor" });
};
export {
COMMON_STATE_CONFIG_EXTENSIONS,
SourceEditor
};
+27
View File
@@ -0,0 +1,27 @@
import { viewMode$, addEditorWrapper$ } from "../core/index.js";
import { DiffSourceWrapper } from "./DiffSourceWrapper.js";
import { Cell } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
const diffMarkdown$ = Cell("");
const cmExtensions$ = Cell([]);
const readOnlyDiff$ = Cell(false);
const diffSourcePlugin = realmPlugin({
update: (r, params) => {
r.pub(diffMarkdown$, (params == null ? void 0 : params.diffMarkdown) ?? "");
},
init(r, params) {
r.pubIn({
[diffMarkdown$]: (params == null ? void 0 : params.diffMarkdown) ?? "",
[cmExtensions$]: (params == null ? void 0 : params.codeMirrorExtensions) ?? [],
[addEditorWrapper$]: DiffSourceWrapper,
[readOnlyDiff$]: (params == null ? void 0 : params.readOnlyDiff) ?? false,
[viewMode$]: (params == null ? void 0 : params.viewMode) ?? "rich-text"
});
}
});
export {
cmExtensions$,
diffMarkdown$,
diffSourcePlugin,
readOnlyDiff$
};
+113
View File
@@ -0,0 +1,113 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import React__default from "react";
import { DecoratorNode } from "lexical";
import { NestedEditorsContext } from "../core/NestedLexicalEditor.js";
import { voidEmitter } from "../../utils/voidEmitter.js";
import { useCellValues } from "@mdxeditor/gurx";
import { directiveDescriptors$ } from "../core/index.js";
class DirectiveNode extends DecoratorNode {
/**
* Constructs a new {@link DirectiveNode} with the specified MDAST directive node as the object to edit.
*/
constructor(mdastNode, key) {
super(key);
/** @internal */
__publicField(this, "__mdastNode");
/** @internal */
__publicField(this, "__focusEmitter", voidEmitter());
/**
* Focuses the direcitive editor.
*/
__publicField(this, "select", () => {
this.__focusEmitter.publish();
});
this.__mdastNode = mdastNode;
}
/** @internal */
static getType() {
return "directive";
}
/** @internal */
static clone(node) {
return new DirectiveNode(structuredClone(node.__mdastNode), node.__key);
}
/** @internal */
static importJSON(serializedNode) {
return $createDirectiveNode(serializedNode.mdastNode);
}
/**
* Returns the MDAST node that is being edited.
*/
getMdastNode() {
return this.__mdastNode;
}
/** @internal */
exportJSON() {
return {
mdastNode: structuredClone(this.__mdastNode),
type: "directive",
version: 1
};
}
/** @internal */
createDOM() {
return document.createElement(this.__mdastNode.type === "textDirective" ? "span" : "div");
}
/** @internal */
updateDOM() {
return false;
}
/**
* Sets a new MDAST node to edit.
*/
setMdastNode(mdastNode) {
this.getWritable().__mdastNode = mdastNode;
}
/** @internal */
decorate(parentEditor, config) {
return /* @__PURE__ */ React__default.createElement(
DirectiveEditorContainer,
{
lexicalNode: this,
mdastNode: this.getMdastNode(),
parentEditor,
config,
focusEmitter: this.__focusEmitter
}
);
}
/** @internal */
isInline() {
return this.__mdastNode.type === "textDirective";
}
/** @internal */
isKeyboardSelectable() {
return true;
}
}
const DirectiveEditorContainer = (props) => {
const { mdastNode } = props;
const [directiveDescriptors] = useCellValues(directiveDescriptors$);
const descriptor = directiveDescriptors.find((descriptor2) => descriptor2.testNode(mdastNode));
if (!descriptor) {
throw new Error(`No descriptor found for directive ${mdastNode.name}`);
}
const Editor = descriptor.Editor;
return /* @__PURE__ */ React__default.createElement(NestedEditorsContext.Provider, { value: props }, /* @__PURE__ */ React__default.createElement(Editor, { descriptor, mdastNode, lexicalNode: props.lexicalNode, parentEditor: props.parentEditor }));
};
function $createDirectiveNode(mdastNode, key) {
return new DirectiveNode(mdastNode, key);
}
function $isDirectiveNode(node) {
return node instanceof DirectiveNode;
}
export {
$createDirectiveNode,
$isDirectiveNode,
DirectiveNode
};
@@ -0,0 +1,10 @@
import { $isDirectiveNode } from "./DirectiveNode.js";
const DirectiveVisitor = {
testLexicalNode: $isDirectiveNode,
visitLexicalNode({ actions, mdastParent, lexicalNode }) {
actions.appendToParent(mdastParent, lexicalNode.getMdastNode());
}
};
export {
DirectiveVisitor
};
@@ -0,0 +1,30 @@
import { $createTextNode } from "lexical";
import { $createDirectiveNode } from "./DirectiveNode.js";
const DIRECTIVE_TYPES = ["leafDirective", "containerDirective", "textDirective"];
function isMdastDirectivesNode(node) {
return DIRECTIVE_TYPES.includes(node.type);
}
const MdastDirectiveVisitor = (escapeUnknownTextDirectives) => ({
testNode: (node, { directiveDescriptors }) => {
if (isMdastDirectivesNode(node)) {
const descriptor = directiveDescriptors.find((descriptor2) => descriptor2.testNode(node));
if (escapeUnknownTextDirectives && !descriptor && node.type === "textDirective") {
return true;
}
return descriptor !== void 0;
}
return false;
},
visitNode({ lexicalParent, mdastNode, descriptors }) {
const isKnown = !escapeUnknownTextDirectives || descriptors.directiveDescriptors.some((d) => d.testNode(mdastNode));
if (isKnown) {
lexicalParent.append($createDirectiveNode(mdastNode));
} else {
lexicalParent.append($createTextNode(`:${mdastNode.name}`));
}
}
});
export {
MdastDirectiveVisitor,
isMdastDirectivesNode
};
+45
View File
@@ -0,0 +1,45 @@
import { realmPlugin } from "../../RealmWithPlugins.js";
import { insertDecoratorNode$, addToMarkdownExtension$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addSyntaxExtension$, addMdastExtension$, directiveDescriptors$ } from "../core/index.js";
import { Signal, map } from "@mdxeditor/gurx";
import { directiveToMarkdown, directiveFromMarkdown } from "mdast-util-directive";
import { directive } from "micromark-extension-directive";
import { $createDirectiveNode, DirectiveNode } from "./DirectiveNode.js";
import { $isDirectiveNode } from "./DirectiveNode.js";
import { DirectiveVisitor } from "./DirectiveVisitor.js";
import { MdastDirectiveVisitor } from "./MdastDirectiveVisitor.js";
const insertDirective$ = Signal((r) => {
r.link(
r.pipe(
insertDirective$,
map((payload) => {
return () => $createDirectiveNode({ children: [], ...payload });
})
),
insertDecoratorNode$
);
});
const directivesPlugin = realmPlugin({
update: (realm, params) => {
realm.pub(directiveDescriptors$, (params == null ? void 0 : params.directiveDescriptors) ?? []);
},
init: (realm, params) => {
realm.pubIn({
[directiveDescriptors$]: (params == null ? void 0 : params.directiveDescriptors) ?? [],
// import
[addMdastExtension$]: directiveFromMarkdown(),
[addSyntaxExtension$]: directive(),
[addImportVisitor$]: MdastDirectiveVisitor(params == null ? void 0 : params.escapeUnknownTextDirectives),
// export
[addLexicalNode$]: DirectiveNode,
[addExportVisitor$]: DirectiveVisitor,
[addToMarkdownExtension$]: directiveToMarkdown()
});
}
});
export {
$createDirectiveNode,
$isDirectiveNode,
DirectiveNode,
directivesPlugin,
insertDirective$
};
@@ -0,0 +1,107 @@
import * as Dialog from "@radix-ui/react-dialog";
import classNames from "classnames";
import YamlParser from "js-yaml";
import React__default from "react";
import { useForm, useFieldArray } from "react-hook-form";
import { frontmatterDialogOpen$, removeFrontmatter$ } from "./index.js";
import styles from "../../styles/ui.module.css.js";
import { readOnly$, editorRootElementRef$, iconComponentFor$, useTranslation } from "../core/index.js";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
const FrontmatterEditor = ({ yaml, onChange }) => {
const [readOnly, editorRootElementRef, iconComponentFor, frontmatterDialogOpen] = useCellValues(
readOnly$,
editorRootElementRef$,
iconComponentFor$,
frontmatterDialogOpen$
);
const t = useTranslation();
const setFrontmatterDialogOpen = usePublisher(frontmatterDialogOpen$);
const removeFrontmatter = usePublisher(removeFrontmatter$);
const yamlConfig = React__default.useMemo(() => {
if (!yaml) {
return [];
}
return Object.entries(YamlParser.load(yaml)).map(([key, value]) => ({ key, value }));
}, [yaml]);
const { register, control, handleSubmit } = useForm({
defaultValues: {
yamlConfig
}
});
const { fields, append, remove } = useFieldArray({
control,
name: "yamlConfig"
});
const onSubmit = React__default.useCallback(
({ yamlConfig: yamlConfig2 }) => {
if (yamlConfig2.length === 0) {
removeFrontmatter();
setFrontmatterDialogOpen(false);
return;
}
const yaml2 = yamlConfig2.reduce((acc, { key, value }) => {
if (key && value) {
acc[key] = value;
}
return acc;
}, {});
onChange(YamlParser.dump(yaml2).trim());
setFrontmatterDialogOpen(false);
},
[onChange, setFrontmatterDialogOpen, removeFrontmatter]
);
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
Dialog.Root,
{
open: frontmatterDialogOpen,
onOpenChange: (open) => {
setFrontmatterDialogOpen(open);
}
},
/* @__PURE__ */ React__default.createElement(Dialog.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(Dialog.Overlay, { className: styles.dialogOverlay }), /* @__PURE__ */ React__default.createElement(Dialog.Content, { className: styles.largeDialogContent, "data-editor-type": "frontmatter" }, /* @__PURE__ */ React__default.createElement(Dialog.Title, { className: styles.dialogTitle }, t("frontmatterEditor.title", "Edit document frontmatter")), /* @__PURE__ */ React__default.createElement(
"form",
{
onSubmit: (e) => {
void handleSubmit(onSubmit)(e);
e.stopPropagation();
},
onReset: (e) => {
e.stopPropagation();
setFrontmatterDialogOpen(false);
}
},
/* @__PURE__ */ React__default.createElement("table", { className: styles.propertyEditorTable }, /* @__PURE__ */ React__default.createElement("colgroup", null, /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null)), /* @__PURE__ */ React__default.createElement("thead", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", null, t("frontmatterEditor.key", "Key")), /* @__PURE__ */ React__default.createElement("th", null, t("frontmatterEditor.value", "Value")), /* @__PURE__ */ React__default.createElement("th", null))), /* @__PURE__ */ React__default.createElement("tbody", null, fields.map((item, index) => {
return /* @__PURE__ */ React__default.createElement("tr", { key: item.id }, /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(TableInput, { ...register(`yamlConfig.${index}.key`, { required: true }), autofocusIfEmpty: true, readOnly })), /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(TableInput, { ...register(`yamlConfig.${index}.value`, { required: true }), readOnly })), /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(
"button",
{
type: "button",
onClick: () => {
remove(index);
},
className: styles.iconButton,
disabled: readOnly
},
iconComponentFor("delete_big")
)));
})), /* @__PURE__ */ React__default.createElement("tfoot", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(
"button",
{
disabled: readOnly,
className: classNames(styles.primaryButton, styles.smallButton),
type: "button",
onClick: () => {
append({ key: "", value: "" });
}
},
t("frontmatterEditor.addEntry", "Add entry")
))))),
/* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement("button", { type: "submit", className: styles.primaryButton }, t("dialogControls.save", "Save")), /* @__PURE__ */ React__default.createElement("button", { type: "reset", className: styles.secondaryButton }, t("dialogControls.cancel", "Cancel")))
), /* @__PURE__ */ React__default.createElement(Dialog.Close, { asChild: true }, /* @__PURE__ */ React__default.createElement("button", { className: styles.dialogCloseButton, "aria-label": t("dialogControls.cancel", "Cancel") }, iconComponentFor("close")))))
));
};
const TableInput = React__default.forwardRef(({ className, autofocusIfEmpty: _, ...props }, ref) => {
return /* @__PURE__ */ React__default.createElement("input", { className: classNames(styles.propertyEditorInput, className), ...props, ref });
});
export {
FrontmatterEditor
};
@@ -0,0 +1,76 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { DecoratorNode } from "lexical";
import React__default from "react";
import { FrontmatterEditor } from "./FrontmatterEditor.js";
class FrontmatterNode extends DecoratorNode {
constructor(code, key) {
super(key);
__publicField(this, "__yaml");
this.__yaml = code;
}
static getType() {
return "frontmatter";
}
static clone(node) {
return new FrontmatterNode(node.__yaml, node.__key);
}
static importJSON(serializedNode) {
const { yaml } = serializedNode;
const node = $createFrontmatterNode(yaml);
return node;
}
exportJSON() {
return {
yaml: this.getYaml(),
type: "frontmatter",
version: 1
};
}
// View
createDOM(_config) {
return document.createElement("div");
}
updateDOM() {
return false;
}
getYaml() {
return this.getLatest().__yaml;
}
setYaml(yaml) {
if (yaml !== this.__yaml) {
this.getWritable().__yaml = yaml;
}
}
decorate(editor) {
return /* @__PURE__ */ React__default.createElement(
FrontmatterEditor,
{
yaml: this.getYaml(),
onChange: (yaml) => {
editor.update(() => {
this.setYaml(yaml);
});
}
}
);
}
isKeyboardSelectable() {
return false;
}
}
function $createFrontmatterNode(yaml) {
return new FrontmatterNode(yaml);
}
function $isFrontmatterNode(node) {
return node instanceof FrontmatterNode;
}
export {
$createFrontmatterNode,
$isFrontmatterNode,
FrontmatterNode
};
@@ -0,0 +1,10 @@
import { $isFrontmatterNode } from "./FrontmatterNode.js";
const LexicalFrontmatterVisitor = {
testLexicalNode: $isFrontmatterNode,
visitLexicalNode: ({ actions, lexicalNode }) => {
actions.addAndStepInto("yaml", { value: lexicalNode.getYaml() });
}
};
export {
LexicalFrontmatterVisitor
};
@@ -0,0 +1,10 @@
import { $createFrontmatterNode } from "./FrontmatterNode.js";
const MdastFrontmatterVisitor = {
testNode: "yaml",
visitNode({ mdastNode, actions }) {
actions.addAndStepInto($createFrontmatterNode(mdastNode.value));
}
};
export {
MdastFrontmatterVisitor
};
+113
View File
@@ -0,0 +1,113 @@
import { realmPlugin } from "../../RealmWithPlugins.js";
import { rootEditor$, createRootEditorSubscription$, addToMarkdownExtension$, addExportVisitor$, addImportVisitor$, addLexicalNode$, addSyntaxExtension$, addMdastExtension$ } from "../core/index.js";
import { Cell, Action, withLatestFrom } from "@mdxeditor/gurx";
import { $getRoot, KEY_DOWN_COMMAND, $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_CRITICAL } from "lexical";
import { frontmatterToMarkdown, frontmatterFromMarkdown } from "mdast-util-frontmatter";
import { frontmatter } from "micromark-extension-frontmatter";
import { $isFrontmatterNode, $createFrontmatterNode, FrontmatterNode } from "./FrontmatterNode.js";
import { LexicalFrontmatterVisitor } from "./LexicalFrontmatterVisitor.js";
import { MdastFrontmatterVisitor } from "./MdastFrontmatterVisitor.js";
const frontmatterDialogOpen$ = Cell(false);
const insertFrontmatter$ = Action((r) => {
r.sub(r.pipe(insertFrontmatter$, withLatestFrom(rootEditor$)), ([, rootEditor]) => {
rootEditor == null ? void 0 : rootEditor.update(() => {
const firstItem = $getRoot().getFirstChild();
if (!$isFrontmatterNode(firstItem)) {
const fmNode = $createFrontmatterNode('"": ""');
if (firstItem) {
firstItem.insertBefore(fmNode);
} else {
$getRoot().append(fmNode);
}
}
});
r.pub(frontmatterDialogOpen$, true);
});
});
const removeFrontmatter$ = Action((r) => {
r.sub(r.pipe(removeFrontmatter$, withLatestFrom(rootEditor$)), ([, rootEditor]) => {
rootEditor == null ? void 0 : rootEditor.update(() => {
const firstItem = $getRoot().getFirstChild();
if ($isFrontmatterNode(firstItem)) {
firstItem.remove();
}
});
r.pub(frontmatterDialogOpen$, false);
});
});
const hasFrontmatter$ = Cell(false, (r) => {
r.pub(createRootEditorSubscription$, (rootEditor) => {
return rootEditor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
r.pub(hasFrontmatter$, $isFrontmatterNode($getRoot().getFirstChild()));
});
});
});
});
const frontmatterPlugin = realmPlugin({
init: (realm) => {
realm.pubIn({
[addMdastExtension$]: frontmatterFromMarkdown("yaml"),
[addSyntaxExtension$]: frontmatter(),
[addLexicalNode$]: FrontmatterNode,
[addImportVisitor$]: MdastFrontmatterVisitor,
[addExportVisitor$]: LexicalFrontmatterVisitor,
[addToMarkdownExtension$]: frontmatterToMarkdown("yaml"),
[createRootEditorSubscription$]: (editor) => {
return editor.registerCommand(
KEY_DOWN_COMMAND,
(event) => {
let shouldPrevent = false;
editor.read(() => {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
if (selection.isCollapsed() && selection.anchor.offset === 0 && selection.focus.offset === 0 && event.key === "Backspace") {
let node = selection.getNodes()[0];
if ($isTextNode(node)) {
node = node.getParent();
}
const prevSibling = node == null ? void 0 : node.getPreviousSibling();
if ($isFrontmatterNode(prevSibling)) {
shouldPrevent = true;
event.preventDefault();
}
} else {
const firstNode = selection.getNodes()[0];
if ($isFrontmatterNode(firstNode)) {
const yaml = firstNode.getYaml();
setTimeout(() => {
editor.update(
() => {
const firstItem = $getRoot().getFirstChild();
if (!$isFrontmatterNode(firstItem)) {
$getRoot().splice(0, 0, [$createFrontmatterNode(yaml)]);
}
},
{ discrete: true }
);
});
}
}
}
});
if (shouldPrevent) {
return true;
}
return false;
},
COMMAND_PRIORITY_CRITICAL
);
}
});
}
});
export {
$createFrontmatterNode,
$isFrontmatterNode,
FrontmatterNode,
frontmatterDialogOpen$,
frontmatterPlugin,
hasFrontmatter$,
insertFrontmatter$,
removeFrontmatter$
};
@@ -0,0 +1,11 @@
import { $isHeadingNode } from "@lexical/rich-text";
const LexicalHeadingVisitor = {
testLexicalNode: $isHeadingNode,
visitLexicalNode: ({ lexicalNode, actions }) => {
const depth = parseInt(lexicalNode.getTag()[1], 10);
actions.addAndStepInto("heading", { depth });
}
};
export {
LexicalHeadingVisitor
};
@@ -0,0 +1,10 @@
import { $createHeadingNode } from "@lexical/rich-text";
const MdastHeadingVisitor = {
testNode: "heading",
visitNode: function({ mdastNode, actions }) {
actions.addAndStepInto($createHeadingNode(`h${mdastNode.depth}`));
}
};
export {
MdastHeadingVisitor
};
+63
View File
@@ -0,0 +1,63 @@
import { $createHeadingNode, HeadingNode } from "@lexical/rich-text";
import { Cell } from "@mdxeditor/gurx";
import { KEY_DOWN_COMMAND, $createParagraphNode, COMMAND_PRIORITY_LOW } from "lexical";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { controlOrMeta } from "../../utils/detectMac.js";
import { createRootEditorSubscription$, convertSelectionToNode$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addActivePlugin$ } from "../core/index.js";
import { LexicalHeadingVisitor } from "./LexicalHeadingVisitor.js";
import { MdastHeadingVisitor } from "./MdastHeadingVisitor.js";
const FORMATTING_KEYS = ["Digit0", "Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6"];
const ALL_HEADING_LEVELS = [1, 2, 3, 4, 5, 6];
const CODE_TO_HEADING_LEVEL_MAP = {
Digit1: 1,
Digit2: 2,
Digit3: 3,
Digit4: 4,
Digit5: 5,
Digit6: 6
};
const allowedHeadingLevels$ = Cell(ALL_HEADING_LEVELS, (r) => {
r.pub(createRootEditorSubscription$, (theRootEditor) => {
return theRootEditor.registerCommand(
KEY_DOWN_COMMAND,
(event) => {
const { code, ctrlKey, metaKey, altKey } = event;
if (FORMATTING_KEYS.includes(code) && controlOrMeta(metaKey, ctrlKey) && altKey) {
event.preventDefault();
theRootEditor.update(() => {
if (code === "Digit0") {
r.pub(convertSelectionToNode$, () => $createParagraphNode());
} else {
const allowedHeadingLevels = r.getValue(allowedHeadingLevels$);
const requestedHeadingLevel = CODE_TO_HEADING_LEVEL_MAP[code];
if (allowedHeadingLevels.includes(requestedHeadingLevel)) {
r.pub(convertSelectionToNode$, () => $createHeadingNode(`h${requestedHeadingLevel}`));
}
}
});
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
);
});
});
const headingsPlugin = realmPlugin({
init(realm) {
realm.pubIn({
[addActivePlugin$]: "headings",
[addImportVisitor$]: MdastHeadingVisitor,
[addLexicalNode$]: HeadingNode,
[addExportVisitor$]: LexicalHeadingVisitor
});
},
update(realm, params) {
realm.pub(allowedHeadingLevels$, (params == null ? void 0 : params.allowedHeadingLevels) ?? ALL_HEADING_LEVELS);
}
});
export {
ALL_HEADING_LEVELS,
allowedHeadingLevels$,
headingsPlugin
};
+56
View File
@@ -0,0 +1,56 @@
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
import classNames from "classnames";
import { $getNodeByKey } from "lexical";
import React__default from "react";
import { disableImageSettingsButton$, openEditImageDialog$, parseImageDimension } from "./index.js";
import styles from "../../styles/ui.module.css.js";
import { iconComponentFor$, readOnly$, useTranslation } from "../core/index.js";
function EditImageToolbar(props) {
const { nodeKey, imageSource, initialImagePath, title, alt, width, height } = props;
const [disableImageSettingsButton, iconComponentFor, readOnly] = useCellValues(disableImageSettingsButton$, iconComponentFor$, readOnly$);
const [editor] = useLexicalComposerContext();
const openEditImageDialog = usePublisher(openEditImageDialog$);
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement("div", { className: styles.editImageToolbar }, /* @__PURE__ */ React__default.createElement(
"button",
{
className: styles.iconButton,
type: "button",
title: t("imageEditor.deleteImage", "Delete image"),
disabled: readOnly,
onClick: (e) => {
e.preventDefault();
editor.update(() => {
var _a;
(_a = $getNodeByKey(nodeKey)) == null ? void 0 : _a.remove();
});
}
},
iconComponentFor("delete_small")
), !disableImageSettingsButton && /* @__PURE__ */ React__default.createElement(
"button",
{
type: "button",
className: classNames(styles.iconButton, styles.editImageButton),
title: t("imageEditor.editImage", "Edit image"),
disabled: readOnly,
onClick: () => {
openEditImageDialog({
nodeKey,
initialValues: {
src: initialImagePath ?? imageSource,
title,
altText: alt,
width: parseImageDimension(width),
height: parseImageDimension(height)
}
});
}
},
iconComponentFor("settings")
));
}
export {
EditImageToolbar
};
+102
View File
@@ -0,0 +1,102 @@
import * as Dialog from "@radix-ui/react-dialog";
import classNames from "classnames";
import React__default from "react";
import { useForm } from "react-hook-form";
import styles from "../../styles/ui.module.css.js";
import { editorRootElementRef$, useTranslation } from "../core/index.js";
import { imageAutocompleteSuggestions$, imageDialogState$, imageUploadHandler$, allowSetImageDimensions$, saveImage$, closeImageDialog$ } from "./index.js";
import { DownshiftAutoComplete } from "../core/ui/DownshiftAutoComplete.js";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
const ImageDialog = () => {
const [imageAutocompleteSuggestions, state, editorRootElementRef, imageUploadHandler, allowSetImageDimensions] = useCellValues(
imageAutocompleteSuggestions$,
imageDialogState$,
editorRootElementRef$,
imageUploadHandler$,
allowSetImageDimensions$
);
const saveImage = usePublisher(saveImage$);
const closeImageDialog = usePublisher(closeImageDialog$);
const t = useTranslation();
const { register, handleSubmit, control, setValue, reset } = useForm({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
values: state.type === "editing" ? state.initialValues : {}
});
const resetFormState = () => {
reset({ src: "", title: "", altText: "", width: void 0, height: void 0 });
};
if (state.type === "inactive")
return null;
return /* @__PURE__ */ React__default.createElement(
Dialog.Root,
{
open: true,
onOpenChange: (open) => {
if (!open) {
closeImageDialog();
resetFormState();
}
}
},
/* @__PURE__ */ React__default.createElement(Dialog.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(Dialog.Overlay, { className: styles.dialogOverlay }), /* @__PURE__ */ React__default.createElement(
Dialog.Content,
{
className: styles.dialogContent,
onOpenAutoFocus: (e) => {
e.preventDefault();
}
},
/* @__PURE__ */ React__default.createElement(Dialog.Title, null, t("uploadImage.dialogTitle", "Upload an image")),
/* @__PURE__ */ React__default.createElement(
"form",
{
onSubmit: async (e) => {
e.preventDefault();
e.stopPropagation();
await handleSubmit(saveImage)(e);
resetFormState();
},
className: styles.multiFieldForm
},
imageUploadHandler === null ? /* @__PURE__ */ React__default.createElement("input", { type: "hidden", accept: "image/*", ...register("file") }) : /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "file" }, t("uploadImage.uploadInstructions", "Upload an image from your device:")), /* @__PURE__ */ React__default.createElement("input", { type: "file", accept: "image/*", ...register("file") })),
/* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "src" }, imageUploadHandler !== null ? t("uploadImage.addViaUrlInstructions", "Or add an image from an URL:") : t("uploadImage.addViaUrlInstructionsNoUpload", "Add an image from an URL:")), /* @__PURE__ */ React__default.createElement(
DownshiftAutoComplete,
{
register,
initialInputValue: state.type === "editing" ? state.initialValues.src ?? "" : "",
inputName: "src",
suggestions: imageAutocompleteSuggestions,
setValue,
control,
placeholder: t("uploadImage.autoCompletePlaceholder", "Select or paste an image src")
}
)),
/* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "alt" }, t("uploadImage.alt", "Alt:")), /* @__PURE__ */ React__default.createElement("input", { type: "text", ...register("altText"), className: styles.textInput })),
/* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "title" }, t("uploadImage.title", "Title:")), /* @__PURE__ */ React__default.createElement("input", { type: "text", ...register("title"), className: styles.textInput })),
allowSetImageDimensions && /* @__PURE__ */ React__default.createElement("div", { className: styles.imageDimensionsContainer }, /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "width" }, t("uploadImage.width", "Width:")), /* @__PURE__ */ React__default.createElement("input", { type: "number", min: 0, ...register("width"), className: styles.textInput })), /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "height" }, t("uploadImage.height", "Height:")), /* @__PURE__ */ React__default.createElement("input", { type: "number", min: 0, ...register("height"), className: styles.textInput }))),
/* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement(
"button",
{
type: "submit",
title: t("dialogControls.save", "Save"),
"aria-label": t("dialogControls.save", "Save"),
className: classNames(styles.primaryButton)
},
t("dialogControls.save", "Save")
), /* @__PURE__ */ React__default.createElement(Dialog.Close, { asChild: true }, /* @__PURE__ */ React__default.createElement(
"button",
{
type: "reset",
title: t("dialogControls.cancel", "Cancel"),
"aria-label": t("dialogControls.cancel", "Cancel"),
className: classNames(styles.secondaryButton)
},
t("dialogControls.cancel", "Cancel")
)))
)
))
);
};
export {
ImageDialog
};
+275
View File
@@ -0,0 +1,275 @@
import React__default from "react";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
import { mergeRegister } from "@lexical/utils";
import { useCellValues } from "@mdxeditor/gurx";
import classNames from "classnames";
import { $isNodeSelection, $getSelection, $getNodeByKey, $setSelection, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_LOW, CLICK_COMMAND, DRAGSTART_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND } from "lexical";
import { imagePlaceholder$, disableImageResize$, allowSetImageDimensions$, imagePreviewHandler$, editImageToolbarComponent$ } from "./index.js";
import styles from "../../styles/ui.module.css.js";
import { readOnly$ } from "../core/index.js";
import { $isImageNode } from "./ImageNode.js";
import ImageResizer from "./ImageResizer.js";
const BROKEN_IMG_URI = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(
/* xml */
`
<svg id="imgLoadError" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<rect x="0" y="0" width="100" height="100" fill="none" stroke="red" stroke-width="4" stroke-dasharray="4" />
<text x="50" y="55" text-anchor="middle" font-size="20" fill="red">⚠️</text>
</svg>
`
);
const imgCache = {
__cache: {},
read(src) {
if (!this.__cache[src]) {
this.__cache[src] = new Promise((resolve) => {
const img = new Image();
img.onerror = () => {
this.__cache[src] = BROKEN_IMG_URI;
resolve();
};
img.onload = () => {
this.__cache[src] = src;
resolve();
};
img.src = src;
});
}
if (this.__cache[src] instanceof Promise) {
throw this.__cache[src];
}
return this.__cache[src];
}
};
function LazyImage({
title,
alt,
className,
imageRef,
src,
width,
height
}) {
return /* @__PURE__ */ React__default.createElement(
"img",
{
className: className ?? void 0,
alt,
src: imgCache.read(src),
title,
ref: imageRef,
draggable: "false",
width,
height
}
);
}
function ImageEditor({ src, title, alt, nodeKey, width, height, rest }) {
const [ImagePlaceholderComponent, disableImageResize, allowSetImageDimensions, imagePreviewHandler, readOnly, EditImageToolbar] = useCellValues(
imagePlaceholder$,
disableImageResize$,
allowSetImageDimensions$,
imagePreviewHandler$,
readOnly$,
editImageToolbarComponent$
);
const imageRef = React__default.useRef(null);
const buttonRef = React__default.useRef(null);
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
const [editor] = useLexicalComposerContext();
const [selection, setSelection] = React__default.useState(null);
const activeEditorRef = React__default.useRef(null);
const [isResizing, setIsResizing] = React__default.useState(false);
const [imageSource, setImageSource] = React__default.useState(null);
const [initialImagePath, setInitialImagePath] = React__default.useState(null);
const onDelete = React__default.useCallback(
(payload) => {
if (isSelected && $isNodeSelection($getSelection())) {
const event = payload;
event.preventDefault();
const node = $getNodeByKey(nodeKey);
if ($isImageNode(node)) {
node.remove();
}
}
return false;
},
[isSelected, nodeKey]
);
const onEnter = React__default.useCallback(
(event) => {
const latestSelection = $getSelection();
const buttonElem = buttonRef.current;
if (isSelected && $isNodeSelection(latestSelection) && latestSelection.getNodes().length === 1) {
if (buttonElem !== null && buttonElem !== document.activeElement) {
event.preventDefault();
buttonElem.focus();
return true;
}
}
return false;
},
[isSelected]
);
const onEscape = React__default.useCallback(
(event) => {
if (buttonRef.current === event.target) {
$setSelection(null);
editor.update(() => {
setSelected(true);
const parentRootElement = editor.getRootElement();
if (parentRootElement !== null) {
parentRootElement.focus();
}
});
return true;
}
return false;
},
[editor, setSelected]
);
React__default.useEffect(() => {
if (imagePreviewHandler) {
const callPreviewHandler = async () => {
if (!initialImagePath)
setInitialImagePath(src);
const updatedSrc = await imagePreviewHandler(src);
setImageSource(updatedSrc);
};
callPreviewHandler().catch((e) => {
console.error(e);
});
} else {
setImageSource(src);
}
}, [src, imagePreviewHandler, initialImagePath]);
React__default.useEffect(() => {
if (allowSetImageDimensions && imageRef.current) {
const { current: image } = imageRef;
syncDimensionWithImageResizer(image, "width", width);
syncDimensionWithImageResizer(image, "height", height);
}
}, [allowSetImageDimensions, width, height]);
React__default.useEffect(() => {
let isMounted = true;
const unregister = mergeRegister(
editor.registerUpdateListener(({ editorState }) => {
if (isMounted) {
setSelection(editorState.read(() => $getSelection()));
}
}),
editor.registerCommand(
SELECTION_CHANGE_COMMAND,
(_, activeEditor) => {
activeEditorRef.current = activeEditor;
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
CLICK_COMMAND,
(payload) => {
const event = payload;
if (isResizing) {
return true;
}
if (event.target === imageRef.current) {
if (event.shiftKey) {
setSelected(!isSelected);
} else {
clearSelection();
setSelected(true);
}
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
DRAGSTART_COMMAND,
(event) => {
if (event.target === imageRef.current) {
event.preventDefault();
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),
editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW)
);
return () => {
isMounted = false;
unregister();
};
}, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
const onResizeEnd = (nextWidth, nextHeight) => {
setTimeout(() => {
setIsResizing(false);
}, 200);
editor.update(() => {
const node = $getNodeByKey(nodeKey);
if ($isImageNode(node)) {
node.setWidthAndHeight(nextWidth, nextHeight);
}
});
};
const onResizeStart = () => {
setIsResizing(true);
};
const draggable = $isNodeSelection(selection);
const isFocused = isSelected;
const passedClassName = React__default.useMemo(() => {
if (rest.length === 0) {
return null;
}
const className = rest.find((attr) => attr.type === "mdxJsxAttribute" && (attr.name === "class" || attr.name === "className"));
if (className) {
return className.value;
}
return null;
}, [rest]);
return imageSource !== null ? /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: ImagePlaceholderComponent ? /* @__PURE__ */ React__default.createElement(ImagePlaceholderComponent, null) : null }, /* @__PURE__ */ React__default.createElement("div", { className: styles.imageWrapper, "data-editor-block-type": "image" }, /* @__PURE__ */ React__default.createElement("div", { draggable }, /* @__PURE__ */ React__default.createElement(
LazyImage,
{
width,
height,
className: classNames(
{
[styles.focusedImage]: isFocused
},
passedClassName
),
src: imageSource,
title: title ?? "",
alt: alt ?? "",
imageRef
}
)), draggable && isFocused && !disableImageResize && /* @__PURE__ */ React__default.createElement(ImageResizer, { editor, imageRef, onResizeStart, onResizeEnd }), readOnly || /* @__PURE__ */ React__default.createElement(
EditImageToolbar,
{
nodeKey,
imageSource,
initialImagePath,
title: title ?? "",
alt: alt ?? "",
width,
height
}
))) : null;
}
const syncDimensionWithImageResizer = (image, key, value) => {
if (typeof value === "number") {
image.style[key] = `${value}px`;
} else {
image.style.removeProperty(key);
}
};
export {
ImageEditor
};
+193
View File
@@ -0,0 +1,193 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import React__default from "react";
import { DecoratorNode } from "lexical";
import { ImageEditor } from "./ImageEditor.js";
function convertImageElement(domNode) {
if (domNode instanceof HTMLImageElement) {
const { alt: altText, src, title, width, height } = domNode;
const node = $createImageNode({ altText, src, title, width, height });
return { node };
}
return null;
}
class ImageNode extends DecoratorNode {
/**
* Constructs a new {@link ImageNode} with the specified image parameters.
* Use {@link $createImageNode} to construct one.
*/
constructor(src, altText, title, width, height, rest, key) {
super(key);
/** @internal */
__publicField(this, "__src");
/** @internal */
__publicField(this, "__altText");
/** @internal */
__publicField(this, "__title");
/** @internal */
__publicField(this, "__width");
/** @internal */
__publicField(this, "__height");
/** @internal */
__publicField(this, "__rest");
this.__src = src;
this.__title = title;
this.__altText = altText;
this.__width = width ?? "inherit";
this.__height = height ?? "inherit";
this.__rest = rest ?? [];
}
/** @internal */
static getType() {
return "image";
}
/** @internal */
static clone(node) {
return new ImageNode(node.__src, node.__altText, node.__title, node.__width, node.__height, node.__rest, node.__key);
}
/** @internal */
afterCloneFrom(prevNode) {
super.afterCloneFrom(prevNode);
this.__src = prevNode.__src;
this.__altText = prevNode.__altText;
this.__title = prevNode.__title;
this.__width = prevNode.__width;
this.__height = prevNode.__height;
this.__rest = prevNode.__rest;
}
/** @internal */
static importJSON(serializedNode) {
const { altText, title, src, width, rest, height } = serializedNode;
const node = $createImageNode({
altText,
title,
src,
height,
width,
rest
});
return node;
}
/** @internal */
exportDOM() {
const element = document.createElement("img");
element.setAttribute("src", this.__src);
element.setAttribute("alt", this.__altText);
if (this.__title) {
element.setAttribute("title", this.__title);
}
if (this.__width) {
element.setAttribute("width", this.__width.toString());
}
if (this.__height) {
element.setAttribute("height", this.__height.toString());
}
return { element };
}
/** @internal */
static importDOM() {
return {
img: () => ({
conversion: convertImageElement,
priority: 0
})
};
}
/** @internal */
exportJSON() {
return {
altText: this.getAltText(),
title: this.getTitle(),
height: this.__height === "inherit" ? 0 : this.__height,
width: this.__width === "inherit" ? 0 : this.__width,
src: this.getSrc(),
rest: this.__rest,
type: "image",
version: 1
};
}
/**
* Sets the image dimensions
*/
setWidthAndHeight(width, height) {
const writable = this.getWritable();
writable.__width = width;
writable.__height = height;
}
/** @internal */
createDOM(config, _editor) {
const span = document.createElement("span");
const theme = config.theme;
const className = theme.image;
if (className !== void 0) {
span.className = className;
}
return span;
}
/** @internal */
updateDOM() {
return false;
}
getSrc() {
return this.__src;
}
getAltText() {
return this.__altText;
}
getTitle() {
return this.__title;
}
getHeight() {
return this.__height;
}
getWidth() {
return this.__width;
}
getRest() {
return this.__rest;
}
setTitle(title) {
this.getWritable().__title = title;
}
setSrc(src) {
this.getWritable().__src = src;
}
setAltText(altText) {
this.getWritable().__altText = altText ?? "";
}
/** @internal */
shouldBeSerializedAsElement() {
return this.__width !== "inherit" || this.__height !== "inherit" || this.__rest.length > 0;
}
/** @internal */
decorate(_parentEditor) {
return /* @__PURE__ */ React__default.createElement(
ImageEditor,
{
src: this.getSrc(),
title: this.getTitle(),
nodeKey: this.getKey(),
width: this.__width,
height: this.__height,
alt: this.__altText,
rest: this.__rest
}
);
}
}
function $createImageNode(params) {
const { altText, title, src, key, width, height, rest } = params;
return new ImageNode(src, altText, title, width, height, rest, key);
}
function $isImageNode(node) {
return node instanceof ImageNode;
}
export {
$createImageNode,
$isImageNode,
ImageNode
};
@@ -0,0 +1,9 @@
import React__default from "react";
import { ImageIcon } from "@radix-ui/react-icons";
import styles from "../../styles/ui.module.css.js";
const ImagePlaceholder = () => {
return /* @__PURE__ */ React__default.createElement("div", { className: styles.imagePlaceholder }, /* @__PURE__ */ React__default.createElement(ImageIcon, null));
};
export {
ImagePlaceholder
};
+214
View File
@@ -0,0 +1,214 @@
import * as React from "react";
import { useRef } from "react";
import styles from "../../styles/ui.module.css.js";
import classNames from "classnames";
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
const Direction = {
east: 1 << 0,
north: 1 << 3,
south: 1 << 1,
west: 1 << 2
};
function ImageResizer({
onResizeStart,
onResizeEnd,
imageRef,
maxWidth,
editor
}) {
const controlWrapperRef = useRef(null);
const userSelect = useRef({
priority: "",
value: "default"
});
const positioningRef = useRef({
currentHeight: 0,
currentWidth: 0,
direction: 0,
isResizing: false,
ratio: 0,
startHeight: 0,
startWidth: 0,
startX: 0,
startY: 0
});
const editorRootElement = editor.getRootElement();
const maxWidthContainer = maxWidth ?? (editorRootElement !== null ? editorRootElement.getBoundingClientRect().width - 20 : 100);
const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 : 100;
const minWidth = 100;
const minHeight = 100;
const setStartCursor = (direction) => {
const ew = direction === Direction.east || direction === Direction.west;
const ns = direction === Direction.north || direction === Direction.south;
const nwse = direction & Direction.north && direction & Direction.west || direction & Direction.south && direction & Direction.east;
const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw";
if (editorRootElement !== null) {
editorRootElement.style.setProperty("cursor", `${cursorDir}-resize`, "important");
}
if (document.body !== null) {
document.body.style.setProperty("cursor", `${cursorDir}-resize`, "important");
userSelect.current.value = document.body.style.getPropertyValue("-webkit-user-select");
userSelect.current.priority = document.body.style.getPropertyPriority("-webkit-user-select");
document.body.style.setProperty("-webkit-user-select", `none`, "important");
}
};
const setEndCursor = () => {
if (editorRootElement !== null) {
editorRootElement.style.setProperty("cursor", "text");
}
if (document.body !== null) {
document.body.style.setProperty("cursor", "default");
document.body.style.setProperty("-webkit-user-select", userSelect.current.value, userSelect.current.priority);
}
};
const handlePointerDown = (event, direction) => {
if (!editor.isEditable()) {
return;
}
const image = imageRef.current;
const controlWrapper = controlWrapperRef.current;
if (image !== null && controlWrapper !== null) {
event.preventDefault();
const { width, height } = image.getBoundingClientRect();
const positioning = positioningRef.current;
positioning.startWidth = width;
positioning.startHeight = height;
positioning.ratio = width / height;
positioning.currentWidth = width;
positioning.currentHeight = height;
positioning.startX = event.clientX;
positioning.startY = event.clientY;
positioning.isResizing = true;
positioning.direction = direction;
setStartCursor(direction);
onResizeStart();
controlWrapper.classList.add(styles.imageControlWrapperResizing);
image.style.height = `${height}px`;
image.style.width = `${width}px`;
document.addEventListener("pointermove", handlePointerMove);
document.addEventListener("pointerup", handlePointerUp);
}
};
const handlePointerMove = (event) => {
const image = imageRef.current;
const positioning = positioningRef.current;
const isHorizontal = positioning.direction & (Direction.east | Direction.west);
const isVertical = positioning.direction & (Direction.south | Direction.north);
if (image !== null && positioning.isResizing) {
if (isHorizontal && isVertical) {
let diff = Math.floor(positioning.startX - event.clientX);
diff = positioning.direction & Direction.east ? -diff : diff;
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
const height = width / positioning.ratio;
image.style.width = `${width}px`;
image.style.height = `${height}px`;
positioning.currentHeight = height;
positioning.currentWidth = width;
} else if (isVertical) {
let diff = Math.floor(positioning.startY - event.clientY);
diff = positioning.direction & Direction.south ? -diff : diff;
const height = clamp(positioning.startHeight + diff, minHeight, maxHeightContainer);
image.style.height = `${height}px`;
positioning.currentHeight = height;
} else {
let diff = Math.floor(positioning.startX - event.clientX);
diff = positioning.direction & Direction.east ? -diff : diff;
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
image.style.width = `${width}px`;
positioning.currentWidth = width;
}
}
};
const handlePointerUp = () => {
const image = imageRef.current;
const positioning = positioningRef.current;
const controlWrapper = controlWrapperRef.current;
if (image !== null && controlWrapper !== null && positioning.isResizing) {
const width = positioning.currentWidth;
const height = positioning.currentHeight;
positioning.startWidth = 0;
positioning.startHeight = 0;
positioning.ratio = 0;
positioning.startX = 0;
positioning.startY = 0;
positioning.currentWidth = 0;
positioning.currentHeight = 0;
positioning.isResizing = false;
controlWrapper.classList.remove(styles.imageControlWrapperResizing);
setEndCursor();
onResizeEnd(width, height);
document.removeEventListener("pointermove", handlePointerMove);
document.removeEventListener("pointerup", handlePointerUp);
}
};
return /* @__PURE__ */ React.createElement("div", { ref: controlWrapperRef }, /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerN),
onPointerDown: (event) => {
handlePointerDown(event, Direction.north);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerNe),
onPointerDown: (event) => {
handlePointerDown(event, Direction.north | Direction.east);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerE),
onPointerDown: (event) => {
handlePointerDown(event, Direction.east);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerSe),
onPointerDown: (event) => {
handlePointerDown(event, Direction.south | Direction.east);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerS),
onPointerDown: (event) => {
handlePointerDown(event, Direction.south);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerSw),
onPointerDown: (event) => {
handlePointerDown(event, Direction.south | Direction.west);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerW),
onPointerDown: (event) => {
handlePointerDown(event, Direction.west);
}
}
), /* @__PURE__ */ React.createElement(
"div",
{
className: classNames(styles.imageResizer, styles.imageResizerNw),
onPointerDown: (event) => {
handlePointerDown(event, Direction.north | Direction.west);
}
}
));
}
export {
ImageResizer as default
};
@@ -0,0 +1,42 @@
import { $isImageNode } from "./ImageNode.js";
const LexicalImageVisitor = {
testLexicalNode: $isImageNode,
visitLexicalNode({ mdastParent, lexicalNode, actions }) {
if (lexicalNode.shouldBeSerializedAsElement()) {
const img = new Image();
if (lexicalNode.getHeight() !== "inherit") {
img.height = lexicalNode.getHeight();
}
if (lexicalNode.getWidth() !== "inherit") {
img.width = lexicalNode.getWidth();
}
if (lexicalNode.getAltText()) {
img.alt = lexicalNode.getAltText();
}
if (lexicalNode.getTitle()) {
img.title = lexicalNode.getTitle();
}
for (const attr of lexicalNode.getRest()) {
if (attr.type === "mdxJsxAttribute") {
if (typeof attr.value === "string") {
img.setAttribute(attr.name, attr.value);
}
}
}
actions.appendToParent(mdastParent, {
type: "html",
value: img.outerHTML.replace(/>$/, ` src="${lexicalNode.getSrc()}" />`)
});
} else {
actions.appendToParent(mdastParent, {
type: "image",
url: lexicalNode.getSrc(),
alt: lexicalNode.getAltText(),
title: lexicalNode.getTitle()
});
}
}
};
export {
LexicalImageVisitor
};
+91
View File
@@ -0,0 +1,91 @@
import { $createImageNode } from "./ImageNode.js";
import { $createParagraphNode } from "lexical";
const MdastImageVisitor = {
testNode: "image",
visitNode({ mdastNode, actions }) {
actions.addAndStepInto(
$createImageNode({
src: mdastNode.url,
altText: mdastNode.alt ?? "",
title: mdastNode.title ?? ""
})
);
}
};
const MdastHtmlImageVisitor = {
testNode: (node) => {
return node.type === "html" && node.value.trim().startsWith("<img");
},
visitNode({ mdastNode, lexicalParent }) {
const wrapper = document.createElement("div");
wrapper.innerHTML = mdastNode.value;
const img = wrapper.querySelector("img");
if (!img) {
throw new Error("Invalid HTML image");
}
const src = img.src;
const altText = img.alt;
const title = img.title;
const width = img.width;
const height = img.height;
const image = $createImageNode({
src: src || "",
altText,
title,
width,
height
});
if (lexicalParent.getType() === "root") {
const paragraph = $createParagraphNode();
paragraph.append(image);
lexicalParent.append(paragraph);
} else {
lexicalParent.append(image);
}
}
};
function getAttributeValue(node, attributeName) {
const attribute = node.attributes.find((a) => a.type === "mdxJsxAttribute" && a.name === attributeName);
if (!attribute) {
return void 0;
}
return attribute.value;
}
const MdastJsxImageVisitor = {
testNode: (node) => {
return (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") && node.name === "img";
},
visitNode({ mdastNode, lexicalParent }) {
const src = getAttributeValue(mdastNode, "src");
if (!src) {
return;
}
const altText = getAttributeValue(mdastNode, "alt") ?? "";
const title = getAttributeValue(mdastNode, "title");
const height = getAttributeValue(mdastNode, "height");
const width = getAttributeValue(mdastNode, "width");
const rest = mdastNode.attributes.filter((a) => {
return a.type === "mdxJsxAttribute" && !["src", "alt", "title", "height", "width"].includes(a.name);
});
const image = $createImageNode({
src,
altText,
title,
width: width ? parseInt(width, 10) : void 0,
height: height ? parseInt(height, 10) : void 0,
rest
});
if (lexicalParent.getType() === "root") {
const paragraph = $createParagraphNode();
paragraph.append(image);
lexicalParent.append(paragraph);
} else {
lexicalParent.append(image);
}
}
};
export {
MdastHtmlImageVisitor,
MdastImageVisitor,
MdastJsxImageVisitor
};
+370
View File
@@ -0,0 +1,370 @@
import { ImagePlaceholder } from "./ImagePlaceholder.js";
import { mergeRegister, $wrapNodeInElement } from "@lexical/utils";
import { Signal, Cell, Action, withLatestFrom, mapTo, map } from "@mdxeditor/gurx";
import { createCommand, $getNodeByKey, $insertNodes, $isRootOrShadowRoot, $createParagraphNode, COMMAND_PRIORITY_EDITOR, DRAGSTART_COMMAND, COMMAND_PRIORITY_HIGH, DRAGOVER_COMMAND, COMMAND_PRIORITY_LOW, DROP_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_CRITICAL, $createRangeSelection, $setSelection, $getSelection, $isNodeSelection } from "lexical";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { CAN_USE_DOM } from "../../utils/detectMac.js";
import { activeEditor$, createActiveEditorSubscription$, addComposerChild$, addExportVisitor$, addLexicalNode$, addImportVisitor$ } from "../core/index.js";
import { EditImageToolbar } from "./EditImageToolbar.js";
import { ImageDialog } from "./ImageDialog.js";
import { $createImageNode, ImageNode, $isImageNode } from "./ImageNode.js";
import { LexicalImageVisitor } from "./LexicalImageVisitor.js";
import { MdastImageVisitor, MdastHtmlImageVisitor, MdastJsxImageVisitor } from "./MdastImageVisitor.js";
const internalInsertImage$ = Signal((r) => {
r.sub(r.pipe(internalInsertImage$, withLatestFrom(activeEditor$)), ([values, theEditor]) => {
theEditor == null ? void 0 : theEditor.update(() => {
const imageNode = $createImageNode({
altText: values.altText ?? "",
src: values.src,
title: values.title ?? "",
width: parseImageDimension(values.width),
height: parseImageDimension(values.height)
});
$insertNodes([imageNode]);
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
}
});
});
});
const insertImage$ = Signal((r) => {
r.sub(r.pipe(insertImage$, withLatestFrom(imageUploadHandler$)), ([values, imageUploadHandler]) => {
const handler = (src) => {
r.pub(internalInsertImage$, { ...values, src });
};
if ("file" in values) {
imageUploadHandler == null ? void 0 : imageUploadHandler(values.file).then(handler).catch((e) => {
throw e;
});
} else {
handler(values.src);
}
});
});
const imageAutocompleteSuggestions$ = Cell([]);
const disableImageResize$ = Cell(false);
const imageUploadHandler$ = Cell(null);
const imagePreviewHandler$ = Cell(null);
const imagePlaceholder$ = Cell(null);
const imageDialogState$ = Cell(
{ type: "inactive" },
(r) => {
r.sub(
r.pipe(saveImage$, withLatestFrom(activeEditor$, imageUploadHandler$, imageDialogState$, allowSetImageDimensions$)),
([values, theEditor, imageUploadHandler, dialogState, allowSetImageDimensions]) => {
const handler = dialogState.type === "editing" ? (src) => {
theEditor == null ? void 0 : theEditor.update(() => {
const { nodeKey } = dialogState;
const imageNode = $getNodeByKey(nodeKey);
imageNode.setTitle(values.title);
imageNode.setAltText(values.altText);
imageNode.setSrc(src);
if (allowSetImageDimensions) {
const width = parseImageDimension(values.width);
const height = parseImageDimension(values.height);
imageNode.setWidthAndHeight(width ?? "inherit", height ?? "inherit");
}
});
r.pub(imageDialogState$, { type: "inactive" });
} : (src) => {
r.pub(internalInsertImage$, { ...values, src });
r.pub(imageDialogState$, { type: "inactive" });
};
if (values.file && values.file.length > 0) {
imageUploadHandler == null ? void 0 : imageUploadHandler(values.file.item(0)).then(handler).catch((e) => {
throw e;
});
} else if (values.src) {
handler(values.src);
}
}
);
r.pub(createActiveEditorSubscription$, (editor) => {
const theUploadHandler = r.getValue(imageUploadHandler$);
return mergeRegister(
editor.registerCommand(
INSERT_IMAGE_COMMAND,
(payload) => {
const imageNode = $createImageNode(payload);
$insertNodes([imageNode]);
if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
$wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
}
return true;
},
COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
DRAGSTART_COMMAND,
(event) => {
return onDragStart(event);
},
COMMAND_PRIORITY_HIGH
),
editor.registerCommand(
DRAGOVER_COMMAND,
(event) => {
return onDragover(event, !!theUploadHandler);
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
DROP_COMMAND,
(event) => {
return onDrop(event, editor, r.getValue(imageUploadHandler$));
},
COMMAND_PRIORITY_HIGH
),
editor.registerCommand(
PASTE_COMMAND,
(event) => {
var _a, _b;
if (!theUploadHandler) {
let fromWeb = Array.from(((_a = event.clipboardData) == null ? void 0 : _a.items) ?? []);
fromWeb = fromWeb.filter((i) => i.type.includes("text"));
if (!fromWeb.length || fromWeb.length === 0) {
return true;
}
return false;
}
const cbPayload = Array.from(((_b = event.clipboardData) == null ? void 0 : _b.items) ?? []);
const isMixedPayload = cbPayload.some((item) => !item.type.includes("image"));
if (isMixedPayload)
return false;
if (!cbPayload.length || cbPayload.length === 0) {
return false;
}
const imageUploadHandlerValue = r.getValue(imageUploadHandler$);
Promise.all(cbPayload.map((file) => imageUploadHandlerValue(file.getAsFile()))).then((urls) => {
urls.forEach((url) => {
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
src: url,
altText: ""
});
});
}).catch((e) => {
throw e;
});
return true;
},
COMMAND_PRIORITY_CRITICAL
)
);
});
}
);
const openNewImageDialog$ = Action((r) => {
r.link(r.pipe(openNewImageDialog$, mapTo({ type: "new" })), imageDialogState$);
});
const openEditImageDialog$ = Signal((r) => {
r.link(
r.pipe(
openEditImageDialog$,
map((payload) => ({ type: "editing", ...payload }))
),
imageDialogState$
);
});
const closeImageDialog$ = Action((r) => {
r.link(r.pipe(closeImageDialog$, mapTo({ type: "inactive" })), imageDialogState$);
});
const disableImageSettingsButton$ = Cell(false);
const allowSetImageDimensions$ = Cell(false);
const saveImage$ = Signal();
const editImageToolbarComponent$ = Cell(EditImageToolbar);
const parseImageDimension = (value) => {
if (typeof value === "undefined")
return void 0;
const parsed = parseInt(String(value), 10);
return Number.isNaN(parsed) ? void 0 : parsed;
};
const imagePlugin = realmPlugin({
init(realm, params) {
realm.pubIn({
[addImportVisitor$]: [MdastImageVisitor, MdastHtmlImageVisitor, MdastJsxImageVisitor],
[addLexicalNode$]: ImageNode,
[addExportVisitor$]: LexicalImageVisitor,
[addComposerChild$]: (params == null ? void 0 : params.ImageDialog) ?? ImageDialog,
[imageUploadHandler$]: (params == null ? void 0 : params.imageUploadHandler) ?? null,
[imageAutocompleteSuggestions$]: (params == null ? void 0 : params.imageAutocompleteSuggestions) ?? [],
[disableImageResize$]: Boolean(params == null ? void 0 : params.disableImageResize),
[disableImageSettingsButton$]: Boolean(params == null ? void 0 : params.disableImageSettingsButton),
[allowSetImageDimensions$]: Boolean(params == null ? void 0 : params.allowSetImageDimensions),
[imagePreviewHandler$]: (params == null ? void 0 : params.imagePreviewHandler) ?? null,
[editImageToolbarComponent$]: (params == null ? void 0 : params.EditImageToolbar) ?? EditImageToolbar,
[imagePlaceholder$]: (params == null ? void 0 : params.imagePlaceholder) ?? ImagePlaceholder
});
},
update(realm, params) {
realm.pubIn({
[imageUploadHandler$]: (params == null ? void 0 : params.imageUploadHandler) ?? null,
[imageAutocompleteSuggestions$]: (params == null ? void 0 : params.imageAutocompleteSuggestions) ?? [],
[disableImageResize$]: Boolean(params == null ? void 0 : params.disableImageResize),
[imagePreviewHandler$]: (params == null ? void 0 : params.imagePreviewHandler) ?? null,
[allowSetImageDimensions$]: Boolean(params == null ? void 0 : params.allowSetImageDimensions),
[editImageToolbarComponent$]: (params == null ? void 0 : params.EditImageToolbar) ?? EditImageToolbar,
[imagePlaceholder$]: (params == null ? void 0 : params.imagePlaceholder) ?? ImagePlaceholder
});
}
});
const getDOMSelection = (targetWindow) => CAN_USE_DOM ? (targetWindow ?? window).getSelection() : null;
const INSERT_IMAGE_COMMAND = createCommand("INSERT_IMAGE_COMMAND");
const TRANSPARENT_IMAGE = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
function onDragStart(event) {
const node = getImageNodeInSelection();
if (!node) {
return false;
}
const dataTransfer = event.dataTransfer;
if (!dataTransfer) {
return false;
}
dataTransfer.setData("text/plain", "_");
const img = document.createElement("img");
img.src = TRANSPARENT_IMAGE;
dataTransfer.setDragImage(img, 0, 0);
dataTransfer.setData(
"application/x-lexical-drag",
JSON.stringify({
data: {
altText: node.__altText,
title: node.__title,
key: node.getKey(),
src: node.__src
},
type: "image"
})
);
return true;
}
function onDragover(event, hasUploadHandler) {
var _a;
if (hasUploadHandler) {
let cbPayload = Array.from(((_a = event.dataTransfer) == null ? void 0 : _a.items) ?? []);
cbPayload = cbPayload.filter((i) => i.type.includes("image"));
if (cbPayload.length > 0) {
event.preventDefault();
return true;
}
}
const node = getImageNodeInSelection();
if (!node) {
return false;
}
if (!canDropImage(event)) {
event.preventDefault();
}
return true;
}
function onDrop(event, editor, imageUploadHandler) {
var _a;
let cbPayload = Array.from(((_a = event.dataTransfer) == null ? void 0 : _a.items) ?? []);
cbPayload = cbPayload.filter((i) => i.type.includes("image"));
if (cbPayload.length > 0) {
if (imageUploadHandler !== null) {
event.preventDefault();
Promise.all(
cbPayload.map((image) => {
if (image.kind === "string") {
return new Promise((rs) => {
image.getAsString(rs);
});
}
return imageUploadHandler(image.getAsFile());
})
).then((urls) => {
urls.forEach((url) => {
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
src: url,
altText: ""
});
});
}).catch((e) => {
throw e;
});
return true;
}
}
const node = getImageNodeInSelection();
if (!node) {
return false;
}
const data = getDragImageData(event);
if (!data) {
return false;
}
event.preventDefault();
if (canDropImage(event)) {
const range = getDragSelection(event);
node.remove();
const rangeSelection = $createRangeSelection();
if (range !== null && range !== void 0) {
rangeSelection.applyDOMRange(range);
}
$setSelection(rangeSelection);
editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
}
return true;
}
function getImageNodeInSelection() {
const selection = $getSelection();
if (!$isNodeSelection(selection)) {
return null;
}
const nodes = selection.getNodes();
const node = nodes[0];
return $isImageNode(node) ? node : null;
}
function getDragImageData(event) {
var _a;
const dragData = (_a = event.dataTransfer) == null ? void 0 : _a.getData("application/x-lexical-drag");
if (!dragData) {
return null;
}
const { type, data } = JSON.parse(dragData);
if (type !== "image") {
return null;
}
return data;
}
function canDropImage(event) {
const target = event.target;
return !!(target && target instanceof HTMLElement && target.parentElement);
}
function getDragSelection(event) {
let range;
const target = event.target;
const targetWindow = target == null ? null : target.nodeType === 9 ? target.defaultView : target.ownerDocument.defaultView;
const domSelection = getDOMSelection(targetWindow);
if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(event.clientX, event.clientY);
} else if (event.rangeParent && domSelection !== null) {
domSelection.collapse(event.rangeParent, event.rangeOffset ?? 0);
range = domSelection.getRangeAt(0);
} else {
throw Error(`Cannot get the selection when dragging`);
}
return range;
}
export {
$createImageNode,
$isImageNode,
INSERT_IMAGE_COMMAND,
ImageNode,
allowSetImageDimensions$,
closeImageDialog$,
disableImageResize$,
disableImageSettingsButton$,
editImageToolbarComponent$,
imageAutocompleteSuggestions$,
imageDialogState$,
imagePlaceholder$,
imagePlugin,
imagePreviewHandler$,
imageUploadHandler$,
insertImage$,
openEditImageDialog$,
openNewImageDialog$,
parseImageDimension,
saveImage$
};
+109
View File
@@ -0,0 +1,109 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { DecoratorNode } from "lexical";
import React__default from "react";
import { NestedEditorsContext } from "../core/NestedLexicalEditor.js";
import { voidEmitter } from "../../utils/voidEmitter.js";
import { useCellValue } from "@mdxeditor/gurx";
import { jsxComponentDescriptors$ } from "../core/index.js";
class LexicalJsxNode extends DecoratorNode {
constructor(mdastNode, importStatement, key) {
super(key);
__publicField(this, "__mdastNode");
__publicField(this, "__focusEmitter", voidEmitter());
__publicField(this, "__importStatement");
__publicField(this, "select", () => {
this.__focusEmitter.publish();
});
this.__mdastNode = mdastNode;
this.__importStatement = importStatement;
}
static getType() {
return "jsx";
}
static clone(node) {
return new LexicalJsxNode(structuredClone(node.__mdastNode), structuredClone(node.__importStatement), node.__key);
}
static importJSON(serializedNode) {
return $createLexicalJsxNode(serializedNode.mdastNode, serializedNode.importStatement);
}
getMdastNode() {
return this.__mdastNode;
}
getImportStatement() {
return this.__importStatement;
}
exportJSON() {
return {
mdastNode: this.getMdastNode(),
importStatement: this.getImportStatement(),
type: "jsx",
version: 1
};
}
createDOM() {
return document.createElement(this.__mdastNode.type === "mdxJsxTextElement" ? "span" : "div");
}
updateDOM() {
return false;
}
setMdastNode(mdastNode) {
this.getWritable().__mdastNode = mdastNode;
}
decorate(parentEditor, config) {
return /* @__PURE__ */ React__default.createElement(
JsxEditorContainer,
{
lexicalJsxNode: this,
config,
mdastNode: this.getMdastNode(),
parentEditor,
focusEmitter: this.__focusEmitter
}
);
}
isInline() {
return this.__mdastNode.type === "mdxJsxTextElement";
}
isKeyboardSelectable() {
return true;
}
}
function JsxEditorContainer(props) {
const { mdastNode } = props;
const jsxComponentDescriptors = useCellValue(jsxComponentDescriptors$);
const descriptor = jsxComponentDescriptors.find((descriptor2) => descriptor2.name === mdastNode.name) ?? jsxComponentDescriptors.find((descriptor2) => descriptor2.name === "*");
if (!descriptor) {
throw new Error(`No JSX descriptor found for ${mdastNode.name}`);
}
const Editor = descriptor.Editor;
return /* @__PURE__ */ React__default.createElement(
NestedEditorsContext.Provider,
{
value: {
config: props.config,
focusEmitter: props.focusEmitter,
mdastNode,
parentEditor: props.parentEditor,
lexicalNode: props.lexicalJsxNode
}
},
/* @__PURE__ */ React__default.createElement(Editor, { descriptor, mdastNode })
);
}
function $createLexicalJsxNode(mdastNode, importStatement) {
return new LexicalJsxNode(mdastNode, importStatement);
}
function $isLexicalJsxNode(node) {
return node instanceof LexicalJsxNode;
}
export {
$createLexicalJsxNode,
$isLexicalJsxNode,
JsxEditorContainer,
LexicalJsxNode
};
+27
View File
@@ -0,0 +1,27 @@
import { $isLexicalJsxNode } from "./LexicalJsxNode.js";
import { isMdastJsxNode } from "./index.js";
import { htmlTags } from "../core/MdastHTMLNode.js";
const LexicalJsxVisitor = {
testLexicalNode: $isLexicalJsxNode,
visitLexicalNode({ actions, mdastParent, lexicalNode }) {
function traverseNestedJsxNodes(node) {
if ("children" in node && node.children instanceof Array) {
node.children.forEach((child) => {
if (isMdastJsxNode(child) && !htmlTags.includes(child.name.toLowerCase())) {
actions.registerReferredComponent(child.name);
}
traverseNestedJsxNodes(child);
});
}
}
const mdastNode = lexicalNode.getMdastNode();
const importStatement = lexicalNode.getImportStatement();
actions.registerReferredComponent(mdastNode.name, importStatement);
traverseNestedJsxNodes(mdastNode);
actions.appendToParent(mdastParent, mdastNode);
},
priority: -200
};
export {
LexicalJsxVisitor
};
@@ -0,0 +1,132 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import React__default from "react";
import { DecoratorNode, $applyNodeReplacement } from "lexical";
import lexicalThemeStyles from "../../styles/lexical-theme.module.css.js";
import styles from "../../styles/ui.module.css.js";
class LexicalMdxExpressionNode extends DecoratorNode {
/**
* Constructs a new {@link GenericHTMLNode} with the specified MDAST HTML node as the object to edit.
*/
constructor(value, mdastType, key) {
super(key);
/** @internal */
__publicField(this, "__value");
/** @internal */
__publicField(this, "__mdastType");
this.__value = value;
this.__mdastType = mdastType;
}
/** @internal */
static getType() {
return "mdx-expression";
}
/** @internal */
static clone(node) {
return new LexicalMdxExpressionNode(node.__value, node.__mdastType, node.__key);
}
getValue() {
return this.__value;
}
getMdastType() {
return this.__mdastType;
}
// View
createDOM() {
const element = document.createElement("span");
element.classList.add(lexicalThemeStyles.mdxExpression);
return element;
}
updateDOM() {
return false;
}
static importDOM() {
return {};
}
exportDOM(editor) {
const { element } = super.exportDOM(editor);
return {
element
};
}
static importJSON(serializedNode) {
return $createLexicalMdxExpressionNode(serializedNode.value, serializedNode.mdastType);
}
exportJSON() {
return {
...super.exportJSON(),
value: this.getValue(),
mdastType: this.getMdastType(),
type: "mdx-expression",
version: 1
};
}
/*
// Mutation
insertNewAfter(selection?: RangeSelection, restoreSelection = true): ParagraphNode | GenericHTMLNode {
const anchorOffset = selection ? selection.anchor.offset : 0
const newElement =
anchorOffset > 0 && anchorOffset < this.getTextContentSize() ? $createHeadingNode(this.getTag()) : $createParagraphNode()
const direction = this.getDirection()
newElement.setDirection(direction)
this.insertAfter(newElement, restoreSelection)
return newElement
}
collapseAtStart(): true {
const newElement = !this.isEmpty() ? $createHeadingNode(this.getTag()) : $createParagraphNode()
const children = this.getChildren()
children.forEach((child) => newElement.append(child))
this.replace(newElement)
return true
}*/
extractWithChild() {
return true;
}
isInline() {
return this.__mdastType === "mdxTextExpression";
}
decorate(editor) {
return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, "{", /* @__PURE__ */ React__default.createElement("span", { className: styles.inputSizer, "data-value": this.getValue() }, /* @__PURE__ */ React__default.createElement(
"input",
{
size: 1,
onKeyDown: (e) => {
const value = e.target.value;
if (value === "" && e.key === "Backspace" || e.key === "Delete") {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
e.preventDefault();
editor.update(() => {
this.selectPrevious();
this.remove();
});
}
},
onChange: (e) => {
e.target.parentElement.dataset.value = e.target.value;
editor.update(() => {
this.getWritable().__value = e.target.value;
});
},
type: "text",
value: this.getValue()
}
)), "}");
}
}
function $createLexicalMdxExpressionNode(value, type) {
return $applyNodeReplacement(new LexicalMdxExpressionNode(value, type));
}
function $isLexicalMdxExpressionNode(node) {
return node instanceof LexicalMdxExpressionNode;
}
export {
$createLexicalMdxExpressionNode,
$isLexicalMdxExpressionNode,
LexicalMdxExpressionNode
};
@@ -0,0 +1,14 @@
import { $isLexicalMdxExpressionNode } from "./LexicalMdxExpressionNode.js";
const LexicalMdxExpressionVisitor = {
testLexicalNode: $isLexicalMdxExpressionNode,
visitLexicalNode({ actions, mdastParent, lexicalNode }) {
const mdastNode = {
type: lexicalNode.getMdastType(),
value: lexicalNode.getValue()
};
actions.appendToParent(mdastParent, mdastNode);
}
};
export {
LexicalMdxExpressionVisitor
};
@@ -0,0 +1,11 @@
import { $createLexicalMdxExpressionNode } from "./LexicalMdxExpressionNode.js";
const MdastMdxExpressionVisitor = {
testNode: (node) => node.type === "mdxTextExpression" || node.type === "mdxFlowExpression",
visitNode({ lexicalParent, mdastNode }) {
lexicalParent.append($createLexicalMdxExpressionNode(mdastNode.value, mdastNode.type));
},
priority: -200
};
export {
MdastMdxExpressionVisitor
};
@@ -0,0 +1,8 @@
const MdastMdxJsEsmVisitor = {
testNode: "mdxjsEsm",
visitNode() {
}
};
export {
MdastMdxJsEsmVisitor
};
@@ -0,0 +1,28 @@
import { $createParagraphNode } from "lexical";
import { $createLexicalJsxNode } from "./LexicalJsxNode.js";
const MdastMdxJsxElementVisitor = {
testNode: (node, { jsxComponentDescriptors }) => {
if (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") {
const descriptor = jsxComponentDescriptors.find((descriptor2) => descriptor2.name === node.name) ?? jsxComponentDescriptors.find((descriptor2) => descriptor2.name === "*");
return descriptor !== void 0;
}
return false;
},
visitNode({ lexicalParent, mdastNode, descriptors: { jsxComponentDescriptors }, metaData }) {
const descriptor = jsxComponentDescriptors.find((descriptor2) => descriptor2.name === mdastNode.name) ?? jsxComponentDescriptors.find((descriptor2) => descriptor2.name === "*");
if ((descriptor == null ? void 0 : descriptor.kind) === "text" && mdastNode.type === "mdxJsxFlowElement") {
const patchedNode = { ...mdastNode, type: "mdxJsxTextElement" };
const paragraph = $createParagraphNode();
paragraph.append($createLexicalJsxNode(patchedNode, mdastNode.name ? metaData.importDeclarations[mdastNode.name] : void 0));
lexicalParent.append(paragraph);
} else {
lexicalParent.append(
$createLexicalJsxNode(mdastNode, mdastNode.name ? metaData.importDeclarations[mdastNode.name] : void 0)
);
}
},
priority: -200
};
export {
MdastMdxJsxElementVisitor
};
+97
View File
@@ -0,0 +1,97 @@
import { mdxToMarkdown, mdxFromMarkdown } from "mdast-util-mdx";
import { mdxjs } from "micromark-extension-mdxjs";
import { insertDecoratorNode$, jsxComponentDescriptors$, addToMarkdownExtension$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addSyntaxExtension$, addMdastExtension$, jsxIsAvailable$ } from "../core/index.js";
import { $createLexicalJsxNode, LexicalJsxNode } from "./LexicalJsxNode.js";
import { LexicalJsxVisitor } from "./LexicalJsxVisitor.js";
import { MdastMdxJsEsmVisitor } from "./MdastMdxJsEsmVisitor.js";
import { MdastMdxJsxElementVisitor } from "./MdastMdxJsxElementVisitor.js";
import { Signal, map } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { MdastMdxExpressionVisitor } from "./MdastMdxExpressionVisitor.js";
import { LexicalMdxExpressionNode } from "./LexicalMdxExpressionNode.js";
import { LexicalMdxExpressionVisitor } from "./LexicalMdxExpressionVisitor.js";
import { GenericJsxEditor } from "../../jsx-editors/GenericJsxEditor.js";
function isMdastJsxNode(node) {
return node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
}
const isExpressionValue = (value) => {
if (value !== null && typeof value === "object" && "type" in value && "value" in value && typeof value.value === "string") {
return true;
}
return false;
};
const toMdastJsxAttributes = (attributes) => Object.entries(attributes).map(
([name, value]) => ({
type: "mdxJsxAttribute",
name,
value: isExpressionValue(value) ? { type: "mdxJsxAttributeValueExpression", value: value.value } : value
})
);
const insertJsx$ = Signal((r) => {
r.link(
r.pipe(
insertJsx$,
map(({ kind, name, children, props }) => {
return () => {
const attributes = toMdastJsxAttributes(props);
if (kind === "flow") {
return $createLexicalJsxNode({
type: "mdxJsxFlowElement",
name,
children: children ?? [],
attributes
});
} else {
return $createLexicalJsxNode({
type: "mdxJsxTextElement",
name,
children: children ?? [],
attributes
});
}
};
})
),
insertDecoratorNode$
);
});
const fragmentDescriptor = {
name: null,
kind: "flow",
props: [],
hasChildren: true,
Editor: GenericJsxEditor
};
const getDescriptors = (params) => {
if (params) {
if (params.allowFragment ?? true) {
return [fragmentDescriptor, ...params.jsxComponentDescriptors];
}
return params.jsxComponentDescriptors;
}
return [fragmentDescriptor];
};
const jsxPlugin = realmPlugin({
init: (realm, params) => {
realm.pubIn({
// import
[jsxIsAvailable$]: true,
[addMdastExtension$]: mdxFromMarkdown(),
[addSyntaxExtension$]: mdxjs(),
[addImportVisitor$]: [MdastMdxJsxElementVisitor, MdastMdxJsEsmVisitor, MdastMdxExpressionVisitor],
// export
[addLexicalNode$]: [LexicalJsxNode, LexicalMdxExpressionNode],
[addExportVisitor$]: [LexicalJsxVisitor, LexicalMdxExpressionVisitor],
[addToMarkdownExtension$]: mdxToMarkdown(),
[jsxComponentDescriptors$]: getDescriptors(params)
});
},
update(realm, params) {
realm.pub(jsxComponentDescriptors$, getDescriptors(params));
}
});
export {
insertJsx$,
isMdastJsxNode,
jsxPlugin
};
+231
View File
@@ -0,0 +1,231 @@
import * as RadixPopover from "@radix-ui/react-popover";
import * as Tooltip from "@radix-ui/react-tooltip";
import React__default from "react";
import { editorRootElementRef$, activeEditor$, iconComponentFor$, useTranslation } from "../core/index.js";
import { DownshiftAutoComplete } from "../core/ui/DownshiftAutoComplete.js";
import styles from "../../styles/ui.module.css.js";
import classNames from "classnames";
import { createCommand } from "lexical";
import { useForm } from "react-hook-form";
import { linkDialogState$, linkAutocompleteSuggestions$, onClickLinkCallback$, showLinkTitleField$, onWindowChange$, updateLink$, cancelLinkEdit$, switchFromPreviewToLinkEdit$, removeLink$ } from "./index.js";
import { useCellValues, usePublisher } from "@mdxeditor/gurx";
createCommand();
function LinkEditForm({
url,
title,
text,
onSubmit,
onCancel,
linkAutocompleteSuggestions,
showLinkTitleField,
showAnchorTextField
}) {
const {
register,
handleSubmit,
control,
setValue,
reset: _
} = useForm({
values: {
url,
title,
text
}
});
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement(
"form",
{
onSubmit: (e) => {
void handleSubmit(onSubmit)(e);
e.stopPropagation();
e.preventDefault();
},
onReset: (e) => {
e.stopPropagation();
onCancel();
},
onKeyDown: (e) => {
if (e.key === "Escape") {
e.stopPropagation();
onCancel();
}
},
className: classNames(styles.multiFieldForm, styles.linkDialogEditForm)
},
/* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-url" }, t("createLink.url", "URL")), /* @__PURE__ */ React__default.createElement(
DownshiftAutoComplete,
{
register,
initialInputValue: url,
inputName: "url",
suggestions: linkAutocompleteSuggestions,
setValue,
control,
placeholder: t("createLink.urlPlaceholder", "Select or paste an URL"),
autofocus: true
}
)),
showAnchorTextField ? /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-text", title: t("createLink.textTooltip", "The text to be displayed for the link") }, t("createLink.text", "Anchor text")), /* @__PURE__ */ React__default.createElement("input", { id: "link-text", className: styles.textInput, size: 40, ...register("text") })) : null,
showLinkTitleField ? /* @__PURE__ */ React__default.createElement("div", { className: styles.formField }, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-title", title: t("createLink.titleTooltip", "The link's title attribute, shown on hover") }, t("createLink.title", "Link title")), /* @__PURE__ */ React__default.createElement("input", { id: "link-title", className: styles.textInput, size: 40, ...register("title") })) : null,
/* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement(
"button",
{
type: "submit",
title: t("createLink.saveTooltip", "Set URL"),
"aria-label": t("createLink.saveTooltip", "Set URL"),
className: classNames(styles.primaryButton)
},
t("dialogControls.save", "Save")
), /* @__PURE__ */ React__default.createElement(
"button",
{
type: "reset",
title: t("createLink.cancelTooltip", "Cancel change"),
"aria-label": t("createLink.cancelTooltip", "Cancel change"),
className: classNames(styles.secondaryButton)
},
t("dialogControls.cancel", "Cancel")
))
);
}
const LinkDialog = () => {
const [
editorRootElementRef,
activeEditor,
iconComponentFor,
linkDialogState,
linkAutocompleteSuggestions,
onClickLinkCallback,
showLinkTitleField
] = useCellValues(
editorRootElementRef$,
activeEditor$,
iconComponentFor$,
linkDialogState$,
linkAutocompleteSuggestions$,
onClickLinkCallback$,
showLinkTitleField$
);
const publishWindowChange = usePublisher(onWindowChange$);
const updateLink = usePublisher(updateLink$);
const cancelLinkEdit = usePublisher(cancelLinkEdit$);
const switchFromPreviewToLinkEdit = usePublisher(switchFromPreviewToLinkEdit$);
const removeLink = usePublisher(removeLink$);
React__default.useEffect(() => {
const update = () => {
activeEditor == null ? void 0 : activeEditor.getEditorState().read(() => {
publishWindowChange(true);
});
};
window.addEventListener("resize", update);
window.addEventListener("scroll", update);
return () => {
window.removeEventListener("resize", update);
window.removeEventListener("scroll", update);
};
}, [activeEditor, publishWindowChange]);
const [copyUrlTooltipOpen, setCopyUrlTooltipOpen] = React__default.useState(false);
const t = useTranslation();
if (linkDialogState.type === "inactive")
return null;
const theRect = linkDialogState.rectangle;
const urlIsExternal = linkDialogState.type === "preview" && linkDialogState.url.startsWith("http");
return /* @__PURE__ */ React__default.createElement(RadixPopover.Root, { open: true }, /* @__PURE__ */ React__default.createElement(
RadixPopover.Anchor,
{
"data-visible": linkDialogState.type === "edit",
className: styles.linkDialogAnchor,
style: {
top: `${theRect.top}px`,
left: `${theRect.left}px`,
width: `${theRect.width}px`,
height: `${theRect.height}px`
}
}
), /* @__PURE__ */ React__default.createElement(RadixPopover.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(
RadixPopover.Content,
{
className: classNames(styles.linkDialogPopoverContent),
sideOffset: 5,
onOpenAutoFocus: (e) => {
e.preventDefault();
},
key: linkDialogState.linkNodeKey
},
linkDialogState.type === "edit" && /* @__PURE__ */ React__default.createElement(
LinkEditForm,
{
url: linkDialogState.url,
title: linkDialogState.title,
text: linkDialogState.text,
onSubmit: updateLink,
onCancel: cancelLinkEdit.bind(null),
linkAutocompleteSuggestions,
showLinkTitleField,
showAnchorTextField: linkDialogState.withAnchorText
}
),
linkDialogState.type === "preview" && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
"a",
{
className: styles.linkDialogPreviewAnchor,
href: linkDialogState.url,
...urlIsExternal ? { target: "_blank", rel: "noreferrer" } : {},
onClick: (e) => {
if (onClickLinkCallback !== null) {
e.preventDefault();
onClickLinkCallback(linkDialogState.url);
}
},
title: urlIsExternal ? t("linkPreview.open", `Open {{url}} in new window`, { url: linkDialogState.url }) : linkDialogState.url
},
/* @__PURE__ */ React__default.createElement("span", null, linkDialogState.url),
urlIsExternal && iconComponentFor("open_in_new")
), /* @__PURE__ */ React__default.createElement(
ActionButton,
{
onClick: () => {
switchFromPreviewToLinkEdit();
},
title: t("linkPreview.edit", "Edit link URL"),
"aria-label": t("linkPreview.edit", "Edit link URL")
},
iconComponentFor("edit")
), /* @__PURE__ */ React__default.createElement(Tooltip.Provider, null, /* @__PURE__ */ React__default.createElement(Tooltip.Root, { open: copyUrlTooltipOpen }, /* @__PURE__ */ React__default.createElement(Tooltip.Trigger, { asChild: true }, /* @__PURE__ */ React__default.createElement(
ActionButton,
{
title: t("linkPreview.copyToClipboard", "Copy to clipboard"),
"aria-label": t("linkPreview.copyToClipboard", "Copy to clipboard"),
onClick: () => {
void window.navigator.clipboard.writeText(linkDialogState.url).then(() => {
setCopyUrlTooltipOpen(true);
setTimeout(() => {
setCopyUrlTooltipOpen(false);
}, 1e3);
});
}
},
copyUrlTooltipOpen ? iconComponentFor("check") : iconComponentFor("content_copy")
)), /* @__PURE__ */ React__default.createElement(Tooltip.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(Tooltip.Content, { className: classNames(styles.tooltipContent), sideOffset: 5 }, t("linkPreview.copied", "Copied!"), /* @__PURE__ */ React__default.createElement(Tooltip.Arrow, null))))), /* @__PURE__ */ React__default.createElement(
ActionButton,
{
title: t("linkPreview.remove", "Remove link"),
"aria-label": t("linkPreview.remove", "Remove link"),
onClick: () => {
removeLink();
}
},
iconComponentFor("link_off")
)),
/* @__PURE__ */ React__default.createElement(RadixPopover.Arrow, { className: styles.popoverArrow })
)));
};
const ActionButton = React__default.forwardRef(({ className, ...props }, ref) => {
return /* @__PURE__ */ React__default.createElement("button", { className: classNames(styles.actionButton, className), ref, ...props });
});
export {
LinkDialog,
LinkEditForm
};
+306
View File
@@ -0,0 +1,306 @@
import { $isLinkNode, $createLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND, LinkNode } from "@lexical/link";
import { Signal, Cell, Action, withLatestFrom, map, filter } from "@mdxeditor/gurx";
import { KEY_ESCAPE_COMMAND, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $getSelection, $isRangeSelection, COMMAND_PRIORITY_HIGH, $getNodeByKey, $createTextNode, $insertNodes, $isTextNode, $getNearestNodeFromDOMNode } from "lexical";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { IS_APPLE } from "../../utils/detectMac.js";
import { getSelectionRectangle, getSelectedNode } from "../../utils/lexicalHelpers.js";
import { createActiveEditorSubscription$, viewMode$, readOnly$, activeEditor$, currentSelection$, addComposerChild$ } from "../core/index.js";
import { LinkDialog } from "./LinkDialog.js";
import { $findMatchingParent } from "@lexical/utils";
function getLinkNodeInSelection(selection) {
if (!selection) {
return null;
}
const node = getSelectedNode(selection);
if (node === null) {
return null;
}
const parent = node.getParent();
if ($isLinkNode(parent)) {
return parent;
} else if ($isLinkNode(node)) {
return node;
}
return null;
}
const onWindowChange$ = Signal();
const linkDialogState$ = Cell({ type: "inactive" }, (r) => {
r.pub(createActiveEditorSubscription$, (editor) => {
return editor.registerCommand(
KEY_ESCAPE_COMMAND,
() => {
const state = r.getValue(linkDialogState$);
if (state.type === "preview") {
r.pub(linkDialogState$, { type: "inactive" });
return true;
}
return false;
},
COMMAND_PRIORITY_LOW
);
});
r.sub(r.pipe(viewMode$), (viewMode) => {
if (viewMode !== "rich-text") {
r.pub(linkDialogState$, { type: "inactive" });
}
});
r.pub(createActiveEditorSubscription$, (editor) => {
return editor.registerCommand(
KEY_DOWN_COMMAND,
(event) => {
if (event.key === "k" && (IS_APPLE ? event.metaKey : event.ctrlKey) && !r.getValue(readOnly$)) {
const selection = $getSelection();
if ($isRangeSelection(selection)) {
r.pub(openLinkEditDialog$);
event.stopPropagation();
event.preventDefault();
return true;
} else {
return false;
}
}
return false;
},
COMMAND_PRIORITY_HIGH
);
});
r.sub(r.pipe(switchFromPreviewToLinkEdit$, withLatestFrom(linkDialogState$, activeEditor$)), ([, state, editor]) => {
if (state.type === "preview") {
setTimeout(() => {
editor == null ? void 0 : editor.getEditorState().read(() => {
const node = $getNodeByKey(state.linkNodeKey);
const withAnchorText = $isLinkNode(node) ? node.getTextContent().length > 0 && node.getChildrenSize() <= 1 : false;
const text = withAnchorText && node ? node.getTextContent() : "";
r.pub(linkDialogState$, {
type: "edit",
initialUrl: state.url,
url: state.url,
title: state.title,
text,
withAnchorText,
linkNodeKey: state.linkNodeKey,
rectangle: state.rectangle
});
});
});
} else {
throw new Error("Cannot switch to edit mode when not in preview mode");
}
});
r.sub(r.pipe(updateLink$, withLatestFrom(activeEditor$, linkDialogState$, currentSelection$)), ([payload, editor, state, selection]) => {
var _a, _b, _c;
const text = ((_a = payload.text) == null ? void 0 : _a.trim()) ?? "";
const url = ((_b = payload.url) == null ? void 0 : _b.trim()) ?? "";
const title = ((_c = payload.title) == null ? void 0 : _c.trim()) ?? "";
if (url !== "") {
if (selection == null ? void 0 : selection.isCollapsed()) {
const linkContent = text || title || url;
editor == null ? void 0 : editor.update(
() => {
const linkNode = getLinkNodeInSelection(selection);
if (!linkNode) {
const node = $createLinkNode(url, { title });
node.append($createTextNode(linkContent));
$insertNodes([node]);
node.select();
} else {
if ($isAutoLinkNode(linkNode)) {
const newLinkNode = $createLinkNode(url, { title });
newLinkNode.append($createTextNode(text));
linkNode.replace(newLinkNode);
newLinkNode.select();
} else {
linkNode.setURL(url);
linkNode.setTitle(title);
updateLinkText(linkNode.getFirstChild(), text);
}
}
},
{ discrete: true }
);
} else {
editor == null ? void 0 : editor.update(() => {
updateLinkText(selection == null ? void 0 : selection.anchor.getNode(), text);
});
editor == null ? void 0 : editor.dispatchCommand(TOGGLE_LINK_COMMAND, { url, title });
}
r.pub(linkDialogState$, {
type: "preview",
linkNodeKey: state.linkNodeKey,
rectangle: state.rectangle,
title,
url
});
} else {
if (state.type === "edit" && state.initialUrl !== "") {
editor == null ? void 0 : editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
}
r.pub(linkDialogState$, {
type: "inactive"
});
}
});
r.link(
r.pipe(
cancelLinkEdit$,
withLatestFrom(linkDialogState$, activeEditor$),
map(([, state, editor]) => {
if (state.type === "edit") {
editor == null ? void 0 : editor.focus();
if (state.initialUrl === "") {
return {
type: "inactive"
};
} else {
return {
type: "preview",
url: state.initialUrl,
linkNodeKey: state.linkNodeKey,
rectangle: state.rectangle
};
}
} else {
throw new Error("Cannot cancel edit when not in edit mode");
}
})
),
linkDialogState$
);
r.link(
r.pipe(
r.combine(currentSelection$, onWindowChange$),
withLatestFrom(activeEditor$, linkDialogState$, readOnly$),
map(([[selection], activeEditor, _, readOnly]) => {
if ($isRangeSelection(selection) && activeEditor && !readOnly) {
const node = getLinkNodeInSelection(selection);
if (!selection.isCollapsed())
return { type: "inactive" };
if (node) {
const rect = getSelectionRectangle(activeEditor);
if (!rect) {
return { type: "inactive" };
}
return {
type: "preview",
url: node.getURL(),
linkNodeKey: node.getKey(),
title: node.getTitle(),
rectangle: rect
};
} else {
return { type: "inactive" };
}
} else {
return { type: "inactive" };
}
})
),
linkDialogState$
);
});
const updateLink$ = Signal();
const cancelLinkEdit$ = Action();
const applyLinkChanges$ = Action();
const switchFromPreviewToLinkEdit$ = Action();
const removeLink$ = Action((r) => {
r.sub(r.pipe(removeLink$, withLatestFrom(activeEditor$)), ([, editor]) => {
editor == null ? void 0 : editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
});
});
const openLinkEditDialog$ = Action((r) => {
r.sub(
r.pipe(
openLinkEditDialog$,
withLatestFrom(currentSelection$, activeEditor$),
filter(([, selection]) => $isRangeSelection(selection))
),
([, selection, editor]) => {
editor == null ? void 0 : editor.focus(() => {
setTimeout(() => {
editor.getEditorState().read(() => {
const linkNode = getLinkNodeInSelection(selection);
const rectangle = getSelectionRectangle(editor);
const initialUrl = (linkNode == null ? void 0 : linkNode.getURL()) ?? "";
const url = (linkNode == null ? void 0 : linkNode.getURL()) ?? "";
const title = (linkNode == null ? void 0 : linkNode.getTitle()) ?? "";
const linkNodeKey = (linkNode == null ? void 0 : linkNode.getKey()) ?? "";
const withAnchorText = linkNode ? linkNode.getTextContent().length > 0 && linkNode.getChildrenSize() <= 1 : Boolean(selection == null ? void 0 : selection.isCollapsed());
const text = withAnchorText && linkNode ? linkNode.getTextContent() : "";
r.pub(linkDialogState$, {
type: "edit",
initialUrl,
url,
title,
text,
withAnchorText,
linkNodeKey,
rectangle
});
});
});
});
}
);
});
const linkAutocompleteSuggestions$ = Cell([]);
const onClickLinkCallback$ = Cell(null);
const onReadOnlyClickLinkCallback$ = Cell(null, (r) => {
r.pub(createActiveEditorSubscription$, (editor) => {
function onClick(event) {
const [readOnly, callback] = r.getValues([readOnly$, onReadOnlyClickLinkCallback$]);
if (!readOnly || callback === null) {
return;
}
editor.update(() => {
const nearestNode = $getNearestNodeFromDOMNode(event.target);
if (nearestNode !== null) {
const targetNode = $findMatchingParent(nearestNode, (node) => node instanceof LinkNode);
if (targetNode !== null) {
callback(event, targetNode, targetNode.getURL());
}
}
});
}
return editor.registerRootListener((rootElement, prevRoot) => {
if (rootElement) {
rootElement.addEventListener("click", onClick);
}
if (prevRoot) {
prevRoot.removeEventListener("click", onClick);
}
});
});
});
function updateLinkText(node, text) {
if ($isTextNode(node) && text) {
node.setTextContent(text);
node.selectStart();
}
}
const showLinkTitleField$ = Cell(true);
const linkDialogPlugin = realmPlugin({
init(r, params) {
r.pub(addComposerChild$, (params == null ? void 0 : params.LinkDialog) ?? LinkDialog);
r.pub(onClickLinkCallback$, (params == null ? void 0 : params.onClickLinkCallback) ?? null);
r.pub(onReadOnlyClickLinkCallback$, (params == null ? void 0 : params.onReadOnlyClickLinkCallback) ?? null);
r.pub(showLinkTitleField$, (params == null ? void 0 : params.showLinkTitleField) ?? true);
},
update(r, params = {}) {
r.pub(linkAutocompleteSuggestions$, params.linkAutocompleteSuggestions ?? []);
}
});
export {
applyLinkChanges$,
cancelLinkEdit$,
linkAutocompleteSuggestions$,
linkDialogPlugin,
linkDialogState$,
onClickLinkCallback$,
onReadOnlyClickLinkCallback$,
onWindowChange$,
openLinkEditDialog$,
removeLink$,
showLinkTitleField$,
switchFromPreviewToLinkEdit$,
updateLink$
};
+18
View File
@@ -0,0 +1,18 @@
import { AutoLinkPlugin, createLinkMatcherWithRegExp } from "@lexical/react/LexicalAutoLinkPlugin";
import React__default from "react";
const URL_REGEX = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
const EMAIL_REGEX = /(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;
const MATCHERS = [
createLinkMatcherWithRegExp(URL_REGEX, (text) => {
return text.startsWith("http") ? text : `https://${text}`;
}),
createLinkMatcherWithRegExp(EMAIL_REGEX, (text) => {
return `mailto:${text}`;
})
];
const LexicalAutoLinkPlugin = () => {
return /* @__PURE__ */ React__default.createElement(AutoLinkPlugin, { matchers: MATCHERS });
};
export {
LexicalAutoLinkPlugin
};
+10
View File
@@ -0,0 +1,10 @@
import { $isLinkNode } from "@lexical/link";
const LexicalLinkVisitor = {
testLexicalNode: $isLinkNode,
visitLexicalNode: ({ lexicalNode, actions }) => {
actions.addAndStepInto("link", { url: lexicalNode.getURL(), title: lexicalNode.getTitle() });
}
};
export {
LexicalLinkVisitor
};
+14
View File
@@ -0,0 +1,14 @@
import { $createLinkNode } from "@lexical/link";
const MdastLinkVisitor = {
testNode: "link",
visitNode({ mdastNode, actions }) {
actions.addAndStepInto(
$createLinkNode(mdastNode.url, {
title: mdastNode.title
})
);
}
};
export {
MdastLinkVisitor
};
+30
View File
@@ -0,0 +1,30 @@
import React__default from "react";
import { MdastLinkVisitor } from "./MdastLinkVisitor.js";
import { LexicalLinkVisitor } from "./LexicalLinkVisitor.js";
import { LinkNode, AutoLinkNode } from "@lexical/link";
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
import { LexicalAutoLinkPlugin } from "./AutoLinkPlugin.js";
import { Cell } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { addComposerChild$, addNestedEditorChild$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addActivePlugin$ } from "../core/index.js";
const disableAutoLink$ = Cell(false);
const linkPlugin = realmPlugin({
init(realm, params) {
const disableAutoLink = Boolean(params == null ? void 0 : params.disableAutoLink);
const linkPluginProps = (params == null ? void 0 : params.validateUrl) ? { validateUrl: params.validateUrl } : {};
const EditorChild = () => /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(LinkPlugin, { ...linkPluginProps }), disableAutoLink ? null : /* @__PURE__ */ React__default.createElement(LexicalAutoLinkPlugin, null));
realm.pubIn({
[addActivePlugin$]: "link",
[addImportVisitor$]: MdastLinkVisitor,
[addLexicalNode$]: [LinkNode, AutoLinkNode],
[addExportVisitor$]: LexicalLinkVisitor,
[disableAutoLink$]: disableAutoLink,
[addNestedEditorChild$]: EditorChild,
[addComposerChild$]: EditorChild
});
}
});
export {
disableAutoLink$,
linkPlugin
};
@@ -0,0 +1,41 @@
import { $isListItemNode, $isListNode } from "@lexical/list";
import { $isTextNode, $isLineBreakNode, $isElementNode, $isDecoratorNode } from "lexical";
const LexicalListItemVisitor = {
testLexicalNode: $isListItemNode,
visitLexicalNode: ({ lexicalNode, mdastParent, actions }) => {
const children = lexicalNode.getChildren();
const firstChild = children[0];
if (children.length === 1 && $isListNode(firstChild)) {
const prevListItemNode = mdastParent.children.at(-1);
if (!prevListItemNode) {
actions.visitChildren(firstChild, mdastParent);
} else {
actions.visitChildren(lexicalNode, prevListItemNode);
}
} else {
const parentList = lexicalNode.getParent();
const listItem = actions.appendToParent(mdastParent, {
type: "listItem",
checked: parentList.getListType() === "check" ? Boolean(lexicalNode.getChecked()) : void 0,
spread: false,
children: []
});
let surroundingParagraph = null;
for (const child of lexicalNode.getChildren()) {
if ($isTextNode(child) || $isLineBreakNode(child) || child.isInline() && ($isElementNode(child) || $isDecoratorNode(child))) {
surroundingParagraph ?? (surroundingParagraph = actions.appendToParent(listItem, {
type: "paragraph",
children: []
}));
actions.visit(child, surroundingParagraph);
} else {
surroundingParagraph = null;
actions.visit(child, listItem);
}
}
}
}
};
export {
LexicalListItemVisitor
};
@@ -0,0 +1,13 @@
import { $isListNode } from "@lexical/list";
const LexicalListVisitor = {
testLexicalNode: $isListNode,
visitLexicalNode: ({ lexicalNode, actions }) => {
actions.addAndStepInto("list", {
ordered: lexicalNode.getListType() === "number",
spread: false
});
}
};
export {
LexicalListVisitor
};
@@ -0,0 +1,11 @@
import { $createListItemNode } from "@lexical/list";
const MdastListItemVisitor = {
testNode: "listItem",
visitNode({ mdastNode, actions, lexicalParent }) {
const isChecked = lexicalParent.getListType() === "check" ? mdastNode.checked ?? false : void 0;
actions.addAndStepInto($createListItemNode(isChecked));
}
};
export {
MdastListItemVisitor
};
+19
View File
@@ -0,0 +1,19 @@
import { $createListNode, $isListItemNode, $createListItemNode } from "@lexical/list";
const MdastListVisitor = {
testNode: "list",
visitNode: function({ mdastNode, lexicalParent, actions }) {
const listType = mdastNode.children.some((e) => typeof e.checked === "boolean") ? "check" : mdastNode.ordered ? "number" : "bullet";
const lexicalNode = $createListNode(listType);
if ($isListItemNode(lexicalParent)) {
const dedicatedParent = $createListItemNode();
dedicatedParent.append(lexicalNode);
lexicalParent.insertAfter(dedicatedParent);
} else {
lexicalParent.append(lexicalNode);
}
actions.visitChildren(mdastNode, lexicalNode);
}
};
export {
MdastListVisitor
};
+105
View File
@@ -0,0 +1,105 @@
import { currentSelection$, activeEditor$, rootEditor$, addNestedEditorChild$, addComposerChild$, addToMarkdownExtension$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addSyntaxExtension$, addMdastExtension$, addActivePlugin$ } from "../core/index.js";
import { MdastListVisitor } from "./MdastListVisitor.js";
import { MdastListItemVisitor } from "./MdastListItemVisitor.js";
import { LexicalListVisitor } from "./LexicalListVisitor.js";
import { LexicalListItemVisitor } from "./LexicalListItemVisitor.js";
import { $isListNode, ListNode, ListItemNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INSERT_CHECK_LIST_COMMAND, REMOVE_LIST_COMMAND, $getListDepth, $isListItemNode } from "@lexical/list";
import { $isRootOrShadowRoot, INDENT_CONTENT_COMMAND, COMMAND_PRIORITY_CRITICAL, $getSelection, $isRangeSelection, $isElementNode } from "lexical";
import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin.js";
import { CheckListPlugin } from "@lexical/react/LexicalCheckListPlugin.js";
import { ListPlugin } from "@lexical/react/LexicalListPlugin.js";
import { $findMatchingParent, $getNearestNodeOfType } from "@lexical/utils";
import { gfmTaskListItem } from "micromark-extension-gfm-task-list-item";
import { gfmTaskListItemToMarkdown, gfmTaskListItemFromMarkdown } from "mdast-util-gfm-task-list-item";
import { Cell, Signal, withLatestFrom } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
const ListTypeCommandMap = /* @__PURE__ */ new Map([
["number", INSERT_ORDERED_LIST_COMMAND],
["bullet", INSERT_UNORDERED_LIST_COMMAND],
["check", INSERT_CHECK_LIST_COMMAND],
["", REMOVE_LIST_COMMAND]
]);
const currentListType$ = Cell("", (r) => {
r.sub(r.pipe(currentSelection$, withLatestFrom(activeEditor$)), ([selection, theEditor]) => {
if (!selection || !theEditor) {
return;
}
const anchorNode = selection.anchor.getNode();
let element = anchorNode.getKey() === "root" ? anchorNode : $findMatchingParent(anchorNode, (e) => {
const parent = e.getParent();
return parent !== null && $isRootOrShadowRoot(parent);
});
element ?? (element = anchorNode.getTopLevelElementOrThrow());
const elementKey = element.getKey();
const elementDOM = theEditor.getElementByKey(elementKey);
if (elementDOM !== null) {
if ($isListNode(element)) {
const parentList = $getNearestNodeOfType(anchorNode, ListNode);
const type = parentList ? parentList.getListType() : element.getListType();
r.pub(currentListType$, type);
} else {
r.pub(currentListType$, "");
}
}
});
});
const applyListType$ = Signal((r) => {
r.sub(r.pipe(applyListType$, withLatestFrom(activeEditor$)), ([listType, theEditor]) => {
theEditor == null ? void 0 : theEditor.dispatchCommand(ListTypeCommandMap.get(listType), void 0);
});
});
const listsPlugin = realmPlugin({
init(realm) {
var _a;
(_a = realm.getValue(rootEditor$)) == null ? void 0 : _a.registerCommand(INDENT_CONTENT_COMMAND, () => !isIndentPermitted(7), COMMAND_PRIORITY_CRITICAL);
realm.pubIn({
[addActivePlugin$]: "lists",
[addMdastExtension$]: gfmTaskListItemFromMarkdown(),
[addSyntaxExtension$]: gfmTaskListItem(),
[addImportVisitor$]: [MdastListVisitor, MdastListItemVisitor],
[addLexicalNode$]: [ListItemNode, ListNode],
[addExportVisitor$]: [LexicalListVisitor, LexicalListItemVisitor],
[addToMarkdownExtension$]: gfmTaskListItemToMarkdown(),
[addComposerChild$]: [TabIndentationPlugin, ListPlugin, CheckListPlugin],
[addNestedEditorChild$]: [TabIndentationPlugin, ListPlugin, CheckListPlugin]
});
}
});
function getElementNodesInSelection(selection) {
const nodesInSelection = selection.getNodes();
if (nodesInSelection.length === 0) {
return /* @__PURE__ */ new Set([selection.anchor.getNode().getParentOrThrow(), selection.focus.getNode().getParentOrThrow()]);
}
return new Set(nodesInSelection.map((n) => $isElementNode(n) ? n : n.getParentOrThrow()));
}
function isIndentPermitted(maxDepth) {
const selection = $getSelection();
if (!$isRangeSelection(selection)) {
return false;
}
const elementNodesInSelection = getElementNodesInSelection(selection);
let totalDepth = 0;
for (const elementNode of elementNodesInSelection) {
if ($isListNode(elementNode)) {
totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth);
} else if ($isListItemNode(elementNode)) {
const parent = elementNode.getParent();
if ((parent == null ? void 0 : parent.getChildren().length) === 1) {
const grandParentListItem = parent.getParent();
if ($isListItemNode(grandParentListItem) && grandParentListItem.getChildren().length === 1) {
return false;
}
}
if (!$isListNode(parent)) {
throw new Error("ListMaxIndentLevelPlugin: A ListItemNode must have a ListNode for a parent.");
}
totalDepth = Math.max($getListDepth(parent) + 1, totalDepth);
}
}
return totalDepth <= maxDepth;
}
export {
applyListType$,
currentListType$,
listsPlugin
};
+111
View File
@@ -0,0 +1,111 @@
import { BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, INLINE_CODE, ITALIC_STAR, ITALIC_UNDERSCORE, QUOTE, LINK, ORDERED_LIST, UNORDERED_LIST, CHECK_LIST, CODE } from "@lexical/markdown";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { HeadingNode, $isHeadingNode, $createHeadingNode } from "@lexical/rich-text";
import React__default from "react";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { CodeBlockNode, $createCodeBlockNode } from "../codeblock/CodeBlockNode.js";
import { activePlugins$, addNestedEditorChild$, addComposerChild$ } from "../core/index.js";
import { allowedHeadingLevels$ } from "../headings/index.js";
import { HorizontalRuleNode, $createHorizontalRuleNode, $isHorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode";
const markdownShortcutPlugin = realmPlugin({
init(realm) {
const pluginIds = realm.getValue(activePlugins$);
const allowedHeadingLevels = pluginIds.includes("headings") ? realm.getValue(allowedHeadingLevels$) : [];
const transformers = pickTransformersForActivePlugins(pluginIds, allowedHeadingLevels);
realm.pubIn({
[addComposerChild$]: () => /* @__PURE__ */ React__default.createElement(MarkdownShortcutPlugin, { transformers }),
[addNestedEditorChild$]: () => /* @__PURE__ */ React__default.createElement(MarkdownShortcutPlugin, { transformers })
});
}
});
const createBlockNode = (createNode) => {
return (parentNode, children, match) => {
const node = createNode(match);
node.append(...children);
parentNode.replace(node);
node.select(0, 0);
};
};
const THEMATIC_BREAK = {
dependencies: [HorizontalRuleNode],
export: (node) => {
return $isHorizontalRuleNode(node) ? "***" : null;
},
regExp: /^(---|\*\*\*|___)\s?$/,
replace: (parentNode, _1, _2, isImport) => {
const line = $createHorizontalRuleNode();
if (isImport || parentNode.getNextSibling() != null) {
parentNode.replace(line);
} else {
parentNode.insertBefore(line);
}
line.selectNext();
},
type: "element"
};
function pickTransformersForActivePlugins(pluginIds, allowedHeadingLevels) {
const transformers = [
BOLD_ITALIC_STAR,
BOLD_ITALIC_UNDERSCORE,
BOLD_STAR,
BOLD_UNDERSCORE,
INLINE_CODE,
ITALIC_STAR,
ITALIC_UNDERSCORE
// HIGHLIGHT,
// STRIKETHROUGH
];
if (pluginIds.includes("headings")) {
const minHeadingLevel = Math.min(...allowedHeadingLevels);
const maxHeadingLevel = Math.max(...allowedHeadingLevels);
const headingRegExp = new RegExp(`^(#{${minHeadingLevel},${maxHeadingLevel}})\\s`);
const HEADING = {
dependencies: [HeadingNode],
export: (node, exportChildren) => {
if (!$isHeadingNode(node)) {
return null;
}
const level = Number(node.getTag().slice(1));
return "#".repeat(level) + " " + exportChildren(node);
},
regExp: headingRegExp,
replace: createBlockNode((match) => {
const tag = `h${match[1].length}`;
return $createHeadingNode(tag);
}),
type: "element"
};
transformers.push(HEADING);
}
if (pluginIds.includes("thematicBreak")) {
transformers.push(THEMATIC_BREAK);
}
if (pluginIds.includes("quote")) {
transformers.push(QUOTE);
}
if (pluginIds.includes("link")) {
transformers.push(LINK);
}
if (pluginIds.includes("lists")) {
transformers.push(ORDERED_LIST, UNORDERED_LIST, CHECK_LIST);
}
if (pluginIds.includes("codeblock")) {
const codeTransformerCopy = {
...CODE,
dependencies: [CodeBlockNode],
replace: (parentNode, _children, match) => {
const codeBlockNode = $createCodeBlockNode({ code: "", language: match[1] ?? "", meta: "" });
parentNode.selectPrevious();
parentNode.replace(codeBlockNode);
setTimeout(() => {
codeBlockNode.select();
}, 80);
}
};
transformers.push(codeTransformerCopy);
}
return transformers;
}
export {
markdownShortcutPlugin
};
+36
View File
@@ -0,0 +1,36 @@
import { $trimTextContentFromAnchor } from "@lexical/selection";
import { $restoreEditorState } from "@lexical/utils";
import { RootNode, $getSelection, $isRangeSelection } from "lexical";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { createRootEditorSubscription$ } from "../core/index.js";
const maxLengthPlugin = realmPlugin({
init: (realm, maxLength = Infinity) => {
realm.pub(createRootEditorSubscription$, (editor) => {
let lastRestoredEditorState = null;
return editor.registerNodeTransform(RootNode, (rootNode) => {
const selection = $getSelection();
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
return;
}
const prevEditorState = editor.getEditorState();
const prevTextContentSize = prevEditorState.read(() => rootNode.getTextContentSize());
const textContentSize = rootNode.getTextContentSize();
if (prevTextContentSize !== textContentSize) {
const delCount = textContentSize - maxLength;
const anchor = selection.anchor;
if (delCount > 0) {
if (prevTextContentSize === maxLength && lastRestoredEditorState !== prevEditorState) {
lastRestoredEditorState = prevEditorState;
$restoreEditorState(editor, prevEditorState);
} else {
$trimTextContentFromAnchor(editor, anchor, delCount);
}
}
}
});
});
}
});
export {
maxLengthPlugin
};
@@ -0,0 +1,12 @@
import { $isQuoteNode } from "@lexical/rich-text";
const LexicalQuoteVisitor = {
testLexicalNode: $isQuoteNode,
visitLexicalNode: ({ lexicalNode, mdastParent, actions }) => {
const paragraph = { type: "paragraph", children: [] };
actions.appendToParent(mdastParent, { type: "blockquote", children: [paragraph] });
actions.visitChildren(lexicalNode, paragraph);
}
};
export {
LexicalQuoteVisitor
};
@@ -0,0 +1,10 @@
import { $createQuoteNode } from "@lexical/rich-text";
const MdastBlockQuoteVisitor = {
testNode: "blockquote",
visitNode({ actions }) {
actions.addAndStepInto($createQuoteNode());
}
};
export {
MdastBlockQuoteVisitor
};
+18
View File
@@ -0,0 +1,18 @@
import { QuoteNode } from "@lexical/rich-text";
import { MdastBlockQuoteVisitor } from "./MdastBlockQuoteVisitor.js";
import { LexicalQuoteVisitor } from "./LexicalQuoteVisitor.js";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { addExportVisitor$, addLexicalNode$, addImportVisitor$, addActivePlugin$ } from "../core/index.js";
const quotePlugin = realmPlugin({
init(realm) {
realm.pubIn({
[addActivePlugin$]: "quote",
[addImportVisitor$]: MdastBlockQuoteVisitor,
[addLexicalNode$]: QuoteNode,
[addExportVisitor$]: LexicalQuoteVisitor
});
}
});
export {
quotePlugin
};
+51
View File
@@ -0,0 +1,51 @@
import React__default, { useEffect, useCallback, useMemo, createContext } from "react";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { addComposerChild$ } from "../core/index.js";
const RemoteMDXEditorRealmContextValueStub = {
editorMap: /* @__PURE__ */ new Map(),
registerEditor: (_id, _realm) => void 0
};
const RemoteMDXEditorRealmContext = createContext(RemoteMDXEditorRealmContextValueStub);
const RemoteMDXEditorRealmProvider = ({ children }) => {
const [editorMap, setEditorMap] = React__default.useState(/* @__PURE__ */ new Map());
useEffect(() => {
return () => {
setEditorMap(/* @__PURE__ */ new Map());
};
}, []);
const registerEditor = useCallback((id, realm) => {
setEditorMap((prev) => {
return new Map(prev).set(id, realm);
});
}, []);
const contextValue = useMemo(
() => ({
editorMap,
registerEditor
}),
[editorMap, registerEditor]
);
return /* @__PURE__ */ React__default.createElement(RemoteMDXEditorRealmContext.Provider, { value: contextValue }, children);
};
const RemotePluginRegister = ({ realm, editorId }) => {
const { registerEditor } = React__default.useContext(RemoteMDXEditorRealmContext);
useEffect(() => {
registerEditor(editorId, realm);
}, [realm, editorId, registerEditor]);
return null;
};
const remoteRealmPlugin = realmPlugin({
init: (realm, params) => {
if (params == null ? void 0 : params.editorId) {
realm.pub(addComposerChild$, () => /* @__PURE__ */ React__default.createElement(RemotePluginRegister, { realm, editorId: params.editorId }));
}
}
});
function useRemoteMDXEditorRealm(editorId) {
return React__default.useContext(RemoteMDXEditorRealmContext).editorMap.get(editorId);
}
export {
RemoteMDXEditorRealmProvider,
remoteRealmPlugin,
useRemoteMDXEditorRealm
};
+56
View File
@@ -0,0 +1,56 @@
import { SandpackProvider, SandpackLayout, SandpackCodeEditor, SandpackPreview, useSandpack } from "@codesandbox/sandpack-react";
import { useCellValues } from "@mdxeditor/gurx";
import React__default from "react";
import styles from "../../styles/ui.module.css.js";
import { useCodeBlockEditorContext } from "../codeblock/CodeBlockNode.js";
import { readOnly$, iconComponentFor$, useTranslation } from "../core/index.js";
import { useCodeMirrorRef } from "./useCodeMirrorRef.js";
const CodeUpdateEmitter = ({ onChange, snippetFileName }) => {
const { sandpack } = useSandpack();
onChange(sandpack.files[snippetFileName].code);
return null;
};
const SandpackEditor = ({ nodeKey, code, focusEmitter, preset }) => {
const codeMirrorRef = useCodeMirrorRef(nodeKey, "sandpack", "jsx", focusEmitter);
const [readOnly, iconComponentFor] = useCellValues(readOnly$, iconComponentFor$);
const { setCode } = useCodeBlockEditorContext();
const { parentEditor, lexicalNode } = useCodeBlockEditorContext();
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement("div", { className: styles.sandPackWrapper }, /* @__PURE__ */ React__default.createElement("div", { className: styles.codeMirrorToolbar }, /* @__PURE__ */ React__default.createElement(
"button",
{
className: styles.iconButton,
type: "button",
disabled: readOnly,
title: t("codeblock.delete", "Delete code block"),
onClick: (e) => {
e.preventDefault();
parentEditor.update(() => {
lexicalNode.remove();
});
}
},
iconComponentFor("delete_small")
)), /* @__PURE__ */ React__default.createElement(
SandpackProvider,
{
template: preset.sandpackTemplate,
theme: preset.sandpackTheme,
files: {
[preset.snippetFileName]: code,
...Object.entries(preset.files ?? {}).reduce(
(acc, [filePath, fileContents]) => ({ ...acc, ...{ [filePath]: { code: fileContents, readOnly: true } } }),
{}
)
},
customSetup: {
dependencies: preset.dependencies
}
},
/* @__PURE__ */ React__default.createElement(SandpackLayout, null, /* @__PURE__ */ React__default.createElement(SandpackCodeEditor, { readOnly, showLineNumbers: true, showInlineErrors: true, ref: codeMirrorRef }), /* @__PURE__ */ React__default.createElement(SandpackPreview, null)),
/* @__PURE__ */ React__default.createElement(CodeUpdateEmitter, { onChange: setCode, snippetFileName: preset.snippetFileName })
));
};
export {
SandpackEditor
};
+83
View File
@@ -0,0 +1,83 @@
import React__default from "react";
import { insertCodeBlock$, appendCodeBlockEditorDescriptor$ } from "../codeblock/index.js";
import { SandpackEditor } from "./SandpackEditor.js";
import { Cell, Signal, withLatestFrom, map, useCellValue } from "@mdxeditor/gurx";
import { realmPlugin } from "../../RealmWithPlugins.js";
const defaultSnippetContent = `
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
`;
const defaultSandpackConfig = {
defaultPreset: "react",
presets: [
{
name: "react",
meta: "live react",
label: "React",
sandpackTemplate: "react",
sandpackTheme: "light",
snippetFileName: "/App.js",
snippetLanguage: "jsx",
initialSnippetContent: defaultSnippetContent
}
]
};
const sandpackConfig$ = Cell(defaultSandpackConfig);
const insertSandpack$ = Signal((r) => {
r.link(
r.pipe(
insertSandpack$,
withLatestFrom(sandpackConfig$),
map(([presetName, sandpackConfig]) => {
const preset = presetName ? sandpackConfig.presets.find((preset2) => preset2.name === presetName) : sandpackConfig.presets.find((preset2) => preset2.name == sandpackConfig.defaultPreset);
if (!preset) {
throw new Error(`No sandpack preset found with name ${presetName}`);
}
return {
code: preset.initialSnippetContent ?? "",
language: preset.snippetLanguage ?? "jsx",
meta: preset.meta
};
})
),
insertCodeBlock$
);
});
const sandpackPlugin = realmPlugin({
init(realm, params) {
realm.pubIn({
[sandpackConfig$]: params == null ? void 0 : params.sandpackConfig,
[appendCodeBlockEditorDescriptor$]: sandpackCodeBlockDescriptor()
});
},
update(realm, params) {
realm.pub(sandpackConfig$, params == null ? void 0 : params.sandpackConfig);
}
});
function sandpackCodeBlockDescriptor() {
return {
match(_language, meta) {
return Boolean(meta == null ? void 0 : meta.startsWith("live"));
},
Editor(props) {
const config = useCellValue(sandpackConfig$);
const preset = config.presets.find((preset2) => preset2.meta === props.meta);
if (!preset) {
throw new Error(`No sandpack preset found with ${props.meta}`);
}
return /* @__PURE__ */ React__default.createElement(SandpackEditor, { ...props, preset });
},
priority: 1
};
}
export {
insertSandpack$,
sandpackConfig$,
sandpackPlugin
};
@@ -0,0 +1,107 @@
import { $getNodeByKey, $createParagraphNode } from "lexical";
import React__default from "react";
import { useCodeBlockEditorContext } from "../codeblock/CodeBlockNode.js";
import { activeEditor$, editorInFocus$ } from "../core/index.js";
import { useCellValue, usePublisher } from "@mdxeditor/gurx";
function useCodeMirrorRef(nodeKey, editorType, language, focusEmitter) {
const activeEditor = useCellValue(activeEditor$);
const setEditorInFocus = usePublisher(editorInFocus$);
const codeMirrorRef = React__default.useRef(null);
const { lexicalNode } = useCodeBlockEditorContext();
const atBottom = React__default.useRef(false);
const atTop = React__default.useRef(false);
const onFocusHandler = React__default.useCallback(() => {
setEditorInFocus({
editorType,
rootNode: lexicalNode,
editorRef: codeMirrorRef.current
});
}, [editorType, lexicalNode, setEditorInFocus]);
const onKeyDownHandler = React__default.useCallback(
(e) => {
var _a, _b, _c, _d, _e, _f;
if (e.key === "ArrowDown") {
const state = (_b = (_a = codeMirrorRef.current) == null ? void 0 : _a.getCodemirror()) == null ? void 0 : _b.state;
if (state) {
const docLength = state.doc.length;
const selectionEnd = state.selection.ranges[0].to;
if (docLength === selectionEnd) {
if (!atBottom.current) {
atBottom.current = true;
} else {
activeEditor == null ? void 0 : activeEditor.update(() => {
var _a2, _b2;
const node = $getNodeByKey(nodeKey);
const nextSibling = node.getNextSibling();
if (nextSibling) {
(_b2 = (_a2 = codeMirrorRef.current) == null ? void 0 : _a2.getCodemirror()) == null ? void 0 : _b2.contentDOM.blur();
node.selectNext();
} else {
node.insertAfter($createParagraphNode());
}
});
atBottom.current = false;
}
}
}
} else if (e.key === "ArrowUp") {
const state = (_d = (_c = codeMirrorRef.current) == null ? void 0 : _c.getCodemirror()) == null ? void 0 : _d.state;
if (state) {
const selectionStart = state.selection.ranges[0].from;
if (selectionStart === 0) {
if (!atTop.current) {
atTop.current = true;
} else {
activeEditor == null ? void 0 : activeEditor.update(() => {
var _a2, _b2;
const node = $getNodeByKey(nodeKey);
const previousSibling = node.getPreviousSibling();
if (previousSibling) {
(_b2 = (_a2 = codeMirrorRef.current) == null ? void 0 : _a2.getCodemirror()) == null ? void 0 : _b2.contentDOM.blur();
node.selectPrevious();
}
});
atTop.current = false;
}
}
}
} else if (e.key === "Enter") {
e.stopPropagation();
} else if (e.key === "Backspace" || e.key === "Delete") {
const state = (_f = (_e = codeMirrorRef.current) == null ? void 0 : _e.getCodemirror()) == null ? void 0 : _f.state;
const docLength = state == null ? void 0 : state.doc.length;
if (docLength === 0) {
activeEditor == null ? void 0 : activeEditor.update(() => {
const node = $getNodeByKey(nodeKey);
node.remove();
});
}
}
},
[activeEditor, nodeKey]
);
React__default.useEffect(() => {
const codeMirror = codeMirrorRef.current;
setTimeout(() => {
var _a, _b;
(_a = codeMirror == null ? void 0 : codeMirror.getCodemirror()) == null ? void 0 : _a.contentDOM.addEventListener("focus", onFocusHandler);
(_b = codeMirror == null ? void 0 : codeMirror.getCodemirror()) == null ? void 0 : _b.contentDOM.addEventListener("keydown", onKeyDownHandler);
}, 300);
return () => {
var _a, _b;
(_a = codeMirror == null ? void 0 : codeMirror.getCodemirror()) == null ? void 0 : _a.contentDOM.removeEventListener("focus", onFocusHandler);
(_b = codeMirror == null ? void 0 : codeMirror.getCodemirror()) == null ? void 0 : _b.contentDOM.removeEventListener("keydown", onKeyDownHandler);
};
}, [codeMirrorRef, onFocusHandler, onKeyDownHandler, language]);
React__default.useEffect(() => {
focusEmitter.subscribe(() => {
var _a, _b;
(_b = (_a = codeMirrorRef.current) == null ? void 0 : _a.getCodemirror()) == null ? void 0 : _b.focus();
onFocusHandler();
});
}, [focusEmitter, codeMirrorRef, nodeKey, onFocusHandler]);
return codeMirrorRef;
}
export {
useCodeMirrorRef
};
+368
View File
@@ -0,0 +1,368 @@
import { Cell, debounceTime, useRealm, useCellValue, useCell } from "@mdxeditor/gurx";
import { getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, $isTextNode, $createRangeSelection } from "lexical";
import { realmPlugin } from "../../RealmWithPlugins.js";
import { contentEditableRef$, createRootEditorSubscription$ } from "../core/index.js";
const EmptyTextNodeIndex = {
allText: "",
nodeIndex: [],
offsetIndex: []
};
const editorSearchTerm$ = Cell("");
const editorSearchRanges$ = Cell([]);
const editorSearchCursor$ = Cell(0);
const editorSearchTextNodeIndex$ = Cell(EmptyTextNodeIndex);
const searchOpen$ = Cell(false);
const editorSearchTermDebounced$ = Cell("", (realm) => {
realm.link(editorSearchTermDebounced$, realm.pipe(editorSearchTerm$, realm.transformer(debounceTime(250))));
});
const editorSearchScrollableContent$ = Cell(
null,
(r) => r.sub(contentEditableRef$, (cref) => {
var _a;
r.pub(editorSearchScrollableContent$, ((_a = cref == null ? void 0 : cref.current) == null ? void 0 : _a.parentNode) ?? null);
})
);
const MDX_SEARCH_NAME = "MdxSearch";
const MDX_FOCUS_SEARCH_NAME = "MdxFocusSearch";
const debouncedIndexer$ = Cell(EmptyTextNodeIndex, (realm) => {
realm.link(debouncedIndexer$, realm.pipe(editorSearchTextNodeIndex$, realm.transformer(debounceTime(250))));
});
function* searchText(allText, searchQuery) {
if (!searchQuery) {
return;
}
let regex;
try {
regex = new RegExp(searchQuery, "gi");
} catch (e) {
console.error("Invalid search pattern:", e);
return;
}
let match;
while ((match = regex.exec(allText)) !== null) {
if (match[0].length === 0) {
if (regex.lastIndex === match.index) {
regex.lastIndex++;
}
continue;
}
const start = match.index;
const end = start + match[0].length - 1;
yield [start, end];
}
}
function indexAllTextNodes(root) {
var _a;
let allText = "";
const nodeIndex = [];
const offsetIndex = [];
if (!root) {
return { allText: "", nodeIndex, offsetIndex };
}
const contentSelector = "p, h1, h2, h3, h4, h5, h6, li, code, pre";
const treeWalker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
// The corrected heuristic: accept any text node that is a descendant of a valid content container.
(node) => {
var _a2;
if ((_a2 = node.parentElement) == null ? void 0 : _a2.closest(contentSelector)) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
}
);
let currentNode;
while (currentNode = treeWalker.nextNode()) {
const nodeContent = ((_a = currentNode.textContent) == null ? void 0 : _a.normalize("NFKD")) ?? currentNode.textContent ?? "";
for (let i = 0; i < nodeContent.length; i++) {
nodeIndex.push(currentNode);
offsetIndex.push(i);
allText += nodeContent[i] ?? "";
}
}
return { allText, nodeIndex, offsetIndex };
}
function* rangeSearchScan(searchQuery, { allText, offsetIndex, nodeIndex }) {
for (const [start, end] of searchText(allText, searchQuery)) {
const startOffset = offsetIndex[start];
const endOffset = offsetIndex[end];
const startNode = nodeIndex[start];
const endNode = nodeIndex[end];
const range = new Range();
if (startNode === void 0 || endNode === void 0 || startOffset === void 0 || endOffset === void 0) {
throw new Error("Invalid range: startNode, endNode, startOffset, or endOffset is undefined.");
}
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset + 1);
yield range;
}
}
const focusHighlightRange = (range) => {
CSS.highlights.delete(MDX_FOCUS_SEARCH_NAME);
if (range)
CSS.highlights.set(MDX_FOCUS_SEARCH_NAME, new Highlight(range));
};
const highlightRanges = (ranges) => {
CSS.highlights.set(MDX_SEARCH_NAME, new Highlight(...ranges));
};
const resetHighlights = () => {
CSS.highlights.delete(MDX_SEARCH_NAME);
CSS.highlights.delete(MDX_FOCUS_SEARCH_NAME);
};
const scrollToRange = (range, contentEditable, options) => {
const ignoreIfInView = (options == null ? void 0 : options.ignoreIfInView) ?? true;
const behavior = (options == null ? void 0 : options.behavior) ?? "smooth";
const [first] = range.getClientRects();
if (!contentEditable) {
console.warn("No content-editable element found for scrolling.");
return;
}
if (!first) {
console.warn("No client rect found for the range, cannot scroll.");
return;
}
const containerRect = contentEditable.getBoundingClientRect();
const topRelativeToContainer = first.top - containerRect.top;
const bottomRelativeToContainer = first.bottom - containerRect.top;
if (ignoreIfInView) {
const rangeTop = topRelativeToContainer + contentEditable.scrollTop;
const rangeBottom = bottomRelativeToContainer + contentEditable.scrollTop;
const visibleTop = contentEditable.scrollTop;
const visibleBottom = visibleTop + contentEditable.clientHeight;
const inView = rangeTop >= visibleTop && rangeBottom <= visibleBottom;
if (inView)
return;
}
const top = topRelativeToContainer + contentEditable.scrollTop - first.height;
contentEditable.scrollTo({ top, behavior });
};
function isSimilarRange(range1, range2) {
return range1.startContainer === range2.startContainer && range1.startOffset === range2.startOffset;
}
function replaceTextInRange(range, str, onUpdate) {
const startDomNode = range.startContainer;
const endDomNode = range.endContainer;
const startOffset = range.startOffset;
const endOffset = range.endOffset;
const editor = getNearestEditorFromDOMNode(startDomNode);
if (!editor) {
console.warn("No editor found for the provided DOM node.");
return;
}
editor.update(
() => {
const startLexicalNode = $getNearestNodeFromDOMNode(startDomNode);
const endLexicalNode = $getNearestNodeFromDOMNode(endDomNode);
if (!$isTextNode(startLexicalNode) || !$isTextNode(endLexicalNode)) {
return;
}
try {
const selection = $createRangeSelection();
selection.anchor.set(startLexicalNode.getKey(), startOffset, "text");
selection.focus.set(endLexicalNode.getKey(), endOffset, "text");
selection.insertText(str);
} catch (e) {
console.warn("Error replacing text in the editor:", e);
if (onUpdate) {
onUpdate();
}
}
},
{
onUpdate
}
);
}
function useEditorSearch() {
const realm = useRealm();
const ranges = useCellValue(editorSearchRanges$);
const cursor = useCellValue(editorSearchCursor$);
const search = useCellValue(editorSearchTerm$);
const currentRange = ranges[cursor - 1] ?? null;
const contentEditable = useCellValue(editorSearchScrollableContent$);
const [isSearchOpen, setIsSearchOpen] = useCell(searchOpen$);
const openSearch = () => {
setIsSearchOpen(true);
};
const closeSearch = () => {
setIsSearchOpen(false);
};
const toggleSearch = () => {
setIsSearchOpen(!isSearchOpen);
};
const rangeCount = ranges.length;
const scrollToRangeOrIndex = (range, options) => {
const scrollRange = typeof range === "number" ? ranges[range - 1] : range;
if (!scrollRange) {
throw new Error("Error scrolling to range, range does not exist");
}
scrollToRange(scrollRange, contentEditable, options);
};
const setSearch = (term) => {
if ((term ?? "") !== search) {
realm.pub(editorSearchCursor$, 0);
}
realm.pub(editorSearchTermDebounced$, term ?? "");
};
const next = () => {
if (!ranges.length)
return;
const newVal = cursor % ranges.length + 1;
scrollToRangeOrIndex(newVal);
realm.pub(editorSearchCursor$, newVal);
};
const prev = () => {
if (!ranges.length)
return;
const newVal = cursor <= 1 ? ranges.length : cursor - 1;
scrollToRangeOrIndex(newVal);
realm.pub(editorSearchCursor$, newVal);
};
const replace = (str, onUpdate) => {
const currentRange2 = ranges[cursor - 1];
if (!currentRange2) {
return;
}
const { startContainer, startOffset } = currentRange2 ?? {};
replaceTextInRange(currentRange2, str, () => {
const unsub = realm.sub(editorSearchRanges$, (newRanges) => {
unsub();
if (isSimilarRange(newRanges[cursor - 1] ?? {}, {
startOffset,
startContainer
})) {
realm.pub(editorSearchCursor$, (cursor + 1) % (newRanges.length + 1) || 1);
}
});
onUpdate == null ? void 0 : onUpdate();
});
};
const replaceAll = (str, onUpdate) => {
const runReplaceAll = () => {
let ticks = 0;
for (let i = ranges.length - 1; i >= 0; i--) {
const textReplaceRange = ranges[i];
if (!textReplaceRange) {
throw new Error("error replacing all text range does not exist");
}
replaceTextInRange(textReplaceRange, str, () => {
ticks++;
if (ticks >= ranges.length) {
onUpdate == null ? void 0 : onUpdate();
}
});
}
};
if (typeof requestIdleCallback === "function") {
requestIdleCallback(runReplaceAll);
} else {
setTimeout(runReplaceAll, 0);
}
};
return {
next,
prev,
total: rangeCount,
cursor,
setSearch,
search,
currentRange,
isSearchOpen,
setIsSearchOpen,
openSearch,
closeSearch,
toggleSearch,
ranges,
scrollToRangeOrIndex,
replace,
replaceAll
};
}
const searchPlugin = realmPlugin({
//TODO: ensure proper event cleanup
init(realm) {
if (typeof CSS.highlights === "undefined") {
console.warn("CSS.highlights is not supported in this browser. Search functionality will be limited.");
return;
}
realm.sub(editorSearchCursor$, (cursor) => {
const ranges = realm.getValue(editorSearchRanges$);
focusHighlightRange(ranges[cursor - 1]);
});
const updateHighlights = (searchQuery, textNodeIndex) => {
if (!searchQuery) {
realm.pub(editorSearchCursor$, 0);
realm.pub(editorSearchRanges$, []);
resetHighlights();
return;
}
const ranges = Array.from(rangeSearchScan(searchQuery, textNodeIndex));
realm.pub(editorSearchRanges$, ranges);
highlightRanges(ranges);
if (ranges.length) {
const currentCursor = realm.getValue(editorSearchCursor$) || 1;
focusHighlightRange(ranges[currentCursor - 1]);
realm.pub(editorSearchCursor$, currentCursor);
const scrollRange = ranges[currentCursor - 1];
if (!scrollRange)
throw new Error("error updating highlights, scroll range does not exist");
const contentEditable = realm.getValue(editorSearchScrollableContent$);
scrollToRange(scrollRange, contentEditable, {
ignoreIfInView: true
});
} else {
resetHighlights();
}
};
realm.sub(editorSearchTextNodeIndex$, (textNodeIndex) => {
updateHighlights(realm.getValue(editorSearchTerm$), textNodeIndex);
});
realm.sub(editorSearchTerm$, (searchQuery) => {
updateHighlights(searchQuery, realm.getValue(editorSearchTextNodeIndex$));
});
realm.pub(createRootEditorSubscription$, (editor) => {
let observer = null;
return editor.registerRootListener((rootElement) => {
if (observer) {
observer.disconnect();
observer = null;
}
if (rootElement) {
const initialIndex = indexAllTextNodes(rootElement);
realm.pub(editorSearchTextNodeIndex$, initialIndex);
observer = new MutationObserver(() => {
const newIndex = indexAllTextNodes(rootElement);
if (realm.getValue(searchOpen$)) {
realm.pub(editorSearchTextNodeIndex$, newIndex);
} else {
realm.pub(debouncedIndexer$, newIndex);
}
});
observer.observe(rootElement, {
childList: true,
subtree: true,
characterData: true
});
return () => observer == null ? void 0 : observer.disconnect();
}
});
});
}
});
export {
EmptyTextNodeIndex,
MDX_FOCUS_SEARCH_NAME,
MDX_SEARCH_NAME,
debouncedIndexer$,
editorSearchCursor$,
editorSearchRanges$,
editorSearchScrollableContent$,
editorSearchTerm$,
editorSearchTermDebounced$,
editorSearchTextNodeIndex$,
rangeSearchScan,
searchOpen$,
searchPlugin,
useEditorSearch
};
@@ -0,0 +1,10 @@
import { $isTableNode } from "./TableNode.js";
const LexicalTableVisitor = {
testLexicalNode: $isTableNode,
visitLexicalNode({ actions, mdastParent, lexicalNode }) {
actions.appendToParent(mdastParent, lexicalNode.getMdastNode());
}
};
export {
LexicalTableVisitor
};
+10
View File
@@ -0,0 +1,10 @@
import { $createTableNode } from "./TableNode.js";
const MdastTableVisitor = {
testNode: "table",
visitNode({ mdastNode, lexicalParent }) {
lexicalParent.append($createTableNode(mdastNode));
}
};
export {
MdastTableVisitor
};
+479
View File
@@ -0,0 +1,479 @@
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import * as RadixPopover from "@radix-ui/react-popover";
import { $createParagraphNode, createEditor, $getRoot, KEY_TAB_COMMAND, COMMAND_PRIORITY_CRITICAL, FOCUS_COMMAND, COMMAND_PRIORITY_LOW, KEY_ENTER_COMMAND, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR } from "lexical";
import React__default from "react";
import { exportLexicalTreeToMdast } from "../../exportMarkdownFromLexical.js";
import { importMdastTreeToLexical } from "../../importMarkdownToLexical.js";
import { lexicalTheme } from "../../styles/lexicalTheme.js";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { mergeRegister } from "@lexical/utils";
import * as RadixToolbar from "@radix-ui/react-toolbar";
import classNames from "classnames";
import styles from "../../styles/ui.module.css.js";
import { isPartOftheEditorUI } from "../../utils/isPartOftheEditorUI.js";
import { uuidv4 } from "../../utils/uuid4.js";
import { iconComponentFor$, readOnly$, useTranslation, editorRootElementRef$, importVisitors$, exportVisitors$, usedLexicalNodes$, jsxComponentDescriptors$, directiveDescriptors$, codeBlockEditorDescriptors$, jsxIsAvailable$, rootEditor$, nestedEditorChildren$, NESTED_EDITOR_UPDATED_COMMAND } from "../core/index.js";
import { useCellValues } from "@mdxeditor/gurx";
const getCellType = (rowIndex) => {
if (rowIndex === 0) {
return "th";
}
return "td";
};
const AlignToTailwindClassMap = {
center: styles.centeredCell,
left: styles.leftAlignedCell,
right: styles.rightAlignedCell
};
const TableEditor = ({ mdastNode, parentEditor, lexicalTable }) => {
const [activeCell, setActiveCell] = React__default.useState(null);
const [iconComponentFor, readOnly] = useCellValues(iconComponentFor$, readOnly$);
const getCellKey = React__default.useMemo(() => {
return (cell) => {
cell.__cacheKey ?? (cell.__cacheKey = uuidv4());
return cell.__cacheKey;
};
}, []);
const setActiveCellWithBoundaries = React__default.useCallback(
(cell) => {
const colCount = lexicalTable.getColCount();
if (cell === null) {
setActiveCell(null);
return;
}
let [colIndex, rowIndex] = cell;
if (colIndex > colCount - 1) {
colIndex = 0;
rowIndex++;
}
if (colIndex < 0) {
colIndex = colCount - 1;
rowIndex -= 1;
}
if (rowIndex > lexicalTable.getRowCount() - 1) {
setActiveCell(null);
parentEditor.update(() => {
const nextSibling = lexicalTable.getLatest().getNextSibling();
if (nextSibling) {
lexicalTable.getLatest().selectNext();
} else {
const newParagraph = $createParagraphNode();
lexicalTable.insertAfter(newParagraph);
newParagraph.select();
}
});
return;
}
if (rowIndex < 0) {
setActiveCell(null);
parentEditor.update(() => {
lexicalTable.getLatest().selectPrevious();
});
return;
}
setActiveCell([colIndex, rowIndex]);
},
[lexicalTable, parentEditor]
);
React__default.useEffect(() => {
lexicalTable.focusEmitter.subscribe(setActiveCellWithBoundaries);
}, [lexicalTable, setActiveCellWithBoundaries]);
const addRowToBottom = React__default.useCallback(
(e) => {
e.preventDefault();
parentEditor.update(() => {
lexicalTable.addRowToBottom();
setActiveCell([0, lexicalTable.getRowCount()]);
});
},
[parentEditor, lexicalTable]
);
const addColumnToRight = React__default.useCallback(
(e) => {
e.preventDefault();
parentEditor.update(() => {
lexicalTable.addColumnToRight();
setActiveCell([lexicalTable.getColCount(), 0]);
});
},
[parentEditor, lexicalTable]
);
const [highlightedCoordinates, setHighlightedCoordinates] = React__default.useState([-1, -1]);
const onTableMouseOver = React__default.useCallback((e) => {
let tableCell = e.target;
while (tableCell && !["TH", "TD"].includes(tableCell.tagName)) {
if (tableCell === e.currentTarget) {
return;
}
tableCell = tableCell.parentElement;
}
if (tableCell === null) {
return;
}
const tableRow = tableCell.parentElement;
const tableContainer = tableRow.parentElement;
const colIndex = tableContainer.tagName === "TFOOT" ? -1 : Array.from(tableRow.children).indexOf(tableCell);
const rowIndex = tableCell.tagName === "TH" ? -1 : Array.from(tableRow.parentElement.children).indexOf(tableRow);
setHighlightedCoordinates([colIndex, rowIndex]);
}, []);
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement(
"table",
{
className: styles.tableEditor,
onMouseOver: onTableMouseOver,
onMouseLeave: () => {
setHighlightedCoordinates([-1, -1]);
}
},
/* @__PURE__ */ React__default.createElement("colgroup", null, readOnly ? null : /* @__PURE__ */ React__default.createElement("col", null), Array.from({ length: mdastNode.children[0].children.length }, (_, colIndex) => {
const align = mdastNode.align ?? [];
const currentColumnAlign = align[colIndex] ?? "left";
const className = AlignToTailwindClassMap[currentColumnAlign];
return /* @__PURE__ */ React__default.createElement("col", { key: colIndex, className });
}), readOnly ? null : /* @__PURE__ */ React__default.createElement("col", null)),
readOnly || /* @__PURE__ */ React__default.createElement("thead", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", { className: styles.tableToolsColumn }), Array.from({ length: mdastNode.children[0].children.length }, (_, colIndex) => {
return /* @__PURE__ */ React__default.createElement("th", { key: colIndex, "data-tool-cell": true }, /* @__PURE__ */ React__default.createElement(
ColumnEditor,
{
...{
setActiveCellWithBoundaries,
parentEditor,
colIndex,
highlightedCoordinates,
lexicalTable,
align: (mdastNode.align ?? [])[colIndex]
}
}
));
}), /* @__PURE__ */ React__default.createElement("th", { className: styles.tableToolsColumn, "data-tool-cell": true }, /* @__PURE__ */ React__default.createElement(
"button",
{
className: styles.iconButton,
type: "button",
title: t("table.deleteTable", "Delete table"),
onClick: (e) => {
e.preventDefault();
parentEditor.update(() => {
lexicalTable.selectNext();
lexicalTable.remove();
});
}
},
iconComponentFor("delete_small")
)))),
/* @__PURE__ */ React__default.createElement("tbody", null, mdastNode.children.map((row, rowIndex) => {
const CellElement = getCellType(rowIndex);
return /* @__PURE__ */ React__default.createElement("tr", { key: rowIndex }, readOnly || /* @__PURE__ */ React__default.createElement(CellElement, { className: styles.toolCell, "data-tool-cell": true }, /* @__PURE__ */ React__default.createElement(RowEditor, { ...{ setActiveCellWithBoundaries, parentEditor, rowIndex, highlightedCoordinates, lexicalTable } })), row.children.map((mdastCell, colIndex) => {
var _a;
return /* @__PURE__ */ React__default.createElement(
Cell,
{
align: (_a = mdastNode.align) == null ? void 0 : _a[colIndex],
key: getCellKey(mdastCell),
contents: mdastCell.children,
setActiveCell: setActiveCellWithBoundaries,
...{
rowIndex,
colIndex,
lexicalTable,
parentEditor,
activeCell: readOnly ? [-1, -1] : activeCell
}
}
);
}), readOnly || rowIndex === 0 && /* @__PURE__ */ React__default.createElement("th", { rowSpan: lexicalTable.getRowCount(), "data-tool-cell": true }, /* @__PURE__ */ React__default.createElement("button", { type: "button", className: styles.addColumnButton, onClick: addColumnToRight }, iconComponentFor("add_column"))));
})),
readOnly || /* @__PURE__ */ React__default.createElement("tfoot", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", null), /* @__PURE__ */ React__default.createElement("th", { colSpan: lexicalTable.getColCount() }, /* @__PURE__ */ React__default.createElement("button", { type: "button", className: styles.addRowButton, onClick: addRowToBottom }, iconComponentFor("add_row"))), /* @__PURE__ */ React__default.createElement("th", null)))
);
};
const Cell = ({ align, ...props }) => {
const { activeCell, setActiveCell } = props;
const isActive = Boolean(activeCell && activeCell[0] === props.colIndex && activeCell[1] === props.rowIndex);
const className = AlignToTailwindClassMap[align ?? "left"];
const CellElement = getCellType(props.rowIndex);
return /* @__PURE__ */ React__default.createElement(
CellElement,
{
className,
"data-active": isActive,
onClick: () => {
setActiveCell([props.colIndex, props.rowIndex]);
}
},
/* @__PURE__ */ React__default.createElement(CellEditor, { ...props, focus: isActive })
);
};
const CellEditor = ({ focus, setActiveCell, parentEditor, lexicalTable, contents, colIndex, rowIndex }) => {
const [
importVisitors,
exportVisitors,
usedLexicalNodes,
jsxComponentDescriptors,
directiveDescriptors,
codeBlockEditorDescriptors,
jsxIsAvailable,
rootEditor,
nestedEditorChildren
] = useCellValues(
importVisitors$,
exportVisitors$,
usedLexicalNodes$,
jsxComponentDescriptors$,
directiveDescriptors$,
codeBlockEditorDescriptors$,
jsxIsAvailable$,
rootEditor$,
nestedEditorChildren$
);
const [editor] = React__default.useState(() => {
const editor2 = createEditor({
nodes: usedLexicalNodes,
theme: lexicalTheme,
namespace: "TableCellEditor"
});
editor2.update(() => {
importMdastTreeToLexical({
root: $getRoot(),
mdastRoot: { type: "root", children: [{ type: "paragraph", children: contents }] },
visitors: importVisitors,
jsxComponentDescriptors,
directiveDescriptors,
codeBlockEditorDescriptors
});
});
return editor2;
});
const saveAndFocus = React__default.useCallback(
(nextCell) => {
editor.getEditorState().read(() => {
const mdast = exportLexicalTreeToMdast({
root: $getRoot(),
jsxComponentDescriptors,
visitors: exportVisitors,
jsxIsAvailable
});
parentEditor.update(
() => {
lexicalTable.updateCellContents(colIndex, rowIndex, mdast.children[0].children);
},
{ discrete: true }
);
parentEditor.dispatchCommand(NESTED_EDITOR_UPDATED_COMMAND, void 0);
});
setActiveCell(nextCell);
},
[colIndex, editor, exportVisitors, jsxComponentDescriptors, jsxIsAvailable, lexicalTable, parentEditor, rowIndex, setActiveCell]
);
React__default.useEffect(() => {
return mergeRegister(
editor.registerCommand(
KEY_TAB_COMMAND,
(payload) => {
payload.preventDefault();
const nextCell = payload.shiftKey ? [colIndex - 1, rowIndex] : [colIndex + 1, rowIndex];
saveAndFocus(nextCell);
return true;
},
COMMAND_PRIORITY_CRITICAL
),
editor.registerCommand(
FOCUS_COMMAND,
() => {
setActiveCell([colIndex, rowIndex]);
return false;
},
COMMAND_PRIORITY_LOW
),
editor.registerCommand(
KEY_ENTER_COMMAND,
(payload) => {
payload == null ? void 0 : payload.preventDefault();
const nextCell = (payload == null ? void 0 : payload.shiftKey) ? [colIndex, rowIndex - 1] : [colIndex, rowIndex + 1];
saveAndFocus(nextCell);
return true;
},
COMMAND_PRIORITY_CRITICAL
),
editor.registerCommand(
BLUR_COMMAND,
(payload) => {
const relatedTarget = payload.relatedTarget;
if (isPartOftheEditorUI(relatedTarget, rootEditor.getRootElement())) {
return false;
}
saveAndFocus(null);
return true;
},
COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
NESTED_EDITOR_UPDATED_COMMAND,
() => {
saveAndFocus(null);
return true;
},
COMMAND_PRIORITY_EDITOR
)
);
}, [colIndex, editor, rootEditor, rowIndex, saveAndFocus, setActiveCell]);
React__default.useEffect(() => {
if (focus) {
editor.focus();
}
}, [focus, editor]);
return /* @__PURE__ */ React__default.createElement(LexicalNestedComposer, { initialEditor: editor }, /* @__PURE__ */ React__default.createElement(RichTextPlugin, { contentEditable: /* @__PURE__ */ React__default.createElement(ContentEditable, null), placeholder: /* @__PURE__ */ React__default.createElement("div", null), ErrorBoundary: LexicalErrorBoundary }), nestedEditorChildren.map((Child, index) => /* @__PURE__ */ React__default.createElement(Child, { key: index })), /* @__PURE__ */ React__default.createElement(HistoryPlugin, null));
};
const ColumnEditor = ({
parentEditor,
highlightedCoordinates,
align,
lexicalTable,
colIndex,
setActiveCellWithBoundaries
}) => {
const [editorRootElementRef, iconComponentFor] = useCellValues(editorRootElementRef$, iconComponentFor$);
const insertColumnAt = React__default.useCallback(
(colIndex2) => {
parentEditor.update(() => {
lexicalTable.insertColumnAt(colIndex2);
});
setActiveCellWithBoundaries([colIndex2, 0]);
},
[parentEditor, lexicalTable, setActiveCellWithBoundaries]
);
const deleteColumnAt = React__default.useCallback(
(colIndex2) => {
parentEditor.update(() => {
lexicalTable.deleteColumnAt(colIndex2);
});
},
[parentEditor, lexicalTable]
);
const setColumnAlign = React__default.useCallback(
(colIndex2, align2) => {
parentEditor.update(() => {
lexicalTable.setColumnAlign(colIndex2, align2);
});
},
[parentEditor, lexicalTable]
);
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement(RadixPopover.Root, null, /* @__PURE__ */ React__default.createElement(
RadixPopover.PopoverTrigger,
{
className: styles.tableColumnEditorTrigger,
"data-active": highlightedCoordinates[0] === colIndex + 1,
title: t("table.columnMenu", "Column menu")
},
iconComponentFor("more_horiz")
), /* @__PURE__ */ React__default.createElement(RadixPopover.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(
RadixPopover.PopoverContent,
{
className: classNames(styles.tableColumnEditorPopoverContent),
onOpenAutoFocus: (e) => {
e.preventDefault();
},
sideOffset: 5,
side: "top"
},
/* @__PURE__ */ React__default.createElement(RadixToolbar.Root, { className: styles.tableColumnEditorToolbar }, /* @__PURE__ */ React__default.createElement(
RadixToolbar.ToggleGroup,
{
className: styles.toggleGroupRoot,
onValueChange: (value) => {
setColumnAlign(colIndex, value);
},
value: align ?? "left",
type: "single",
"aria-label": t("table.textAlignment", "Text alignment")
},
/* @__PURE__ */ React__default.createElement(RadixToolbar.ToggleItem, { value: "left", title: t("table.alignLeft", "Align left") }, iconComponentFor("format_align_left")),
/* @__PURE__ */ React__default.createElement(RadixToolbar.ToggleItem, { value: "center", title: t("table.alignCenter", "Align center") }, iconComponentFor("format_align_center")),
/* @__PURE__ */ React__default.createElement(RadixToolbar.ToggleItem, { value: "right", title: t("table.alignRight", "Align right") }, iconComponentFor("format_align_right"))
), /* @__PURE__ */ React__default.createElement(RadixToolbar.Separator, null), /* @__PURE__ */ React__default.createElement(
RadixToolbar.Button,
{
onClick: insertColumnAt.bind(null, colIndex),
title: t("table.insertColumnLeft", "Insert a column to the left of this one")
},
iconComponentFor("insert_col_left")
), /* @__PURE__ */ React__default.createElement(
RadixToolbar.Button,
{
onClick: insertColumnAt.bind(null, colIndex + 1),
title: t("table.insertColumnRight", "Insert a column to the right of this one")
},
iconComponentFor("insert_col_right")
), /* @__PURE__ */ React__default.createElement(RadixToolbar.Button, { onClick: deleteColumnAt.bind(null, colIndex), title: t("table.deleteColumn", "Delete this column") }, iconComponentFor("delete_small"))),
/* @__PURE__ */ React__default.createElement(RadixPopover.Arrow, { className: styles.popoverArrow })
)));
};
const RowEditor = ({
parentEditor,
highlightedCoordinates,
lexicalTable,
rowIndex,
setActiveCellWithBoundaries
}) => {
const [editorRootElementRef, iconComponentFor] = useCellValues(editorRootElementRef$, iconComponentFor$);
const insertRowAt = React__default.useCallback(
(rowIndex2) => {
parentEditor.update(() => {
lexicalTable.insertRowAt(rowIndex2);
});
setActiveCellWithBoundaries([0, rowIndex2]);
},
[parentEditor, lexicalTable, setActiveCellWithBoundaries]
);
const deleteRowAt = React__default.useCallback(
(rowIndex2) => {
parentEditor.update(() => {
lexicalTable.deleteRowAt(rowIndex2);
});
},
[parentEditor, lexicalTable]
);
const t = useTranslation();
return /* @__PURE__ */ React__default.createElement(RadixPopover.Root, null, /* @__PURE__ */ React__default.createElement(
RadixPopover.PopoverTrigger,
{
className: styles.tableColumnEditorTrigger,
"data-active": highlightedCoordinates[1] === rowIndex,
title: t("table.rowMenu", "Row menu")
},
iconComponentFor("more_horiz")
), /* @__PURE__ */ React__default.createElement(RadixPopover.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(
RadixPopover.PopoverContent,
{
className: classNames(styles.tableColumnEditorPopoverContent),
onOpenAutoFocus: (e) => {
e.preventDefault();
},
sideOffset: 5,
side: "bottom"
},
/* @__PURE__ */ React__default.createElement(RadixToolbar.Root, { className: styles.tableColumnEditorToolbar }, /* @__PURE__ */ React__default.createElement(
RadixToolbar.Button,
{
onClick: insertRowAt.bind(null, rowIndex),
title: t("table.insertRowAbove", "Insert a row above this one")
},
iconComponentFor("insert_row_above")
), /* @__PURE__ */ React__default.createElement(
RadixToolbar.Button,
{
onClick: insertRowAt.bind(null, rowIndex + 1),
title: t("table.insertRowBelow", "Insert a row below this one")
},
iconComponentFor("insert_row_below")
), /* @__PURE__ */ React__default.createElement(RadixToolbar.Button, { onClick: deleteRowAt.bind(null, rowIndex), title: t("table.deleteRow", "Delete this row") }, iconComponentFor("delete_small"))),
/* @__PURE__ */ React__default.createElement(RadixPopover.Arrow, { className: styles.popoverArrow })
)));
};
export {
TableEditor
};
+215
View File
@@ -0,0 +1,215 @@
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { DecoratorNode } from "lexical";
import React__default from "react";
import { noop } from "../../utils/fp.js";
import { TableEditor } from "./TableEditor.js";
const EMPTY_CELL = { type: "tableCell", children: [] };
function coordinatesEmitter() {
let subscription = noop;
return {
publish: (coords) => {
subscription(coords);
},
subscribe: (cb) => {
subscription = cb;
}
};
}
class TableNode extends DecoratorNode {
/**
* Constructs a new {@link TableNode} with the specified MDAST table node as the object to edit.
* See {@link https://github.com/micromark/micromark-extension-gfm-table | micromark/micromark-extension-gfm-table} for more information on the MDAST table node.
*/
constructor(mdastNode, key) {
super(key);
/** @internal */
__publicField(this, "__mdastNode");
/** @internal */
__publicField(this, "focusEmitter", coordinatesEmitter());
this.__mdastNode = mdastNode ?? { type: "table", children: [] };
}
/** @internal */
static getType() {
return "table";
}
/** @internal */
static clone(node) {
return new TableNode(structuredClone(node.__mdastNode), node.__key);
}
/** @internal */
static importJSON(serializedNode) {
return $createTableNode(serializedNode.mdastNode);
}
/** @internal */
static importDOM() {
return {
table: () => {
return {
conversion: $convertTableElement,
priority: 3
};
}
};
}
/** @internal */
exportJSON() {
return {
mdastNode: structuredClone(this.__mdastNode),
type: "table",
version: 1
};
}
/**
* Returns the mdast node that this node is constructed from.
*/
getMdastNode() {
return this.__mdastNode;
}
/**
* Returns the number of rows in the table.
*/
getRowCount() {
return this.__mdastNode.children.length;
}
/**
* Returns the number of columns in the table.
*/
getColCount() {
var _a;
return ((_a = this.__mdastNode.children[0]) == null ? void 0 : _a.children.length) || 0;
}
/** @internal */
createDOM() {
return document.createElement("div");
}
/** @internal */
updateDOM() {
return false;
}
/** @internal */
updateCellContents(colIndex, rowIndex, children) {
const self = this.getWritable();
const table = self.__mdastNode;
const row = table.children[rowIndex];
const cells = row.children;
const cell = cells[colIndex];
const cellsClone = Array.from(cells);
const cellClone = { ...cell, children };
const rowClone = { ...row, children: cellsClone };
cellsClone[colIndex] = cellClone;
table.children[rowIndex] = rowClone;
}
insertColumnAt(colIndex) {
const self = this.getWritable();
const table = self.__mdastNode;
for (let rowIndex = 0; rowIndex < table.children.length; rowIndex++) {
const row = table.children[rowIndex];
const cells = row.children;
const cellsClone = Array.from(cells);
const rowClone = { ...row, children: cellsClone };
cellsClone.splice(colIndex, 0, structuredClone(EMPTY_CELL));
table.children[rowIndex] = rowClone;
}
if (table.align && table.align.length > 0) {
table.align.splice(colIndex, 0, "left");
}
}
deleteColumnAt(colIndex) {
const self = this.getWritable();
const table = self.__mdastNode;
for (let rowIndex = 0; rowIndex < table.children.length; rowIndex++) {
const row = table.children[rowIndex];
const cells = row.children;
const cellsClone = Array.from(cells);
const rowClone = { ...row, children: cellsClone };
cellsClone.splice(colIndex, 1);
table.children[rowIndex] = rowClone;
}
}
insertRowAt(y) {
const self = this.getWritable();
const table = self.__mdastNode;
const newRow = {
type: "tableRow",
children: Array.from({ length: this.getColCount() }, () => structuredClone(EMPTY_CELL))
};
table.children.splice(y, 0, newRow);
}
deleteRowAt(rowIndex) {
if (this.getRowCount() === 1) {
this.selectNext();
this.remove();
} else {
this.getWritable().__mdastNode.children.splice(rowIndex, 1);
}
}
addRowToBottom() {
this.insertRowAt(this.getRowCount());
}
addColumnToRight() {
this.insertColumnAt(this.getColCount());
}
setColumnAlign(colIndex, align) {
const self = this.getWritable();
const table = self.__mdastNode;
table.align ?? (table.align = []);
table.align[colIndex] = align;
}
/** @internal */
decorate(parentEditor) {
return /* @__PURE__ */ React__default.createElement(TableEditor, { lexicalTable: this, mdastNode: this.__mdastNode, parentEditor });
}
/**
* Focuses the table cell at the specified coordinates.
* Pass `undefined` to remove the focus.
*/
select(coords) {
this.focusEmitter.publish(coords ?? [0, 0]);
}
/** @internal */
isInline() {
return false;
}
}
function $isTableNode(node) {
return node instanceof TableNode;
}
function $createTableNode(mdastNode) {
return new TableNode(mdastNode);
}
function $convertTableElement(element) {
const rows = element.querySelectorAll("tr");
const children = Array.from(rows).map((row) => {
return {
type: "tableRow",
children: Array.from(row.querySelectorAll("td, th")).map((cell) => {
return {
type: "tableCell",
children: [
{
type: "text",
value: cell.textContent
}
]
};
})
};
});
return {
node: new TableNode({
type: "table",
children
})
};
}
export {
$convertTableElement,
$createTableNode,
$isTableNode,
TableNode
};
+66
View File
@@ -0,0 +1,66 @@
import { realmPlugin } from "../../RealmWithPlugins.js";
import { Signal, map } from "@mdxeditor/gurx";
import { gfmTableToMarkdown, gfmTableFromMarkdown } from "mdast-util-gfm-table";
import { gfmTable } from "micromark-extension-gfm-table";
import { insertDecoratorNode$, addToMarkdownExtension$, addExportVisitor$, addLexicalNode$, addImportVisitor$, addSyntaxExtension$, addMdastExtension$ } from "../core/index.js";
import { LexicalTableVisitor } from "./LexicalTableVisitor.js";
import { MdastTableVisitor } from "./MdastTableVisitor.js";
import { $createTableNode, TableNode } from "./TableNode.js";
import { $convertTableElement, $isTableNode } from "./TableNode.js";
function seedTable(rows = 1, columns = 1) {
const table = {
type: "table",
children: []
};
for (let i = 0; i < rows; i++) {
const tableRow = {
type: "tableRow",
children: []
};
for (let j = 0; j < columns; j++) {
const cell = {
type: "tableCell",
children: []
};
tableRow.children.push(cell);
}
table.children.push(tableRow);
}
return table;
}
const insertTable$ = Signal((r) => {
r.link(
r.pipe(
insertTable$,
map(({ rows, columns }) => {
return () => $createTableNode(seedTable(rows, columns));
})
),
insertDecoratorNode$
);
});
const tablePlugin = realmPlugin({
init(realm, params) {
realm.pubIn({
// import
[addMdastExtension$]: gfmTableFromMarkdown(),
[addSyntaxExtension$]: gfmTable(),
[addImportVisitor$]: MdastTableVisitor,
// export
[addLexicalNode$]: TableNode,
[addExportVisitor$]: LexicalTableVisitor,
[addToMarkdownExtension$]: gfmTableToMarkdown({
tableCellPadding: (params == null ? void 0 : params.tableCellPadding) ?? true,
tablePipeAlign: (params == null ? void 0 : params.tablePipeAlign) ?? true
})
});
}
});
export {
$convertTableElement,
$createTableNode,
$isTableNode,
TableNode,
insertTable$,
tablePlugin
};
@@ -0,0 +1,10 @@
import { $isHorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode.js";
const LexicalThematicBreakVisitor = {
testLexicalNode: $isHorizontalRuleNode,
visitLexicalNode({ actions }) {
actions.addAndStepInto("thematicBreak");
}
};
export {
LexicalThematicBreakVisitor
};
@@ -0,0 +1,10 @@
import { $createHorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode.js";
const MdastThematicBreakVisitor = {
testNode: "thematicBreak",
visitNode({ actions }) {
actions.addAndStepInto($createHorizontalRuleNode());
}
};
export {
MdastThematicBreakVisitor
};

Some files were not shown because too many files have changed in this diff Show More