@choice-ui/react 1.6.7 → 1.6.9

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.
Files changed (100) hide show
  1. package/dist/components/code-block/dist/index.d.ts +11 -14
  2. package/dist/components/code-block/dist/index.js +120 -93
  3. package/dist/components/code-block/src/code-block.d.ts +1 -2
  4. package/dist/components/code-block/src/code-block.js +11 -21
  5. package/dist/components/code-block/src/components/code-block-code.js +25 -13
  6. package/dist/components/code-block/src/components/code-block-content.d.ts +1 -1
  7. package/dist/components/code-block/src/components/code-block-content.js +14 -11
  8. package/dist/components/code-block/src/components/code-block-footer.js +4 -3
  9. package/dist/components/code-block/src/components/code-block-header.js +5 -6
  10. package/dist/components/code-block/src/hooks/index.d.ts +0 -1
  11. package/dist/components/code-block/src/hooks/use-code-block.js +26 -5
  12. package/dist/components/code-block/src/hooks/use-scroll-detection.d.ts +1 -2
  13. package/dist/components/code-block/src/hooks/use-scroll-detection.js +18 -12
  14. package/dist/components/code-block/src/types.d.ts +3 -5
  15. package/dist/components/code-block/src/utils/extract-code.js +23 -0
  16. package/dist/components/command/src/components/command-list.js +15 -3
  17. package/dist/components/description/dist/index.d.ts +8 -0
  18. package/dist/components/description/dist/index.js +29 -0
  19. package/dist/components/description/src/description.d.ts +6 -0
  20. package/dist/components/description/src/description.js +18 -0
  21. package/dist/components/description/src/index.d.ts +2 -0
  22. package/dist/components/description/src/tv.d.ts +13 -0
  23. package/dist/components/description/src/tv.js +15 -0
  24. package/dist/components/description/tsup.config.d.ts +2 -0
  25. package/dist/components/emoji-picker/dist/index.d.ts +1 -0
  26. package/dist/components/emoji-picker/dist/index.js +4 -2
  27. package/dist/components/emoji-picker/src/emoji-picker.d.ts +1 -0
  28. package/dist/components/emoji-picker/src/emoji-picker.js +4 -2
  29. package/dist/components/error-message/dist/index.d.ts +8 -0
  30. package/dist/components/error-message/dist/index.js +30 -0
  31. package/dist/components/error-message/src/error-message.d.ts +6 -0
  32. package/dist/components/error-message/src/error-message.js +19 -0
  33. package/dist/components/error-message/src/index.d.ts +2 -0
  34. package/dist/components/error-message/src/tv.d.ts +13 -0
  35. package/dist/components/error-message/src/tv.js +15 -0
  36. package/dist/components/error-message/tsup.config.d.ts +2 -0
  37. package/dist/components/form/src/adapters/base-adapter.js +4 -2
  38. package/dist/components/form/src/tv.d.ts +0 -12
  39. package/dist/components/form/src/tv.js +1 -13
  40. package/dist/components/index.d.ts +3 -0
  41. package/dist/components/md-render/dist/index.d.ts +2 -1
  42. package/dist/components/md-render/dist/index.js +5 -9
  43. package/dist/components/md-render/src/components/markdown-components.js +1 -7
  44. package/dist/components/md-render/src/md-render.js +4 -2
  45. package/dist/components/md-render/src/types.d.ts +2 -1
  46. package/dist/components/notifications/dist/index.d.ts +1 -5
  47. package/dist/components/notifications/src/notifications.d.ts +0 -1
  48. package/dist/components/notifications/src/notifications.js +0 -1
  49. package/dist/components/numeric-input/dist/index.d.ts +23 -9
  50. package/dist/components/numeric-input/dist/index.js +26 -3
  51. package/dist/components/numeric-input/src/components/numeric-input-menu-trigger.js +4 -1
  52. package/dist/components/numeric-input/src/hooks/index.d.ts +1 -0
  53. package/dist/components/numeric-input/src/hooks/use-numeric-long-press.d.ts +13 -0
  54. package/dist/components/numeric-input/src/hooks/use-numeric-long-press.js +27 -0
  55. package/dist/components/numeric-input/src/index.d.ts +1 -0
  56. package/dist/components/numeric-input/src/tv.js +22 -2
  57. package/dist/components/picture-preview/dist/index.d.ts +5 -0
  58. package/dist/components/picture-preview/dist/index.js +287 -140
  59. package/dist/components/picture-preview/src/hooks/useWheelHandler.d.ts +6 -1
  60. package/dist/components/picture-preview/src/hooks/useWheelHandler.js +25 -7
  61. package/dist/components/picture-preview/src/picture-preview.d.ts +5 -0
  62. package/dist/components/picture-preview/src/picture-preview.js +214 -123
  63. package/dist/components/picture-preview/src/tv.d.ts +93 -3
  64. package/dist/components/picture-preview/src/tv.js +48 -10
  65. package/dist/components/separator/dist/index.d.ts +1 -8
  66. package/dist/components/separator/src/separator.d.ts +1 -8
  67. package/dist/components/separator/src/separator.js +33 -5
  68. package/dist/components/separator/src/tv.d.ts +39 -18
  69. package/dist/components/separator/src/tv.js +37 -7
  70. package/dist/components/text-field/dist/index.d.ts +2 -3
  71. package/dist/components/text-field/dist/index.js +4 -19
  72. package/dist/components/text-field/src/components/index.d.ts +0 -1
  73. package/dist/components/text-field/src/text-field.d.ts +3 -2
  74. package/dist/components/text-field/src/text-field.js +2 -2
  75. package/dist/components/text-field/src/tv.d.ts +3 -3
  76. package/dist/components/text-field/src/tv.js +1 -6
  77. package/dist/components/toast/dist/index.d.ts +248 -0
  78. package/dist/components/toast/src/components/index.d.ts +3 -0
  79. package/dist/components/toast/src/components/toast-progress-bar.d.ts +7 -0
  80. package/dist/components/toast/src/components/toast-progress-bar.js +53 -0
  81. package/dist/components/toast/src/components/toaster-item.d.ts +26 -0
  82. package/dist/components/toast/src/components/toaster-item.js +416 -0
  83. package/dist/components/toast/src/components/toaster-slots.d.ts +87 -0
  84. package/dist/components/toast/src/components/toaster-slots.js +38 -0
  85. package/dist/components/toast/src/index.d.ts +5 -0
  86. package/dist/components/toast/src/store.d.ts +101 -0
  87. package/dist/components/toast/src/store.js +205 -0
  88. package/dist/components/toast/src/toaster.d.ts +87 -0
  89. package/dist/components/toast/src/toaster.js +271 -0
  90. package/dist/components/toast/src/tv.d.ts +365 -0
  91. package/dist/components/toast/src/tv.js +412 -0
  92. package/dist/components/toast/src/types.d.ts +79 -0
  93. package/dist/components/toast/tsup.config.d.ts +2 -0
  94. package/dist/index.js +11 -2
  95. package/dist/styles/components.css +2 -0
  96. package/package.json +1 -1
  97. package/dist/components/code-block/src/hooks/use-line-count.d.ts +0 -2
  98. package/dist/components/code-block/src/hooks/use-line-count.js +0 -27
  99. package/dist/components/text-field/src/components/field-description.d.ts +0 -2
  100. package/dist/components/text-field/src/components/field-description.js +0 -16
