@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.
@@ -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,GACT,EAAE,kBAAkB,2CAgGpB"}
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
+ };
@@ -1,73 +1,76 @@
1
- //#region src/constants.ts
2
- var languageToIconMap = {
3
- javascript: "JS",
4
- js: "JS",
5
- typescript: "TS",
6
- ts: "TS",
7
- jsx: "JSX",
8
- tsx: "TSX",
9
- html: "HTML",
10
- css: "CSS",
11
- scss: "SCSS",
12
- json: "JSON",
13
- markdown: "MD",
14
- md: "MD",
15
- bash: "SH",
16
- sh: "SH",
17
- shell: "SH",
18
- zsh: "SH",
19
- python: "PY",
20
- py: "PY",
21
- rust: "RS",
22
- go: "GO",
23
- java: "JAVA",
24
- c: "C",
25
- cpp: "C++",
26
- "c++": "C++",
27
- swift: "SW",
28
- kotlin: "KT",
29
- yaml: "YAML",
30
- yml: "YAML",
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
- var languageToColorMap = {
34
- javascript: "#f7df1e",
35
- js: "#f7df1e",
36
- typescript: "#3178c6",
37
- ts: "#3178c6",
38
- jsx: "#61dafb",
39
- tsx: "#61dafb",
40
- html: "#e34f26",
41
- css: "#1572b6",
42
- scss: "#cc6699",
43
- json: "#f59e0b",
44
- markdown: "#737373",
45
- md: "#737373",
46
- bash: "#4eaa25",
47
- sh: "#4eaa25",
48
- shell: "#4eaa25",
49
- zsh: "#4eaa25",
50
- python: "#3776ab",
51
- py: "#3776ab",
52
- rust: "#dea584",
53
- go: "#00add8",
54
- java: "#b07219",
55
- c: "#a8b9cc",
56
- cpp: "#00599c",
57
- "c++": "#00599c",
58
- swift: "#fa7343",
59
- kotlin: "#7f52ff",
60
- yaml: "#cb171e",
61
- yml: "#cb171e",
62
- sql: "#e38c00"
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
- if (!lang) return "text";
66
- return lang.trim().toLowerCase() || "text";
64
+ if (!lang) return "text";
65
+ return lang.trim().toLowerCase() || "text";
67
66
  }
68
67
  function getLanguageDisplayName(lang) {
69
- if (lang === "text") return "TEXT";
70
- return lang.toUpperCase();
68
+ if (lang === "text") return "TEXT";
69
+ return lang.toUpperCase();
71
70
  }
72
- //#endregion
73
- export { getLanguageDisplayName, languageToColorMap, languageToIconMap, normalizeLanguage };
71
+ export {
72
+ getLanguageDisplayName,
73
+ languageToColorMap,
74
+ languageToIconMap,
75
+ normalizeLanguage
76
+ };
package/dist/icons.mjs CHANGED
@@ -1,2 +1,91 @@
1
- import { n as LanguageIcon, r as hasLanguageIcon, t as LANG_TO_ICON, v as FileIcon, y as getMaterialIconSvg } from "./icons-CnFW5y6g.js";
2
- export { FileIcon, LANG_TO_ICON, LanguageIcon, getMaterialIconSvg, hasLanguageIcon };
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
+ };