@btst/components 0.0.0-stage → 1.0.0-beta.0

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 (161) hide show
  1. package/LICENSE.md +17 -0
  2. package/dist/_private/ui/styles/markdown-editor.css +31 -0
  3. package/dist/auto-form/index.cjs +8 -0
  4. package/dist/auto-form/index.d.cts +26 -0
  5. package/dist/auto-form/index.d.mts +26 -0
  6. package/dist/auto-form/index.d.ts +26 -0
  7. package/dist/auto-form/index.mjs +1 -0
  8. package/dist/empty/index.cjs +12 -0
  9. package/dist/empty/index.d.cts +15 -0
  10. package/dist/empty/index.d.mts +15 -0
  11. package/dist/empty/index.d.ts +15 -0
  12. package/dist/empty/index.mjs +1 -0
  13. package/dist/form-builder/index.cjs +16 -0
  14. package/dist/form-builder/index.d.cts +395 -0
  15. package/dist/form-builder/index.d.mts +395 -0
  16. package/dist/form-builder/index.d.ts +395 -0
  17. package/dist/form-builder/index.mjs +4 -0
  18. package/dist/markdown/index.cjs +9 -0
  19. package/dist/markdown/index.d.cts +46 -0
  20. package/dist/markdown/index.d.mts +46 -0
  21. package/dist/markdown/index.d.ts +46 -0
  22. package/dist/markdown/index.mjs +2 -0
  23. package/dist/multi-select/index.cjs +7 -0
  24. package/dist/multi-select/index.d.cts +78 -0
  25. package/dist/multi-select/index.d.mts +78 -0
  26. package/dist/multi-select/index.d.ts +78 -0
  27. package/dist/multi-select/index.mjs +1 -0
  28. package/dist/search-select/index.cjs +7 -0
  29. package/dist/search-select/index.d.cts +16 -0
  30. package/dist/search-select/index.d.mts +16 -0
  31. package/dist/search-select/index.d.ts +16 -0
  32. package/dist/search-select/index.mjs +1 -0
  33. package/dist/shared/components.BdvBiegH.d.cts +291 -0
  34. package/dist/shared/components.BdvBiegH.d.mts +291 -0
  35. package/dist/shared/components.BdvBiegH.d.ts +291 -0
  36. package/dist/shared/components.CZMWR72v.d.cts +10 -0
  37. package/dist/shared/components.CZMWR72v.d.mts +10 -0
  38. package/dist/shared/components.CZMWR72v.d.ts +10 -0
  39. package/dist/stepped-auto-form/index.cjs +7 -0
  40. package/dist/stepped-auto-form/index.d.cts +37 -0
  41. package/dist/stepped-auto-form/index.d.mts +37 -0
  42. package/dist/stepped-auto-form/index.d.ts +37 -0
  43. package/dist/stepped-auto-form/index.mjs +1 -0
  44. package/dist/style.css +440 -0
  45. package/dist/ui/src/components/accordion.cjs +81 -0
  46. package/dist/ui/src/components/accordion.mjs +62 -0
  47. package/dist/ui/src/components/auto-form/common/label.cjs +18 -0
  48. package/dist/ui/src/components/auto-form/common/label.mjs +16 -0
  49. package/dist/ui/src/components/auto-form/common/tooltip.cjs +9 -0
  50. package/dist/ui/src/components/auto-form/common/tooltip.mjs +7 -0
  51. package/dist/ui/src/components/auto-form/config.cjs +40 -0
  52. package/dist/ui/src/components/auto-form/config.mjs +37 -0
  53. package/dist/ui/src/components/auto-form/dependencies.cjs +48 -0
  54. package/dist/ui/src/components/auto-form/dependencies.mjs +46 -0
  55. package/dist/ui/src/components/auto-form/fields/array.cjs +266 -0
  56. package/dist/ui/src/components/auto-form/fields/array.mjs +264 -0
  57. package/dist/ui/src/components/auto-form/fields/checkbox.cjs +38 -0
  58. package/dist/ui/src/components/auto-form/fields/checkbox.mjs +36 -0
  59. package/dist/ui/src/components/auto-form/fields/date.cjs +67 -0
  60. package/dist/ui/src/components/auto-form/fields/date.mjs +65 -0
  61. package/dist/ui/src/components/auto-form/fields/enum.cjs +73 -0
  62. package/dist/ui/src/components/auto-form/fields/enum.mjs +71 -0
  63. package/dist/ui/src/components/auto-form/fields/input.cjs +32 -0
  64. package/dist/ui/src/components/auto-form/fields/input.mjs +30 -0
  65. package/dist/ui/src/components/auto-form/fields/number.cjs +31 -0
  66. package/dist/ui/src/components/auto-form/fields/number.mjs +29 -0
  67. package/dist/ui/src/components/auto-form/fields/object.cjs +233 -0
  68. package/dist/ui/src/components/auto-form/fields/object.mjs +217 -0
  69. package/dist/ui/src/components/auto-form/fields/radio-group.cjs +67 -0
  70. package/dist/ui/src/components/auto-form/fields/radio-group.mjs +65 -0
  71. package/dist/ui/src/components/auto-form/fields/switch.cjs +38 -0
  72. package/dist/ui/src/components/auto-form/fields/switch.mjs +36 -0
  73. package/dist/ui/src/components/auto-form/fields/textarea.cjs +31 -0
  74. package/dist/ui/src/components/auto-form/fields/textarea.mjs +29 -0
  75. package/dist/ui/src/components/auto-form/helpers.cjs +270 -0
  76. package/dist/ui/src/components/auto-form/helpers.mjs +259 -0
  77. package/dist/ui/src/components/auto-form/index.cjs +102 -0
  78. package/dist/ui/src/components/auto-form/index.mjs +93 -0
  79. package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +374 -0
  80. package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  81. package/dist/ui/src/components/auto-form/types.cjs +11 -0
  82. package/dist/ui/src/components/auto-form/types.mjs +9 -0
  83. package/dist/ui/src/components/badge.cjs +42 -0
  84. package/dist/ui/src/components/badge.mjs +39 -0
  85. package/dist/ui/src/components/button.cjs +56 -0
  86. package/dist/ui/src/components/button.mjs +53 -0
  87. package/dist/ui/src/components/calendar.cjs +207 -0
  88. package/dist/ui/src/components/calendar.mjs +190 -0
  89. package/dist/ui/src/components/checkbox.cjs +48 -0
  90. package/dist/ui/src/components/checkbox.mjs +32 -0
  91. package/dist/ui/src/components/command.cjs +118 -0
  92. package/dist/ui/src/components/command.mjs +111 -0
  93. package/dist/ui/src/components/date-picker.cjs +41 -0
  94. package/dist/ui/src/components/date-picker.mjs +39 -0
  95. package/dist/ui/src/components/dialog.cjs +141 -0
  96. package/dist/ui/src/components/dialog.mjs +118 -0
  97. package/dist/ui/src/components/empty.cjs +104 -0
  98. package/dist/ui/src/components/empty.mjs +97 -0
  99. package/dist/ui/src/components/form-builder/canvas.cjs +111 -0
  100. package/dist/ui/src/components/form-builder/canvas.mjs +109 -0
  101. package/dist/ui/src/components/form-builder/components/index.cjs +571 -0
  102. package/dist/ui/src/components/form-builder/components/index.mjs +554 -0
  103. package/dist/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
  104. package/dist/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
  105. package/dist/ui/src/components/form-builder/form-preview.cjs +73 -0
  106. package/dist/ui/src/components/form-builder/form-preview.mjs +71 -0
  107. package/dist/ui/src/components/form-builder/index.cjs +350 -0
  108. package/dist/ui/src/components/form-builder/index.mjs +344 -0
  109. package/dist/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
  110. package/dist/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
  111. package/dist/ui/src/components/form-builder/palette.cjs +52 -0
  112. package/dist/ui/src/components/form-builder/palette.mjs +49 -0
  113. package/dist/ui/src/components/form-builder/schema-utils.cjs +128 -0
  114. package/dist/ui/src/components/form-builder/schema-utils.mjs +122 -0
  115. package/dist/ui/src/components/form-builder/sortable-field.cjs +151 -0
  116. package/dist/ui/src/components/form-builder/sortable-field.mjs +148 -0
  117. package/dist/ui/src/components/form-builder/step-tabs.cjs +180 -0
  118. package/dist/ui/src/components/form-builder/step-tabs.mjs +178 -0
  119. package/dist/ui/src/components/form-builder/types.cjs +7 -0
  120. package/dist/ui/src/components/form-builder/types.mjs +5 -0
  121. package/dist/ui/src/components/form-builder/validation-schemas.cjs +67 -0
  122. package/dist/ui/src/components/form-builder/validation-schemas.mjs +56 -0
  123. package/dist/ui/src/components/form.cjs +120 -0
  124. package/dist/ui/src/components/form.mjs +98 -0
  125. package/dist/ui/src/components/input.cjs +23 -0
  126. package/dist/ui/src/components/input.mjs +21 -0
  127. package/dist/ui/src/components/label.cjs +39 -0
  128. package/dist/ui/src/components/label.mjs +23 -0
  129. package/dist/ui/src/components/markdown-content.cjs +334 -0
  130. package/dist/ui/src/components/markdown-content.mjs +325 -0
  131. package/dist/ui/src/components/markdown-editor.cjs +180 -0
  132. package/dist/ui/src/components/markdown-editor.mjs +178 -0
  133. package/dist/ui/src/components/multi-select.cjs +555 -0
  134. package/dist/ui/src/components/multi-select.mjs +540 -0
  135. package/dist/ui/src/components/popover.cjs +60 -0
  136. package/dist/ui/src/components/popover.mjs +42 -0
  137. package/dist/ui/src/components/radio-group.cjs +62 -0
  138. package/dist/ui/src/components/radio-group.mjs +45 -0
  139. package/dist/ui/src/components/search-select.cjs +99 -0
  140. package/dist/ui/src/components/search-select.mjs +83 -0
  141. package/dist/ui/src/components/select.cjs +161 -0
  142. package/dist/ui/src/components/select.mjs +139 -0
  143. package/dist/ui/src/components/separator.cjs +43 -0
  144. package/dist/ui/src/components/separator.mjs +27 -0
  145. package/dist/ui/src/components/switch.cjs +48 -0
  146. package/dist/ui/src/components/switch.mjs +32 -0
  147. package/dist/ui/src/components/tabs.cjs +84 -0
  148. package/dist/ui/src/components/tabs.mjs +65 -0
  149. package/dist/ui/src/components/textarea.cjs +20 -0
  150. package/dist/ui/src/components/textarea.mjs +18 -0
  151. package/dist/ui/src/hooks/use-debounce.cjs +16 -0
  152. package/dist/ui/src/hooks/use-debounce.mjs +14 -0
  153. package/dist/ui/src/lib/schema-converter.cjs +94 -0
  154. package/dist/ui/src/lib/schema-converter.mjs +92 -0
  155. package/dist/ui/src/lib/throttle.cjs +56 -0
  156. package/dist/ui/src/lib/throttle.mjs +54 -0
  157. package/dist/ui/src/lib/utils.cjs +10 -0
  158. package/dist/ui/src/lib/utils.mjs +8 -0
  159. package/dist/ui.css +131 -0
  160. package/package.json +176 -3
  161. package/README.md +0 -3
