@lobehub/editor 4.6.1 → 4.6.2
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/es/renderer.js +10 -211
- package/package.json +1 -1
package/es/renderer.js
CHANGED
|
@@ -4,19 +4,17 @@ import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
|
4
4
|
import { $getRoot, $isElementNode, $isTextNode } from "lexical";
|
|
5
5
|
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { createElement,
|
|
8
|
-
import {
|
|
7
|
+
import { createElement, useMemo } from "react";
|
|
8
|
+
import { Highlighter, Mermaid } from "@lobehub/ui";
|
|
9
9
|
import katex from "katex";
|
|
10
10
|
import { createStaticStyles, cx } from "antd-style";
|
|
11
11
|
import { ListItemNode, ListNode } from "@lexical/list";
|
|
12
|
-
import {
|
|
13
|
-
import { createHighlighterCoreSync, isSpecialLang, stringifyTokenStyle } from "@shikijs/core";
|
|
12
|
+
import { createHighlighterCoreSync, isSpecialLang } from "@shikijs/core";
|
|
14
13
|
import { createJavaScriptRegexEngine } from "@shikijs/engine-javascript";
|
|
15
14
|
import { bundledLanguagesInfo } from "shiki";
|
|
16
15
|
import { createHeadlessEditor } from "@lexical/headless";
|
|
17
16
|
//#region src/renderer/engine/shiki.ts
|
|
18
17
|
let _highlighter = null;
|
|
19
|
-
let _themeLoaded = false;
|
|
20
18
|
function getHighlighter() {
|
|
21
19
|
if (!_highlighter) _highlighter = createHighlighterCoreSync({
|
|
22
20
|
engine: createJavaScriptRegexEngine(),
|
|
@@ -25,63 +23,15 @@ function getHighlighter() {
|
|
|
25
23
|
});
|
|
26
24
|
return _highlighter;
|
|
27
25
|
}
|
|
28
|
-
async function ensureTheme(highlighter) {
|
|
29
|
-
if (_themeLoaded) return;
|
|
30
|
-
try {
|
|
31
|
-
const { ShikiLobeTheme } = await import("@lobehub/ui");
|
|
32
|
-
highlighter.loadThemeSync(ShikiLobeTheme);
|
|
33
|
-
_themeLoaded = true;
|
|
34
|
-
} catch {}
|
|
35
|
-
}
|
|
36
26
|
function isLanguageLoaded(language) {
|
|
37
27
|
if (isSpecialLang(language)) return true;
|
|
38
28
|
return getHighlighter().getLoadedLanguages().includes(language);
|
|
39
29
|
}
|
|
40
|
-
function parseInlineStyle(style) {
|
|
41
|
-
const result = {};
|
|
42
|
-
for (const part of style.split(";")) {
|
|
43
|
-
const colonIdx = part.indexOf(":");
|
|
44
|
-
if (colonIdx === -1) continue;
|
|
45
|
-
const prop = part.slice(0, colonIdx).trim();
|
|
46
|
-
const val = part.slice(colonIdx + 1).trim();
|
|
47
|
-
if (!prop || !val) continue;
|
|
48
|
-
const camelProp = prop.replaceAll(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
49
|
-
result[camelProp] = val;
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
30
|
async function loadLanguage(language) {
|
|
54
31
|
if (isLanguageLoaded(language)) return;
|
|
55
32
|
const info = bundledLanguagesInfo.find((desc) => desc.id === language || desc.aliases?.includes(language));
|
|
56
33
|
if (info) await getHighlighter().loadLanguage(info.import());
|
|
57
34
|
}
|
|
58
|
-
function highlightCode(code, language, key) {
|
|
59
|
-
if (!language || !isLanguageLoaded(language)) return null;
|
|
60
|
-
const highlighter = getHighlighter();
|
|
61
|
-
ensureTheme(highlighter);
|
|
62
|
-
const theme = _themeLoaded ? "lobe-theme" : highlighter.getLoadedThemes()[0] || "none";
|
|
63
|
-
try {
|
|
64
|
-
const { tokens } = highlighter.codeToTokens(code, {
|
|
65
|
-
lang: language,
|
|
66
|
-
theme
|
|
67
|
-
});
|
|
68
|
-
const nodes = [];
|
|
69
|
-
for (const [lineIdx, line] of tokens.entries()) {
|
|
70
|
-
if (lineIdx > 0) nodes.push(createElement("br", { key: `${key}-br-${lineIdx}` }));
|
|
71
|
-
for (const [tokenIdx, token] of line.entries()) {
|
|
72
|
-
const tokenStyle = stringifyTokenStyle(token.htmlStyle || (token.color ? `color: ${token.color}` : ""));
|
|
73
|
-
if (tokenStyle) nodes.push(createElement("span", {
|
|
74
|
-
key: `${key}-${lineIdx}-${tokenIdx}`,
|
|
75
|
-
style: parseInlineStyle(tokenStyle)
|
|
76
|
-
}, token.content));
|
|
77
|
-
else nodes.push(token.content);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return nodes;
|
|
81
|
-
} catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
35
|
//#endregion
|
|
86
36
|
//#region src/renderer/style.ts
|
|
87
37
|
const tableHeaderFix = createStaticStyles(({ css, cssVar }) => css`
|
|
@@ -375,170 +325,19 @@ function renderMermaidBlock(node, key) {
|
|
|
375
325
|
}
|
|
376
326
|
//#endregion
|
|
377
327
|
//#region src/renderer/renderers/codeblock.tsx
|
|
378
|
-
|
|
379
|
-
cursor: default;
|
|
380
|
-
|
|
381
|
-
overflow: hidden;
|
|
382
|
-
display: flex;
|
|
383
|
-
flex-direction: column;
|
|
384
|
-
align-items: center;
|
|
385
|
-
|
|
386
|
-
width: 100%;
|
|
387
|
-
margin-block: calc(var(--lobe-markdown-margin-multiple) * 0.5em);
|
|
388
|
-
border-radius: var(--lobe-markdown-border-radius);
|
|
389
|
-
|
|
390
|
-
background: ${cssVar.colorFillQuaternary};
|
|
391
|
-
|
|
392
|
-
.renderer-cm-header {
|
|
393
|
-
width: 100%;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.renderer-cm-code {
|
|
397
|
-
width: 100%;
|
|
398
|
-
border-block-start: 1px solid ${cssVar.colorFillQuaternary};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
.renderer-cm-pre {
|
|
402
|
-
overflow-x: auto;
|
|
403
|
-
|
|
404
|
-
margin: 0;
|
|
405
|
-
padding: 16px;
|
|
406
|
-
|
|
407
|
-
font-family: ${cssVar.fontFamilyCode};
|
|
408
|
-
font-size: calc(var(--lobe-markdown-font-size) * 0.85);
|
|
409
|
-
line-height: 1.6;
|
|
410
|
-
white-space: pre;
|
|
411
|
-
|
|
412
|
-
background: transparent;
|
|
413
|
-
|
|
414
|
-
code {
|
|
415
|
-
font-family: inherit;
|
|
416
|
-
font-size: inherit;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
.renderer-cm-collapsed {
|
|
421
|
-
overflow: hidden;
|
|
422
|
-
height: 0;
|
|
423
|
-
border-block-start: none;
|
|
424
|
-
}
|
|
425
|
-
`);
|
|
426
|
-
function CodeBlockCopyButton({ code }) {
|
|
427
|
-
const [copied, setCopied] = useState(false);
|
|
428
|
-
const handleCopy = useCallback(() => {
|
|
429
|
-
navigator.clipboard.writeText(code).catch(() => {});
|
|
430
|
-
setCopied(true);
|
|
431
|
-
setTimeout(() => setCopied(false), 1e3);
|
|
432
|
-
}, [code]);
|
|
433
|
-
return /* @__PURE__ */ jsx(ActionIcon, {
|
|
434
|
-
active: copied,
|
|
435
|
-
icon: copied ? Check : CopyIcon,
|
|
436
|
-
onClick: handleCopy,
|
|
437
|
-
size: "small",
|
|
438
|
-
title: "Copy"
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
function getLanguageName(lang) {
|
|
442
|
-
return {
|
|
443
|
-
bash: "Bash",
|
|
444
|
-
c: "C",
|
|
445
|
-
cpp: "C++",
|
|
446
|
-
csharp: "C#",
|
|
447
|
-
css: "CSS",
|
|
448
|
-
dart: "Dart",
|
|
449
|
-
go: "Go",
|
|
450
|
-
graphql: "GraphQL",
|
|
451
|
-
html: "HTML",
|
|
452
|
-
java: "Java",
|
|
453
|
-
javascript: "JavaScript",
|
|
454
|
-
json: "JSON",
|
|
455
|
-
jsx: "JSX",
|
|
456
|
-
kotlin: "Kotlin",
|
|
457
|
-
lua: "Lua",
|
|
458
|
-
markdown: "Markdown",
|
|
459
|
-
php: "PHP",
|
|
460
|
-
python: "Python",
|
|
461
|
-
ruby: "Ruby",
|
|
462
|
-
rust: "Rust",
|
|
463
|
-
scss: "SCSS",
|
|
464
|
-
shell: "Shell",
|
|
465
|
-
sql: "SQL",
|
|
466
|
-
swift: "Swift",
|
|
467
|
-
tsx: "TSX",
|
|
468
|
-
typescript: "TypeScript",
|
|
469
|
-
xml: "XML",
|
|
470
|
-
yaml: "YAML"
|
|
471
|
-
}[lang] || lang.charAt(0).toUpperCase() + lang.slice(1);
|
|
472
|
-
}
|
|
473
|
-
function getFileExt(lang) {
|
|
474
|
-
return {
|
|
475
|
-
bash: "sh",
|
|
476
|
-
csharp: "cs",
|
|
477
|
-
javascript: "js",
|
|
478
|
-
kotlin: "kt",
|
|
479
|
-
markdown: "md",
|
|
480
|
-
python: "py",
|
|
481
|
-
ruby: "rb",
|
|
482
|
-
rust: "rs",
|
|
483
|
-
shell: "sh",
|
|
484
|
-
swift: "swift",
|
|
485
|
-
typescript: "ts",
|
|
486
|
-
yaml: "yml"
|
|
487
|
-
}[lang] || lang;
|
|
488
|
-
}
|
|
489
|
-
function CodeBlockRenderer({ node, codeChildren }) {
|
|
328
|
+
function CodeBlockRenderer({ node }) {
|
|
490
329
|
const language = node.language || "";
|
|
491
330
|
const code = node.code || "";
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
className: useStyles$1,
|
|
331
|
+
return /* @__PURE__ */ jsx(Highlighter, {
|
|
332
|
+
defaultExpand: true,
|
|
333
|
+
language: language || "text",
|
|
496
334
|
variant: "filled",
|
|
497
|
-
children:
|
|
498
|
-
align: "center",
|
|
499
|
-
className: "renderer-cm-header",
|
|
500
|
-
horizontal: true,
|
|
501
|
-
justify: "space-between",
|
|
502
|
-
padding: 4,
|
|
503
|
-
children: [/* @__PURE__ */ jsxs(Flexbox, {
|
|
504
|
-
align: "center",
|
|
505
|
-
gap: 4,
|
|
506
|
-
horizontal: true,
|
|
507
|
-
children: [language && /* @__PURE__ */ jsx(MaterialFileTypeIcon, {
|
|
508
|
-
fallbackUnknownType: false,
|
|
509
|
-
filename: `*.${getFileExt(language)}`,
|
|
510
|
-
size: 18,
|
|
511
|
-
type: "file",
|
|
512
|
-
variant: "raw"
|
|
513
|
-
}), /* @__PURE__ */ jsx(Text, {
|
|
514
|
-
ellipsis: true,
|
|
515
|
-
fontSize: 13,
|
|
516
|
-
children: language ? getLanguageName(language) : "Plain Text"
|
|
517
|
-
})]
|
|
518
|
-
}), /* @__PURE__ */ jsxs(Flexbox, {
|
|
519
|
-
gap: 4,
|
|
520
|
-
horizontal: true,
|
|
521
|
-
children: [/* @__PURE__ */ jsx(CodeBlockCopyButton, { code }), /* @__PURE__ */ jsx(ActionIcon, {
|
|
522
|
-
icon: expand ? ChevronDown : ChevronRight,
|
|
523
|
-
onClick: () => setExpand(!expand),
|
|
524
|
-
size: "small"
|
|
525
|
-
})]
|
|
526
|
-
})]
|
|
527
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
528
|
-
className: expand ? "renderer-cm-code" : "renderer-cm-code renderer-cm-collapsed",
|
|
529
|
-
children: /* @__PURE__ */ jsx("pre", {
|
|
530
|
-
className: "renderer-cm-pre",
|
|
531
|
-
children: /* @__PURE__ */ jsx("code", { children: codeContent })
|
|
532
|
-
})
|
|
533
|
-
})]
|
|
335
|
+
children: code
|
|
534
336
|
});
|
|
535
337
|
}
|
|
536
|
-
function renderCodeBlock(node, key
|
|
338
|
+
function renderCodeBlock(node, key) {
|
|
537
339
|
if ((node.language || "").toLowerCase() === "mermaid") return renderMermaidBlock(node, key);
|
|
538
|
-
return /* @__PURE__ */ jsx(CodeBlockRenderer, {
|
|
539
|
-
codeChildren: children,
|
|
540
|
-
node
|
|
541
|
-
}, key);
|
|
340
|
+
return /* @__PURE__ */ jsx(CodeBlockRenderer, { node }, key);
|
|
542
341
|
}
|
|
543
342
|
//#endregion
|
|
544
343
|
//#region src/renderer/renderers/file.tsx
|
package/package.json
CHANGED