@haklex/rich-renderer-codeblock 0.0.40 → 0.0.41

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.
@@ -0,0 +1,180 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useVariant } from "@haklex/rich-editor";
3
+ import { useState, useRef, useEffect, useCallback, useMemo } from "react";
4
+ import { Check, Copy, ChevronDown } from "lucide-react";
5
+ import { normalizeLanguage, getLanguageDisplayName, languageToColorMap } from "./constants.mjs";
6
+ import "@iconify/utils";
7
+ import "@iconify-json/material-icon-theme";
8
+ import { e as card, s as semanticClassNames, f as lang, h as hasLanguageIcon, d as LanguageIcon, i as langInput, j as copyButton, k as bodyBackground, m as expandWrap, n as expandButton, o as scroll, p as scrollCollapsed, l as lined, a as linedWithNumbers } from "./language-DlgJtK5A.js";
9
+ import { getHighlighterWithLang, SHIKI_DUAL_THEMES } from "./shiki.mjs";
10
+ const CopyIcon = /* @__PURE__ */ jsx(Copy, { size: 16 });
11
+ const CheckIcon = /* @__PURE__ */ jsx(Check, { size: 16 });
12
+ const ExpandIcon = /* @__PURE__ */ jsx(ChevronDown, { size: 14 });
13
+ function CodeBlockCard({
14
+ code,
15
+ language,
16
+ collapsible = true,
17
+ editable = false,
18
+ onLanguageChange,
19
+ children
20
+ }) {
21
+ const normalizedLanguage = normalizeLanguage(language);
22
+ const [copied, setCopied] = useState(false);
23
+ const copyTimerRef = useRef(void 0);
24
+ const [isCollapsed, setIsCollapsed] = useState(true);
25
+ const [isOverflow, setIsOverflow] = useState(false);
26
+ const scrollRef = useRef(null);
27
+ useEffect(() => {
28
+ if (!collapsible) {
29
+ setIsOverflow(false);
30
+ return;
31
+ }
32
+ const el = scrollRef.current;
33
+ if (!el) return;
34
+ const check = () => {
35
+ const halfVh = window.innerHeight / 2;
36
+ setIsOverflow(el.scrollHeight >= halfVh);
37
+ };
38
+ const raf = requestAnimationFrame(check);
39
+ return () => cancelAnimationFrame(raf);
40
+ }, [code, collapsible]);
41
+ useEffect(() => {
42
+ return () => clearTimeout(copyTimerRef.current);
43
+ }, []);
44
+ const handleCopy = useCallback(() => {
45
+ navigator.clipboard.writeText(code).then(() => {
46
+ setCopied(true);
47
+ clearTimeout(copyTimerRef.current);
48
+ copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
49
+ }).catch(() => {
50
+ });
51
+ }, [code]);
52
+ const languageLabel = getLanguageDisplayName(normalizedLanguage);
53
+ const accent = languageToColorMap[normalizedLanguage] || "#737373";
54
+ const cardStyle = useMemo(
55
+ () => ({ "--rr-code-accent": accent }),
56
+ [accent]
57
+ );
58
+ const scrollClassName = [
59
+ scroll,
60
+ semanticClassNames.scroll,
61
+ collapsible && isCollapsed && isOverflow && scrollCollapsed,
62
+ collapsible && isCollapsed && isOverflow && semanticClassNames.scrollCollapsed
63
+ ].filter(Boolean).join(" ");
64
+ return /* @__PURE__ */ jsxs(
65
+ "div",
66
+ {
67
+ className: `${card} ${semanticClassNames.card}`,
68
+ style: cardStyle,
69
+ children: [
70
+ editable ? /* @__PURE__ */ jsxs("div", { className: `${lang} ${semanticClassNames.lang}`, children: [
71
+ hasLanguageIcon(normalizedLanguage) && /* @__PURE__ */ jsx(LanguageIcon, { language: normalizedLanguage, size: 14 }),
72
+ /* @__PURE__ */ jsx(
73
+ "input",
74
+ {
75
+ className: langInput,
76
+ value: language,
77
+ placeholder: "language",
78
+ onChange: (e) => onLanguageChange?.(e.target.value),
79
+ onKeyDown: (e) => {
80
+ e.stopPropagation();
81
+ if (e.key === "Escape" || e.key === "Enter") {
82
+ e.currentTarget.blur();
83
+ }
84
+ }
85
+ }
86
+ )
87
+ ] }) : normalizedLanguage !== "text" && /* @__PURE__ */ jsx(
88
+ "div",
89
+ {
90
+ className: `${lang} ${semanticClassNames.lang}`,
91
+ "aria-hidden": true,
92
+ children: hasLanguageIcon(normalizedLanguage) ? /* @__PURE__ */ jsx(LanguageIcon, { language: normalizedLanguage, size: 14 }) : /* @__PURE__ */ jsx("span", { children: languageLabel })
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsx(
96
+ "button",
97
+ {
98
+ type: "button",
99
+ className: `${copyButton} ${semanticClassNames.copyButton}`,
100
+ onClick: handleCopy,
101
+ "aria-label": copied ? "Copied" : "Copy code",
102
+ children: copied ? CheckIcon : CopyIcon
103
+ }
104
+ ),
105
+ /* @__PURE__ */ jsxs(
106
+ "div",
107
+ {
108
+ className: `${bodyBackground} ${semanticClassNames.bodyBackground}`,
109
+ children: [
110
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, className: scrollClassName, children }),
111
+ collapsible && isOverflow && isCollapsed && /* @__PURE__ */ jsx(
112
+ "div",
113
+ {
114
+ className: `${expandWrap} ${semanticClassNames.expandWrap}`,
115
+ children: /* @__PURE__ */ jsxs(
116
+ "button",
117
+ {
118
+ type: "button",
119
+ className: `${expandButton} ${semanticClassNames.expandButton}`,
120
+ onClick: () => setIsCollapsed(false),
121
+ children: [
122
+ ExpandIcon,
123
+ /* @__PURE__ */ jsx("span", { children: "展开" })
124
+ ]
125
+ }
126
+ )
127
+ }
128
+ )
129
+ ]
130
+ }
131
+ )
132
+ ]
133
+ }
134
+ );
135
+ }
136
+ const CodeBlockRenderer = ({
137
+ code,
138
+ language,
139
+ showLineNumbers: showLineNumbersProp
140
+ }) => {
141
+ const variant = useVariant();
142
+ const showLineNumbers = showLineNumbersProp ?? variant !== "comment";
143
+ const normalizedLanguage = normalizeLanguage(language);
144
+ const [html, setHtml] = useState(null);
145
+ useEffect(() => {
146
+ let cancelled = false;
147
+ (async () => {
148
+ const highlighter = await getHighlighterWithLang(normalizedLanguage);
149
+ if (cancelled) return;
150
+ const loaded = highlighter.getLoadedLanguages();
151
+ const lang2 = loaded.includes(normalizedLanguage) ? normalizedLanguage : "text";
152
+ const result = highlighter.codeToHtml(code, {
153
+ lang: lang2,
154
+ themes: SHIKI_DUAL_THEMES
155
+ });
156
+ if (!cancelled) setHtml(result);
157
+ })();
158
+ return () => {
159
+ cancelled = true;
160
+ };
161
+ }, [code, normalizedLanguage]);
162
+ const fallbackLines = useMemo(() => code.split("\n"), [code]);
163
+ const linedClassName = [
164
+ showLineNumbers && lined,
165
+ showLineNumbers && semanticClassNames.lined,
166
+ showLineNumbers && linedWithNumbers,
167
+ showLineNumbers && semanticClassNames.linedWithNumbers
168
+ ].filter(Boolean).join(" ");
169
+ return /* @__PURE__ */ jsx(CodeBlockCard, { code, language, children: html ? /* @__PURE__ */ jsx(
170
+ "div",
171
+ {
172
+ className: linedClassName,
173
+ dangerouslySetInnerHTML: { __html: html }
174
+ }
175
+ ) : /* @__PURE__ */ jsx("pre", { className: linedClassName, children: /* @__PURE__ */ jsx("code", { children: fallbackLines.map((line, i) => /* @__PURE__ */ jsx("span", { className: "line", children: line }, i)) }) }) });
176
+ };
177
+ export {
178
+ CodeBlockRenderer as C,
179
+ CodeBlockCard as a
180
+ };
package/dist/icons.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
- import { o as getMaterialIconSvg } from "./language-V8hnDWn4.js";
4
- import { p, L, h } from "./language-V8hnDWn4.js";
3
+ import { g as getMaterialIconSvg } from "./language-DlgJtK5A.js";
4
+ import { L, d, h } from "./language-DlgJtK5A.js";
5
5
  const EXT_TO_ICON = {
6
6
  ts: "typescript",
7
7
  tsx: "react-ts",
@@ -89,8 +89,8 @@ const FileIcon = ({
89
89
  };
90
90
  export {
91
91
  FileIcon,
92
- p as LANG_TO_ICON,
93
- L as LanguageIcon,
92
+ L as LANG_TO_ICON,
93
+ d as LanguageIcon,
94
94
  getMaterialIconSvg,
95
95
  h as hasLanguageIcon
96
96
  };
package/dist/index.mjs CHANGED
@@ -4,139 +4,11 @@ import { Compartment, Prec, EditorState, EditorSelection } from "@codemirror/sta
4
4
  import { EditorView, keymap, lineNumbers } from "@codemirror/view";
5
5
  import { isOnFirstLine, isOnLastLine, getThemeExtensions, loadLanguageExtension } from "@haklex/cm-editor";
6
6
  import { useVariant, useColorScheme } from "@haklex/rich-editor";
7
- import { useState, useRef, useEffect, useCallback, useMemo } from "react";
8
- import { Check, Copy, ChevronDown } from "lucide-react";
9
- import { normalizeLanguage, getLanguageDisplayName, languageToColorMap } from "./constants.mjs";
10
- import "@iconify/utils";
11
- import "@iconify-json/material-icon-theme";
12
- import { c as card, s as semanticClassNames, l as lang, h as hasLanguageIcon, L as LanguageIcon, a as langInput, b as copyButton, d as bodyBackground, e as expandWrap, f as expandButton, g as scroll, i as scrollCollapsed, j as lined, k as linedWithNumbers, m as body, n as bodyReadonly } from "./language-V8hnDWn4.js";
13
- import { getHighlighterWithLang, SHIKI_DUAL_THEMES } from "./shiki.mjs";
14
- const CopyIcon = /* @__PURE__ */ jsx(Copy, { size: 16 });
15
- const CheckIcon = /* @__PURE__ */ jsx(Check, { size: 16 });
16
- const ExpandIcon = /* @__PURE__ */ jsx(ChevronDown, { size: 14 });
17
- function CodeBlockCard({
18
- code,
19
- language,
20
- collapsible = true,
21
- editable = false,
22
- onLanguageChange,
23
- children
24
- }) {
25
- const normalizedLanguage = normalizeLanguage(language);
26
- const [copied, setCopied] = useState(false);
27
- const copyTimerRef = useRef(void 0);
28
- const [isCollapsed, setIsCollapsed] = useState(true);
29
- const [isOverflow, setIsOverflow] = useState(false);
30
- const scrollRef = useRef(null);
31
- useEffect(() => {
32
- if (!collapsible) {
33
- setIsOverflow(false);
34
- return;
35
- }
36
- const el = scrollRef.current;
37
- if (!el) return;
38
- const check = () => {
39
- const halfVh = window.innerHeight / 2;
40
- setIsOverflow(el.scrollHeight >= halfVh);
41
- };
42
- const raf = requestAnimationFrame(check);
43
- return () => cancelAnimationFrame(raf);
44
- }, [code, collapsible]);
45
- useEffect(() => {
46
- return () => clearTimeout(copyTimerRef.current);
47
- }, []);
48
- const handleCopy = useCallback(() => {
49
- navigator.clipboard.writeText(code).then(() => {
50
- setCopied(true);
51
- clearTimeout(copyTimerRef.current);
52
- copyTimerRef.current = setTimeout(() => setCopied(false), 2e3);
53
- }).catch(() => {
54
- });
55
- }, [code]);
56
- const languageLabel = getLanguageDisplayName(normalizedLanguage);
57
- const accent = languageToColorMap[normalizedLanguage] || "#737373";
58
- const cardStyle = useMemo(
59
- () => ({ "--rr-code-accent": accent }),
60
- [accent]
61
- );
62
- const scrollClassName = [
63
- scroll,
64
- semanticClassNames.scroll,
65
- collapsible && isCollapsed && isOverflow && scrollCollapsed,
66
- collapsible && isCollapsed && isOverflow && semanticClassNames.scrollCollapsed
67
- ].filter(Boolean).join(" ");
68
- return /* @__PURE__ */ jsxs(
69
- "div",
70
- {
71
- className: `${card} ${semanticClassNames.card}`,
72
- style: cardStyle,
73
- children: [
74
- editable ? /* @__PURE__ */ jsxs("div", { className: `${lang} ${semanticClassNames.lang}`, children: [
75
- hasLanguageIcon(normalizedLanguage) && /* @__PURE__ */ jsx(LanguageIcon, { language: normalizedLanguage, size: 14 }),
76
- /* @__PURE__ */ jsx(
77
- "input",
78
- {
79
- className: langInput,
80
- value: language,
81
- placeholder: "language",
82
- onChange: (e) => onLanguageChange?.(e.target.value),
83
- onKeyDown: (e) => {
84
- e.stopPropagation();
85
- if (e.key === "Escape" || e.key === "Enter") {
86
- e.currentTarget.blur();
87
- }
88
- }
89
- }
90
- )
91
- ] }) : normalizedLanguage !== "text" && /* @__PURE__ */ jsx(
92
- "div",
93
- {
94
- className: `${lang} ${semanticClassNames.lang}`,
95
- "aria-hidden": true,
96
- children: hasLanguageIcon(normalizedLanguage) ? /* @__PURE__ */ jsx(LanguageIcon, { language: normalizedLanguage, size: 14 }) : /* @__PURE__ */ jsx("span", { children: languageLabel })
97
- }
98
- ),
99
- /* @__PURE__ */ jsx(
100
- "button",
101
- {
102
- type: "button",
103
- className: `${copyButton} ${semanticClassNames.copyButton}`,
104
- onClick: handleCopy,
105
- "aria-label": copied ? "Copied" : "Copy code",
106
- children: copied ? CheckIcon : CopyIcon
107
- }
108
- ),
109
- /* @__PURE__ */ jsxs(
110
- "div",
111
- {
112
- className: `${bodyBackground} ${semanticClassNames.bodyBackground}`,
113
- children: [
114
- /* @__PURE__ */ jsx("div", { ref: scrollRef, className: scrollClassName, children }),
115
- collapsible && isOverflow && isCollapsed && /* @__PURE__ */ jsx(
116
- "div",
117
- {
118
- className: `${expandWrap} ${semanticClassNames.expandWrap}`,
119
- children: /* @__PURE__ */ jsxs(
120
- "button",
121
- {
122
- type: "button",
123
- className: `${expandButton} ${semanticClassNames.expandButton}`,
124
- onClick: () => setIsCollapsed(false),
125
- children: [
126
- ExpandIcon,
127
- /* @__PURE__ */ jsx("span", { children: "展开" })
128
- ]
129
- }
130
- )
131
- }
132
- )
133
- ]
134
- }
135
- )
136
- ]
137
- }
138
- );
139
- }
7
+ import { useState, useRef, useMemo, useEffect } from "react";
8
+ import { a as CodeBlockCard } from "./CodeBlockRenderer-BAfW72ee.js";
9
+ import { C } from "./CodeBlockRenderer-BAfW72ee.js";
10
+ import { normalizeLanguage } from "./constants.mjs";
11
+ import { l as lined, s as semanticClassNames, a as linedWithNumbers, b as body, c as bodyReadonly } from "./language-DlgJtK5A.js";
140
12
  function stopHandledEvent(event) {
141
13
  event.preventDefault();
142
14
  event.stopPropagation();
@@ -356,48 +228,7 @@ const CodeBlockEditRenderer = ({
356
228
  }
357
229
  );
358
230
  };
359
- const CodeBlockRenderer = ({
360
- code,
361
- language,
362
- showLineNumbers: showLineNumbersProp
363
- }) => {
364
- const variant = useVariant();
365
- const showLineNumbers = showLineNumbersProp ?? variant !== "comment";
366
- const normalizedLanguage = normalizeLanguage(language);
367
- const [html, setHtml] = useState(null);
368
- useEffect(() => {
369
- let cancelled = false;
370
- (async () => {
371
- const highlighter = await getHighlighterWithLang(normalizedLanguage);
372
- if (cancelled) return;
373
- const loaded = highlighter.getLoadedLanguages();
374
- const lang2 = loaded.includes(normalizedLanguage) ? normalizedLanguage : "text";
375
- const result = highlighter.codeToHtml(code, {
376
- lang: lang2,
377
- themes: SHIKI_DUAL_THEMES
378
- });
379
- if (!cancelled) setHtml(result);
380
- })();
381
- return () => {
382
- cancelled = true;
383
- };
384
- }, [code, normalizedLanguage]);
385
- const fallbackLines = useMemo(() => code.split("\n"), [code]);
386
- const linedClassName = [
387
- showLineNumbers && lined,
388
- showLineNumbers && semanticClassNames.lined,
389
- showLineNumbers && linedWithNumbers,
390
- showLineNumbers && semanticClassNames.linedWithNumbers
391
- ].filter(Boolean).join(" ");
392
- return /* @__PURE__ */ jsx(CodeBlockCard, { code, language, children: html ? /* @__PURE__ */ jsx(
393
- "div",
394
- {
395
- className: linedClassName,
396
- dangerouslySetInnerHTML: { __html: html }
397
- }
398
- ) : /* @__PURE__ */ jsx("pre", { className: linedClassName, children: /* @__PURE__ */ jsx("code", { children: fallbackLines.map((line, i) => /* @__PURE__ */ jsx("span", { className: "line", children: line }, i)) }) }) });
399
- };
400
231
  export {
401
232
  CodeBlockEditRenderer,
402
- CodeBlockRenderer
233
+ C as CodeBlockRenderer
403
234
  };
@@ -112,22 +112,22 @@ const LanguageIcon = ({
112
112
  );
113
113
  };
114
114
  export {
115
- LanguageIcon as L,
116
- langInput as a,
117
- copyButton as b,
118
- card as c,
119
- bodyBackground as d,
120
- expandWrap as e,
121
- expandButton as f,
122
- scroll as g,
115
+ LANG_TO_ICON as L,
116
+ linedWithNumbers as a,
117
+ body as b,
118
+ bodyReadonly as c,
119
+ LanguageIcon as d,
120
+ card as e,
121
+ lang as f,
122
+ getMaterialIconSvg as g,
123
123
  hasLanguageIcon as h,
124
- scrollCollapsed as i,
125
- lined as j,
126
- linedWithNumbers as k,
127
- lang as l,
128
- body as m,
129
- bodyReadonly as n,
130
- getMaterialIconSvg as o,
131
- LANG_TO_ICON as p,
124
+ langInput as i,
125
+ copyButton as j,
126
+ bodyBackground as k,
127
+ lined as l,
128
+ expandWrap as m,
129
+ expandButton as n,
130
+ scroll as o,
131
+ scrollCollapsed as p,
132
132
  semanticClassNames as s
133
133
  };
@@ -0,0 +1,4 @@
1
+ import { C } from "./CodeBlockRenderer-BAfW72ee.js";
2
+ export {
3
+ C as CodeBlockRenderer
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-renderer-codeblock",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "Code block renderer with Shiki syntax highlighting",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,9 +39,9 @@
39
39
  "@iconify/utils": "^2.3.0",
40
40
  "lucide-react": "^0.574.0",
41
41
  "shiki": "^3.23.0",
42
- "@haklex/cm-editor": "0.0.40",
43
- "@haklex/rich-editor": "0.0.40",
44
- "@haklex/rich-style-token": "0.0.40"
42
+ "@haklex/cm-editor": "0.0.41",
43
+ "@haklex/rich-editor": "0.0.41",
44
+ "@haklex/rich-style-token": "0.0.41"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/react": "^19.2.14",