@@ -1,6 +1,5 @@
1
1
  import { default as React__default, ReactNode, HTMLProps, RefObject } from 'react';
2
2
  import * as React from 'react';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
4
3
 
5
4
  interface CodeBlockContextValue {
6
5
  codeExpanded: boolean;
@@ -14,7 +13,7 @@ interface CodeBlockContextValue {
14
13
  isExpanded: boolean;
15
14
  language: string;
16
15
  lineCount: number;
17
- lineThreshold: number;
16
+ lineThreshold?: number;
18
17
  needsScroll: boolean;
19
18
  scrollRef?: RefObject<HTMLDivElement>;
20
19
  variant?: "default" | "light" | "dark";
@@ -51,35 +50,33 @@ interface CodeBlockFooterProps extends CodeBlockInjectedProps {
51
50
  }
52
51
  interface CodeBlockContentProps extends CodeBlockInjectedProps {
53
52
  className?: string;
54
- code: string;
55
53
  language?: string;
56
54
  withScrollArea?: boolean;
55
+ children?: string;
57
56
  }
58
57
  interface CodeBlockCodeProps extends HTMLProps<HTMLDivElement> {
59
58
  className?: string;
60
- code: string;
61
- /** 从 CodeBlock 传递的 context */
59
+ children?: string;
62
60
  codeBlock?: CodeBlockContextValue;
63
61
  codeBlockId?: string;
64
62
  language?: string;
65
- /** 手动指定主题,覆盖自动检测 */
66
63
  variant?: "light" | "dark";
67
64
  }
68
65
 
66
+ declare const CodeBlock: React__default.NamedExoticComponent<CodeBlockProps> & {
67
+ Code: React__default.NamedExoticComponent<CodeBlockCodeProps>;
68
+ Content: React__default.NamedExoticComponent<CodeBlockContentProps>;
69
+ Footer: React__default.NamedExoticComponent<CodeBlockFooterProps>;
70
+ Header: React__default.NamedExoticComponent<CodeBlockHeaderProps>;
71
+ };
72
+
69
73
  declare const CodeBlockCode: React.NamedExoticComponent<CodeBlockCodeProps>;
70
74
 
71
75
  declare const CodeBlockHeader: React__default.NamedExoticComponent<CodeBlockHeaderProps>;
72
76
 
73
77
  declare const CodeBlockFooter: React.NamedExoticComponent<CodeBlockFooterProps>;
74
78
 
75
- declare function CodeBlockContent(props: CodeBlockContentProps): react_jsx_runtime.JSX.Element | null;
76
-
77
- declare const CodeBlock: React__default.NamedExoticComponent<CodeBlockProps> & {
78
- Code: React__default.NamedExoticComponent<CodeBlockCodeProps>;
79
- Content: typeof CodeBlockContent;
80
- Footer: React__default.NamedExoticComponent<CodeBlockFooterProps>;
81
- Header: React__default.NamedExoticComponent<CodeBlockHeaderProps>;
82
- };
79
+ declare const CodeBlockContent: React.NamedExoticComponent<CodeBlockContentProps>;
83
80
 
84
81
  declare function getDefaultFilenameForLanguage(language: string): string;
85
82
 
@@ -1,4 +1,4 @@
1
- import React__default, { memo, useState, useEffect } from "react";
1
+ import React__default, { memo, useState, useEffect, useRef, useCallback } from "react";
2
2
  import { useStickToBottom } from "use-stick-to-bottom";
3
3
  import { codeToHtml } from "shiki";
4
4
  import { useEventCallback } from "usehooks-ts";
@@ -34,13 +34,34 @@ function useCodeBlock({
34
34
  if (!code || typeof code !== "string") {
35
35
  return;
36
36
  }
37
- try {
38
- if (typeof navigator !== "undefined" && navigator.clipboard) {
37
+ const onSuccess = () => {
38
+ setCopied(true);
39
+ setTimeout(() => setCopied(false), 2e3);
40
+ };
41
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
42
+ try {
39
43
  await navigator.clipboard.writeText(code);
40
- setCopied(true);
41
- setTimeout(() => setCopied(false), 2e3);
44
+ onSuccess();
45
+ return;
46
+ } catch {
47
+ }
48
+ }
49
+ const textArea = document.createElement("textarea");
50
+ try {
51
+ textArea.value = code;
52
+ textArea.style.cssText = "position:fixed;top:0;left:0;width:2em;height:2em;padding:0;border:none;outline:none;box-shadow:none;background:transparent;";
53
+ document.body.appendChild(textArea);
54
+ textArea.focus();
55
+ textArea.select();
56
+ const successful = document.execCommand("copy");
57
+ if (successful) {
58
+ onSuccess();
59
+ }
60
+ } catch {
61
+ } finally {
62
+ if (textArea.parentNode) {
63
+ textArea.parentNode.removeChild(textArea);
42
64
  }
43
- } catch (error) {
44
65
  }
45
66
  });
46
67
  return {
@@ -56,12 +77,15 @@ function useScrollDetection({
56
77
  scrollRef,
57
78
  contentRef,
58
79
  isExpanded,
59
- codeExpanded,
60
- children
80
+ codeExpanded
61
81
  }) {
62
82
  const [needsScroll, setNeedsScroll] = useState(false);
63
- useEffect(() => {
64
- const checkScrollNeeded = () => {
83
+ const debounceRef = useRef(null);
84
+ const checkScrollNeeded = useCallback(() => {
85
+ if (debounceRef.current) {
86
+ clearTimeout(debounceRef.current);
87
+ }
88
+ debounceRef.current = setTimeout(() => {
65
89
  if (!scrollRef.current || !contentRef.current) {
66
90
  setNeedsScroll(false);
67
91
  return;
@@ -71,50 +95,30 @@ function useScrollDetection({
71
95
  const content = contentRef.current;
72
96
  const hasScroll = content.scrollHeight > viewport.clientHeight;
73
97
  setNeedsScroll(hasScroll);
74
- } catch (error) {
98
+ } catch {
75
99
  setNeedsScroll(false);
76
100
  }
77
- };
101
+ }, 50);
102
+ }, [scrollRef, contentRef]);
103
+ useEffect(() => {
78
104
  const timeoutId = setTimeout(checkScrollNeeded, 100);
79
105
  window.addEventListener("resize", checkScrollNeeded);
80
106
  let resizeObserver = null;
81
- if (typeof ResizeObserver !== "undefined") {
107
+ if (typeof ResizeObserver !== "undefined" && contentRef.current) {
82
108
  resizeObserver = new ResizeObserver(checkScrollNeeded);
83
- if (contentRef.current) {
84
- resizeObserver.observe(contentRef.current);
85
- }
109
+ resizeObserver.observe(contentRef.current);
86
110
  }
87
111
  return () => {
88
112
  clearTimeout(timeoutId);
113
+ if (debounceRef.current) {
114
+ clearTimeout(debounceRef.current);
115
+ }
89
116
  window.removeEventListener("resize", checkScrollNeeded);
90
117
  resizeObserver == null ? void 0 : resizeObserver.disconnect();
91
118
  };
92
- }, [scrollRef, contentRef, isExpanded, codeExpanded, children]);
119
+ }, [checkScrollNeeded, isExpanded, codeExpanded]);
93
120
  return needsScroll;
94
121
  }
95
- function useLineCount(children) {
96
- const [lineCount, setLineCount] = useState(0);
97
- useEffect(() => {
98
- try {
99
- const codeContent = React__default.Children.toArray(children).map((child) => {
100
- var _a;
101
- if (React__default.isValidElement(child) && ((_a = child.props) == null ? void 0 : _a.code) && typeof child.props.code === "string") {
102
- return child.props.code;
103
- }
104
- return "";
105
- }).join("");
106
- if (codeContent) {
107
- const lines = codeContent.split("\n").length;
108
- setLineCount(lines);
109
- } else {
110
- setLineCount(0);
111
- }
112
- } catch (error) {
113
- setLineCount(0);
114
- }
115
- }, [children]);
116
- return lineCount;
117
- }
118
122
  function useTheme() {
119
123
  const [theme, setTheme] = useState(() => {
120
124
  if (typeof window === "undefined") return "light";
@@ -176,34 +180,46 @@ var codeBlockCodeTv = tcv({
176
180
  base: "text-message-code w-fit min-w-full bg-transparent font-mono [&>pre]:!bg-transparent [&>pre]:px-4 [&>pre]:py-4"
177
181
  });
178
182
  var CodeBlockCode = memo(function CodeBlockCode2(props) {
179
- const { code, language = "tsx", className, variant: variantProp, codeBlock, ...rest } = props;
183
+ const { children, language = "tsx", className, variant: variantProp, codeBlock, ...rest } = props;
180
184
  const systemTheme = useTheme();
181
185
  const resolvedVariant = variantProp ?? ((codeBlock == null ? void 0 : codeBlock.variant) === "default" ? void 0 : codeBlock == null ? void 0 : codeBlock.variant);
182
186
  const theme = resolvedVariant ?? systemTheme;
183
187
  const [highlightedHtml, setHighlightedHtml] = useState(null);
184
188
  useEffect(() => {
189
+ let isMounted = true;
185
190
  async function highlight() {
186
- if (!code) {
187
- setHighlightedHtml("<pre><code></code></pre>");
191
+ if (!children) {
192
+ if (isMounted) setHighlightedHtml("<pre><code></code></pre>");
188
193
  return;
189
194
  }
190
195
  const resolvedLang = resolveLanguage(language);
196
+ const themeConfig = theme === "light" ? "github-light" : "github-dark";
191
197
  try {
192
- const html = await codeToHtml(code, {
198
+ const html = await codeToHtml(children, {
193
199
  lang: resolvedLang,
194
- theme: theme === "light" ? "github-light" : "github-dark"
200
+ theme: themeConfig
195
201
  });
196
- setHighlightedHtml(html);
202
+ if (isMounted) setHighlightedHtml(html);
197
203
  } catch {
198
- const html = await codeToHtml(code, {
199
- lang: "text",
200
- theme: theme === "light" ? "github-light" : "github-dark"
201
- });
202
- setHighlightedHtml(html);
204
+ try {
205
+ const html = await codeToHtml(children, {
206
+ lang: "text",
207
+ theme: themeConfig
208
+ });
209
+ if (isMounted) setHighlightedHtml(html);
210
+ } catch {
211
+ if (isMounted) {
212
+ const escaped = children.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
213
+ setHighlightedHtml(`<pre><code>${escaped}</code></pre>`);
214
+ }
215
+ }
203
216
  }
204
217
  }
205
218
  highlight();
206
- }, [code, language, theme]);
219
+ return () => {
220
+ isMounted = false;
221
+ };
222
+ }, [children, language, theme]);
207
223
  const classNames = codeBlockCodeTv({ className });
208
224
  return highlightedHtml ? /* @__PURE__ */ jsx(
209
225
  "div",
@@ -217,10 +233,29 @@ var CodeBlockCode = memo(function CodeBlockCode2(props) {
217
233
  {
218
234
  className: tcx("min-w-0", classNames),
219
235
  ...rest,
220
- children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) })
236
+ children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children }) })
221
237
  }
222
238
  );
223
239
  });
240
+ function extractCodeFromChildren(children) {
241
+ if (!children) return "";
242
+ try {
243
+ return React__default.Children.toArray(children).map((child) => {
244
+ var _a, _b;
245
+ if (React__default.isValidElement(child)) {
246
+ if (((_a = child.props) == null ? void 0 : _a.code) && typeof child.props.code === "string") {
247
+ return child.props.code;
248
+ }
249
+ if (((_b = child.props) == null ? void 0 : _b.children) && typeof child.props.children === "string") {
250
+ return child.props.children;
251
+ }
252
+ }
253
+ return "";
254
+ }).filter(Boolean).join("");
255
+ } catch {
256
+ return "";
257
+ }
258
+ }
224
259
  var icons = {
225
260
  js: /* @__PURE__ */ jsxs(
226
261
  "svg",
@@ -908,9 +943,8 @@ var CodeBlockHeader = memo(function CodeBlockHeader2(props) {
908
943
  handleCopy,
909
944
  variant
910
945
  } = codeBlock;
911
- if (!handleExpand || !handleCopy) {
912
- return null;
913
- }
946
+ const canExpand = Boolean(handleExpand);
947
+ const canCopy = Boolean(handleCopy);
914
948
  const tv = codeBlockHeaderTv({ isExpanded, variant });
915
949
  let icon = null;
916
950
  try {
@@ -934,17 +968,17 @@ var CodeBlockHeader = memo(function CodeBlockHeader2(props) {
934
968
  children
935
969
  ] }),
936
970
  /* @__PURE__ */ jsxs("div", { className: tv.actions(), children: [
937
- isExpanded && /* @__PURE__ */ jsx(
971
+ isExpanded && canCopy && /* @__PURE__ */ jsx(
938
972
  IconButton,
939
973
  {
940
974
  className: tv.button(),
941
975
  variant: variant === "dark" ? "dark" : "ghost",
942
- onClick: () => handleCopy(),
976
+ onClick: () => handleCopy == null ? void 0 : handleCopy(),
943
977
  tooltip: { content: copyTooltipContent },
944
978
  children: copied ? /* @__PURE__ */ jsx(Check, { className: "text-success-foreground" }) : /* @__PURE__ */ jsx(ClipboardSmall, {})
945
979
  }
946
980
  ),
947
- expandable && /* @__PURE__ */ jsx(
981
+ expandable && canExpand && /* @__PURE__ */ jsx(
948
982
  IconButton,
949
983
  {
950
984
  className: tv.button(),
@@ -972,9 +1006,10 @@ var codeBlockFooterTv = tcv({
972
1006
  },
973
1007
  false: {
974
1008
  footer: [
975
- "bg-secondary-background/90 absolute inset-x-0 bottom-0 z-3",
1009
+ "bg-secondary-background/80 absolute inset-x-0 bottom-0 z-3",
976
1010
  "opacity-0 group-hover:opacity-100",
977
- "hover:bg-tertiary-background"
1011
+ "hover:bg-tertiary-background",
1012
+ "backdrop-blur-xs"
978
1013
  ]
979
1014
  }
980
1015
  }
@@ -988,7 +1023,7 @@ var CodeBlockFooter = memo(function CodeBlockFooter2(props) {
988
1023
  if (!codeBlock) return null;
989
1024
  const { isExpanded, codeExpanded, handleCodeExpand, lineCount, lineThreshold, needsScroll } = codeBlock;
990
1025
  const tv = codeBlockFooterTv({ codeExpanded });
991
- if (!(lineCount > lineThreshold || needsScroll || codeExpanded) || !isExpanded) {
1026
+ if (lineThreshold && !(lineCount > lineThreshold || needsScroll || codeExpanded) || !isExpanded) {
992
1027
  return null;
993
1028
  }
994
1029
  return /* @__PURE__ */ jsx(
@@ -1000,24 +1035,26 @@ var CodeBlockFooter = memo(function CodeBlockFooter2(props) {
1000
1035
  }
1001
1036
  );
1002
1037
  });
1038
+ var LINE_HEIGHT_PX = 16;
1039
+ var HEIGHT_PADDING_PX = 32 + 40;
1003
1040
  var codeBlockTv = tcv({
1004
1041
  slots: {
1005
- code: "overflow-hidden",
1042
+ code: "overflow-hidden min-h-0 flex-1",
1006
1043
  content: "flex w-fit flex-col overflow-clip p-[inherit]"
1007
1044
  }
1008
1045
  });
1009
- function CodeBlockContent(props) {
1010
- const { code, className, codeBlock, withScrollArea = true } = props;
1046
+ var CodeBlockContent = memo(function CodeBlockContent2(props) {
1047
+ const { className, codeBlock, withScrollArea = true, children } = props;
1011
1048
  if (!codeBlock) return null;
1012
1049
  const { language, isExpanded, codeExpanded, scrollRef, contentRef, lineCount, lineThreshold } = codeBlock;
1013
1050
  if (!isExpanded) {
1014
1051
  return null;
1015
1052
  }
1016
- if (typeof code !== "string") {
1053
+ if (typeof children !== "string") {
1017
1054
  return null;
1018
1055
  }
1019
1056
  const tv = codeBlockTv();
1020
- const shouldLimitHeight = lineCount > lineThreshold && !codeExpanded;
1057
+ const shouldLimitHeight = lineThreshold && lineCount > lineThreshold && !codeExpanded;
1021
1058
  return /* @__PURE__ */ jsx(Fragment, { children: withScrollArea ? /* @__PURE__ */ jsx(
1022
1059
  ScrollArea,
1023
1060
  {
@@ -1030,7 +1067,7 @@ function CodeBlockContent(props) {
1030
1067
  {
1031
1068
  ref: scrollRef,
1032
1069
  style: {
1033
- maxHeight: shouldLimitHeight ? `${lineThreshold * 14 + 32}px` : "none"
1070
+ maxHeight: shouldLimitHeight ? `${lineThreshold * LINE_HEIGHT_PX + HEIGHT_PADDING_PX}px` : "none"
1034
1071
  },
1035
1072
  children: /* @__PURE__ */ jsx(
1036
1073
  ScrollArea.Content,
@@ -1040,9 +1077,9 @@ function CodeBlockContent(props) {
1040
1077
  children: /* @__PURE__ */ jsx(
1041
1078
  CodeBlockCode,
1042
1079
  {
1043
- code,
1044
1080
  language,
1045
- codeBlock
1081
+ codeBlock,
1082
+ children
1046
1083
  }
1047
1084
  )
1048
1085
  }
@@ -1053,19 +1090,19 @@ function CodeBlockContent(props) {
1053
1090
  ) : /* @__PURE__ */ jsx("div", { className: tcx(tv.content(), className), children: /* @__PURE__ */ jsx(
1054
1091
  CodeBlockCode,
1055
1092
  {
1056
- code,
1057
1093
  language,
1058
- codeBlock
1094
+ codeBlock,
1095
+ children
1059
1096
  }
1060
1097
  ) }) });
1061
- }
1098
+ });
1062
1099
  var CodeBlockRoot = memo(function CodeBlock(props) {
1063
1100
  const {
1064
1101
  children,
1065
1102
  className,
1066
1103
  filename,
1067
1104
  language = "code",
1068
- lineThreshold = 20,
1105
+ lineThreshold,
1069
1106
  expandable = true,
1070
1107
  defaultExpanded = true,
1071
1108
  defaultCodeExpanded = false,
@@ -1077,7 +1114,6 @@ var CodeBlockRoot = memo(function CodeBlock(props) {
1077
1114
  resize: "smooth",
1078
1115
  initial: "smooth"
1079
1116
  });
1080
- const codeContentRef = React__default.useRef("");
1081
1117
  const {
1082
1118
  isExpanded,
1083
1119
  codeExpanded,
@@ -1092,34 +1128,25 @@ var CodeBlockRoot = memo(function CodeBlock(props) {
1092
1128
  onCodeExpandChange,
1093
1129
  scrollToBottom
1094
1130
  });
1131
+ const codeContent = React__default.useMemo(() => extractCodeFromChildren(children), [children]);
1132
+ const lineCount = React__default.useMemo(
1133
+ () => codeContent ? codeContent.split("\n").length : 0,
1134
+ [codeContent]
1135
+ );
1095
1136
  const handleCopy = React__default.useCallback(
1096
1137
  (code) => {
1097
- const codeToUse = code || codeContentRef.current;
1138
+ const codeToUse = code || codeContent;
1098
1139
  if (codeToUse) {
1099
1140
  originalHandleCopy(codeToUse);
1100
1141
  }
1101
1142
  },
1102
- [originalHandleCopy]
1143
+ [originalHandleCopy, codeContent]
1103
1144
  );
1104
- React__default.useEffect(() => {
1105
- try {
1106
- React__default.Children.forEach(children, (child) => {
1107
- if (React__default.isValidElement(child) && child.props) {
1108
- if (child.props.code && typeof child.props.code === "string") {
1109
- codeContentRef.current = child.props.code;
1110
- }
1111
- }
1112
- });
1113
- } catch {
1114
- }
1115
- }, [children]);
1116
- const lineCount = useLineCount(children);
1117
1145
  const needsScroll = useScrollDetection({
1118
1146
  scrollRef,
1119
1147
  contentRef,
1120
1148
  isExpanded,
1121
- codeExpanded,
1122
- children
1149
+ codeExpanded
1123
1150
  });
1124
1151
  const contextValue = React__default.useMemo(
1125
1152
  () => ({
@@ -1178,7 +1205,7 @@ var CodeBlockRoot = memo(function CodeBlock(props) {
1178
1205
  "div",
1179
1206
  {
1180
1207
  className: tcx(
1181
- "group/code-block relative overflow-hidden rounded-lg",
1208
+ "group/code-block relative flex flex-col overflow-hidden rounded-lg",
1182
1209
  variant === "default" && "bg-secondary-background",
1183
1210
  variant === "light" && "bg-gray-100",
1184
1211
  variant === "dark" && "bg-gray-700",
@@ -1,10 +1,9 @@
1
1
  import { default as React } from 'react';
2
- import { CodeBlockContent } from './components';
3
2
  import { CodeBlockProps } from './types';
4
3
  export declare const CodeBlockRoot: React.NamedExoticComponent<CodeBlockProps>;
5
4
  export declare const CodeBlock: React.NamedExoticComponent<CodeBlockProps> & {
6
5
  Code: React.NamedExoticComponent<import('../..').CodeBlockCodeProps>;
7
- Content: typeof CodeBlockContent;
6
+ Content: React.NamedExoticComponent<import('../..').CodeBlockContentProps>;
8
7
  Footer: React.NamedExoticComponent<import('../..').CodeBlockFooterProps>;
9
8
  Header: React.NamedExoticComponent<import('../..').CodeBlockHeaderProps>;
10
9
  };
@@ -6,7 +6,7 @@ import { CodeBlockFooter } from "./components/code-block-footer.js";
6
6
  import { CodeBlockContent } from "./components/code-block-content.js";
7
7
  import { CodeBlockCode } from "./components/code-block-code.js";
8
8
  import { useCodeBlock } from "./hooks/use-code-block.js";
9
- import { useLineCount } from "./hooks/use-line-count.js";
9
+ import { extractCodeFromChildren } from "./utils/extract-code.js";
10
10
  import { useScrollDetection } from "./hooks/use-scroll-detection.js";
11
11
  import { tcx } from "../../../shared/utils/tcx/tcx.js";
12
12
  const CodeBlockRoot = memo(function CodeBlock2(props) {
@@ -15,7 +15,7 @@ const CodeBlockRoot = memo(function CodeBlock2(props) {
15
15
  className,
16
16
  filename,
17
17
  language = "code",
18
- lineThreshold = 20,
18
+ lineThreshold,
19
19
  expandable = true,
20
20
  defaultExpanded = true,
21
21
  defaultCodeExpanded = false,
@@ -27,7 +27,6 @@ const CodeBlockRoot = memo(function CodeBlock2(props) {
27
27
  resize: "smooth",
28
28
  initial: "smooth"
29
29
  });
30
- const codeContentRef = React__default.useRef("");
31
30
  const {
32
31
  isExpanded,
33
32
  codeExpanded,
@@ -42,34 +41,25 @@ const CodeBlockRoot = memo(function CodeBlock2(props) {
42
41
  onCodeExpandChange,
43
42
  scrollToBottom
44
43
  });
44
+ const codeContent = React__default.useMemo(() => extractCodeFromChildren(children), [children]);
45
+ const lineCount = React__default.useMemo(
46
+ () => codeContent ? codeContent.split("\n").length : 0,
47
+ [codeContent]
48
+ );
45
49
  const handleCopy = React__default.useCallback(
46
50
  (code) => {
47
- const codeToUse = code || codeContentRef.current;
51
+ const codeToUse = code || codeContent;
48
52
  if (codeToUse) {
49
53
  originalHandleCopy(codeToUse);
50
54
  }
51
55
  },
52
- [originalHandleCopy]
56
+ [originalHandleCopy, codeContent]
53
57
  );
54
- React__default.useEffect(() => {
55
- try {
56
- React__default.Children.forEach(children, (child) => {
57
- if (React__default.isValidElement(child) && child.props) {
58
- if (child.props.code && typeof child.props.code === "string") {
59
- codeContentRef.current = child.props.code;
60
- }
61
- }
62
- });
63
- } catch {
64
- }
65
- }, [children]);
66
- const lineCount = useLineCount(children);
67
58
  const needsScroll = useScrollDetection({
68
59
  scrollRef,
69
60
  contentRef,
70
61
  isExpanded,
71
- codeExpanded,
72
- children
62
+ codeExpanded
73
63
  });
74
64
  const contextValue = React__default.useMemo(
75
65
  () => ({
@@ -128,7 +118,7 @@ const CodeBlockRoot = memo(function CodeBlock2(props) {
128
118
  "div",
129
119
  {
130
120
  className: tcx(
131
- "group/code-block relative overflow-hidden rounded-lg",
121
+ "group/code-block relative flex flex-col overflow-hidden rounded-lg",
132
122
  variant === "default" && "bg-secondary-background",
133
123
  variant === "light" && "bg-gray-100",
134
124
  variant === "dark" && "bg-gray-700",
@@ -24,34 +24,46 @@ const codeBlockCodeTv = tcv({
24
24
  base: "text-message-code w-fit min-w-full bg-transparent font-mono [&>pre]:!bg-transparent [&>pre]:px-4 [&>pre]:py-4"
25
25
  });
26
26
  const CodeBlockCode = memo(function CodeBlockCode2(props) {
27
- const { code, language = "tsx", className, variant: variantProp, codeBlock, ...rest } = props;
27
+ const { children, language = "tsx", className, variant: variantProp, codeBlock, ...rest } = props;
28
28
  const systemTheme = useTheme();
29
29
  const resolvedVariant = variantProp ?? ((codeBlock == null ? void 0 : codeBlock.variant) === "default" ? void 0 : codeBlock == null ? void 0 : codeBlock.variant);
30
30
  const theme = resolvedVariant ?? systemTheme;
31
31
  const [highlightedHtml, setHighlightedHtml] = useState(null);
32
32
  useEffect(() => {
33
+ let isMounted = true;
33
34
  async function highlight() {
34
- if (!code) {
35
- setHighlightedHtml("<pre><code></code></pre>");
35
+ if (!children) {
36
+ if (isMounted) setHighlightedHtml("<pre><code></code></pre>");
36
37
  return;
37
38
  }
38
39
  const resolvedLang = resolveLanguage(language);
40
+ const themeConfig = theme === "light" ? "github-light" : "github-dark";
39
41
  try {
40
- const html = await codeToHtml(code, {
42
+ const html = await codeToHtml(children, {
41
43
  lang: resolvedLang,
42
- theme: theme === "light" ? "github-light" : "github-dark"
44
+ theme: themeConfig
43
45
  });
44
- setHighlightedHtml(html);
46
+ if (isMounted) setHighlightedHtml(html);
45
47
  } catch {
46
- const html = await codeToHtml(code, {
47
- lang: "text",
48
- theme: theme === "light" ? "github-light" : "github-dark"
49
- });
50
- setHighlightedHtml(html);
48
+ try {
49
+ const html = await codeToHtml(children, {
50
+ lang: "text",
51
+ theme: themeConfig
52
+ });
53
+ if (isMounted) setHighlightedHtml(html);
54
+ } catch {
55
+ if (isMounted) {
56
+ const escaped = children.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
57
+ setHighlightedHtml(`<pre><code>${escaped}</code></pre>`);
58
+ }
59
+ }
51
60
  }
52
61
  }
53
62
  highlight();
54
- }, [code, language, theme]);
63
+ return () => {
64
+ isMounted = false;
65
+ };
66
+ }, [children, language, theme]);
55
67
  const classNames = codeBlockCodeTv({ className });
56
68
  return highlightedHtml ? /* @__PURE__ */ jsx(
57
69
  "div",
@@ -65,7 +77,7 @@ const CodeBlockCode = memo(function CodeBlockCode2(props) {
65
77
  {
66
78
  className: tcx("min-w-0", classNames),
67
79
  ...rest,
68
- children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) })
80
+ children: /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children }) })
69
81
  }
70
82
  );
71
83
  });
@@ -1,2 +1,2 @@
1
1
  import { CodeBlockContentProps } from '../types';
2
- export declare function CodeBlockContent(props: CodeBlockContentProps): import("react/jsx-runtime").JSX.Element | null;
2
+ export declare const CodeBlockContent: import('react').NamedExoticComponent<CodeBlockContentProps>;