ds-markdown 0.1.10-beta.1 → 0.1.10-beta.3
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 +18 -10
- package/dist/cjs/i18n/en/index.js +8 -0
- package/dist/cjs/i18n/en/index.js.map +1 -1
- package/dist/cjs/i18n/zh/index.d.ts +18 -10
- package/dist/cjs/i18n/zh/index.js +8 -0
- package/dist/cjs/i18n/zh/index.js.map +1 -1
- package/dist/cjs/index.d.ts +70 -35
- package/dist/cjs/index.js +128 -267
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/i18n/en/index.d.ts +18 -10
- package/dist/esm/i18n/en/index.js +8 -0
- package/dist/esm/i18n/en/index.js.map +1 -1
- package/dist/esm/i18n/zh/index.d.ts +18 -10
- package/dist/esm/i18n/zh/index.js +8 -0
- package/dist/esm/i18n/zh/index.js.map +1 -1
- package/dist/esm/index.d.ts +70 -35
- package/dist/esm/index.js +83 -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,21 @@ const zhCN = {
|
|
|
120
101
|
zoomOut: '缩小',
|
|
121
102
|
zoomIn: '放大',
|
|
122
103
|
download: '下载',
|
|
104
|
+
fullScreen: '全屏',
|
|
105
|
+
exitFullScreen: '退出全屏',
|
|
106
|
+
downloadImage: '下载图片',
|
|
107
|
+
downloadedImage: '已下载',
|
|
108
|
+
copyImage: '复制图片',
|
|
109
|
+
copiedImage: '已复制',
|
|
110
|
+
fitInView: '适应页面',
|
|
123
111
|
},
|
|
124
112
|
};
|
|
125
113
|
|
|
126
|
-
const ConfigContext =
|
|
114
|
+
const ConfigContext = react.createContext({
|
|
127
115
|
locale: zhCN,
|
|
128
116
|
});
|
|
129
117
|
const ConfigProvider = ({ locale, children, mermaidConfig }) => {
|
|
130
|
-
const contextValue =
|
|
118
|
+
const contextValue = react.useMemo(() => {
|
|
131
119
|
const contextValue = {
|
|
132
120
|
locale: locale || zhCN,
|
|
133
121
|
};
|
|
@@ -140,7 +128,7 @@ const ConfigProvider = ({ locale, children, mermaidConfig }) => {
|
|
|
140
128
|
};
|
|
141
129
|
// Hook 用于在组件中使用配置
|
|
142
130
|
const useConfig = () => {
|
|
143
|
-
const context =
|
|
131
|
+
const context = react.useContext(ConfigContext);
|
|
144
132
|
return context;
|
|
145
133
|
};
|
|
146
134
|
// Hook 用于获取当前语言包
|
|
@@ -149,60 +137,86 @@ const useLocale = () => {
|
|
|
149
137
|
return locale;
|
|
150
138
|
};
|
|
151
139
|
|
|
152
|
-
const
|
|
140
|
+
const Button = ({ className = '', children, icon, onClick, style, disabled = false, ...restProps }) => {
|
|
141
|
+
const handleClick = (e) => {
|
|
142
|
+
if (disabled) {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
onClick?.();
|
|
147
|
+
};
|
|
148
|
+
return (jsxRuntime.jsxs("div", { role: "button", className: classNames({
|
|
149
|
+
'ds-button': true,
|
|
150
|
+
'ds-button__disabled': disabled,
|
|
151
|
+
}, className), onClick: handleClick, style: style, "aria-disabled": disabled, ...restProps, children: [icon && jsxRuntime.jsx("div", { className: "ds-button__icon", children: icon }), children] }));
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const SuccessButton = (props) => {
|
|
155
|
+
const { onClick, icon, executeText, children, ...rest } = props;
|
|
156
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
157
|
+
const [isSuccess, setIsSuccess] = react.useState(false);
|
|
158
|
+
const isUnmounted = react.useRef(false);
|
|
159
|
+
const handleClick = async () => {
|
|
160
|
+
if (isLoading || isSuccess) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
// 如果onClick不是异步函数,则直接调用
|
|
165
|
+
const returnValue = onClick();
|
|
166
|
+
if (returnValue instanceof Promise) {
|
|
167
|
+
setIsLoading(true);
|
|
168
|
+
const result = await returnValue;
|
|
169
|
+
if (result) {
|
|
170
|
+
setIsSuccess(true);
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
if (!isUnmounted.current) {
|
|
173
|
+
setIsSuccess(false);
|
|
174
|
+
}
|
|
175
|
+
}, 1000);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
setIsSuccess(false);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
setIsLoading(false);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
react.useEffect(() => {
|
|
187
|
+
isUnmounted.current = false;
|
|
188
|
+
return () => {
|
|
189
|
+
isUnmounted.current = true;
|
|
190
|
+
};
|
|
191
|
+
}, []);
|
|
192
|
+
return (jsxRuntime.jsx(Button, { ...rest, onClick: handleClick, icon: isSuccess ? jsxRuntime.jsx(CheckMarkIcon, { size: 24 }) : icon, children: isSuccess ? executeText || children : children }));
|
|
193
|
+
};
|
|
194
|
+
|
|
153
195
|
const CopyButton = ({ codeContent, style, className }) => {
|
|
154
196
|
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
197
|
const handleCopy = async () => {
|
|
161
|
-
if (isCopying || !codeContent)
|
|
162
|
-
return;
|
|
163
|
-
setIsCopying(true);
|
|
164
198
|
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);
|
|
199
|
+
await navigator.clipboard.writeText(codeContent || '');
|
|
200
|
+
return true;
|
|
173
201
|
}
|
|
174
202
|
catch (err) {
|
|
175
203
|
// 降级方案:使用传统方法
|
|
176
204
|
const textArea = document.createElement('textarea');
|
|
177
|
-
textArea.value = codeContent;
|
|
178
|
-
document.body.appendChild(textArea);
|
|
205
|
+
textArea.value = codeContent || '';
|
|
179
206
|
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);
|
|
207
|
+
document.execCommand('copy');
|
|
208
|
+
return true;
|
|
195
209
|
}
|
|
196
210
|
};
|
|
197
|
-
return (jsxRuntime.jsx(
|
|
211
|
+
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
212
|
};
|
|
199
213
|
|
|
200
214
|
const DownloadButton = ({ codeContent, language, style, className }) => {
|
|
201
215
|
const { locale } = useConfig();
|
|
202
216
|
// 下载文件
|
|
203
|
-
const handleDownload = () => {
|
|
217
|
+
const handleDownload = async () => {
|
|
204
218
|
if (!codeContent)
|
|
205
|
-
return;
|
|
219
|
+
return false;
|
|
206
220
|
const blob = new Blob([codeContent], { type: 'text/plain;charset=utf-8' });
|
|
207
221
|
const url = URL.createObjectURL(blob);
|
|
208
222
|
const link = document.createElement('a');
|
|
@@ -248,8 +262,9 @@ const DownloadButton = ({ codeContent, language, style, className }) => {
|
|
|
248
262
|
link.click();
|
|
249
263
|
document.body.removeChild(link);
|
|
250
264
|
URL.revokeObjectURL(url);
|
|
265
|
+
return true;
|
|
251
266
|
};
|
|
252
|
-
return (jsxRuntime.jsx(
|
|
267
|
+
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
268
|
};
|
|
254
269
|
|
|
255
270
|
const CodeBlockActions = ({ codeContent, language }) => {
|
|
@@ -291,7 +306,7 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
291
306
|
const currentPlugins = themeState.plugins;
|
|
292
307
|
const mathSplitSymbol = currentMath?.splitSymbol ?? 'dollar';
|
|
293
308
|
const finalReplaceMathBracket = currentMath?.replaceMathBracket ?? replaceMathBracket;
|
|
294
|
-
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } =
|
|
309
|
+
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } = react.useMemo(() => {
|
|
295
310
|
let hasKatexPlugin = false;
|
|
296
311
|
const components = {};
|
|
297
312
|
const remarkPlugins = [gfmPlugin];
|
|
@@ -327,7 +342,7 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
327
342
|
components,
|
|
328
343
|
};
|
|
329
344
|
}, [currentPlugins]);
|
|
330
|
-
const children =
|
|
345
|
+
const children = react.useMemo(() => {
|
|
331
346
|
/** 如果存在数学公式插件,并且数学公式分隔符为括号,则替换成 $ 符号 */
|
|
332
347
|
if (hasKatexPlugin && mathSplitSymbol === 'bracket') {
|
|
333
348
|
return finalReplaceMathBracket(_children);
|
|
@@ -342,30 +357,30 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
342
357
|
...components,
|
|
343
358
|
}, ...props, children: children }));
|
|
344
359
|
};
|
|
345
|
-
var HighReactMarkdown$1 =
|
|
360
|
+
var HighReactMarkdown$1 = react.memo(HighReactMarkdown);
|
|
346
361
|
|
|
347
362
|
const useTypingTask = (options) => {
|
|
348
363
|
const { timerType = 'setTimeout', interval, charsRef, onEnd, onStart, onBeforeTypedChar, onTypedChar, processCharDisplay, wholeContentRef, disableTyping, triggerUpdate, resetWholeContent, } = options;
|
|
349
364
|
/** 是否卸载 */
|
|
350
|
-
const isUnmountRef =
|
|
365
|
+
const isUnmountRef = react.useRef(false);
|
|
351
366
|
/** 是否正在打字 */
|
|
352
|
-
const isTypingRef =
|
|
367
|
+
const isTypingRef = react.useRef(false);
|
|
353
368
|
/** 动画帧ID */
|
|
354
|
-
const animationFrameRef =
|
|
369
|
+
const animationFrameRef = react.useRef(null);
|
|
355
370
|
/** 传统定时器(兼容模式) */
|
|
356
|
-
const timerRef =
|
|
371
|
+
const timerRef = react.useRef(null);
|
|
357
372
|
// 已经打过的字记录
|
|
358
|
-
const typedCharsRef =
|
|
373
|
+
const typedCharsRef = react.useRef(undefined);
|
|
359
374
|
// 是否主动调用 stop 方法
|
|
360
|
-
const typedIsManualStopRef =
|
|
361
|
-
const disableTypingRef =
|
|
375
|
+
const typedIsManualStopRef = react.useRef(false);
|
|
376
|
+
const disableTypingRef = react.useRef(disableTyping);
|
|
362
377
|
disableTypingRef.current = disableTyping;
|
|
363
|
-
const intervalRef =
|
|
378
|
+
const intervalRef = react.useRef(interval);
|
|
364
379
|
intervalRef.current = interval;
|
|
365
380
|
const getChars = () => {
|
|
366
381
|
return charsRef.current;
|
|
367
382
|
};
|
|
368
|
-
|
|
383
|
+
react.useEffect(() => {
|
|
369
384
|
isUnmountRef.current = false;
|
|
370
385
|
return () => {
|
|
371
386
|
isUnmountRef.current = true;
|
|
@@ -678,30 +693,30 @@ const useTypingTask = (options) => {
|
|
|
678
693
|
};
|
|
679
694
|
};
|
|
680
695
|
|
|
681
|
-
const MarkdownContext =
|
|
696
|
+
const MarkdownContext = react.createContext({});
|
|
682
697
|
const MarkdownProvider = ({ value, children }) => {
|
|
683
|
-
const contextValue =
|
|
698
|
+
const contextValue = react.useMemo(() => value, [value]);
|
|
684
699
|
return jsxRuntime.jsx(MarkdownContext.Provider, { value: contextValue, children: children });
|
|
685
700
|
};
|
|
686
701
|
|
|
687
|
-
const MarkdownCMDInner =
|
|
702
|
+
const MarkdownCMDInner = react.forwardRef(({ interval = 30, onEnd, onStart, onTypedChar, onBeforeTypedChar, timerType = 'setTimeout', disableTyping = false, autoStartTyping = true }, ref) => {
|
|
688
703
|
const { state: themeState } = useMarkdownThemeContext();
|
|
689
704
|
// 从 context 中获取主题配置
|
|
690
705
|
const currentTheme = themeState.theme;
|
|
691
706
|
/** 是否自动开启打字动画, 后面发生变化将不会生效 */
|
|
692
|
-
const autoStartTypingRef =
|
|
707
|
+
const autoStartTypingRef = react.useRef(autoStartTyping);
|
|
693
708
|
/** 是否打过字 */
|
|
694
|
-
const isStartedTypingRef =
|
|
709
|
+
const isStartedTypingRef = react.useRef(false);
|
|
695
710
|
/** 当前需要打字的内容 */
|
|
696
|
-
const charsRef =
|
|
711
|
+
const charsRef = react.useRef([]);
|
|
697
712
|
/**
|
|
698
713
|
* 打字是否已经完全结束
|
|
699
714
|
* 如果打字已经完全结束,则不会再触发打字效果
|
|
700
715
|
*/
|
|
701
|
-
const isWholeTypedEndRef =
|
|
702
|
-
const charIndexRef =
|
|
716
|
+
const isWholeTypedEndRef = react.useRef(false);
|
|
717
|
+
const charIndexRef = react.useRef(0);
|
|
703
718
|
/** 整个内容引用 */
|
|
704
|
-
const wholeContentRef =
|
|
719
|
+
const wholeContentRef = react.useRef({
|
|
705
720
|
thinking: {
|
|
706
721
|
content: '',
|
|
707
722
|
length: 0,
|
|
@@ -714,7 +729,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
714
729
|
},
|
|
715
730
|
allLength: 0,
|
|
716
731
|
});
|
|
717
|
-
const [, setUpdate] =
|
|
732
|
+
const [, setUpdate] = react.useState(0);
|
|
718
733
|
const triggerUpdate = () => {
|
|
719
734
|
setUpdate((prev) => prev + 1);
|
|
720
735
|
};
|
|
@@ -798,7 +813,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
798
813
|
manual: false,
|
|
799
814
|
});
|
|
800
815
|
};
|
|
801
|
-
|
|
816
|
+
react.useImperativeHandle(ref, () => ({
|
|
802
817
|
/**
|
|
803
818
|
* 添加内容
|
|
804
819
|
* @param content 内容 {string}
|
|
@@ -871,7 +886,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
871
886
|
if (__DEV__) {
|
|
872
887
|
MarkdownCMDInner.displayName = 'MarkdownCMD';
|
|
873
888
|
}
|
|
874
|
-
const MarkdownCMD =
|
|
889
|
+
const MarkdownCMD = react.forwardRef((props, ref) => {
|
|
875
890
|
const { children = '', answerType = 'answer', isInnerRender, ...reset } = props;
|
|
876
891
|
if (__DEV__) {
|
|
877
892
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -881,9 +896,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
881
896
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
882
897
|
}
|
|
883
898
|
}
|
|
884
|
-
const contextValue =
|
|
899
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
885
900
|
// 分离主题相关的 props
|
|
886
|
-
const themeProps =
|
|
901
|
+
const themeProps = react.useMemo(() => ({
|
|
887
902
|
theme: props.theme || DEFAULT_THEME,
|
|
888
903
|
math: props.math,
|
|
889
904
|
codeBlock: props.codeBlock,
|
|
@@ -898,9 +913,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
898
913
|
});
|
|
899
914
|
|
|
900
915
|
const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...rest }) => {
|
|
901
|
-
const cmdRef =
|
|
902
|
-
const prefixRef =
|
|
903
|
-
const content =
|
|
916
|
+
const cmdRef = react.useRef(null);
|
|
917
|
+
const prefixRef = react.useRef('');
|
|
918
|
+
const content = react.useMemo(() => {
|
|
904
919
|
if (typeof _children === 'string') {
|
|
905
920
|
return _children;
|
|
906
921
|
}
|
|
@@ -909,7 +924,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
909
924
|
}
|
|
910
925
|
return '';
|
|
911
926
|
}, [_children]);
|
|
912
|
-
|
|
927
|
+
react.useEffect(() => {
|
|
913
928
|
if (prefixRef.current !== content) {
|
|
914
929
|
let newContent = '';
|
|
915
930
|
if (prefixRef.current === '') {
|
|
@@ -928,7 +943,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
928
943
|
prefixRef.current = content;
|
|
929
944
|
}
|
|
930
945
|
}, [answerType, content]);
|
|
931
|
-
|
|
946
|
+
react.useImperativeHandle(markdownRef, () => ({
|
|
932
947
|
stop: () => {
|
|
933
948
|
cmdRef.current.stop();
|
|
934
949
|
},
|
|
@@ -946,7 +961,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
946
961
|
const { theme, math, plugins, codeBlock, ...baseProps } = rest;
|
|
947
962
|
return jsxRuntime.jsx(MarkdownCMD, { ref: cmdRef, ...baseProps, isInnerRender: true });
|
|
948
963
|
};
|
|
949
|
-
const Markdown =
|
|
964
|
+
const Markdown = react.forwardRef((props, ref) => {
|
|
950
965
|
const { children = '', answerType = 'answer', ...reset } = props;
|
|
951
966
|
if (__DEV__) {
|
|
952
967
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -956,9 +971,9 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
956
971
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
957
972
|
}
|
|
958
973
|
}
|
|
959
|
-
const contextValue =
|
|
974
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
960
975
|
// 分离主题相关的 props
|
|
961
|
-
const themeProps =
|
|
976
|
+
const themeProps = react.useMemo(() => ({
|
|
962
977
|
theme: props.theme || DEFAULT_THEME,
|
|
963
978
|
math: props.math,
|
|
964
979
|
codeBlock: props.codeBlock,
|
|
@@ -967,169 +982,12 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
967
982
|
}), [props.theme, props.math, props.codeBlock, props.plugins, props.answerType]);
|
|
968
983
|
return (jsxRuntime.jsx(MarkdownProvider, { value: contextValue, children: jsxRuntime.jsx(MarkdownThemeProvider, { value: themeProps, children: jsxRuntime.jsx(MarkdownInner, { ...props, answerType: answerType, markdownRef: ref }) }) }));
|
|
969
984
|
});
|
|
970
|
-
var Markdown$1 =
|
|
985
|
+
var Markdown$1 = react.memo(Markdown);
|
|
971
986
|
|
|
972
|
-
const IconButton = ({ icon,
|
|
973
|
-
return jsxRuntime.jsx(Button, { icon: icon, className: `ds-icon-button ${className}`,
|
|
987
|
+
const IconButton = ({ icon, className = '', ...restProps }) => {
|
|
988
|
+
return jsxRuntime.jsx(Button, { icon: icon, className: `ds-icon-button ${className}`, ...restProps });
|
|
974
989
|
};
|
|
975
990
|
|
|
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
991
|
const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
1134
992
|
const handleClick = (itemValue) => {
|
|
1135
993
|
onChange?.(itemValue);
|
|
@@ -1140,17 +998,20 @@ const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
|
1140
998
|
/* eslint-disable react-refresh/only-export-components */
|
|
1141
999
|
|
|
1142
1000
|
exports.Button = Button;
|
|
1001
|
+
exports.CheckMarkIcon = CheckMarkIcon;
|
|
1143
1002
|
exports.CodeBlockActions = CodeBlockActions;
|
|
1144
1003
|
exports.CodeBlockWrap = CodeBlockWrap;
|
|
1145
1004
|
exports.ConfigProvider = ConfigProvider;
|
|
1146
1005
|
exports.CopyButton = CopyButton;
|
|
1006
|
+
exports.CopyIcon = CopyIcon;
|
|
1147
1007
|
exports.DownloadButton = DownloadButton;
|
|
1008
|
+
exports.DownloadIcon = DownloadIcon;
|
|
1148
1009
|
exports.HighlightCode = HighlightCode;
|
|
1149
1010
|
exports.IconButton = IconButton;
|
|
1150
1011
|
exports.Markdown = Markdown$1;
|
|
1151
1012
|
exports.MarkdownCMD = MarkdownCMD;
|
|
1152
1013
|
exports.Segmented = Segmented;
|
|
1153
|
-
exports.
|
|
1014
|
+
exports.SuccessButton = SuccessButton;
|
|
1154
1015
|
exports.default = Markdown$1;
|
|
1155
1016
|
exports.useConfig = useConfig;
|
|
1156
1017
|
exports.useLocale = useLocale;
|