ds-markdown 0.0.13 → 0.0.14-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.en.md +12 -8
  2. package/README.ja.md +10 -8
  3. package/README.ko.md +10 -8
  4. package/README.md +13 -8
  5. package/dist/cjs/Markdown/index.d.ts +1 -1
  6. package/dist/cjs/Markdown/index.js +12 -4
  7. package/dist/cjs/Markdown/index.js.map +1 -1
  8. package/dist/cjs/MarkdownCMD/index.d.ts +2 -7
  9. package/dist/cjs/MarkdownCMD/index.js +64 -207
  10. package/dist/cjs/MarkdownCMD/index.js.map +1 -1
  11. package/dist/cjs/defined.d.ts +34 -12
  12. package/dist/cjs/hooks/useTypingTask.d.ts +8 -8
  13. package/dist/cjs/hooks/useTypingTask.js +42 -10
  14. package/dist/cjs/hooks/useTypingTask.js.map +1 -1
  15. package/dist/cjs/index.d.ts +4 -2
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/utils/Tokenizer.js +1 -1
  18. package/dist/cjs/utils/Tokenizer.js.map +1 -1
  19. package/dist/cjs/utils/compiler.js +9 -0
  20. package/dist/cjs/utils/compiler.js.map +1 -1
  21. package/dist/esm/Markdown/index.d.ts +1 -1
  22. package/dist/esm/Markdown/index.js +13 -5
  23. package/dist/esm/Markdown/index.js.map +1 -1
  24. package/dist/esm/MarkdownCMD/index.d.ts +2 -7
  25. package/dist/esm/MarkdownCMD/index.js +65 -208
  26. package/dist/esm/MarkdownCMD/index.js.map +1 -1
  27. package/dist/esm/defined.d.ts +34 -12
  28. package/dist/esm/hooks/useTypingTask.d.ts +8 -8
  29. package/dist/esm/hooks/useTypingTask.js +42 -10
  30. package/dist/esm/hooks/useTypingTask.js.map +1 -1
  31. package/dist/esm/index.d.ts +4 -2
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/utils/Tokenizer.js +1 -1
  34. package/dist/esm/utils/Tokenizer.js.map +1 -1
  35. package/dist/esm/utils/compiler.js +9 -0
  36. package/dist/esm/utils/compiler.js.map +1 -1
  37. package/package.json +2 -2
