@haklex/rich-editor 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +164 -0
- package/dist/RichEditor-DeRWrU51.js +1125 -0
- package/dist/RichRenderer-BmM4j0fv.js +95 -0
- package/dist/components/ContentEditable.d.ts +7 -0
- package/dist/components/ContentEditable.d.ts.map +1 -0
- package/dist/components/RendererWrapper.d.ts +31 -0
- package/dist/components/RendererWrapper.d.ts.map +1 -0
- package/dist/components/RichEditor.d.ts +3 -0
- package/dist/components/RichEditor.d.ts.map +1 -0
- package/dist/components/RichRenderer.d.ts +3 -0
- package/dist/components/RichRenderer.d.ts.map +1 -0
- package/dist/components/decorators/AlertEditDecorator.d.ts +10 -0
- package/dist/components/decorators/AlertEditDecorator.d.ts.map +1 -0
- package/dist/components/decorators/BannerEditDecorator.d.ts +10 -0
- package/dist/components/decorators/BannerEditDecorator.d.ts.map +1 -0
- package/dist/components/decorators/CodeBlockEditDecorator.d.ts +8 -0
- package/dist/components/decorators/CodeBlockEditDecorator.d.ts.map +1 -0
- package/dist/components/decorators/GridEditDecorator.d.ts +10 -0
- package/dist/components/decorators/GridEditDecorator.d.ts.map +1 -0
- package/dist/components/renderers/AlertReadOnlyDecorator.d.ts +9 -0
- package/dist/components/renderers/AlertReadOnlyDecorator.d.ts.map +1 -0
- package/dist/components/renderers/AlertRenderer.d.ts +9 -0
- package/dist/components/renderers/AlertRenderer.d.ts.map +1 -0
- package/dist/components/renderers/BannerReadOnlyDecorator.d.ts +9 -0
- package/dist/components/renderers/BannerReadOnlyDecorator.d.ts.map +1 -0
- package/dist/components/renderers/BannerRenderer.d.ts +9 -0
- package/dist/components/renderers/BannerRenderer.d.ts.map +1 -0
- package/dist/components/renderers/CodeBlockRenderer.d.ts +9 -0
- package/dist/components/renderers/CodeBlockRenderer.d.ts.map +1 -0
- package/dist/components/renderers/FootnoteRenderer.d.ts +5 -0
- package/dist/components/renderers/FootnoteRenderer.d.ts.map +1 -0
- package/dist/components/renderers/FootnoteSectionEditRenderer.d.ts +6 -0
- package/dist/components/renderers/FootnoteSectionEditRenderer.d.ts.map +1 -0
- package/dist/components/renderers/FootnoteSectionRenderer.d.ts +6 -0
- package/dist/components/renderers/FootnoteSectionRenderer.d.ts.map +1 -0
- package/dist/components/renderers/GridReadOnlyDecorator.d.ts +9 -0
- package/dist/components/renderers/GridReadOnlyDecorator.d.ts.map +1 -0
- package/dist/components/renderers/ImageRenderer.d.ts +11 -0
- package/dist/components/renderers/ImageRenderer.d.ts.map +1 -0
- package/dist/components/renderers/KaTeXRenderer.d.ts +6 -0
- package/dist/components/renderers/KaTeXRenderer.d.ts.map +1 -0
- package/dist/components/renderers/LinkCardRenderer.d.ts +11 -0
- package/dist/components/renderers/LinkCardRenderer.d.ts.map +1 -0
- package/dist/components/renderers/MentionRenderer.d.ts +7 -0
- package/dist/components/renderers/MentionRenderer.d.ts.map +1 -0
- package/dist/components/renderers/MermaidRenderer.d.ts +6 -0
- package/dist/components/renderers/MermaidRenderer.d.ts.map +1 -0
- package/dist/components/renderers/VideoRenderer.d.ts +8 -0
- package/dist/components/renderers/VideoRenderer.d.ts.map +1 -0
- package/dist/components/utils.d.ts +5 -0
- package/dist/components/utils.d.ts.map +1 -0
- package/dist/config-edit.d.ts +4 -0
- package/dist/config-edit.d.ts.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/context/ColorSchemeContext.d.ts +8 -0
- package/dist/context/ColorSchemeContext.d.ts.map +1 -0
- package/dist/context/FootnoteDefinitionsContext.d.ts +12 -0
- package/dist/context/FootnoteDefinitionsContext.d.ts.map +1 -0
- package/dist/context/RendererConfigContext.d.ts +12 -0
- package/dist/context/RendererConfigContext.d.ts.map +1 -0
- package/dist/editor.d.ts +6 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.mjs +12 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +79 -0
- package/dist/node-registry.d.ts +4 -0
- package/dist/node-registry.d.ts.map +1 -0
- package/dist/nodes/AlertQuoteEditNode.d.ts +9 -0
- package/dist/nodes/AlertQuoteEditNode.d.ts.map +1 -0
- package/dist/nodes/AlertQuoteNode.d.ts +31 -0
- package/dist/nodes/AlertQuoteNode.d.ts.map +1 -0
- package/dist/nodes/BannerEditNode.d.ts +9 -0
- package/dist/nodes/BannerEditNode.d.ts.map +1 -0
- package/dist/nodes/BannerNode.d.ts +32 -0
- package/dist/nodes/BannerNode.d.ts.map +1 -0
- package/dist/nodes/CodeBlockEditNode.d.ts +9 -0
- package/dist/nodes/CodeBlockEditNode.d.ts.map +1 -0
- package/dist/nodes/CodeBlockNode.d.ts +28 -0
- package/dist/nodes/CodeBlockNode.d.ts.map +1 -0
- package/dist/nodes/DetailsNode.d.ts +28 -0
- package/dist/nodes/DetailsNode.d.ts.map +1 -0
- package/dist/nodes/FootnoteNode.d.ts +22 -0
- package/dist/nodes/FootnoteNode.d.ts.map +1 -0
- package/dist/nodes/FootnoteSectionEditNode.d.ts +10 -0
- package/dist/nodes/FootnoteSectionEditNode.d.ts.map +1 -0
- package/dist/nodes/FootnoteSectionNode.d.ts +25 -0
- package/dist/nodes/FootnoteSectionNode.d.ts.map +1 -0
- package/dist/nodes/GridContainerNode.d.ts +34 -0
- package/dist/nodes/GridContainerNode.d.ts.map +1 -0
- package/dist/nodes/GridEditNode.d.ts +9 -0
- package/dist/nodes/GridEditNode.d.ts.map +1 -0
- package/dist/nodes/HorizontalRuleNode.d.ts +19 -0
- package/dist/nodes/HorizontalRuleNode.d.ts.map +1 -0
- package/dist/nodes/ImageNode.d.ts +43 -0
- package/dist/nodes/ImageNode.d.ts.map +1 -0
- package/dist/nodes/KaTeXBlockNode.d.ts +24 -0
- package/dist/nodes/KaTeXBlockNode.d.ts.map +1 -0
- package/dist/nodes/KaTeXInlineNode.d.ts +22 -0
- package/dist/nodes/KaTeXInlineNode.d.ts.map +1 -0
- package/dist/nodes/LinkCardNode.d.ts +49 -0
- package/dist/nodes/LinkCardNode.d.ts.map +1 -0
- package/dist/nodes/MentionNode.d.ts +27 -0
- package/dist/nodes/MentionNode.d.ts.map +1 -0
- package/dist/nodes/MermaidNode.d.ts +24 -0
- package/dist/nodes/MermaidNode.d.ts.map +1 -0
- package/dist/nodes/SpoilerNode.d.ts +16 -0
- package/dist/nodes/SpoilerNode.d.ts.map +1 -0
- package/dist/nodes/TaskListItemNode.d.ts +21 -0
- package/dist/nodes/TaskListItemNode.d.ts.map +1 -0
- package/dist/nodes/VideoNode.d.ts +36 -0
- package/dist/nodes/VideoNode.d.ts.map +1 -0
- package/dist/nodes/index.d.ts +30 -0
- package/dist/nodes/index.d.ts.map +1 -0
- package/dist/nodes/shared.d.ts +3 -0
- package/dist/nodes/shared.d.ts.map +1 -0
- package/dist/plugins/AlertPlugin.d.ts +4 -0
- package/dist/plugins/AlertPlugin.d.ts.map +1 -0
- package/dist/plugins/AutoFocusPlugin.d.ts +2 -0
- package/dist/plugins/AutoFocusPlugin.d.ts.map +1 -0
- package/dist/plugins/AutoLinkPlugin.d.ts +7 -0
- package/dist/plugins/AutoLinkPlugin.d.ts.map +1 -0
- package/dist/plugins/DragDropPlugin.d.ts +2 -0
- package/dist/plugins/DragDropPlugin.d.ts.map +1 -0
- package/dist/plugins/EditorRefPlugin.d.ts +7 -0
- package/dist/plugins/EditorRefPlugin.d.ts.map +1 -0
- package/dist/plugins/FootnotePlugin.d.ts +4 -0
- package/dist/plugins/FootnotePlugin.d.ts.map +1 -0
- package/dist/plugins/HeadingAnchorPlugin.d.ts +2 -0
- package/dist/plugins/HeadingAnchorPlugin.d.ts.map +1 -0
- package/dist/plugins/ImagePlugin.d.ts +5 -0
- package/dist/plugins/ImagePlugin.d.ts.map +1 -0
- package/dist/plugins/ImageUploadPlugin.d.ts +14 -0
- package/dist/plugins/ImageUploadPlugin.d.ts.map +1 -0
- package/dist/plugins/KaTeXPlugin.d.ts +4 -0
- package/dist/plugins/KaTeXPlugin.d.ts.map +1 -0
- package/dist/plugins/MarkdownShortcutsPlugin.d.ts +2 -0
- package/dist/plugins/MarkdownShortcutsPlugin.d.ts.map +1 -0
- package/dist/plugins/MermaidPlugin.d.ts +3 -0
- package/dist/plugins/MermaidPlugin.d.ts.map +1 -0
- package/dist/plugins/OnChangePlugin.d.ts +8 -0
- package/dist/plugins/OnChangePlugin.d.ts.map +1 -0
- package/dist/plugins/SubmitShortcutPlugin.d.ts +6 -0
- package/dist/plugins/SubmitShortcutPlugin.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +16 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/renderer.d.ts +3 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.mjs +4 -0
- package/dist/rich-editor.css +1328 -0
- package/dist/styles/article.css.d.ts +3 -0
- package/dist/styles/article.css.d.ts.map +1 -0
- package/dist/styles/comment.css.d.ts +3 -0
- package/dist/styles/comment.css.d.ts.map +1 -0
- package/dist/styles/details.css.d.ts +2 -0
- package/dist/styles/details.css.d.ts.map +1 -0
- package/dist/styles/grid.css.d.ts +2 -0
- package/dist/styles/grid.css.d.ts.map +1 -0
- package/dist/styles/index.d.ts +7 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/katex.css.d.ts +2 -0
- package/dist/styles/katex.css.d.ts.map +1 -0
- package/dist/styles/note.css.d.ts +3 -0
- package/dist/styles/note.css.d.ts.map +1 -0
- package/dist/styles/shared.css.d.ts +2 -0
- package/dist/styles/shared.css.d.ts.map +1 -0
- package/dist/styles/theme.d.ts +3 -0
- package/dist/styles/theme.d.ts.map +1 -0
- package/dist/styles/vars.css.d.ts +8 -0
- package/dist/styles/vars.css.d.ts.map +1 -0
- package/dist/transformers/alert.d.ts +3 -0
- package/dist/transformers/alert.d.ts.map +1 -0
- package/dist/transformers/container.d.ts +3 -0
- package/dist/transformers/container.d.ts.map +1 -0
- package/dist/transformers/footnote.d.ts +10 -0
- package/dist/transformers/footnote.d.ts.map +1 -0
- package/dist/transformers/index.d.ts +10 -0
- package/dist/transformers/index.d.ts.map +1 -0
- package/dist/transformers/insert.d.ts +6 -0
- package/dist/transformers/insert.d.ts.map +1 -0
- package/dist/transformers/katex.d.ts +4 -0
- package/dist/transformers/katex.d.ts.map +1 -0
- package/dist/transformers/mention.d.ts +3 -0
- package/dist/transformers/mention.d.ts.map +1 -0
- package/dist/transformers/spoiler.d.ts +3 -0
- package/dist/transformers/spoiler.d.ts.map +1 -0
- package/dist/transformers/tasklist.d.ts +11 -0
- package/dist/transformers/tasklist.d.ts.map +1 -0
- package/dist/types/renderer-config.d.ts +67 -0
- package/dist/types/renderer-config.d.ts.map +1 -0
- package/dist/types/slash-menu.d.ts +11 -0
- package/dist/types/slash-menu.d.ts.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/lucide-dom.d.ts +4 -0
- package/dist/utils/lucide-dom.d.ts.map +1 -0
- package/dist/utils/shiki.d.ts +6 -0
- package/dist/utils/shiki.d.ts.map +1 -0
- package/dist/utils/thumbhash.d.ts +7 -0
- package/dist/utils/thumbhash.d.ts.map +1 -0
- package/dist/utils-CctLX9nj.js +2702 -0
- package/package.json +88 -0
|
@@ -0,0 +1,2702 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
5
|
+
import { createContext, useMemo, use, createElement, useCallback, useState, useEffect, useRef } from "react";
|
|
6
|
+
import { CodeNode } from "@lexical/code";
|
|
7
|
+
import { HorizontalRuleNode } from "@lexical/extension";
|
|
8
|
+
import { LinkNode, AutoLinkNode } from "@lexical/link";
|
|
9
|
+
import { ListNode, ListItemNode } from "@lexical/list";
|
|
10
|
+
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
11
|
+
import { TableNode, TableCellNode, TableRowNode } from "@lexical/table";
|
|
12
|
+
import { DecoratorNode, ElementNode, $insertNodes, $getRoot, createEditor, $getNodeByKey, $nodesOfType } from "lexical";
|
|
13
|
+
import { OctagonAlert, TriangleAlert, MessageSquareWarning, Lightbulb, Info, Flag, Code, ChevronDown, ChevronRight, LayoutGrid, ImageIcon, Sigma, Link, Workflow, Video } from "lucide-react";
|
|
14
|
+
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
15
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
16
|
+
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
|
|
17
|
+
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
18
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
19
|
+
import { TooltipRoot, TooltipTrigger, TooltipContent } from "@haklex/rich-editor-ui";
|
|
20
|
+
const PortalThemeContext = createContext({
|
|
21
|
+
className: ""
|
|
22
|
+
});
|
|
23
|
+
function PortalThemeProvider({
|
|
24
|
+
className,
|
|
25
|
+
children
|
|
26
|
+
}) {
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
PortalThemeContext.Provider,
|
|
29
|
+
{
|
|
30
|
+
value: useMemo(() => ({ className }), [className]),
|
|
31
|
+
children
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const RendererConfigContext = createContext({
|
|
36
|
+
config: void 0,
|
|
37
|
+
mode: "renderer"
|
|
38
|
+
});
|
|
39
|
+
function RendererConfigProvider({
|
|
40
|
+
config,
|
|
41
|
+
mode,
|
|
42
|
+
children
|
|
43
|
+
}) {
|
|
44
|
+
const value = useMemo(() => ({ config, mode }), [config, mode]);
|
|
45
|
+
return /* @__PURE__ */ jsx(RendererConfigContext.Provider, { value, children });
|
|
46
|
+
}
|
|
47
|
+
function useRendererConfig() {
|
|
48
|
+
return use(RendererConfigContext).config;
|
|
49
|
+
}
|
|
50
|
+
function useRendererMode() {
|
|
51
|
+
return use(RendererConfigContext).mode;
|
|
52
|
+
}
|
|
53
|
+
function RendererWrapper({
|
|
54
|
+
rendererKey,
|
|
55
|
+
defaultRenderer: DefaultRenderer,
|
|
56
|
+
props
|
|
57
|
+
}) {
|
|
58
|
+
const config = useRendererConfig();
|
|
59
|
+
const Renderer = config?.[rendererKey] ?? DefaultRenderer;
|
|
60
|
+
return /* @__PURE__ */ jsx(Renderer, { ...props });
|
|
61
|
+
}
|
|
62
|
+
function createRendererDecoration(rendererKey, defaultRenderer, props) {
|
|
63
|
+
return createElement(RendererWrapper, {
|
|
64
|
+
rendererKey,
|
|
65
|
+
defaultRenderer,
|
|
66
|
+
props
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const InfoIcon = () => /* @__PURE__ */ jsx(Info, { size: 16 });
|
|
70
|
+
const LightbulbIcon = () => /* @__PURE__ */ jsx(Lightbulb, { size: 16 });
|
|
71
|
+
const MessageWarningIcon = () => /* @__PURE__ */ jsx(MessageSquareWarning, { size: 16 });
|
|
72
|
+
const TriangleAlertIcon = () => /* @__PURE__ */ jsx(TriangleAlert, { size: 16 });
|
|
73
|
+
const OctagonAlertIcon = () => /* @__PURE__ */ jsx(OctagonAlert, { size: 16 });
|
|
74
|
+
const ALERT_ICONS = {
|
|
75
|
+
note: InfoIcon,
|
|
76
|
+
tip: LightbulbIcon,
|
|
77
|
+
important: MessageWarningIcon,
|
|
78
|
+
warning: TriangleAlertIcon,
|
|
79
|
+
caution: OctagonAlertIcon
|
|
80
|
+
};
|
|
81
|
+
const AlertRenderer = ({ type }) => {
|
|
82
|
+
const Icon = ALERT_ICONS[type];
|
|
83
|
+
return /* @__PURE__ */ jsxs("div", { className: `rich-alert-header rich-alert-header-${type}`, children: [
|
|
84
|
+
/* @__PURE__ */ jsx("span", { className: "rich-alert-icon", children: /* @__PURE__ */ jsx(Icon, {}) }),
|
|
85
|
+
/* @__PURE__ */ jsx("span", { className: "rich-alert-label", children: ALERT_LABELS[type] })
|
|
86
|
+
] });
|
|
87
|
+
};
|
|
88
|
+
function AlertReadOnlyDecorator({
|
|
89
|
+
alertType,
|
|
90
|
+
contentEditor
|
|
91
|
+
}) {
|
|
92
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
RendererWrapper,
|
|
95
|
+
{
|
|
96
|
+
rendererKey: "Alert",
|
|
97
|
+
defaultRenderer: AlertRenderer,
|
|
98
|
+
props: { type: alertType }
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ jsx("div", { className: "rich-alert-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
|
|
102
|
+
RichTextPlugin,
|
|
103
|
+
{
|
|
104
|
+
contentEditable: /* @__PURE__ */ jsx(
|
|
105
|
+
ContentEditable,
|
|
106
|
+
{
|
|
107
|
+
className: "rich-alert-content-editable",
|
|
108
|
+
style: { outline: "none" },
|
|
109
|
+
"aria-placeholder": "",
|
|
110
|
+
placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
ErrorBoundary: LexicalErrorBoundary
|
|
114
|
+
}
|
|
115
|
+
) }) })
|
|
116
|
+
] });
|
|
117
|
+
}
|
|
118
|
+
const editorTheme = {
|
|
119
|
+
text: {
|
|
120
|
+
bold: "rich-text-bold",
|
|
121
|
+
italic: "rich-text-italic",
|
|
122
|
+
underline: "rich-text-underline",
|
|
123
|
+
strikethrough: "rich-text-strikethrough",
|
|
124
|
+
code: "rich-text-code",
|
|
125
|
+
highlight: "rich-text-highlight"
|
|
126
|
+
},
|
|
127
|
+
heading: {
|
|
128
|
+
h1: "rich-heading-h1",
|
|
129
|
+
h2: "rich-heading-h2",
|
|
130
|
+
h3: "rich-heading-h3",
|
|
131
|
+
h4: "rich-heading-h4",
|
|
132
|
+
h5: "rich-heading-h5",
|
|
133
|
+
h6: "rich-heading-h6"
|
|
134
|
+
},
|
|
135
|
+
list: {
|
|
136
|
+
ol: "rich-list-ol",
|
|
137
|
+
ul: "rich-list-ul",
|
|
138
|
+
listitem: "rich-list-item",
|
|
139
|
+
nested: {
|
|
140
|
+
listitem: "rich-list-nested-item"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
quote: "rich-quote",
|
|
144
|
+
link: "rich-link",
|
|
145
|
+
paragraph: "rich-paragraph",
|
|
146
|
+
code: "rich-code-block",
|
|
147
|
+
table: "rich-table",
|
|
148
|
+
tableCell: "rich-table-cell",
|
|
149
|
+
tableCellHeader: "rich-table-cell-header",
|
|
150
|
+
tableScrollableWrapper: "rich-table-scrollable-wrapper",
|
|
151
|
+
/** Used by @lexical/extension HorizontalRuleNode */
|
|
152
|
+
hr: "rich-hr"
|
|
153
|
+
};
|
|
154
|
+
const FootnoteDefinitionsContext = createContext({
|
|
155
|
+
definitions: {},
|
|
156
|
+
displayNumberMap: {}
|
|
157
|
+
});
|
|
158
|
+
function FootnoteDefinitionsProvider({
|
|
159
|
+
definitions,
|
|
160
|
+
displayNumberMap,
|
|
161
|
+
children
|
|
162
|
+
}) {
|
|
163
|
+
const value = useMemo(
|
|
164
|
+
() => ({ definitions, displayNumberMap }),
|
|
165
|
+
[definitions, displayNumberMap]
|
|
166
|
+
);
|
|
167
|
+
return /* @__PURE__ */ jsx(FootnoteDefinitionsContext, { value, children });
|
|
168
|
+
}
|
|
169
|
+
function useFootnoteDefinitions() {
|
|
170
|
+
return use(FootnoteDefinitionsContext);
|
|
171
|
+
}
|
|
172
|
+
function useFootnoteContent(identifier) {
|
|
173
|
+
const { definitions } = use(FootnoteDefinitionsContext);
|
|
174
|
+
return definitions[identifier];
|
|
175
|
+
}
|
|
176
|
+
function useFootnoteDisplayNumber(identifier) {
|
|
177
|
+
const { displayNumberMap } = use(FootnoteDefinitionsContext);
|
|
178
|
+
return displayNumberMap[identifier];
|
|
179
|
+
}
|
|
180
|
+
function FootnoteRenderer({ identifier }) {
|
|
181
|
+
const content = useFootnoteContent(identifier);
|
|
182
|
+
const displayNumber = useFootnoteDisplayNumber(identifier);
|
|
183
|
+
const referenceId = `footnote-ref-${identifier}`;
|
|
184
|
+
const targetId = `footnote-${identifier}`;
|
|
185
|
+
const handleClick = useCallback(
|
|
186
|
+
(e) => {
|
|
187
|
+
const target = document.getElementById(targetId) || document.getElementById(`fn-${identifier}`);
|
|
188
|
+
if (!target) return;
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
191
|
+
target.classList.add("rich-footnote-highlight");
|
|
192
|
+
window.setTimeout(() => {
|
|
193
|
+
target.classList.remove("rich-footnote-highlight");
|
|
194
|
+
}, 1200);
|
|
195
|
+
},
|
|
196
|
+
[identifier, targetId]
|
|
197
|
+
);
|
|
198
|
+
const label = displayNumber ?? identifier;
|
|
199
|
+
return /* @__PURE__ */ jsx("span", { className: "rich-footnote-ref-wrapper", children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
|
|
200
|
+
/* @__PURE__ */ jsx(
|
|
201
|
+
TooltipTrigger,
|
|
202
|
+
{
|
|
203
|
+
render: (props) => /* @__PURE__ */ jsx(
|
|
204
|
+
"a",
|
|
205
|
+
{
|
|
206
|
+
...props,
|
|
207
|
+
className: "rich-footnote-ref",
|
|
208
|
+
href: `#${targetId}`,
|
|
209
|
+
id: referenceId,
|
|
210
|
+
role: "doc-noteref",
|
|
211
|
+
"aria-label": content ? `Footnote ${label}: ${content}` : `Footnote ${label}`,
|
|
212
|
+
onClick: handleClick,
|
|
213
|
+
"data-footnote-ref": identifier,
|
|
214
|
+
children: label
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
content ? /* @__PURE__ */ jsx(TooltipContent, { children: content }) : null
|
|
220
|
+
] }) });
|
|
221
|
+
}
|
|
222
|
+
class FootnoteNode extends DecoratorNode {
|
|
223
|
+
constructor(identifier, key) {
|
|
224
|
+
super(key);
|
|
225
|
+
__publicField(this, "__identifier");
|
|
226
|
+
this.__identifier = identifier;
|
|
227
|
+
}
|
|
228
|
+
static getType() {
|
|
229
|
+
return "footnote";
|
|
230
|
+
}
|
|
231
|
+
static clone(node) {
|
|
232
|
+
return new FootnoteNode(node.__identifier, node.__key);
|
|
233
|
+
}
|
|
234
|
+
createDOM(_config) {
|
|
235
|
+
const sup = document.createElement("sup");
|
|
236
|
+
sup.className = "rich-footnote";
|
|
237
|
+
return sup;
|
|
238
|
+
}
|
|
239
|
+
updateDOM() {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
isInline() {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
static importJSON(serializedNode) {
|
|
246
|
+
return $createFootnoteNode(serializedNode.identifier);
|
|
247
|
+
}
|
|
248
|
+
exportJSON() {
|
|
249
|
+
return {
|
|
250
|
+
...super.exportJSON(),
|
|
251
|
+
type: "footnote",
|
|
252
|
+
identifier: this.__identifier,
|
|
253
|
+
version: 1
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
getIdentifier() {
|
|
257
|
+
return this.getLatest().__identifier;
|
|
258
|
+
}
|
|
259
|
+
setIdentifier(identifier) {
|
|
260
|
+
const writable = this.getWritable();
|
|
261
|
+
writable.__identifier = identifier;
|
|
262
|
+
}
|
|
263
|
+
decorate(_editor, _config) {
|
|
264
|
+
return createRendererDecoration("Footnote", FootnoteRenderer, {
|
|
265
|
+
identifier: this.__identifier
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function $createFootnoteNode(identifier) {
|
|
270
|
+
return new FootnoteNode(identifier);
|
|
271
|
+
}
|
|
272
|
+
function $isFootnoteNode(node) {
|
|
273
|
+
return node instanceof FootnoteNode;
|
|
274
|
+
}
|
|
275
|
+
let katexModule = null;
|
|
276
|
+
let katexLoadPromise = null;
|
|
277
|
+
function loadKaTeX() {
|
|
278
|
+
if (katexModule) return Promise.resolve(katexModule);
|
|
279
|
+
if (!katexLoadPromise) {
|
|
280
|
+
katexLoadPromise = import("katex").then((mod) => {
|
|
281
|
+
katexModule = mod;
|
|
282
|
+
return katexModule;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return katexLoadPromise;
|
|
286
|
+
}
|
|
287
|
+
function KaTeXRenderer({ equation, displayMode }) {
|
|
288
|
+
const [html, setHtml] = useState(null);
|
|
289
|
+
const [error, setError] = useState(null);
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
let cancelled = false;
|
|
292
|
+
loadKaTeX().then((katex) => {
|
|
293
|
+
if (cancelled) return;
|
|
294
|
+
const rendered = katex.default.renderToString(equation, {
|
|
295
|
+
displayMode,
|
|
296
|
+
throwOnError: false
|
|
297
|
+
});
|
|
298
|
+
setHtml(rendered);
|
|
299
|
+
setError(null);
|
|
300
|
+
}).catch(() => {
|
|
301
|
+
if (cancelled) return;
|
|
302
|
+
setHtml(null);
|
|
303
|
+
setError("KaTeX is not available");
|
|
304
|
+
});
|
|
305
|
+
return () => {
|
|
306
|
+
cancelled = true;
|
|
307
|
+
};
|
|
308
|
+
}, [equation, displayMode]);
|
|
309
|
+
if (error) {
|
|
310
|
+
return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
|
|
311
|
+
}
|
|
312
|
+
if (html) {
|
|
313
|
+
return /* @__PURE__ */ jsx(
|
|
314
|
+
"span",
|
|
315
|
+
{
|
|
316
|
+
className: displayMode ? "rich-katex-block" : "rich-katex-inline",
|
|
317
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return /* @__PURE__ */ jsx("code", { className: "rich-katex-fallback", children: equation });
|
|
322
|
+
}
|
|
323
|
+
class KaTeXInlineNode extends DecoratorNode {
|
|
324
|
+
constructor(equation, key) {
|
|
325
|
+
super(key);
|
|
326
|
+
__publicField(this, "__equation");
|
|
327
|
+
this.__equation = equation;
|
|
328
|
+
}
|
|
329
|
+
static getType() {
|
|
330
|
+
return "katex-inline";
|
|
331
|
+
}
|
|
332
|
+
static clone(node) {
|
|
333
|
+
return new KaTeXInlineNode(node.__equation, node.__key);
|
|
334
|
+
}
|
|
335
|
+
createDOM(_config) {
|
|
336
|
+
return document.createElement("span");
|
|
337
|
+
}
|
|
338
|
+
updateDOM() {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
isInline() {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
static importJSON(serializedNode) {
|
|
345
|
+
return $createKaTeXInlineNode(serializedNode.equation);
|
|
346
|
+
}
|
|
347
|
+
exportJSON() {
|
|
348
|
+
return {
|
|
349
|
+
...super.exportJSON(),
|
|
350
|
+
type: "katex-inline",
|
|
351
|
+
equation: this.__equation,
|
|
352
|
+
version: 1
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
getEquation() {
|
|
356
|
+
return this.__equation;
|
|
357
|
+
}
|
|
358
|
+
setEquation(equation) {
|
|
359
|
+
const writable = this.getWritable();
|
|
360
|
+
writable.__equation = equation;
|
|
361
|
+
}
|
|
362
|
+
decorate(_editor, _config) {
|
|
363
|
+
return createRendererDecoration("KaTeX", KaTeXRenderer, {
|
|
364
|
+
equation: this.__equation,
|
|
365
|
+
displayMode: false
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function $createKaTeXInlineNode(equation) {
|
|
370
|
+
return new KaTeXInlineNode(equation);
|
|
371
|
+
}
|
|
372
|
+
function $isKaTeXInlineNode(node) {
|
|
373
|
+
return node instanceof KaTeXInlineNode;
|
|
374
|
+
}
|
|
375
|
+
function MentionRenderer({ handle, displayName }) {
|
|
376
|
+
const normalizedHandle = handle.replace(/^@+/, "");
|
|
377
|
+
const label = displayName || normalizedHandle;
|
|
378
|
+
return /* @__PURE__ */ jsx("span", { className: "rich-mention rich-mention-plain", children: /* @__PURE__ */ jsxs("span", { className: "rich-mention-handle", children: [
|
|
379
|
+
"@",
|
|
380
|
+
label
|
|
381
|
+
] }) });
|
|
382
|
+
}
|
|
383
|
+
class MentionNode extends DecoratorNode {
|
|
384
|
+
constructor(platform, handle, displayName, key) {
|
|
385
|
+
super(key);
|
|
386
|
+
__publicField(this, "__platform");
|
|
387
|
+
__publicField(this, "__handle");
|
|
388
|
+
__publicField(this, "__displayName");
|
|
389
|
+
this.__platform = platform;
|
|
390
|
+
this.__handle = handle;
|
|
391
|
+
this.__displayName = displayName;
|
|
392
|
+
}
|
|
393
|
+
static getType() {
|
|
394
|
+
return "mention";
|
|
395
|
+
}
|
|
396
|
+
static clone(node) {
|
|
397
|
+
return new MentionNode(
|
|
398
|
+
node.__platform,
|
|
399
|
+
node.__handle,
|
|
400
|
+
node.__displayName,
|
|
401
|
+
node.__key
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
createDOM(_config) {
|
|
405
|
+
const el = document.createElement("span");
|
|
406
|
+
el.style.display = "inline-flex";
|
|
407
|
+
el.style.alignItems = "center";
|
|
408
|
+
el.style.height = "1lh";
|
|
409
|
+
return el;
|
|
410
|
+
}
|
|
411
|
+
updateDOM() {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
isInline() {
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
getPlatform() {
|
|
418
|
+
return this.getLatest().__platform;
|
|
419
|
+
}
|
|
420
|
+
getHandle() {
|
|
421
|
+
return this.getLatest().__handle;
|
|
422
|
+
}
|
|
423
|
+
getDisplayName() {
|
|
424
|
+
return this.getLatest().__displayName;
|
|
425
|
+
}
|
|
426
|
+
static importJSON(serializedNode) {
|
|
427
|
+
return $createMentionNode(
|
|
428
|
+
serializedNode.platform,
|
|
429
|
+
serializedNode.handle,
|
|
430
|
+
serializedNode.displayName
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
exportJSON() {
|
|
434
|
+
return {
|
|
435
|
+
...super.exportJSON(),
|
|
436
|
+
type: "mention",
|
|
437
|
+
platform: this.__platform,
|
|
438
|
+
handle: this.__handle,
|
|
439
|
+
...this.__displayName ? { displayName: this.__displayName } : {},
|
|
440
|
+
version: 1
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
decorate(_editor, _config) {
|
|
444
|
+
return createRendererDecoration("Mention", MentionRenderer, {
|
|
445
|
+
platform: this.__platform,
|
|
446
|
+
handle: this.__handle,
|
|
447
|
+
displayName: this.__displayName
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function $createMentionNode(platform, handle, displayName) {
|
|
452
|
+
return new MentionNode(platform, handle, displayName);
|
|
453
|
+
}
|
|
454
|
+
function $isMentionNode(node) {
|
|
455
|
+
return node instanceof MentionNode;
|
|
456
|
+
}
|
|
457
|
+
class SpoilerNode extends ElementNode {
|
|
458
|
+
static getType() {
|
|
459
|
+
return "spoiler";
|
|
460
|
+
}
|
|
461
|
+
static clone(node) {
|
|
462
|
+
return new SpoilerNode(node.__key);
|
|
463
|
+
}
|
|
464
|
+
constructor(key) {
|
|
465
|
+
super(key);
|
|
466
|
+
}
|
|
467
|
+
createDOM(_config) {
|
|
468
|
+
const span = document.createElement("span");
|
|
469
|
+
span.className = "rich-spoiler";
|
|
470
|
+
span.setAttribute("role", "button");
|
|
471
|
+
span.setAttribute("tabindex", "0");
|
|
472
|
+
span.setAttribute("aria-label", "Spoiler (click to reveal)");
|
|
473
|
+
const toggle = () => {
|
|
474
|
+
if (span.isContentEditable) return;
|
|
475
|
+
const revealed = span.classList.toggle("rich-spoiler-revealed");
|
|
476
|
+
span.setAttribute(
|
|
477
|
+
"aria-label",
|
|
478
|
+
revealed ? "Spoiler (revealed)" : "Spoiler (click to reveal)"
|
|
479
|
+
);
|
|
480
|
+
};
|
|
481
|
+
span.addEventListener("click", toggle);
|
|
482
|
+
span.addEventListener("keydown", (e) => {
|
|
483
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
484
|
+
e.preventDefault();
|
|
485
|
+
toggle();
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
return span;
|
|
489
|
+
}
|
|
490
|
+
updateDOM() {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
static importJSON(_serializedNode) {
|
|
494
|
+
return $createSpoilerNode();
|
|
495
|
+
}
|
|
496
|
+
exportJSON() {
|
|
497
|
+
return {
|
|
498
|
+
...super.exportJSON(),
|
|
499
|
+
type: "spoiler",
|
|
500
|
+
version: 1
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
canInsertTextBefore() {
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
canInsertTextAfter() {
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
isInline() {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function $createSpoilerNode() {
|
|
514
|
+
return new SpoilerNode();
|
|
515
|
+
}
|
|
516
|
+
function $isSpoilerNode(node) {
|
|
517
|
+
return node instanceof SpoilerNode;
|
|
518
|
+
}
|
|
519
|
+
class TaskListItemNode extends ElementNode {
|
|
520
|
+
constructor(checked, key) {
|
|
521
|
+
super(key);
|
|
522
|
+
__publicField(this, "__checked");
|
|
523
|
+
this.__checked = checked;
|
|
524
|
+
}
|
|
525
|
+
static getType() {
|
|
526
|
+
return "task-list-item";
|
|
527
|
+
}
|
|
528
|
+
static clone(node) {
|
|
529
|
+
return new TaskListItemNode(node.__checked, node.__key);
|
|
530
|
+
}
|
|
531
|
+
createDOM(_config) {
|
|
532
|
+
const li = document.createElement("li");
|
|
533
|
+
li.className = "rich-task-list-item";
|
|
534
|
+
li.setAttribute("role", "listitem");
|
|
535
|
+
const checkbox = document.createElement("input");
|
|
536
|
+
checkbox.type = "checkbox";
|
|
537
|
+
checkbox.checked = this.__checked;
|
|
538
|
+
checkbox.disabled = true;
|
|
539
|
+
checkbox.className = "rich-task-checkbox";
|
|
540
|
+
li.prepend(checkbox);
|
|
541
|
+
if (this.__checked) {
|
|
542
|
+
li.classList.add("rich-task-checked");
|
|
543
|
+
}
|
|
544
|
+
return li;
|
|
545
|
+
}
|
|
546
|
+
updateDOM(prevNode, dom) {
|
|
547
|
+
if (prevNode.__checked !== this.__checked) {
|
|
548
|
+
const checkbox = dom.querySelector(
|
|
549
|
+
'input[type="checkbox"]'
|
|
550
|
+
);
|
|
551
|
+
if (checkbox) {
|
|
552
|
+
checkbox.checked = this.__checked;
|
|
553
|
+
}
|
|
554
|
+
dom.classList.toggle("rich-task-checked", this.__checked);
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
static importJSON(serializedNode) {
|
|
559
|
+
return $createTaskListItemNode(serializedNode.checked);
|
|
560
|
+
}
|
|
561
|
+
exportJSON() {
|
|
562
|
+
return {
|
|
563
|
+
...super.exportJSON(),
|
|
564
|
+
type: "task-list-item",
|
|
565
|
+
checked: this.__checked,
|
|
566
|
+
version: 1
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
getChecked() {
|
|
570
|
+
return this.getLatest().__checked;
|
|
571
|
+
}
|
|
572
|
+
setChecked(checked) {
|
|
573
|
+
const writable = this.getWritable();
|
|
574
|
+
writable.__checked = checked;
|
|
575
|
+
}
|
|
576
|
+
toggleChecked() {
|
|
577
|
+
this.setChecked(!this.getChecked());
|
|
578
|
+
}
|
|
579
|
+
isInline() {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function $createTaskListItemNode(checked = false) {
|
|
584
|
+
return new TaskListItemNode(checked);
|
|
585
|
+
}
|
|
586
|
+
function $isTaskListItemNode(node) {
|
|
587
|
+
return node instanceof TaskListItemNode;
|
|
588
|
+
}
|
|
589
|
+
const NESTED_EDITOR_NODES = [
|
|
590
|
+
HeadingNode,
|
|
591
|
+
QuoteNode,
|
|
592
|
+
ListNode,
|
|
593
|
+
ListItemNode,
|
|
594
|
+
LinkNode,
|
|
595
|
+
AutoLinkNode,
|
|
596
|
+
HorizontalRuleNode,
|
|
597
|
+
CodeNode,
|
|
598
|
+
TableNode,
|
|
599
|
+
TableCellNode,
|
|
600
|
+
TableRowNode,
|
|
601
|
+
SpoilerNode,
|
|
602
|
+
MentionNode,
|
|
603
|
+
TaskListItemNode,
|
|
604
|
+
FootnoteNode,
|
|
605
|
+
KaTeXInlineNode
|
|
606
|
+
];
|
|
607
|
+
const ALERT_TYPES = [
|
|
608
|
+
"note",
|
|
609
|
+
"tip",
|
|
610
|
+
"important",
|
|
611
|
+
"warning",
|
|
612
|
+
"caution"
|
|
613
|
+
];
|
|
614
|
+
const ALERT_LABELS = {
|
|
615
|
+
note: "Note",
|
|
616
|
+
tip: "Tip",
|
|
617
|
+
important: "Important",
|
|
618
|
+
warning: "Warning",
|
|
619
|
+
caution: "Caution"
|
|
620
|
+
};
|
|
621
|
+
function createContentEditor$1() {
|
|
622
|
+
return createEditor({
|
|
623
|
+
namespace: "AlertContent",
|
|
624
|
+
nodes: NESTED_EDITOR_NODES,
|
|
625
|
+
theme: editorTheme,
|
|
626
|
+
onError: (error) => {
|
|
627
|
+
console.error("[AlertContent]", error);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const _AlertQuoteNode = class _AlertQuoteNode extends DecoratorNode {
|
|
632
|
+
constructor(alertType, contentEditor, key) {
|
|
633
|
+
super(key);
|
|
634
|
+
__publicField(this, "__alertType");
|
|
635
|
+
__publicField(this, "__contentEditor");
|
|
636
|
+
this.__alertType = alertType;
|
|
637
|
+
this.__contentEditor = contentEditor || createContentEditor$1();
|
|
638
|
+
}
|
|
639
|
+
static getType() {
|
|
640
|
+
return "alert-quote";
|
|
641
|
+
}
|
|
642
|
+
static clone(node) {
|
|
643
|
+
return new _AlertQuoteNode(
|
|
644
|
+
node.__alertType,
|
|
645
|
+
node.__contentEditor,
|
|
646
|
+
node.__key
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
createDOM(_config) {
|
|
650
|
+
const div = document.createElement("div");
|
|
651
|
+
div.className = `rich-alert rich-alert-${this.__alertType}`;
|
|
652
|
+
return div;
|
|
653
|
+
}
|
|
654
|
+
updateDOM(prevNode, dom) {
|
|
655
|
+
if (prevNode.__alertType !== this.__alertType) {
|
|
656
|
+
dom.className = `rich-alert rich-alert-${this.__alertType}`;
|
|
657
|
+
}
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
isInline() {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
getAlertType() {
|
|
664
|
+
return this.__alertType;
|
|
665
|
+
}
|
|
666
|
+
setAlertType(alertType) {
|
|
667
|
+
const writable = this.getWritable();
|
|
668
|
+
writable.__alertType = alertType;
|
|
669
|
+
}
|
|
670
|
+
getContentEditor() {
|
|
671
|
+
return this.__contentEditor;
|
|
672
|
+
}
|
|
673
|
+
getTextContent() {
|
|
674
|
+
return this.__contentEditor.getEditorState().read(() => {
|
|
675
|
+
return $getRoot().getTextContent();
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
static importJSON(serializedNode) {
|
|
679
|
+
const node = new _AlertQuoteNode(serializedNode.alertType);
|
|
680
|
+
if (serializedNode.content) {
|
|
681
|
+
const editorState = node.__contentEditor.parseEditorState(
|
|
682
|
+
serializedNode.content
|
|
683
|
+
);
|
|
684
|
+
node.__contentEditor.setEditorState(editorState);
|
|
685
|
+
}
|
|
686
|
+
return node;
|
|
687
|
+
}
|
|
688
|
+
exportJSON() {
|
|
689
|
+
return {
|
|
690
|
+
...super.exportJSON(),
|
|
691
|
+
type: "alert-quote",
|
|
692
|
+
alertType: this.__alertType,
|
|
693
|
+
content: this.__contentEditor.getEditorState().toJSON(),
|
|
694
|
+
version: 1
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
decorate(_editor, _config) {
|
|
698
|
+
return createElement(AlertReadOnlyDecorator, {
|
|
699
|
+
alertType: this.__alertType,
|
|
700
|
+
contentEditor: this.__contentEditor
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
__publicField(_AlertQuoteNode, "slashMenuItems", [
|
|
705
|
+
{
|
|
706
|
+
title: "Callout",
|
|
707
|
+
icon: createElement(Info, { size: 20 }),
|
|
708
|
+
description: "Info callout block",
|
|
709
|
+
keywords: ["alert", "note", "info", "callout"],
|
|
710
|
+
section: "ADVANCED",
|
|
711
|
+
onSelect: (editor) => {
|
|
712
|
+
editor.update(() => {
|
|
713
|
+
$insertNodes([$createAlertQuoteNode("note")]);
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
title: "Tip",
|
|
719
|
+
icon: createElement(Lightbulb, { size: 20 }),
|
|
720
|
+
description: "Highlight a useful tip",
|
|
721
|
+
keywords: ["alert", "tip", "hint"],
|
|
722
|
+
section: "ADVANCED",
|
|
723
|
+
onSelect: (editor) => {
|
|
724
|
+
editor.update(() => {
|
|
725
|
+
$insertNodes([$createAlertQuoteNode("tip")]);
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
title: "Warning",
|
|
731
|
+
icon: createElement(TriangleAlert, { size: 20 }),
|
|
732
|
+
description: "Warn about something",
|
|
733
|
+
keywords: ["alert", "warning", "caution"],
|
|
734
|
+
section: "ADVANCED",
|
|
735
|
+
onSelect: (editor) => {
|
|
736
|
+
editor.update(() => {
|
|
737
|
+
$insertNodes([$createAlertQuoteNode("warning")]);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
]);
|
|
742
|
+
let AlertQuoteNode = _AlertQuoteNode;
|
|
743
|
+
function $createAlertQuoteNode(alertType) {
|
|
744
|
+
return new AlertQuoteNode(alertType);
|
|
745
|
+
}
|
|
746
|
+
function $isAlertQuoteNode(node) {
|
|
747
|
+
return node instanceof AlertQuoteNode;
|
|
748
|
+
}
|
|
749
|
+
const BannerRenderer = ({ type }) => {
|
|
750
|
+
return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
|
|
751
|
+
};
|
|
752
|
+
function BannerReadOnlyDecorator({
|
|
753
|
+
bannerType,
|
|
754
|
+
contentEditor
|
|
755
|
+
}) {
|
|
756
|
+
return /* @__PURE__ */ jsxs("div", { className: "rich-banner-inner", children: [
|
|
757
|
+
/* @__PURE__ */ jsx(
|
|
758
|
+
RendererWrapper,
|
|
759
|
+
{
|
|
760
|
+
rendererKey: "Banner",
|
|
761
|
+
defaultRenderer: BannerRenderer,
|
|
762
|
+
props: { type: bannerType }
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsx("div", { className: "rich-banner-content", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: contentEditor, children: /* @__PURE__ */ jsx(
|
|
766
|
+
RichTextPlugin,
|
|
767
|
+
{
|
|
768
|
+
contentEditable: /* @__PURE__ */ jsx(
|
|
769
|
+
ContentEditable,
|
|
770
|
+
{
|
|
771
|
+
className: "rich-banner-content-editable",
|
|
772
|
+
style: { outline: "none" },
|
|
773
|
+
"aria-placeholder": "",
|
|
774
|
+
placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
|
|
775
|
+
}
|
|
776
|
+
),
|
|
777
|
+
ErrorBoundary: LexicalErrorBoundary
|
|
778
|
+
}
|
|
779
|
+
) }) })
|
|
780
|
+
] });
|
|
781
|
+
}
|
|
782
|
+
const LEGACY_TYPE_MAP = {
|
|
783
|
+
info: "note",
|
|
784
|
+
success: "tip",
|
|
785
|
+
error: "caution"
|
|
786
|
+
};
|
|
787
|
+
function normalizeBannerType(type) {
|
|
788
|
+
if (type in LEGACY_TYPE_MAP) return LEGACY_TYPE_MAP[type];
|
|
789
|
+
return type || "note";
|
|
790
|
+
}
|
|
791
|
+
const BANNER_TYPES = [
|
|
792
|
+
"note",
|
|
793
|
+
"tip",
|
|
794
|
+
"important",
|
|
795
|
+
"warning",
|
|
796
|
+
"caution"
|
|
797
|
+
];
|
|
798
|
+
const BANNER_LABELS = {
|
|
799
|
+
note: "Note",
|
|
800
|
+
tip: "Tip",
|
|
801
|
+
important: "Important",
|
|
802
|
+
warning: "Warning",
|
|
803
|
+
caution: "Caution"
|
|
804
|
+
};
|
|
805
|
+
function createContentEditor() {
|
|
806
|
+
return createEditor({
|
|
807
|
+
namespace: "BannerContent",
|
|
808
|
+
nodes: NESTED_EDITOR_NODES,
|
|
809
|
+
theme: editorTheme,
|
|
810
|
+
onError: (error) => {
|
|
811
|
+
console.error("[BannerContent]", error);
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
const _BannerNode = class _BannerNode extends DecoratorNode {
|
|
816
|
+
constructor(bannerType, contentEditor, key) {
|
|
817
|
+
super(key);
|
|
818
|
+
__publicField(this, "__bannerType");
|
|
819
|
+
__publicField(this, "__contentEditor");
|
|
820
|
+
this.__bannerType = bannerType;
|
|
821
|
+
this.__contentEditor = contentEditor || createContentEditor();
|
|
822
|
+
}
|
|
823
|
+
static getType() {
|
|
824
|
+
return "banner";
|
|
825
|
+
}
|
|
826
|
+
static clone(node) {
|
|
827
|
+
return new _BannerNode(node.__bannerType, node.__contentEditor, node.__key);
|
|
828
|
+
}
|
|
829
|
+
createDOM(_config) {
|
|
830
|
+
const div = document.createElement("div");
|
|
831
|
+
div.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
832
|
+
return div;
|
|
833
|
+
}
|
|
834
|
+
updateDOM(prevNode, dom) {
|
|
835
|
+
if (prevNode.__bannerType !== this.__bannerType) {
|
|
836
|
+
dom.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
isInline() {
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
getBannerType() {
|
|
844
|
+
return this.__bannerType;
|
|
845
|
+
}
|
|
846
|
+
setBannerType(bannerType) {
|
|
847
|
+
const writable = this.getWritable();
|
|
848
|
+
writable.__bannerType = bannerType;
|
|
849
|
+
}
|
|
850
|
+
getContentEditor() {
|
|
851
|
+
return this.__contentEditor;
|
|
852
|
+
}
|
|
853
|
+
getTextContent() {
|
|
854
|
+
return this.__contentEditor.getEditorState().read(() => {
|
|
855
|
+
return $getRoot().getTextContent();
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
static importJSON(serializedNode) {
|
|
859
|
+
const legacy = serializedNode;
|
|
860
|
+
const bannerType = normalizeBannerType(serializedNode.bannerType);
|
|
861
|
+
const node = new _BannerNode(bannerType);
|
|
862
|
+
if (serializedNode.content) {
|
|
863
|
+
const editorState = node.__contentEditor.parseEditorState(
|
|
864
|
+
serializedNode.content
|
|
865
|
+
);
|
|
866
|
+
node.__contentEditor.setEditorState(editorState);
|
|
867
|
+
} else if (legacy.children) {
|
|
868
|
+
const content = {
|
|
869
|
+
root: {
|
|
870
|
+
children: legacy.children,
|
|
871
|
+
direction: null,
|
|
872
|
+
format: "",
|
|
873
|
+
indent: 0,
|
|
874
|
+
type: "root",
|
|
875
|
+
version: 1
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
const editorState = node.__contentEditor.parseEditorState(content);
|
|
879
|
+
node.__contentEditor.setEditorState(editorState);
|
|
880
|
+
}
|
|
881
|
+
return node;
|
|
882
|
+
}
|
|
883
|
+
exportJSON() {
|
|
884
|
+
return {
|
|
885
|
+
...super.exportJSON(),
|
|
886
|
+
type: "banner",
|
|
887
|
+
bannerType: this.__bannerType,
|
|
888
|
+
content: this.__contentEditor.getEditorState().toJSON(),
|
|
889
|
+
version: 1
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
decorate(_editor, _config) {
|
|
893
|
+
return createElement(BannerReadOnlyDecorator, {
|
|
894
|
+
bannerType: this.__bannerType,
|
|
895
|
+
contentEditor: this.__contentEditor
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
__publicField(_BannerNode, "slashMenuItems", [
|
|
900
|
+
{
|
|
901
|
+
title: "Banner",
|
|
902
|
+
icon: createElement(Flag, { size: 20 }),
|
|
903
|
+
description: "Highlighted banner block",
|
|
904
|
+
keywords: ["banner", "notice", "announcement"],
|
|
905
|
+
section: "ADVANCED",
|
|
906
|
+
onSelect: (editor) => {
|
|
907
|
+
editor.update(() => {
|
|
908
|
+
$insertNodes([$createBannerNode("note")]);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
]);
|
|
913
|
+
let BannerNode = _BannerNode;
|
|
914
|
+
function $createBannerNode(bannerType) {
|
|
915
|
+
return new BannerNode(bannerType);
|
|
916
|
+
}
|
|
917
|
+
function $isBannerNode(node) {
|
|
918
|
+
return node instanceof BannerNode;
|
|
919
|
+
}
|
|
920
|
+
const ColorSchemeContext = createContext("light");
|
|
921
|
+
function ColorSchemeProvider({
|
|
922
|
+
colorScheme,
|
|
923
|
+
children
|
|
924
|
+
}) {
|
|
925
|
+
return /* @__PURE__ */ jsx(ColorSchemeContext.Provider, { value: colorScheme, children });
|
|
926
|
+
}
|
|
927
|
+
function useColorScheme() {
|
|
928
|
+
return use(ColorSchemeContext);
|
|
929
|
+
}
|
|
930
|
+
let codeToHtmlFn = null;
|
|
931
|
+
let shikiLoadPromise = null;
|
|
932
|
+
function loadCodeToHtml() {
|
|
933
|
+
if (codeToHtmlFn) return Promise.resolve(codeToHtmlFn);
|
|
934
|
+
if (!shikiLoadPromise) {
|
|
935
|
+
shikiLoadPromise = import("shiki/bundle/web").then((mod) => {
|
|
936
|
+
codeToHtmlFn = mod.codeToHtml;
|
|
937
|
+
return mod.codeToHtml;
|
|
938
|
+
}).catch((err) => {
|
|
939
|
+
shikiLoadPromise = null;
|
|
940
|
+
throw err;
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
return shikiLoadPromise;
|
|
944
|
+
}
|
|
945
|
+
function CodeBlockRenderer({
|
|
946
|
+
code,
|
|
947
|
+
language,
|
|
948
|
+
showLineNumbers
|
|
949
|
+
}) {
|
|
950
|
+
const colorScheme = useColorScheme();
|
|
951
|
+
const shikiTheme = colorScheme === "dark" ? "github-dark" : "github-light";
|
|
952
|
+
const [highlightedHtml, setHighlightedHtml] = useState(null);
|
|
953
|
+
const [copied, setCopied] = useState(false);
|
|
954
|
+
const copyTimerRef = useRef(void 0);
|
|
955
|
+
useEffect(() => {
|
|
956
|
+
let cancelled = false;
|
|
957
|
+
setHighlightedHtml(null);
|
|
958
|
+
loadCodeToHtml().then(
|
|
959
|
+
(toHtml) => toHtml(code, {
|
|
960
|
+
lang: language,
|
|
961
|
+
theme: shikiTheme
|
|
962
|
+
})
|
|
963
|
+
).then((html) => {
|
|
964
|
+
if (!cancelled) {
|
|
965
|
+
setHighlightedHtml(html);
|
|
966
|
+
}
|
|
967
|
+
}).catch(() => {
|
|
968
|
+
if (!cancelled) {
|
|
969
|
+
setHighlightedHtml(null);
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
return () => {
|
|
973
|
+
cancelled = true;
|
|
974
|
+
};
|
|
975
|
+
}, [code, language, shikiTheme]);
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
return () => clearTimeout(copyTimerRef.current);
|
|
978
|
+
}, []);
|
|
979
|
+
const handleCopy = useCallback(() => {
|
|
980
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
981
|
+
setCopied(true);
|
|
982
|
+
clearTimeout(copyTimerRef.current);
|
|
983
|
+
copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
984
|
+
}).catch(() => {
|
|
985
|
+
});
|
|
986
|
+
}, [code]);
|
|
987
|
+
const header = language ? /* @__PURE__ */ jsxs("div", { className: "rich-code-block-header", children: [
|
|
988
|
+
/* @__PURE__ */ jsx("span", { className: "rich-code-block-lang", children: language }),
|
|
989
|
+
/* @__PURE__ */ jsx(
|
|
990
|
+
"button",
|
|
991
|
+
{
|
|
992
|
+
type: "button",
|
|
993
|
+
className: "rich-code-block-copy",
|
|
994
|
+
onClick: handleCopy,
|
|
995
|
+
"aria-label": copied ? "Copied to clipboard" : "Copy code",
|
|
996
|
+
children: copied ? "Copied" : "Copy"
|
|
997
|
+
}
|
|
998
|
+
)
|
|
999
|
+
] }) : null;
|
|
1000
|
+
const wrapperClass = showLineNumbers ? "rich-code-block rich-code-block-numbered" : "rich-code-block";
|
|
1001
|
+
if (highlightedHtml) {
|
|
1002
|
+
return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
|
|
1003
|
+
header,
|
|
1004
|
+
/* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } })
|
|
1005
|
+
] });
|
|
1006
|
+
}
|
|
1007
|
+
const lines = code.split("\n");
|
|
1008
|
+
return /* @__PURE__ */ jsxs("div", { className: wrapperClass, children: [
|
|
1009
|
+
header,
|
|
1010
|
+
/* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("span", { className: "line", children: [
|
|
1011
|
+
line,
|
|
1012
|
+
i < lines.length - 1 ? "\n" : ""
|
|
1013
|
+
] }, i)) }) })
|
|
1014
|
+
] });
|
|
1015
|
+
}
|
|
1016
|
+
const _CodeBlockNode = class _CodeBlockNode extends DecoratorNode {
|
|
1017
|
+
constructor(code, language, key) {
|
|
1018
|
+
super(key);
|
|
1019
|
+
__publicField(this, "__code");
|
|
1020
|
+
__publicField(this, "__language");
|
|
1021
|
+
this.__code = code;
|
|
1022
|
+
this.__language = language;
|
|
1023
|
+
}
|
|
1024
|
+
static getType() {
|
|
1025
|
+
return "code-block";
|
|
1026
|
+
}
|
|
1027
|
+
static clone(node) {
|
|
1028
|
+
return new _CodeBlockNode(node.__code, node.__language, node.__key);
|
|
1029
|
+
}
|
|
1030
|
+
createDOM(_config) {
|
|
1031
|
+
const div = document.createElement("div");
|
|
1032
|
+
div.className = "rich-code-block-wrapper";
|
|
1033
|
+
return div;
|
|
1034
|
+
}
|
|
1035
|
+
updateDOM() {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
isInline() {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
static importJSON(serializedNode) {
|
|
1042
|
+
return $createCodeBlockNode(serializedNode.code, serializedNode.language);
|
|
1043
|
+
}
|
|
1044
|
+
exportJSON() {
|
|
1045
|
+
return {
|
|
1046
|
+
...super.exportJSON(),
|
|
1047
|
+
type: "code-block",
|
|
1048
|
+
code: this.__code,
|
|
1049
|
+
language: this.__language,
|
|
1050
|
+
version: 1
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
getCode() {
|
|
1054
|
+
return this.__code;
|
|
1055
|
+
}
|
|
1056
|
+
setCode(code) {
|
|
1057
|
+
const writable = this.getWritable();
|
|
1058
|
+
writable.__code = code;
|
|
1059
|
+
}
|
|
1060
|
+
getLanguage() {
|
|
1061
|
+
return this.__language;
|
|
1062
|
+
}
|
|
1063
|
+
setLanguage(language) {
|
|
1064
|
+
const writable = this.getWritable();
|
|
1065
|
+
writable.__language = language;
|
|
1066
|
+
}
|
|
1067
|
+
decorate(_editor, _config) {
|
|
1068
|
+
return createRendererDecoration("CodeBlock", CodeBlockRenderer, {
|
|
1069
|
+
code: this.__code,
|
|
1070
|
+
language: this.__language
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
__publicField(_CodeBlockNode, "slashMenuItems", [
|
|
1075
|
+
{
|
|
1076
|
+
title: "Code Block",
|
|
1077
|
+
icon: createElement(Code, { size: 20 }),
|
|
1078
|
+
description: "Syntax-highlighted code",
|
|
1079
|
+
keywords: ["code", "snippet", "codeblock"],
|
|
1080
|
+
section: "MEDIA",
|
|
1081
|
+
onSelect: (editor) => {
|
|
1082
|
+
editor.update(() => {
|
|
1083
|
+
$insertNodes([$createCodeBlockNode("", "text")]);
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
]);
|
|
1088
|
+
let CodeBlockNode = _CodeBlockNode;
|
|
1089
|
+
function $createCodeBlockNode(code, language) {
|
|
1090
|
+
return new CodeBlockNode(code, language);
|
|
1091
|
+
}
|
|
1092
|
+
function $isCodeBlockNode(node) {
|
|
1093
|
+
return node instanceof CodeBlockNode;
|
|
1094
|
+
}
|
|
1095
|
+
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
1096
|
+
const DEFAULT_ATTRS = {
|
|
1097
|
+
xmlns: SVG_NS,
|
|
1098
|
+
width: "24",
|
|
1099
|
+
height: "24",
|
|
1100
|
+
viewBox: "0 0 24 24",
|
|
1101
|
+
fill: "none",
|
|
1102
|
+
stroke: "currentColor",
|
|
1103
|
+
"stroke-width": "2",
|
|
1104
|
+
"stroke-linecap": "round",
|
|
1105
|
+
"stroke-linejoin": "round"
|
|
1106
|
+
};
|
|
1107
|
+
function createLucideSvg(iconNode, attrs = {}) {
|
|
1108
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1109
|
+
const merged = { ...DEFAULT_ATTRS, ...attrs };
|
|
1110
|
+
for (const [k, v] of Object.entries(merged)) {
|
|
1111
|
+
svg.setAttribute(k, v);
|
|
1112
|
+
}
|
|
1113
|
+
for (const [tag, elAttrs] of iconNode) {
|
|
1114
|
+
const el = document.createElementNS(SVG_NS, tag);
|
|
1115
|
+
for (const [k, v] of Object.entries(elAttrs)) {
|
|
1116
|
+
if (k === "key") continue;
|
|
1117
|
+
el.setAttribute(k, v);
|
|
1118
|
+
}
|
|
1119
|
+
svg.append(el);
|
|
1120
|
+
}
|
|
1121
|
+
return svg;
|
|
1122
|
+
}
|
|
1123
|
+
const ChevronDownIconNode = ChevronDown.iconNode ?? ChevronDown.__iconNode ?? [];
|
|
1124
|
+
const _DetailsNode = class _DetailsNode extends ElementNode {
|
|
1125
|
+
constructor(summary, open = false, key) {
|
|
1126
|
+
super(key);
|
|
1127
|
+
__publicField(this, "__summary");
|
|
1128
|
+
__publicField(this, "__open");
|
|
1129
|
+
this.__summary = summary;
|
|
1130
|
+
this.__open = open;
|
|
1131
|
+
}
|
|
1132
|
+
static getType() {
|
|
1133
|
+
return "details";
|
|
1134
|
+
}
|
|
1135
|
+
static clone(node) {
|
|
1136
|
+
return new _DetailsNode(node.__summary, node.__open, node.__key);
|
|
1137
|
+
}
|
|
1138
|
+
createDOM(_config) {
|
|
1139
|
+
const details = document.createElement("details");
|
|
1140
|
+
details.className = "rich-details";
|
|
1141
|
+
if (this.__open) {
|
|
1142
|
+
details.open = true;
|
|
1143
|
+
}
|
|
1144
|
+
const summary = document.createElement("summary");
|
|
1145
|
+
summary.className = "rich-details-summary";
|
|
1146
|
+
const label = document.createElement("span");
|
|
1147
|
+
label.className = "rich-details-summary-text";
|
|
1148
|
+
label.textContent = this.__summary;
|
|
1149
|
+
summary.append(label);
|
|
1150
|
+
const chevron = document.createElement("span");
|
|
1151
|
+
chevron.className = "rich-details-chevron";
|
|
1152
|
+
chevron.append(
|
|
1153
|
+
createLucideSvg(ChevronDownIconNode, { width: "1em", height: "1em" })
|
|
1154
|
+
);
|
|
1155
|
+
summary.append(chevron);
|
|
1156
|
+
const content = document.createElement("div");
|
|
1157
|
+
content.className = "rich-details-content";
|
|
1158
|
+
details.append(summary, content);
|
|
1159
|
+
return details;
|
|
1160
|
+
}
|
|
1161
|
+
updateDOM(prevNode, dom) {
|
|
1162
|
+
const details = dom;
|
|
1163
|
+
if (prevNode.__open !== this.__open) {
|
|
1164
|
+
details.open = this.__open;
|
|
1165
|
+
}
|
|
1166
|
+
if (prevNode.__summary !== this.__summary) {
|
|
1167
|
+
const label = dom.querySelector(".rich-details-summary-text");
|
|
1168
|
+
if (label) {
|
|
1169
|
+
label.textContent = this.__summary;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
static importJSON(serializedNode) {
|
|
1175
|
+
return $createDetailsNode(serializedNode.summary, serializedNode.open);
|
|
1176
|
+
}
|
|
1177
|
+
exportJSON() {
|
|
1178
|
+
return {
|
|
1179
|
+
...super.exportJSON(),
|
|
1180
|
+
type: "details",
|
|
1181
|
+
summary: this.__summary,
|
|
1182
|
+
open: this.__open,
|
|
1183
|
+
version: 1
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
getSummary() {
|
|
1187
|
+
return this.getLatest().__summary;
|
|
1188
|
+
}
|
|
1189
|
+
setSummary(summary) {
|
|
1190
|
+
const writable = this.getWritable();
|
|
1191
|
+
writable.__summary = summary;
|
|
1192
|
+
}
|
|
1193
|
+
getOpen() {
|
|
1194
|
+
return this.getLatest().__open;
|
|
1195
|
+
}
|
|
1196
|
+
setOpen(open) {
|
|
1197
|
+
const writable = this.getWritable();
|
|
1198
|
+
writable.__open = open;
|
|
1199
|
+
}
|
|
1200
|
+
toggleOpen() {
|
|
1201
|
+
this.setOpen(!this.getOpen());
|
|
1202
|
+
}
|
|
1203
|
+
getDOMSlot(element) {
|
|
1204
|
+
const content = element.querySelector(
|
|
1205
|
+
".rich-details-content"
|
|
1206
|
+
);
|
|
1207
|
+
return super.getDOMSlot(element).withElement(content);
|
|
1208
|
+
}
|
|
1209
|
+
isInline() {
|
|
1210
|
+
return false;
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
__publicField(_DetailsNode, "slashMenuItems", [
|
|
1214
|
+
{
|
|
1215
|
+
title: "Details",
|
|
1216
|
+
icon: createElement(ChevronRight, { size: 20 }),
|
|
1217
|
+
description: "Collapsible content block",
|
|
1218
|
+
keywords: ["details", "toggle", "collapse", "accordion"],
|
|
1219
|
+
section: "ADVANCED",
|
|
1220
|
+
onSelect: (editor) => {
|
|
1221
|
+
editor.update(() => {
|
|
1222
|
+
$insertNodes([$createDetailsNode("Details")]);
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
]);
|
|
1227
|
+
let DetailsNode = _DetailsNode;
|
|
1228
|
+
function $createDetailsNode(summary, open = false) {
|
|
1229
|
+
return new DetailsNode(summary, open);
|
|
1230
|
+
}
|
|
1231
|
+
function $isDetailsNode(node) {
|
|
1232
|
+
return node instanceof DetailsNode;
|
|
1233
|
+
}
|
|
1234
|
+
function FootnoteSectionRenderer({
|
|
1235
|
+
definitions
|
|
1236
|
+
}) {
|
|
1237
|
+
const { displayNumberMap } = useFootnoteDefinitions();
|
|
1238
|
+
const sortedEntries = Object.entries(definitions).sort(
|
|
1239
|
+
([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0)
|
|
1240
|
+
);
|
|
1241
|
+
if (sortedEntries.length === 0) return null;
|
|
1242
|
+
return /* @__PURE__ */ jsxs("div", { className: "rich-footnote-section-content", role: "doc-endnotes", children: [
|
|
1243
|
+
/* @__PURE__ */ jsx("hr", { className: "rich-footnote-section-divider" }),
|
|
1244
|
+
/* @__PURE__ */ jsx("ol", { className: "rich-footnote-section-list", children: sortedEntries.map(([identifier, content]) => {
|
|
1245
|
+
const displayNum = displayNumberMap[identifier] ?? identifier;
|
|
1246
|
+
return /* @__PURE__ */ jsx(
|
|
1247
|
+
FootnoteSectionItem,
|
|
1248
|
+
{
|
|
1249
|
+
identifier,
|
|
1250
|
+
content,
|
|
1251
|
+
displayNum
|
|
1252
|
+
},
|
|
1253
|
+
identifier
|
|
1254
|
+
);
|
|
1255
|
+
}) })
|
|
1256
|
+
] });
|
|
1257
|
+
}
|
|
1258
|
+
function FootnoteSectionItem({
|
|
1259
|
+
identifier,
|
|
1260
|
+
content,
|
|
1261
|
+
displayNum
|
|
1262
|
+
}) {
|
|
1263
|
+
const targetId = `footnote-${identifier}`;
|
|
1264
|
+
const refId = `footnote-ref-${identifier}`;
|
|
1265
|
+
const handleBackClick = useCallback(
|
|
1266
|
+
(e) => {
|
|
1267
|
+
e.preventDefault();
|
|
1268
|
+
const refElement = document.getElementById(refId);
|
|
1269
|
+
if (!refElement) return;
|
|
1270
|
+
refElement.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1271
|
+
refElement.classList.add("rich-footnote-highlight");
|
|
1272
|
+
window.setTimeout(() => {
|
|
1273
|
+
refElement.classList.remove("rich-footnote-highlight");
|
|
1274
|
+
}, 1200);
|
|
1275
|
+
},
|
|
1276
|
+
[refId]
|
|
1277
|
+
);
|
|
1278
|
+
return /* @__PURE__ */ jsxs(
|
|
1279
|
+
"li",
|
|
1280
|
+
{
|
|
1281
|
+
id: targetId,
|
|
1282
|
+
className: "rich-footnote-section-item",
|
|
1283
|
+
value: typeof displayNum === "number" ? displayNum : void 0,
|
|
1284
|
+
children: [
|
|
1285
|
+
/* @__PURE__ */ jsx("span", { className: "rich-footnote-section-item-content", children: content }),
|
|
1286
|
+
/* @__PURE__ */ jsx(
|
|
1287
|
+
"a",
|
|
1288
|
+
{
|
|
1289
|
+
href: `#${refId}`,
|
|
1290
|
+
onClick: handleBackClick,
|
|
1291
|
+
className: "rich-footnote-back-ref",
|
|
1292
|
+
role: "doc-backlink",
|
|
1293
|
+
"aria-label": `Back to reference ${displayNum}`,
|
|
1294
|
+
children: "↩"
|
|
1295
|
+
}
|
|
1296
|
+
)
|
|
1297
|
+
]
|
|
1298
|
+
}
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
class FootnoteSectionNode extends DecoratorNode {
|
|
1302
|
+
constructor(definitions, key) {
|
|
1303
|
+
super(key);
|
|
1304
|
+
__publicField(this, "__definitions");
|
|
1305
|
+
this.__definitions = definitions;
|
|
1306
|
+
}
|
|
1307
|
+
static getType() {
|
|
1308
|
+
return "footnote-section";
|
|
1309
|
+
}
|
|
1310
|
+
static clone(node) {
|
|
1311
|
+
return new FootnoteSectionNode({ ...node.__definitions }, node.__key);
|
|
1312
|
+
}
|
|
1313
|
+
createDOM(_config) {
|
|
1314
|
+
const div = document.createElement("div");
|
|
1315
|
+
div.className = "rich-footnote-section";
|
|
1316
|
+
return div;
|
|
1317
|
+
}
|
|
1318
|
+
updateDOM() {
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
isInline() {
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
static importJSON(serializedNode) {
|
|
1325
|
+
return $createFootnoteSectionNode(serializedNode.definitions);
|
|
1326
|
+
}
|
|
1327
|
+
exportJSON() {
|
|
1328
|
+
return {
|
|
1329
|
+
...super.exportJSON(),
|
|
1330
|
+
type: "footnote-section",
|
|
1331
|
+
definitions: this.__definitions,
|
|
1332
|
+
version: 1
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
getDefinitions() {
|
|
1336
|
+
return this.getLatest().__definitions;
|
|
1337
|
+
}
|
|
1338
|
+
setDefinitions(definitions) {
|
|
1339
|
+
const writable = this.getWritable();
|
|
1340
|
+
writable.__definitions = definitions;
|
|
1341
|
+
}
|
|
1342
|
+
getDefinition(identifier) {
|
|
1343
|
+
return this.getLatest().__definitions[identifier];
|
|
1344
|
+
}
|
|
1345
|
+
setDefinition(identifier, content) {
|
|
1346
|
+
const writable = this.getWritable();
|
|
1347
|
+
writable.__definitions = {
|
|
1348
|
+
...writable.__definitions,
|
|
1349
|
+
[identifier]: content
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
removeDefinition(identifier) {
|
|
1353
|
+
const writable = this.getWritable();
|
|
1354
|
+
const { [identifier]: _, ...rest } = writable.__definitions;
|
|
1355
|
+
writable.__definitions = rest;
|
|
1356
|
+
}
|
|
1357
|
+
decorate(_editor, _config) {
|
|
1358
|
+
return createRendererDecoration(
|
|
1359
|
+
"FootnoteSection",
|
|
1360
|
+
FootnoteSectionRenderer,
|
|
1361
|
+
{
|
|
1362
|
+
definitions: this.__definitions,
|
|
1363
|
+
nodeKey: this.__key
|
|
1364
|
+
}
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function $createFootnoteSectionNode(definitions = {}) {
|
|
1369
|
+
return new FootnoteSectionNode(definitions);
|
|
1370
|
+
}
|
|
1371
|
+
function $isFootnoteSectionNode(node) {
|
|
1372
|
+
return node instanceof FootnoteSectionNode;
|
|
1373
|
+
}
|
|
1374
|
+
function GridReadOnlyDecorator({
|
|
1375
|
+
cols,
|
|
1376
|
+
gap,
|
|
1377
|
+
cellEditors
|
|
1378
|
+
}) {
|
|
1379
|
+
return /* @__PURE__ */ jsx(
|
|
1380
|
+
"div",
|
|
1381
|
+
{
|
|
1382
|
+
className: "rich-grid-inner",
|
|
1383
|
+
style: {
|
|
1384
|
+
display: "grid",
|
|
1385
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
1386
|
+
gap
|
|
1387
|
+
},
|
|
1388
|
+
children: cellEditors.map((editor, i) => /* @__PURE__ */ jsx("div", { className: "rich-grid-cell", children: /* @__PURE__ */ jsx(LexicalNestedComposer, { initialEditor: editor, children: /* @__PURE__ */ jsx(
|
|
1389
|
+
RichTextPlugin,
|
|
1390
|
+
{
|
|
1391
|
+
contentEditable: /* @__PURE__ */ jsx(
|
|
1392
|
+
ContentEditable,
|
|
1393
|
+
{
|
|
1394
|
+
className: "rich-grid-cell-editable",
|
|
1395
|
+
style: { outline: "none" },
|
|
1396
|
+
"aria-placeholder": "",
|
|
1397
|
+
placeholder: /* @__PURE__ */ jsx("span", { style: { display: "none" } })
|
|
1398
|
+
}
|
|
1399
|
+
),
|
|
1400
|
+
ErrorBoundary: LexicalErrorBoundary
|
|
1401
|
+
}
|
|
1402
|
+
) }) }, i))
|
|
1403
|
+
}
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
function createCellEditor() {
|
|
1407
|
+
return createEditor({
|
|
1408
|
+
namespace: "GridCell",
|
|
1409
|
+
nodes: NESTED_EDITOR_NODES,
|
|
1410
|
+
theme: editorTheme,
|
|
1411
|
+
onError: (error) => {
|
|
1412
|
+
console.error("[GridCell]", error);
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
const _GridContainerNode = class _GridContainerNode extends DecoratorNode {
|
|
1417
|
+
constructor(cols = 2, gap, cellEditors, key) {
|
|
1418
|
+
super(key);
|
|
1419
|
+
__publicField(this, "__cols");
|
|
1420
|
+
__publicField(this, "__gap");
|
|
1421
|
+
__publicField(this, "__cellEditors");
|
|
1422
|
+
this.__cols = cols;
|
|
1423
|
+
this.__gap = gap || "16px";
|
|
1424
|
+
if (cellEditors) {
|
|
1425
|
+
this.__cellEditors = cellEditors;
|
|
1426
|
+
} else {
|
|
1427
|
+
this.__cellEditors = Array.from(
|
|
1428
|
+
{ length: cols },
|
|
1429
|
+
() => createCellEditor()
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
static getType() {
|
|
1434
|
+
return "grid-container";
|
|
1435
|
+
}
|
|
1436
|
+
static clone(node) {
|
|
1437
|
+
return new _GridContainerNode(
|
|
1438
|
+
node.__cols,
|
|
1439
|
+
node.__gap,
|
|
1440
|
+
[...node.__cellEditors],
|
|
1441
|
+
node.__key
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
createDOM(_config) {
|
|
1445
|
+
const div = document.createElement("div");
|
|
1446
|
+
div.className = "rich-grid-container";
|
|
1447
|
+
return div;
|
|
1448
|
+
}
|
|
1449
|
+
updateDOM() {
|
|
1450
|
+
return false;
|
|
1451
|
+
}
|
|
1452
|
+
isInline() {
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1455
|
+
getCols() {
|
|
1456
|
+
return this.getLatest().__cols;
|
|
1457
|
+
}
|
|
1458
|
+
setCols(cols) {
|
|
1459
|
+
const writable = this.getWritable();
|
|
1460
|
+
const prev = writable.__cellEditors.length;
|
|
1461
|
+
writable.__cols = cols;
|
|
1462
|
+
if (cols > prev) {
|
|
1463
|
+
for (let i = prev; i < cols; i++) {
|
|
1464
|
+
writable.__cellEditors.push(createCellEditor());
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
getGap() {
|
|
1469
|
+
return this.getLatest().__gap;
|
|
1470
|
+
}
|
|
1471
|
+
setGap(gap) {
|
|
1472
|
+
const writable = this.getWritable();
|
|
1473
|
+
writable.__gap = gap;
|
|
1474
|
+
}
|
|
1475
|
+
getCellEditors() {
|
|
1476
|
+
return this.getLatest().__cellEditors;
|
|
1477
|
+
}
|
|
1478
|
+
addCells(count) {
|
|
1479
|
+
const writable = this.getWritable();
|
|
1480
|
+
for (let i = 0; i < count; i++) {
|
|
1481
|
+
writable.__cellEditors.push(createCellEditor());
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
removeCells(count) {
|
|
1485
|
+
const writable = this.getWritable();
|
|
1486
|
+
const editors = writable.__cellEditors;
|
|
1487
|
+
const toRemove = Math.min(count, editors.length);
|
|
1488
|
+
for (let i = 0; i < toRemove; i++) {
|
|
1489
|
+
const editor = editors.at(-1);
|
|
1490
|
+
if (!editor) break;
|
|
1491
|
+
const isEmpty = editor.getEditorState().read(() => {
|
|
1492
|
+
return $getRoot().getTextContentSize() === 0;
|
|
1493
|
+
});
|
|
1494
|
+
if (!isEmpty) break;
|
|
1495
|
+
editors.pop();
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
getTextContent() {
|
|
1499
|
+
return this.__cellEditors.map(
|
|
1500
|
+
(editor) => editor.getEditorState().read(() => $getRoot().getTextContent())
|
|
1501
|
+
).join("\n");
|
|
1502
|
+
}
|
|
1503
|
+
static importJSON(serializedNode) {
|
|
1504
|
+
const legacy = serializedNode;
|
|
1505
|
+
const cols = legacy.cols || 2;
|
|
1506
|
+
const rawGap = legacy.gap;
|
|
1507
|
+
const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
|
|
1508
|
+
const node = new _GridContainerNode(cols, gap);
|
|
1509
|
+
if (legacy.cells && legacy.cells.length > 0) {
|
|
1510
|
+
const editors = legacy.cells.map((cellState) => {
|
|
1511
|
+
const editor = createCellEditor();
|
|
1512
|
+
const editorState = editor.parseEditorState(cellState);
|
|
1513
|
+
editor.setEditorState(editorState);
|
|
1514
|
+
return editor;
|
|
1515
|
+
});
|
|
1516
|
+
node.__cellEditors = editors;
|
|
1517
|
+
} else if (legacy.children) {
|
|
1518
|
+
const { children } = legacy;
|
|
1519
|
+
const editors = children.map((child) => {
|
|
1520
|
+
const editor = createCellEditor();
|
|
1521
|
+
const content = {
|
|
1522
|
+
root: {
|
|
1523
|
+
children: [child],
|
|
1524
|
+
direction: null,
|
|
1525
|
+
format: "",
|
|
1526
|
+
indent: 0,
|
|
1527
|
+
type: "root",
|
|
1528
|
+
version: 1
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
const editorState = editor.parseEditorState(content);
|
|
1532
|
+
editor.setEditorState(editorState);
|
|
1533
|
+
return editor;
|
|
1534
|
+
});
|
|
1535
|
+
node.__cellEditors = editors;
|
|
1536
|
+
}
|
|
1537
|
+
return node;
|
|
1538
|
+
}
|
|
1539
|
+
exportJSON() {
|
|
1540
|
+
return {
|
|
1541
|
+
...super.exportJSON(),
|
|
1542
|
+
type: "grid-container",
|
|
1543
|
+
cols: this.__cols,
|
|
1544
|
+
gap: this.__gap,
|
|
1545
|
+
cells: this.__cellEditors.map(
|
|
1546
|
+
(editor) => editor.getEditorState().toJSON()
|
|
1547
|
+
),
|
|
1548
|
+
version: 1
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
decorate(_editor, _config) {
|
|
1552
|
+
return createElement(GridReadOnlyDecorator, {
|
|
1553
|
+
cols: this.__cols,
|
|
1554
|
+
gap: this.__gap,
|
|
1555
|
+
cellEditors: this.__cellEditors
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
__publicField(_GridContainerNode, "slashMenuItems", [
|
|
1560
|
+
{
|
|
1561
|
+
title: "Grid",
|
|
1562
|
+
icon: createElement(LayoutGrid, { size: 20 }),
|
|
1563
|
+
description: "Grid layout container",
|
|
1564
|
+
keywords: ["grid", "columns", "layout"],
|
|
1565
|
+
section: "LAYOUT",
|
|
1566
|
+
onSelect: (editor) => {
|
|
1567
|
+
editor.update(() => {
|
|
1568
|
+
$insertNodes([$createGridContainerNode(2)]);
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
]);
|
|
1573
|
+
let GridContainerNode = _GridContainerNode;
|
|
1574
|
+
function $createGridContainerNode(cols = 2, gap) {
|
|
1575
|
+
return new GridContainerNode(cols, gap);
|
|
1576
|
+
}
|
|
1577
|
+
function $isGridContainerNode(node) {
|
|
1578
|
+
return node instanceof GridContainerNode;
|
|
1579
|
+
}
|
|
1580
|
+
function rgbaToThumbHash(w, h, rgba) {
|
|
1581
|
+
if (w > 100 || h > 100) throw new Error(`${w}x${h} doesn't fit in 100x100`);
|
|
1582
|
+
let { PI, round, max, cos, abs } = Math;
|
|
1583
|
+
let avg_r = 0, avg_g = 0, avg_b = 0, avg_a = 0;
|
|
1584
|
+
for (let i = 0, j = 0; i < w * h; i++, j += 4) {
|
|
1585
|
+
let alpha = rgba[j + 3] / 255;
|
|
1586
|
+
avg_r += alpha / 255 * rgba[j];
|
|
1587
|
+
avg_g += alpha / 255 * rgba[j + 1];
|
|
1588
|
+
avg_b += alpha / 255 * rgba[j + 2];
|
|
1589
|
+
avg_a += alpha;
|
|
1590
|
+
}
|
|
1591
|
+
if (avg_a) {
|
|
1592
|
+
avg_r /= avg_a;
|
|
1593
|
+
avg_g /= avg_a;
|
|
1594
|
+
avg_b /= avg_a;
|
|
1595
|
+
}
|
|
1596
|
+
let hasAlpha = avg_a < w * h;
|
|
1597
|
+
let l_limit = hasAlpha ? 5 : 7;
|
|
1598
|
+
let lx = max(1, round(l_limit * w / max(w, h)));
|
|
1599
|
+
let ly = max(1, round(l_limit * h / max(w, h)));
|
|
1600
|
+
let l = [];
|
|
1601
|
+
let p = [];
|
|
1602
|
+
let q = [];
|
|
1603
|
+
let a = [];
|
|
1604
|
+
for (let i = 0, j = 0; i < w * h; i++, j += 4) {
|
|
1605
|
+
let alpha = rgba[j + 3] / 255;
|
|
1606
|
+
let r = avg_r * (1 - alpha) + alpha / 255 * rgba[j];
|
|
1607
|
+
let g = avg_g * (1 - alpha) + alpha / 255 * rgba[j + 1];
|
|
1608
|
+
let b = avg_b * (1 - alpha) + alpha / 255 * rgba[j + 2];
|
|
1609
|
+
l[i] = (r + g + b) / 3;
|
|
1610
|
+
p[i] = (r + g) / 2 - b;
|
|
1611
|
+
q[i] = r - g;
|
|
1612
|
+
a[i] = alpha;
|
|
1613
|
+
}
|
|
1614
|
+
let encodeChannel = (channel, nx, ny) => {
|
|
1615
|
+
let dc = 0, ac = [], scale = 0, fx = [];
|
|
1616
|
+
for (let cy = 0; cy < ny; cy++) {
|
|
1617
|
+
for (let cx = 0; cx * ny < nx * (ny - cy); cx++) {
|
|
1618
|
+
let f = 0;
|
|
1619
|
+
for (let x = 0; x < w; x++)
|
|
1620
|
+
fx[x] = cos(PI / w * cx * (x + 0.5));
|
|
1621
|
+
for (let y = 0; y < h; y++)
|
|
1622
|
+
for (let x = 0, fy = cos(PI / h * cy * (y + 0.5)); x < w; x++)
|
|
1623
|
+
f += channel[x + y * w] * fx[x] * fy;
|
|
1624
|
+
f /= w * h;
|
|
1625
|
+
if (cx || cy) {
|
|
1626
|
+
ac.push(f);
|
|
1627
|
+
scale = max(scale, abs(f));
|
|
1628
|
+
} else {
|
|
1629
|
+
dc = f;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
if (scale)
|
|
1634
|
+
for (let i = 0; i < ac.length; i++)
|
|
1635
|
+
ac[i] = 0.5 + 0.5 / scale * ac[i];
|
|
1636
|
+
return [dc, ac, scale];
|
|
1637
|
+
};
|
|
1638
|
+
let [l_dc, l_ac, l_scale] = encodeChannel(l, max(3, lx), max(3, ly));
|
|
1639
|
+
let [p_dc, p_ac, p_scale] = encodeChannel(p, 3, 3);
|
|
1640
|
+
let [q_dc, q_ac, q_scale] = encodeChannel(q, 3, 3);
|
|
1641
|
+
let [a_dc, a_ac, a_scale] = hasAlpha ? encodeChannel(a, 5, 5) : [];
|
|
1642
|
+
let isLandscape = w > h;
|
|
1643
|
+
let header24 = round(63 * l_dc) | round(31.5 + 31.5 * p_dc) << 6 | round(31.5 + 31.5 * q_dc) << 12 | round(31 * l_scale) << 18 | hasAlpha << 23;
|
|
1644
|
+
let header16 = (isLandscape ? ly : lx) | round(63 * p_scale) << 3 | round(63 * q_scale) << 9 | isLandscape << 15;
|
|
1645
|
+
let hash = [header24 & 255, header24 >> 8 & 255, header24 >> 16, header16 & 255, header16 >> 8];
|
|
1646
|
+
let ac_start = hasAlpha ? 6 : 5;
|
|
1647
|
+
let ac_index = 0;
|
|
1648
|
+
if (hasAlpha) hash.push(round(15 * a_dc) | round(15 * a_scale) << 4);
|
|
1649
|
+
for (let ac of hasAlpha ? [l_ac, p_ac, q_ac, a_ac] : [l_ac, p_ac, q_ac])
|
|
1650
|
+
for (let f of ac)
|
|
1651
|
+
hash[ac_start + (ac_index >> 1)] |= round(15 * f) << ((ac_index++ & 1) << 2);
|
|
1652
|
+
return new Uint8Array(hash);
|
|
1653
|
+
}
|
|
1654
|
+
function thumbHashToRGBA(hash) {
|
|
1655
|
+
let { PI, min, max, cos, round } = Math;
|
|
1656
|
+
let header24 = hash[0] | hash[1] << 8 | hash[2] << 16;
|
|
1657
|
+
let header16 = hash[3] | hash[4] << 8;
|
|
1658
|
+
let l_dc = (header24 & 63) / 63;
|
|
1659
|
+
let p_dc = (header24 >> 6 & 63) / 31.5 - 1;
|
|
1660
|
+
let q_dc = (header24 >> 12 & 63) / 31.5 - 1;
|
|
1661
|
+
let l_scale = (header24 >> 18 & 31) / 31;
|
|
1662
|
+
let hasAlpha = header24 >> 23;
|
|
1663
|
+
let p_scale = (header16 >> 3 & 63) / 63;
|
|
1664
|
+
let q_scale = (header16 >> 9 & 63) / 63;
|
|
1665
|
+
let isLandscape = header16 >> 15;
|
|
1666
|
+
let lx = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7);
|
|
1667
|
+
let ly = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7);
|
|
1668
|
+
let a_dc = hasAlpha ? (hash[5] & 15) / 15 : 1;
|
|
1669
|
+
let a_scale = (hash[5] >> 4) / 15;
|
|
1670
|
+
let ac_start = hasAlpha ? 6 : 5;
|
|
1671
|
+
let ac_index = 0;
|
|
1672
|
+
let decodeChannel = (nx, ny, scale) => {
|
|
1673
|
+
let ac = [];
|
|
1674
|
+
for (let cy = 0; cy < ny; cy++)
|
|
1675
|
+
for (let cx = cy ? 0 : 1; cx * ny < nx * (ny - cy); cx++)
|
|
1676
|
+
ac.push(((hash[ac_start + (ac_index >> 1)] >> ((ac_index++ & 1) << 2) & 15) / 7.5 - 1) * scale);
|
|
1677
|
+
return ac;
|
|
1678
|
+
};
|
|
1679
|
+
let l_ac = decodeChannel(lx, ly, l_scale);
|
|
1680
|
+
let p_ac = decodeChannel(3, 3, p_scale * 1.25);
|
|
1681
|
+
let q_ac = decodeChannel(3, 3, q_scale * 1.25);
|
|
1682
|
+
let a_ac = hasAlpha && decodeChannel(5, 5, a_scale);
|
|
1683
|
+
let ratio = thumbHashToApproximateAspectRatio(hash);
|
|
1684
|
+
let w = round(ratio > 1 ? 32 : 32 * ratio);
|
|
1685
|
+
let h = round(ratio > 1 ? 32 / ratio : 32);
|
|
1686
|
+
let rgba = new Uint8Array(w * h * 4), fx = [], fy = [];
|
|
1687
|
+
for (let y = 0, i = 0; y < h; y++) {
|
|
1688
|
+
for (let x = 0; x < w; x++, i += 4) {
|
|
1689
|
+
let l = l_dc, p = p_dc, q = q_dc, a = a_dc;
|
|
1690
|
+
for (let cx = 0, n = max(lx, hasAlpha ? 5 : 3); cx < n; cx++)
|
|
1691
|
+
fx[cx] = cos(PI / w * (x + 0.5) * cx);
|
|
1692
|
+
for (let cy = 0, n = max(ly, hasAlpha ? 5 : 3); cy < n; cy++)
|
|
1693
|
+
fy[cy] = cos(PI / h * (y + 0.5) * cy);
|
|
1694
|
+
for (let cy = 0, j = 0; cy < ly; cy++)
|
|
1695
|
+
for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx * ly < lx * (ly - cy); cx++, j++)
|
|
1696
|
+
l += l_ac[j] * fx[cx] * fy2;
|
|
1697
|
+
for (let cy = 0, j = 0; cy < 3; cy++) {
|
|
1698
|
+
for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 3 - cy; cx++, j++) {
|
|
1699
|
+
let f = fx[cx] * fy2;
|
|
1700
|
+
p += p_ac[j] * f;
|
|
1701
|
+
q += q_ac[j] * f;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (hasAlpha)
|
|
1705
|
+
for (let cy = 0, j = 0; cy < 5; cy++)
|
|
1706
|
+
for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 5 - cy; cx++, j++)
|
|
1707
|
+
a += a_ac[j] * fx[cx] * fy2;
|
|
1708
|
+
let b = l - 2 / 3 * p;
|
|
1709
|
+
let r = (3 * l - b + q) / 2;
|
|
1710
|
+
let g = r - q;
|
|
1711
|
+
rgba[i] = max(0, 255 * min(1, r));
|
|
1712
|
+
rgba[i + 1] = max(0, 255 * min(1, g));
|
|
1713
|
+
rgba[i + 2] = max(0, 255 * min(1, b));
|
|
1714
|
+
rgba[i + 3] = max(0, 255 * min(1, a));
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
return { w, h, rgba };
|
|
1718
|
+
}
|
|
1719
|
+
function thumbHashToApproximateAspectRatio(hash) {
|
|
1720
|
+
let header = hash[3];
|
|
1721
|
+
let hasAlpha = hash[2] & 128;
|
|
1722
|
+
let isLandscape = hash[4] & 128;
|
|
1723
|
+
let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7;
|
|
1724
|
+
let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7;
|
|
1725
|
+
return lx / ly;
|
|
1726
|
+
}
|
|
1727
|
+
function rgbaToDataURL(w, h, rgba) {
|
|
1728
|
+
let row = w * 4 + 1;
|
|
1729
|
+
let idat = 6 + h * (5 + row);
|
|
1730
|
+
let bytes = [
|
|
1731
|
+
137,
|
|
1732
|
+
80,
|
|
1733
|
+
78,
|
|
1734
|
+
71,
|
|
1735
|
+
13,
|
|
1736
|
+
10,
|
|
1737
|
+
26,
|
|
1738
|
+
10,
|
|
1739
|
+
0,
|
|
1740
|
+
0,
|
|
1741
|
+
0,
|
|
1742
|
+
13,
|
|
1743
|
+
73,
|
|
1744
|
+
72,
|
|
1745
|
+
68,
|
|
1746
|
+
82,
|
|
1747
|
+
0,
|
|
1748
|
+
0,
|
|
1749
|
+
w >> 8,
|
|
1750
|
+
w & 255,
|
|
1751
|
+
0,
|
|
1752
|
+
0,
|
|
1753
|
+
h >> 8,
|
|
1754
|
+
h & 255,
|
|
1755
|
+
8,
|
|
1756
|
+
6,
|
|
1757
|
+
0,
|
|
1758
|
+
0,
|
|
1759
|
+
0,
|
|
1760
|
+
0,
|
|
1761
|
+
0,
|
|
1762
|
+
0,
|
|
1763
|
+
0,
|
|
1764
|
+
idat >>> 24,
|
|
1765
|
+
idat >> 16 & 255,
|
|
1766
|
+
idat >> 8 & 255,
|
|
1767
|
+
idat & 255,
|
|
1768
|
+
73,
|
|
1769
|
+
68,
|
|
1770
|
+
65,
|
|
1771
|
+
84,
|
|
1772
|
+
120,
|
|
1773
|
+
1
|
|
1774
|
+
];
|
|
1775
|
+
let table = [
|
|
1776
|
+
0,
|
|
1777
|
+
498536548,
|
|
1778
|
+
997073096,
|
|
1779
|
+
651767980,
|
|
1780
|
+
1994146192,
|
|
1781
|
+
1802195444,
|
|
1782
|
+
1303535960,
|
|
1783
|
+
1342533948,
|
|
1784
|
+
-306674912,
|
|
1785
|
+
-267414716,
|
|
1786
|
+
-690576408,
|
|
1787
|
+
-882789492,
|
|
1788
|
+
-1687895376,
|
|
1789
|
+
-2032938284,
|
|
1790
|
+
-1609899400,
|
|
1791
|
+
-1111625188
|
|
1792
|
+
];
|
|
1793
|
+
let a = 1, b = 0;
|
|
1794
|
+
for (let y = 0, i = 0, end = row - 1; y < h; y++, end += row - 1) {
|
|
1795
|
+
bytes.push(y + 1 < h ? 0 : 1, row & 255, row >> 8, ~row & 255, row >> 8 ^ 255, 0);
|
|
1796
|
+
for (b = (b + a) % 65521; i < end; i++) {
|
|
1797
|
+
let u = rgba[i] & 255;
|
|
1798
|
+
bytes.push(u);
|
|
1799
|
+
a = (a + u) % 65521;
|
|
1800
|
+
b = (b + a) % 65521;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
bytes.push(
|
|
1804
|
+
b >> 8,
|
|
1805
|
+
b & 255,
|
|
1806
|
+
a >> 8,
|
|
1807
|
+
a & 255,
|
|
1808
|
+
0,
|
|
1809
|
+
0,
|
|
1810
|
+
0,
|
|
1811
|
+
0,
|
|
1812
|
+
0,
|
|
1813
|
+
0,
|
|
1814
|
+
0,
|
|
1815
|
+
0,
|
|
1816
|
+
73,
|
|
1817
|
+
69,
|
|
1818
|
+
78,
|
|
1819
|
+
68,
|
|
1820
|
+
174,
|
|
1821
|
+
66,
|
|
1822
|
+
96,
|
|
1823
|
+
130
|
|
1824
|
+
);
|
|
1825
|
+
for (let [start, end] of [[12, 29], [37, 41 + idat]]) {
|
|
1826
|
+
let c = -1;
|
|
1827
|
+
for (let i = start; i < end; i++) {
|
|
1828
|
+
c ^= bytes[i];
|
|
1829
|
+
c = c >>> 4 ^ table[c & 15];
|
|
1830
|
+
c = c >>> 4 ^ table[c & 15];
|
|
1831
|
+
}
|
|
1832
|
+
c = ~c;
|
|
1833
|
+
bytes[end++] = c >>> 24;
|
|
1834
|
+
bytes[end++] = c >> 16 & 255;
|
|
1835
|
+
bytes[end++] = c >> 8 & 255;
|
|
1836
|
+
bytes[end++] = c & 255;
|
|
1837
|
+
}
|
|
1838
|
+
return "data:image/png;base64," + btoa(String.fromCharCode(...bytes));
|
|
1839
|
+
}
|
|
1840
|
+
function thumbHashToDataURL(hash) {
|
|
1841
|
+
let image = thumbHashToRGBA(hash);
|
|
1842
|
+
return rgbaToDataURL(image.w, image.h, image.rgba);
|
|
1843
|
+
}
|
|
1844
|
+
const MAX_DIM = 100;
|
|
1845
|
+
async function computeImageMeta(file) {
|
|
1846
|
+
const url = URL.createObjectURL(file);
|
|
1847
|
+
try {
|
|
1848
|
+
const img = await loadImage(url);
|
|
1849
|
+
const { naturalWidth: w, naturalHeight: h } = img;
|
|
1850
|
+
const scale = Math.min(MAX_DIM / w, MAX_DIM / h, 1);
|
|
1851
|
+
const sw = Math.round(w * scale);
|
|
1852
|
+
const sh = Math.round(h * scale);
|
|
1853
|
+
const canvas = document.createElement("canvas");
|
|
1854
|
+
canvas.width = sw;
|
|
1855
|
+
canvas.height = sh;
|
|
1856
|
+
const ctx = canvas.getContext("2d");
|
|
1857
|
+
ctx.drawImage(img, 0, 0, sw, sh);
|
|
1858
|
+
const { data } = ctx.getImageData(0, 0, sw, sh);
|
|
1859
|
+
const hash = rgbaToThumbHash(sw, sh, data);
|
|
1860
|
+
const thumbhash = uint8ToBase64(hash);
|
|
1861
|
+
return { width: w, height: h, thumbhash };
|
|
1862
|
+
} finally {
|
|
1863
|
+
URL.revokeObjectURL(url);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function decodeThumbHash(hash) {
|
|
1867
|
+
try {
|
|
1868
|
+
const bytes = base64ToUint8(hash);
|
|
1869
|
+
return thumbHashToDataURL(bytes);
|
|
1870
|
+
} catch {
|
|
1871
|
+
return void 0;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
function loadImage(src) {
|
|
1875
|
+
return new Promise((resolve, reject) => {
|
|
1876
|
+
const img = new Image();
|
|
1877
|
+
img.onload = () => resolve(img);
|
|
1878
|
+
img.onerror = reject;
|
|
1879
|
+
img.src = src;
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
function uint8ToBase64(bytes) {
|
|
1883
|
+
let bin = "";
|
|
1884
|
+
for (const b of bytes) bin += String.fromCodePoint(b);
|
|
1885
|
+
return btoa(bin);
|
|
1886
|
+
}
|
|
1887
|
+
function base64ToUint8(str) {
|
|
1888
|
+
const bin = atob(str);
|
|
1889
|
+
const bytes = new Uint8Array(bin.length);
|
|
1890
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.codePointAt(i);
|
|
1891
|
+
return bytes;
|
|
1892
|
+
}
|
|
1893
|
+
function ImageRenderer({
|
|
1894
|
+
src,
|
|
1895
|
+
altText,
|
|
1896
|
+
width,
|
|
1897
|
+
height,
|
|
1898
|
+
caption,
|
|
1899
|
+
thumbhash,
|
|
1900
|
+
accent
|
|
1901
|
+
}) {
|
|
1902
|
+
const [loaded, setLoaded] = useState(false);
|
|
1903
|
+
const [zoomed, setZoomed] = useState(false);
|
|
1904
|
+
const handleLoad = useCallback(() => setLoaded(true), []);
|
|
1905
|
+
const handleZoomOpen = useCallback(() => {
|
|
1906
|
+
if (!loaded) return;
|
|
1907
|
+
setZoomed(true);
|
|
1908
|
+
}, [loaded]);
|
|
1909
|
+
const handleZoomClose = useCallback(() => setZoomed(false), []);
|
|
1910
|
+
useEffect(() => {
|
|
1911
|
+
if (!zoomed) return;
|
|
1912
|
+
const onKeyDown = (e) => {
|
|
1913
|
+
if (e.key === "Escape") setZoomed(false);
|
|
1914
|
+
};
|
|
1915
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1916
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
1917
|
+
}, [zoomed]);
|
|
1918
|
+
useEffect(() => {
|
|
1919
|
+
if (!zoomed) return;
|
|
1920
|
+
const prev = document.body.style.overflow;
|
|
1921
|
+
document.body.style.overflow = "hidden";
|
|
1922
|
+
return () => {
|
|
1923
|
+
document.body.style.overflow = prev;
|
|
1924
|
+
};
|
|
1925
|
+
}, [zoomed]);
|
|
1926
|
+
const handleContainerKeyDown = useCallback(
|
|
1927
|
+
(e) => {
|
|
1928
|
+
if ((e.key === "Enter" || e.key === " ") && loaded) {
|
|
1929
|
+
e.preventDefault();
|
|
1930
|
+
setZoomed(true);
|
|
1931
|
+
}
|
|
1932
|
+
},
|
|
1933
|
+
[loaded]
|
|
1934
|
+
);
|
|
1935
|
+
const placeholderUrl = useMemo(
|
|
1936
|
+
() => thumbhash ? decodeThumbHash(thumbhash) : void 0,
|
|
1937
|
+
[thumbhash]
|
|
1938
|
+
);
|
|
1939
|
+
const aspectStyle = width && height ? { aspectRatio: `${width} / ${height}`, maxWidth: "100%", width } : { maxWidth: "100%" };
|
|
1940
|
+
return /* @__PURE__ */ jsxs("figure", { className: "rich-image", children: [
|
|
1941
|
+
/* @__PURE__ */ jsx(
|
|
1942
|
+
"div",
|
|
1943
|
+
{
|
|
1944
|
+
className: `rich-image-container${loaded ? " rich-image-loaded" : ""}`,
|
|
1945
|
+
style: {
|
|
1946
|
+
...aspectStyle,
|
|
1947
|
+
backgroundColor: !loaded && !placeholderUrl ? accent : void 0,
|
|
1948
|
+
backgroundImage: !loaded && placeholderUrl ? `url(${placeholderUrl})` : void 0,
|
|
1949
|
+
backgroundSize: "cover",
|
|
1950
|
+
cursor: loaded ? "zoom-in" : void 0
|
|
1951
|
+
},
|
|
1952
|
+
onClick: handleZoomOpen,
|
|
1953
|
+
onKeyDown: handleContainerKeyDown,
|
|
1954
|
+
role: "button",
|
|
1955
|
+
tabIndex: loaded ? 0 : -1,
|
|
1956
|
+
"aria-label": loaded ? `Zoom image: ${altText}` : void 0,
|
|
1957
|
+
children: /* @__PURE__ */ jsx(
|
|
1958
|
+
"img",
|
|
1959
|
+
{
|
|
1960
|
+
src,
|
|
1961
|
+
alt: altText,
|
|
1962
|
+
width,
|
|
1963
|
+
height,
|
|
1964
|
+
loading: "lazy",
|
|
1965
|
+
onLoad: handleLoad,
|
|
1966
|
+
style: { maxWidth: "100%", height: "auto" },
|
|
1967
|
+
className: loaded ? "rich-image-visible" : "rich-image-hidden"
|
|
1968
|
+
}
|
|
1969
|
+
)
|
|
1970
|
+
}
|
|
1971
|
+
),
|
|
1972
|
+
caption && /* @__PURE__ */ jsx("figcaption", { children: caption }),
|
|
1973
|
+
zoomed && /* @__PURE__ */ jsx(
|
|
1974
|
+
"div",
|
|
1975
|
+
{
|
|
1976
|
+
className: "rich-image-zoom-overlay",
|
|
1977
|
+
onClick: handleZoomClose,
|
|
1978
|
+
role: "dialog",
|
|
1979
|
+
"aria-modal": "true",
|
|
1980
|
+
"aria-label": `Zoomed image: ${altText}`,
|
|
1981
|
+
tabIndex: 0,
|
|
1982
|
+
children: /* @__PURE__ */ jsx("img", { src, alt: altText, className: "rich-image-zoom-img" })
|
|
1983
|
+
}
|
|
1984
|
+
)
|
|
1985
|
+
] });
|
|
1986
|
+
}
|
|
1987
|
+
function sanitizeImageSrc(src) {
|
|
1988
|
+
const trimmed = src.trim();
|
|
1989
|
+
if (/^(javascript\s*:|vbscript\s*:|data\s*:(?!image\/))/i.test(trimmed)) {
|
|
1990
|
+
return "";
|
|
1991
|
+
}
|
|
1992
|
+
return trimmed;
|
|
1993
|
+
}
|
|
1994
|
+
function sanitizeColor(value) {
|
|
1995
|
+
if (!value) return void 0;
|
|
1996
|
+
const trimmed = value.trim();
|
|
1997
|
+
if (/^#[\da-f]{3,8}$/i.test(trimmed)) return trimmed;
|
|
1998
|
+
if (/^(rgb|hsl)a?\([^)]+\)$/i.test(trimmed)) return trimmed;
|
|
1999
|
+
if (/^[a-z]{3,20}$/i.test(trimmed)) return trimmed;
|
|
2000
|
+
return void 0;
|
|
2001
|
+
}
|
|
2002
|
+
const _ImageNode = class _ImageNode extends DecoratorNode {
|
|
2003
|
+
constructor(payload, key) {
|
|
2004
|
+
super(key);
|
|
2005
|
+
__publicField(this, "__src");
|
|
2006
|
+
__publicField(this, "__altText");
|
|
2007
|
+
__publicField(this, "__width");
|
|
2008
|
+
__publicField(this, "__height");
|
|
2009
|
+
__publicField(this, "__caption");
|
|
2010
|
+
__publicField(this, "__thumbhash");
|
|
2011
|
+
__publicField(this, "__accent");
|
|
2012
|
+
this.__src = sanitizeImageSrc(payload.src);
|
|
2013
|
+
this.__altText = payload.altText;
|
|
2014
|
+
this.__width = payload.width;
|
|
2015
|
+
this.__height = payload.height;
|
|
2016
|
+
this.__caption = payload.caption;
|
|
2017
|
+
this.__thumbhash = payload.thumbhash;
|
|
2018
|
+
this.__accent = sanitizeColor(payload.accent);
|
|
2019
|
+
}
|
|
2020
|
+
static getType() {
|
|
2021
|
+
return "image";
|
|
2022
|
+
}
|
|
2023
|
+
static clone(node) {
|
|
2024
|
+
return new _ImageNode(
|
|
2025
|
+
{
|
|
2026
|
+
src: node.__src,
|
|
2027
|
+
altText: node.__altText,
|
|
2028
|
+
width: node.__width,
|
|
2029
|
+
height: node.__height,
|
|
2030
|
+
caption: node.__caption,
|
|
2031
|
+
thumbhash: node.__thumbhash,
|
|
2032
|
+
accent: node.__accent
|
|
2033
|
+
},
|
|
2034
|
+
node.__key
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
createDOM(_config) {
|
|
2038
|
+
const div = document.createElement("div");
|
|
2039
|
+
div.className = "rich-image-wrapper";
|
|
2040
|
+
return div;
|
|
2041
|
+
}
|
|
2042
|
+
updateDOM() {
|
|
2043
|
+
return false;
|
|
2044
|
+
}
|
|
2045
|
+
isInline() {
|
|
2046
|
+
return false;
|
|
2047
|
+
}
|
|
2048
|
+
static importJSON(serializedNode) {
|
|
2049
|
+
return $createImageNode({
|
|
2050
|
+
src: serializedNode.src,
|
|
2051
|
+
altText: serializedNode.altText,
|
|
2052
|
+
width: serializedNode.width,
|
|
2053
|
+
height: serializedNode.height,
|
|
2054
|
+
caption: serializedNode.caption,
|
|
2055
|
+
thumbhash: serializedNode.thumbhash,
|
|
2056
|
+
accent: serializedNode.accent
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
exportJSON() {
|
|
2060
|
+
return {
|
|
2061
|
+
...super.exportJSON(),
|
|
2062
|
+
type: "image",
|
|
2063
|
+
src: this.__src,
|
|
2064
|
+
altText: this.__altText,
|
|
2065
|
+
width: this.__width,
|
|
2066
|
+
height: this.__height,
|
|
2067
|
+
caption: this.__caption,
|
|
2068
|
+
thumbhash: this.__thumbhash,
|
|
2069
|
+
accent: this.__accent,
|
|
2070
|
+
version: 1
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
decorate(_editor, _config) {
|
|
2074
|
+
return createRendererDecoration("Image", ImageRenderer, {
|
|
2075
|
+
src: this.__src,
|
|
2076
|
+
altText: this.__altText,
|
|
2077
|
+
width: this.__width,
|
|
2078
|
+
height: this.__height,
|
|
2079
|
+
caption: this.__caption,
|
|
2080
|
+
thumbhash: this.__thumbhash,
|
|
2081
|
+
accent: this.__accent
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
};
|
|
2085
|
+
__publicField(_ImageNode, "slashMenuItems", [
|
|
2086
|
+
{
|
|
2087
|
+
title: "Image",
|
|
2088
|
+
icon: createElement(ImageIcon, { size: 20 }),
|
|
2089
|
+
description: "Upload or embed an image",
|
|
2090
|
+
keywords: ["image", "picture", "photo"],
|
|
2091
|
+
section: "MEDIA",
|
|
2092
|
+
onSelect: (editor) => {
|
|
2093
|
+
editor.update(() => {
|
|
2094
|
+
$insertNodes([$createImageNode({ src: "", altText: "" })]);
|
|
2095
|
+
});
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
]);
|
|
2099
|
+
let ImageNode = _ImageNode;
|
|
2100
|
+
function $createImageNode(payload) {
|
|
2101
|
+
return new ImageNode(payload);
|
|
2102
|
+
}
|
|
2103
|
+
const _KaTeXBlockNode = class _KaTeXBlockNode extends DecoratorNode {
|
|
2104
|
+
constructor(equation, key) {
|
|
2105
|
+
super(key);
|
|
2106
|
+
__publicField(this, "__equation");
|
|
2107
|
+
this.__equation = equation;
|
|
2108
|
+
}
|
|
2109
|
+
static getType() {
|
|
2110
|
+
return "katex-block";
|
|
2111
|
+
}
|
|
2112
|
+
static clone(node) {
|
|
2113
|
+
return new _KaTeXBlockNode(node.__equation, node.__key);
|
|
2114
|
+
}
|
|
2115
|
+
createDOM(_config) {
|
|
2116
|
+
const div = document.createElement("div");
|
|
2117
|
+
div.className = "rich-katex-block-wrapper";
|
|
2118
|
+
return div;
|
|
2119
|
+
}
|
|
2120
|
+
updateDOM() {
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2123
|
+
isInline() {
|
|
2124
|
+
return false;
|
|
2125
|
+
}
|
|
2126
|
+
static importJSON(serializedNode) {
|
|
2127
|
+
return $createKaTeXBlockNode(serializedNode.equation);
|
|
2128
|
+
}
|
|
2129
|
+
exportJSON() {
|
|
2130
|
+
return {
|
|
2131
|
+
...super.exportJSON(),
|
|
2132
|
+
type: "katex-block",
|
|
2133
|
+
equation: this.__equation,
|
|
2134
|
+
version: 1
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
getEquation() {
|
|
2138
|
+
return this.__equation;
|
|
2139
|
+
}
|
|
2140
|
+
setEquation(equation) {
|
|
2141
|
+
const writable = this.getWritable();
|
|
2142
|
+
writable.__equation = equation;
|
|
2143
|
+
}
|
|
2144
|
+
decorate(_editor, _config) {
|
|
2145
|
+
return createRendererDecoration("KaTeX", KaTeXRenderer, {
|
|
2146
|
+
equation: this.__equation,
|
|
2147
|
+
displayMode: true
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
__publicField(_KaTeXBlockNode, "slashMenuItems", [
|
|
2152
|
+
{
|
|
2153
|
+
title: "Math Equation",
|
|
2154
|
+
icon: createElement(Sigma, { size: 20 }),
|
|
2155
|
+
description: "KaTeX block formula",
|
|
2156
|
+
keywords: ["math", "equation", "latex", "katex"],
|
|
2157
|
+
section: "ADVANCED",
|
|
2158
|
+
onSelect: (editor) => {
|
|
2159
|
+
editor.update(() => {
|
|
2160
|
+
$insertNodes([$createKaTeXBlockNode("")]);
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
]);
|
|
2165
|
+
let KaTeXBlockNode = _KaTeXBlockNode;
|
|
2166
|
+
function $createKaTeXBlockNode(equation) {
|
|
2167
|
+
return new KaTeXBlockNode(equation);
|
|
2168
|
+
}
|
|
2169
|
+
function $isKaTeXBlockNode(node) {
|
|
2170
|
+
return node instanceof KaTeXBlockNode;
|
|
2171
|
+
}
|
|
2172
|
+
function LinkCardRenderer({
|
|
2173
|
+
url,
|
|
2174
|
+
title,
|
|
2175
|
+
description,
|
|
2176
|
+
favicon,
|
|
2177
|
+
image
|
|
2178
|
+
}) {
|
|
2179
|
+
const displayTitle = title || url;
|
|
2180
|
+
return /* @__PURE__ */ jsxs(
|
|
2181
|
+
"a",
|
|
2182
|
+
{
|
|
2183
|
+
className: "rich-link-card",
|
|
2184
|
+
href: url,
|
|
2185
|
+
target: "_blank",
|
|
2186
|
+
rel: "noopener noreferrer",
|
|
2187
|
+
children: [
|
|
2188
|
+
image && /* @__PURE__ */ jsx("span", { className: "rich-link-card-image", children: /* @__PURE__ */ jsx("img", { src: image, alt: "", loading: "lazy" }) }),
|
|
2189
|
+
/* @__PURE__ */ jsxs("span", { className: "rich-link-card-content", children: [
|
|
2190
|
+
/* @__PURE__ */ jsxs("span", { className: "rich-link-card-title", children: [
|
|
2191
|
+
favicon && /* @__PURE__ */ jsx(
|
|
2192
|
+
"img",
|
|
2193
|
+
{
|
|
2194
|
+
className: "rich-link-card-favicon",
|
|
2195
|
+
src: favicon,
|
|
2196
|
+
alt: "",
|
|
2197
|
+
width: 16,
|
|
2198
|
+
height: 16,
|
|
2199
|
+
onError: (e) => {
|
|
2200
|
+
e.target.style.display = "none";
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
),
|
|
2204
|
+
displayTitle
|
|
2205
|
+
] }),
|
|
2206
|
+
description && /* @__PURE__ */ jsx("span", { className: "rich-link-card-description", children: description }),
|
|
2207
|
+
/* @__PURE__ */ jsx("span", { className: "rich-link-card-url", children: url })
|
|
2208
|
+
] })
|
|
2209
|
+
]
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
const _LinkCardNode = class _LinkCardNode extends DecoratorNode {
|
|
2214
|
+
constructor(payload, key) {
|
|
2215
|
+
super(key);
|
|
2216
|
+
__publicField(this, "__url");
|
|
2217
|
+
__publicField(this, "__source");
|
|
2218
|
+
__publicField(this, "__id");
|
|
2219
|
+
__publicField(this, "__title");
|
|
2220
|
+
__publicField(this, "__description");
|
|
2221
|
+
__publicField(this, "__favicon");
|
|
2222
|
+
__publicField(this, "__image");
|
|
2223
|
+
this.__url = payload.url;
|
|
2224
|
+
this.__source = payload.source;
|
|
2225
|
+
this.__id = payload.id;
|
|
2226
|
+
this.__title = payload.title;
|
|
2227
|
+
this.__description = payload.description;
|
|
2228
|
+
this.__favicon = payload.favicon;
|
|
2229
|
+
this.__image = payload.image;
|
|
2230
|
+
}
|
|
2231
|
+
static getType() {
|
|
2232
|
+
return "link-card";
|
|
2233
|
+
}
|
|
2234
|
+
static clone(node) {
|
|
2235
|
+
return new _LinkCardNode(
|
|
2236
|
+
{
|
|
2237
|
+
url: node.__url,
|
|
2238
|
+
source: node.__source,
|
|
2239
|
+
id: node.__id,
|
|
2240
|
+
title: node.__title,
|
|
2241
|
+
description: node.__description,
|
|
2242
|
+
favicon: node.__favicon,
|
|
2243
|
+
image: node.__image
|
|
2244
|
+
},
|
|
2245
|
+
node.__key
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
createDOM(_config) {
|
|
2249
|
+
const div = document.createElement("div");
|
|
2250
|
+
div.className = "rich-link-card-wrapper";
|
|
2251
|
+
return div;
|
|
2252
|
+
}
|
|
2253
|
+
updateDOM() {
|
|
2254
|
+
return false;
|
|
2255
|
+
}
|
|
2256
|
+
isInline() {
|
|
2257
|
+
return false;
|
|
2258
|
+
}
|
|
2259
|
+
static importJSON(serializedNode) {
|
|
2260
|
+
return $createLinkCardNode({
|
|
2261
|
+
url: serializedNode.url,
|
|
2262
|
+
source: serializedNode.source,
|
|
2263
|
+
id: serializedNode.id,
|
|
2264
|
+
title: serializedNode.title,
|
|
2265
|
+
description: serializedNode.description,
|
|
2266
|
+
favicon: serializedNode.favicon,
|
|
2267
|
+
image: serializedNode.image
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
exportJSON() {
|
|
2271
|
+
return {
|
|
2272
|
+
...super.exportJSON(),
|
|
2273
|
+
type: "link-card",
|
|
2274
|
+
url: this.__url,
|
|
2275
|
+
source: this.__source,
|
|
2276
|
+
id: this.__id,
|
|
2277
|
+
title: this.__title,
|
|
2278
|
+
description: this.__description,
|
|
2279
|
+
favicon: this.__favicon,
|
|
2280
|
+
image: this.__image,
|
|
2281
|
+
version: 1
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
getUrl() {
|
|
2285
|
+
return this.getLatest().__url;
|
|
2286
|
+
}
|
|
2287
|
+
setUrl(url) {
|
|
2288
|
+
const writable = this.getWritable();
|
|
2289
|
+
writable.__url = url;
|
|
2290
|
+
}
|
|
2291
|
+
getSource() {
|
|
2292
|
+
return this.getLatest().__source;
|
|
2293
|
+
}
|
|
2294
|
+
setSource(source) {
|
|
2295
|
+
const writable = this.getWritable();
|
|
2296
|
+
writable.__source = source;
|
|
2297
|
+
}
|
|
2298
|
+
getId() {
|
|
2299
|
+
return this.getLatest().__id;
|
|
2300
|
+
}
|
|
2301
|
+
setId(id) {
|
|
2302
|
+
const writable = this.getWritable();
|
|
2303
|
+
writable.__id = id;
|
|
2304
|
+
}
|
|
2305
|
+
decorate(_editor, _config) {
|
|
2306
|
+
return createRendererDecoration("LinkCard", LinkCardRenderer, {
|
|
2307
|
+
url: this.__url,
|
|
2308
|
+
source: this.__source,
|
|
2309
|
+
id: this.__id,
|
|
2310
|
+
title: this.__title,
|
|
2311
|
+
description: this.__description,
|
|
2312
|
+
favicon: this.__favicon,
|
|
2313
|
+
image: this.__image
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
__publicField(_LinkCardNode, "slashMenuItems", [
|
|
2318
|
+
{
|
|
2319
|
+
title: "Link Card",
|
|
2320
|
+
icon: createElement(Link, { size: 20 }),
|
|
2321
|
+
description: "Link preview card",
|
|
2322
|
+
keywords: ["link", "card", "bookmark", "embed"],
|
|
2323
|
+
section: "MEDIA",
|
|
2324
|
+
onSelect: (editor) => {
|
|
2325
|
+
editor.update(() => {
|
|
2326
|
+
$insertNodes([$createLinkCardNode({ url: "" })]);
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
]);
|
|
2331
|
+
let LinkCardNode = _LinkCardNode;
|
|
2332
|
+
function $createLinkCardNode(payload) {
|
|
2333
|
+
return new LinkCardNode(payload);
|
|
2334
|
+
}
|
|
2335
|
+
function $isLinkCardNode(node) {
|
|
2336
|
+
return node instanceof LinkCardNode;
|
|
2337
|
+
}
|
|
2338
|
+
function MermaidRenderer({ content }) {
|
|
2339
|
+
return /* @__PURE__ */ jsx("div", { className: "rich-mermaid-block", children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: content }) }) });
|
|
2340
|
+
}
|
|
2341
|
+
const _MermaidNode = class _MermaidNode extends DecoratorNode {
|
|
2342
|
+
constructor(diagram, key) {
|
|
2343
|
+
super(key);
|
|
2344
|
+
__publicField(this, "__diagram");
|
|
2345
|
+
this.__diagram = diagram;
|
|
2346
|
+
}
|
|
2347
|
+
static getType() {
|
|
2348
|
+
return "mermaid";
|
|
2349
|
+
}
|
|
2350
|
+
static clone(node) {
|
|
2351
|
+
return new _MermaidNode(node.__diagram, node.__key);
|
|
2352
|
+
}
|
|
2353
|
+
createDOM(_config) {
|
|
2354
|
+
const div = document.createElement("div");
|
|
2355
|
+
div.className = "rich-mermaid-wrapper";
|
|
2356
|
+
return div;
|
|
2357
|
+
}
|
|
2358
|
+
updateDOM() {
|
|
2359
|
+
return false;
|
|
2360
|
+
}
|
|
2361
|
+
isInline() {
|
|
2362
|
+
return false;
|
|
2363
|
+
}
|
|
2364
|
+
static importJSON(serializedNode) {
|
|
2365
|
+
return $createMermaidNode(serializedNode.diagram);
|
|
2366
|
+
}
|
|
2367
|
+
exportJSON() {
|
|
2368
|
+
return {
|
|
2369
|
+
...super.exportJSON(),
|
|
2370
|
+
type: "mermaid",
|
|
2371
|
+
diagram: this.__diagram,
|
|
2372
|
+
version: 1
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
getDiagram() {
|
|
2376
|
+
return this.__diagram;
|
|
2377
|
+
}
|
|
2378
|
+
setDiagram(diagram) {
|
|
2379
|
+
const writable = this.getWritable();
|
|
2380
|
+
writable.__diagram = diagram;
|
|
2381
|
+
}
|
|
2382
|
+
decorate(editor, _config) {
|
|
2383
|
+
const nodeKey = this.__key;
|
|
2384
|
+
return createRendererDecoration("Mermaid", MermaidRenderer, {
|
|
2385
|
+
content: this.__diagram,
|
|
2386
|
+
onContentChange: (newDiagram) => {
|
|
2387
|
+
editor.update(() => {
|
|
2388
|
+
const node = $getNodeByKey(nodeKey);
|
|
2389
|
+
if (node) {
|
|
2390
|
+
node.setDiagram(newDiagram);
|
|
2391
|
+
}
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
__publicField(_MermaidNode, "slashMenuItems", [
|
|
2398
|
+
{
|
|
2399
|
+
title: "Mermaid Diagram",
|
|
2400
|
+
icon: createElement(Workflow, { size: 20 }),
|
|
2401
|
+
description: "Flowchart, sequence diagram",
|
|
2402
|
+
keywords: ["mermaid", "diagram", "chart", "flowchart"],
|
|
2403
|
+
section: "MEDIA",
|
|
2404
|
+
onSelect: (editor) => {
|
|
2405
|
+
editor.update(() => {
|
|
2406
|
+
$insertNodes([
|
|
2407
|
+
$createMermaidNode("graph TD\n A[Start] --> B[End]")
|
|
2408
|
+
]);
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
]);
|
|
2413
|
+
let MermaidNode = _MermaidNode;
|
|
2414
|
+
function $createMermaidNode(diagram) {
|
|
2415
|
+
return new MermaidNode(diagram);
|
|
2416
|
+
}
|
|
2417
|
+
function $isMermaidNode(node) {
|
|
2418
|
+
return node instanceof MermaidNode;
|
|
2419
|
+
}
|
|
2420
|
+
function VideoRenderer({
|
|
2421
|
+
src,
|
|
2422
|
+
poster,
|
|
2423
|
+
width,
|
|
2424
|
+
height
|
|
2425
|
+
}) {
|
|
2426
|
+
return /* @__PURE__ */ jsx("figure", { className: "rich-video", children: /* @__PURE__ */ jsx(
|
|
2427
|
+
"video",
|
|
2428
|
+
{
|
|
2429
|
+
src,
|
|
2430
|
+
poster,
|
|
2431
|
+
width,
|
|
2432
|
+
height,
|
|
2433
|
+
controls: true,
|
|
2434
|
+
preload: "metadata",
|
|
2435
|
+
style: { maxWidth: "100%", height: "auto" }
|
|
2436
|
+
}
|
|
2437
|
+
) });
|
|
2438
|
+
}
|
|
2439
|
+
const _VideoNode = class _VideoNode extends DecoratorNode {
|
|
2440
|
+
constructor(payload, key) {
|
|
2441
|
+
super(key);
|
|
2442
|
+
__publicField(this, "__src");
|
|
2443
|
+
__publicField(this, "__poster");
|
|
2444
|
+
__publicField(this, "__width");
|
|
2445
|
+
__publicField(this, "__height");
|
|
2446
|
+
this.__src = payload.src;
|
|
2447
|
+
this.__poster = payload.poster;
|
|
2448
|
+
this.__width = payload.width;
|
|
2449
|
+
this.__height = payload.height;
|
|
2450
|
+
}
|
|
2451
|
+
static getType() {
|
|
2452
|
+
return "video";
|
|
2453
|
+
}
|
|
2454
|
+
static clone(node) {
|
|
2455
|
+
return new _VideoNode(
|
|
2456
|
+
{
|
|
2457
|
+
src: node.__src,
|
|
2458
|
+
poster: node.__poster,
|
|
2459
|
+
width: node.__width,
|
|
2460
|
+
height: node.__height
|
|
2461
|
+
},
|
|
2462
|
+
node.__key
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
createDOM(_config) {
|
|
2466
|
+
const div = document.createElement("div");
|
|
2467
|
+
div.className = "rich-video-wrapper";
|
|
2468
|
+
return div;
|
|
2469
|
+
}
|
|
2470
|
+
updateDOM() {
|
|
2471
|
+
return false;
|
|
2472
|
+
}
|
|
2473
|
+
isInline() {
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
static importJSON(serializedNode) {
|
|
2477
|
+
return $createVideoNode({
|
|
2478
|
+
src: serializedNode.src,
|
|
2479
|
+
poster: serializedNode.poster,
|
|
2480
|
+
width: serializedNode.width,
|
|
2481
|
+
height: serializedNode.height
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
exportJSON() {
|
|
2485
|
+
return {
|
|
2486
|
+
...super.exportJSON(),
|
|
2487
|
+
type: "video",
|
|
2488
|
+
src: this.__src,
|
|
2489
|
+
poster: this.__poster,
|
|
2490
|
+
width: this.__width,
|
|
2491
|
+
height: this.__height,
|
|
2492
|
+
version: 1
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
getSrc() {
|
|
2496
|
+
return this.getLatest().__src;
|
|
2497
|
+
}
|
|
2498
|
+
setSrc(src) {
|
|
2499
|
+
const writable = this.getWritable();
|
|
2500
|
+
writable.__src = src;
|
|
2501
|
+
}
|
|
2502
|
+
decorate(_editor, _config) {
|
|
2503
|
+
return createRendererDecoration("Video", VideoRenderer, {
|
|
2504
|
+
src: this.__src,
|
|
2505
|
+
poster: this.__poster,
|
|
2506
|
+
width: this.__width,
|
|
2507
|
+
height: this.__height
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
__publicField(_VideoNode, "slashMenuItems", [
|
|
2512
|
+
{
|
|
2513
|
+
title: "Video",
|
|
2514
|
+
icon: createElement(Video, { size: 20 }),
|
|
2515
|
+
description: "Embed a video",
|
|
2516
|
+
keywords: ["video", "media", "mp4"],
|
|
2517
|
+
section: "MEDIA",
|
|
2518
|
+
onSelect: (editor) => {
|
|
2519
|
+
editor.update(() => {
|
|
2520
|
+
$insertNodes([$createVideoNode({ src: "" })]);
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
]);
|
|
2525
|
+
let VideoNode = _VideoNode;
|
|
2526
|
+
function $createVideoNode(payload) {
|
|
2527
|
+
return new VideoNode(payload);
|
|
2528
|
+
}
|
|
2529
|
+
const builtinNodes = [
|
|
2530
|
+
HeadingNode,
|
|
2531
|
+
QuoteNode,
|
|
2532
|
+
ListNode,
|
|
2533
|
+
ListItemNode,
|
|
2534
|
+
LinkNode,
|
|
2535
|
+
AutoLinkNode,
|
|
2536
|
+
HorizontalRuleNode,
|
|
2537
|
+
TableNode,
|
|
2538
|
+
TableCellNode,
|
|
2539
|
+
TableRowNode,
|
|
2540
|
+
CodeNode
|
|
2541
|
+
];
|
|
2542
|
+
const customNodes = [
|
|
2543
|
+
SpoilerNode,
|
|
2544
|
+
MentionNode,
|
|
2545
|
+
KaTeXInlineNode,
|
|
2546
|
+
KaTeXBlockNode,
|
|
2547
|
+
ImageNode,
|
|
2548
|
+
AlertQuoteNode,
|
|
2549
|
+
CodeBlockNode,
|
|
2550
|
+
FootnoteNode,
|
|
2551
|
+
FootnoteSectionNode,
|
|
2552
|
+
TaskListItemNode,
|
|
2553
|
+
VideoNode,
|
|
2554
|
+
LinkCardNode,
|
|
2555
|
+
DetailsNode,
|
|
2556
|
+
GridContainerNode,
|
|
2557
|
+
BannerNode,
|
|
2558
|
+
MermaidNode
|
|
2559
|
+
];
|
|
2560
|
+
const allNodes = [
|
|
2561
|
+
...builtinNodes,
|
|
2562
|
+
...customNodes
|
|
2563
|
+
];
|
|
2564
|
+
function FootnotePlugin({ children }) {
|
|
2565
|
+
const [editor] = useLexicalComposerContext();
|
|
2566
|
+
const [definitions, setDefinitions] = useState({});
|
|
2567
|
+
const [displayNumberMap, setDisplayNumberMap] = useState({});
|
|
2568
|
+
useEffect(() => {
|
|
2569
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
2570
|
+
editorState.read(() => {
|
|
2571
|
+
const footnoteNodes = $nodesOfType(FootnoteNode);
|
|
2572
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2573
|
+
const numberMap = {};
|
|
2574
|
+
let counter = 1;
|
|
2575
|
+
for (const node of footnoteNodes) {
|
|
2576
|
+
const id = node.getIdentifier();
|
|
2577
|
+
if (!seen.has(id)) {
|
|
2578
|
+
seen.add(id);
|
|
2579
|
+
numberMap[id] = counter++;
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
setDisplayNumberMap(numberMap);
|
|
2583
|
+
const sectionNodes = $nodesOfType(FootnoteSectionNode);
|
|
2584
|
+
if (sectionNodes.length > 0) {
|
|
2585
|
+
setDefinitions(sectionNodes[0].getDefinitions());
|
|
2586
|
+
} else {
|
|
2587
|
+
setDefinitions({});
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2590
|
+
});
|
|
2591
|
+
}, [editor]);
|
|
2592
|
+
return /* @__PURE__ */ jsx(
|
|
2593
|
+
FootnoteDefinitionsProvider,
|
|
2594
|
+
{
|
|
2595
|
+
definitions,
|
|
2596
|
+
displayNumberMap,
|
|
2597
|
+
children
|
|
2598
|
+
}
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
var articleVariant = "v10dui1 r8uj4t0 _17pm0gw0 v10dui0";
|
|
2602
|
+
var darkArticleVariant = "v10dui2 r8uj4t0 _17pm0gw2 v10dui0";
|
|
2603
|
+
var commentVariant = "_14ko36c1 r8uj4t0 _17pm0gw1 _14ko36c0";
|
|
2604
|
+
var darkCommentVariant = "_14ko36c2 r8uj4t0 _17pm0gw3 _14ko36c0";
|
|
2605
|
+
var noteVariant = "ys4fw91 r8uj4t0 _17pm0gw4 ys4fw90";
|
|
2606
|
+
var darkNoteVariant = "ys4fw92 r8uj4t0 _17pm0gw5 ys4fw90";
|
|
2607
|
+
function clsx(...args) {
|
|
2608
|
+
return args.filter(Boolean).join(" ");
|
|
2609
|
+
}
|
|
2610
|
+
function getVariantClass(variant, colorScheme) {
|
|
2611
|
+
if (variant === "comment") {
|
|
2612
|
+
return colorScheme === "dark" ? darkCommentVariant : commentVariant;
|
|
2613
|
+
}
|
|
2614
|
+
if (variant === "note") {
|
|
2615
|
+
return colorScheme === "dark" ? darkNoteVariant : noteVariant;
|
|
2616
|
+
}
|
|
2617
|
+
return colorScheme === "dark" ? darkArticleVariant : articleVariant;
|
|
2618
|
+
}
|
|
2619
|
+
export {
|
|
2620
|
+
$createFootnoteSectionNode as $,
|
|
2621
|
+
ALERT_LABELS as A,
|
|
2622
|
+
BANNER_LABELS as B,
|
|
2623
|
+
ColorSchemeProvider as C,
|
|
2624
|
+
darkArticleVariant as D,
|
|
2625
|
+
darkCommentVariant as E,
|
|
2626
|
+
FootnotePlugin as F,
|
|
2627
|
+
GridContainerNode as G,
|
|
2628
|
+
darkNoteVariant as H,
|
|
2629
|
+
decodeThumbHash as I,
|
|
2630
|
+
noteVariant as J,
|
|
2631
|
+
KaTeXBlockNode as K,
|
|
2632
|
+
LinkCardNode as L,
|
|
2633
|
+
MermaidNode as M,
|
|
2634
|
+
NESTED_EDITOR_NODES as N,
|
|
2635
|
+
useColorScheme as O,
|
|
2636
|
+
PortalThemeProvider as P,
|
|
2637
|
+
useFootnoteContent as Q,
|
|
2638
|
+
RendererConfigProvider as R,
|
|
2639
|
+
useFootnoteDefinitions as S,
|
|
2640
|
+
useFootnoteDisplayNumber as T,
|
|
2641
|
+
useRendererConfig as U,
|
|
2642
|
+
useRendererMode as V,
|
|
2643
|
+
$isAlertQuoteNode as W,
|
|
2644
|
+
RendererWrapper as X,
|
|
2645
|
+
AlertRenderer as Y,
|
|
2646
|
+
AlertQuoteNode as Z,
|
|
2647
|
+
$isBannerNode as _,
|
|
2648
|
+
allNodes as a,
|
|
2649
|
+
BannerRenderer as a0,
|
|
2650
|
+
BannerNode as a1,
|
|
2651
|
+
normalizeBannerType as a2,
|
|
2652
|
+
$isCodeBlockNode as a3,
|
|
2653
|
+
CodeBlockRenderer as a4,
|
|
2654
|
+
CodeBlockNode as a5,
|
|
2655
|
+
SpoilerNode as a6,
|
|
2656
|
+
MentionNode as a7,
|
|
2657
|
+
ImageNode as a8,
|
|
2658
|
+
FootnoteNode as a9,
|
|
2659
|
+
TaskListItemNode as aa,
|
|
2660
|
+
VideoNode as ab,
|
|
2661
|
+
DetailsNode as ac,
|
|
2662
|
+
$createAlertQuoteNode as ad,
|
|
2663
|
+
$createImageNode as ae,
|
|
2664
|
+
$createKaTeXInlineNode as af,
|
|
2665
|
+
$createKaTeXBlockNode as ag,
|
|
2666
|
+
$createBannerNode as ah,
|
|
2667
|
+
$createDetailsNode as ai,
|
|
2668
|
+
$isDetailsNode as aj,
|
|
2669
|
+
$createFootnoteNode as ak,
|
|
2670
|
+
$isFootnoteNode as al,
|
|
2671
|
+
$createMentionNode as am,
|
|
2672
|
+
$isMentionNode as an,
|
|
2673
|
+
$createSpoilerNode as ao,
|
|
2674
|
+
$isSpoilerNode as ap,
|
|
2675
|
+
$createTaskListItemNode as aq,
|
|
2676
|
+
$isTaskListItemNode as ar,
|
|
2677
|
+
builtinNodes as b,
|
|
2678
|
+
customNodes as c,
|
|
2679
|
+
clsx as d,
|
|
2680
|
+
editorTheme as e,
|
|
2681
|
+
$createGridContainerNode as f,
|
|
2682
|
+
getVariantClass as g,
|
|
2683
|
+
$createLinkCardNode as h,
|
|
2684
|
+
$createMermaidNode as i,
|
|
2685
|
+
$isFootnoteSectionNode as j,
|
|
2686
|
+
$isGridContainerNode as k,
|
|
2687
|
+
$isKaTeXBlockNode as l,
|
|
2688
|
+
$isKaTeXInlineNode as m,
|
|
2689
|
+
$isLinkCardNode as n,
|
|
2690
|
+
$isMermaidNode as o,
|
|
2691
|
+
ALERT_TYPES as p,
|
|
2692
|
+
BANNER_TYPES as q,
|
|
2693
|
+
FootnoteDefinitionsProvider as r,
|
|
2694
|
+
FootnoteSectionNode as s,
|
|
2695
|
+
KaTeXInlineNode as t,
|
|
2696
|
+
KaTeXRenderer as u,
|
|
2697
|
+
LinkCardRenderer as v,
|
|
2698
|
+
articleVariant as w,
|
|
2699
|
+
commentVariant as x,
|
|
2700
|
+
computeImageMeta as y,
|
|
2701
|
+
createRendererDecoration as z
|
|
2702
|
+
};
|