ds-markdown 0.1.10-beta.1 → 0.1.10-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +272 -758
- package/README.md +7 -7
- package/dist/cjs/i18n/en/index.d.ts +17 -10
- package/dist/cjs/i18n/en/index.js +7 -0
- package/dist/cjs/i18n/en/index.js.map +1 -1
- package/dist/cjs/i18n/zh/index.d.ts +17 -10
- package/dist/cjs/i18n/zh/index.js +7 -0
- package/dist/cjs/i18n/zh/index.js.map +1 -1
- package/dist/cjs/index.d.ts +68 -35
- package/dist/cjs/index.js +127 -267
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/i18n/en/index.d.ts +17 -10
- package/dist/esm/i18n/en/index.js +7 -0
- package/dist/esm/i18n/en/index.js.map +1 -1
- package/dist/esm/i18n/zh/index.d.ts +17 -10
- package/dist/esm/i18n/zh/index.js +7 -0
- package/dist/esm/i18n/zh/index.js.map +1 -1
- package/dist/esm/index.d.ts +68 -35
- package/dist/esm/index.js +82 -225
- package/dist/esm/index.js.map +1 -1
- package/dist/style.css +4 -87
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var
|
|
6
|
+
var react = require('react');
|
|
7
7
|
var Markdown$2 = require('react-markdown');
|
|
8
8
|
var gfmPlugin = require('remark-gfm');
|
|
9
9
|
var classNames = require('classnames');
|
|
10
10
|
var reactSyntaxHighlighter = require('react-syntax-highlighter');
|
|
11
|
-
var reactDom = require('react-dom');
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* 将括号格式的数学公式转换为美元符号格式
|
|
@@ -24,44 +23,32 @@ var reactDom = require('react-dom');
|
|
|
24
23
|
* @returns 转换后的字符串
|
|
25
24
|
*/
|
|
26
25
|
const replaceMathBracket = (value) => {
|
|
27
|
-
// 1.
|
|
28
|
-
// 匹配  或 [text](url) 形式
|
|
29
|
-
const mdLinkMatches = [];
|
|
30
|
-
const protectedValue = value.replace(/!?\[[^\]]*\]\([^)]*\)/g, (m) => {
|
|
31
|
-
mdLinkMatches.push(m);
|
|
32
|
-
return `__MD_LINK_${mdLinkMatches.length - 1}__`;
|
|
33
|
-
});
|
|
34
|
-
// 2. 提取所有块级公式内容,临时替换为占位符
|
|
35
|
-
// 处理 \[...\] 格式的块级公式
|
|
26
|
+
// 1. 提取所有块级公式内容,临时替换为占位符, [...]
|
|
36
27
|
const blockMatches = [];
|
|
37
|
-
let replaced =
|
|
28
|
+
let replaced = value.replace(/\\+\[([\s\S]+?)\\+\]/g, (_m, p1) => {
|
|
38
29
|
blockMatches.push(p1);
|
|
39
30
|
return `__BLOCK_MATH_${blockMatches.length - 1}__`;
|
|
40
31
|
});
|
|
41
|
-
//
|
|
32
|
+
// 也需要兼容 $$ xxxx $$ 这种写法
|
|
42
33
|
replaced = replaced.replace(/\$\$([\s\S]+?)\$\$/g, (_m, p1) => {
|
|
43
34
|
blockMatches.push(p1);
|
|
44
35
|
return `__BLOCK_MATH_${blockMatches.length - 1}__`;
|
|
45
36
|
});
|
|
46
|
-
//
|
|
47
|
-
replaced = replaced.replace(
|
|
37
|
+
// 2. 替换块级公式外部的 ( ... ) 为 $...$
|
|
38
|
+
replaced = replaced.replace(/\\+\(([^)]+?)\\+\)/g, (_m, p1) => {
|
|
48
39
|
return '$' + p1 + '$';
|
|
49
40
|
});
|
|
50
|
-
//
|
|
41
|
+
// 3. 还原块级公式内容,保持其内部小括号原样
|
|
51
42
|
replaced = replaced.replace(/__BLOCK_MATH_(\d+)__/g, (_m, idx) => {
|
|
52
43
|
return '$$' + blockMatches[Number(idx)] + '$$';
|
|
53
44
|
});
|
|
54
|
-
// 5. 还原被保护的 Markdown 链接
|
|
55
|
-
replaced = replaced.replace(/__MD_LINK_(\d+)__/g, (_m, idx) => {
|
|
56
|
-
return mdLinkMatches[Number(idx)];
|
|
57
|
-
});
|
|
58
45
|
return replaced;
|
|
59
46
|
};
|
|
60
47
|
|
|
61
48
|
const DEFAULT_THEME = 'light';
|
|
62
49
|
const DEFAULT_ANSWER_TYPE = 'answer';
|
|
63
50
|
const DEFAULT_PLUGINS = [];
|
|
64
|
-
const MarkdownThemeContext =
|
|
51
|
+
const MarkdownThemeContext = react.createContext({
|
|
65
52
|
state: {
|
|
66
53
|
theme: DEFAULT_THEME,
|
|
67
54
|
answerType: DEFAULT_ANSWER_TYPE,
|
|
@@ -69,7 +56,7 @@ const MarkdownThemeContext = React.createContext({
|
|
|
69
56
|
methods: {},
|
|
70
57
|
});
|
|
71
58
|
const MarkdownThemeProvider = ({ value = {}, children }) => {
|
|
72
|
-
const contextValue =
|
|
59
|
+
const contextValue = react.useMemo(() => ({
|
|
73
60
|
state: {
|
|
74
61
|
theme: DEFAULT_THEME,
|
|
75
62
|
answerType: DEFAULT_ANSWER_TYPE,
|
|
@@ -81,9 +68,9 @@ const MarkdownThemeProvider = ({ value = {}, children }) => {
|
|
|
81
68
|
}), [value]);
|
|
82
69
|
return jsxRuntime.jsx(MarkdownThemeContext.Provider, { value: contextValue, children: children });
|
|
83
70
|
};
|
|
84
|
-
const useMarkdownThemeContext = () =>
|
|
71
|
+
const useMarkdownThemeContext = () => react.useContext(MarkdownThemeContext);
|
|
85
72
|
const useThemeState = () => {
|
|
86
|
-
return
|
|
73
|
+
return react.useContext(MarkdownThemeContext).state;
|
|
87
74
|
};
|
|
88
75
|
|
|
89
76
|
const CodeBlockWrap = ({ children, title }) => {
|
|
@@ -91,13 +78,6 @@ const CodeBlockWrap = ({ children, title }) => {
|
|
|
91
78
|
return (jsxRuntime.jsxs("div", { className: `md-code-block md-code-block-${theme}`, children: [jsxRuntime.jsx("div", { className: "md-code-block-banner-wrap", children: jsxRuntime.jsx("div", { className: "md-code-block-banner md-code-block-banner-lite", children: title }) }), jsxRuntime.jsx("div", { className: "md-code-block-content", children: children })] }));
|
|
92
79
|
};
|
|
93
80
|
|
|
94
|
-
const Button = ({ className = '', children, icon, onClick, style, ...restProps }) => {
|
|
95
|
-
return (jsxRuntime.jsxs("div", { role: "button", className: classNames({
|
|
96
|
-
'ds-button': true,
|
|
97
|
-
[className]: !!className,
|
|
98
|
-
}), onClick: onClick, style: style, ...restProps, children: [icon && jsxRuntime.jsx("div", { className: "ds-button__icon", children: icon }), children] }));
|
|
99
|
-
};
|
|
100
|
-
|
|
101
81
|
const CheckMarkIcon = ({ size }) => {
|
|
102
82
|
return (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M9.338 21.575a1.058 1.058 0 0 1-.53-.363L2.275 13.17a1.063 1.063 0 0 1 1.65-1.341l5.63 6.928L19.33 3.86a1.064 1.064 0 0 1 1.778 1.167L10.551 21.115a1.065 1.065 0 0 1-1.213.46z", fill: "currentColor" }) }));
|
|
103
83
|
};
|
|
@@ -113,6 +93,7 @@ const zhCN = {
|
|
|
113
93
|
copy: '复制',
|
|
114
94
|
copied: '已复制',
|
|
115
95
|
download: '下载',
|
|
96
|
+
downloaded: '已下载',
|
|
116
97
|
},
|
|
117
98
|
mermaid: {
|
|
118
99
|
diagram: '图表',
|
|
@@ -120,14 +101,20 @@ const zhCN = {
|
|
|
120
101
|
zoomOut: '缩小',
|
|
121
102
|
zoomIn: '放大',
|
|
122
103
|
download: '下载',
|
|
104
|
+
fullScreen: '全屏',
|
|
105
|
+
exitFullScreen: '退出全屏',
|
|
106
|
+
downloadImage: '下载图片',
|
|
107
|
+
downloadedImage: '已下载',
|
|
108
|
+
copyImage: '复制图片',
|
|
109
|
+
copiedImage: '已复制',
|
|
123
110
|
},
|
|
124
111
|
};
|
|
125
112
|
|
|
126
|
-
const ConfigContext =
|
|
113
|
+
const ConfigContext = react.createContext({
|
|
127
114
|
locale: zhCN,
|
|
128
115
|
});
|
|
129
116
|
const ConfigProvider = ({ locale, children, mermaidConfig }) => {
|
|
130
|
-
const contextValue =
|
|
117
|
+
const contextValue = react.useMemo(() => {
|
|
131
118
|
const contextValue = {
|
|
132
119
|
locale: locale || zhCN,
|
|
133
120
|
};
|
|
@@ -140,7 +127,7 @@ const ConfigProvider = ({ locale, children, mermaidConfig }) => {
|
|
|
140
127
|
};
|
|
141
128
|
// Hook 用于在组件中使用配置
|
|
142
129
|
const useConfig = () => {
|
|
143
|
-
const context =
|
|
130
|
+
const context = react.useContext(ConfigContext);
|
|
144
131
|
return context;
|
|
145
132
|
};
|
|
146
133
|
// Hook 用于获取当前语言包
|
|
@@ -149,60 +136,86 @@ const useLocale = () => {
|
|
|
149
136
|
return locale;
|
|
150
137
|
};
|
|
151
138
|
|
|
152
|
-
const
|
|
139
|
+
const Button = ({ className = '', children, icon, onClick, style, disabled = false, ...restProps }) => {
|
|
140
|
+
const handleClick = (e) => {
|
|
141
|
+
if (disabled) {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
onClick?.();
|
|
146
|
+
};
|
|
147
|
+
return (jsxRuntime.jsxs("div", { role: "button", className: classNames({
|
|
148
|
+
'ds-button': true,
|
|
149
|
+
'ds-button__disabled': disabled,
|
|
150
|
+
}, className), onClick: handleClick, style: style, "aria-disabled": disabled, ...restProps, children: [icon && jsxRuntime.jsx("div", { className: "ds-button__icon", children: icon }), children] }));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const SuccessButton = (props) => {
|
|
154
|
+
const { onClick, icon, executeText, children, ...rest } = props;
|
|
155
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
156
|
+
const [isSuccess, setIsSuccess] = react.useState(false);
|
|
157
|
+
const isUnmounted = react.useRef(false);
|
|
158
|
+
const handleClick = async () => {
|
|
159
|
+
if (isLoading || isSuccess) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
// 如果onClick不是异步函数,则直接调用
|
|
164
|
+
const returnValue = onClick();
|
|
165
|
+
if (returnValue instanceof Promise) {
|
|
166
|
+
setIsLoading(true);
|
|
167
|
+
const result = await returnValue;
|
|
168
|
+
if (result) {
|
|
169
|
+
setIsSuccess(true);
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
if (!isUnmounted.current) {
|
|
172
|
+
setIsSuccess(false);
|
|
173
|
+
}
|
|
174
|
+
}, 1000);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
setIsSuccess(false);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
setIsLoading(false);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
react.useEffect(() => {
|
|
186
|
+
isUnmounted.current = false;
|
|
187
|
+
return () => {
|
|
188
|
+
isUnmounted.current = true;
|
|
189
|
+
};
|
|
190
|
+
}, []);
|
|
191
|
+
return (jsxRuntime.jsx(Button, { ...rest, onClick: handleClick, icon: isSuccess ? jsxRuntime.jsx(CheckMarkIcon, { size: 24 }) : icon, children: isSuccess ? executeText || children : children }));
|
|
192
|
+
};
|
|
193
|
+
|
|
153
194
|
const CopyButton = ({ codeContent, style, className }) => {
|
|
154
195
|
const { locale } = useConfig();
|
|
155
|
-
const [copyText, setCopyText] = React.useState(locale.codeBlock.copy);
|
|
156
|
-
const [showCheckmark, setShowCheckmark] = React.useState(false);
|
|
157
|
-
const [isCopying, setIsCopying] = React.useState(false);
|
|
158
|
-
// 下载文件
|
|
159
|
-
// 复制到剪贴板
|
|
160
196
|
const handleCopy = async () => {
|
|
161
|
-
if (isCopying || !codeContent)
|
|
162
|
-
return;
|
|
163
|
-
setIsCopying(true);
|
|
164
197
|
try {
|
|
165
|
-
await navigator.clipboard.writeText(codeContent);
|
|
166
|
-
|
|
167
|
-
setShowCheckmark(true);
|
|
168
|
-
setTimeout(() => {
|
|
169
|
-
setCopyText(locale.codeBlock.copy);
|
|
170
|
-
setShowCheckmark(false);
|
|
171
|
-
setIsCopying(false);
|
|
172
|
-
}, TIMEOUT);
|
|
198
|
+
await navigator.clipboard.writeText(codeContent || '');
|
|
199
|
+
return true;
|
|
173
200
|
}
|
|
174
201
|
catch (err) {
|
|
175
202
|
// 降级方案:使用传统方法
|
|
176
203
|
const textArea = document.createElement('textarea');
|
|
177
|
-
textArea.value = codeContent;
|
|
178
|
-
document.body.appendChild(textArea);
|
|
204
|
+
textArea.value = codeContent || '';
|
|
179
205
|
textArea.select();
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
setCopyText(locale.codeBlock.copied);
|
|
183
|
-
setShowCheckmark(true);
|
|
184
|
-
setTimeout(() => {
|
|
185
|
-
setCopyText(locale.codeBlock.copy);
|
|
186
|
-
setShowCheckmark(false);
|
|
187
|
-
setIsCopying(false);
|
|
188
|
-
}, TIMEOUT);
|
|
189
|
-
}
|
|
190
|
-
catch (fallbackErr) {
|
|
191
|
-
console.error('Fallback copy failed:', fallbackErr);
|
|
192
|
-
setIsCopying(false);
|
|
193
|
-
}
|
|
194
|
-
document.body.removeChild(textArea);
|
|
206
|
+
document.execCommand('copy');
|
|
207
|
+
return true;
|
|
195
208
|
}
|
|
196
209
|
};
|
|
197
|
-
return (jsxRuntime.jsx(
|
|
210
|
+
return (jsxRuntime.jsx(SuccessButton, { onClick: handleCopy, icon: jsxRuntime.jsx(CopyIcon, { size: 24 }), executeText: locale.codeBlock.copied || 'copied', style: style, className: className, children: locale.codeBlock.copy || 'copy' }));
|
|
198
211
|
};
|
|
199
212
|
|
|
200
213
|
const DownloadButton = ({ codeContent, language, style, className }) => {
|
|
201
214
|
const { locale } = useConfig();
|
|
202
215
|
// 下载文件
|
|
203
|
-
const handleDownload = () => {
|
|
216
|
+
const handleDownload = async () => {
|
|
204
217
|
if (!codeContent)
|
|
205
|
-
return;
|
|
218
|
+
return false;
|
|
206
219
|
const blob = new Blob([codeContent], { type: 'text/plain;charset=utf-8' });
|
|
207
220
|
const url = URL.createObjectURL(blob);
|
|
208
221
|
const link = document.createElement('a');
|
|
@@ -248,8 +261,9 @@ const DownloadButton = ({ codeContent, language, style, className }) => {
|
|
|
248
261
|
link.click();
|
|
249
262
|
document.body.removeChild(link);
|
|
250
263
|
URL.revokeObjectURL(url);
|
|
264
|
+
return true;
|
|
251
265
|
};
|
|
252
|
-
return (jsxRuntime.jsx(
|
|
266
|
+
return (jsxRuntime.jsx(SuccessButton, { onClick: handleDownload, icon: jsxRuntime.jsx(DownloadIcon, { size: 24 }), executeText: locale.codeBlock.downloaded || 'Downloaded', style: style, className: className, children: locale.codeBlock.download || 'Download' }));
|
|
253
267
|
};
|
|
254
268
|
|
|
255
269
|
const CodeBlockActions = ({ codeContent, language }) => {
|
|
@@ -291,7 +305,7 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
291
305
|
const currentPlugins = themeState.plugins;
|
|
292
306
|
const mathSplitSymbol = currentMath?.splitSymbol ?? 'dollar';
|
|
293
307
|
const finalReplaceMathBracket = currentMath?.replaceMathBracket ?? replaceMathBracket;
|
|
294
|
-
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } =
|
|
308
|
+
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } = react.useMemo(() => {
|
|
295
309
|
let hasKatexPlugin = false;
|
|
296
310
|
const components = {};
|
|
297
311
|
const remarkPlugins = [gfmPlugin];
|
|
@@ -327,7 +341,7 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
327
341
|
components,
|
|
328
342
|
};
|
|
329
343
|
}, [currentPlugins]);
|
|
330
|
-
const children =
|
|
344
|
+
const children = react.useMemo(() => {
|
|
331
345
|
/** 如果存在数学公式插件,并且数学公式分隔符为括号,则替换成 $ 符号 */
|
|
332
346
|
if (hasKatexPlugin && mathSplitSymbol === 'bracket') {
|
|
333
347
|
return finalReplaceMathBracket(_children);
|
|
@@ -342,30 +356,30 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
342
356
|
...components,
|
|
343
357
|
}, ...props, children: children }));
|
|
344
358
|
};
|
|
345
|
-
var HighReactMarkdown$1 =
|
|
359
|
+
var HighReactMarkdown$1 = react.memo(HighReactMarkdown);
|
|
346
360
|
|
|
347
361
|
const useTypingTask = (options) => {
|
|
348
362
|
const { timerType = 'setTimeout', interval, charsRef, onEnd, onStart, onBeforeTypedChar, onTypedChar, processCharDisplay, wholeContentRef, disableTyping, triggerUpdate, resetWholeContent, } = options;
|
|
349
363
|
/** 是否卸载 */
|
|
350
|
-
const isUnmountRef =
|
|
364
|
+
const isUnmountRef = react.useRef(false);
|
|
351
365
|
/** 是否正在打字 */
|
|
352
|
-
const isTypingRef =
|
|
366
|
+
const isTypingRef = react.useRef(false);
|
|
353
367
|
/** 动画帧ID */
|
|
354
|
-
const animationFrameRef =
|
|
368
|
+
const animationFrameRef = react.useRef(null);
|
|
355
369
|
/** 传统定时器(兼容模式) */
|
|
356
|
-
const timerRef =
|
|
370
|
+
const timerRef = react.useRef(null);
|
|
357
371
|
// 已经打过的字记录
|
|
358
|
-
const typedCharsRef =
|
|
372
|
+
const typedCharsRef = react.useRef(undefined);
|
|
359
373
|
// 是否主动调用 stop 方法
|
|
360
|
-
const typedIsManualStopRef =
|
|
361
|
-
const disableTypingRef =
|
|
374
|
+
const typedIsManualStopRef = react.useRef(false);
|
|
375
|
+
const disableTypingRef = react.useRef(disableTyping);
|
|
362
376
|
disableTypingRef.current = disableTyping;
|
|
363
|
-
const intervalRef =
|
|
377
|
+
const intervalRef = react.useRef(interval);
|
|
364
378
|
intervalRef.current = interval;
|
|
365
379
|
const getChars = () => {
|
|
366
380
|
return charsRef.current;
|
|
367
381
|
};
|
|
368
|
-
|
|
382
|
+
react.useEffect(() => {
|
|
369
383
|
isUnmountRef.current = false;
|
|
370
384
|
return () => {
|
|
371
385
|
isUnmountRef.current = true;
|
|
@@ -678,30 +692,30 @@ const useTypingTask = (options) => {
|
|
|
678
692
|
};
|
|
679
693
|
};
|
|
680
694
|
|
|
681
|
-
const MarkdownContext =
|
|
695
|
+
const MarkdownContext = react.createContext({});
|
|
682
696
|
const MarkdownProvider = ({ value, children }) => {
|
|
683
|
-
const contextValue =
|
|
697
|
+
const contextValue = react.useMemo(() => value, [value]);
|
|
684
698
|
return jsxRuntime.jsx(MarkdownContext.Provider, { value: contextValue, children: children });
|
|
685
699
|
};
|
|
686
700
|
|
|
687
|
-
const MarkdownCMDInner =
|
|
701
|
+
const MarkdownCMDInner = react.forwardRef(({ interval = 30, onEnd, onStart, onTypedChar, onBeforeTypedChar, timerType = 'setTimeout', disableTyping = false, autoStartTyping = true }, ref) => {
|
|
688
702
|
const { state: themeState } = useMarkdownThemeContext();
|
|
689
703
|
// 从 context 中获取主题配置
|
|
690
704
|
const currentTheme = themeState.theme;
|
|
691
705
|
/** 是否自动开启打字动画, 后面发生变化将不会生效 */
|
|
692
|
-
const autoStartTypingRef =
|
|
706
|
+
const autoStartTypingRef = react.useRef(autoStartTyping);
|
|
693
707
|
/** 是否打过字 */
|
|
694
|
-
const isStartedTypingRef =
|
|
708
|
+
const isStartedTypingRef = react.useRef(false);
|
|
695
709
|
/** 当前需要打字的内容 */
|
|
696
|
-
const charsRef =
|
|
710
|
+
const charsRef = react.useRef([]);
|
|
697
711
|
/**
|
|
698
712
|
* 打字是否已经完全结束
|
|
699
713
|
* 如果打字已经完全结束,则不会再触发打字效果
|
|
700
714
|
*/
|
|
701
|
-
const isWholeTypedEndRef =
|
|
702
|
-
const charIndexRef =
|
|
715
|
+
const isWholeTypedEndRef = react.useRef(false);
|
|
716
|
+
const charIndexRef = react.useRef(0);
|
|
703
717
|
/** 整个内容引用 */
|
|
704
|
-
const wholeContentRef =
|
|
718
|
+
const wholeContentRef = react.useRef({
|
|
705
719
|
thinking: {
|
|
706
720
|
content: '',
|
|
707
721
|
length: 0,
|
|
@@ -714,7 +728,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
714
728
|
},
|
|
715
729
|
allLength: 0,
|
|
716
730
|
});
|
|
717
|
-
const [, setUpdate] =
|
|
731
|
+
const [, setUpdate] = react.useState(0);
|
|
718
732
|
const triggerUpdate = () => {
|
|
719
733
|
setUpdate((prev) => prev + 1);
|
|
720
734
|
};
|
|
@@ -798,7 +812,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
798
812
|
manual: false,
|
|
799
813
|
});
|
|
800
814
|
};
|
|
801
|
-
|
|
815
|
+
react.useImperativeHandle(ref, () => ({
|
|
802
816
|
/**
|
|
803
817
|
* 添加内容
|
|
804
818
|
* @param content 内容 {string}
|
|
@@ -871,7 +885,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
871
885
|
if (__DEV__) {
|
|
872
886
|
MarkdownCMDInner.displayName = 'MarkdownCMD';
|
|
873
887
|
}
|
|
874
|
-
const MarkdownCMD =
|
|
888
|
+
const MarkdownCMD = react.forwardRef((props, ref) => {
|
|
875
889
|
const { children = '', answerType = 'answer', isInnerRender, ...reset } = props;
|
|
876
890
|
if (__DEV__) {
|
|
877
891
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -881,9 +895,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
881
895
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
882
896
|
}
|
|
883
897
|
}
|
|
884
|
-
const contextValue =
|
|
898
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
885
899
|
// 分离主题相关的 props
|
|
886
|
-
const themeProps =
|
|
900
|
+
const themeProps = react.useMemo(() => ({
|
|
887
901
|
theme: props.theme || DEFAULT_THEME,
|
|
888
902
|
math: props.math,
|
|
889
903
|
codeBlock: props.codeBlock,
|
|
@@ -898,9 +912,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
898
912
|
});
|
|
899
913
|
|
|
900
914
|
const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...rest }) => {
|
|
901
|
-
const cmdRef =
|
|
902
|
-
const prefixRef =
|
|
903
|
-
const content =
|
|
915
|
+
const cmdRef = react.useRef(null);
|
|
916
|
+
const prefixRef = react.useRef('');
|
|
917
|
+
const content = react.useMemo(() => {
|
|
904
918
|
if (typeof _children === 'string') {
|
|
905
919
|
return _children;
|
|
906
920
|
}
|
|
@@ -909,7 +923,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
909
923
|
}
|
|
910
924
|
return '';
|
|
911
925
|
}, [_children]);
|
|
912
|
-
|
|
926
|
+
react.useEffect(() => {
|
|
913
927
|
if (prefixRef.current !== content) {
|
|
914
928
|
let newContent = '';
|
|
915
929
|
if (prefixRef.current === '') {
|
|
@@ -928,7 +942,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
928
942
|
prefixRef.current = content;
|
|
929
943
|
}
|
|
930
944
|
}, [answerType, content]);
|
|
931
|
-
|
|
945
|
+
react.useImperativeHandle(markdownRef, () => ({
|
|
932
946
|
stop: () => {
|
|
933
947
|
cmdRef.current.stop();
|
|
934
948
|
},
|
|
@@ -946,7 +960,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
946
960
|
const { theme, math, plugins, codeBlock, ...baseProps } = rest;
|
|
947
961
|
return jsxRuntime.jsx(MarkdownCMD, { ref: cmdRef, ...baseProps, isInnerRender: true });
|
|
948
962
|
};
|
|
949
|
-
const Markdown =
|
|
963
|
+
const Markdown = react.forwardRef((props, ref) => {
|
|
950
964
|
const { children = '', answerType = 'answer', ...reset } = props;
|
|
951
965
|
if (__DEV__) {
|
|
952
966
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -956,9 +970,9 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
956
970
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
957
971
|
}
|
|
958
972
|
}
|
|
959
|
-
const contextValue =
|
|
973
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
960
974
|
// 分离主题相关的 props
|
|
961
|
-
const themeProps =
|
|
975
|
+
const themeProps = react.useMemo(() => ({
|
|
962
976
|
theme: props.theme || DEFAULT_THEME,
|
|
963
977
|
math: props.math,
|
|
964
978
|
codeBlock: props.codeBlock,
|
|
@@ -967,169 +981,12 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
967
981
|
}), [props.theme, props.math, props.codeBlock, props.plugins, props.answerType]);
|
|
968
982
|
return (jsxRuntime.jsx(MarkdownProvider, { value: contextValue, children: jsxRuntime.jsx(MarkdownThemeProvider, { value: themeProps, children: jsxRuntime.jsx(MarkdownInner, { ...props, answerType: answerType, markdownRef: ref }) }) }));
|
|
969
983
|
});
|
|
970
|
-
var Markdown$1 =
|
|
984
|
+
var Markdown$1 = react.memo(Markdown);
|
|
971
985
|
|
|
972
|
-
const IconButton = ({ icon,
|
|
973
|
-
return jsxRuntime.jsx(Button, { icon: icon, className: `ds-icon-button ${className}`,
|
|
986
|
+
const IconButton = ({ icon, className = '', ...restProps }) => {
|
|
987
|
+
return jsxRuntime.jsx(Button, { icon: icon, className: `ds-icon-button ${className}`, ...restProps });
|
|
974
988
|
};
|
|
975
989
|
|
|
976
|
-
const ToolTip = React.forwardRef(({ title, children, position = 'top', className = '', style, showArrow = true, delay = 200 }, ref) => {
|
|
977
|
-
const [isVisible, setIsVisible] = React.useState(false);
|
|
978
|
-
const [tooltipStyle, setTooltipStyle] = React.useState({});
|
|
979
|
-
const triggerRef = React.useRef(null);
|
|
980
|
-
const tooltipRef = React.useRef(null);
|
|
981
|
-
const timeoutRef = React.useRef(undefined);
|
|
982
|
-
// 转发ref到trigger元素
|
|
983
|
-
React.useImperativeHandle(ref, () => triggerRef.current, []);
|
|
984
|
-
const showTooltip = React.useCallback(() => {
|
|
985
|
-
timeoutRef.current = window.setTimeout(() => {
|
|
986
|
-
setIsVisible(true);
|
|
987
|
-
}, delay);
|
|
988
|
-
}, [delay]);
|
|
989
|
-
const hideTooltip = React.useCallback(() => {
|
|
990
|
-
if (timeoutRef.current) {
|
|
991
|
-
clearTimeout(timeoutRef.current);
|
|
992
|
-
}
|
|
993
|
-
setIsVisible(false);
|
|
994
|
-
}, []);
|
|
995
|
-
const updatePosition = React.useCallback(() => {
|
|
996
|
-
if (!triggerRef.current || !tooltipRef.current)
|
|
997
|
-
return;
|
|
998
|
-
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
999
|
-
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
1000
|
-
let top = 0;
|
|
1001
|
-
let left = 0;
|
|
1002
|
-
switch (position) {
|
|
1003
|
-
case 'top':
|
|
1004
|
-
top = triggerRect.top - tooltipRect.height - 8;
|
|
1005
|
-
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
1006
|
-
break;
|
|
1007
|
-
case 'bottom':
|
|
1008
|
-
top = triggerRect.bottom + 8;
|
|
1009
|
-
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
1010
|
-
break;
|
|
1011
|
-
case 'left':
|
|
1012
|
-
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
1013
|
-
left = triggerRect.left - tooltipRect.width - 8;
|
|
1014
|
-
break;
|
|
1015
|
-
case 'right':
|
|
1016
|
-
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
1017
|
-
left = triggerRect.right + 8;
|
|
1018
|
-
break;
|
|
1019
|
-
}
|
|
1020
|
-
setTooltipStyle({
|
|
1021
|
-
position: 'fixed',
|
|
1022
|
-
top: `${top}px`,
|
|
1023
|
-
left: `${left}px`,
|
|
1024
|
-
zIndex: 1000,
|
|
1025
|
-
});
|
|
1026
|
-
}, [position]);
|
|
1027
|
-
React.useEffect(() => {
|
|
1028
|
-
if (isVisible) {
|
|
1029
|
-
updatePosition();
|
|
1030
|
-
}
|
|
1031
|
-
}, [isVisible, position, updatePosition]);
|
|
1032
|
-
React.useEffect(() => {
|
|
1033
|
-
const handleScroll = () => {
|
|
1034
|
-
if (isVisible) {
|
|
1035
|
-
updatePosition();
|
|
1036
|
-
}
|
|
1037
|
-
};
|
|
1038
|
-
const handleResize = () => {
|
|
1039
|
-
if (isVisible) {
|
|
1040
|
-
updatePosition();
|
|
1041
|
-
}
|
|
1042
|
-
};
|
|
1043
|
-
window.addEventListener('scroll', handleScroll, true);
|
|
1044
|
-
window.addEventListener('resize', handleResize);
|
|
1045
|
-
return () => {
|
|
1046
|
-
window.removeEventListener('scroll', handleScroll, true);
|
|
1047
|
-
window.removeEventListener('resize', handleResize);
|
|
1048
|
-
};
|
|
1049
|
-
}, [isVisible, updatePosition]);
|
|
1050
|
-
React.useEffect(() => {
|
|
1051
|
-
return () => {
|
|
1052
|
-
if (timeoutRef.current) {
|
|
1053
|
-
clearTimeout(timeoutRef.current);
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
}, []);
|
|
1057
|
-
const renderTooltip = () => {
|
|
1058
|
-
if (!isVisible)
|
|
1059
|
-
return null;
|
|
1060
|
-
const tooltipElement = (jsxRuntime.jsxs("div", { ref: tooltipRef, className: classNames('ds-tooltip', `ds-tooltip--${position}`), style: tooltipStyle, children: [jsxRuntime.jsx("div", { className: "ds-tooltip__title", children: title }), showArrow && jsxRuntime.jsx("div", { className: classNames('ds-tooltip__arrow', `ds-tooltip__arrow--${position}`) })] }));
|
|
1061
|
-
return reactDom.createPortal(tooltipElement, document.body);
|
|
1062
|
-
};
|
|
1063
|
-
// 合并原有的事件处理器
|
|
1064
|
-
const handleMouseEnter = React.useCallback((e) => {
|
|
1065
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1066
|
-
showTooltip();
|
|
1067
|
-
// 只在子元素有onMouseEnter时才调用
|
|
1068
|
-
const props = children.props;
|
|
1069
|
-
if (props.onMouseEnter) {
|
|
1070
|
-
props.onMouseEnter(e);
|
|
1071
|
-
}
|
|
1072
|
-
}, [children.props, showTooltip]);
|
|
1073
|
-
const handleMouseLeave = React.useCallback((e) => {
|
|
1074
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1075
|
-
hideTooltip();
|
|
1076
|
-
// 只在子元素有onMouseLeave时才调用
|
|
1077
|
-
const props = children.props;
|
|
1078
|
-
if (props.onMouseLeave) {
|
|
1079
|
-
props.onMouseLeave(e);
|
|
1080
|
-
}
|
|
1081
|
-
}, [children.props, hideTooltip]);
|
|
1082
|
-
const handleFocus = React.useCallback((e) => {
|
|
1083
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1084
|
-
showTooltip();
|
|
1085
|
-
// 只在子元素有onFocus时才调用
|
|
1086
|
-
const props = children.props;
|
|
1087
|
-
if (props.onFocus) {
|
|
1088
|
-
props.onFocus(e);
|
|
1089
|
-
}
|
|
1090
|
-
}, [children.props, showTooltip]);
|
|
1091
|
-
const handleBlur = React.useCallback((e) => {
|
|
1092
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1093
|
-
hideTooltip();
|
|
1094
|
-
// 只在子元素有onBlur时才调用
|
|
1095
|
-
const props = children.props;
|
|
1096
|
-
if (props.onBlur) {
|
|
1097
|
-
props.onBlur(e);
|
|
1098
|
-
}
|
|
1099
|
-
}, [children.props, hideTooltip]);
|
|
1100
|
-
// 处理onClick事件
|
|
1101
|
-
const handleClick = React.useCallback((e) => {
|
|
1102
|
-
// 调用原有的onClick
|
|
1103
|
-
const props = children.props;
|
|
1104
|
-
if (props.onClick) {
|
|
1105
|
-
props.onClick(e);
|
|
1106
|
-
}
|
|
1107
|
-
}, [children.props]);
|
|
1108
|
-
// 检查组件是否支持ref
|
|
1109
|
-
const isForwardRef = React.isValidElement(children) &&
|
|
1110
|
-
typeof children.type === 'function' &&
|
|
1111
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1112
|
-
children.type.$$typeof === Symbol.for('react.forward_ref');
|
|
1113
|
-
// 如果组件不支持ref,使用包装div的方式
|
|
1114
|
-
if (!isForwardRef && typeof children.type === 'function') {
|
|
1115
|
-
return (jsxRuntime.jsxs("div", { ref: triggerRef, className: classNames('ds-tooltip-trigger', className), style: style, onMouseEnter: showTooltip, onMouseLeave: hideTooltip, onFocus: showTooltip, onBlur: hideTooltip, children: [children, renderTooltip()] }));
|
|
1116
|
-
}
|
|
1117
|
-
// 使用 cloneElement 直接给子元素添加事件监听器和ref
|
|
1118
|
-
const enhancedChild = React.cloneElement(children, {
|
|
1119
|
-
ref: triggerRef,
|
|
1120
|
-
className: classNames(children.props.className, className),
|
|
1121
|
-
style: { ...children.props.style, ...style },
|
|
1122
|
-
onMouseEnter: handleMouseEnter,
|
|
1123
|
-
onMouseLeave: handleMouseLeave,
|
|
1124
|
-
onFocus: handleFocus,
|
|
1125
|
-
onBlur: handleBlur,
|
|
1126
|
-
onClick: handleClick,
|
|
1127
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1128
|
-
});
|
|
1129
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [enhancedChild, renderTooltip()] }));
|
|
1130
|
-
});
|
|
1131
|
-
ToolTip.displayName = 'ToolTip';
|
|
1132
|
-
|
|
1133
990
|
const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
1134
991
|
const handleClick = (itemValue) => {
|
|
1135
992
|
onChange?.(itemValue);
|
|
@@ -1140,17 +997,20 @@ const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
|
1140
997
|
/* eslint-disable react-refresh/only-export-components */
|
|
1141
998
|
|
|
1142
999
|
exports.Button = Button;
|
|
1000
|
+
exports.CheckMarkIcon = CheckMarkIcon;
|
|
1143
1001
|
exports.CodeBlockActions = CodeBlockActions;
|
|
1144
1002
|
exports.CodeBlockWrap = CodeBlockWrap;
|
|
1145
1003
|
exports.ConfigProvider = ConfigProvider;
|
|
1146
1004
|
exports.CopyButton = CopyButton;
|
|
1005
|
+
exports.CopyIcon = CopyIcon;
|
|
1147
1006
|
exports.DownloadButton = DownloadButton;
|
|
1007
|
+
exports.DownloadIcon = DownloadIcon;
|
|
1148
1008
|
exports.HighlightCode = HighlightCode;
|
|
1149
1009
|
exports.IconButton = IconButton;
|
|
1150
1010
|
exports.Markdown = Markdown$1;
|
|
1151
1011
|
exports.MarkdownCMD = MarkdownCMD;
|
|
1152
1012
|
exports.Segmented = Segmented;
|
|
1153
|
-
exports.
|
|
1013
|
+
exports.SuccessButton = SuccessButton;
|
|
1154
1014
|
exports.default = Markdown$1;
|
|
1155
1015
|
exports.useConfig = useConfig;
|
|
1156
1016
|
exports.useLocale = useLocale;
|