package/README.en.md CHANGED
@@ -12,7 +12,9 @@ A React component designed specifically for modern AI applications, providing sm
12
12
  [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
13
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
14
 
15
- [📖 Live Demo](https://onshinpei.github.io/ds-markdown/) | [🔧 StackBlitz Experience](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
15
+ [📖 Live Demo](https://onshinpei.github.io/ds-markdown/)
16
+
17
+ [DEMO:🔧 StackBlitz Experience](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
16
18
 
17
19
  ---
18
20
 
@@ -178,9 +180,9 @@ Let's explore these new features together!`);
178
180
  ### Imperative API (Recommended for Streaming Scenarios)
179
181
 
180
182
  ```typescript
181
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
183
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
182
184
 
183
- interface MarkdownRef {
185
+ interface MarkdownCMDRef {
184
186
  push: (content: string, answerType: AnswerType) => void;
185
187
  clear: () => void;
186
188
  triggerWholeEnd: () => void;
@@ -246,12 +248,14 @@ High-frequency recommends `requestAnimationFrame`, low-frequency recommends `set
246
248
 
247
249
  ### 📝 AI Streaming Conversation
248
250
 
251
+ [DEMO: 🔧 StackBlitz 体验](https://stackblitz.com/edit/vitejs-vite-2ri8kex3?file=src%2FApp.tsx)
252
+
249
253
  ````tsx
250
254
  import { useRef } from 'react';
251
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
255
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
252
256
 
253
257
  function StreamingChat() {
254
- const markdownRef = useRef<MarkdownRef>(null);
258
+ const markdownRef = useRef<MarkdownCMDRef>(null);
255
259
 
256
260
  // Simulate AI streaming response
257
261
  const simulateAIResponse = async () => {
@@ -378,7 +382,7 @@ const handleStreamingMarkdown = () => {
378
382
 
379
383
  ```tsx
380
384
  // ✅ Recommended: Imperative API
381
- const ref = useRef<MarkdownRef>(null);
385
+ const ref = useRef<MarkdownCMDRef>(null);
382
386
  useEffect(() => {
383
387
  ref.current?.push(newChunk, 'answer');
384
388
  }, [newChunk]);
@@ -391,9 +395,9 @@ const [content, setContent] = useState('');
391
395
  ### 3. Type Safety
392
396
 
393
397
  ```tsx
394
- import { MarkdownRef } from 'ds-markdown';
398
+ import { MarkdownCMDRef } from 'ds-markdown';
395
399
 
396
- const ref = useRef<MarkdownRef>(null);
400
+ const ref = useRef<MarkdownCMDRef>(null);
397
401
  // Complete TypeScript type hints
398
402
  ```
399
403
 
package/README.ja.md CHANGED
@@ -12,7 +12,9 @@
12
12
  [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
13
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
14
 
15
- [📖 ライブデモ](https://onshinpei.github.io/ds-markdown/) | [🔧 StackBlitz 体験](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
15
+ [📖 オンラインデモ](https://onshinpei.github.io/ds-markdown/)
16
+
17
+ [DEMO:🔧 StackBlitz 体験](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
16
18
 
17
19
  ---
18
20
 
@@ -178,9 +180,9 @@ React 19 は多くのエキサイティングな新機能をもたらします
178
180
  ### 命令的 API(ストリーミングシナリオにおすすめ)
179
181
 
180
182
  ```typescript
181
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
183
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
182
184
 
183
- interface MarkdownRef {
185
+ interface MarkdownCMDRef {
184
186
  push: (content: string, answerType: AnswerType) => void;
185
187
  clear: () => void;
186
188
  triggerWholeEnd: () => void;
@@ -248,10 +250,10 @@ interface MarkdownRef {
248
250
 
249
251
  ````tsx
250
252
  import { useRef } from 'react';
251
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
253
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
252
254
 
253
255
  function StreamingChat() {
254
- const markdownRef = useRef<MarkdownRef>(null);
256
+ const markdownRef = useRef<MarkdownCMDRef>(null);
255
257
 
256
258
  // AI ストリーミング応答をシミュレート
257
259
  const simulateAIResponse = async () => {
@@ -378,7 +380,7 @@ const handleStreamingMarkdown = () => {
378
380
 
379
381
  ```tsx
380
382
  // ✅ 推奨:命令的 API
381
- const ref = useRef<MarkdownRef>(null);
383
+ const ref = useRef<MarkdownCMDRef>(null);
382
384
  useEffect(() => {
383
385
  ref.current?.push(newChunk, 'answer');
384
386
  }, [newChunk]);
@@ -391,9 +393,9 @@ const [content, setContent] = useState('');
391
393
  ### 3. 型安全
392
394
 
393
395
  ```tsx
394
- import { MarkdownRef } from 'ds-markdown';
396
+ import { MarkdownCMDRef } from 'ds-markdown';
395
397
 
396
- const ref = useRef<MarkdownRef>(null);
398
+ const ref = useRef<MarkdownCMDRef>(null);
397
399
  // 完全な TypeScript 型ヒント
398
400
  ```
399
401
 
package/README.ko.md CHANGED
@@ -12,7 +12,9 @@
12
12
  [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
13
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
14
 
15
- [📖 라이브 데모](https://onshinpei.github.io/ds-markdown/) | [🔧 StackBlitz 체험](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
15
+ [📖 온라인 데모](https://onshinpei.github.io/ds-markdown/)
16
+
17
+ [DEMO:🔧 StackBlitz 체험](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
16
18
 
17
19
  ---
18
20
 
@@ -178,9 +180,9 @@ React 19는 많은 흥미로운 새 기능을 제공합니다:
178
180
  ### 명령형 API (스트리밍 시나리오 추천)
179
181
 
180
182
  ```typescript
181
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
183
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
182
184
 
183
- interface MarkdownRef {
185
+ interface MarkdownCMDRef {
184
186
  push: (content: string, answerType: AnswerType) => void;
185
187
  clear: () => void;
186
188
  triggerWholeEnd: () => void;
@@ -248,10 +250,10 @@ interface MarkdownRef {
248
250
 
249
251
  ````tsx
250
252
  import { useRef } from 'react';
251
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
253
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
252
254
 
253
255
  function StreamingChat() {
254
- const markdownRef = useRef<MarkdownRef>(null);
256
+ const markdownRef = useRef<MarkdownCMDRef>(null);
255
257
 
256
258
  // AI 스트리밍 응답 시뮬레이션
257
259
  const simulateAIResponse = async () => {
@@ -378,7 +380,7 @@ const handleStreamingMarkdown = () => {
378
380
 
379
381
  ```tsx
380
382
  // ✅ 추천: 명령형 API
381
- const ref = useRef<MarkdownRef>(null);
383
+ const ref = useRef<MarkdownCMDRef>(null);
382
384
  useEffect(() => {
383
385
  ref.current?.push(newChunk, 'answer');
384
386
  }, [newChunk]);
@@ -391,9 +393,9 @@ const [content, setContent] = useState('');
391
393
  ### 3. 타입 안전성
392
394
 
393
395
  ```tsx
394
- import { MarkdownRef } from 'ds-markdown';
396
+ import { MarkdownCMDRef } from 'ds-markdown';
395
397
 
396
- const ref = useRef<MarkdownRef>(null);
398
+ const ref = useRef<MarkdownCMDRef>(null);
397
399
  // 완전한 TypeScript 타입 힌트
398
400
  ```
399
401
 
package/README.md CHANGED
@@ -12,7 +12,9 @@
12
12
  [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
13
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
14
 
15
- [📖 在线演示](https://onshinpei.github.io/ds-markdown/) | [🔧 StackBlitz 体验](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
15
+ [📖 在线演示](https://onshinpei.github.io/ds-markdown/)
16
+
17
+ [DEMO:🔧 StackBlitz 体验](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
16
18
 
17
19
  ---
18
20
 
@@ -36,6 +38,7 @@
36
38
  - 高频打字支持(`requestAnimationFrame`模式下打字间隔最低可接近于`0ms`)
37
39
  - 帧同步渲染,与浏览器 60fps 完美配合
38
40
  - 智能字符批量处理,视觉效果更自然
41
+ - 支持打字的中断 `stop` 和 继续`resume`
39
42
 
40
43
  ### 🔧 **灵活易用**
41
44
 
@@ -178,9 +181,9 @@ React 19 带来了许多激动人心的新特性:
178
181
  ### 命令式 API (推荐流式场景)
179
182
 
180
183
  ```typescript
181
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
184
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
182
185
 
183
- interface MarkdownRef {
186
+ interface MarkdownCMDRef {
184
187
  push: (content: string, answerType: AnswerType) => void;
185
188
  clear: () => void;
186
189
  triggerWholeEnd: () => void;
@@ -246,12 +249,14 @@ interface MarkdownRef {
246
249
 
247
250
  ### 📝 AI 流式对话
248
251
 
252
+ [DEMO: 🔧 StackBlitz 体验](https://stackblitz.com/edit/vitejs-vite-2ri8kex3?file=src%2FApp.tsx)
253
+
249
254
  ````tsx
250
255
  import { useRef } from 'react';
251
- import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
256
+ import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
252
257
 
253
258
  function StreamingChat() {
254
- const markdownRef = useRef<MarkdownRef>(null);
259
+ const markdownRef = useRef<MarkdownCMDRef>(null);
255
260
 
256
261
  // 模拟 AI 流式响应
257
262
  const simulateAIResponse = async () => {
@@ -378,7 +383,7 @@ const handleStreamingMarkdown = () => {
378
383
 
379
384
  ```tsx
380
385
  // ✅ 推荐:命令式 API
381
- const ref = useRef<MarkdownRef>(null);
386
+ const ref = useRef<MarkdownCMDRef>(null);
382
387
  useEffect(() => {
383
388
  ref.current?.push(newChunk, 'answer');
384
389
  }, [newChunk]);
@@ -391,9 +396,9 @@ const [content, setContent] = useState('');
391
396
  ### 3. 类型安全
392
397
 
393
398
  ```tsx
394
- import { MarkdownRef } from 'ds-markdown';
399
+ import { MarkdownCMDRef } from 'ds-markdown';
395
400
 
396
- const ref = useRef<MarkdownRef>(null);
401
+ const ref = useRef<MarkdownCMDRef>(null);
397
402
  // 完整的 TypeScript 类型提示
398
403
  ```
399
404
 
@@ -5,5 +5,5 @@ interface MarkdownImplProps extends MarkdownProps {
5
5
  answerType: AnswerType;
6
6
  theme?: Theme;
7
7
  }
8
- declare const _default: React.NamedExoticComponent<MarkdownImplProps>;
8
+ declare const _default: React.NamedExoticComponent<MarkdownImplProps & React.RefAttributes<import("../defined.js").MarkdownBaseRef>>;
9
9
  export default _default;
@@ -7,7 +7,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const react_1 = require("react");
8
8
  const constant_js_1 = require("../constant.js");
9
9
  const index_js_1 = __importDefault(require("../MarkdownCMD/index.js"));
10
- const MarkdownInner = ({ children: _children = '', answerType, ...rest }) => {
10
+ const MarkdownInner = ({ children: _children = '', answerType, markdownRef, ...rest }) => {
11
11
  const cmdRef = (0, react_1.useRef)(null);
12
12
  const prefixRef = (0, react_1.useRef)('');
13
13
  const content = (0, react_1.useMemo)(() => {
@@ -38,9 +38,17 @@ const MarkdownInner = ({ children: _children = '', answerType, ...rest }) => {
38
38
  prefixRef.current = content;
39
39
  }
40
40
  }, [answerType, content]);
41
+ (0, react_1.useImperativeHandle)(markdownRef, () => ({
42
+ stop: () => {
43
+ cmdRef.current.stop();
44
+ },
45
+ resume: () => {
46
+ cmdRef.current.resume();
47
+ },
48
+ }));
41
49
  return (0, jsx_runtime_1.jsx)(index_js_1.default, { ref: cmdRef, ...rest });
42
50
  };
43
- const Markdown = (props) => {
51
+ const Markdown = (0, react_1.forwardRef)((props, ref) => {
44
52
  const { children = '', answerType = 'answer' } = props;
45
53
  if (constant_js_1.__DEV__) {
46
54
  if (!['thinking', 'answer'].includes(answerType)) {
@@ -50,7 +58,7 @@ const Markdown = (props) => {
50
58
  throw new Error('Markdown组件的子元素必须是一个字符串');
51
59
  }
52
60
  }
53
- return (0, jsx_runtime_1.jsx)(MarkdownInner, { ...props, answerType: answerType });
54
- };
61
+ return (0, jsx_runtime_1.jsx)(MarkdownInner, { ...props, answerType: answerType, markdownRef: ref });
62
+ });
55
63
  exports.default = (0, react_1.memo)(Markdown);
56
64
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/Markdown/index.tsx"],"names":[],"mappings":";;;;;;AAAA,iCAAgE;AAChE,gDAAyC;AAEzC,uEAAmE;AAQnE,MAAM,aAAa,GAAgC,CAAC,EAAE,QAAQ,EAAE,SAAS,GAAG,EAAE,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;IACvG,MAAM,MAAM,GAAG,IAAA,cAAM,EAAc,IAAK,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAA,cAAM,EAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAC3B,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,qBAAO,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAClC,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,IAAI,SAAS,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;gBAC7B,UAAU,GAAG,OAAO,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1C,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACvD,CAAC;qBAAM,CAAC;oBACN,UAAU,GAAG,OAAO,CAAC;oBACrB,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAC5C,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAE1B,OAAO,uBAAC,kBAAW,IAAC,GAAG,EAAE,MAAM,KAAM,IAAI,GAAI,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAgC,CAAC,KAAK,EAAE,EAAE;IACtD,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,UAAU,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC;IAEvD,IAAI,qBAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,uBAAC,aAAa,OAAK,KAAK,EAAE,UAAU,EAAE,UAAU,GAAI,CAAC;AAC9D,CAAC,CAAC;AAEF,kBAAe,IAAA,YAAI,EAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/Markdown/index.tsx"],"names":[],"mappings":";;;;;;AAAA,iCAAiG;AACjG,gDAAyC;AAEzC,uEAAkD;AAYlD,MAAM,aAAa,GAAiC,CAAC,EAAE,QAAQ,EAAE,SAAS,GAAG,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE;IACrH,MAAM,MAAM,GAAG,IAAA,cAAM,EAAiB,IAAK,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAA,cAAM,EAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAC3B,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,qBAAO,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAClC,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,IAAI,SAAS,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC;gBAC7B,UAAU,GAAG,OAAO,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC1C,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACvD,CAAC;qBAAM,CAAC;oBACN,UAAU,GAAG,OAAO,CAAC;oBACrB,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAC5C,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAE1B,IAAA,2BAAmB,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QACtC,IAAI,EAAE,GAAG,EAAE;YACT,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;QACD,MAAM,EAAE,GAAG,EAAE;YACX,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC,CAAC,CAAC;IAEJ,OAAO,uBAAC,kBAAW,IAAC,GAAG,EAAE,MAAM,KAAM,IAAI,GAAI,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,IAAA,kBAAU,EAAiC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACzE,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,UAAU,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC;IAEvD,IAAI,qBAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,uBAAC,aAAa,OAAK,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,GAAI,CAAC;AAChF,CAAC,CAAC,CAAC;AAEH,kBAAe,IAAA,YAAI,EAAC,QAAQ,CAAC,CAAC"}
@@ -1,8 +1,3 @@
1
- import { AnswerType, MarkdownProps } from '../defined.js';
2
- export interface MarkdownRef {
3
- push: (content: string, answerType: AnswerType) => void;
4
- clear: () => void;
5
- triggerWholeEnd: () => void;
6
- }
7
- declare const MarkdownCMD: import("react").ForwardRefExoticComponent<MarkdownProps & import("react").RefAttributes<MarkdownRef>>;
1
+ import { MarkdownProps, MarkdownCMDRef } from '../defined.js';
2
+ declare const MarkdownCMD: import("react").ForwardRefExoticComponent<MarkdownProps & import("react").RefAttributes<MarkdownCMDRef>>;
8
3
  export default MarkdownCMD;
@@ -7,9 +7,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  const react_1 = require("react");
8
8
  const index_js_1 = __importDefault(require("../components/HighReactMarkdown/index.js"));
9
9
  const classnames_1 = __importDefault(require("classnames"));
10
- const compiler_js_1 = require("../utils/compiler.js");
11
10
  const constant_js_1 = require("../constant.js");
12
- const deepClone_js_1 = __importDefault(require("../utils/methods/deepClone.js"));
13
11
  const useTypingTask_js_1 = require("../hooks/useTypingTask.js");
14
12
  const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, onTypedChar, timerType = 'setTimeout', theme = 'light' }, ref) => {
15
13
  /** 当前需要打字的内容 */
@@ -19,83 +17,37 @@ const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, on
19
17
  * 如果打字已经完全结束,则不会再触发打字效果
20
18
  */
21
19
  const isWholeTypedEndRef = (0, react_1.useRef)(false);
22
- /**
23
- * 稳定段落
24
- * 稳定段落是已经打过字,并且不会再变化的段落
25
- */
26
- const [stableSegments, setStableSegments] = (0, react_1.useState)([]);
27
- /** 当前段落 */
28
- const [currentSegment, setCurrentSegment] = (0, react_1.useState)(undefined);
29
- /** 当前段落引用 */
30
- const currentParagraphRef = (0, react_1.useRef)(undefined);
20
+ const charIndexRef = (0, react_1.useRef)(0);
21
+ /** 整个内容引用 */
22
+ const wholeContentRef = (0, react_1.useRef)({
23
+ thinking: {
24
+ content: '',
25
+ length: 0,
26
+ },
27
+ answer: {
28
+ content: '',
29
+ length: 0,
30
+ },
31
+ allLength: 0,
32
+ });
33
+ const [, setUpdate] = (0, react_1.useState)(false);
34
+ const triggerUpdate = () => {
35
+ setUpdate((prev) => !prev);
36
+ };
31
37
  /**
32
38
  * 处理字符显示逻辑
33
39
  */
34
40
  const processCharDisplay = (char) => {
35
- const currentSegment = currentParagraphRef.current;
36
- // debugger;
37
- /** 如果碰到 space,和split_segment 则需要处理成两个段落 */
38
- if (char.contentType === 'space' || char.contentType === 'split_segment') {
39
- if (currentSegment) {
40
- setStableSegments((prev) => {
41
- const newParagraphs = [...prev];
42
- // 放入到稳定队列
43
- if (currentSegment) {
44
- newParagraphs.push({ ...currentSegment, isTyped: false });
45
- }
46
- return newParagraphs;
47
- });
48
- setCurrentSegment(() => undefined);
49
- currentParagraphRef.current = undefined;
50
- }
51
- return;
52
- }
53
- // 处理当前段落
54
- const newCurrentParagraph = {
55
- content: '',
56
- isTyped: false,
57
- type: 'text',
58
- answerType: char.answerType,
59
- tokensReference: {},
60
- };
61
- let _currentParagraph = currentSegment;
62
- if (!_currentParagraph) {
63
- // 如果当前没有段落,则直接设置为新当前段落
64
- _currentParagraph = newCurrentParagraph;
65
- }
66
- else if (currentSegment && currentSegment?.answerType !== char.answerType) {
67
- // 如果当前段落和当前字符的回答类型不一致,则需要处理成两个段落
68
- setStableSegments((prev) => {
69
- const newParagraphs = [...prev];
70
- newParagraphs.push({ ...currentSegment, isTyped: false });
71
- return newParagraphs;
72
- });
73
- _currentParagraph = newCurrentParagraph;
74
- }
75
- const tokensReference = (0, deepClone_js_1.default)(_currentParagraph.tokensReference);
76
- if (tokensReference[char.tokenId]) {
77
- tokensReference[char.tokenId].raw += char.content;
78
- tokensReference[char.tokenId].startIndex = currentSegment?.content?.length || 0;
41
+ if (char.answerType === 'thinking') {
42
+ wholeContentRef.current.thinking.content += char.content;
43
+ wholeContentRef.current.thinking.length += 1;
79
44
  }
80
45
  else {
81
- tokensReference[char.tokenId] = {
82
- startIndex: currentSegment?.content?.length || 0,
83
- raw: char.content,
84
- };
46
+ wholeContentRef.current.answer.content += char.content;
47
+ wholeContentRef.current.answer.length += 1;
85
48
  }
86
- const newCurrentSegment = {
87
- ..._currentParagraph,
88
- tokensReference,
89
- content: (currentSegment?.content || '') + char.content,
90
- isTyped: true,
91
- };
92
- currentParagraphRef.current = newCurrentSegment;
93
- setCurrentSegment(() => newCurrentSegment);
49
+ triggerUpdate();
94
50
  };
95
- /** 思考段落 */
96
- const thinkingParagraphs = (0, react_1.useMemo)(() => stableSegments.filter((paragraph) => paragraph.answerType === 'thinking'), [stableSegments]);
97
- /** 回答段落 */
98
- const answerParagraphs = (0, react_1.useMemo)(() => stableSegments.filter((paragraph) => paragraph.answerType === 'answer'), [stableSegments]);
99
51
  // 使用新的打字任务 hook
100
52
  const typingTask = (0, useTypingTask_js_1.useTypingTask)({
101
53
  timerType,
@@ -105,20 +57,7 @@ const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, on
105
57
  onStart,
106
58
  onTypedChar,
107
59
  processCharDisplay,
108
- });
109
- const lastSegmentRawRef = (0, react_1.useRef)({
110
- thinkingReference: null,
111
- answerReference: null,
112
- });
113
- const wholeContentRef = (0, react_1.useRef)({
114
- thinking: {
115
- content: '',
116
- length: 0,
117
- },
118
- answer: {
119
- content: '',
120
- length: 0,
121
- },
60
+ wholeContentRef,
122
61
  });
123
62
  /**
124
63
  * 内部推送处理逻辑
@@ -127,108 +66,18 @@ const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, on
127
66
  if (content.length === 0) {
128
67
  return;
129
68
  }
130
- const lastSegmentReference = lastSegmentRawRef.current[`${answerType}Reference`];
131
- if (isWholeTypedEndRef.current) {
132
- if (constant_js_1.__DEV__) {
133
- console.warn('打字已经完全结束,不能再添加新的内容');
134
- }
135
- return;
136
- }
137
- const wholeContent = wholeContentRef.current[`${answerType}`] || '';
138
- let currentIndex = wholeContent.length;
139
- let currentLastSegmentReference = null;
140
- let currentLastSegmentRaw = '';
141
- let lastSegmentRaw = '';
142
- if (lastSegmentReference) {
143
- lastSegmentRaw = lastSegmentReference.raw;
144
- currentLastSegmentRaw = lastSegmentRaw + content;
145
- }
146
- else {
147
- currentLastSegmentRaw = content;
148
- }
149
- const tokens = (0, compiler_js_1.compiler)(currentLastSegmentRaw);
150
- // 如果最后一个token是space,则把lastSegmentRaw设置为空
151
- if (tokens[tokens.length - 1].type === 'space') {
152
- currentLastSegmentReference = null;
153
- }
154
- else {
155
- // 如果上一个segment存在并且当前只有一个token,则说明是同一个segment
156
- if (lastSegmentReference !== null && tokens.length === 1) {
157
- const newCurrentLastSegmentReference = lastSegmentReference;
158
- newCurrentLastSegmentReference.raw = newCurrentLastSegmentReference.raw + content;
159
- currentLastSegmentReference = newCurrentLastSegmentReference;
160
- }
161
- else {
162
- currentLastSegmentReference = tokens[tokens.length - 1];
163
- }
164
- }
165
- const pushAndSplitSegment = (raw, tokenIndex, segmentTokenId) => {
166
- const currentToken = tokens[tokenIndex];
167
- if (tokenIndex > 0) {
168
- const prevToken = tokens[tokenIndex - 1];
169
- if (prevToken.type !== 'space' && currentToken.type !== 'space') {
170
- charsRef.current.push({ content: '', answerType, contentType: 'split_segment', tokenId: currentToken.id, index: currentIndex++ });
171
- }
172
- }
173
- charsRef.current.push(...raw.split('').map((char) => ({ content: char, answerType, contentType: 'segment', tokenId: segmentTokenId, index: currentIndex++ })));
174
- };
175
- if (!lastSegmentReference) {
176
- tokens.forEach((token, i) => {
177
- if (token.type === 'space') {
178
- charsRef.current.push(...token.raw.split('').map((char) => ({ content: char, answerType, contentType: 'space', tokenId: token.id, index: currentIndex++ })));
179
- }
180
- else {
181
- pushAndSplitSegment(token.raw, i, token.id);
182
- }
183
- });
184
- }
185
- else {
186
- // debugger;
187
- let str = '';
188
- let firstSpaceIndex = -1;
189
- let nextTokenIndex = lastSegmentRaw.length;
190
- for (let i = 0; i < tokens.length; i++) {
191
- const token = tokens[i];
192
- if (token.type === 'space') {
193
- if (firstSpaceIndex === -1) {
194
- firstSpaceIndex = str.length;
195
- }
196
- str += token.raw;
197
- if (lastSegmentRaw.length > firstSpaceIndex) {
198
- // 如果lastSegmentRaw的长度大于firstSpaceIndex,则需要将当前设置为 segment
199
- charsRef.current.push(...token.raw.split('').map((char) => ({ content: char, answerType, contentType: 'segment', tokenId: token.id, index: currentIndex++ })));
200
- }
201
- else {
202
- charsRef.current.push(...token.raw.split('').map((char) => ({ content: char, answerType, contentType: 'space', tokenId: token.id, index: currentIndex++ })));
203
- }
204
- }
205
- else {
206
- str += token.raw;
207
- if (str.length < nextTokenIndex && i == 0) {
208
- /** 如果当前字符串长度小于下一个token的索引,则需要将当前段落更新, 以修正不完整的token */
209
- const lastSegmentReferenceId = lastSegmentReference.id;
210
- const currentSegment = currentParagraphRef.current;
211
- const tokensReference = currentSegment?.tokensReference || {};
212
- const lastTokenReference = tokensReference[lastSegmentReferenceId];
213
- if (lastTokenReference) {
214
- const newTokensReference = { ...tokensReference, [lastSegmentReferenceId]: { startIndex: lastTokenReference.startIndex, raw: token.raw } };
215
- const newCurrentSegment = { ...currentSegment, tokensReference: newTokensReference, isTyped: false, type: 'text', answerType };
216
- newCurrentSegment.content = Object.values(newTokensReference).reduce((acc, curr) => acc + curr.raw, '');
217
- setCurrentSegment(newCurrentSegment);
218
- currentParagraphRef.current = newCurrentSegment;
219
- }
220
- }
221
- const realRaw = str.slice(nextTokenIndex);
222
- if (realRaw.length > 0) {
223
- pushAndSplitSegment(realRaw, i, lastSegmentReference.id);
224
- }
225
- }
226
- nextTokenIndex = str.length;
227
- }
228
- }
229
- lastSegmentRawRef.current[`${answerType}Reference`] = currentLastSegmentReference;
230
- wholeContent.content = wholeContent.content + content;
231
- wholeContent.length = wholeContent.content.length;
69
+ charsRef.current.push(...content.split('').map((chatStr) => {
70
+ const index = charIndexRef.current++;
71
+ const charObj = {
72
+ content: chatStr,
73
+ answerType,
74
+ contentType: 'segment',
75
+ tokenId: 0,
76
+ index,
77
+ };
78
+ return charObj;
79
+ }));
80
+ wholeContentRef.current.allLength += content.length;
232
81
  if (!typingTask.isTyping()) {
233
82
  typingTask.start();
234
83
  }
@@ -247,20 +96,30 @@ const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, on
247
96
  */
248
97
  clear: () => {
249
98
  typingTask.stop();
99
+ typingTask.typedIsManualStopRef.current = false;
250
100
  charsRef.current = [];
251
- setStableSegments([]);
252
- setCurrentSegment(undefined);
101
+ wholeContentRef.current = {
102
+ thinking: {
103
+ content: '',
104
+ length: 0,
105
+ },
106
+ answer: {
107
+ content: '',
108
+ length: 0,
109
+ },
110
+ allLength: 0,
111
+ };
253
112
  isWholeTypedEndRef.current = false;
254
- currentParagraphRef.current = undefined;
255
- typingTask.clear();
256
- // 清理缓冲区
257
- const lastSegmentRef = lastSegmentRawRef.current;
258
- lastSegmentRef.thinkingReference = null;
259
- lastSegmentRef.answerReference = null;
260
- wholeContentRef.current.thinking.content = '';
261
- wholeContentRef.current.thinking.length = 0;
262
- wholeContentRef.current.answer.content = '';
263
- wholeContentRef.current.answer.length = 0;
113
+ charIndexRef.current = 0;
114
+ triggerUpdate();
115
+ },
116
+ /** 停止打字任务 */
117
+ stop: () => {
118
+ typingTask.stop();
119
+ },
120
+ /** 重新开始打字任务 */
121
+ resume: () => {
122
+ typingTask.resume();
264
123
  },
265
124
  /**
266
125
  * 主动触发打字结束
@@ -279,19 +138,17 @@ const MarkdownCMD = (0, react_1.forwardRef)(({ interval = 30, onEnd, onStart, on
279
138
  * 刷新缓冲区 (新增方法)
280
139
  */
281
140
  }));
282
- const getParagraphs = (paragraphs, answerType) => {
283
- return ((0, jsx_runtime_1.jsxs)("div", { className: `ds-markdown-paragraph ds-typed-${answerType}`, children: [paragraphs.map((paragraph, index) => {
284
- if (paragraph.type === 'br') {
285
- return null;
286
- }
287
- return ((0, jsx_runtime_1.jsx)(index_js_1.default, { theme: theme, children: paragraph.content || '' }, index));
288
- }), currentSegment?.answerType === answerType && ((0, jsx_runtime_1.jsx)(index_js_1.default, { theme: theme, children: currentSegment.content || '' }, currentSegment.content))] }));
141
+ const getParagraphs = (answerType) => {
142
+ return ((0, jsx_runtime_1.jsx)("div", { className: `ds-markdown-paragraph ds-typed-${answerType}`, children: (0, jsx_runtime_1.jsx)(index_js_1.default, { theme: theme, children: wholeContentRef.current[answerType].content || '' }) }));
289
143
  };
290
144
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)({
291
145
  'ds-markdown': true,
292
146
  apple: true,
293
147
  'ds-markdown-dark': theme === 'dark',
294
- }), children: [(thinkingParagraphs.length > 0 || currentSegment?.answerType === 'thinking') && (0, jsx_runtime_1.jsx)("div", { className: "ds-markdown-thinking", children: getParagraphs(thinkingParagraphs, 'thinking') }), (answerParagraphs.length > 0 || currentSegment?.answerType === 'answer') && (0, jsx_runtime_1.jsx)("div", { className: "ds-markdown-answer", children: getParagraphs(answerParagraphs, 'answer') })] }));
148
+ }), children: [(0, jsx_runtime_1.jsx)("div", { className: "ds-markdown-thinking", children: getParagraphs('thinking') }), (0, jsx_runtime_1.jsx)("div", { className: "ds-markdown-answer", children: getParagraphs('answer') })] }));
295
149
  });
150
+ if (constant_js_1.__DEV__) {
151
+ MarkdownCMD.displayName = 'MarkdownCMD';
152
+ }
296
153
  exports.default = MarkdownCMD;
297
154
  //# sourceMappingURL=index.js.map