ds-markdown 0.0.10-beta.2 → 0.0.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.
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTypingTask = void 0;
4
+ const react_1 = require("react");
5
+ const useTypingTask = (options) => {
6
+ const { timerType = 'setTimeout', interval, charsRef, onEnd, onStart, onTypedChar, processCharDisplay } = options;
7
+ /** 是否卸载 */
8
+ const isUnmountRef = (0, react_1.useRef)(false);
9
+ /** 是否正在打字 */
10
+ const isTypedRef = (0, react_1.useRef)(false);
11
+ /** 动画帧ID */
12
+ const animationFrameRef = (0, react_1.useRef)(null);
13
+ /** 传统定时器(兼容模式) */
14
+ const timerRef = (0, react_1.useRef)(null);
15
+ // 已经打过的字记录
16
+ const typedCharsRef = (0, react_1.useRef)(undefined);
17
+ (0, react_1.useEffect)(() => {
18
+ isUnmountRef.current = false;
19
+ return () => {
20
+ isUnmountRef.current = true;
21
+ };
22
+ }, []);
23
+ /**
24
+ * 记录打过的字
25
+ * @param char 当前字符
26
+ * @returns
27
+ */
28
+ const recordTypedChars = (char) => {
29
+ let prevStr = '';
30
+ if (!typedCharsRef.current || typedCharsRef.current.answerType !== char.answerType) {
31
+ typedCharsRef.current = {
32
+ typedContent: char.content,
33
+ answerType: char.answerType,
34
+ prevStr: '',
35
+ };
36
+ }
37
+ else {
38
+ prevStr = typedCharsRef.current.typedContent;
39
+ typedCharsRef.current.typedContent += char.content;
40
+ typedCharsRef.current.prevStr = prevStr;
41
+ }
42
+ return {
43
+ prevStr,
44
+ nextStr: typedCharsRef.current?.typedContent || '',
45
+ };
46
+ };
47
+ /**
48
+ * 触发打字开始回调
49
+ * @param char 当前字符
50
+ */
51
+ const triggerOnStart = (char) => {
52
+ if (!onStart) {
53
+ return;
54
+ }
55
+ const { prevStr } = recordTypedChars(char);
56
+ onStart({
57
+ currentIndex: prevStr.length,
58
+ currentChar: char.content,
59
+ answerType: char.answerType,
60
+ prevStr,
61
+ });
62
+ };
63
+ /**
64
+ * 触发打字结束回调
65
+ */
66
+ const triggerOnEnd = () => {
67
+ if (!onEnd) {
68
+ return;
69
+ }
70
+ onEnd({
71
+ str: typedCharsRef.current?.typedContent,
72
+ answerType: typedCharsRef.current?.answerType,
73
+ });
74
+ };
75
+ /**
76
+ * 触发打字过程中回调
77
+ * @param char 当前字符
78
+ * @param isStartPoint 是否是开始打字(第一个字)
79
+ */
80
+ const triggerOnTypedChar = (char, isStartPoint = false) => {
81
+ if (!isStartPoint) {
82
+ recordTypedChars(char);
83
+ }
84
+ if (!onTypedChar) {
85
+ return;
86
+ }
87
+ onTypedChar({
88
+ currentIndex: typedCharsRef.current?.prevStr.length || 0,
89
+ currentChar: char.content,
90
+ answerType: char.answerType,
91
+ prevStr: typedCharsRef.current?.prevStr || '',
92
+ });
93
+ };
94
+ /** 清除定时器 */
95
+ const clearTimer = () => {
96
+ // 清理 requestAnimationFrame
97
+ if (animationFrameRef.current) {
98
+ cancelAnimationFrame(animationFrameRef.current);
99
+ animationFrameRef.current = null;
100
+ }
101
+ // 清理 setTimeout (可能被 timestamp 模式使用)
102
+ if (timerRef.current) {
103
+ clearTimeout(timerRef.current);
104
+ timerRef.current = null;
105
+ }
106
+ isTypedRef.current = false;
107
+ typedCharsRef.current = undefined;
108
+ };
109
+ /** 开始打字任务 */
110
+ const startTypedTask = () => {
111
+ if (isTypedRef.current) {
112
+ return;
113
+ }
114
+ if (timerType === 'requestAnimationFrame') {
115
+ startAnimationFrameMode();
116
+ }
117
+ else {
118
+ startTimeoutMode();
119
+ }
120
+ };
121
+ /** requestAnimationFrame 模式 */
122
+ const startAnimationFrameMode = () => {
123
+ const chars = charsRef.current;
124
+ let lastFrameTime = 0;
125
+ const frameLoop = (currentTime) => {
126
+ if (isUnmountRef.current)
127
+ return;
128
+ if (chars.length === 0) {
129
+ stopAnimationFrame();
130
+ return;
131
+ }
132
+ // 计算这一帧应该打多少个字符
133
+ if (lastFrameTime === 0) {
134
+ lastFrameTime = currentTime;
135
+ }
136
+ const deltaTime = currentTime - lastFrameTime;
137
+ const charsToType = Math.max(1, Math.floor(deltaTime / interval));
138
+ const actualCharsToType = Math.min(charsToType, chars.length);
139
+ // 处理字符
140
+ for (let i = 0; i < actualCharsToType; i++) {
141
+ const char = chars.shift();
142
+ if (char === undefined)
143
+ break;
144
+ if (!isTypedRef.current) {
145
+ isTypedRef.current = true;
146
+ triggerOnStart(char);
147
+ triggerOnTypedChar(char, true);
148
+ }
149
+ else {
150
+ triggerOnTypedChar(char);
151
+ }
152
+ processCharDisplay(char);
153
+ }
154
+ lastFrameTime = currentTime;
155
+ // 继续下一帧
156
+ if (chars.length > 0) {
157
+ animationFrameRef.current = requestAnimationFrame(frameLoop);
158
+ }
159
+ else {
160
+ isTypedRef.current = false;
161
+ triggerOnEnd();
162
+ }
163
+ };
164
+ animationFrameRef.current = requestAnimationFrame(frameLoop);
165
+ };
166
+ /** 停止动画帧模式 */
167
+ const stopAnimationFrame = () => {
168
+ isTypedRef.current = false;
169
+ if (animationFrameRef.current) {
170
+ cancelAnimationFrame(animationFrameRef.current);
171
+ animationFrameRef.current = null;
172
+ }
173
+ triggerOnEnd();
174
+ };
175
+ /** setTimeout 模式 */
176
+ const startTimeoutMode = () => {
177
+ const chars = charsRef.current;
178
+ const nextTyped = () => {
179
+ if (chars.length === 0) {
180
+ stopTimeout();
181
+ return;
182
+ }
183
+ timerRef.current = setTimeout(startTyped, interval);
184
+ };
185
+ const startTyped = (isStartPoint = false) => {
186
+ if (isUnmountRef.current)
187
+ return;
188
+ isTypedRef.current = true;
189
+ const char = chars.shift();
190
+ if (char === undefined) {
191
+ stopTimeout();
192
+ return;
193
+ }
194
+ if (isStartPoint) {
195
+ triggerOnStart(char);
196
+ triggerOnTypedChar(char, isStartPoint);
197
+ }
198
+ else {
199
+ triggerOnTypedChar(char);
200
+ }
201
+ processCharDisplay(char);
202
+ nextTyped();
203
+ };
204
+ startTyped(true);
205
+ };
206
+ /** 停止超时模式 */
207
+ const stopTimeout = () => {
208
+ isTypedRef.current = false;
209
+ if (timerRef.current) {
210
+ clearTimeout(timerRef.current);
211
+ timerRef.current = null;
212
+ }
213
+ triggerOnEnd();
214
+ };
215
+ return {
216
+ start: startTypedTask,
217
+ stop: clearTimer,
218
+ clear: () => {
219
+ clearTimer();
220
+ typedCharsRef.current = undefined;
221
+ },
222
+ isTyping: () => isTypedRef.current,
223
+ };
224
+ };
225
+ exports.useTypingTask = useTypingTask;
226
+ //# sourceMappingURL=useTypingTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTypingTask.js","sourceRoot":"","sources":["../../../src/hooks/useTypingTask.ts"],"names":[],"mappings":"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"}
@@ -58,7 +58,6 @@ const segment = edit(_segment)
58
58
  .replace(/list/, list)