@@ -0,0 +1,334 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ const jsxRuntime = require('react/jsx-runtime');
5
+ const utils = require('../lib/utils.cjs');
6
+ const React = require('react');
7
+ const ReactMarkdown = require('react-markdown');
8
+ const rehypeHighlight = require('rehype-highlight');
9
+ const rehypeRaw = require('rehype-raw');
10
+ const remarkGfm = require('remark-gfm');
11
+ const lucideReact = require('lucide-react');
12
+
13
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
14
+
15
+ const ReactMarkdown__default = /*#__PURE__*/_interopDefaultCompat(ReactMarkdown);
16
+ const rehypeHighlight__default = /*#__PURE__*/_interopDefaultCompat(rehypeHighlight);
17
+ const rehypeRaw__default = /*#__PURE__*/_interopDefaultCompat(rehypeRaw);
18
+ const remarkGfm__default = /*#__PURE__*/_interopDefaultCompat(remarkGfm);
19
+
20
+ const IntroductionSlotContext = React.createContext(null);
21
+ function ParagraphRenderer({
22
+ node,
23
+ children,
24
+ ...props
25
+ }) {
26
+ const slot = React.useContext(IntroductionSlotContext);
27
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
28
+ /* @__PURE__ */ jsxRuntime.jsx("p", { ...props, children }),
29
+ node?.data && "btstIntroduction" in node.data && node.data.btstIntroduction === true ? slot : null
30
+ ] });
31
+ }
32
+ const DefaultLink = (props) => {
33
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { ...props });
34
+ };
35
+ const DefaultImage = (props) => {
36
+ return /* @__PURE__ */ jsxRuntime.jsx("img", { ...props });
37
+ };
38
+ function containsMath(markdown) {
39
+ return /\$\$[\s\S]+?\$\$/.test(markdown) || // Display math
40
+ /(?<!\$)\$(?!\$)[^\$\n]+?\$(?!\$)/.test(markdown) || // Inline math (not $$)
41
+ /\\begin\{(equation|align|gather|math)\}/.test(markdown);
42
+ }
43
+ function slugify(text) {
44
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
45
+ }
46
+ function getNodeText(node) {
47
+ if (node == null) return "";
48
+ if (typeof node === "string" || typeof node === "number") return String(node);
49
+ if (Array.isArray(node)) return node.map(getNodeText).join("");
50
+ if (React.isValidElement(node)) {
51
+ const props = node.props;
52
+ return getNodeText(props.children);
53
+ }
54
+ return "";
55
+ }
56
+ function simpleHash(str) {
57
+ let hash = 0;
58
+ for (let i = 0; i < str.length; i++) {
59
+ const char = str.charCodeAt(i);
60
+ hash = (hash << 5) - hash + char;
61
+ hash = hash & hash;
62
+ }
63
+ return Math.abs(hash).toString(36).slice(0, 6);
64
+ }
65
+ function isCheckboxElement(node) {
66
+ return React.isValidElement(node) && node.type === "input" && node.props.type === "checkbox";
67
+ }
68
+ function createTaskListItemRenderer() {
69
+ return function LiRenderer(props) {
70
+ const { className, children, ...rest } = props;
71
+ const isTaskItem = (className ?? "").split(" ").includes("task-list-item");
72
+ if (!isTaskItem) {
73
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { className, ...rest, children });
74
+ }
75
+ const childArray = Array.isArray(children) ? children : [children];
76
+ const checkboxNode = childArray.find(isCheckboxElement);
77
+ const nonCheckboxChildren = childArray.filter((c) => !isCheckboxElement(c));
78
+ const labelText = getNodeText(nonCheckboxChildren);
79
+ const baseId = slugify(labelText || "task-item");
80
+ const hashSuffix = simpleHash(labelText || "task-item");
81
+ const uniqueId = `${baseId}-${hashSuffix}`;
82
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className, ...rest, children: [
83
+ checkboxNode ? React.createElement(checkboxNode.type, {
84
+ ...checkboxNode.props,
85
+ id: uniqueId,
86
+ "aria-label": labelText || "Task item"
87
+ }) : null,
88
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: uniqueId, children: nonCheckboxChildren })
89
+ ] });
90
+ };
91
+ }
92
+ function createHeadingRenderer(tag, LinkComponent) {
93
+ return function HeadingRenderer(props) {
94
+ const { children, ...rest } = props;
95
+ const text = getNodeText(children);
96
+ const id = slugify(text);
97
+ return React.createElement(
98
+ tag,
99
+ { id, ...rest },
100
+ children,
101
+ text ? /* @__PURE__ */ jsxRuntime.jsx(
102
+ LinkComponent,
103
+ {
104
+ className: "heading-anchor",
105
+ href: `#${id}`,
106
+ "aria-label": "Link to heading",
107
+ children: "#"
108
+ }
109
+ ) : null
110
+ );
111
+ };
112
+ }
113
+ function CodeRenderer({ inline, className, children, ...rest }) {
114
+ const hasLanguage = /language-([a-z0-9-]+)/i.test(className ?? "");
115
+ const isInline = inline ?? !hasLanguage;
116
+ if (isInline) {
117
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className, ...rest, children });
118
+ }
119
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className, ...rest, children });
120
+ }
121
+ function PreRenderer(props) {
122
+ const { children, ...rest } = props;
123
+ const child = Array.isArray(children) ? children[0] : children;
124
+ let language;
125
+ if (React.isValidElement(child)) {
126
+ const className = child.props.className;
127
+ const match = /language-([a-z0-9-]+)/i.exec(className ?? "");
128
+ language = match?.[1];
129
+ }
130
+ const label = (language ?? "text").toUpperCase();
131
+ const preRef = React.useRef(null);
132
+ const [copied, setCopied] = React.useState(false);
133
+ const resetTimerRef = React.useRef(null);
134
+ let codeText = "";
135
+ if (React.isValidElement(child)) {
136
+ const childProps = child.props;
137
+ codeText = getNodeText(childProps.children);
138
+ }
139
+ const normalized = codeText.endsWith("\n") ? codeText.slice(0, -1) : codeText;
140
+ const lineCount = Math.max(1, normalized.split("\n").length);
141
+ const digitCount = String(lineCount).length;
142
+ const onCopy = async (e) => {
143
+ e.preventDefault();
144
+ e.stopPropagation();
145
+ try {
146
+ const text = preRef.current?.textContent ?? "";
147
+ if (text && typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
148
+ await navigator.clipboard.writeText(text);
149
+ setCopied(true);
150
+ if (resetTimerRef.current) window.clearTimeout(resetTimerRef.current);
151
+ resetTimerRef.current = window.setTimeout(() => {
152
+ setCopied(false);
153
+ resetTimerRef.current = null;
154
+ }, 2e3);
155
+ }
156
+ } catch {
157
+ }
158
+ };
159
+ return /* @__PURE__ */ jsxRuntime.jsxs(
160
+ "div",
161
+ {
162
+ className: "markdown-code-block",
163
+ style: {
164
+ ["--code-gutter-width"]: `${digitCount + 1}ch`
165
+ },
166
+ children: [
167
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "code-toolbar", children: [
168
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "language-label", children: label }),
169
+ /* @__PURE__ */ jsxRuntime.jsx(
170
+ "button",
171
+ {
172
+ type: "button",
173
+ className: "copy-button",
174
+ onClick: onCopy,
175
+ "aria-label": copied ? "Copied" : "Copy code",
176
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { size: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CopyIcon, { size: 16 })
177
+ }
178
+ )
179
+ ] }),
180
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "code-content", children: [
181
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "line-numbers", "aria-hidden": true, children: Array.from({ length: lineCount }).map((_, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: idx + 1 }, idx + 1)) }),
182
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { ref: preRef, ...rest, children })
183
+ ] })
184
+ ]
185
+ }
186
+ );
187
+ }
188
+ function markIntroduction() {
189
+ return (tree) => {
190
+ const paragraph = tree.children.find((node) => node.type === "element" && node.tagName === "p");
191
+ if (paragraph) paragraph.data = { ...paragraph.data, btstIntroduction: true };
192
+ };
193
+ }
194
+ function MarkdownContent({
195
+ markdown,
196
+ afterFirstParagraph,
197
+ className,
198
+ variant = "default",
199
+ LinkComponent = DefaultLink,
200
+ ImageComponent = DefaultImage
201
+ }) {
202
+ const [mathPlugins, setMathPlugins] = React.useState(null);
203
+ const [isLoadingMath, setIsLoadingMath] = React.useState(false);
204
+ const hasMath = React.useMemo(() => containsMath(markdown), [markdown]);
205
+ React.useEffect(() => {
206
+ if (!hasMath || mathPlugins || isLoadingMath) return;
207
+ setIsLoadingMath(true);
208
+ const katexCSSId = "katex-css";
209
+ if (typeof document !== "undefined" && !document.getElementById(katexCSSId)) {
210
+ const link = document.createElement("link");
211
+ link.id = katexCSSId;
212
+ link.rel = "stylesheet";
213
+ link.href = "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
214
+ link.integrity = "sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+";
215
+ link.crossOrigin = "anonymous";
216
+ document.head.appendChild(link);
217
+ const fontDisplayStyleId = "katex-font-display-override";
218
+ if (!document.getElementById(fontDisplayStyleId)) {
219
+ const style = document.createElement("style");
220
+ style.id = fontDisplayStyleId;
221
+ style.textContent = `
222
+ @font-face {
223
+ font-family: 'KaTeX_Main';
224
+ font-style: normal;
225
+ font-weight: normal;
226
+ font-display: swap;
227
+ src: url('https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/fonts/KaTeX_Main-Regular.woff2') format('woff2');
228
+ }
229
+ @font-face {
230
+ font-family: 'KaTeX_Math';
231
+ font-style: italic;
232
+ font-weight: normal;
233
+ font-display: swap;
234
+ src: url('https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/fonts/KaTeX_Math-Italic.woff2') format('woff2');
235
+ }
236
+ `;
237
+ document.head.appendChild(style);
238
+ }
239
+ }
240
+ Promise.all([import('remark-math'), import('rehype-katex')]).then(([remarkMathModule, rehypeKatexModule]) => {
241
+ setMathPlugins({
242
+ remarkMath: remarkMathModule.default,
243
+ rehypeKatex: rehypeKatexModule.default
244
+ });
245
+ }).catch((error) => {
246
+ console.error("Failed to load math plugins:", error);
247
+ }).finally(() => {
248
+ setIsLoadingMath(false);
249
+ });
250
+ }, [hasMath, mathPlugins, isLoadingMath]);
251
+ const AnchorRenderer = React.useMemo(() => {
252
+ return function AnchorRendererComponent(props) {
253
+ const { href = "", children, className: anchorClassName, ...rest } = props;
254
+ return /* @__PURE__ */ jsxRuntime.jsx(LinkComponent, { href, className: anchorClassName, ...rest, children });
255
+ };
256
+ }, [LinkComponent]);
257
+ const ImgRenderer = React.useMemo(() => {
258
+ return function ImgRendererComponent(props) {
259
+ const {
260
+ src = "",
261
+ alt = "",
262
+ className: imgClassName,
263
+ width,
264
+ height,
265
+ style,
266
+ ...rest
267
+ } = props;
268
+ const imageProps = {
269
+ src,
270
+ alt,
271
+ className: imgClassName,
272
+ style,
273
+ ...rest
274
+ };
275
+ if (width != null && height != null) {
276
+ imageProps.width = width;
277
+ imageProps.height = height;
278
+ } else {
279
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "markdown-image-wrapper", children: /* @__PURE__ */ jsxRuntime.jsx(ImageComponent, { ...imageProps }) });
280
+ }
281
+ return /* @__PURE__ */ jsxRuntime.jsx(ImageComponent, { ...imageProps });
282
+ };
283
+ }, [ImageComponent]);
284
+ const components = React.useMemo(() => {
285
+ return {
286
+ p: ParagraphRenderer,
287
+ a: AnchorRenderer,
288
+ img: ImgRenderer,
289
+ span: ({ node: _node, className: className2, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
290
+ "span",
291
+ {
292
+ ...props,
293
+ className: className2,
294
+ tabIndex: className2?.split(/\s+/).includes("katex") ? 0 : props.tabIndex
295
+ }
296
+ ),
297
+ code: CodeRenderer,
298
+ pre: PreRenderer,
299
+ h1: createHeadingRenderer("h1", LinkComponent),
300
+ h2: createHeadingRenderer("h2", LinkComponent),
301
+ h3: createHeadingRenderer("h3", LinkComponent),
302
+ h4: createHeadingRenderer("h4", LinkComponent),
303
+ h5: createHeadingRenderer("h5", LinkComponent),
304
+ h6: createHeadingRenderer("h6", LinkComponent),
305
+ li: createTaskListItemRenderer()
306
+ };
307
+ }, [AnchorRenderer, ImgRenderer, LinkComponent]);
308
+ const remarkPlugins = React.useMemo(() => {
309
+ const plugins = [remarkGfm__default];
310
+ if (hasMath && mathPlugins?.remarkMath) {
311
+ plugins.push(mathPlugins.remarkMath);
312
+ }
313
+ return plugins;
314
+ }, [hasMath, mathPlugins]);
315
+ const rehypePlugins = React.useMemo(() => {
316
+ const plugins = [rehypeRaw__default, rehypeHighlight__default, markIntroduction];
317
+ if (hasMath && mathPlugins?.rehypeKatex) {
318
+ plugins.push(mathPlugins.rehypeKatex);
319
+ }
320
+ return plugins;
321
+ }, [hasMath, mathPlugins]);
322
+ const variantClass = variant === "chat" ? "markdown-content-chat" : "";
323
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("markdown-content", variantClass, className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "markdown-inner", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "markdown-body", children: /* @__PURE__ */ jsxRuntime.jsx(IntroductionSlotContext.Provider, { value: afterFirstParagraph, children: /* @__PURE__ */ jsxRuntime.jsx(
324
+ ReactMarkdown__default,
325
+ {
326
+ remarkPlugins,
327
+ rehypePlugins,
328
+ components,
329
+ children: markdown
330
+ }
331
+ ) }) }) }) });
332
+ }
333
+
334
+ exports.MarkdownContent = MarkdownContent;
@@ -0,0 +1,325 @@
1
+ 'use client';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { cn } from '../lib/utils.mjs';
4
+ import { createContext, useState, useMemo, useEffect, isValidElement, useRef, useContext, createElement } from 'react';
5
+ import ReactMarkdown from 'react-markdown';
6
+ import rehypeHighlight from 'rehype-highlight';
7
+ import rehypeRaw from 'rehype-raw';
8
+ import remarkGfm from 'remark-gfm';
9
+ import { CheckIcon, CopyIcon } from 'lucide-react';
10
+
11
+ const IntroductionSlotContext = createContext(null);
12
+ function ParagraphRenderer({
13
+ node,
14
+ children,
15
+ ...props
16
+ }) {
17
+ const slot = useContext(IntroductionSlotContext);
18
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
19
+ /* @__PURE__ */ jsx("p", { ...props, children }),
20
+ node?.data && "btstIntroduction" in node.data && node.data.btstIntroduction === true ? slot : null
21
+ ] });
22
+ }
23
+ const DefaultLink = (props) => {
24
+ return /* @__PURE__ */ jsx("a", { ...props });
25
+ };
26
+ const DefaultImage = (props) => {
27
+ return /* @__PURE__ */ jsx("img", { ...props });
28
+ };
29
+ function containsMath(markdown) {
30
+ return /\$\$[\s\S]+?\$\$/.test(markdown) || // Display math
31
+ /(?<!\$)\$(?!\$)[^\$\n]+?\$(?!\$)/.test(markdown) || // Inline math (not $$)
32
+ /\\begin\{(equation|align|gather|math)\}/.test(markdown);
33
+ }
34
+ function slugify(text) {
35
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
36
+ }
37
+ function getNodeText(node) {
38
+ if (node == null) return "";
39
+ if (typeof node === "string" || typeof node === "number") return String(node);
40
+ if (Array.isArray(node)) return node.map(getNodeText).join("");
41
+ if (isValidElement(node)) {
42
+ const props = node.props;
43
+ return getNodeText(props.children);
44
+ }
45
+ return "";
46
+ }
47
+ function simpleHash(str) {
48
+ let hash = 0;
49
+ for (let i = 0; i < str.length; i++) {
50
+ const char = str.charCodeAt(i);
51
+ hash = (hash << 5) - hash + char;
52
+ hash = hash & hash;
53
+ }
54
+ return Math.abs(hash).toString(36).slice(0, 6);
55
+ }
56
+ function isCheckboxElement(node) {
57
+ return isValidElement(node) && node.type === "input" && node.props.type === "checkbox";
58
+ }
59
+ function createTaskListItemRenderer() {
60
+ return function LiRenderer(props) {
61
+ const { className, children, ...rest } = props;
62
+ const isTaskItem = (className ?? "").split(" ").includes("task-list-item");
63
+ if (!isTaskItem) {
64
+ return /* @__PURE__ */ jsx("li", { className, ...rest, children });
65
+ }
66
+ const childArray = Array.isArray(children) ? children : [children];
67
+ const checkboxNode = childArray.find(isCheckboxElement);
68
+ const nonCheckboxChildren = childArray.filter((c) => !isCheckboxElement(c));
69
+ const labelText = getNodeText(nonCheckboxChildren);
70
+ const baseId = slugify(labelText || "task-item");
71
+ const hashSuffix = simpleHash(labelText || "task-item");
72
+ const uniqueId = `${baseId}-${hashSuffix}`;
73
+ return /* @__PURE__ */ jsxs("li", { className, ...rest, children: [
74
+ checkboxNode ? createElement(checkboxNode.type, {
75
+ ...checkboxNode.props,
76
+ id: uniqueId,
77
+ "aria-label": labelText || "Task item"
78
+ }) : null,
79
+ /* @__PURE__ */ jsx("label", { htmlFor: uniqueId, children: nonCheckboxChildren })
80
+ ] });
81
+ };
82
+ }
83
+ function createHeadingRenderer(tag, LinkComponent) {
84
+ return function HeadingRenderer(props) {
85
+ const { children, ...rest } = props;
86
+ const text = getNodeText(children);
87
+ const id = slugify(text);
88
+ return createElement(
89
+ tag,
90
+ { id, ...rest },
91
+ children,
92
+ text ? /* @__PURE__ */ jsx(
93
+ LinkComponent,
94
+ {
95
+ className: "heading-anchor",
96
+ href: `#${id}`,
97
+ "aria-label": "Link to heading",
98
+ children: "#"
99
+ }
100
+ ) : null
101
+ );
102
+ };
103
+ }
104
+ function CodeRenderer({ inline, className, children, ...rest }) {
105
+ const hasLanguage = /language-([a-z0-9-]+)/i.test(className ?? "");
106
+ const isInline = inline ?? !hasLanguage;
107
+ if (isInline) {
108
+ return /* @__PURE__ */ jsx("code", { className, ...rest, children });
109
+ }
110
+ return /* @__PURE__ */ jsx("code", { className, ...rest, children });
111
+ }
112
+ function PreRenderer(props) {
113
+ const { children, ...rest } = props;
114
+ const child = Array.isArray(children) ? children[0] : children;
115
+ let language;
116
+ if (isValidElement(child)) {
117
+ const className = child.props.className;
118
+ const match = /language-([a-z0-9-]+)/i.exec(className ?? "");
119
+ language = match?.[1];
120
+ }
121
+ const label = (language ?? "text").toUpperCase();
122
+ const preRef = useRef(null);
123
+ const [copied, setCopied] = useState(false);
124
+ const resetTimerRef = useRef(null);
125
+ let codeText = "";
126
+ if (isValidElement(child)) {
127
+ const childProps = child.props;
128
+ codeText = getNodeText(childProps.children);
129
+ }
130
+ const normalized = codeText.endsWith("\n") ? codeText.slice(0, -1) : codeText;
131
+ const lineCount = Math.max(1, normalized.split("\n").length);
132
+ const digitCount = String(lineCount).length;
133
+ const onCopy = async (e) => {
134
+ e.preventDefault();
135
+ e.stopPropagation();
136
+ try {
137
+ const text = preRef.current?.textContent ?? "";
138
+ if (text && typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
139
+ await navigator.clipboard.writeText(text);
140
+ setCopied(true);
141
+ if (resetTimerRef.current) window.clearTimeout(resetTimerRef.current);
142
+ resetTimerRef.current = window.setTimeout(() => {
143
+ setCopied(false);
144
+ resetTimerRef.current = null;
145
+ }, 2e3);
146
+ }
147
+ } catch {
148
+ }
149
+ };
150
+ return /* @__PURE__ */ jsxs(
151
+ "div",
152
+ {
153
+ className: "markdown-code-block",
154
+ style: {
155
+ ["--code-gutter-width"]: `${digitCount + 1}ch`
156
+ },
157
+ children: [
158
+ /* @__PURE__ */ jsxs("div", { className: "code-toolbar", children: [
159
+ /* @__PURE__ */ jsx("span", { className: "language-label", children: label }),
160
+ /* @__PURE__ */ jsx(
161
+ "button",
162
+ {
163
+ type: "button",
164
+ className: "copy-button",
165
+ onClick: onCopy,
166
+ "aria-label": copied ? "Copied" : "Copy code",
167
+ children: copied ? /* @__PURE__ */ jsx(CheckIcon, { size: 16 }) : /* @__PURE__ */ jsx(CopyIcon, { size: 16 })
168
+ }
169
+ )
170
+ ] }),
171
+ /* @__PURE__ */ jsxs("div", { className: "code-content", children: [
172
+ /* @__PURE__ */ jsx("ol", { className: "line-numbers", "aria-hidden": true, children: Array.from({ length: lineCount }).map((_, idx) => /* @__PURE__ */ jsx("li", { children: idx + 1 }, idx + 1)) }),
173
+ /* @__PURE__ */ jsx("pre", { ref: preRef, ...rest, children })
174
+ ] })
175
+ ]
176
+ }
177
+ );
178
+ }
179
+ function markIntroduction() {
180
+ return (tree) => {
181
+ const paragraph = tree.children.find((node) => node.type === "element" && node.tagName === "p");
182
+ if (paragraph) paragraph.data = { ...paragraph.data, btstIntroduction: true };
183
+ };
184
+ }
185
+ function MarkdownContent({
186
+ markdown,
187
+ afterFirstParagraph,
188
+ className,
189
+ variant = "default",
190
+ LinkComponent = DefaultLink,
191
+ ImageComponent = DefaultImage
192
+ }) {
193
+ const [mathPlugins, setMathPlugins] = useState(null);
194
+ const [isLoadingMath, setIsLoadingMath] = useState(false);
195
+ const hasMath = useMemo(() => containsMath(markdown), [markdown]);
196
+ useEffect(() => {
197
+ if (!hasMath || mathPlugins || isLoadingMath) return;
198
+ setIsLoadingMath(true);
199
+ const katexCSSId = "katex-css";
200
+ if (typeof document !== "undefined" && !document.getElementById(katexCSSId)) {
201
+ const link = document.createElement("link");
202
+ link.id = katexCSSId;
203
+ link.rel = "stylesheet";
204
+ link.href = "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
205
+ link.integrity = "sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+";
206
+ link.crossOrigin = "anonymous";
207
+ document.head.appendChild(link);
208
+ const fontDisplayStyleId = "katex-font-display-override";
209
+ if (!document.getElementById(fontDisplayStyleId)) {
210
+ const style = document.createElement("style");
211
+ style.id = fontDisplayStyleId;
212
+ style.textContent = `
213
+ @font-face {
214
+ font-family: 'KaTeX_Main';
215
+ font-style: normal;
216
+ font-weight: normal;
217
+ font-display: swap;
218
+ src: url('https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/fonts/KaTeX_Main-Regular.woff2') format('woff2');
219
+ }
220
+ @font-face {
221
+ font-family: 'KaTeX_Math';
222
+ font-style: italic;
223
+ font-weight: normal;
224
+ font-display: swap;
225
+ src: url('https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/fonts/KaTeX_Math-Italic.woff2') format('woff2');
226
+ }
227
+ `;
228
+ document.head.appendChild(style);
229
+ }
230
+ }
231
+ Promise.all([import('remark-math'), import('rehype-katex')]).then(([remarkMathModule, rehypeKatexModule]) => {
232
+ setMathPlugins({
233
+ remarkMath: remarkMathModule.default,
234
+ rehypeKatex: rehypeKatexModule.default
235
+ });
236
+ }).catch((error) => {
237
+ console.error("Failed to load math plugins:", error);
238
+ }).finally(() => {
239
+ setIsLoadingMath(false);
240
+ });
241
+ }, [hasMath, mathPlugins, isLoadingMath]);
242
+ const AnchorRenderer = useMemo(() => {
243
+ return function AnchorRendererComponent(props) {
244
+ const { href = "", children, className: anchorClassName, ...rest } = props;
245
+ return /* @__PURE__ */ jsx(LinkComponent, { href, className: anchorClassName, ...rest, children });
246
+ };
247
+ }, [LinkComponent]);
248
+ const ImgRenderer = useMemo(() => {
249
+ return function ImgRendererComponent(props) {
250
+ const {
251
+ src = "",
252
+ alt = "",
253
+ className: imgClassName,
254
+ width,
255
+ height,
256
+ style,
257
+ ...rest
258
+ } = props;
259
+ const imageProps = {
260
+ src,
261
+ alt,
262
+ className: imgClassName,
263
+ style,
264
+ ...rest
265
+ };
266
+ if (width != null && height != null) {
267
+ imageProps.width = width;
268
+ imageProps.height = height;
269
+ } else {
270
+ return /* @__PURE__ */ jsx("span", { className: "markdown-image-wrapper", children: /* @__PURE__ */ jsx(ImageComponent, { ...imageProps }) });
271
+ }
272
+ return /* @__PURE__ */ jsx(ImageComponent, { ...imageProps });
273
+ };
274
+ }, [ImageComponent]);
275
+ const components = useMemo(() => {
276
+ return {
277
+ p: ParagraphRenderer,
278
+ a: AnchorRenderer,
279
+ img: ImgRenderer,
280
+ span: ({ node: _node, className: className2, ...props }) => /* @__PURE__ */ jsx(
281
+ "span",
282
+ {
283
+ ...props,
284
+ className: className2,
285
+ tabIndex: className2?.split(/\s+/).includes("katex") ? 0 : props.tabIndex
286
+ }
287
+ ),
288
+ code: CodeRenderer,
289
+ pre: PreRenderer,
290
+ h1: createHeadingRenderer("h1", LinkComponent),
291
+ h2: createHeadingRenderer("h2", LinkComponent),
292
+ h3: createHeadingRenderer("h3", LinkComponent),
293
+ h4: createHeadingRenderer("h4", LinkComponent),
294
+ h5: createHeadingRenderer("h5", LinkComponent),
295
+ h6: createHeadingRenderer("h6", LinkComponent),
296
+ li: createTaskListItemRenderer()
297
+ };
298
+ }, [AnchorRenderer, ImgRenderer, LinkComponent]);
299
+ const remarkPlugins = useMemo(() => {
300
+ const plugins = [remarkGfm];
301
+ if (hasMath && mathPlugins?.remarkMath) {
302
+ plugins.push(mathPlugins.remarkMath);
303
+ }
304
+ return plugins;
305
+ }, [hasMath, mathPlugins]);
306
+ const rehypePlugins = useMemo(() => {
307
+ const plugins = [rehypeRaw, rehypeHighlight, markIntroduction];
308
+ if (hasMath && mathPlugins?.rehypeKatex) {
309
+ plugins.push(mathPlugins.rehypeKatex);
310
+ }
311
+ return plugins;
312
+ }, [hasMath, mathPlugins]);
313
+ const variantClass = variant === "chat" ? "markdown-content-chat" : "";
314
+ return /* @__PURE__ */ jsx("div", { className: cn("markdown-content", variantClass, className), children: /* @__PURE__ */ jsx("div", { className: "markdown-inner", children: /* @__PURE__ */ jsx("div", { className: "markdown-body", children: /* @__PURE__ */ jsx(IntroductionSlotContext.Provider, { value: afterFirstParagraph, children: /* @__PURE__ */ jsx(
315
+ ReactMarkdown,
316
+ {
317
+ remarkPlugins,
318
+ rehypePlugins,
319
+ components,
320
+ children: markdown
321
+ }
322
+ ) }) }) }) });
323
+ }
324
+
325
+ export { MarkdownContent };