@haklex/rich-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AlertQuoteEditNode-C55sxsR3.js +267 -0
- package/dist/KaTeXRenderer-CQQT3BMw.js +215 -0
- package/dist/LinkCardRenderer-CigqFwCv.js +45 -0
- package/dist/MermaidPlugin-BrOr-wQi.js +67 -0
- package/dist/PresentDialogContext-DKNicgia.js +74 -0
- package/dist/RubyRenderer-jOkydJHg.js +15 -0
- package/dist/SubmitShortcutPlugin-D-7XrQfm.js +2186 -0
- package/dist/commands-entry.mjs +54 -74
- package/dist/config-CNiK9v2M.js +1246 -0
- package/dist/grid.css-CJCkLTZc.js +44 -0
- package/dist/index.mjs +121 -180
- package/dist/katex.css-CIOEOXyd.js +145 -0
- package/dist/node-registry-DOYK_WIp.js +669 -0
- package/dist/nodes-entry.mjs +5 -50
- package/dist/normalizeSerializedEditorState-B-1wmGzd.js +78 -0
- package/dist/plugins-entry.mjs +3 -28
- package/dist/renderers-entry.mjs +41 -61
- package/dist/rich-editor.css +2 -1
- package/dist/static-entry.mjs +16 -66
- package/dist/styles-entry.mjs +3 -21
- package/dist/theme-B5B2EOWM.js +1099 -0
- package/package.json +30 -30
- package/dist/AlertQuoteEditNode-sPNf3_7P.js +0 -293
- package/dist/KaTeXRenderer-CQyQzNTJ.js +0 -218
- package/dist/LinkCardRenderer-QmkOlyXb.js +0 -36
- package/dist/MermaidPlugin-DKuGUcCG.js +0 -101
- package/dist/PresentDialogContext-DRroMIoK.js +0 -71
- package/dist/RubyRenderer-CJQmODir.js +0 -14
- package/dist/SubmitShortcutPlugin-D9uKYHda.js +0 -2427
- package/dist/config-Dl3ZkytB.js +0 -1362
- package/dist/grid.css-Md5-Cfx_.js +0 -11
- package/dist/katex.css-Csc-7N7u.js +0 -28
- package/dist/node-registry-CovhHUB6.js +0 -824
- package/dist/normalizeSerializedEditorState-k5G4xSi9.js +0 -85
- package/dist/theme-lEwScxEX.js +0 -1113
|
@@ -0,0 +1,1246 @@
|
|
|
1
|
+
import { A as AlertQuoteNode, C as ImageNode, E as FootnoteNode, F as useNestedContentRenderer, M as extractTextContent, _ as KaTeXInlineNode, b as KaTeXBlockNode, c as SpoilerNode, d as MermaidNode, i as TagNode, j as _defineProperty, m as MentionNode } from "./theme-B5B2EOWM.js";
|
|
2
|
+
import { l as RendererWrapper, m as useVariant, s as useFootnoteDefinitions, u as createRendererDecoration } from "./KaTeXRenderer-CQQT3BMw.js";
|
|
3
|
+
import { l as semanticClassNames, r as clsx, u as sharedStyles } from "./katex.css-CIOEOXyd.js";
|
|
4
|
+
import { i as detailsStyles, n as gridStyles, r as detailsClassNames, t as gridClassNames } from "./grid.css-CJCkLTZc.js";
|
|
5
|
+
import { t as LinkCardRenderer } from "./LinkCardRenderer-CigqFwCv.js";
|
|
6
|
+
import { AutoLinkNode, LinkNode } from "@lexical/link";
|
|
7
|
+
import { createContext, createElement, use, useCallback, useEffect, useRef, useState } from "react";
|
|
8
|
+
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
9
|
+
import { $createTextNode, $insertNodes, DecoratorNode, ElementNode, TextNode } from "lexical";
|
|
10
|
+
import { HorizontalRuleNode } from "@lexical/extension";
|
|
11
|
+
import { CodeNode } from "@lexical/code-core";
|
|
12
|
+
import { ListItemNode, ListNode } from "@lexical/list";
|
|
13
|
+
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import { ChevronRight, Code, Link, MessageSquareQuote, Video } from "lucide-react";
|
|
16
|
+
//#region src/components/renderers/BannerRenderer.tsx
|
|
17
|
+
var BannerRenderer = ({ type }) => {
|
|
18
|
+
return /* @__PURE__ */ jsx("span", { className: `rich-banner-icon rich-banner-icon-${type}` });
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/components/renderers/BannerStaticDecorator.tsx
|
|
22
|
+
function BannerStaticDecorator({ bannerType, contentState }) {
|
|
23
|
+
const renderContent = useNestedContentRenderer();
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
25
|
+
className: "rich-banner-inner",
|
|
26
|
+
children: [/* @__PURE__ */ jsx(RendererWrapper, {
|
|
27
|
+
defaultRenderer: BannerRenderer,
|
|
28
|
+
props: { type: bannerType },
|
|
29
|
+
rendererKey: "Banner"
|
|
30
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
31
|
+
className: "rich-banner-content",
|
|
32
|
+
children: renderContent(contentState)
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/nodes/BannerNode.ts
|
|
38
|
+
var LEGACY_TYPE_MAP = {
|
|
39
|
+
info: "note",
|
|
40
|
+
success: "tip",
|
|
41
|
+
error: "caution"
|
|
42
|
+
};
|
|
43
|
+
function normalizeBannerType(type) {
|
|
44
|
+
if (type in LEGACY_TYPE_MAP) return LEGACY_TYPE_MAP[type];
|
|
45
|
+
return type || "note";
|
|
46
|
+
}
|
|
47
|
+
var BANNER_TYPES = [
|
|
48
|
+
"note",
|
|
49
|
+
"tip",
|
|
50
|
+
"important",
|
|
51
|
+
"warning",
|
|
52
|
+
"caution"
|
|
53
|
+
];
|
|
54
|
+
var BANNER_LABELS = {
|
|
55
|
+
note: "Note",
|
|
56
|
+
tip: "Tip",
|
|
57
|
+
important: "Important",
|
|
58
|
+
warning: "Warning",
|
|
59
|
+
caution: "Caution"
|
|
60
|
+
};
|
|
61
|
+
var BannerNode = class BannerNode extends DecoratorNode {
|
|
62
|
+
static getType() {
|
|
63
|
+
return "banner";
|
|
64
|
+
}
|
|
65
|
+
static clone(node) {
|
|
66
|
+
return new BannerNode(node.__bannerType, node.__contentState, node.__key);
|
|
67
|
+
}
|
|
68
|
+
constructor(bannerType, contentState, key) {
|
|
69
|
+
super(key);
|
|
70
|
+
_defineProperty(this, "__bannerType", void 0);
|
|
71
|
+
_defineProperty(this, "__contentState", void 0);
|
|
72
|
+
this.__bannerType = bannerType;
|
|
73
|
+
this.__contentState = contentState || { root: {
|
|
74
|
+
children: [{
|
|
75
|
+
type: "paragraph",
|
|
76
|
+
children: [],
|
|
77
|
+
direction: null,
|
|
78
|
+
format: "",
|
|
79
|
+
indent: 0,
|
|
80
|
+
textFormat: 0,
|
|
81
|
+
textStyle: "",
|
|
82
|
+
version: 1
|
|
83
|
+
}],
|
|
84
|
+
direction: null,
|
|
85
|
+
format: "",
|
|
86
|
+
indent: 0,
|
|
87
|
+
type: "root",
|
|
88
|
+
version: 1
|
|
89
|
+
} };
|
|
90
|
+
}
|
|
91
|
+
createDOM(_config) {
|
|
92
|
+
const div = document.createElement("div");
|
|
93
|
+
div.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
94
|
+
return div;
|
|
95
|
+
}
|
|
96
|
+
updateDOM(prevNode, dom) {
|
|
97
|
+
if (prevNode.__bannerType !== this.__bannerType) dom.className = `rich-banner rich-banner-${this.__bannerType}`;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
isInline() {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
getBannerType() {
|
|
104
|
+
return this.__bannerType;
|
|
105
|
+
}
|
|
106
|
+
setBannerType(bannerType) {
|
|
107
|
+
const writable = this.getWritable();
|
|
108
|
+
writable.__bannerType = bannerType;
|
|
109
|
+
}
|
|
110
|
+
getContentState() {
|
|
111
|
+
return this.getLatest().__contentState;
|
|
112
|
+
}
|
|
113
|
+
setContentState(state) {
|
|
114
|
+
const writable = this.getWritable();
|
|
115
|
+
writable.__contentState = state;
|
|
116
|
+
}
|
|
117
|
+
getTextContent() {
|
|
118
|
+
return extractTextContent(this.__contentState);
|
|
119
|
+
}
|
|
120
|
+
static importJSON(serializedNode) {
|
|
121
|
+
const legacy = serializedNode;
|
|
122
|
+
const bannerType = normalizeBannerType(serializedNode.bannerType);
|
|
123
|
+
if (serializedNode.content) return new BannerNode(bannerType, serializedNode.content);
|
|
124
|
+
if (legacy.children) return new BannerNode(bannerType, { root: {
|
|
125
|
+
children: legacy.children,
|
|
126
|
+
direction: null,
|
|
127
|
+
format: "",
|
|
128
|
+
indent: 0,
|
|
129
|
+
type: "root",
|
|
130
|
+
version: 1
|
|
131
|
+
} });
|
|
132
|
+
return new BannerNode(bannerType);
|
|
133
|
+
}
|
|
134
|
+
exportJSON() {
|
|
135
|
+
return {
|
|
136
|
+
...super.exportJSON(),
|
|
137
|
+
type: "banner",
|
|
138
|
+
bannerType: this.__bannerType,
|
|
139
|
+
content: this.__contentState,
|
|
140
|
+
version: 1
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
decorate(_editor, _config) {
|
|
144
|
+
return createElement(BannerStaticDecorator, {
|
|
145
|
+
bannerType: this.__bannerType,
|
|
146
|
+
contentState: this.__contentState
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
function $isBannerNode(node) {
|
|
151
|
+
return node instanceof BannerNode;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/context/ColorSchemeContext.tsx
|
|
155
|
+
var ColorSchemeContext = createContext("light");
|
|
156
|
+
function ColorSchemeProvider({ colorScheme, children }) {
|
|
157
|
+
return /* @__PURE__ */ jsx(ColorSchemeContext.Provider, {
|
|
158
|
+
value: colorScheme,
|
|
159
|
+
children
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function useColorScheme() {
|
|
163
|
+
return use(ColorSchemeContext);
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/utils/shiki.ts
|
|
167
|
+
var codeToHtmlFn = null;
|
|
168
|
+
var shikiLoadPromise = null;
|
|
169
|
+
function loadCodeToHtml() {
|
|
170
|
+
if (codeToHtmlFn) return Promise.resolve(codeToHtmlFn);
|
|
171
|
+
if (!shikiLoadPromise) shikiLoadPromise = import("shiki/bundle/web").then((mod) => {
|
|
172
|
+
codeToHtmlFn = mod.codeToHtml;
|
|
173
|
+
return mod.codeToHtml;
|
|
174
|
+
}).catch((err) => {
|
|
175
|
+
shikiLoadPromise = null;
|
|
176
|
+
throw err;
|
|
177
|
+
});
|
|
178
|
+
return shikiLoadPromise;
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/components/renderers/CodeBlockRenderer.tsx
|
|
182
|
+
function CodeBlockRenderer({ code, language, showLineNumbers: showLineNumbersProp }) {
|
|
183
|
+
const variant = useVariant();
|
|
184
|
+
const showLineNumbers = showLineNumbersProp ?? variant !== "comment";
|
|
185
|
+
const shikiTheme = useColorScheme() === "dark" ? "github-dark" : "github-light";
|
|
186
|
+
const [highlightedHtml, setHighlightedHtml] = useState(null);
|
|
187
|
+
const [copied, setCopied] = useState(false);
|
|
188
|
+
const copyTimerRef = useRef(void 0);
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
let cancelled = false;
|
|
191
|
+
setHighlightedHtml(null);
|
|
192
|
+
loadCodeToHtml().then((toHtml) => toHtml(code, {
|
|
193
|
+
lang: language,
|
|
194
|
+
theme: shikiTheme
|
|
195
|
+
})).then((html) => {
|
|
196
|
+
if (!cancelled) setHighlightedHtml(html);
|
|
197
|
+
}).catch(() => {
|
|
198
|
+
if (!cancelled) setHighlightedHtml(null);
|
|
199
|
+
});
|
|
200
|
+
return () => {
|
|
201
|
+
cancelled = true;
|
|
202
|
+
};
|
|
203
|
+
}, [
|
|
204
|
+
code,
|
|
205
|
+
language,
|
|
206
|
+
shikiTheme
|
|
207
|
+
]);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
return () => clearTimeout(copyTimerRef.current);
|
|
210
|
+
}, []);
|
|
211
|
+
const handleCopy = useCallback(() => {
|
|
212
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
213
|
+
setCopied(true);
|
|
214
|
+
clearTimeout(copyTimerRef.current);
|
|
215
|
+
copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
216
|
+
}).catch(() => {});
|
|
217
|
+
}, [code]);
|
|
218
|
+
const header = language ? /* @__PURE__ */ jsxs("div", {
|
|
219
|
+
className: "rich-code-block-header",
|
|
220
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
221
|
+
className: "rich-code-block-lang",
|
|
222
|
+
children: language
|
|
223
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
224
|
+
"aria-label": copied ? "Copied to clipboard" : "Copy code",
|
|
225
|
+
className: "rich-code-block-copy",
|
|
226
|
+
type: "button",
|
|
227
|
+
onClick: handleCopy,
|
|
228
|
+
children: copied ? "Copied" : "Copy"
|
|
229
|
+
})]
|
|
230
|
+
}) : null;
|
|
231
|
+
const wrapperClass = showLineNumbers ? "rich-code-block rich-code-block-numbered" : "rich-code-block";
|
|
232
|
+
if (highlightedHtml) return /* @__PURE__ */ jsxs("div", {
|
|
233
|
+
className: wrapperClass,
|
|
234
|
+
children: [header, /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: highlightedHtml } })]
|
|
235
|
+
});
|
|
236
|
+
const lines = code.split("\n");
|
|
237
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
238
|
+
className: wrapperClass,
|
|
239
|
+
children: [header, /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: lines.map((line, i) => /* @__PURE__ */ jsxs("span", {
|
|
240
|
+
className: "line",
|
|
241
|
+
children: [line, i < lines.length - 1 ? "\n" : ""]
|
|
242
|
+
}, i)) }) })]
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/nodes/CodeBlockNode.ts
|
|
247
|
+
var CodeBlockNode = class CodeBlockNode extends DecoratorNode {
|
|
248
|
+
static getType() {
|
|
249
|
+
return "code-block";
|
|
250
|
+
}
|
|
251
|
+
static clone(node) {
|
|
252
|
+
return new CodeBlockNode(node.__code, node.__language, node.__key);
|
|
253
|
+
}
|
|
254
|
+
constructor(code, language, key) {
|
|
255
|
+
super(key);
|
|
256
|
+
_defineProperty(this, "__code", void 0);
|
|
257
|
+
_defineProperty(this, "__language", void 0);
|
|
258
|
+
this.__code = code;
|
|
259
|
+
this.__language = language;
|
|
260
|
+
}
|
|
261
|
+
createDOM(_config) {
|
|
262
|
+
const div = document.createElement("div");
|
|
263
|
+
div.className = "rich-code-block-wrapper";
|
|
264
|
+
return div;
|
|
265
|
+
}
|
|
266
|
+
updateDOM() {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
isInline() {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
isKeyboardSelectable() {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
static importJSON(serializedNode) {
|
|
276
|
+
return $createCodeBlockNode(serializedNode.code, serializedNode.language);
|
|
277
|
+
}
|
|
278
|
+
exportJSON() {
|
|
279
|
+
return {
|
|
280
|
+
...super.exportJSON(),
|
|
281
|
+
type: "code-block",
|
|
282
|
+
code: this.__code,
|
|
283
|
+
language: this.__language,
|
|
284
|
+
version: 1
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
getCode() {
|
|
288
|
+
return this.__code;
|
|
289
|
+
}
|
|
290
|
+
setCode(code) {
|
|
291
|
+
const writable = this.getWritable();
|
|
292
|
+
writable.__code = code;
|
|
293
|
+
}
|
|
294
|
+
getLanguage() {
|
|
295
|
+
return this.__language;
|
|
296
|
+
}
|
|
297
|
+
setLanguage(language) {
|
|
298
|
+
const writable = this.getWritable();
|
|
299
|
+
writable.__language = language;
|
|
300
|
+
}
|
|
301
|
+
decorate(_editor, _config) {
|
|
302
|
+
return createRendererDecoration("CodeBlock", CodeBlockRenderer, {
|
|
303
|
+
code: this.__code,
|
|
304
|
+
language: this.__language
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
_defineProperty(CodeBlockNode, "commandItems", [{
|
|
309
|
+
title: "Code Block",
|
|
310
|
+
icon: createElement(Code, { size: 20 }),
|
|
311
|
+
description: "Syntax-highlighted code",
|
|
312
|
+
keywords: [
|
|
313
|
+
"code",
|
|
314
|
+
"snippet",
|
|
315
|
+
"codeblock"
|
|
316
|
+
],
|
|
317
|
+
section: "MEDIA",
|
|
318
|
+
placement: ["slash", "toolbar"],
|
|
319
|
+
group: "insert",
|
|
320
|
+
onSelect: (editor) => {
|
|
321
|
+
editor.update(() => {
|
|
322
|
+
$insertNodes([$createCodeBlockNode("", "text")]);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}]);
|
|
326
|
+
function $createCodeBlockNode(code, language) {
|
|
327
|
+
return new CodeBlockNode(code, language);
|
|
328
|
+
}
|
|
329
|
+
function $isCodeBlockNode(node) {
|
|
330
|
+
return node instanceof CodeBlockNode;
|
|
331
|
+
}
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region src/nodes/CommentNode.ts
|
|
334
|
+
var DEFAULT_COMMENT_TEXT = "comment";
|
|
335
|
+
var CommentNode = class CommentNode extends TextNode {
|
|
336
|
+
static getType() {
|
|
337
|
+
return "comment";
|
|
338
|
+
}
|
|
339
|
+
static clone(node) {
|
|
340
|
+
return new CommentNode(node.__text, node.__key);
|
|
341
|
+
}
|
|
342
|
+
static importDOM() {
|
|
343
|
+
return {
|
|
344
|
+
"#comment": () => ({
|
|
345
|
+
conversion: (domNode) => {
|
|
346
|
+
if (!(domNode instanceof Comment)) return null;
|
|
347
|
+
return { node: $createCommentNode(domNode.data) };
|
|
348
|
+
},
|
|
349
|
+
priority: 4
|
|
350
|
+
}),
|
|
351
|
+
"span": () => ({
|
|
352
|
+
conversion: (domNode) => {
|
|
353
|
+
if (!(domNode instanceof HTMLElement)) return null;
|
|
354
|
+
if (!domNode.classList.contains(semanticClassNames.comment)) return null;
|
|
355
|
+
return { node: $createCommentNode(domNode.dataset.comment ?? domNode.textContent ?? "") };
|
|
356
|
+
},
|
|
357
|
+
priority: 2
|
|
358
|
+
})
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
constructor(text, key) {
|
|
362
|
+
super(text, key);
|
|
363
|
+
}
|
|
364
|
+
createDOM(config) {
|
|
365
|
+
const element = super.createDOM(config);
|
|
366
|
+
element.classList.add(semanticClassNames.comment, sharedStyles.comment);
|
|
367
|
+
element.dataset.comment = this.__text;
|
|
368
|
+
return element;
|
|
369
|
+
}
|
|
370
|
+
updateDOM(prevNode, dom, config) {
|
|
371
|
+
const updated = super.updateDOM(prevNode, dom, config);
|
|
372
|
+
dom.classList.add(semanticClassNames.comment, sharedStyles.comment);
|
|
373
|
+
if (prevNode.__text !== this.__text) dom.dataset.comment = this.__text;
|
|
374
|
+
return updated;
|
|
375
|
+
}
|
|
376
|
+
exportDOM(_editor) {
|
|
377
|
+
return { element: document.createComment(this.getTextContent()) };
|
|
378
|
+
}
|
|
379
|
+
static importJSON(serializedNode) {
|
|
380
|
+
const node = $createCommentNode(serializedNode.text ?? "");
|
|
381
|
+
node.setFormat(serializedNode.format ?? 0);
|
|
382
|
+
node.setDetail(serializedNode.detail ?? 0);
|
|
383
|
+
node.setMode(serializedNode.mode ?? "normal");
|
|
384
|
+
node.setStyle(serializedNode.style ?? "");
|
|
385
|
+
return node;
|
|
386
|
+
}
|
|
387
|
+
exportJSON() {
|
|
388
|
+
return {
|
|
389
|
+
...super.exportJSON(),
|
|
390
|
+
type: "comment",
|
|
391
|
+
version: 1
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
_defineProperty(CommentNode, "commandItems", [{
|
|
396
|
+
title: "HTML Comment",
|
|
397
|
+
icon: createElement(MessageSquareQuote, { size: 20 }),
|
|
398
|
+
description: "Insert an HTML comment node",
|
|
399
|
+
keywords: [
|
|
400
|
+
"comment",
|
|
401
|
+
"html",
|
|
402
|
+
"annotation",
|
|
403
|
+
"hidden",
|
|
404
|
+
"<!-- -->"
|
|
405
|
+
],
|
|
406
|
+
section: "INLINE",
|
|
407
|
+
placement: ["slash", "toolbar"],
|
|
408
|
+
group: "insert",
|
|
409
|
+
onSelect: (editor, _queryString) => {
|
|
410
|
+
editor.update(() => {
|
|
411
|
+
const node = $createCommentNode(DEFAULT_COMMENT_TEXT);
|
|
412
|
+
$insertNodes([node]);
|
|
413
|
+
node.select(0, node.getTextContentSize());
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}]);
|
|
417
|
+
function $createCommentNode(text) {
|
|
418
|
+
return new CommentNode(text);
|
|
419
|
+
}
|
|
420
|
+
function $createCommentPlaceholderNode() {
|
|
421
|
+
return $createCommentNode(DEFAULT_COMMENT_TEXT);
|
|
422
|
+
}
|
|
423
|
+
function $isCommentNode(node) {
|
|
424
|
+
return node instanceof CommentNode;
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/utils/lucide-dom.ts
|
|
428
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
429
|
+
var DEFAULT_ATTRS = {
|
|
430
|
+
xmlns: SVG_NS,
|
|
431
|
+
width: "24",
|
|
432
|
+
height: "24",
|
|
433
|
+
viewBox: "0 0 24 24",
|
|
434
|
+
fill: "none",
|
|
435
|
+
stroke: "currentColor",
|
|
436
|
+
"stroke-width": "2",
|
|
437
|
+
"stroke-linecap": "round",
|
|
438
|
+
"stroke-linejoin": "round"
|
|
439
|
+
};
|
|
440
|
+
function createLucideSvg(iconNode, attrs = {}) {
|
|
441
|
+
const svg = document.createElementNS(SVG_NS, "svg");
|
|
442
|
+
const merged = {
|
|
443
|
+
...DEFAULT_ATTRS,
|
|
444
|
+
...attrs
|
|
445
|
+
};
|
|
446
|
+
for (const [k, v] of Object.entries(merged)) svg.setAttribute(k, v);
|
|
447
|
+
for (const [tag, elAttrs] of iconNode) {
|
|
448
|
+
const el = document.createElementNS(SVG_NS, tag);
|
|
449
|
+
for (const [k, v] of Object.entries(elAttrs)) {
|
|
450
|
+
if (k === "key") continue;
|
|
451
|
+
el.setAttribute(k, v);
|
|
452
|
+
}
|
|
453
|
+
svg.append(el);
|
|
454
|
+
}
|
|
455
|
+
return svg;
|
|
456
|
+
}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/nodes/DetailsNode.ts
|
|
459
|
+
var ChevronRightIconNode = [["path", { d: "M8 6L12 10L8 14" }]];
|
|
460
|
+
var DetailsNode = class DetailsNode extends ElementNode {
|
|
461
|
+
static getType() {
|
|
462
|
+
return "details";
|
|
463
|
+
}
|
|
464
|
+
static clone(node) {
|
|
465
|
+
return new DetailsNode(node.__summary, node.__open, node.__key);
|
|
466
|
+
}
|
|
467
|
+
constructor(summary, open = false, key) {
|
|
468
|
+
super(key);
|
|
469
|
+
_defineProperty(this, "__summary", void 0);
|
|
470
|
+
_defineProperty(this, "__open", void 0);
|
|
471
|
+
this.__summary = summary;
|
|
472
|
+
this.__open = open;
|
|
473
|
+
}
|
|
474
|
+
createDOM(_config) {
|
|
475
|
+
const details = document.createElement("details");
|
|
476
|
+
details.className = `${detailsClassNames.details} ${detailsStyles.details}`;
|
|
477
|
+
if (this.__open) details.open = true;
|
|
478
|
+
const summary = document.createElement("summary");
|
|
479
|
+
summary.className = `${detailsClassNames.summary} ${detailsStyles.summary}`;
|
|
480
|
+
const chevron = document.createElement("span");
|
|
481
|
+
chevron.className = `${detailsClassNames.chevron} ${detailsStyles.chevron}`;
|
|
482
|
+
chevron.setAttribute("aria-hidden", "true");
|
|
483
|
+
chevron.append(createLucideSvg(ChevronRightIconNode, {
|
|
484
|
+
"width": "20",
|
|
485
|
+
"height": "20",
|
|
486
|
+
"viewBox": "0 0 20 20",
|
|
487
|
+
"stroke-width": "1.5"
|
|
488
|
+
}));
|
|
489
|
+
summary.append(chevron);
|
|
490
|
+
const label = document.createElement("span");
|
|
491
|
+
label.className = `${detailsClassNames.summaryText} ${detailsStyles.summaryText}`;
|
|
492
|
+
label.textContent = this.__summary;
|
|
493
|
+
summary.append(label);
|
|
494
|
+
const content = document.createElement("div");
|
|
495
|
+
content.className = `${detailsClassNames.content} ${detailsStyles.content}`;
|
|
496
|
+
details.append(summary, content);
|
|
497
|
+
return details;
|
|
498
|
+
}
|
|
499
|
+
updateDOM(prevNode, dom) {
|
|
500
|
+
const details = dom;
|
|
501
|
+
if (prevNode.__open !== this.__open) details.open = this.__open;
|
|
502
|
+
if (prevNode.__summary !== this.__summary) {
|
|
503
|
+
const label = dom.querySelector(`.${detailsClassNames.summaryText}`);
|
|
504
|
+
if (label) label.textContent = this.__summary;
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
static importJSON(serializedNode) {
|
|
509
|
+
return $createDetailsNode(serializedNode.summary, serializedNode.open);
|
|
510
|
+
}
|
|
511
|
+
exportJSON() {
|
|
512
|
+
return {
|
|
513
|
+
...super.exportJSON(),
|
|
514
|
+
type: "details",
|
|
515
|
+
summary: this.__summary,
|
|
516
|
+
open: this.__open,
|
|
517
|
+
version: 1
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
getSummary() {
|
|
521
|
+
return this.getLatest().__summary;
|
|
522
|
+
}
|
|
523
|
+
setSummary(summary) {
|
|
524
|
+
const writable = this.getWritable();
|
|
525
|
+
writable.__summary = summary;
|
|
526
|
+
}
|
|
527
|
+
getOpen() {
|
|
528
|
+
return this.getLatest().__open;
|
|
529
|
+
}
|
|
530
|
+
setOpen(open) {
|
|
531
|
+
const writable = this.getWritable();
|
|
532
|
+
writable.__open = open;
|
|
533
|
+
}
|
|
534
|
+
toggleOpen() {
|
|
535
|
+
this.setOpen(!this.getOpen());
|
|
536
|
+
}
|
|
537
|
+
getDOMSlot(element) {
|
|
538
|
+
const content = element.querySelector(`.${detailsClassNames.content}`);
|
|
539
|
+
return super.getDOMSlot(element).withElement(content);
|
|
540
|
+
}
|
|
541
|
+
isInline() {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
_defineProperty(DetailsNode, "slashMenuItems", [{
|
|
546
|
+
title: "Details",
|
|
547
|
+
icon: createElement(ChevronRight, { size: 20 }),
|
|
548
|
+
description: "Collapsible content block",
|
|
549
|
+
keywords: [
|
|
550
|
+
"details",
|
|
551
|
+
"toggle",
|
|
552
|
+
"collapse",
|
|
553
|
+
"accordion"
|
|
554
|
+
],
|
|
555
|
+
section: "ADVANCED",
|
|
556
|
+
onSelect: (editor) => {
|
|
557
|
+
editor.update(() => {
|
|
558
|
+
$insertNodes([$createDetailsNode("Details")]);
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}]);
|
|
562
|
+
function $createDetailsNode(summary, open = false) {
|
|
563
|
+
return new DetailsNode(summary, open);
|
|
564
|
+
}
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region src/components/renderers/FootnoteSectionRenderer.tsx
|
|
567
|
+
function FootnoteSectionRenderer({ definitions }) {
|
|
568
|
+
const { displayNumberMap } = useFootnoteDefinitions();
|
|
569
|
+
const sortedEntries = Object.entries(definitions).sort(([a], [b]) => (displayNumberMap[a] ?? 0) - (displayNumberMap[b] ?? 0));
|
|
570
|
+
if (sortedEntries.length === 0) return null;
|
|
571
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
572
|
+
role: "doc-endnotes",
|
|
573
|
+
className: clsx("rich-footnote-section-content", semanticClassNames.footnoteSection, sharedStyles.footnoteSection),
|
|
574
|
+
children: [/* @__PURE__ */ jsx("hr", { className: clsx(semanticClassNames.footnoteSectionDivider, sharedStyles.footnoteSectionDivider) }), /* @__PURE__ */ jsx("ol", {
|
|
575
|
+
className: clsx(semanticClassNames.footnoteSectionList, sharedStyles.footnoteSectionList),
|
|
576
|
+
children: sortedEntries.map(([identifier, content]) => {
|
|
577
|
+
return /* @__PURE__ */ jsx(FootnoteSectionItem, {
|
|
578
|
+
content,
|
|
579
|
+
displayNum: displayNumberMap[identifier] ?? identifier,
|
|
580
|
+
identifier
|
|
581
|
+
}, identifier);
|
|
582
|
+
})
|
|
583
|
+
})]
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
function FootnoteSectionItem({ identifier, content, displayNum }) {
|
|
587
|
+
const targetId = `footnote-${identifier}`;
|
|
588
|
+
const refId = `footnote-ref-${identifier}`;
|
|
589
|
+
const handleBackClick = useCallback((e) => {
|
|
590
|
+
e.preventDefault();
|
|
591
|
+
const refElement = document.getElementById(refId);
|
|
592
|
+
if (!refElement) return;
|
|
593
|
+
refElement.scrollIntoView({
|
|
594
|
+
behavior: "smooth",
|
|
595
|
+
block: "center"
|
|
596
|
+
});
|
|
597
|
+
refElement.classList.add(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
|
|
598
|
+
window.setTimeout(() => {
|
|
599
|
+
refElement.classList.remove(semanticClassNames.footnoteHighlight, sharedStyles.footnoteHighlight);
|
|
600
|
+
}, 1200);
|
|
601
|
+
}, [refId]);
|
|
602
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
603
|
+
className: clsx(semanticClassNames.footnoteSectionItem, sharedStyles.footnoteSectionItem),
|
|
604
|
+
id: targetId,
|
|
605
|
+
value: typeof displayNum === "number" ? displayNum : void 0,
|
|
606
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
607
|
+
className: "rich-footnote-section-item-content",
|
|
608
|
+
children: content
|
|
609
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
610
|
+
"aria-label": `Back to reference ${displayNum}`,
|
|
611
|
+
className: clsx(semanticClassNames.footnoteBackRef, sharedStyles.footnoteBackRef),
|
|
612
|
+
href: `#${refId}`,
|
|
613
|
+
role: "doc-backlink",
|
|
614
|
+
onClick: handleBackClick,
|
|
615
|
+
children: "↩"
|
|
616
|
+
})]
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/nodes/FootnoteSectionNode.ts
|
|
621
|
+
var FootnoteSectionNode = class FootnoteSectionNode extends DecoratorNode {
|
|
622
|
+
static getType() {
|
|
623
|
+
return "footnote-section";
|
|
624
|
+
}
|
|
625
|
+
static clone(node) {
|
|
626
|
+
return new FootnoteSectionNode({ ...node.__definitions }, node.__key);
|
|
627
|
+
}
|
|
628
|
+
constructor(definitions, key) {
|
|
629
|
+
super(key);
|
|
630
|
+
_defineProperty(this, "__definitions", void 0);
|
|
631
|
+
this.__definitions = definitions;
|
|
632
|
+
}
|
|
633
|
+
createDOM(_config) {
|
|
634
|
+
const div = document.createElement("div");
|
|
635
|
+
div.className = `${semanticClassNames.footnoteSection} ${sharedStyles.footnoteSection}`;
|
|
636
|
+
return div;
|
|
637
|
+
}
|
|
638
|
+
updateDOM() {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
isInline() {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
static importJSON(serializedNode) {
|
|
645
|
+
return $createFootnoteSectionNode(serializedNode.definitions);
|
|
646
|
+
}
|
|
647
|
+
exportJSON() {
|
|
648
|
+
return {
|
|
649
|
+
...super.exportJSON(),
|
|
650
|
+
type: "footnote-section",
|
|
651
|
+
definitions: this.__definitions,
|
|
652
|
+
version: 1
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
getDefinitions() {
|
|
656
|
+
return this.getLatest().__definitions;
|
|
657
|
+
}
|
|
658
|
+
setDefinitions(definitions) {
|
|
659
|
+
const writable = this.getWritable();
|
|
660
|
+
writable.__definitions = definitions;
|
|
661
|
+
}
|
|
662
|
+
getDefinition(identifier) {
|
|
663
|
+
return this.getLatest().__definitions[identifier];
|
|
664
|
+
}
|
|
665
|
+
setDefinition(identifier, content) {
|
|
666
|
+
const writable = this.getWritable();
|
|
667
|
+
writable.__definitions = {
|
|
668
|
+
...writable.__definitions,
|
|
669
|
+
[identifier]: content
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
removeDefinition(identifier) {
|
|
673
|
+
const writable = this.getWritable();
|
|
674
|
+
const { [identifier]: _, ...rest } = writable.__definitions;
|
|
675
|
+
writable.__definitions = rest;
|
|
676
|
+
}
|
|
677
|
+
decorate(_editor, _config) {
|
|
678
|
+
return createRendererDecoration("FootnoteSection", FootnoteSectionRenderer, {
|
|
679
|
+
definitions: this.__definitions,
|
|
680
|
+
nodeKey: this.__key
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
function $createFootnoteSectionNode(definitions = {}) {
|
|
685
|
+
return new FootnoteSectionNode(definitions);
|
|
686
|
+
}
|
|
687
|
+
function $isFootnoteSectionNode(node) {
|
|
688
|
+
return node instanceof FootnoteSectionNode;
|
|
689
|
+
}
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/components/renderers/GridStaticDecorator.tsx
|
|
692
|
+
function GridStaticDecorator({ cols, gap, cellStates }) {
|
|
693
|
+
const renderContent = useNestedContentRenderer();
|
|
694
|
+
return /* @__PURE__ */ jsx("div", {
|
|
695
|
+
className: clsx(gridClassNames.inner, gridStyles.inner),
|
|
696
|
+
style: {
|
|
697
|
+
display: "grid",
|
|
698
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
699
|
+
gap
|
|
700
|
+
},
|
|
701
|
+
children: cellStates.map((state, i) => /* @__PURE__ */ jsx("div", {
|
|
702
|
+
className: clsx(gridClassNames.cell, gridStyles.cell),
|
|
703
|
+
children: renderContent(state)
|
|
704
|
+
}, i))
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/nodes/GridContainerNode.ts
|
|
709
|
+
var GridContainerNode = class GridContainerNode extends DecoratorNode {
|
|
710
|
+
static getType() {
|
|
711
|
+
return "grid-container";
|
|
712
|
+
}
|
|
713
|
+
static clone(node) {
|
|
714
|
+
return new GridContainerNode(node.__cols, node.__gap, [...node.__cellStates], node.__key);
|
|
715
|
+
}
|
|
716
|
+
constructor(cols = 2, gap, cellStates, key) {
|
|
717
|
+
super(key);
|
|
718
|
+
_defineProperty(this, "__cols", void 0);
|
|
719
|
+
_defineProperty(this, "__gap", void 0);
|
|
720
|
+
_defineProperty(this, "__cellStates", void 0);
|
|
721
|
+
this.__cols = cols;
|
|
722
|
+
this.__gap = gap || "16px";
|
|
723
|
+
if (cellStates) this.__cellStates = cellStates;
|
|
724
|
+
else {
|
|
725
|
+
const emptyState = { root: {
|
|
726
|
+
children: [{
|
|
727
|
+
type: "paragraph",
|
|
728
|
+
children: [],
|
|
729
|
+
direction: null,
|
|
730
|
+
format: "",
|
|
731
|
+
indent: 0,
|
|
732
|
+
textFormat: 0,
|
|
733
|
+
textStyle: "",
|
|
734
|
+
version: 1
|
|
735
|
+
}],
|
|
736
|
+
direction: null,
|
|
737
|
+
format: "",
|
|
738
|
+
indent: 0,
|
|
739
|
+
type: "root",
|
|
740
|
+
version: 1
|
|
741
|
+
} };
|
|
742
|
+
this.__cellStates = Array.from({ length: cols }, () => emptyState);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
createDOM(_config) {
|
|
746
|
+
const div = document.createElement("div");
|
|
747
|
+
div.className = `${gridClassNames.container} ${gridStyles.container}`;
|
|
748
|
+
return div;
|
|
749
|
+
}
|
|
750
|
+
updateDOM() {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
isInline() {
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
getCols() {
|
|
757
|
+
return this.getLatest().__cols;
|
|
758
|
+
}
|
|
759
|
+
setCols(cols) {
|
|
760
|
+
const writable = this.getWritable();
|
|
761
|
+
const prev = writable.__cellStates.length;
|
|
762
|
+
writable.__cols = cols;
|
|
763
|
+
if (cols > prev) {
|
|
764
|
+
const emptyState = { root: {
|
|
765
|
+
children: [{
|
|
766
|
+
type: "paragraph",
|
|
767
|
+
children: [],
|
|
768
|
+
direction: null,
|
|
769
|
+
format: "",
|
|
770
|
+
indent: 0,
|
|
771
|
+
textFormat: 0,
|
|
772
|
+
textStyle: "",
|
|
773
|
+
version: 1
|
|
774
|
+
}],
|
|
775
|
+
direction: null,
|
|
776
|
+
format: "",
|
|
777
|
+
indent: 0,
|
|
778
|
+
type: "root",
|
|
779
|
+
version: 1
|
|
780
|
+
} };
|
|
781
|
+
for (let i = prev; i < cols; i++) writable.__cellStates.push(emptyState);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
getGap() {
|
|
785
|
+
return this.getLatest().__gap;
|
|
786
|
+
}
|
|
787
|
+
setGap(gap) {
|
|
788
|
+
const writable = this.getWritable();
|
|
789
|
+
writable.__gap = gap;
|
|
790
|
+
}
|
|
791
|
+
getCellStates() {
|
|
792
|
+
return this.getLatest().__cellStates;
|
|
793
|
+
}
|
|
794
|
+
addCells(count) {
|
|
795
|
+
const writable = this.getWritable();
|
|
796
|
+
const emptyState = { root: {
|
|
797
|
+
children: [{
|
|
798
|
+
type: "paragraph",
|
|
799
|
+
children: [],
|
|
800
|
+
direction: null,
|
|
801
|
+
format: "",
|
|
802
|
+
indent: 0,
|
|
803
|
+
textFormat: 0,
|
|
804
|
+
textStyle: "",
|
|
805
|
+
version: 1
|
|
806
|
+
}],
|
|
807
|
+
direction: null,
|
|
808
|
+
format: "",
|
|
809
|
+
indent: 0,
|
|
810
|
+
type: "root",
|
|
811
|
+
version: 1
|
|
812
|
+
} };
|
|
813
|
+
for (let i = 0; i < count; i++) writable.__cellStates.push(emptyState);
|
|
814
|
+
}
|
|
815
|
+
removeCells(count) {
|
|
816
|
+
const states = this.getWritable().__cellStates;
|
|
817
|
+
const toRemove = Math.min(count, states.length);
|
|
818
|
+
for (let i = 0; i < toRemove; i++) {
|
|
819
|
+
const state = states.at(-1);
|
|
820
|
+
if (!state) break;
|
|
821
|
+
if (!(extractTextContent(state).trim() === "")) break;
|
|
822
|
+
states.pop();
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
getTextContent() {
|
|
826
|
+
return this.__cellStates.map((s) => extractTextContent(s)).join("\n");
|
|
827
|
+
}
|
|
828
|
+
static importJSON(serializedNode) {
|
|
829
|
+
const legacy = serializedNode;
|
|
830
|
+
const cols = legacy.cols || 2;
|
|
831
|
+
const rawGap = legacy.gap;
|
|
832
|
+
const gap = typeof rawGap === "number" ? `${rawGap}px` : rawGap;
|
|
833
|
+
if (legacy.cells && legacy.cells.length > 0) return new GridContainerNode(cols, gap, legacy.cells);
|
|
834
|
+
if (legacy.children) return new GridContainerNode(cols, gap, legacy.children.map((child) => {
|
|
835
|
+
return { root: {
|
|
836
|
+
children: [child],
|
|
837
|
+
direction: null,
|
|
838
|
+
format: "",
|
|
839
|
+
indent: 0,
|
|
840
|
+
type: "root",
|
|
841
|
+
version: 1
|
|
842
|
+
} };
|
|
843
|
+
}));
|
|
844
|
+
return new GridContainerNode(cols, gap);
|
|
845
|
+
}
|
|
846
|
+
exportJSON() {
|
|
847
|
+
return {
|
|
848
|
+
...super.exportJSON(),
|
|
849
|
+
type: "grid-container",
|
|
850
|
+
cols: this.__cols,
|
|
851
|
+
gap: this.__gap,
|
|
852
|
+
cells: this.__cellStates,
|
|
853
|
+
version: 1
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
decorate(_editor, _config) {
|
|
857
|
+
return createElement(GridStaticDecorator, {
|
|
858
|
+
cols: this.__cols,
|
|
859
|
+
gap: this.__gap,
|
|
860
|
+
cellStates: this.__cellStates
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
function $createGridContainerNode(cols = 2, gap) {
|
|
865
|
+
return new GridContainerNode(cols, gap);
|
|
866
|
+
}
|
|
867
|
+
function $isGridContainerNode(node) {
|
|
868
|
+
return node instanceof GridContainerNode;
|
|
869
|
+
}
|
|
870
|
+
//#endregion
|
|
871
|
+
//#region src/nodes/LinkCardNode.ts
|
|
872
|
+
var LinkCardNode = class LinkCardNode extends DecoratorNode {
|
|
873
|
+
static getType() {
|
|
874
|
+
return "link-card";
|
|
875
|
+
}
|
|
876
|
+
static clone(node) {
|
|
877
|
+
return new LinkCardNode({
|
|
878
|
+
url: node.__url,
|
|
879
|
+
source: node.__source,
|
|
880
|
+
id: node.__id,
|
|
881
|
+
title: node.__title,
|
|
882
|
+
description: node.__description,
|
|
883
|
+
favicon: node.__favicon,
|
|
884
|
+
image: node.__image
|
|
885
|
+
}, node.__key);
|
|
886
|
+
}
|
|
887
|
+
constructor(payload, key) {
|
|
888
|
+
super(key);
|
|
889
|
+
_defineProperty(this, "__url", void 0);
|
|
890
|
+
_defineProperty(this, "__source", void 0);
|
|
891
|
+
_defineProperty(this, "__id", void 0);
|
|
892
|
+
_defineProperty(this, "__title", void 0);
|
|
893
|
+
_defineProperty(this, "__description", void 0);
|
|
894
|
+
_defineProperty(this, "__favicon", void 0);
|
|
895
|
+
_defineProperty(this, "__image", void 0);
|
|
896
|
+
this.__url = payload.url;
|
|
897
|
+
this.__source = payload.source;
|
|
898
|
+
this.__id = payload.id;
|
|
899
|
+
this.__title = payload.title;
|
|
900
|
+
this.__description = payload.description;
|
|
901
|
+
this.__favicon = payload.favicon;
|
|
902
|
+
this.__image = payload.image;
|
|
903
|
+
}
|
|
904
|
+
createDOM(_config) {
|
|
905
|
+
const div = document.createElement("div");
|
|
906
|
+
div.className = "rich-link-card-wrapper";
|
|
907
|
+
return div;
|
|
908
|
+
}
|
|
909
|
+
updateDOM() {
|
|
910
|
+
return false;
|
|
911
|
+
}
|
|
912
|
+
isInline() {
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
static importJSON(serializedNode) {
|
|
916
|
+
return $createLinkCardNode({
|
|
917
|
+
url: serializedNode.url,
|
|
918
|
+
source: serializedNode.source,
|
|
919
|
+
id: serializedNode.id,
|
|
920
|
+
title: serializedNode.title,
|
|
921
|
+
description: serializedNode.description,
|
|
922
|
+
favicon: serializedNode.favicon,
|
|
923
|
+
image: serializedNode.image
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
exportJSON() {
|
|
927
|
+
return {
|
|
928
|
+
...super.exportJSON(),
|
|
929
|
+
type: "link-card",
|
|
930
|
+
url: this.__url,
|
|
931
|
+
source: this.__source,
|
|
932
|
+
id: this.__id,
|
|
933
|
+
title: this.__title,
|
|
934
|
+
description: this.__description,
|
|
935
|
+
favicon: this.__favicon,
|
|
936
|
+
image: this.__image,
|
|
937
|
+
version: 1
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
getUrl() {
|
|
941
|
+
return this.getLatest().__url;
|
|
942
|
+
}
|
|
943
|
+
setUrl(url) {
|
|
944
|
+
const writable = this.getWritable();
|
|
945
|
+
writable.__url = url;
|
|
946
|
+
}
|
|
947
|
+
getSource() {
|
|
948
|
+
return this.getLatest().__source;
|
|
949
|
+
}
|
|
950
|
+
setSource(source) {
|
|
951
|
+
const writable = this.getWritable();
|
|
952
|
+
writable.__source = source;
|
|
953
|
+
}
|
|
954
|
+
getId() {
|
|
955
|
+
return this.getLatest().__id;
|
|
956
|
+
}
|
|
957
|
+
setId(id) {
|
|
958
|
+
const writable = this.getWritable();
|
|
959
|
+
writable.__id = id;
|
|
960
|
+
}
|
|
961
|
+
decorate(_editor, _config) {
|
|
962
|
+
return createRendererDecoration("LinkCard", LinkCardRenderer, {
|
|
963
|
+
url: this.__url,
|
|
964
|
+
source: this.__source,
|
|
965
|
+
id: this.__id,
|
|
966
|
+
title: this.__title,
|
|
967
|
+
description: this.__description,
|
|
968
|
+
favicon: this.__favicon,
|
|
969
|
+
image: this.__image
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
_defineProperty(LinkCardNode, "commandItems", [{
|
|
974
|
+
title: "Link Card",
|
|
975
|
+
icon: createElement(Link, { size: 20 }),
|
|
976
|
+
description: "Link preview card",
|
|
977
|
+
keywords: [
|
|
978
|
+
"link",
|
|
979
|
+
"card",
|
|
980
|
+
"bookmark",
|
|
981
|
+
"embed"
|
|
982
|
+
],
|
|
983
|
+
section: "MEDIA",
|
|
984
|
+
placement: ["slash", "toolbar"],
|
|
985
|
+
group: "insert",
|
|
986
|
+
onSelect: (editor) => {
|
|
987
|
+
editor.update(() => {
|
|
988
|
+
$insertNodes([$createLinkCardNode({ url: "" })]);
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}]);
|
|
992
|
+
function $createLinkCardNode(payload) {
|
|
993
|
+
return new LinkCardNode(payload);
|
|
994
|
+
}
|
|
995
|
+
function $isLinkCardNode(node) {
|
|
996
|
+
return node instanceof LinkCardNode;
|
|
997
|
+
}
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region src/nodes/RubyNode.ts
|
|
1000
|
+
function readBaseTextFromRuby(element) {
|
|
1001
|
+
let base = "";
|
|
1002
|
+
for (const child of Array.from(element.childNodes)) {
|
|
1003
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
1004
|
+
base += child.textContent ?? "";
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
|
1008
|
+
const childElement = child;
|
|
1009
|
+
const tag = childElement.tagName.toLowerCase();
|
|
1010
|
+
if (tag === "rt" || tag === "rp") continue;
|
|
1011
|
+
base += childElement.textContent ?? "";
|
|
1012
|
+
}
|
|
1013
|
+
return base;
|
|
1014
|
+
}
|
|
1015
|
+
var RubyNode = class RubyNode extends ElementNode {
|
|
1016
|
+
static getType() {
|
|
1017
|
+
return "ruby";
|
|
1018
|
+
}
|
|
1019
|
+
static clone(node) {
|
|
1020
|
+
return new RubyNode(node.__reading, node.__key);
|
|
1021
|
+
}
|
|
1022
|
+
constructor(reading, key) {
|
|
1023
|
+
super(key);
|
|
1024
|
+
_defineProperty(this, "__reading", void 0);
|
|
1025
|
+
this.__reading = reading;
|
|
1026
|
+
}
|
|
1027
|
+
static importJSON(serializedNode) {
|
|
1028
|
+
return $createRubyNode(serializedNode.reading ?? "");
|
|
1029
|
+
}
|
|
1030
|
+
exportJSON() {
|
|
1031
|
+
return {
|
|
1032
|
+
...super.exportJSON(),
|
|
1033
|
+
type: "ruby",
|
|
1034
|
+
reading: this.__reading,
|
|
1035
|
+
version: 1
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
static importDOM() {
|
|
1039
|
+
return { ruby: () => ({
|
|
1040
|
+
conversion: (domNode) => {
|
|
1041
|
+
if (!(domNode instanceof HTMLElement)) return null;
|
|
1042
|
+
const reading = domNode.querySelector("rt")?.textContent ?? "";
|
|
1043
|
+
const baseText = readBaseTextFromRuby(domNode);
|
|
1044
|
+
const node = $createRubyNode(reading);
|
|
1045
|
+
if (baseText) node.append($createTextNode(baseText));
|
|
1046
|
+
return { node };
|
|
1047
|
+
},
|
|
1048
|
+
priority: 2
|
|
1049
|
+
}) };
|
|
1050
|
+
}
|
|
1051
|
+
exportDOM() {
|
|
1052
|
+
const ruby = document.createElement("ruby");
|
|
1053
|
+
ruby.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
|
|
1054
|
+
const baseText = this.getTextContent();
|
|
1055
|
+
if (baseText) ruby.append(baseText);
|
|
1056
|
+
if (this.__reading) {
|
|
1057
|
+
const rt = document.createElement("rt");
|
|
1058
|
+
rt.className = `${semanticClassNames.rubyRt} ${sharedStyles.rubyRt}`;
|
|
1059
|
+
rt.textContent = this.__reading;
|
|
1060
|
+
ruby.append(rt);
|
|
1061
|
+
}
|
|
1062
|
+
return { element: ruby };
|
|
1063
|
+
}
|
|
1064
|
+
createDOM(_config) {
|
|
1065
|
+
const span = document.createElement("span");
|
|
1066
|
+
span.className = `${semanticClassNames.ruby} ${sharedStyles.ruby}`;
|
|
1067
|
+
if (this.__reading) span.dataset.ruby = this.__reading;
|
|
1068
|
+
return span;
|
|
1069
|
+
}
|
|
1070
|
+
updateDOM(prevNode, dom) {
|
|
1071
|
+
if (prevNode.__reading !== this.__reading) if (this.__reading) dom.dataset.ruby = this.__reading;
|
|
1072
|
+
else delete dom.dataset.ruby;
|
|
1073
|
+
return false;
|
|
1074
|
+
}
|
|
1075
|
+
canInsertTextBefore() {
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
canInsertTextAfter() {
|
|
1079
|
+
return true;
|
|
1080
|
+
}
|
|
1081
|
+
isInline() {
|
|
1082
|
+
return true;
|
|
1083
|
+
}
|
|
1084
|
+
getReading() {
|
|
1085
|
+
return this.getLatest().__reading;
|
|
1086
|
+
}
|
|
1087
|
+
setReading(reading) {
|
|
1088
|
+
const writable = this.getWritable();
|
|
1089
|
+
writable.__reading = reading;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
function $createRubyNode(reading) {
|
|
1093
|
+
return new RubyNode(reading);
|
|
1094
|
+
}
|
|
1095
|
+
function $isRubyNode(node) {
|
|
1096
|
+
return node instanceof RubyNode;
|
|
1097
|
+
}
|
|
1098
|
+
//#endregion
|
|
1099
|
+
//#region src/components/renderers/VideoRenderer.tsx
|
|
1100
|
+
function VideoRenderer({ src, poster, width, height }) {
|
|
1101
|
+
return /* @__PURE__ */ jsx("figure", {
|
|
1102
|
+
className: "rich-video",
|
|
1103
|
+
children: /* @__PURE__ */ jsx("video", {
|
|
1104
|
+
controls: true,
|
|
1105
|
+
height,
|
|
1106
|
+
poster,
|
|
1107
|
+
preload: "metadata",
|
|
1108
|
+
src,
|
|
1109
|
+
style: {
|
|
1110
|
+
maxWidth: "100%",
|
|
1111
|
+
height: "auto"
|
|
1112
|
+
},
|
|
1113
|
+
width
|
|
1114
|
+
})
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
//#endregion
|
|
1118
|
+
//#region src/nodes/VideoNode.ts
|
|
1119
|
+
var VideoNode = class VideoNode extends DecoratorNode {
|
|
1120
|
+
static getType() {
|
|
1121
|
+
return "video";
|
|
1122
|
+
}
|
|
1123
|
+
static clone(node) {
|
|
1124
|
+
return new VideoNode({
|
|
1125
|
+
src: node.__src,
|
|
1126
|
+
poster: node.__poster,
|
|
1127
|
+
width: node.__width,
|
|
1128
|
+
height: node.__height
|
|
1129
|
+
}, node.__key);
|
|
1130
|
+
}
|
|
1131
|
+
constructor(payload, key) {
|
|
1132
|
+
super(key);
|
|
1133
|
+
_defineProperty(this, "__src", void 0);
|
|
1134
|
+
_defineProperty(this, "__poster", void 0);
|
|
1135
|
+
_defineProperty(this, "__width", void 0);
|
|
1136
|
+
_defineProperty(this, "__height", void 0);
|
|
1137
|
+
this.__src = payload.src;
|
|
1138
|
+
this.__poster = payload.poster;
|
|
1139
|
+
this.__width = payload.width;
|
|
1140
|
+
this.__height = payload.height;
|
|
1141
|
+
}
|
|
1142
|
+
createDOM(_config) {
|
|
1143
|
+
const div = document.createElement("div");
|
|
1144
|
+
div.className = "rich-video-wrapper";
|
|
1145
|
+
return div;
|
|
1146
|
+
}
|
|
1147
|
+
updateDOM() {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
isInline() {
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
static importJSON(serializedNode) {
|
|
1154
|
+
return $createVideoNode({
|
|
1155
|
+
src: serializedNode.src,
|
|
1156
|
+
poster: serializedNode.poster,
|
|
1157
|
+
width: serializedNode.width,
|
|
1158
|
+
height: serializedNode.height
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
exportJSON() {
|
|
1162
|
+
return {
|
|
1163
|
+
...super.exportJSON(),
|
|
1164
|
+
type: "video",
|
|
1165
|
+
src: this.__src,
|
|
1166
|
+
poster: this.__poster,
|
|
1167
|
+
width: this.__width,
|
|
1168
|
+
height: this.__height,
|
|
1169
|
+
version: 1
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
getSrc() {
|
|
1173
|
+
return this.getLatest().__src;
|
|
1174
|
+
}
|
|
1175
|
+
setSrc(src) {
|
|
1176
|
+
const writable = this.getWritable();
|
|
1177
|
+
writable.__src = src;
|
|
1178
|
+
}
|
|
1179
|
+
decorate(_editor, _config) {
|
|
1180
|
+
return createRendererDecoration("Video", VideoRenderer, {
|
|
1181
|
+
src: this.__src,
|
|
1182
|
+
poster: this.__poster,
|
|
1183
|
+
width: this.__width,
|
|
1184
|
+
height: this.__height
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
_defineProperty(VideoNode, "commandItems", [{
|
|
1189
|
+
title: "Video",
|
|
1190
|
+
icon: createElement(Video, { size: 20 }),
|
|
1191
|
+
description: "Embed a video",
|
|
1192
|
+
keywords: [
|
|
1193
|
+
"video",
|
|
1194
|
+
"media",
|
|
1195
|
+
"mp4"
|
|
1196
|
+
],
|
|
1197
|
+
section: "MEDIA",
|
|
1198
|
+
placement: ["slash", "toolbar"],
|
|
1199
|
+
group: "insert",
|
|
1200
|
+
onSelect: (editor) => {
|
|
1201
|
+
editor.update(() => {
|
|
1202
|
+
$insertNodes([$createVideoNode({ src: "" })]);
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}]);
|
|
1206
|
+
function $createVideoNode(payload) {
|
|
1207
|
+
return new VideoNode(payload);
|
|
1208
|
+
}
|
|
1209
|
+
//#endregion
|
|
1210
|
+
//#region src/config.ts
|
|
1211
|
+
var builtinNodes = [
|
|
1212
|
+
HeadingNode,
|
|
1213
|
+
QuoteNode,
|
|
1214
|
+
ListNode,
|
|
1215
|
+
ListItemNode,
|
|
1216
|
+
LinkNode,
|
|
1217
|
+
AutoLinkNode,
|
|
1218
|
+
HorizontalRuleNode,
|
|
1219
|
+
TableNode,
|
|
1220
|
+
TableCellNode,
|
|
1221
|
+
TableRowNode,
|
|
1222
|
+
CodeNode
|
|
1223
|
+
];
|
|
1224
|
+
var customNodes = [
|
|
1225
|
+
SpoilerNode,
|
|
1226
|
+
MentionNode,
|
|
1227
|
+
KaTeXInlineNode,
|
|
1228
|
+
KaTeXBlockNode,
|
|
1229
|
+
ImageNode,
|
|
1230
|
+
AlertQuoteNode,
|
|
1231
|
+
CodeBlockNode,
|
|
1232
|
+
FootnoteNode,
|
|
1233
|
+
FootnoteSectionNode,
|
|
1234
|
+
VideoNode,
|
|
1235
|
+
LinkCardNode,
|
|
1236
|
+
CommentNode,
|
|
1237
|
+
DetailsNode,
|
|
1238
|
+
GridContainerNode,
|
|
1239
|
+
BannerNode,
|
|
1240
|
+
MermaidNode,
|
|
1241
|
+
RubyNode,
|
|
1242
|
+
TagNode
|
|
1243
|
+
];
|
|
1244
|
+
var allNodes = [...builtinNodes, ...customNodes];
|
|
1245
|
+
//#endregion
|
|
1246
|
+
export { BANNER_TYPES as A, $isCodeBlockNode as C, useColorScheme as D, ColorSchemeProvider as E, normalizeBannerType as M, BannerRenderer as N, $isBannerNode as O, CommentNode as S, CodeBlockRenderer as T, $createDetailsNode as _, $createRubyNode as a, $createCommentPlaceholderNode as b, $createLinkCardNode as c, $createGridContainerNode as d, $isGridContainerNode as f, FootnoteSectionNode as g, $isFootnoteSectionNode as h, VideoNode as i, BannerNode as j, BANNER_LABELS as k, $isLinkCardNode as l, $createFootnoteSectionNode as m, builtinNodes as n, $isRubyNode as o, GridContainerNode as p, customNodes as r, RubyNode as s, allNodes as t, LinkCardNode as u, DetailsNode as v, CodeBlockNode as w, $isCommentNode as x, $createCommentNode as y };
|