59
59
  .replace(/hr/, hr)
60
60
  .getRegex();
61
- console.log(segment.source);
62
61
  const blockNormal = {
63
62
  newline,
64
63
  fences,
@@ -1 +1 @@
1
- {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/utils/rule.ts"],"names":[],"mappings":";AAAA,yBAAyB;;;AAEzB,MAAM,KAAK,GAAG;IACZ,KAAK,EAAE,aAAa;IACpB,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,WAAW,IAAI,+BAA+B,CAAC;IAC3F,eAAe,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,qDAAqD,CAAC;IACrI,eAAe,EAAE,MAAM;IACvB,YAAY,EAAE,MAAM;IACpB,SAAS,EAAE,UAAU;IACrB,UAAU,EAAE,aAAa;IACzB,eAAe,EAAE,cAAc;IAC/B,aAAa,EAAE,KAAK;IACpB,OAAO,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,oDAAoD,CAAC;IAC5H,iBAAiB,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IACtF,cAAc,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,oBAAoB,EAAE,GAAG,CAAC;IACxG,gBAAgB,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,iBAAiB,CAAC;CACnG,CAAC;AAEF;;;;;GAKG;AACH,SAAS,IAAI,CAAC,KAAsB,EAAE,GAAG,GAAG,EAAE;IAC5C,IAAI,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IAC9D,MAAM,GAAG,GAAG;QACV,OAAO,EAAE,CAAC,IAAqB,EAAE,GAAoB,EAAE,EAAE;YACvD,IAAI,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YAC3D,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACjD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACzC,OAAO,GAAG,CAAC;QACb,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,OAAO,GAAG,sBAAsB,CAAC;AACvC,MAAM,EAAE,GAAG,oEAAoE,CAAC;AAChF,SAAS;AACT,MAAM,MAAM,GAAG,uBAAuB,CAAC;AACvC,SAAS;AACT,MAAM,IAAI,GAAG,IAAI,CAAC,sCAAsC,CAAC;KACtD,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC;KACxB,QAAQ,EAAE,CAAC;AAEd;;GAEG;AACH,MAAM,MAAM,GAAG,6GAA6G,CAAC;AAC7H,MAAM;AACN,oEAAoE;AACpE,MAAM,QAAQ,GAAG,qDAAqD,CAAC;AAEvE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;KAC3B,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC;KACzB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;KACrB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;KACjB,QAAQ,EAAE,CAAC;AAEd,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5B,MAAM,WAAW,GAAG;IAClB,OAAO;IACP,MAAM;IACN,OAAO;IACP,IAAI;IACJ,EAAE;CACH,CAAC;AAEW,QAAA,KAAK,GAAG;IACnB,KAAK,EAAE,WAAW;IAClB,KAAK;CACN,CAAC"}
1
+ {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/utils/rule.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2
+ import { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
3
  import HighReactMarkdown from '../components/HighReactMarkdown/index.js';
4
4
  import classNames from 'classnames';
5
5
  import { compiler } from '../utils/compiler.js';
6
6
  import { __DEV__ } from '../constant.js';
7
7
  import deepClone from '../utils/methods/deepClone.js';
8
- const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onEnd, onStart, onTypedChar }, ref) => {
8
+ import { useTypingTask } from '../hooks/useTypingTask.js';
9
+ const MarkdownCMD = forwardRef(({ interval = 30, onEnd, onStart, onTypedChar, timerType = 'requestAnimationFrame' }, ref) => {
9
10
  /** 当前需要打字的内容 */
10
11
  const charsRef = useRef([]);
11
12
  /**
@@ -13,23 +14,6 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
13
14
  * 如果打字已经完全结束,则不会再触发打字效果
14
15
  */
15
16
  const isWholeTypedEndRef = useRef(false);
16
- /** 已经打过的字 */
17
- const typedCharsRef = useRef(undefined);
18
- /** 是否卸载 */
19
- const isUnmountRef = useRef(false);
20
- /** 是否正在打字 */
21
- const isTypedRef = useRef(false);
22
- /** 打字结束回调, */
23
- const onEndRef = useRef(onEnd);
24
- onEndRef.current = onEnd;
25
- /** 打字开始回调 */
26
- const onStartRef = useRef(onStart);
27
- onStartRef.current = onStart;
28
- /** 打字过程中回调 */
29
- const onTypedCharRef = useRef(onTypedChar);
30
- onTypedCharRef.current = onTypedChar;
31
- /** 打字定时器 */
32
- const timerRef = useRef(null);
33
17
  /**
34
18
  * 稳定段落
35
19
  * 稳定段落是已经打过字,并且不会再变化的段落
@@ -39,174 +23,22 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
39
23
  const [currentSegment, setCurrentSegment] = useState(undefined);
40
24
  /** 当前段落引用 */
41
25
  const currentParagraphRef = useRef(undefined);
42
- currentParagraphRef.current = currentSegment;
43
- /** 清除打字定时器 */
44
- const clearTimer = () => {
45
- if (timerRef.current) {
46
- clearTimeout(timerRef.current);
47
- timerRef.current = null;
48
- }
49
- isTypedRef.current = false;
50
- };
51
- useEffect(() => {
52
- isUnmountRef.current = false;
53
- return () => {
54
- isUnmountRef.current = true;
55
- };
56
- }, []);
57
- /** 思考段落 */
58
- const thinkingParagraphs = useMemo(() => stableSegments.filter((paragraph) => paragraph.answerType === 'thinking'), [stableSegments]);
59
- /** 回答段落 */
60
- const answerParagraphs = useMemo(() => stableSegments.filter((paragraph) => paragraph.answerType === 'answer'), [stableSegments]);
61
- /**
62
- * 记录打过的字
63
- * @param char 当前字符
64
- * @returns
65
- */
66
- const recordTypedChars = (char) => {
67
- let prevStr = '';
68
- if (!typedCharsRef.current || typedCharsRef.current.answerType !== char.answerType) {
69
- typedCharsRef.current = {
70
- typedContent: char.content,
71
- answerType: char.answerType,
72
- prevStr: '',
73
- };
74
- }
75
- else {
76
- prevStr = typedCharsRef.current.typedContent;
77
- typedCharsRef.current.typedContent += char.content;
78
- typedCharsRef.current.prevStr = prevStr;
79
- }
80
- return {
81
- prevStr,
82
- nextStr: typedCharsRef.current?.typedContent || '',
83
- };
84
- };
26
+ // currentParagraphRef.current = currentSegment;
85
27
  /**
86
- * 触发打字开始回调
87
- * @param char 当前字符
28
+ * 处理字符显示逻辑
88
29
  */
89
- const triggerOnStart = (char) => {
90
- const onStartFn = onStartRef.current;
91
- if (!onStartFn) {
92
- return;
93
- }
94
- const { prevStr } = recordTypedChars(char);
95
- onStartRef.current?.({
96
- currentIndex: prevStr.length,
97
- currentChar: char.content,
98
- answerType: char.answerType,
99
- prevStr,
100
- });
101
- };
102
- /**
103
- * 触发打字结束回调
104
- */
105
- const triggerOnEnd = () => {
106
- const onEndFn = onEndRef.current;
107
- if (!onEndFn) {
108
- return;
109
- }
110
- onEndFn({
111
- str: typedCharsRef.current?.typedContent,
112
- answerType: typedCharsRef.current?.answerType,
113
- });
114
- };
115
- /**
116
- * 触发打字过程中回调
117
- * @param char 当前字符
118
- * @param isStartPoint 是否是开始打字(第一个字)
119
- */
120
- const triggerOnTypedChar = (char, isStartPoint = false) => {
121
- const onTypedCharFn = onTypedCharRef.current;
122
- if (!isStartPoint) {
123
- recordTypedChars(char);
124
- }
125
- if (!onTypedCharFn) {
126
- return;
127
- }
128
- onTypedCharFn({
129
- currentIndex: typedCharsRef.current?.prevStr.length || 0,
130
- currentChar: char.content,
131
- answerType: char.answerType,
132
- prevStr: typedCharsRef.current?.prevStr || '',
133
- });
134
- };
135
- /** 开始打字任务 */
136
- const startTypedTask = () => {
137
- if (isTypedRef.current) {
138
- return;
139
- }
140
- const chars = charsRef.current;
141
- /** 停止打字 */
142
- const stopTyped = () => {
143
- isTypedRef.current = false;
144
- if (timerRef.current) {
145
- clearTimeout(timerRef.current);
146
- timerRef.current = null;
147
- }
148
- triggerOnEnd();
149
- };
150
- /** 打下一个字 */
151
- const nextTyped = () => {
152
- if (chars.length === 0) {
153
- stopTyped();
154
- return;
155
- }
156
- timerRef.current = setTimeout(startTyped, interval);
157
- };
158
- /**
159
- * 开始打字
160
- * @param isStartPoint 是否是开始打字
161
- */
162
- function startTyped(isStartPoint = false) {
163
- if (isUnmountRef.current) {
164
- return;
165
- }
166
- isTypedRef.current = true;
167
- const char = chars.shift();
168
- if (char === undefined) {
169
- stopTyped();
170
- return;
171
- }
172
- if (isStartPoint) {
173
- triggerOnStart(char);
174
- triggerOnTypedChar(char, isStartPoint);
175
- }
176
- else {
177
- triggerOnTypedChar(char);
178
- }
179
- const currentSegment = currentParagraphRef.current;
180
- /** 如果碰到 space,和split_segment 则需要处理成两个段落 */
181
- if (char.contentType === 'space' || char.contentType === 'split_segment') {
182
- if (currentSegment) {
183
- setStableSegments((prev) => {
184
- const newParagraphs = [...prev];
185
- // 放入到稳定队列
186
- if (currentSegment) {
187
- newParagraphs.push({ ...currentSegment, isTyped: false });
188
- }
189
- if (char.contentType === 'space') {
190
- newParagraphs.push({
191
- content: '',
192
- isTyped: false,
193
- type: 'br',
194
- answerType: char.answerType,
195
- tokensReference: {
196
- [char.tokenId]: {
197
- startIndex: 0,
198
- raw: char.content,
199
- },
200
- },
201
- });
202
- }
203
- return newParagraphs;
204
- });
205
- setCurrentSegment(undefined);
206
- }
207
- else {
208
- setStableSegments((prev) => {
209
- const newParagraphs = [...prev];
30
+ const processCharDisplay = (char) => {
31
+ const currentSegment = currentParagraphRef.current;
32
+ /** 如果碰到 space,和split_segment 则需要处理成两个段落 */
33
+ if (char.contentType === 'space' || char.contentType === 'split_segment') {
34
+ if (currentSegment) {
35
+ setStableSegments((prev) => {
36
+ const newParagraphs = [...prev];
37
+ // 放入到稳定队列
38
+ if (currentSegment) {
39
+ newParagraphs.push({ ...currentSegment, isTyped: false });
40
+ }
41
+ if (char.contentType === 'space') {
210
42
  newParagraphs.push({
211
43
  content: '',
212
44
  isTyped: false,
@@ -219,58 +51,88 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
219
51
  },
220
52
  },
221
53
  });
222
- return newParagraphs;
223
- });
224
- }
225
- nextTyped();
226
- return;
227
- }
228
- // 处理当前段落
229
- let _currentParagraph = currentSegment;
230
- const newCurrentParagraph = {
231
- content: '',
232
- isTyped: false,
233
- type: 'text',
234
- answerType: char.answerType,
235
- tokensReference: {},
236
- };
237
- if (!_currentParagraph) {
238
- // 如果当前没有段落,则直接设置为当前段落
239
- _currentParagraph = newCurrentParagraph;
54
+ }
55
+ return newParagraphs;
56
+ });
57
+ setCurrentSegment(() => undefined);
58
+ currentParagraphRef.current = undefined;
240
59
  }
241
- else if (currentSegment && currentSegment?.answerType !== char.answerType) {
242
- // 如果当前段落和当前字符的回答类型不一致,则需要处理成两个段落
60
+ else {
243
61
  setStableSegments((prev) => {
244
62
  const newParagraphs = [...prev];
245
- newParagraphs.push({ ...currentSegment, isTyped: false });
63
+ newParagraphs.push({
64
+ content: '',
65
+ isTyped: false,
66
+ type: 'br',
67
+ answerType: char.answerType,
68
+ tokensReference: {
69
+ [char.tokenId]: {
70
+ startIndex: 0,
71
+ raw: char.content,
72
+ },
73
+ },
74
+ });
246
75
  return newParagraphs;
247
76
  });
248
- _currentParagraph = newCurrentParagraph;
249
- setCurrentSegment(_currentParagraph);
250
77
  }
251
- setCurrentSegment((prev) => {
252
- const tokensReference = deepClone(_currentParagraph.tokensReference);
253
- if (tokensReference[char.tokenId]) {
254
- tokensReference[char.tokenId].raw += char.content;
255
- tokensReference[char.tokenId].startIndex = prev?.content?.length || 0;
256
- }
257
- else {
258
- tokensReference[char.tokenId] = {
259
- startIndex: prev?.content?.length || 0,
260
- raw: char.content,
261
- };
262
- }
263
- return {
264
- ..._currentParagraph,
265
- tokensReference,
266
- content: (prev?.content || '') + char.content,
267
- isTyped: true,
268
- };
78
+ return;
79
+ }
80
+ // 处理当前段落
81
+ const newCurrentParagraph = {
82
+ content: '',
83
+ isTyped: false,
84
+ type: 'text',
85
+ answerType: char.answerType,
86
+ tokensReference: {},
87
+ };
88
+ let _currentParagraph = currentSegment;
89
+ if (!_currentParagraph) {
90
+ // 如果当前没有段落,则直接设置为新当前段落
91
+ _currentParagraph = newCurrentParagraph;
92
+ }
93
+ else if (currentSegment && currentSegment?.answerType !== char.answerType) {
94
+ // 如果当前段落和当前字符的回答类型不一致,则需要处理成两个段落
95
+ setStableSegments((prev) => {
96
+ const newParagraphs = [...prev];
97
+ newParagraphs.push({ ...currentSegment, isTyped: false });
98
+ return newParagraphs;
269
99
  });
270
- nextTyped();
100
+ _currentParagraph = newCurrentParagraph;
101
+ }
102
+ const tokensReference = deepClone(_currentParagraph.tokensReference);
103
+ if (tokensReference[char.tokenId]) {
104
+ tokensReference[char.tokenId].raw += char.content;
105
+ tokensReference[char.tokenId].startIndex = currentSegment?.content?.length || 0;
271
106
  }
272
- startTyped(true);
107
+ else {
108
+ tokensReference[char.tokenId] = {
109
+ startIndex: currentSegment?.content?.length || 0,
110
+ raw: char.content,
111
+ };
112
+ }
113
+ const newCurrentSegment = {
114
+ ..._currentParagraph,
115
+ tokensReference,
116
+ content: (currentSegment?.content || '') + char.content,
117
+ isTyped: true,
118
+ };
119
+ currentParagraphRef.current = newCurrentSegment;
120
+ setCurrentSegment(() => newCurrentSegment);
273
121
  };
122
+ /** 思考段落 */
123
+ const thinkingParagraphs = useMemo(() => stableSegments.filter((paragraph) => paragraph.answerType === 'thinking'), [stableSegments]);
124
+ /** 回答段落 */
125
+ const answerParagraphs = useMemo(() => stableSegments.filter((paragraph) => paragraph.answerType === 'answer'), [stableSegments]);
126
+ // 使用新的打字任务 hook
127
+ const typingTask = useTypingTask({
128
+ timerType,
129
+ interval,
130
+ charsRef,
131
+ onEnd,
132
+ onStart,
133
+ onTypedChar,
134
+ processCharDisplay,
135
+ });
274
136
  const lastSegmentRawRef = useRef({
275
137
  thinking: '',
276
138
  answer: '',
@@ -284,6 +146,9 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
284
146
  * @param answerType 回答类型 {AnswerType}
285
147
  */
286
148
  push: (content, answerType) => {
149
+ if (content.length === 0) {
150
+ return;
151
+ }
287
152
  const lastSegmentReference = lastSegmentRawRef.current[`${answerType}Reference`];
288
153
  if (isWholeTypedEndRef.current) {
289
154
  if (__DEV__) {
@@ -301,7 +166,6 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
301
166
  else {
302
167
  currentLastSegmentRaw = content;
303
168
  }
304
- // debugger;
305
169
  const tokens = compiler(currentLastSegmentRaw);
306
170
  // 如果最后一个token是space,则把lastSegmentRaw设置为空
307
171
  if (tokens[tokens.length - 1].type === 'space') {
@@ -360,19 +224,21 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
360
224
  }
361
225
  }
362
226
  lastSegmentRawRef.current[`${answerType}Reference`] = currentLastSegmentReference;
363
- if (!isTypedRef.current) {
364
- startTypedTask();
227
+ if (!typingTask.isTyping()) {
228
+ typingTask.start();
365
229
  }
366
230
  },
367
231
  /**
368
232
  * 清除打字任务
369
233
  */
370
234
  clear: () => {
371
- clearTimer();
235
+ typingTask.stop();
372
236
  charsRef.current = [];
373
237
  setStableSegments([]);
374
238
  setCurrentSegment(undefined);
375
239
  isWholeTypedEndRef.current = false;
240
+ currentParagraphRef.current = undefined;
241
+ typingTask.clear();
376
242
  lastSegmentRawRef.current = {
377
243
  thinking: '',
378
244
  answer: '',
@@ -385,8 +251,12 @@ const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onE
385
251
  */
386
252
  triggerWholeEnd: () => {
387
253
  isWholeTypedEndRef.current = true;
388
- if (!isTypedRef.current) {
389
- triggerOnEnd();
254
+ if (!typingTask.isTyping()) {
255
+ // 这里需要手动触发结束回调,因为 hook 中的 triggerOnEnd 不能直接调用
256
+ onEnd?.({
257
+ str: undefined,
258
+ answerType: undefined,
259
+ });
390
260
  }
391
261
  },
392
262
  }));