@haklex/rich-renderer-codeblock 0.0.82 → 0.0.84
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/CodeBlockCard.d.ts +3 -1
- package/dist/CodeBlockCard.d.ts.map +1 -1
- package/dist/CodeBlockRenderer-CJOZSBIy.js +132 -0
- package/dist/constants.mjs +70 -67
- package/dist/icons.mjs +91 -2
- package/dist/index.mjs +270 -238
- package/dist/language-DEZONKsW.js +156 -0
- package/dist/rich-renderer-codeblock.css +1 -2
- package/dist/shiki.mjs +28 -19
- package/dist/static.mjs +4 -3
- package/dist/styles.css.d.ts +1 -0
- package/dist/styles.css.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/CodeBlockRenderer-ECu_hR7l.js +0 -134
- package/dist/icons-CnFW5y6g.js +0 -230
package/dist/CodeBlockCard.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ interface CodeBlockCardProps {
|
|
|
5
5
|
collapsible?: boolean;
|
|
6
6
|
langSlot?: ReactNode;
|
|
7
7
|
language: string;
|
|
8
|
+
/** Use bodyBackgroundStatic (with paddingBlock) when true. Set by static CodeBlockRenderer. */
|
|
9
|
+
static?: boolean;
|
|
8
10
|
}
|
|
9
|
-
export declare function CodeBlockCard({ code, language, collapsible, langSlot, children, }: CodeBlockCardProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function CodeBlockCard({ code, language, collapsible, langSlot, children, static: staticMode, }: CodeBlockCardProps): import("react/jsx-runtime").JSX.Element;
|
|
10
12
|
export {};
|
|
11
13
|
//# sourceMappingURL=CodeBlockCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlockCard.d.ts","sourceRoot":"","sources":["../src/CodeBlockCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAWtD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,WAAkB,EAClB,QAAQ,EACR,QAAQ,
|
|
1
|
+
{"version":3,"file":"CodeBlockCard.d.ts","sourceRoot":"","sources":["../src/CodeBlockCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAWtD,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,WAAkB,EAClB,QAAQ,EACR,QAAQ,EACR,MAAM,EAAE,UAAkB,GAC3B,EAAE,kBAAkB,2CAkGpB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
|
3
|
+
import { Check, Copy, ChevronDown } from "lucide-react";
|
|
4
|
+
import { normalizeLanguage, getLanguageDisplayName, languageToColorMap } from "./constants.mjs";
|
|
5
|
+
import "@iconify/utils";
|
|
6
|
+
import "@iconify-json/material-icon-theme";
|
|
7
|
+
import { i as card, s as semanticClassNames, l as lang, h as hasLanguageIcon, a as LanguageIcon, j as copyButton, k as bodyBackgroundStatic, m as bodyBackground, n as expandWrap, o as expandButton, p as scroll, q as scrollCollapsed, c as lined, d as linedWithNumbers } from "./language-DEZONKsW.js";
|
|
8
|
+
import { getHighlighterWithLang, SHIKI_DUAL_THEMES } from "./shiki.mjs";
|
|
9
|
+
const CopyIcon = /* @__PURE__ */ jsx(Copy, { size: 16 });
|
|
10
|
+
const CheckIcon = /* @__PURE__ */ jsx(Check, { size: 16 });
|
|
11
|
+
const ExpandIcon = /* @__PURE__ */ jsx(ChevronDown, { size: 14 });
|
|
12
|
+
function CodeBlockCard({
|
|
13
|
+
code,
|
|
14
|
+
language,
|
|
15
|
+
collapsible = true,
|
|
16
|
+
langSlot,
|
|
17
|
+
children,
|
|
18
|
+
static: staticMode = false
|
|
19
|
+
}) {
|
|
20
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
const copyTimerRef = useRef(void 0);
|
|
23
|
+
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
24
|
+
const [isOverflow, setIsOverflow] = useState(false);
|
|
25
|
+
const scrollRef = useRef(null);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!collapsible) {
|
|
28
|
+
setIsOverflow(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const el = scrollRef.current;
|
|
32
|
+
if (!el) return;
|
|
33
|
+
const check = () => {
|
|
34
|
+
const halfVh = window.innerHeight / 2;
|
|
35
|
+
setIsOverflow(el.scrollHeight >= halfVh);
|
|
36
|
+
};
|
|
37
|
+
const raf = requestAnimationFrame(check);
|
|
38
|
+
return () => cancelAnimationFrame(raf);
|
|
39
|
+
}, [code, collapsible]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
return () => clearTimeout(copyTimerRef.current);
|
|
42
|
+
}, []);
|
|
43
|
+
const handleCopy = useCallback(() => {
|
|
44
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
45
|
+
setCopied(true);
|
|
46
|
+
clearTimeout(copyTimerRef.current);
|
|
47
|
+
copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
48
|
+
}).catch(() => {
|
|
49
|
+
});
|
|
50
|
+
}, [code]);
|
|
51
|
+
const languageLabel = getLanguageDisplayName(normalizedLanguage);
|
|
52
|
+
const accent = languageToColorMap[normalizedLanguage] || "#737373";
|
|
53
|
+
const cardStyle = useMemo(() => ({ "--rr-code-accent": accent }), [accent]);
|
|
54
|
+
const scrollClassName = [
|
|
55
|
+
scroll,
|
|
56
|
+
semanticClassNames.scroll,
|
|
57
|
+
collapsible && isCollapsed && isOverflow && scrollCollapsed,
|
|
58
|
+
collapsible && isCollapsed && isOverflow && semanticClassNames.scrollCollapsed
|
|
59
|
+
].filter(Boolean).join(" ");
|
|
60
|
+
return /* @__PURE__ */ jsxs("div", { className: `${card} ${semanticClassNames.card}`, style: cardStyle, children: [
|
|
61
|
+
langSlot ?? (normalizedLanguage !== "text" && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: `${lang} ${semanticClassNames.lang}`, children: hasLanguageIcon(normalizedLanguage) ? /* @__PURE__ */ jsx(LanguageIcon, { language: normalizedLanguage, size: 14 }) : /* @__PURE__ */ jsx("span", { children: languageLabel }) })),
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"button",
|
|
64
|
+
{
|
|
65
|
+
"aria-label": copied ? "Copied" : "Copy code",
|
|
66
|
+
className: `${copyButton} ${semanticClassNames.copyButton}`,
|
|
67
|
+
type: "button",
|
|
68
|
+
onClick: handleCopy,
|
|
69
|
+
children: copied ? CheckIcon : CopyIcon
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsxs(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
className: `${staticMode ? bodyBackgroundStatic : bodyBackground} ${semanticClassNames.bodyBackground}`,
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsx("div", { className: scrollClassName, ref: scrollRef, children }),
|
|
78
|
+
collapsible && isOverflow && isCollapsed && /* @__PURE__ */ jsx("div", { className: `${expandWrap} ${semanticClassNames.expandWrap}`, children: /* @__PURE__ */ jsxs(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
className: `${expandButton} ${semanticClassNames.expandButton}`,
|
|
82
|
+
type: "button",
|
|
83
|
+
onClick: () => setIsCollapsed(false),
|
|
84
|
+
children: [
|
|
85
|
+
ExpandIcon,
|
|
86
|
+
/* @__PURE__ */ jsx("span", { children: "展开" })
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
) })
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
const CodeBlockRenderer = ({
|
|
96
|
+
code,
|
|
97
|
+
language,
|
|
98
|
+
showLineNumbers: showLineNumbersProp
|
|
99
|
+
}) => {
|
|
100
|
+
const showLineNumbers = showLineNumbersProp ?? false;
|
|
101
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
102
|
+
const [html, setHtml] = useState(null);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
let cancelled = false;
|
|
105
|
+
(async () => {
|
|
106
|
+
const highlighter = await getHighlighterWithLang(normalizedLanguage);
|
|
107
|
+
if (cancelled) return;
|
|
108
|
+
const loaded = highlighter.getLoadedLanguages();
|
|
109
|
+
const lang2 = loaded.includes(normalizedLanguage) ? normalizedLanguage : "text";
|
|
110
|
+
const result = highlighter.codeToHtml(code, {
|
|
111
|
+
lang: lang2,
|
|
112
|
+
themes: SHIKI_DUAL_THEMES
|
|
113
|
+
});
|
|
114
|
+
if (!cancelled) setHtml(result);
|
|
115
|
+
})();
|
|
116
|
+
return () => {
|
|
117
|
+
cancelled = true;
|
|
118
|
+
};
|
|
119
|
+
}, [code, normalizedLanguage]);
|
|
120
|
+
const fallbackLines = useMemo(() => code.split("\n"), [code]);
|
|
121
|
+
const linedClassName = [
|
|
122
|
+
showLineNumbers && lined,
|
|
123
|
+
showLineNumbers && semanticClassNames.lined,
|
|
124
|
+
showLineNumbers && linedWithNumbers,
|
|
125
|
+
showLineNumbers && semanticClassNames.linedWithNumbers
|
|
126
|
+
].filter(Boolean).join(" ");
|
|
127
|
+
return /* @__PURE__ */ jsx(CodeBlockCard, { static: true, code, language, children: html ? /* @__PURE__ */ jsx("div", { className: linedClassName, dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx("pre", { className: linedClassName, children: /* @__PURE__ */ jsx("code", { children: fallbackLines.map((line, i) => /* @__PURE__ */ jsx("span", { className: "line", children: line }, i)) }) }) });
|
|
128
|
+
};
|
|
129
|
+
export {
|
|
130
|
+
CodeBlockRenderer as C,
|
|
131
|
+
CodeBlockCard as a
|
|
132
|
+
};
|
package/dist/constants.mjs
CHANGED
|
@@ -1,73 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
sql: "SQL"
|
|
1
|
+
const languageToIconMap = {
|
|
2
|
+
javascript: "JS",
|
|
3
|
+
js: "JS",
|
|
4
|
+
typescript: "TS",
|
|
5
|
+
ts: "TS",
|
|
6
|
+
jsx: "JSX",
|
|
7
|
+
tsx: "TSX",
|
|
8
|
+
html: "HTML",
|
|
9
|
+
css: "CSS",
|
|
10
|
+
scss: "SCSS",
|
|
11
|
+
json: "JSON",
|
|
12
|
+
markdown: "MD",
|
|
13
|
+
md: "MD",
|
|
14
|
+
bash: "SH",
|
|
15
|
+
sh: "SH",
|
|
16
|
+
shell: "SH",
|
|
17
|
+
zsh: "SH",
|
|
18
|
+
python: "PY",
|
|
19
|
+
py: "PY",
|
|
20
|
+
rust: "RS",
|
|
21
|
+
go: "GO",
|
|
22
|
+
java: "JAVA",
|
|
23
|
+
c: "C",
|
|
24
|
+
cpp: "C++",
|
|
25
|
+
"c++": "C++",
|
|
26
|
+
swift: "SW",
|
|
27
|
+
kotlin: "KT",
|
|
28
|
+
yaml: "YAML",
|
|
29
|
+
yml: "YAML",
|
|
30
|
+
sql: "SQL"
|
|
32
31
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
32
|
+
const languageToColorMap = {
|
|
33
|
+
javascript: "#f7df1e",
|
|
34
|
+
js: "#f7df1e",
|
|
35
|
+
typescript: "#3178c6",
|
|
36
|
+
ts: "#3178c6",
|
|
37
|
+
jsx: "#61dafb",
|
|
38
|
+
tsx: "#61dafb",
|
|
39
|
+
html: "#e34f26",
|
|
40
|
+
css: "#1572b6",
|
|
41
|
+
scss: "#cc6699",
|
|
42
|
+
json: "#f59e0b",
|
|
43
|
+
markdown: "#737373",
|
|
44
|
+
md: "#737373",
|
|
45
|
+
bash: "#4eaa25",
|
|
46
|
+
sh: "#4eaa25",
|
|
47
|
+
shell: "#4eaa25",
|
|
48
|
+
zsh: "#4eaa25",
|
|
49
|
+
python: "#3776ab",
|
|
50
|
+
py: "#3776ab",
|
|
51
|
+
rust: "#dea584",
|
|
52
|
+
go: "#00add8",
|
|
53
|
+
java: "#b07219",
|
|
54
|
+
c: "#a8b9cc",
|
|
55
|
+
cpp: "#00599c",
|
|
56
|
+
"c++": "#00599c",
|
|
57
|
+
swift: "#fa7343",
|
|
58
|
+
kotlin: "#7f52ff",
|
|
59
|
+
yaml: "#cb171e",
|
|
60
|
+
yml: "#cb171e",
|
|
61
|
+
sql: "#e38c00"
|
|
63
62
|
};
|
|
64
63
|
function normalizeLanguage(lang) {
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
if (!lang) return "text";
|
|
65
|
+
return lang.trim().toLowerCase() || "text";
|
|
67
66
|
}
|
|
68
67
|
function getLanguageDisplayName(lang) {
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (lang === "text") return "TEXT";
|
|
69
|
+
return lang.toUpperCase();
|
|
71
70
|
}
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
export {
|
|
72
|
+
getLanguageDisplayName,
|
|
73
|
+
languageToColorMap,
|
|
74
|
+
languageToIconMap,
|
|
75
|
+
normalizeLanguage
|
|
76
|
+
};
|
package/dist/icons.mjs
CHANGED
|
@@ -1,2 +1,91 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { g as getMaterialIconSvg } from "./language-DEZONKsW.js";
|
|
4
|
+
import { L, a, h } from "./language-DEZONKsW.js";
|
|
5
|
+
const EXT_TO_ICON = {
|
|
6
|
+
ts: "typescript",
|
|
7
|
+
tsx: "react-ts",
|
|
8
|
+
// TSX → React icon (same as JSX)
|
|
9
|
+
mts: "typescript",
|
|
10
|
+
cts: "typescript",
|
|
11
|
+
js: "javascript",
|
|
12
|
+
jsx: "react",
|
|
13
|
+
mjs: "javascript",
|
|
14
|
+
cjs: "javascript",
|
|
15
|
+
py: "python",
|
|
16
|
+
rs: "rust",
|
|
17
|
+
go: "go",
|
|
18
|
+
java: "java",
|
|
19
|
+
html: "html",
|
|
20
|
+
htm: "html",
|
|
21
|
+
css: "css",
|
|
22
|
+
scss: "sass",
|
|
23
|
+
sass: "sass",
|
|
24
|
+
less: "sass",
|
|
25
|
+
json: "json",
|
|
26
|
+
jsonc: "json",
|
|
27
|
+
json5: "json",
|
|
28
|
+
md: "markdown",
|
|
29
|
+
mdx: "markdown",
|
|
30
|
+
sh: "console",
|
|
31
|
+
bash: "console",
|
|
32
|
+
zsh: "console",
|
|
33
|
+
yml: "yaml",
|
|
34
|
+
yaml: "yaml",
|
|
35
|
+
sql: "database",
|
|
36
|
+
c: "c",
|
|
37
|
+
h: "c",
|
|
38
|
+
cpp: "cpp",
|
|
39
|
+
cc: "cpp",
|
|
40
|
+
cxx: "cpp",
|
|
41
|
+
hpp: "cpp",
|
|
42
|
+
swift: "swift",
|
|
43
|
+
kt: "kotlin",
|
|
44
|
+
kts: "kotlin",
|
|
45
|
+
rb: "ruby",
|
|
46
|
+
php: "php",
|
|
47
|
+
vue: "vue",
|
|
48
|
+
svelte: "svelte",
|
|
49
|
+
toml: "toml",
|
|
50
|
+
xml: "xml",
|
|
51
|
+
graphql: "graphql",
|
|
52
|
+
gql: "graphql",
|
|
53
|
+
dockerfile: "docker",
|
|
54
|
+
lua: "lua",
|
|
55
|
+
zig: "zig"
|
|
56
|
+
};
|
|
57
|
+
function getFileIconSvg(filename) {
|
|
58
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
59
|
+
const iconName = EXT_TO_ICON[ext] ?? "file";
|
|
60
|
+
return getMaterialIconSvg(iconName);
|
|
61
|
+
}
|
|
62
|
+
const FileIcon = ({ filename, size = 16, className, fallback = "F" }) => {
|
|
63
|
+
const html = useMemo(() => getFileIconSvg(filename), [filename]);
|
|
64
|
+
const wrapperStyle = {
|
|
65
|
+
width: size,
|
|
66
|
+
height: size,
|
|
67
|
+
display: "inline-flex",
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
justifyContent: "center",
|
|
70
|
+
opacity: html ? 1 : 0.5,
|
|
71
|
+
fontSize: size * 0.6
|
|
72
|
+
};
|
|
73
|
+
if (!html) {
|
|
74
|
+
return /* @__PURE__ */ jsx("span", { className, style: wrapperStyle, children: fallback });
|
|
75
|
+
}
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
"span",
|
|
78
|
+
{
|
|
79
|
+
className,
|
|
80
|
+
dangerouslySetInnerHTML: { __html: html },
|
|
81
|
+
style: { width: size, height: size }
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
export {
|
|
86
|
+
FileIcon,
|
|
87
|
+
L as LANG_TO_ICON,
|
|
88
|
+
a as LanguageIcon,
|
|
89
|
+
getMaterialIconSvg,
|
|
90
|
+
h as hasLanguageIcon
|
|
91
|
+
};
|