ds-markdown 0.1.10-beta.0 → 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 +9 -3
- 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 +70 -35
- package/dist/cjs/index.js +130 -269
- 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 +70 -35
- package/dist/esm/index.js +85 -227
- 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 }) => {
|
|
@@ -290,7 +304,8 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
290
304
|
const currentMath = themeState.math;
|
|
291
305
|
const currentPlugins = themeState.plugins;
|
|
292
306
|
const mathSplitSymbol = currentMath?.splitSymbol ?? 'dollar';
|
|
293
|
-
const
|
|
307
|
+
const finalReplaceMathBracket = currentMath?.replaceMathBracket ?? replaceMathBracket;
|
|
308
|
+
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } = react.useMemo(() => {
|
|
294
309
|
let hasKatexPlugin = false;
|
|
295
310
|
const components = {};
|
|
296
311
|
const remarkPlugins = [gfmPlugin];
|
|
@@ -326,13 +341,13 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
326
341
|
components,
|
|
327
342
|
};
|
|
328
343
|
}, [currentPlugins]);
|
|
329
|
-
const children =
|
|
344
|
+
const children = react.useMemo(() => {
|
|
330
345
|
/** 如果存在数学公式插件,并且数学公式分隔符为括号,则替换成 $ 符号 */
|
|
331
346
|
if (hasKatexPlugin && mathSplitSymbol === 'bracket') {
|
|
332
|
-
return
|
|
347
|
+
return finalReplaceMathBracket(_children);
|
|
333
348
|
}
|
|
334
349
|
return _children;
|
|
335
|
-
}, [hasKatexPlugin, mathSplitSymbol, _children]);
|
|
350
|
+
}, [hasKatexPlugin, mathSplitSymbol, finalReplaceMathBracket, _children]);
|
|
336
351
|
return (jsxRuntime.jsx(Markdown$2, { remarkPlugins: remarkPlugins, rehypePlugins: rehypePlugins, components: {
|
|
337
352
|
code: CodeComponent,
|
|
338
353
|
table: ({ children, ...props }) => {
|
|
@@ -341,30 +356,30 @@ const HighReactMarkdown = ({ children: _children, ...props }) => {
|
|
|
341
356
|
...components,
|
|
342
357
|
}, ...props, children: children }));
|
|
343
358
|
};
|
|
344
|
-
var HighReactMarkdown$1 =
|
|
359
|
+
var HighReactMarkdown$1 = react.memo(HighReactMarkdown);
|
|
345
360
|
|
|
346
361
|
const useTypingTask = (options) => {
|
|
347
362
|
const { timerType = 'setTimeout', interval, charsRef, onEnd, onStart, onBeforeTypedChar, onTypedChar, processCharDisplay, wholeContentRef, disableTyping, triggerUpdate, resetWholeContent, } = options;
|
|
348
363
|
/** 是否卸载 */
|
|
349
|
-
const isUnmountRef =
|
|
364
|
+
const isUnmountRef = react.useRef(false);
|
|
350
365
|
/** 是否正在打字 */
|
|
351
|
-
const isTypingRef =
|
|
366
|
+
const isTypingRef = react.useRef(false);
|
|
352
367
|
/** 动画帧ID */
|
|
353
|
-
const animationFrameRef =
|
|
368
|
+
const animationFrameRef = react.useRef(null);
|
|
354
369
|
/** 传统定时器(兼容模式) */
|
|
355
|
-
const timerRef =
|
|
370
|
+
const timerRef = react.useRef(null);
|
|
356
371
|
// 已经打过的字记录
|
|
357
|
-
const typedCharsRef =
|
|
372
|
+
const typedCharsRef = react.useRef(undefined);
|
|
358
373
|
// 是否主动调用 stop 方法
|
|
359
|
-
const typedIsManualStopRef =
|
|
360
|
-
const disableTypingRef =
|
|
374
|
+
const typedIsManualStopRef = react.useRef(false);
|
|
375
|
+
const disableTypingRef = react.useRef(disableTyping);
|
|
361
376
|
disableTypingRef.current = disableTyping;
|
|
362
|
-
const intervalRef =
|
|
377
|
+
const intervalRef = react.useRef(interval);
|
|
363
378
|
intervalRef.current = interval;
|
|
364
379
|
const getChars = () => {
|
|
365
380
|
return charsRef.current;
|
|
366
381
|
};
|
|
367
|
-
|
|
382
|
+
react.useEffect(() => {
|
|
368
383
|
isUnmountRef.current = false;
|
|
369
384
|
return () => {
|
|
370
385
|
isUnmountRef.current = true;
|
|
@@ -677,30 +692,30 @@ const useTypingTask = (options) => {
|
|
|
677
692
|
};
|
|
678
693
|
};
|
|
679
694
|
|
|
680
|
-
const MarkdownContext =
|
|
695
|
+
const MarkdownContext = react.createContext({});
|
|
681
696
|
const MarkdownProvider = ({ value, children }) => {
|
|
682
|
-
const contextValue =
|
|
697
|
+
const contextValue = react.useMemo(() => value, [value]);
|
|
683
698
|
return jsxRuntime.jsx(MarkdownContext.Provider, { value: contextValue, children: children });
|
|
684
699
|
};
|
|
685
700
|
|
|
686
|
-
const MarkdownCMDInner =
|
|
701
|
+
const MarkdownCMDInner = react.forwardRef(({ interval = 30, onEnd, onStart, onTypedChar, onBeforeTypedChar, timerType = 'setTimeout', disableTyping = false, autoStartTyping = true }, ref) => {
|
|
687
702
|
const { state: themeState } = useMarkdownThemeContext();
|
|
688
703
|
// 从 context 中获取主题配置
|
|
689
704
|
const currentTheme = themeState.theme;
|
|
690
705
|
/** 是否自动开启打字动画, 后面发生变化将不会生效 */
|
|
691
|
-
const autoStartTypingRef =
|
|
706
|
+
const autoStartTypingRef = react.useRef(autoStartTyping);
|
|
692
707
|
/** 是否打过字 */
|
|
693
|
-
const isStartedTypingRef =
|
|
708
|
+
const isStartedTypingRef = react.useRef(false);
|
|
694
709
|
/** 当前需要打字的内容 */
|
|
695
|
-
const charsRef =
|
|
710
|
+
const charsRef = react.useRef([]);
|
|
696
711
|
/**
|
|
697
712
|
* 打字是否已经完全结束
|
|
698
713
|
* 如果打字已经完全结束,则不会再触发打字效果
|
|
699
714
|
*/
|
|
700
|
-
const isWholeTypedEndRef =
|
|
701
|
-
const charIndexRef =
|
|
715
|
+
const isWholeTypedEndRef = react.useRef(false);
|
|
716
|
+
const charIndexRef = react.useRef(0);
|
|
702
717
|
/** 整个内容引用 */
|
|
703
|
-
const wholeContentRef =
|
|
718
|
+
const wholeContentRef = react.useRef({
|
|
704
719
|
thinking: {
|
|
705
720
|
content: '',
|
|
706
721
|
length: 0,
|
|
@@ -713,7 +728,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
713
728
|
},
|
|
714
729
|
allLength: 0,
|
|
715
730
|
});
|
|
716
|
-
const [, setUpdate] =
|
|
731
|
+
const [, setUpdate] = react.useState(0);
|
|
717
732
|
const triggerUpdate = () => {
|
|
718
733
|
setUpdate((prev) => prev + 1);
|
|
719
734
|
};
|
|
@@ -797,7 +812,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
797
812
|
manual: false,
|
|
798
813
|
});
|
|
799
814
|
};
|
|
800
|
-
|
|
815
|
+
react.useImperativeHandle(ref, () => ({
|
|
801
816
|
/**
|
|
802
817
|
* 添加内容
|
|
803
818
|
* @param content 内容 {string}
|
|
@@ -870,7 +885,7 @@ const MarkdownCMDInner = React.forwardRef(({ interval = 30, onEnd, onStart, onTy
|
|
|
870
885
|
if (__DEV__) {
|
|
871
886
|
MarkdownCMDInner.displayName = 'MarkdownCMD';
|
|
872
887
|
}
|
|
873
|
-
const MarkdownCMD =
|
|
888
|
+
const MarkdownCMD = react.forwardRef((props, ref) => {
|
|
874
889
|
const { children = '', answerType = 'answer', isInnerRender, ...reset } = props;
|
|
875
890
|
if (__DEV__) {
|
|
876
891
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -880,9 +895,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
880
895
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
881
896
|
}
|
|
882
897
|
}
|
|
883
|
-
const contextValue =
|
|
898
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
884
899
|
// 分离主题相关的 props
|
|
885
|
-
const themeProps =
|
|
900
|
+
const themeProps = react.useMemo(() => ({
|
|
886
901
|
theme: props.theme || DEFAULT_THEME,
|
|
887
902
|
math: props.math,
|
|
888
903
|
codeBlock: props.codeBlock,
|
|
@@ -897,9 +912,9 @@ const MarkdownCMD = React.forwardRef((props, ref) => {
|
|
|
897
912
|
});
|
|
898
913
|
|
|
899
914
|
const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...rest }) => {
|
|
900
|
-
const cmdRef =
|
|
901
|
-
const prefixRef =
|
|
902
|
-
const content =
|
|
915
|
+
const cmdRef = react.useRef(null);
|
|
916
|
+
const prefixRef = react.useRef('');
|
|
917
|
+
const content = react.useMemo(() => {
|
|
903
918
|
if (typeof _children === 'string') {
|
|
904
919
|
return _children;
|
|
905
920
|
}
|
|
@@ -908,7 +923,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
908
923
|
}
|
|
909
924
|
return '';
|
|
910
925
|
}, [_children]);
|
|
911
|
-
|
|
926
|
+
react.useEffect(() => {
|
|
912
927
|
if (prefixRef.current !== content) {
|
|
913
928
|
let newContent = '';
|
|
914
929
|
if (prefixRef.current === '') {
|
|
@@ -927,7 +942,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
927
942
|
prefixRef.current = content;
|
|
928
943
|
}
|
|
929
944
|
}, [answerType, content]);
|
|
930
|
-
|
|
945
|
+
react.useImperativeHandle(markdownRef, () => ({
|
|
931
946
|
stop: () => {
|
|
932
947
|
cmdRef.current.stop();
|
|
933
948
|
},
|
|
@@ -945,7 +960,7 @@ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...r
|
|
|
945
960
|
const { theme, math, plugins, codeBlock, ...baseProps } = rest;
|
|
946
961
|
return jsxRuntime.jsx(MarkdownCMD, { ref: cmdRef, ...baseProps, isInnerRender: true });
|
|
947
962
|
};
|
|
948
|
-
const Markdown =
|
|
963
|
+
const Markdown = react.forwardRef((props, ref) => {
|
|
949
964
|
const { children = '', answerType = 'answer', ...reset } = props;
|
|
950
965
|
if (__DEV__) {
|
|
951
966
|
if (!['thinking', 'answer'].includes(answerType)) {
|
|
@@ -955,9 +970,9 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
955
970
|
throw new Error('Markdown组件的子元素必须是一个字符串');
|
|
956
971
|
}
|
|
957
972
|
}
|
|
958
|
-
const contextValue =
|
|
973
|
+
const contextValue = react.useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
|
|
959
974
|
// 分离主题相关的 props
|
|
960
|
-
const themeProps =
|
|
975
|
+
const themeProps = react.useMemo(() => ({
|
|
961
976
|
theme: props.theme || DEFAULT_THEME,
|
|
962
977
|
math: props.math,
|
|
963
978
|
codeBlock: props.codeBlock,
|
|
@@ -966,169 +981,12 @@ const Markdown = React.forwardRef((props, ref) => {
|
|
|
966
981
|
}), [props.theme, props.math, props.codeBlock, props.plugins, props.answerType]);
|
|
967
982
|
return (jsxRuntime.jsx(MarkdownProvider, { value: contextValue, children: jsxRuntime.jsx(MarkdownThemeProvider, { value: themeProps, children: jsxRuntime.jsx(MarkdownInner, { ...props, answerType: answerType, markdownRef: ref }) }) }));
|
|
968
983
|
});
|
|
969
|
-
var Markdown$1 =
|
|
984
|
+
var Markdown$1 = react.memo(Markdown);
|
|
970
985
|
|
|
971
|
-
const IconButton = ({ icon,
|
|
972
|
-
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 });
|
|
973
988
|
};
|
|
974
989
|
|
|
975
|
-
const ToolTip = React.forwardRef(({ title, children, position = 'top', className = '', style, showArrow = true, delay = 200 }, ref) => {
|
|
976
|
-
const [isVisible, setIsVisible] = React.useState(false);
|
|
977
|
-
const [tooltipStyle, setTooltipStyle] = React.useState({});
|
|
978
|
-
const triggerRef = React.useRef(null);
|
|
979
|
-
const tooltipRef = React.useRef(null);
|
|
980
|
-
const timeoutRef = React.useRef(undefined);
|
|
981
|
-
// 转发ref到trigger元素
|
|
982
|
-
React.useImperativeHandle(ref, () => triggerRef.current, []);
|
|
983
|
-
const showTooltip = React.useCallback(() => {
|
|
984
|
-
timeoutRef.current = window.setTimeout(() => {
|
|
985
|
-
setIsVisible(true);
|
|
986
|
-
}, delay);
|
|
987
|
-
}, [delay]);
|
|
988
|
-
const hideTooltip = React.useCallback(() => {
|
|
989
|
-
if (timeoutRef.current) {
|
|
990
|
-
clearTimeout(timeoutRef.current);
|
|
991
|
-
}
|
|
992
|
-
setIsVisible(false);
|
|
993
|
-
}, []);
|
|
994
|
-
const updatePosition = React.useCallback(() => {
|
|
995
|
-
if (!triggerRef.current || !tooltipRef.current)
|
|
996
|
-
return;
|
|
997
|
-
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
998
|
-
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
999
|
-
let top = 0;
|
|
1000
|
-
let left = 0;
|
|
1001
|
-
switch (position) {
|
|
1002
|
-
case 'top':
|
|
1003
|
-
top = triggerRect.top - tooltipRect.height - 8;
|
|
1004
|
-
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
1005
|
-
break;
|
|
1006
|
-
case 'bottom':
|
|
1007
|
-
top = triggerRect.bottom + 8;
|
|
1008
|
-
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
1009
|
-
break;
|
|
1010
|
-
case 'left':
|
|
1011
|
-
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
1012
|
-
left = triggerRect.left - tooltipRect.width - 8;
|
|
1013
|
-
break;
|
|
1014
|
-
case 'right':
|
|
1015
|
-
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
1016
|
-
left = triggerRect.right + 8;
|
|
1017
|
-
break;
|
|
1018
|
-
}
|
|
1019
|
-
setTooltipStyle({
|
|
1020
|
-
position: 'fixed',
|
|
1021
|
-
top: `${top}px`,
|
|
1022
|
-
left: `${left}px`,
|
|
1023
|
-
zIndex: 1000,
|
|
1024
|
-
});
|
|
1025
|
-
}, [position]);
|
|
1026
|
-
React.useEffect(() => {
|
|
1027
|
-
if (isVisible) {
|
|
1028
|
-
updatePosition();
|
|
1029
|
-
}
|
|
1030
|
-
}, [isVisible, position, updatePosition]);
|
|
1031
|
-
React.useEffect(() => {
|
|
1032
|
-
const handleScroll = () => {
|
|
1033
|
-
if (isVisible) {
|
|
1034
|
-
updatePosition();
|
|
1035
|
-
}
|
|
1036
|
-
};
|
|
1037
|
-
const handleResize = () => {
|
|
1038
|
-
if (isVisible) {
|
|
1039
|
-
updatePosition();
|
|
1040
|
-
}
|
|
1041
|
-
};
|
|
1042
|
-
window.addEventListener('scroll', handleScroll, true);
|
|
1043
|
-
window.addEventListener('resize', handleResize);
|
|
1044
|
-
return () => {
|
|
1045
|
-
window.removeEventListener('scroll', handleScroll, true);
|
|
1046
|
-
window.removeEventListener('resize', handleResize);
|
|
1047
|
-
};
|
|
1048
|
-
}, [isVisible, updatePosition]);
|
|
1049
|
-
React.useEffect(() => {
|
|
1050
|
-
return () => {
|
|
1051
|
-
if (timeoutRef.current) {
|
|
1052
|
-
clearTimeout(timeoutRef.current);
|
|
1053
|
-
}
|
|
1054
|
-
};
|
|
1055
|
-
}, []);
|
|
1056
|
-
const renderTooltip = () => {
|
|
1057
|
-
if (!isVisible)
|
|
1058
|
-
return null;
|
|
1059
|
-
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}`) })] }));
|
|
1060
|
-
return reactDom.createPortal(tooltipElement, document.body);
|
|
1061
|
-
};
|
|
1062
|
-
// 合并原有的事件处理器
|
|
1063
|
-
const handleMouseEnter = React.useCallback((e) => {
|
|
1064
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1065
|
-
showTooltip();
|
|
1066
|
-
// 只在子元素有onMouseEnter时才调用
|
|
1067
|
-
const props = children.props;
|
|
1068
|
-
if (props.onMouseEnter) {
|
|
1069
|
-
props.onMouseEnter(e);
|
|
1070
|
-
}
|
|
1071
|
-
}, [children.props, showTooltip]);
|
|
1072
|
-
const handleMouseLeave = React.useCallback((e) => {
|
|
1073
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1074
|
-
hideTooltip();
|
|
1075
|
-
// 只在子元素有onMouseLeave时才调用
|
|
1076
|
-
const props = children.props;
|
|
1077
|
-
if (props.onMouseLeave) {
|
|
1078
|
-
props.onMouseLeave(e);
|
|
1079
|
-
}
|
|
1080
|
-
}, [children.props, hideTooltip]);
|
|
1081
|
-
const handleFocus = React.useCallback((e) => {
|
|
1082
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1083
|
-
showTooltip();
|
|
1084
|
-
// 只在子元素有onFocus时才调用
|
|
1085
|
-
const props = children.props;
|
|
1086
|
-
if (props.onFocus) {
|
|
1087
|
-
props.onFocus(e);
|
|
1088
|
-
}
|
|
1089
|
-
}, [children.props, showTooltip]);
|
|
1090
|
-
const handleBlur = React.useCallback((e) => {
|
|
1091
|
-
e.stopPropagation(); // 阻止事件冒泡
|
|
1092
|
-
hideTooltip();
|
|
1093
|
-
// 只在子元素有onBlur时才调用
|
|
1094
|
-
const props = children.props;
|
|
1095
|
-
if (props.onBlur) {
|
|
1096
|
-
props.onBlur(e);
|
|
1097
|
-
}
|
|
1098
|
-
}, [children.props, hideTooltip]);
|
|
1099
|
-
// 处理onClick事件
|
|
1100
|
-
const handleClick = React.useCallback((e) => {
|
|
1101
|
-
// 调用原有的onClick
|
|
1102
|
-
const props = children.props;
|
|
1103
|
-
if (props.onClick) {
|
|
1104
|
-
props.onClick(e);
|
|
1105
|
-
}
|
|
1106
|
-
}, [children.props]);
|
|
1107
|
-
// 检查组件是否支持ref
|
|
1108
|
-
const isForwardRef = React.isValidElement(children) &&
|
|
1109
|
-
typeof children.type === 'function' &&
|
|
1110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1111
|
-
children.type.$$typeof === Symbol.for('react.forward_ref');
|
|
1112
|
-
// 如果组件不支持ref,使用包装div的方式
|
|
1113
|
-
if (!isForwardRef && typeof children.type === 'function') {
|
|
1114
|
-
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()] }));
|
|
1115
|
-
}
|
|
1116
|
-
// 使用 cloneElement 直接给子元素添加事件监听器和ref
|
|
1117
|
-
const enhancedChild = React.cloneElement(children, {
|
|
1118
|
-
ref: triggerRef,
|
|
1119
|
-
className: classNames(children.props.className, className),
|
|
1120
|
-
style: { ...children.props.style, ...style },
|
|
1121
|
-
onMouseEnter: handleMouseEnter,
|
|
1122
|
-
onMouseLeave: handleMouseLeave,
|
|
1123
|
-
onFocus: handleFocus,
|
|
1124
|
-
onBlur: handleBlur,
|
|
1125
|
-
onClick: handleClick,
|
|
1126
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1127
|
-
});
|
|
1128
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [enhancedChild, renderTooltip()] }));
|
|
1129
|
-
});
|
|
1130
|
-
ToolTip.displayName = 'ToolTip';
|
|
1131
|
-
|
|
1132
990
|
const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
1133
991
|
const handleClick = (itemValue) => {
|
|
1134
992
|
onChange?.(itemValue);
|
|
@@ -1139,17 +997,20 @@ const Segmented = ({ Segmented: segments, value, onChange }) => {
|
|
|
1139
997
|
/* eslint-disable react-refresh/only-export-components */
|
|
1140
998
|
|
|
1141
999
|
exports.Button = Button;
|
|
1000
|
+
exports.CheckMarkIcon = CheckMarkIcon;
|
|
1142
1001
|
exports.CodeBlockActions = CodeBlockActions;
|
|
1143
1002
|
exports.CodeBlockWrap = CodeBlockWrap;
|
|
1144
1003
|
exports.ConfigProvider = ConfigProvider;
|
|
1145
1004
|
exports.CopyButton = CopyButton;
|
|
1005
|
+
exports.CopyIcon = CopyIcon;
|
|
1146
1006
|
exports.DownloadButton = DownloadButton;
|
|
1007
|
+
exports.DownloadIcon = DownloadIcon;
|
|
1147
1008
|
exports.HighlightCode = HighlightCode;
|
|
1148
1009
|
exports.IconButton = IconButton;
|
|
1149
1010
|
exports.Markdown = Markdown$1;
|
|
1150
1011
|
exports.MarkdownCMD = MarkdownCMD;
|
|
1151
1012
|
exports.Segmented = Segmented;
|
|
1152
|
-
exports.
|
|
1013
|
+
exports.SuccessButton = SuccessButton;
|
|
1153
1014
|
exports.default = Markdown$1;
|
|
1154
1015
|
exports.useConfig = useConfig;
|
|
1155
1016
|
exports.useLocale = useLocale;
|