├── .gitignore
├── README.md
├── docs
├── assets
│ ├── index-D7ADuXm3.css
│ └── index-Dkuq6TsQ.js
└── index.html
├── eslint.config.js
├── example
├── basic
│ ├── data.json
│ └── index.tsx
├── cdm
│ ├── cozeData.ts
│ ├── define.ts
│ ├── index.tsx
│ └── util.ts
├── index.css
└── index.tsx
├── index.html
├── license
├── package-lock.json
├── package.json
├── prettier.config.js
├── scripts
├── prepublish.js
└── set-tag.cjs
├── shim.d.ts
├── src
├── AutoScroll
│ └── index.tsx
├── Markdown
│ └── index.tsx
├── MarkdownCMD
│ └── index.tsx
├── components
│ ├── BlockWrap
│ │ ├── index.less
│ │ └── index.tsx
│ └── HighReactMarkdown
│ │ ├── highlightTheme.less
│ │ ├── index.less
│ │ └── index.tsx
├── constant.ts
├── defined.less
├── defined.ts
├── index.less
├── index.tsx
└── style.less
├── tsconfig-base.json
├── tsconfig-cjs.json
├── tsconfig-esm.json
├── tsconfig.json
├── vite-env.d.ts
├── vite.build.config.ts
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ds-markdown
2 |
3 | `ds-markdown`是一个[React](https://react.dev)组件, 类似[deepseek官网](https://chat.deepseek.com/)风格的 `Markdown`
4 |
5 | [DEMO](https://onshinpei.github.io/ds-markdown/)
6 |
7 | ## 特性
8 |
9 | - 🦮 对`deepseek`官网的聊天响应效果进行了1:1还原
10 | - 🛠 自带打字效果
11 | - 🦮 内部封装了常用的`markdown`格式的文本显示
12 | - 🔤 对大文档进行了性能优化,进行分批处理,生成打字效果的时候不会对页面造成卡顿现象
13 |
14 | ## 安装
15 |
16 | ```bash
17 | npm install ds-markdown
18 | ```
19 |
20 |
21 |
22 |
23 | ## props
24 |
25 | ### 默认导出
26 |
27 | | 属性名 | 类型 | 说明 | 默认值 |
28 | | ------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------- |
29 | | `interval` | `number` | 打字的速度`ms` | `30` |
30 | | `answerType` | `thinking` \| `answer` | `markdown`类型 | `answer` |
31 | | `onEnd` | `(data: { str: string; answerType: AnswerType }) => void` | 打字结束后回调,**可能会触发多次,因为AI的响应可能是一段一段,间隔可能较久** | - |
32 | | `onStart` | `(data: { currentIndex: number; currentChar: string; answerType: AnswerType; prevStr: string }) => void` | 打字开始回调 **可能会触发多次** | - |
33 | | `onTypedChar` | `(data: { currentIndex: number; currentChar: string; answerType: AnswerType; prevStr: string }) => void` | 当前正在打字的回调 | - |
34 |
35 | ## 使用示例 - default export
36 |
37 | [在线查看](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
38 |
39 | ```tsx
40 | import { useState } from 'react';
41 | import DsMarkdown from 'ds-markdown';
42 | import 'ds-markdown/style.css';
43 |
44 | const markdown = `# ds-markdown
45 |
46 | \`ds-markdown\`是一个[React](https://react.dev)组件, 类似[deepseek官网](https://chat.deepseek.com/)风格的 \`Markdown\`
47 |
48 | ## 特性
49 |
50 | - 🦮 对`deepseek`官网的聊天响应效果进行了1:1还原
51 | - 🛠 自带打字效果
52 | - 🦮 内部封装了常用的\`markdown\`格式的文本显示
53 | - 🔤 对大文档进行了性能优化,进行分批处理,生成打字效果的时候不会对页面造成卡顿现象
54 | `;
55 |
56 | function App() {
57 | const [thinkingContent, setThinkingContent] = useState('');
58 | const [answerContent, setAnswerContent] = useState('');
59 |
60 | const onClick = () => {
61 | // 如果重复点击,则会清空之前的效果
62 | setThinkingContent('这是我思考的内容,我已经思考完成,下面是我的答案');
63 | };
64 |
65 | console.log(answerContent);
66 | return (
67 |
68 |
69 | {
73 | console.log('思考完成');
74 | setAnswerContent(markdown);
75 | }}
76 | >
77 | {thinkingContent}
78 |
79 |
80 | {!!answerContent && (
81 |
82 | {answerContent}
83 |
84 | )}
85 |
86 | );
87 | }
88 |
89 | export default App;
90 | ```
91 |
92 | ## 命令式示例
93 |
94 | 上面的示例中使用声明式方式来进行`markdown`的打字效果,当我们用流式拉取到数据时,文字是一个不断变化的过程,我们可以进行命令式的方式来加入文字,这样可以减少`markdown`的`rerender`
95 | 使用方式:
96 | `import { MarkdownCMD } from 'ds-markdown';`
97 |
98 | [在线查看](https://stackblitz.com/edit/vitejs-vite-2ri8kex3?file=src%2FApp.tsx)
99 |
100 | ```tsx
101 | import { useRef, useState } from 'react';
102 | import { MarkdownCMD } from 'ds-markdown';
103 | import 'ds-markdown/style.css';
104 |
105 | const markdown = `# ds-markdown
106 |
107 | \`ds-markdown\`是一个[React](https://react.dev)组件, 类似[deepseek官网](https://chat.deepseek.com/)风格的 \`Markdown\`
108 |
109 | ## 特性
110 |
111 | - 🦮 对`deepseek`官网的聊天响应效果进行了1:1还原
112 | - 🛠 自带打字效果
113 | - 🦮 内部封装了常用的\`markdown\`格式的文本显示
114 | - 🔤 对大文档进行了性能优化,进行分批处理,生成打字效果的时候不会对页面造成卡顿现象
115 | `;
116 |
117 | function App() {
118 | const ref = useRef();
119 |
120 | const onClick = () => {
121 | // 如果重复点击,则会清空之前的效果
122 | ref.current.clear();
123 | // 显示思考过程
124 | ref.current.push('这是思考过程:我正在思考 ds-markdown是什么\n\n思考完成,准备发送答案', 'thinking');
125 | // 显示结果
126 | ref.current.push(markdown, 'answer');
127 | };
128 |
129 | return (
130 |
131 |
132 |
133 |
134 | );
135 | }
136 |
137 | export default App;
138 | ```
139 |
140 | ## 兼容性
141 |
142 | 由于本组件采用`react hooks`编写,所以`react`版本最低为`v16.8.0`
143 |
144 | ## 期待中的功能
145 |
146 | - 支持多种主题(开发中)
147 | - 增加测试用例(开发中)
148 | - 支持H5端(未开始,预计2025年5月底完成)
149 | - 支持微信、支付宝小程序端(未开始,预计2025年6月底完成)
150 |
151 | 由于本页面仔写业务写的飞起,只能在闲暇之余来开发本库,也希望各位小伙伴一起来共建😘
152 |
153 | ## License
154 |
155 | MIT
156 |
--------------------------------------------------------------------------------
/docs/assets/index-D7ADuXm3.css:
--------------------------------------------------------------------------------
1 | :root{--ds-rgb-neutral-50: 250 250 250;--ds-rgb-neutral-100: 245 245 245;--ds-rgb-neutral-150: 237 237 237;--ds-rgb-neutral-200: 229 229 229;--ds-rgb-neutral-250: 220 220 220;--ds-rgb-neutral-300: 212 212 212;--ds-rgb-neutral-350: 187 187 187;--ds-rgb-neutral-400: 163 163 163;--ds-rgb-neutral-450: 139 139 139;--ds-rgb-neutral-500: 115 115 115;--ds-rgb-neutral-550: 98 98 98;--ds-rgb-neutral-600: 82 82 82;--ds-rgb-neutral-650: 73 73 73;--ds-rgb-neutral-700: 64 64 64;--ds-rgb-neutral-750: 51 51 51;--ds-rgb-neutral-800: 38 38 38;--ds-rgb-neutral-850: 30 30 30;--ds-rgb-neutral-900: 23 23 23;--ds-rgb-neutral-950: 10 10 10;--ds-rgb-blue-500: 59 130 246}.ds-markdown{--ds-font-family-code: Menlo, "Roboto Mono", "Courier New", Courier, monospace, "Inter", sans-serif}.ds-markdown{--ds-rgb-primary: var(--ds-rgb-blue-500);--ds-rgb-label-1: var(--ds-rgb-neutral-700);--ds-rgb-label-2: var(--ds-rgb-neutral-450);--ds-rgb-label-3: var(--ds-rgb-neutral-400);--dsr-border-1: rgb(var(--ds-rgb-neutral-350));--dsr-border-2: rgb(var(--ds-rgb-neutral-200))}.ds-markdown{--ds-font-size-m: 14px;--ds-font-size-xsp: 11px;--ds-line-height-m: 25px;--ds-transition-duration: .2s;--ds-ease-in-out: cubic-bezier(.4, 0, .2, 1);--ds-rgba-transparent: 255 255 255 / 0;--ds-rgb-link: var(--ds-rgb-primary)}.ds-markdown.apple{--ds-font-weight-strong: 500}.ds-markdown{--ds-md-zoom: 1.143;--ds-md-font-size: calc(var(--ds-md-zoom) * var(--ds-font-size-m));--ds-md-line-height: calc(var(--ds-md-zoom) * var(--ds-line-height-m));font-size:var(--ds-md-font-size);min-height:var(--ds-md-font-size);line-height:var(--ds-md-line-height)}.ds-markdown img{max-width:100%}.ds-markdown h1,.ds-markdown h2,.ds-markdown h3,.ds-markdown h4,.ds-markdown h5,.ds-markdown h6{font-weight:var(--ds-font-weight-strong);font-size:var(--ds-md-font-size);line-height:var(--ds-md-line-height);margin:calc(var(--ds-md-zoom) * 16px) 0 calc(var(--ds-md-zoom) * 12px) 0}.ds-markdown h1{font-size:calc(var(--ds-md-zoom) * 24px);line-height:1.5}.ds-markdown h2{font-size:calc(var(--ds-md-zoom) * 20px);line-height:1.5}.ds-markdown h3{font-size:calc(var(--ds-md-zoom) * 16px);line-height:1.5}.ds-markdown h1 .header-anchor,.ds-markdown h2 .header-anchor,.ds-markdown h3 .header-anchor,.ds-markdown h4 .header-anchor,.ds-markdown h5 .header-anchor,.ds-markdown h6 .header-anchor{opacity:0;margin-left:4px}.ds-markdown h1:hover .header-anchor,.ds-markdown h2:hover .header-anchor,.ds-markdown h3:hover .header-anchor,.ds-markdown h4:hover .header-anchor,.ds-markdown h5:hover .header-anchor,.ds-markdown h6:hover .header-anchor{opacity:1}.ds-markdown .ds-markdown-paragraph{margin:calc(var(--ds-md-zoom) * 12px) 0;font-size:var(--ds-md-font-size);line-height:var(--ds-md-line-height)}.ds-markdown a:not(.ds-a){color:rgb(var(--ds-rgb-link));transition:box-shadow var(--ds-transition-duration) var(--ds-ease-in-out);border-radius:calc(var(--ds-md-zoom) * 6px);border-left:3px solid rgba(var(--ds-rgba-transparent));border-right:3px solid rgba(var(--ds-rgba-transparent));border-top:2px solid rgba(var(--ds-rgba-transparent));border-bottom:2px solid rgba(var(--ds-rgba-transparent));margin-left:-3px;margin-right:-3px;text-decoration:none;position:relative}.ds-markdown a:not(.ds-a):focus{outline:none}.ds-markdown a:not(.ds-a):focus-visible{box-shadow:0 0 0 2px rgb(var(--ds-rgb-primary))}.ds-markdown li>ul,.ds-markdown li>ol{margin-top:4px}.ds-markdown ul,.ds-markdown ol{margin:calc(var(--ds-md-zoom) * 12px) 0;padding-left:calc(var(--ds-md-zoom) * 24px)}.ds-markdown li:not(:first-child){margin-top:4px}.ds-markdown li::marker{line-height:var(--ds-md-line-height);color:rgb(var(--ds-rgb-label-2))}.ds-markdown hr{height:1px;margin:32px 0;background-color:var(--dsr-border-2);border:none;display:block}.ds-markdown blockquote{border-left:2px solid rgb(var(--ds-rgb-label-3));padding-left:calc(var(--ds-md-zoom) * 16px);margin:0}.ds-markdown table{border-collapse:collapse}.ds-markdown th{color:rgb(var(--ds-rgb-label-1));padding:calc(var(--ds-md-zoom) * 6px) calc(var(--ds-md-zoom) * 12px);border-bottom:1px solid rgb(var(--ds-rgb-label-3));border-top:1px solid rgb(var(--ds-rgb-label-3));font-weight:600}.ds-markdown th,.ds-markdown td{text-align:left}.ds-markdown th:first-child{padding-left:0}.ds-markdown td{padding:calc(var(--ds-md-zoom) * 6px) calc(var(--ds-md-zoom) * 12px);border-bottom:1px solid rgb(var(--ds-rgb-label-3))}.ds-markdown td:first-child{padding-left:0}.ds-markdown pre{margin:calc(var(--ds-md-zoom) * 12px) 0;font-family:var(--ds-font-family-code);overflow:auto}.ds-markdown code{font-size:.875em;font-weight:var(--ds-font-weight-strong);font-family:var(--ds-font-family-code);background-color:#ececec;border-radius:4px;padding:.15rem .3rem}.ds-markdown code:before{margin-left:4px}.ds-markdown code:after{margin-right:4px}ds-markdown-math{text-align:center;display:block;overflow-x:auto;overflow-y:hidden}.ds-markdown>* .ds-markdown-math{margin:calc(var(--ds-md-zoom) * 12px) 0}.ds-markdown>* .ds-markdown-math:first-child{margin-top:0}.ds-markdown>* .ds-markdown-math:last-child{margin-bottom:0}.ds-markdown-code-copy-button{background-color:rgba(var(--ds-rgba-transparent));color:inherit;cursor:pointer;border:none;margin:0;padding:0}.ds-markdown li>.ds-markdown-paragraph{margin:4px 0}.ds-markdown li>:first-child{margin-top:0}.ds-markdown li>:last-child{margin-bottom:0}.ds-markdown .ds-markdown-paragraph:last-child{margin-bottom:0!important}.ds-markdown>:first-child{margin-top:0!important}.ds-markdown .ds-typed-thinking{border-left:2px solid #e5e5e5;padding:0 0 0 12px;color:#8b8b8b;line-height:26px;font-size:var(--ds-font-size-m)}.ds-markdown.ds-typed-answer{margin:calc(var(--ds-md-zoom) * 12px) 0;font-size:var(--ds-md-font-size);line-height:var(--ds-md-line-height)}.md-code-block-banner-content{justify-content:space-between;align-items:center;padding:6px 14px 6px 6px;display:flex}.md-code-block-language{color:var(--dsr-text-2);margin-left:8px;font-size:12px;line-height:18px}.ds-markdown .markdown-table-wrapper{overflow-x:auto}.ds-markdown .markdown-table-wrapper table{width:max-content;max-width:max-content;min-width:100%}.ds-markdown .markdown-table-wrapper th,.ds-markdown .markdown-table-wrapper td{padding:10px;font-size:15px;line-height:1.72}.ds-markdown .markdown-table-wrapper th{border-color:var(--dsr-border-1);border-top:none}.ds-markdown .markdown-table-wrapper td{border-color:var(--dsr-border-2);max-width:320px;min-width:100px;max-width:max(30vw,320px)}.md-code-block.md-code-block-dark code[class*=language-],.md-code-block.md-code-block-dark pre[class*=language-]{color:#e3eaf2;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;background:0 0;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;line-height:1.5}.md-code-block.md-code-block-dark pre[class*=language-]::-moz-selection{background:#3c526d}.md-code-block.md-code-block-dark pre[class*=language-]::selection{background:#3c526d}.md-code-block.md-code-block-dark pre[class*=language-] ::-moz-selection{background:#3c526d}.md-code-block.md-code-block-dark pre[class*=language-] ::selection{background:#3c526d}.md-code-block.md-code-block-dark code[class*=language-]::-moz-selection{background:#3c526d}.md-code-block.md-code-block-dark code[class*=language-]::selection{background:#3c526d}.md-code-block.md-code-block-dark code[class*=language-] ::-moz-selection{background:#3c526d}.md-code-block.md-code-block-dark code[class*=language-] ::selection{background:#3c526d}.md-code-block.md-code-block-dark pre[class*=language-]{margin:.5em 0;padding:1em;overflow:auto}.md-code-block.md-code-block-dark :not(pre)>code[class*=language-],.md-code-block.md-code-block-dark pre[class*=language-]{background:#111b27}.md-code-block.md-code-block-dark :not(pre)>code[class*=language-]{white-space:normal;border-radius:.3em;padding:.1em .3em}.md-code-block.md-code-block-dark .token.comment,.md-code-block.md-code-block-dark .token.prolog,.md-code-block.md-code-block-dark .token.doctype,.md-code-block.md-code-block-dark .token.cdata{color:#8da1b9}.md-code-block.md-code-block-dark .token.punctuation{color:#e3eaf2}.md-code-block.md-code-block-dark .token.delimiter.important,.md-code-block.md-code-block-dark .token.selector .parent,.md-code-block.md-code-block-dark .token.tag,.md-code-block.md-code-block-dark .token.tag .token.punctuation{color:#6cc}.md-code-block.md-code-block-dark .token.attr-name,.md-code-block.md-code-block-dark .token.boolean,.md-code-block.md-code-block-dark .token.boolean.important,.md-code-block.md-code-block-dark .token.number,.md-code-block.md-code-block-dark .token.constant,.md-code-block.md-code-block-dark .token.selector .token.attribute{color:#e6d37a}.md-code-block.md-code-block-dark .token.class-name,.md-code-block.md-code-block-dark .token.key,.md-code-block.md-code-block-dark .token.parameter,.md-code-block.md-code-block-dark .token.property,.md-code-block.md-code-block-dark .token.property-access,.md-code-block.md-code-block-dark .token.variable{color:#6cb8e6}.md-code-block.md-code-block-dark .token.attr-value,.md-code-block.md-code-block-dark .token.inserted,.md-code-block.md-code-block-dark .token.color,.md-code-block.md-code-block-dark .token.selector .token.value,.md-code-block.md-code-block-dark .token.string,.md-code-block.md-code-block-dark .token.string .token.url-link{color:#91d076}.md-code-block.md-code-block-dark .token.builtin,.md-code-block.md-code-block-dark .token.keyword-array,.md-code-block.md-code-block-dark .token.package,.md-code-block.md-code-block-dark .token.regex{color:#f4adf4}.md-code-block.md-code-block-dark .token.function,.md-code-block.md-code-block-dark .token.selector .token.class,.md-code-block.md-code-block-dark .token.selector .token.id{color:#c699e3}.md-code-block.md-code-block-dark .token.atrule .token.rule,.md-code-block.md-code-block-dark .token.combinator,.md-code-block.md-code-block-dark .token.keyword,.md-code-block.md-code-block-dark .token.operator,.md-code-block.md-code-block-dark .token.pseudo-class,.md-code-block.md-code-block-dark .token.pseudo-element,.md-code-block.md-code-block-dark .token.selector,.md-code-block.md-code-block-dark .token.unit{color:#e9ae7e}.md-code-block.md-code-block-dark .token.deleted,.md-code-block.md-code-block-dark .token.important{color:#cd6660}.md-code-block.md-code-block-dark .token.keyword-this,.md-code-block.md-code-block-dark .token.this{color:#6cb8e6}.md-code-block.md-code-block-dark .token.important,.md-code-block.md-code-block-dark .token.keyword-this,.md-code-block.md-code-block-dark .token.this,.md-code-block.md-code-block-dark .token.bold{font-weight:700}.md-code-block.md-code-block-dark .token.delimiter.important{font-weight:inherit}.md-code-block.md-code-block-dark .token.italic{font-style:italic}.md-code-block.md-code-block-dark .token.entity{cursor:help}.md-code-block.md-code-block-dark .language-markdown .token.title,.md-code-block.md-code-block-dark .language-markdown .token.title .token.punctuation{color:#6cb8e6;font-weight:700}.md-code-block.md-code-block-dark .language-markdown .token.blockquote.punctuation{color:#f4adf4}.md-code-block.md-code-block-dark .language-markdown .token.code{color:#6cc}.md-code-block.md-code-block-dark .language-markdown .token.hr.punctuation{color:#6cb8e6}.md-code-block.md-code-block-dark .language-markdown .token.url .token.content{color:#91d076}.md-code-block.md-code-block-dark .language-markdown .token.url-link{color:#e6d37a}.md-code-block.md-code-block-dark .language-markdown .token.list.punctuation{color:#f4adf4}.md-code-block.md-code-block-dark .language-markdown .token.table-header,.md-code-block.md-code-block-dark .language-json .token.operator{color:#e3eaf2}.md-code-block.md-code-block-dark .language-scss .token.variable{color:#6cc}.md-code-block.md-code-block-dark .token.token.tab:not(:empty):before,.md-code-block.md-code-block-dark .token.token.cr:before,.md-code-block.md-code-block-dark .token.token.lf:before,.md-code-block.md-code-block-dark .token.token.space:before{color:#8da1b9}.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>a,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>button{color:#111b27;background:#6cb8e6}.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>a:hover,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>a:focus,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>button:hover,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>button:focus{color:#111b27;background:#6cb8e6da;text-decoration:none}.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>span,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>span:hover,.md-code-block.md-code-block-dark div.code-toolbar>.toolbar.toolbar>.toolbar-item>span:focus{color:#111b27;background:#8da1b9}.md-code-block.md-code-block-dark .line-highlight.line-highlight{background:linear-gradient(90deg,#3c526d5f 70%,#3c526d55)}.md-code-block.md-code-block-dark .line-highlight.line-highlight:before,.md-code-block.md-code-block-dark .line-highlight.line-highlight[data-end]:after{color:#111b27;background-color:#8da1b9;box-shadow:0 1px #3c526d}.md-code-block.md-code-block-dark pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:#8da1b918}.md-code-block.md-code-block-dark .line-numbers.line-numbers .line-numbers-rows{background:#0b121b7a;border-right:1px solid #0b121b}.md-code-block.md-code-block-dark .line-numbers .line-numbers-rows>span:before{color:#8da1b9da}.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-1,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-5,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-9{color:#e6d37a}.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-2,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-6,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-10{color:#f4adf4}.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-3,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-7,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-11{color:#6cb8e6}.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-4,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-8,.md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-12{color:#c699e3}.md-code-block.md-code-block-dark pre.diff-highlight>code .token.token.deleted:not(.prefix),.md-code-block.md-code-block-dark pre>code.diff-highlight .token.token.deleted:not(.prefix){background-color:#cd66601f}.md-code-block.md-code-block-dark pre.diff-highlight>code .token.token.inserted:not(.prefix),.md-code-block.md-code-block-dark pre>code.diff-highlight .token.token.inserted:not(.prefix){background-color:#91d0761f}.md-code-block.md-code-block-dark .command-line .command-line-prompt{border-right:1px solid #0b121b}.md-code-block.md-code-block-dark .command-line .command-line-prompt>span:before{color:#8da1b9da}.md-code-block.md-code-block-light code[class*=language-],.md-code-block.md-code-block-light pre[class*=language-]{color:#383a42;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;-moz-tab-size:2;tab-size:2;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;direction:ltr;background:#fafafa;font-family:Fira Code,Fira Mono,Menlo,Consolas,DejaVu Sans Mono,monospace;line-height:1.5}.md-code-block.md-code-block-light code[class*=language-]::-moz-selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light code[class*=language-]::selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light code[class*=language-] ::-moz-selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light code[class*=language-] ::selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light pre[class*=language-] ::-moz-selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light pre[class*=language-] ::selection{color:inherit;background:#e5e5e6}.md-code-block.md-code-block-light pre[class*=language-]{border-radius:.3em;margin:.5em 0;padding:1em;overflow:auto}.md-code-block.md-code-block-light :not(pre)>code[class*=language-]{white-space:normal;border-radius:.3em;padding:.2em .3em}.md-code-block.md-code-block-light .token.comment,.md-code-block.md-code-block-light .token.prolog,.md-code-block.md-code-block-light .token.cdata{color:#a0a1a7}.md-code-block.md-code-block-light .token.doctype,.md-code-block.md-code-block-light .token.punctuation,.md-code-block.md-code-block-light .token.entity{color:#383a42}.md-code-block.md-code-block-light .token.attr-name,.md-code-block.md-code-block-light .token.class-name,.md-code-block.md-code-block-light .token.boolean,.md-code-block.md-code-block-light .token.constant,.md-code-block.md-code-block-light .token.number,.md-code-block.md-code-block-light .token.atrule{color:#b76b01}.md-code-block.md-code-block-light .token.keyword{color:#a626a4}.md-code-block.md-code-block-light .token.property,.md-code-block.md-code-block-light .token.tag,.md-code-block.md-code-block-light .token.symbol,.md-code-block.md-code-block-light .token.deleted,.md-code-block.md-code-block-light .token.important{color:#e45649}.md-code-block.md-code-block-light .token.selector,.md-code-block.md-code-block-light .token.string,.md-code-block.md-code-block-light .token.char,.md-code-block.md-code-block-light .token.builtin,.md-code-block.md-code-block-light .token.inserted,.md-code-block.md-code-block-light .token.regex,.md-code-block.md-code-block-light .token.attr-value,.md-code-block.md-code-block-light .token.attr-value>.token.punctuation{color:#50a14f}.md-code-block.md-code-block-light .token.variable,.md-code-block.md-code-block-light .token.operator,.md-code-block.md-code-block-light .token.function{color:#4078f2}.md-code-block.md-code-block-light .token.url{color:#0184bc}.md-code-block.md-code-block-light .token.attr-value>.token.punctuation.attr-equals,.md-code-block.md-code-block-light .token.special-attr>.token.attr-value>.token.value.css{color:#383a42}.md-code-block.md-code-block-light .language-css .token.selector{color:#e45649}.md-code-block.md-code-block-light .language-css .token.property{color:#383a42}.md-code-block.md-code-block-light .language-css .token.function,.md-code-block.md-code-block-light .language-css .token.url>.token.function{color:#0184bc}.md-code-block.md-code-block-light .language-css .token.url>.token.string.url{color:#50a14f}.md-code-block.md-code-block-light .language-css .token.important,.md-code-block.md-code-block-light .language-css .token.atrule .token.rule,.md-code-block.md-code-block-light .language-javascript .token.operator{color:#a626a4}.md-code-block.md-code-block-light .language-javascript .token.template-string>.token.interpolation>.token.interpolation-punctuation.punctuation{color:#ca1243}.md-code-block.md-code-block-light .language-json .token.operator{color:#383a42}.md-code-block.md-code-block-light .language-json .token.null.keyword{color:#b76b01}.md-code-block.md-code-block-light .language-markdown .token.url,.md-code-block.md-code-block-light .language-markdown .token.url>.token.operator,.md-code-block.md-code-block-light .language-markdown .token.url-reference.url>.token.string{color:#383a42}.md-code-block.md-code-block-light .language-markdown .token.url>.token.content{color:#4078f2}.md-code-block.md-code-block-light .language-markdown .token.url>.token.url,.md-code-block.md-code-block-light .language-markdown .token.url-reference.url{color:#0184bc}.md-code-block.md-code-block-light .language-markdown .token.blockquote.punctuation,.md-code-block.md-code-block-light .language-markdown .token.hr.punctuation{color:#a0a1a7;font-style:italic}.md-code-block.md-code-block-light .language-markdown .token.code-snippet{color:#50a14f}.md-code-block.md-code-block-light .language-markdown .token.bold .token.content{color:#b76b01}.md-code-block.md-code-block-light .language-markdown .token.italic .token.content{color:#a626a4}.md-code-block.md-code-block-light .language-markdown .token.strike .token.content,.md-code-block.md-code-block-light .language-markdown .token.strike .token.punctuation,.md-code-block.md-code-block-light .language-markdown .token.list.punctuation,.md-code-block.md-code-block-light .language-markdown .token.title.important>.token.punctuation{color:#e45649}.md-code-block.md-code-block-light .token.bold{font-weight:700}.md-code-block.md-code-block-light .token.comment,.md-code-block.md-code-block-light .token.italic{font-style:italic}.md-code-block.md-code-block-light .token.entity{cursor:help}.md-code-block.md-code-block-light .token.namespace{opacity:.8}.md-code-block.md-code-block-light .token.token.tab:not(:empty):before,.md-code-block.md-code-block-light .token.token.cr:before,.md-code-block.md-code-block-light .token.token.lf:before,.md-code-block.md-code-block-light .token.token.space:before{color:#383a4233}.md-code-block-light{--ds-md-code-block-border-color: #e5e5e5;--ds-md-code-block-banner-wrap-background-color: white;--ds-md-code-banner-background-color: #f5f5f5;color:#494949;background:#fafafa}.md-code-block-dark{--ds-md-code-block-border-color: var(--dsr-bg, transparent);--ds-md-code-block-banner-wrap-background-color: #292a2d;--ds-md-code-banner-background-color: #404045;color:#fff;background:#212327}.md-code-block{--ds-md-code-block-border-radius: 12px;--ds-md-code-block-font-size: calc(var(--ds-md-zoom) * var(--ds-font-size-xsp));border-radius:var(--ds-md-code-block-border-radius);font-size:var(--ds-md-code-block-font-size);line-height:calc(var(--ds-md-code-block-font-size) * 1.6)}.md-code-block:not(:last-child){margin-bottom:calc(var(--ds-md-zoom) * 10px)}.md-code-block-banner-wrap{background-color:var(--ds-md-code-block-banner-wrap-background-color);z-index:6;position:-webkit-sticky;position:sticky;top:0}.md-code-block-banner{background:var(--ds-md-code-banner-background-color);padding:calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);font-size:var(--ds-md-code-block-font-size);line-height:var(--ds-md-code-block-font-size);justify-content:space-between;display:flex}.md-code-block-banner-lite{padding:0}.md-code-block-banner{border-top-left-radius:var(--ds-md-code-block-border-radius);border-top-right-radius:var(--ds-md-code-block-border-radius)}.md-code-block-footer{background:var(--ds-md-code-banner-background-color);padding:calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);font-size:var(--ds-md-code-block-font-size);line-height:var(--ds-md-code-block-font-size);border-bottom-left-radius:var(--ds-md-code-block-border-radius);border-bottom-right-radius:var(--ds-md-code-block-border-radius);justify-content:flex-end;display:flex}.md-code-block-action{align-items:center;display:flex}.md-code-block>pre{padding:calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);white-space:pre-wrap;word-break:break-all;margin:0!important}:root{--message-list-padding-horizontal: 16px;--message-list-max-width: 800px}html,body{margin:0;padding:0;box-sizing:border-box;height:100%;overflow:hidden}#root{height:100%;overflow:hidden}.ds-message{height:100%;overflow:auto;display:flex;flex-direction:column}.ds-message-box{flex:1;overflow:auto}.ds-message-list{max-width:var(--message-list-max-width);margin:0 auto;padding:var(--message-list-padding-horizontal)}@media (min-width: 800px){.ds-message-actions{width:var(--message-list-max-width)}}.ds-message-actions{padding:30px;max-width:var(--message-list-max-width);padding:var(--message-list-padding-horizontal);margin:0 auto}.ds-message-actions button{padding:10px 20px;border-radius:5px;border:1px solid #1890ff;background-color:#1890ff;cursor:pointer;color:#fff;font-size:20px;font-weight:600}
2 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ds-markdown
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import globals from 'globals';
3 | import reactHooks from 'eslint-plugin-react-hooks';
4 | import reactRefresh from 'eslint-plugin-react-refresh';
5 | import tseslint from 'typescript-eslint';
6 | import eslintConfigPrettier from 'eslint-config-prettier/flat';
7 |
8 | export default tseslint.config(
9 | { ignores: ['dist'] },
10 | {
11 | extends: [js.configs.recommended, ...tseslint.configs.recommended],
12 | files: ['**/*.{ts,tsx}'],
13 | languageOptions: {
14 | ecmaVersion: 2020,
15 | globals: globals.browser,
16 | },
17 | plugins: {
18 | 'react-hooks': reactHooks,
19 | 'react-refresh': reactRefresh,
20 | },
21 | rules: {
22 | ...reactHooks.configs.recommended.rules,
23 | 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
24 | 'no-unused-vars': 'warn',
25 | '@typescript-eslint/no-unused-vars': 'warn',
26 | },
27 | },
28 | eslintConfigPrettier,
29 | );
30 |
--------------------------------------------------------------------------------
/example/basic/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "message_id": 2,
3 | "parent_id": 1,
4 | "model": "",
5 | "role": "ASSISTANT",
6 | "content": "React 19 是 React 生态的重要更新,引入了多项改进和新特性,旨在简化开发流程、优化性能并增强功能。以下是 React 19 的主要新特性:\n\n---\n\n### 1. **React 编译器(React Compiler)**\n - **自动优化组件**:通过静态分析和动态跟踪,自动对组件进行记忆化(memoization),减少不必要的重新渲染。\n - **无需手动优化**:开发者不再需要手动使用 `useMemo` 或 `useCallback`,编译器会智能处理依赖项。\n\n---\n\n### 2. **服务端组件(Server Components)正式支持**\n - **服务端渲染优化**:直接在服务端渲染组件,减少客户端加载的代码量,提升首屏性能。\n - **无缝数据获取**:服务端组件可直接访问数据库或 API,简化数据获取流程。\n - **渐进式集成**:支持与服务端框架(如 Next.js、Remix)深度集成。\n\n---\n\n### 3. **动作处理(Actions)**\n - **简化表单处理**:通过 `useActionState` 和 `useFormStatus` 等新 Hook,统一管理表单提交状态和结果。\n - **示例代码**:\n ```jsx\n function Form({ action }) {\n const [state, formAction] = useActionState(action, null);\n return (\n \n );\n }\n ```\n\n---\n\n### 4. **文档元数据支持**\n - **SEO 优化**:直接在组件内使用 ``、`` 等标签管理文档元数据,无需第三方库。\n - **示例**:\n ```jsx\n function Page() {\n return (\n <>\n React 19 新特性\n \n >\n );\n }\n ```\n\n---\n\n### 5. **资源加载优化**\n - **异步资源预加载**:通过 `preload`、`preinit` 等 API 提前加载脚本或样式,减少页面阻塞。\n - **代码示例**:\n ```jsx\n import { prefetchDNS, preconnect, preload } from 'react-dom';\n prefetchDNS(\"https://example.com\");\n preload(\"https://example.com/style.css\", { as: \"style\" });\n ```\n\n---\n\n### 6. **增强的 Hooks**\n - **`use` Hook**:支持在条件语句或循环中使用异步操作,简化数据获取逻辑。\n ```jsx\n const data = use(fetchData());\n ```\n - **`useOptimistic`**:乐观更新 UI,适用于实时应用(如聊天、投票)。\n ```jsx\n const [optimisticState, addOptimistic] = useOptimistic(\n state,\n (currentState, optimisticValue) => [...currentState, optimisticValue]\n );\n ```\n\n---\n\n### 7. **错误处理改进**\n - **服务端错误恢复**:服务端组件渲染出错时,客户端可自动尝试重新渲染,避免页面崩溃。\n - **更清晰的错误边界**:通过 `errorBoundary` 属性指定错误处理组件。\n\n---\n\n### 8. **兼容性调整**\n - **弃用 `react-dom/createRoot`**:推荐使用新的根 API `react-dom/client`。\n - **移除旧版 API**:如 `ReactDOM.render` 和 `react-dom/server` 的遗留方法。\n\n---\n\n### 升级建议\n- **测试环境验证**:在非生产环境充分测试现有代码,尤其是涉及服务端渲染和表单处理的逻辑。\n- **关注官方迁移指南**:React 官网通常会提供详细的版本迁移说明。\n\n---\n\nReact 19 的这些改进显著降低了开发复杂性,同时提升了性能和用户体验。建议开发者参考 [React 官方博客](https://react.dev/blog) 获取最新动态和详细文档。",
7 | "thinking_enabled": true,
8 | "thinking_content": "好的,我现在需要回答用户关于React 19有哪些新特性的问题。首先,我得回想一下React的最新版本和官方发布的信息。React的版本号有时候有点让人困惑,因为用户提到的React 19可能指的是尚未发布或者刚刚发布的版本。我记得之前React 18是在2022年发布的,引入了并发特性。现在可能React 19已经进入测试阶段或者已经发布,但需要确认。\n\n接下来,我应该检查官方文档或React团队的官方公告,看看React 19的新特性有哪些。如果官方文档还没有更新,可能需要查看Beta版或Canary版的发布说明。比如,React的更新通常会先发布在Canary通道,供开发者测试。\n\n然后,我需要列出React 19的主要新特性。根据之前的社区讨论和RFC(Request for Comments)提案,可能包括自动批处理改进、新的钩子、服务器组件支持、文档元数据支持、资源加载优化、动作处理改进、错误处理增强等。比如,React Server Components可能已经稳定,并成为正式版的一部分。另外,React可能会引入use钩子来处理异步操作,简化数据获取。\n\n另外,需要注意React 19是否引入了破坏性变更,或者是否有弃用的功能。例如,可能移除了某些旧的API,或者改变了生命周期方法的行为。需要提醒用户注意这些变化,以便升级时做好准备。\n\n可能还需要提到一些性能方面的改进,比如更高效的渲染机制,或者与React Compiler相关的优化,自动处理组件的记忆化,减少不必要的重新渲染。\n\n然后,我需要将这些信息组织成结构化的回答,分点列出主要特性,每个特性简要说明其作用和好处。同时,确保信息准确,避免猜测,不确定的部分需要注明可能还在测试阶段或需要参考官方文档。\n\n最后,提醒用户由于React 19可能尚未正式发布,部分特性可能变更,建议关注官方发布说明。另外,如果用户现在使用的是旧版本,可能需要了解升级路径和注意事项。",
9 | "thinking_elapsed_secs": 19,
10 | "ban_edit": false,
11 | "ban_regenerate": false,
12 | "status": "FINISHED",
13 | "accumulated_token_usage": 868,
14 | "files": [],
15 | "tips": [
16 | {
17 | "type": "warning",
18 | "position": "bottom",
19 | "content": "本回答由 AI 生成,内容仅供参考,请仔细甄别。"
20 | }
21 | ],
22 | "inserted_at": 1746086789.538057,
23 | "search_enabled": false,
24 | "search_status": null,
25 | "search_results": null
26 | }
27 |
--------------------------------------------------------------------------------
/example/basic/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import Markdown from '../../src';
5 | import json from './data.json';
6 |
7 | function throttle(fn: (...args: any[]) => void, delay: number) {
8 | let lastTime = 0;
9 | return (...args: unknown[]) => {
10 | const now = Date.now();
11 | if (now - lastTime > delay) {
12 | fn(...args);
13 | lastTime = now;
14 | }
15 | };
16 | }
17 |
18 | const BasicDemo = () => {
19 | const [thinkingContent, setThinkingContent] = useState('');
20 | const [answerContent, setAnswerContent] = useState('');
21 | const messageDivRef = useRef(null!);
22 |
23 | const scrollCacheRef = useRef<{
24 | type: 'manual' | 'auto';
25 | needAutoScroll: boolean;
26 | prevScrollTop: number;
27 | }>({
28 | type: 'manual',
29 | needAutoScroll: true,
30 | prevScrollTop: 0,
31 | });
32 |
33 | const onClick = () => {
34 | setThinkingContent(json.thinking_content);
35 | };
36 | const onReset = () => {
37 | setThinkingContent('');
38 | setAnswerContent('');
39 | };
40 |
41 | const throttleOnTypedChar = useMemo(() => {
42 | return throttle(() => {
43 | if (!scrollCacheRef.current.needAutoScroll) return;
44 | const messageDiv = messageDivRef.current;
45 | // 自动滑动到最底部
46 | if (messageDiv) {
47 | messageDiv.scrollTo({
48 | top: messageDiv.scrollHeight,
49 | behavior: 'smooth',
50 | });
51 | }
52 | }, 50);
53 | }, []);
54 |
55 | const onScroll = useMemo(() => {
56 | return throttle((e: React.UIEvent) => {
57 | // 如果是往上滚动,则说明是手动滚动,则需要停止自动向下滚动
58 | // console.log(e.currentTarget.scrollTop - scrollCacheRef.current.prevScrollTop);
59 | if (e.currentTarget.scrollTop < scrollCacheRef.current.prevScrollTop) {
60 | scrollCacheRef.current.needAutoScroll = false;
61 | }
62 | scrollCacheRef.current.prevScrollTop = e.currentTarget.scrollTop;
63 | }, 50);
64 | }, []);
65 |
66 | return (
67 |
68 |
69 | {thinkingContent ? : } React 19有哪些新特性
70 |
71 |
72 |
73 | {
77 | // console.log('思考完成', args);
78 | if (thinkingContent) {
79 | setAnswerContent(json.content);
80 | }
81 | }}
82 | // onStart={(args) => {
83 | // console.log('思考开始', args);
84 | // }}
85 | // onTypedChar={(args) => {
86 | // console.log('打字中', args);
87 | // }}
88 | onTypedChar={throttleOnTypedChar}
89 | >
90 | {thinkingContent}
91 |
92 |
93 | {answerContent && (
94 | {
98 | // console.log('思考完成', args);
99 | // if (thinkingContent) {
100 | // setAnswerContent(json.content);
101 | // }
102 | // }}
103 | // onStart={(args) => {
104 | // console.log('思考开始', args);
105 | // }}
106 | // onTypedChar={(args) => {
107 | // console.log('打字中', args);
108 | // }}
109 | onTypedChar={throttleOnTypedChar}
110 | >
111 | {answerContent}
112 |
113 | )}
114 |
115 |
116 |
117 | );
118 | };
119 |
120 | export default BasicDemo;
121 |
--------------------------------------------------------------------------------
/example/cdm/define.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | export enum CozeMessageEnum {
4 | Created = 'event:conversation.chat.created',
5 | InProgress = 'event:conversation.chat.in_progress',
6 | Delta = 'event:conversation.message.delta',
7 | MessageCompleted = 'event:conversation.message.completed',
8 | ChatCompleted = 'event:conversation.chat.completed',
9 | }
10 |
11 |
12 | export interface CozeResultI {
13 | content?: string;
14 | usage?: {
15 | token_count: number;
16 | output_count: number;
17 | input_count: number;
18 | };
19 | completed_at?: number;
20 | created_at?: number;
21 | }
22 |
23 | export interface CozeMessageDataI extends CozeResultI {
24 | id: string;
25 | conversation_id: string;
26 | bot_id: string;
27 | last_error: { code: number; msg: string };
28 | status: string;
29 | role?: 'user' | 'assistant';
30 |
31 | /**
32 | * 推理思考过程
33 | */
34 | reasoning_content?: string;
35 | type?: 'answer';
36 |
37 | }
38 |
39 | export interface CozeMessageI {
40 | type: CozeMessageEnum;
41 | data: CozeMessageDataI;
42 | }
43 |
--------------------------------------------------------------------------------
/example/cdm/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | // 导入 ./cozeData.text
4 |
5 | import cozeData from './cozeData';
6 |
7 | interface CMDDemoProps {
8 | id?: number;
9 | }
10 |
11 | const modulePrefix = 'CMDDemo';
12 | const CMDDemo: React.FC = (props: CMDDemoProps) => {
13 | return ;
14 | };
15 |
16 | export default CMDDemo;
17 |
--------------------------------------------------------------------------------
/example/cdm/util.ts:
--------------------------------------------------------------------------------
1 | import { CozeMessageEnum, CozeMessageI } from './define';
2 |
3 | export function parseSSEData(rawData: string) {
4 | const events: CozeMessageI[] = [];
5 | const lines = rawData.split('\n');
6 | let currentEvent: Partial = {};
7 |
8 | for (const line of lines) {
9 | const trimmedLine = line.trim();
10 | if (!trimmedLine) continue; // 忽略空行
11 |
12 | // 解析 event 行
13 | if (trimmedLine.startsWith('event:')) {
14 | currentEvent.type = trimmedLine.trim() as CozeMessageEnum;
15 | } else if (trimmedLine.startsWith('data:')) {
16 | // 解析 data 行
17 | try {
18 | const jsonString = trimmedLine.slice(5).trim();
19 | currentEvent.data = JSON.parse(jsonString);
20 | events.push(currentEvent as CozeMessageI); // 保存完整事件
21 | currentEvent = {}; // 重置临时对象
22 | } catch (e) {
23 | console.error('JSON 解析失败:', e);
24 | }
25 | }
26 | }
27 |
28 | return events;
29 | }
30 |
--------------------------------------------------------------------------------
/example/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --message-list-padding-horizontal: 16px;
3 | --message-list-max-width: 800px;
4 | }
5 |
6 | html,
7 | body {
8 | margin: 0;
9 | padding: 0;
10 | box-sizing: border-box;
11 | height: 100%;
12 | overflow: hidden;
13 | }
14 |
15 | #root {
16 | height: 100%;
17 | overflow: hidden;
18 | }
19 |
20 | .ds-message {
21 | height: 100%;
22 | overflow: auto;
23 | display: flex;
24 | flex-direction: column;
25 | }
26 |
27 | .ds-message-box {
28 | flex: 1;
29 | overflow: auto;
30 | }
31 |
32 | .ds-message-list {
33 | max-width: var(--message-list-max-width);
34 | margin: 0 auto;
35 | padding: var(--message-list-padding-horizontal);
36 | }
37 |
38 | @media (min-width: 800px) {
39 | .ds-message-actions {
40 | width: var(--message-list-max-width);
41 | }
42 | }
43 |
44 | .ds-message-actions {
45 | padding: 30px;
46 | max-width: var(--message-list-max-width);
47 | padding: var(--message-list-padding-horizontal);
48 | margin: 0 auto;
49 | button {
50 | padding: 10px 20px;
51 | border-radius: 5px;
52 | border: 1px solid #1890ff;
53 | background-color: #1890ff;
54 | cursor: pointer;
55 | color: #fff;
56 | font-size: 20px;
57 | font-weight: 600;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import '../src/style.less';
5 | import './index.css';
6 |
7 | import BasicDemo from './basic';
8 | const App = () => {
9 | return ;
10 | };
11 |
12 | createRoot(document.getElementById('root')!).render(
13 |
14 |
15 | ,
16 | );
17 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ds-markdown
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) onshinpei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ds-markdown",
3 | "private": false,
4 | "version": "0.0.9",
5 | "main": "./dist/cjs/index.js",
6 | "types": "./dist/cjs/index.d.ts",
7 | "module": "./dist/esm/index.js",
8 | "style": "./src/style.css",
9 | "type": "module",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/onshinpei/ds-markdown"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/onshinpei/ds-markdown/issues"
17 | },
18 | "exports": {
19 | ".": {
20 | "import": {
21 | "types": "./dist/esm/index.d.ts",
22 | "default": "./dist/esm/index.js"
23 | },
24 | "require": {
25 | "types": "./dist/cjs/index.d.ts",
26 | "default": "./dist/cjs/index.js"
27 | }
28 | },
29 | "./style.css": "./dist/style.css"
30 | },
31 | "files": [
32 | "dist"
33 | ],
34 | "scripts": {
35 | "dev": "vite",
36 | "build:docs": "rimraf docs && vite build",
37 | "build": "rimraf dist &&npm run build:cjs && npm run build:esm && npm run build:less",
38 | "build:cjs": "tsc --project tsconfig-cjs.json && echo '{ \"type\": \"commonjs\" }' > dist/cjs/package.json",
39 | "build:esm": "tsc --project tsconfig-esm.json",
40 | "build:less": "lessc src/style.less dist/style.css",
41 | "lint": "eslint .",
42 | "preview": "vite preview",
43 | "release": "npm run build && node scripts/set-tag.cjs && cross-env RELEASE_MODE=true npm publish",
44 | "prepublishOnly": "node scripts/prepublish.js"
45 | },
46 | "dependencies": {
47 | "classnames": "^2.5.1",
48 | "react-markdown": "^10.1.0",
49 | "react-syntax-highlighter": "^15.6.1",
50 | "remark-gfm": "^4.0.1"
51 | },
52 | "devDependencies": {
53 | "@antfu/eslint-config": "^4.12.0",
54 | "@babel/core": "^7.27.1",
55 | "@babel/preset-react": "^7.27.1",
56 | "@babel/preset-typescript": "^7.27.1",
57 | "@eslint-react/eslint-plugin": "^1.48.5",
58 | "@eslint/js": "^9.25.0",
59 | "@rollup/plugin-babel": "^6.0.4",
60 | "@rollup/plugin-commonjs": "^28.0.3",
61 | "@rollup/plugin-node-resolve": "^16.0.1",
62 | "@rollup/plugin-typescript": "^12.1.2",
63 | "@testing-library/jest-dom": "^6.6.3",
64 | "@types/jest": "^29.5.14",
65 | "@types/react": "^19.1.2",
66 | "@types/react-dom": "^19.1.2",
67 | "@types/react-syntax-highlighter": "^15.5.13",
68 | "@vitejs/plugin-react": "^4.4.1",
69 | "cross-env": "^7.0.3",
70 | "eslint": "^9.25.0",
71 | "eslint-config-prettier": "^10.1.2",
72 | "eslint-plugin-compat": "^6.0.2",
73 | "eslint-plugin-prettier": "^5.3.1",
74 | "eslint-plugin-react": "^7.37.5",
75 | "eslint-plugin-react-hooks": "^5.2.0",
76 | "eslint-plugin-react-refresh": "^0.4.19",
77 | "globals": "^16.0.0",
78 | "less": "^4.3.0",
79 | "prettier": "3.5.3",
80 | "react": "^19.1.0",
81 | "react-dom": "^19.1.0",
82 | "react-refresh": "^0.17.0",
83 | "rehype-stringify": "^10.0.1",
84 | "remark-parse": "^11.0.0",
85 | "remark-rehype": "^11.1.2",
86 | "rimraf": "^6.0.1",
87 | "rollup": "^4.40.2",
88 | "rollup-plugin-postcss": "^4.0.2",
89 | "typescript": "~5.8.3",
90 | "typescript-eslint": "^8.30.1",
91 | "unified": "^11.0.5",
92 | "vite": "^6.3.2"
93 | },
94 | "peerDependencies": {
95 | "react": ">=16.8.0"
96 | },
97 | "keywords": [
98 | "markdown",
99 | "deepseek",
100 | "deepseek-markdown",
101 | "deepseek-markdown-react",
102 | "react",
103 | "react-component",
104 | "react-markdown"
105 | ],
106 | "publishConfig": {
107 | "tag": "latest"
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | printWidth: 200,
3 | tabWidth: 2,
4 | useTabs: false,
5 | singleQuote: true,
6 | };
7 |
--------------------------------------------------------------------------------
/scripts/prepublish.js:
--------------------------------------------------------------------------------
1 | const RELEASE_MODE = !!process.env.RELEASE_MODE;
2 |
3 | if (!RELEASE_MODE) {
4 | console.log('Run `npm run release` to publish the package');
5 | process.exit(1);
6 | }
7 |
--------------------------------------------------------------------------------
/scripts/set-tag.cjs:
--------------------------------------------------------------------------------
1 | // scripts/set-tag.js
2 | const { version } = require('../package.json');
3 | const fs = require('fs');
4 | const path = require('path');
5 | const { execSync } = require('child_process');
6 | // 如果当前分支是main,则设置tag为latest,否则设置为beta
7 |
8 | const currentBranch = execSync('git branch --show-current').toString().trim();
9 |
10 | const packagePath = path.join(__dirname, '../package.json');
11 | const packageData = require(packagePath);
12 |
13 | if (currentBranch === 'main') {
14 | packageData.publishConfig = { tag: 'latest' };
15 | // 如果读取到版本号位 0.0.7-beta.x,则latest版本号设置为0.0.7
16 | // 0.0.7-beta.x -> 0.0.7
17 | if (version.includes('beta')) {
18 | packageData.version = version.split('-')[0];
19 | } else {
20 | packageData.version = version.split('.')[0] + '.' + parseInt(version.split('.')[1]) + '.' + (parseInt(version.split('.')[2]) + 1);
21 | }
22 | } else {
23 | packageData.publishConfig = { tag: 'beta' };
24 |
25 | if (version.includes('beta')) {
26 | // 如果读取到版本号位 0.0.7-beta.0,则beta版本号设置为0.0.8-beta.1
27 | packageData.version = version.split('-')[0] + '-beta.' + (parseInt(version.split('-')[1].split('.')[1]) + 1);
28 | } else {
29 | // 如果读取到版本号位 0.0.7,则beta版本号设置为0.0.8-beta.0
30 | packageData.version = version.split('.')[0] + '.' + parseInt(version.split('.')[1]) + '.' + (parseInt(version.split('.')[2]) + 1) + '-beta.0';
31 | }
32 | }
33 |
34 | fs.writeFileSync(packagePath, JSON.stringify(packageData, null, 2));
35 |
--------------------------------------------------------------------------------
/shim.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css' {
2 | const content: { [className: string]: string };
3 | export default content;
4 | }
5 |
6 | declare module '*.less' {
7 | const content: { [className: string]: string };
8 | export default content;
9 | }
10 |
--------------------------------------------------------------------------------
/src/AutoScroll/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | interface AutoScrollProps {
4 | children: React.ReactNode;
5 | }
6 |
7 | const modulePrefix = 'AutoScroll';
8 | const AutoScroll: React.FC = (props: AutoScrollProps) => {
9 | return {props.children}
;
10 | };
11 |
12 | export default AutoScroll;
13 |
--------------------------------------------------------------------------------
/src/Markdown/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useEffect, useMemo, useRef } from 'react';
2 | import { __DEV__ } from '../constant.js';
3 | import { AnswerType, MarkdownProps } from '../defined.js';
4 | import MarkdownCMD, { MarkdownRef } from '../MarkdownCMD/index.js';
5 |
6 | interface MarkdownImplProps extends MarkdownProps {
7 | children: string | undefined;
8 | answerType: AnswerType;
9 | }
10 |
11 | const MarkdownInner: React.FC = ({ children: _children = '', answerType, ...rest }) => {
12 | const cmdRef = useRef(null!);
13 | const prefixRef = useRef('');
14 | const content = useMemo(() => {
15 | if (typeof _children === 'string') {
16 | return _children;
17 | }
18 | if (__DEV__) {
19 | console.error('Markdown组件的子元素必须是一个字符串');
20 | }
21 | return '';
22 | }, [_children]);
23 |
24 | useEffect(() => {
25 | if (prefixRef.current !== content) {
26 | let newContent = '';
27 | if (prefixRef.current === '') {
28 | newContent = content;
29 | } else {
30 | if (content.startsWith(prefixRef.current)) {
31 | newContent = content.slice(prefixRef.current.length);
32 | } else {
33 | newContent = content;
34 | cmdRef.current.clear();
35 | }
36 | }
37 | cmdRef.current.push(newContent, answerType);
38 | prefixRef.current = content;
39 | }
40 | }, [answerType, content]);
41 |
42 | return ;
43 | };
44 |
45 | const Markdown: React.FC = (props) => {
46 | const { children = '', answerType = 'answer' } = props;
47 |
48 | if (__DEV__) {
49 | if (!['thinking', 'answer'].includes(answerType)) {
50 | throw new Error('Markdown组件的answerType必须是thinking或answer');
51 | }
52 | if (typeof children !== 'string') {
53 | throw new Error('Markdown组件的子元素必须是一个字符串');
54 | }
55 | }
56 |
57 | return ;
58 | };
59 |
60 | export default memo(Markdown);
61 |
--------------------------------------------------------------------------------
/src/MarkdownCMD/index.tsx:
--------------------------------------------------------------------------------
1 | import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2 |
3 | import HighReactMarkdown from '../components/HighReactMarkdown/index.js';
4 | import classNames from 'classnames';
5 | import { AnswerType, IParagraph, MarkdownProps } from '../defined.js';
6 | import { __DEV__ } from '../constant.js';
7 |
8 | type MarkdownCMDProps = MarkdownProps;
9 |
10 | interface IChar {
11 | content: string;
12 | answerType: AnswerType;
13 | }
14 |
15 | export interface MarkdownRef {
16 | push: (content: string, answerType: AnswerType) => void;
17 | clear: () => void;
18 | triggerWholeEnd: () => void;
19 | }
20 | const MarkdownCMD = forwardRef(({ interval = 30, isClosePrettyTyped = false, onEnd, onStart, onTypedChar }, ref) => {
21 | /** 当前需要打字的内容 */
22 | const charsRef = useRef([]);
23 |
24 | /**
25 | * 打字是否已经完全结束
26 | * 如果打字已经完全结束,则不会再触发打字效果
27 | */
28 | const isWholeTypedEndRef = useRef(false);
29 |
30 | /** 已经打过的字 */
31 | const typedCharsRef = useRef<{ typedContent: string; answerType: AnswerType; prevStr: string } | undefined>(undefined);
32 | /** 是否卸载 */
33 | const isUnmountRef = useRef(false);
34 | /** 是否正在打字 */
35 | const isTypedRef = useRef(false);
36 |
37 | /** 打字结束回调, */
38 | const onEndRef = useRef(onEnd);
39 | onEndRef.current = onEnd;
40 | /** 打字开始回调 */
41 | const onStartRef = useRef(onStart);
42 | onStartRef.current = onStart;
43 | /** 打字过程中回调 */
44 | const onTypedCharRef = useRef(onTypedChar);
45 | onTypedCharRef.current = onTypedChar;
46 |
47 | /** 打字定时器 */
48 | const timerRef = useRef(null);
49 | /**
50 | * 稳定段落
51 | * 稳定段落是已经打过字,并且不会再变化的段落
52 | */
53 | const [stableParagraphs, setStableParagraphs] = useState([]);
54 | /** 当前段落 */
55 | const [currentParagraph, setCurrentParagraph] = useState(undefined);
56 | /** 当前段落引用 */
57 | const currentParagraphRef = useRef(undefined);
58 | currentParagraphRef.current = currentParagraph;
59 |
60 | /** 清除打字定时器 */
61 | const clearTimer = () => {
62 | if (timerRef.current) {
63 | clearTimeout(timerRef.current);
64 | timerRef.current = null;
65 | }
66 | isTypedRef.current = false;
67 | };
68 |
69 | useEffect(() => {
70 | isUnmountRef.current = false;
71 | return () => {
72 | isUnmountRef.current = true;
73 | };
74 | }, []);
75 |
76 | /** 思考段落 */
77 | const thinkingParagraphs = useMemo(() => stableParagraphs.filter((paragraph) => paragraph.answerType === 'thinking'), [stableParagraphs]);
78 | /** 回答段落 */
79 | const answerParagraphs = useMemo(() => stableParagraphs.filter((paragraph) => paragraph.answerType === 'answer'), [stableParagraphs]);
80 |
81 | /**
82 | * 记录打过的字
83 | * @param char 当前字符
84 | * @returns
85 | */
86 | const recordTypedChars = (char: IChar) => {
87 | let prevStr = '';
88 | if (!typedCharsRef.current || typedCharsRef.current.answerType !== char.answerType) {
89 | typedCharsRef.current = {
90 | typedContent: char.content,
91 | answerType: char.answerType,
92 | prevStr: '',
93 | };
94 | } else {
95 | prevStr = typedCharsRef.current.typedContent;
96 | typedCharsRef.current.typedContent += char.content;
97 | typedCharsRef.current.prevStr = prevStr;
98 | }
99 |
100 | return {
101 | prevStr,
102 | nextStr: typedCharsRef.current?.typedContent || '',
103 | };
104 | };
105 |
106 | /**
107 | * 触发打字开始回调
108 | * @param char 当前字符
109 | */
110 | const triggerOnStart = (char: IChar) => {
111 | const onStartFn = onStartRef.current;
112 | if (!onStartFn) {
113 | return;
114 | }
115 | const { prevStr } = recordTypedChars(char);
116 | onStartRef.current?.({
117 | currentIndex: prevStr.length,
118 | currentChar: char.content,
119 | answerType: char.answerType,
120 | prevStr,
121 | });
122 | };
123 |
124 | /**
125 | * 触发打字结束回调
126 | */
127 | const triggerOnEnd = () => {
128 | const onEndFn = onEndRef.current;
129 | if (!onEndFn) {
130 | return;
131 | }
132 |
133 | onEndFn({
134 | str: typedCharsRef.current?.typedContent,
135 | answerType: typedCharsRef.current?.answerType,
136 | });
137 | };
138 |
139 | /**
140 | * 触发打字过程中回调
141 | * @param char 当前字符
142 | * @param isStartPoint 是否是开始打字(第一个字)
143 | */
144 | const triggerOnTypedChar = (char: IChar, isStartPoint = false) => {
145 | const onTypedCharFn = onTypedCharRef.current;
146 | if (!isStartPoint) {
147 | recordTypedChars(char);
148 | }
149 | if (!onTypedCharFn) {
150 | return;
151 | }
152 |
153 | onTypedCharFn({
154 | currentIndex: typedCharsRef.current?.prevStr.length || 0,
155 | currentChar: char.content,
156 | answerType: char.answerType,
157 | prevStr: typedCharsRef.current?.prevStr || '',
158 | });
159 | };
160 |
161 | /** 开始打字任务 */
162 | const startTypedTask = () => {
163 | if (isTypedRef.current) {
164 | return;
165 | }
166 |
167 | const chars = charsRef.current;
168 |
169 | /** 停止打字 */
170 | const stopTyped = () => {
171 | isTypedRef.current = false;
172 | if (timerRef.current) {
173 | clearTimeout(timerRef.current);
174 | timerRef.current = null;
175 | }
176 | triggerOnEnd();
177 | };
178 |
179 | /** 打下一个字 */
180 | const nextTyped = () => {
181 | if (chars.length === 0) {
182 | stopTyped();
183 | return;
184 | }
185 | timerRef.current = setTimeout(startTyped, interval);
186 | };
187 |
188 | /**
189 | * 开始打字
190 | * @param isStartPoint 是否是开始打字
191 | */
192 | function startTyped(isStartPoint = false) {
193 | if (isUnmountRef.current) {
194 | return;
195 | }
196 | isTypedRef.current = true;
197 |
198 | const char = chars.shift();
199 | if (char === undefined) {
200 | stopTyped();
201 | return;
202 | }
203 |
204 | if (isStartPoint) {
205 | triggerOnStart(char);
206 | triggerOnTypedChar(char, isStartPoint);
207 | } else {
208 | triggerOnTypedChar(char);
209 | }
210 |
211 | const currentParagraph = currentParagraphRef.current;
212 | /** 如果碰到 则需要处理成两个段落 */
213 | if (char.content === '\n\n') {
214 | if (currentParagraph) {
215 | setStableParagraphs((prev) => {
216 | const newParagraphs = [...prev];
217 | if (currentParagraph) {
218 | newParagraphs.push({ ...currentParagraph, isTyped: false });
219 | }
220 | newParagraphs.push({
221 | content: '',
222 | isTyped: false,
223 | type: 'br',
224 | answerType: char.answerType,
225 | });
226 | return newParagraphs;
227 | });
228 | setCurrentParagraph(undefined);
229 | } else {
230 | setStableParagraphs((prev) => {
231 | const newParagraphs = [...prev];
232 | newParagraphs.push({
233 | content: '',
234 | isTyped: false,
235 | type: 'br',
236 | answerType: char.answerType,
237 | });
238 | return newParagraphs;
239 | });
240 | }
241 | nextTyped();
242 | return;
243 | }
244 |
245 | // 处理当前段落
246 | let _currentParagraph = currentParagraph;
247 | const newCurrentParagraph: IParagraph = {
248 | content: '',
249 | isTyped: false,
250 | type: 'text',
251 | answerType: char.answerType,
252 | };
253 |
254 | if (!_currentParagraph) {
255 | // 如果当前没有段落,则直接设置为当前段落
256 | _currentParagraph = newCurrentParagraph;
257 | } else if (currentParagraph && currentParagraph?.answerType !== char.answerType) {
258 | // 如果当前段落和当前字符的回答类型不一致,则需要处理成两个段落
259 | setStableParagraphs((prev) => {
260 | const newParagraphs = [...prev];
261 | newParagraphs.push({ ...currentParagraph, isTyped: false });
262 | return newParagraphs;
263 | });
264 | _currentParagraph = newCurrentParagraph;
265 | setCurrentParagraph(_currentParagraph);
266 | }
267 |
268 | setCurrentParagraph((prev) => {
269 | return {
270 | ..._currentParagraph,
271 | content: (prev?.content || '') + char.content,
272 | isTyped: true,
273 | };
274 | });
275 |
276 | nextTyped();
277 | }
278 |
279 | startTyped(true);
280 | };
281 |
282 | useImperativeHandle(ref, () => ({
283 | /**
284 | * 添加内容
285 | * @param content 内容 {string}
286 | * @param answerType 回答类型 {AnswerType}
287 | */
288 | push: (content: string, answerType: AnswerType) => {
289 | if (isWholeTypedEndRef.current) {
290 | if (__DEV__) {
291 | console.warn('打字已经完全结束,不能再添加新的内容');
292 | }
293 | return;
294 | }
295 | // 如果两个\n,则\n这两个字符要合一起,作为一个字符处理,并且把多个\n处理成一个\n
296 | const charsGroup = content.split('\n\n');
297 | charsGroup.forEach((chars, index) => {
298 | if (isClosePrettyTyped) {
299 | charsRef.current.push({ content: chars, answerType });
300 | } else {
301 | charsRef.current.push(...chars.split('').map((char) => ({ content: char, answerType })));
302 | }
303 | if (index !== charsGroup.length - 1) {
304 | charsRef.current.push({ content: '\n\n', answerType });
305 | }
306 | });
307 |
308 | if (!isTypedRef.current) {
309 | startTypedTask();
310 | }
311 | },
312 | /**
313 | * 清除打字任务
314 | */
315 | clear: () => {
316 | clearTimer();
317 | charsRef.current = [];
318 | setStableParagraphs([]);
319 | setCurrentParagraph(undefined);
320 | isWholeTypedEndRef.current = false;
321 | },
322 | /**
323 | * 主动触发打字结束
324 | */
325 | triggerWholeEnd: () => {
326 | isWholeTypedEndRef.current = true;
327 | if (!isTypedRef.current) {
328 | triggerOnEnd();
329 | }
330 | },
331 | }));
332 |
333 | const getParagraphs = (paragraphs: IParagraph[], answerType: AnswerType) => {
334 | return (
335 |
336 | {paragraphs.map((paragraph, index) => {
337 | if (paragraph.type === 'br') {
338 | return null;
339 | }
340 | return {paragraph.content || ''};
341 | })}
342 | {currentParagraph?.answerType === answerType && {currentParagraph.content || ''}}
343 |
344 | );
345 | };
346 |
347 | return (
348 |
354 | {(thinkingParagraphs.length > 0 || currentParagraph?.answerType === 'thinking') &&
{getParagraphs(thinkingParagraphs, 'thinking')}
}
355 | {(answerParagraphs.length > 0 || currentParagraph?.answerType === 'answer') &&
{getParagraphs(answerParagraphs, 'answer')}
}
356 |
357 | );
358 | });
359 |
360 | export default MarkdownCMD;
361 |
--------------------------------------------------------------------------------
/src/components/BlockWrap/index.less:
--------------------------------------------------------------------------------
1 | .md-code-block {
2 | --ds-md-code-block-border-radius: 12px;
3 | --ds-md-code-block-font-size: calc(var(--ds-md-zoom) * var(--ds-font-size-xsp));
4 | border-radius: var(--ds-md-code-block-border-radius);
5 | font-size: var(--ds-md-code-block-font-size);
6 | line-height: calc(var(--ds-md-code-block-font-size) * 1.6);
7 | }
8 |
9 | .md-code-block-light {
10 | --ds-md-code-block-border-color: #e5e5e5;
11 | --ds-md-code-block-banner-wrap-background-color: white;
12 | --ds-md-code-banner-background-color: #f5f5f5;
13 | color: #494949;
14 | background: #fafafa;
15 | }
16 |
17 | .md-code-block-banner {
18 | border-top-left-radius: var(--ds-md-code-block-border-radius);
19 | border-top-right-radius: var(--ds-md-code-block-border-radius);
20 | }
21 |
22 | .md-code-block-banner-lite {
23 | padding: 0;
24 | }
25 |
26 | .md-code-block-banner-content {
27 | justify-content: space-between;
28 | align-items: center;
29 | padding: 6px 14px 6px 6px;
30 | display: flex;
31 | }
32 |
33 | .md-code-block-language {
34 | color: var(--dsr-text-2);
35 | margin-left: 8px;
36 | font-size: 12px;
37 | line-height: 18px;
38 | }
39 |
40 | .md-code-block-content {
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/BlockWrap/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | interface BlockWrapProps {
4 | children: React.ReactNode;
5 | language: string;
6 | }
7 |
8 | const BlockWrap: React.FC = ({ children, language }) => {
9 | return (
10 |
11 |
12 |
13 |
14 |
{language}
15 | {/*
复制
*/}
16 |
17 |
18 |
19 |
{children}
20 |
21 | );
22 | };
23 |
24 | export default BlockWrap;
25 |
--------------------------------------------------------------------------------
/src/components/HighReactMarkdown/highlightTheme.less:
--------------------------------------------------------------------------------
1 | .md-code-block.md-code-block-dark code[class*='language-'],
2 | .md-code-block.md-code-block-dark pre[class*='language-'] {
3 | color: #e3eaf2;
4 | text-align: left;
5 | white-space: pre;
6 | word-spacing: normal;
7 | word-break: normal;
8 | word-wrap: normal;
9 | -moz-tab-size: 4;
10 | tab-size: 4;
11 | -webkit-hyphens: none;
12 | -ms-hyphens: none;
13 | hyphens: none;
14 | background: 0 0;
15 | font-family:
16 | Consolas,
17 | Monaco,
18 | Andale Mono,
19 | Ubuntu Mono,
20 | monospace;
21 | line-height: 1.5;
22 | }
23 |
24 | .md-code-block.md-code-block-dark pre[class*='language-']::-moz-selection {
25 | background: #3c526d;
26 | }
27 |
28 | .md-code-block.md-code-block-dark pre[class*='language-'] ::-moz-selection {
29 | background: #3c526d;
30 | }
31 |
32 | .md-code-block.md-code-block-dark code[class*='language-']::-moz-selection {
33 | background: #3c526d;
34 | }
35 |
36 | .md-code-block.md-code-block-dark code[class*='language-'] ::-moz-selection {
37 | background: #3c526d;
38 | }
39 |
40 | .md-code-block.md-code-block-dark pre[class*='language-']::-moz-selection {
41 | background: #3c526d;
42 | }
43 |
44 | .md-code-block.md-code-block-dark pre[class*='language-'] ::-moz-selection {
45 | background: #3c526d;
46 | }
47 |
48 | .md-code-block.md-code-block-dark code[class*='language-']::-moz-selection {
49 | background: #3c526d;
50 | }
51 |
52 | .md-code-block.md-code-block-dark code[class*='language-'] ::-moz-selection {
53 | background: #3c526d;
54 | }
55 |
56 | .md-code-block.md-code-block-dark pre[class*='language-']::-moz-selection {
57 | background: #3c526d;
58 | }
59 |
60 | .md-code-block.md-code-block-dark pre[class*='language-']::selection {
61 | background: #3c526d;
62 | }
63 |
64 | .md-code-block.md-code-block-dark pre[class*='language-'] ::-moz-selection {
65 | background: #3c526d;
66 | }
67 |
68 | .md-code-block.md-code-block-dark pre[class*='language-'] ::selection {
69 | background: #3c526d;
70 | }
71 |
72 | .md-code-block.md-code-block-dark code[class*='language-']::-moz-selection {
73 | background: #3c526d;
74 | }
75 |
76 | .md-code-block.md-code-block-dark code[class*='language-']::selection {
77 | background: #3c526d;
78 | }
79 |
80 | .md-code-block.md-code-block-dark code[class*='language-'] ::-moz-selection {
81 | background: #3c526d;
82 | }
83 |
84 | .md-code-block.md-code-block-dark code[class*='language-'] ::selection {
85 | background: #3c526d;
86 | }
87 |
88 | .md-code-block.md-code-block-dark pre[class*='language-'] {
89 | margin: 0.5em 0;
90 | padding: 1em;
91 | overflow: auto;
92 | }
93 |
94 | .md-code-block.md-code-block-dark :not(pre) > code[class*='language-'],
95 | .md-code-block.md-code-block-dark pre[class*='language-'] {
96 | background: #111b27;
97 | }
98 |
99 | .md-code-block.md-code-block-dark :not(pre) > code[class*='language-'] {
100 | white-space: normal;
101 | border-radius: 0.3em;
102 | padding: 0.1em 0.3em;
103 | }
104 |
105 | .md-code-block.md-code-block-dark .token.comment,
106 | .md-code-block.md-code-block-dark .token.prolog,
107 | .md-code-block.md-code-block-dark .token.doctype,
108 | .md-code-block.md-code-block-dark .token.cdata {
109 | color: #8da1b9;
110 | }
111 |
112 | .md-code-block.md-code-block-dark .token.punctuation {
113 | color: #e3eaf2;
114 | }
115 |
116 | .md-code-block.md-code-block-dark .token.delimiter.important,
117 | .md-code-block.md-code-block-dark .token.selector .parent,
118 | .md-code-block.md-code-block-dark .token.tag,
119 | .md-code-block.md-code-block-dark .token.tag .token.punctuation {
120 | color: #6cc;
121 | }
122 |
123 | .md-code-block.md-code-block-dark .token.attr-name,
124 | .md-code-block.md-code-block-dark .token.boolean,
125 | .md-code-block.md-code-block-dark .token.boolean.important,
126 | .md-code-block.md-code-block-dark .token.number,
127 | .md-code-block.md-code-block-dark .token.constant,
128 | .md-code-block.md-code-block-dark .token.selector .token.attribute {
129 | color: #e6d37a;
130 | }
131 |
132 | .md-code-block.md-code-block-dark .token.class-name,
133 | .md-code-block.md-code-block-dark .token.key,
134 | .md-code-block.md-code-block-dark .token.parameter,
135 | .md-code-block.md-code-block-dark .token.property,
136 | .md-code-block.md-code-block-dark .token.property-access,
137 | .md-code-block.md-code-block-dark .token.variable {
138 | color: #6cb8e6;
139 | }
140 |
141 | .md-code-block.md-code-block-dark .token.attr-value,
142 | .md-code-block.md-code-block-dark .token.inserted,
143 | .md-code-block.md-code-block-dark .token.color,
144 | .md-code-block.md-code-block-dark .token.selector .token.value,
145 | .md-code-block.md-code-block-dark .token.string,
146 | .md-code-block.md-code-block-dark .token.string .token.url-link {
147 | color: #91d076;
148 | }
149 |
150 | .md-code-block.md-code-block-dark .token.builtin,
151 | .md-code-block.md-code-block-dark .token.keyword-array,
152 | .md-code-block.md-code-block-dark .token.package,
153 | .md-code-block.md-code-block-dark .token.regex {
154 | color: #f4adf4;
155 | }
156 |
157 | .md-code-block.md-code-block-dark .token.function,
158 | .md-code-block.md-code-block-dark .token.selector .token.class,
159 | .md-code-block.md-code-block-dark .token.selector .token.id {
160 | color: #c699e3;
161 | }
162 |
163 | .md-code-block.md-code-block-dark .token.atrule .token.rule,
164 | .md-code-block.md-code-block-dark .token.combinator,
165 | .md-code-block.md-code-block-dark .token.keyword,
166 | .md-code-block.md-code-block-dark .token.operator,
167 | .md-code-block.md-code-block-dark .token.pseudo-class,
168 | .md-code-block.md-code-block-dark .token.pseudo-element,
169 | .md-code-block.md-code-block-dark .token.selector,
170 | .md-code-block.md-code-block-dark .token.unit {
171 | color: #e9ae7e;
172 | }
173 |
174 | .md-code-block.md-code-block-dark .token.deleted,
175 | .md-code-block.md-code-block-dark .token.important {
176 | color: #cd6660;
177 | }
178 |
179 | .md-code-block.md-code-block-dark .token.keyword-this,
180 | .md-code-block.md-code-block-dark .token.this {
181 | color: #6cb8e6;
182 | }
183 |
184 | .md-code-block.md-code-block-dark .token.important,
185 | .md-code-block.md-code-block-dark .token.keyword-this,
186 | .md-code-block.md-code-block-dark .token.this,
187 | .md-code-block.md-code-block-dark .token.bold {
188 | font-weight: 700;
189 | }
190 |
191 | .md-code-block.md-code-block-dark .token.delimiter.important {
192 | font-weight: inherit;
193 | }
194 |
195 | .md-code-block.md-code-block-dark .token.italic {
196 | font-style: italic;
197 | }
198 |
199 | .md-code-block.md-code-block-dark .token.entity {
200 | cursor: help;
201 | }
202 |
203 | .md-code-block.md-code-block-dark .language-markdown .token.title,
204 | .md-code-block.md-code-block-dark .language-markdown .token.title .token.punctuation {
205 | color: #6cb8e6;
206 | font-weight: 700;
207 | }
208 |
209 | .md-code-block.md-code-block-dark .language-markdown .token.blockquote.punctuation {
210 | color: #f4adf4;
211 | }
212 |
213 | .md-code-block.md-code-block-dark .language-markdown .token.code {
214 | color: #6cc;
215 | }
216 |
217 | .md-code-block.md-code-block-dark .language-markdown .token.hr.punctuation {
218 | color: #6cb8e6;
219 | }
220 |
221 | .md-code-block.md-code-block-dark .language-markdown .token.url .token.content {
222 | color: #91d076;
223 | }
224 |
225 | .md-code-block.md-code-block-dark .language-markdown .token.url-link {
226 | color: #e6d37a;
227 | }
228 |
229 | .md-code-block.md-code-block-dark .language-markdown .token.list.punctuation {
230 | color: #f4adf4;
231 | }
232 |
233 | .md-code-block.md-code-block-dark .language-markdown .token.table-header,
234 | .md-code-block.md-code-block-dark .language-json .token.operator {
235 | color: #e3eaf2;
236 | }
237 |
238 | .md-code-block.md-code-block-dark .language-scss .token.variable {
239 | color: #6cc;
240 | }
241 |
242 | .md-code-block.md-code-block-dark .token.token.tab:not(:empty):before,
243 | .md-code-block.md-code-block-dark .token.token.cr:before,
244 | .md-code-block.md-code-block-dark .token.token.lf:before,
245 | .md-code-block.md-code-block-dark .token.token.space:before {
246 | color: #8da1b9;
247 | }
248 |
249 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
250 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > button {
251 | color: #111b27;
252 | background: #6cb8e6;
253 | }
254 |
255 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
256 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
257 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
258 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus {
259 | color: #111b27;
260 | background: rgba(108, 184, 230, 0.855);
261 | text-decoration: none;
262 | }
263 |
264 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > span,
265 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
266 | .md-code-block.md-code-block-dark div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
267 | color: #111b27;
268 | background: #8da1b9;
269 | }
270 |
271 | .md-code-block.md-code-block-dark .line-highlight.line-highlight {
272 | background: linear-gradient(90deg, rgba(60, 82, 109, 0.373) 70%, rgba(60, 82, 109, 0.333));
273 | }
274 |
275 | .md-code-block.md-code-block-dark .line-highlight.line-highlight:before,
276 | .md-code-block.md-code-block-dark .line-highlight.line-highlight[data-end]:after {
277 | color: #111b27;
278 | background-color: #8da1b9;
279 | box-shadow: 0 1px #3c526d;
280 | }
281 |
282 | .md-code-block.md-code-block-dark pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before {
283 | background-color: rgba(141, 161, 185, 0.094);
284 | }
285 |
286 | .md-code-block.md-code-block-dark .line-numbers.line-numbers .line-numbers-rows {
287 | background: rgba(11, 18, 27, 0.48);
288 | border-right: 1px solid #0b121b;
289 | }
290 |
291 | .md-code-block.md-code-block-dark .line-numbers .line-numbers-rows > span:before {
292 | color: rgba(141, 161, 185, 0.855);
293 | }
294 |
295 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-1,
296 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-5,
297 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-9 {
298 | color: #e6d37a;
299 | }
300 |
301 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-2,
302 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-6,
303 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-10 {
304 | color: #f4adf4;
305 | }
306 |
307 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-3,
308 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-7,
309 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-11 {
310 | color: #6cb8e6;
311 | }
312 |
313 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-4,
314 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-8,
315 | .md-code-block.md-code-block-dark .rainbow-braces .token.token.punctuation.brace-level-12 {
316 | color: #c699e3;
317 | }
318 |
319 | .md-code-block.md-code-block-dark pre.diff-highlight > code .token.token.deleted:not(.prefix),
320 | .md-code-block.md-code-block-dark pre > code.diff-highlight .token.token.deleted:not(.prefix) {
321 | background-color: rgba(205, 102, 96, 0.12);
322 | }
323 |
324 | .md-code-block.md-code-block-dark pre.diff-highlight > code .token.token.inserted:not(.prefix),
325 | .md-code-block.md-code-block-dark pre > code.diff-highlight .token.token.inserted:not(.prefix) {
326 | background-color: rgba(145, 208, 118, 0.12);
327 | }
328 |
329 | .md-code-block.md-code-block-dark .command-line .command-line-prompt {
330 | border-right: 1px solid #0b121b;
331 | }
332 |
333 | .md-code-block.md-code-block-dark .command-line .command-line-prompt > span:before {
334 | color: rgba(141, 161, 185, 0.855);
335 | }
336 |
337 | .md-code-block.md-code-block-light code[class*='language-'],
338 | .md-code-block.md-code-block-light pre[class*='language-'] {
339 | color: #383a42;
340 | text-align: left;
341 | white-space: pre;
342 | word-spacing: normal;
343 | word-break: normal;
344 | -moz-tab-size: 2;
345 | tab-size: 2;
346 | -webkit-hyphens: none;
347 | -ms-hyphens: none;
348 | hyphens: none;
349 | direction: ltr;
350 | background: #fafafa;
351 | font-family:
352 | Fira Code,
353 | Fira Mono,
354 | Menlo,
355 | Consolas,
356 | DejaVu Sans Mono,
357 | monospace;
358 | line-height: 1.5;
359 | }
360 |
361 | .md-code-block.md-code-block-light code[class*='language-']::-moz-selection {
362 | color: inherit;
363 | background: #e5e5e6;
364 | }
365 |
366 | .md-code-block.md-code-block-light code[class*='language-'] ::-moz-selection {
367 | color: inherit;
368 | background: #e5e5e6;
369 | }
370 |
371 | .md-code-block.md-code-block-light pre[class*='language-'] ::-moz-selection {
372 | color: inherit;
373 | background: #e5e5e6;
374 | }
375 |
376 | .md-code-block.md-code-block-light code[class*='language-']::-moz-selection {
377 | color: inherit;
378 | background: #e5e5e6;
379 | }
380 |
381 | .md-code-block.md-code-block-light code[class*='language-'] ::-moz-selection {
382 | color: inherit;
383 | background: #e5e5e6;
384 | }
385 |
386 | .md-code-block.md-code-block-light pre[class*='language-'] ::-moz-selection {
387 | color: inherit;
388 | background: #e5e5e6;
389 | }
390 |
391 | .md-code-block.md-code-block-light code[class*='language-']::-moz-selection {
392 | color: inherit;
393 | background: #e5e5e6;
394 | }
395 |
396 | .md-code-block.md-code-block-light code[class*='language-']::selection {
397 | color: inherit;
398 | background: #e5e5e6;
399 | }
400 |
401 | .md-code-block.md-code-block-light code[class*='language-'] ::-moz-selection {
402 | color: inherit;
403 | background: #e5e5e6;
404 | }
405 |
406 | .md-code-block.md-code-block-light code[class*='language-'] ::selection {
407 | color: inherit;
408 | background: #e5e5e6;
409 | }
410 |
411 | .md-code-block.md-code-block-light pre[class*='language-'] ::-moz-selection {
412 | color: inherit;
413 | background: #e5e5e6;
414 | }
415 |
416 | .md-code-block.md-code-block-light pre[class*='language-'] ::selection {
417 | color: inherit;
418 | background: #e5e5e6;
419 | }
420 |
421 | .md-code-block.md-code-block-light pre[class*='language-'] {
422 | border-radius: 0.3em;
423 | margin: 0.5em 0;
424 | padding: 1em;
425 | overflow: auto;
426 | }
427 |
428 | .md-code-block.md-code-block-light :not(pre) > code[class*='language-'] {
429 | white-space: normal;
430 | border-radius: 0.3em;
431 | padding: 0.2em 0.3em;
432 | }
433 |
434 | .md-code-block.md-code-block-light .token.comment,
435 | .md-code-block.md-code-block-light .token.prolog,
436 | .md-code-block.md-code-block-light .token.cdata {
437 | color: #a0a1a7;
438 | }
439 |
440 | .md-code-block.md-code-block-light .token.doctype,
441 | .md-code-block.md-code-block-light .token.punctuation,
442 | .md-code-block.md-code-block-light .token.entity {
443 | color: #383a42;
444 | }
445 |
446 | .md-code-block.md-code-block-light .token.attr-name,
447 | .md-code-block.md-code-block-light .token.class-name,
448 | .md-code-block.md-code-block-light .token.boolean,
449 | .md-code-block.md-code-block-light .token.constant,
450 | .md-code-block.md-code-block-light .token.number,
451 | .md-code-block.md-code-block-light .token.atrule {
452 | color: #b76b01;
453 | }
454 |
455 | .md-code-block.md-code-block-light .token.keyword {
456 | color: #a626a4;
457 | }
458 |
459 | .md-code-block.md-code-block-light .token.property,
460 | .md-code-block.md-code-block-light .token.tag,
461 | .md-code-block.md-code-block-light .token.symbol,
462 | .md-code-block.md-code-block-light .token.deleted,
463 | .md-code-block.md-code-block-light .token.important {
464 | color: #e45649;
465 | }
466 |
467 | .md-code-block.md-code-block-light .token.selector,
468 | .md-code-block.md-code-block-light .token.string,
469 | .md-code-block.md-code-block-light .token.char,
470 | .md-code-block.md-code-block-light .token.builtin,
471 | .md-code-block.md-code-block-light .token.inserted,
472 | .md-code-block.md-code-block-light .token.regex,
473 | .md-code-block.md-code-block-light .token.attr-value,
474 | .md-code-block.md-code-block-light .token.attr-value > .token.punctuation {
475 | color: #50a14f;
476 | }
477 |
478 | .md-code-block.md-code-block-light .token.variable,
479 | .md-code-block.md-code-block-light .token.operator,
480 | .md-code-block.md-code-block-light .token.function {
481 | color: #4078f2;
482 | }
483 |
484 | .md-code-block.md-code-block-light .token.url {
485 | color: #0184bc;
486 | }
487 |
488 | .md-code-block.md-code-block-light .token.attr-value > .token.punctuation.attr-equals,
489 | .md-code-block.md-code-block-light .token.special-attr > .token.attr-value > .token.value.css {
490 | color: #383a42;
491 | }
492 |
493 | .md-code-block.md-code-block-light .language-css .token.selector {
494 | color: #e45649;
495 | }
496 |
497 | .md-code-block.md-code-block-light .language-css .token.property {
498 | color: #383a42;
499 | }
500 |
501 | .md-code-block.md-code-block-light .language-css .token.function,
502 | .md-code-block.md-code-block-light .language-css .token.url > .token.function {
503 | color: #0184bc;
504 | }
505 |
506 | .md-code-block.md-code-block-light .language-css .token.url > .token.string.url {
507 | color: #50a14f;
508 | }
509 |
510 | .md-code-block.md-code-block-light .language-css .token.important,
511 | .md-code-block.md-code-block-light .language-css .token.atrule .token.rule,
512 | .md-code-block.md-code-block-light .language-javascript .token.operator {
513 | color: #a626a4;
514 | }
515 |
516 | .md-code-block.md-code-block-light .language-javascript .token.template-string > .token.interpolation > .token.interpolation-punctuation.punctuation {
517 | color: #ca1243;
518 | }
519 |
520 | .md-code-block.md-code-block-light .language-json .token.operator {
521 | color: #383a42;
522 | }
523 |
524 | .md-code-block.md-code-block-light .language-json .token.null.keyword {
525 | color: #b76b01;
526 | }
527 |
528 | .md-code-block.md-code-block-light .language-markdown .token.url,
529 | .md-code-block.md-code-block-light .language-markdown .token.url > .token.operator,
530 | .md-code-block.md-code-block-light .language-markdown .token.url-reference.url > .token.string {
531 | color: #383a42;
532 | }
533 |
534 | .md-code-block.md-code-block-light .language-markdown .token.url > .token.content {
535 | color: #4078f2;
536 | }
537 |
538 | .md-code-block.md-code-block-light .language-markdown .token.url > .token.url,
539 | .md-code-block.md-code-block-light .language-markdown .token.url-reference.url {
540 | color: #0184bc;
541 | }
542 |
543 | .md-code-block.md-code-block-light .language-markdown .token.blockquote.punctuation,
544 | .md-code-block.md-code-block-light .language-markdown .token.hr.punctuation {
545 | color: #a0a1a7;
546 | font-style: italic;
547 | }
548 |
549 | .md-code-block.md-code-block-light .language-markdown .token.code-snippet {
550 | color: #50a14f;
551 | }
552 |
553 | .md-code-block.md-code-block-light .language-markdown .token.bold .token.content {
554 | color: #b76b01;
555 | }
556 |
557 | .md-code-block.md-code-block-light .language-markdown .token.italic .token.content {
558 | color: #a626a4;
559 | }
560 |
561 | .md-code-block.md-code-block-light .language-markdown .token.strike .token.content,
562 | .md-code-block.md-code-block-light .language-markdown .token.strike .token.punctuation,
563 | .md-code-block.md-code-block-light .language-markdown .token.list.punctuation,
564 | .md-code-block.md-code-block-light .language-markdown .token.title.important > .token.punctuation {
565 | color: #e45649;
566 | }
567 |
568 | .md-code-block.md-code-block-light .token.bold {
569 | font-weight: 700;
570 | }
571 |
572 | .md-code-block.md-code-block-light .token.comment,
573 | .md-code-block.md-code-block-light .token.italic {
574 | font-style: italic;
575 | }
576 |
577 | .md-code-block.md-code-block-light .token.entity {
578 | cursor: help;
579 | }
580 |
581 | .md-code-block.md-code-block-light .token.namespace {
582 | opacity: 0.8;
583 | }
584 |
585 | .md-code-block.md-code-block-light .token.token.tab:not(:empty):before,
586 | .md-code-block.md-code-block-light .token.token.cr:before,
587 | .md-code-block.md-code-block-light .token.token.lf:before,
588 | .md-code-block.md-code-block-light .token.token.space:before {
589 | color: rgba(56, 58, 66, 0.2);
590 | }
591 |
592 | .md-code-block-light {
593 | --ds-md-code-block-border-color: #e5e5e5;
594 | --ds-md-code-block-banner-wrap-background-color: white;
595 | --ds-md-code-banner-background-color: #f5f5f5;
596 | color: #494949;
597 | background: #fafafa;
598 | }
599 |
600 | .md-code-block-dark {
601 | --ds-md-code-block-border-color: var(--dsr-bg, transparent);
602 | --ds-md-code-block-banner-wrap-background-color: #292a2d;
603 | --ds-md-code-banner-background-color: #404045;
604 | color: #fff;
605 | background: #212327;
606 | }
607 |
608 | .md-code-block {
609 | --ds-md-code-block-border-radius: 12px;
610 | --ds-md-code-block-font-size: calc(var(--ds-md-zoom) * var(--ds-font-size-xsp));
611 | border-radius: var(--ds-md-code-block-border-radius);
612 | font-size: var(--ds-md-code-block-font-size);
613 | line-height: calc(var(--ds-md-code-block-font-size) * 1.6);
614 | }
615 |
616 | .md-code-block:not(:last-child) {
617 | margin-bottom: calc(var(--ds-md-zoom) * 10px);
618 | }
619 |
620 | .md-code-block-banner-wrap {
621 | background-color: var(--ds-md-code-block-banner-wrap-background-color);
622 | z-index: 6;
623 | position: -webkit-sticky;
624 | position: sticky;
625 | top: 0;
626 | }
627 |
628 | .md-code-block-banner {
629 | background: var(--ds-md-code-banner-background-color);
630 | padding: calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);
631 | font-size: var(--ds-md-code-block-font-size);
632 | line-height: var(--ds-md-code-block-font-size);
633 | justify-content: space-between;
634 | display: flex;
635 | }
636 |
637 | .md-code-block-banner-lite {
638 | padding: 0;
639 | }
640 |
641 | .md-code-block-banner {
642 | border-top-left-radius: var(--ds-md-code-block-border-radius);
643 | border-top-right-radius: var(--ds-md-code-block-border-radius);
644 | }
645 |
646 | .md-code-block-footer {
647 | background: var(--ds-md-code-banner-background-color);
648 | padding: calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);
649 | font-size: var(--ds-md-code-block-font-size);
650 | line-height: var(--ds-md-code-block-font-size);
651 | border-bottom-left-radius: var(--ds-md-code-block-border-radius);
652 | border-bottom-right-radius: var(--ds-md-code-block-border-radius);
653 | justify-content: flex-end;
654 | display: flex;
655 | }
656 |
657 | .md-code-block-action {
658 | align-items: center;
659 | display: flex;
660 | }
661 |
662 | .md-code-block > pre {
663 | padding: calc(var(--ds-md-zoom) * 8px) calc(var(--ds-md-zoom) * 12px);
664 | white-space: pre-wrap;
665 | word-break: break-all;
666 | margin: 0 !important;
667 | }
668 |
--------------------------------------------------------------------------------
/src/components/HighReactMarkdown/index.less:
--------------------------------------------------------------------------------
1 | .ds-markdown {
2 | .markdown-table-wrapper {
3 | overflow-x: auto;
4 | }
5 | .markdown-table-wrapper table {
6 | width: max-content;
7 | max-width: max-content;
8 | min-width: 100%;
9 | }
10 |
11 | .markdown-table-wrapper th,
12 | .markdown-table-wrapper td {
13 | padding: 10px;
14 | font-size: 15px;
15 | line-height: 1.72;
16 | }
17 |
18 | .markdown-table-wrapper th {
19 | border-color: var(--dsr-border-1);
20 | border-top: none;
21 | }
22 |
23 | .markdown-table-wrapper td {
24 | border-color: var(--dsr-border-2);
25 | max-width: 320px;
26 | min-width: 100px;
27 | max-width: max(30vw, 320px);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/HighReactMarkdown/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import Markdown from 'react-markdown';
3 | import type { Options } from 'react-markdown';
4 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
5 | import gfmPlugin from 'remark-gfm';
6 |
7 | import BlockWrap from '../BlockWrap/index.js';
8 |
9 | interface HighReactMarkdownProps extends Options {
10 | id?: number;
11 | }
12 |
13 | const modulePrefix = 'HighReactMarkdown';
14 | const HighReactMarkdown: React.FC = (props) => {
15 | return (
16 | {
20 | const match = /language-(\w+)/.exec(className || '');
21 | return match ? (
22 |
23 |
24 | {String(children).replace(/\n$/, '')}
25 |
26 |
27 | ) : (
28 |
29 | {children}
30 |
31 | );
32 | },
33 | table: ({ children, ...props }) => {
34 | return (
35 |
38 | );
39 | },
40 | }}
41 | {...props}
42 | />
43 | );
44 | };
45 | export default memo(HighReactMarkdown);
46 |
--------------------------------------------------------------------------------
/src/constant.ts:
--------------------------------------------------------------------------------
1 | export const __DEV__ = process.env.NODE_ENV === 'development';
2 |
--------------------------------------------------------------------------------
/src/defined.less:
--------------------------------------------------------------------------------
1 | :root,
2 | page {
3 | --ds-rgb-black: 0 0 0;
4 | --ds-rgb-white: 255 255 255;
5 | --ds-rgb-slate-50: 248 250 252;
6 | --ds-rgb-slate-100: 241 245 249;
7 | --ds-rgb-slate-150: 233 238 244;
8 | --ds-rgb-slate-200: 226 232 240;
9 | --ds-rgb-slate-250: 214 222 232;
10 | --ds-rgb-slate-300: 203 213 225;
11 | --ds-rgb-slate-350: 175 188 204;
12 | --ds-rgb-slate-400: 148 163 184;
13 | --ds-rgb-slate-450: 124 139 161;
14 | --ds-rgb-slate-500: 100 116 139;
15 | --ds-rgb-slate-550: 85 100 122;
16 | --ds-rgb-slate-600: 71 85 105;
17 | --ds-rgb-slate-650: 61 75 95;
18 | --ds-rgb-slate-700: 51 65 85;
19 | --ds-rgb-slate-750: 40 53 72;
20 | --ds-rgb-slate-800: 30 41 59;
21 | --ds-rgb-slate-850: 22 32 50;
22 | --ds-rgb-slate-900: 15 23 42;
23 | --ds-rgb-slate-950: 2 6 23;
24 | --ds-rgb-gray-50: 249 250 251;
25 | --ds-rgb-gray-100: 243 244 246;
26 | --ds-rgb-gray-150: 236 237 240;
27 | --ds-rgb-gray-200: 229 231 235;
28 | --ds-rgb-gray-250: 219 222 227;
29 | --ds-rgb-gray-300: 209 213 219;
30 | --ds-rgb-gray-350: 182 188 197;
31 | --ds-rgb-gray-400: 156 163 175;
32 | --ds-rgb-gray-450: 131 138 151;
33 | --ds-rgb-gray-500: 107 114 128;
34 | --ds-rgb-gray-550: 91 99 113;
35 | --ds-rgb-gray-600: 75 85 99;
36 | --ds-rgb-gray-650: 65 75 90;
37 | --ds-rgb-gray-700: 55 65 81;
38 | --ds-rgb-gray-750: 43 53 68;
39 | --ds-rgb-gray-800: 31 41 55;
40 | --ds-rgb-gray-850: 24 32 47;
41 | --ds-rgb-gray-900: 17 24 39;
42 | --ds-rgb-gray-950: 3 7 18;
43 | --ds-rgb-zinc-50: 250 250 250;
44 | --ds-rgb-zinc-100: 244 244 245;
45 | --ds-rgb-zinc-150: 236 236 238;
46 | --ds-rgb-zinc-200: 228 228 231;
47 | --ds-rgb-zinc-250: 220 220 223;
48 | --ds-rgb-zinc-300: 212 212 216;
49 | --ds-rgb-zinc-350: 186 186 193;
50 | --ds-rgb-zinc-400: 161 161 170;
51 | --ds-rgb-zinc-450: 137 137 146;
52 | --ds-rgb-zinc-500: 113 113 122;
53 | --ds-rgb-zinc-550: 97 97 106;
54 | --ds-rgb-zinc-600: 82 82 91;
55 | --ds-rgb-zinc-650: 72 72 80;
56 | --ds-rgb-zinc-700: 63 63 70;
57 | --ds-rgb-zinc-750: 51 51 56;
58 | --ds-rgb-zinc-800: 39 39 42;
59 | --ds-rgb-zinc-850: 31 31 34;
60 | --ds-rgb-zinc-900: 24 24 27;
61 | --ds-rgb-zinc-950: 9 9 11;
62 | --ds-rgb-neutral-50: 250 250 250;
63 | --ds-rgb-neutral-100: 245 245 245;
64 | --ds-rgb-neutral-150: 237 237 237;
65 | --ds-rgb-neutral-200: 229 229 229;
66 | --ds-rgb-neutral-250: 220 220 220;
67 | --ds-rgb-neutral-300: 212 212 212;
68 | --ds-rgb-neutral-350: 187 187 187;
69 | --ds-rgb-neutral-400: 163 163 163;
70 | --ds-rgb-neutral-450: 139 139 139;
71 | --ds-rgb-neutral-500: 115 115 115;
72 | --ds-rgb-neutral-550: 98 98 98;
73 | --ds-rgb-neutral-600: 82 82 82;
74 | --ds-rgb-neutral-650: 73 73 73;
75 | --ds-rgb-neutral-700: 64 64 64;
76 | --ds-rgb-neutral-750: 51 51 51;
77 | --ds-rgb-neutral-800: 38 38 38;
78 | --ds-rgb-neutral-850: 30 30 30;
79 | --ds-rgb-neutral-900: 23 23 23;
80 | --ds-rgb-neutral-950: 10 10 10;
81 | --ds-rgb-stone-50: 250 250 249;
82 | --ds-rgb-stone-100: 245 245 244;
83 | --ds-rgb-stone-150: 238 237 236;
84 | --ds-rgb-stone-200: 231 229 228;
85 | --ds-rgb-stone-250: 222 220 218;
86 | --ds-rgb-stone-300: 214 211 209;
87 | --ds-rgb-stone-350: 191 186 183;
88 | --ds-rgb-stone-400: 168 162 158;
89 | --ds-rgb-stone-450: 144 137 133;
90 | --ds-rgb-stone-500: 120 113 108;
91 | --ds-rgb-stone-550: 103 98 93;
92 | --ds-rgb-stone-600: 87 83 78;
93 | --ds-rgb-stone-650: 77 73 69;
94 | --ds-rgb-stone-700: 68 64 60;
95 | --ds-rgb-stone-750: 54 50 48;
96 | --ds-rgb-stone-800: 41 37 36;
97 | --ds-rgb-stone-850: 34 31 29;
98 | --ds-rgb-stone-900: 28 25 23;
99 | --ds-rgb-stone-950: 12 10 9;
100 | --ds-rgb-red-50: 254 242 242;
101 | --ds-rgb-red-100: 254 226 226;
102 | --ds-rgb-red-150: 254 214 214;
103 | --ds-rgb-red-200: 254 202 202;
104 | --ds-rgb-red-250: 253 183 183;
105 | --ds-rgb-red-300: 252 165 165;
106 | --ds-rgb-red-350: 250 139 139;
107 | --ds-rgb-red-400: 248 113 113;
108 | --ds-rgb-red-450: 243 90 90;
109 | --ds-rgb-red-500: 239 68 68;
110 | --ds-rgb-red-550: 229 53 53;
111 | --ds-rgb-red-600: 220 38 38;
112 | --ds-rgb-red-650: 202 33 33;
113 | --ds-rgb-red-700: 185 28 28;
114 | --ds-rgb-red-750: 169 27 27;
115 | --ds-rgb-red-800: 153 27 27;
116 | --ds-rgb-red-850: 140 28 28;
117 | --ds-rgb-red-900: 127 29 29;
118 | --ds-rgb-red-950: 69 10 10;
119 | --ds-rgb-orange-50: 255 247 237;
120 | --ds-rgb-orange-100: 255 237 213;
121 | --ds-rgb-orange-150: 254 226 191;
122 | --ds-rgb-orange-200: 254 215 170;
123 | --ds-rgb-orange-250: 253 200 143;
124 | --ds-rgb-orange-300: 253 186 116;
125 | --ds-rgb-orange-350: 252 166 88;
126 | --ds-rgb-orange-400: 251 146 60;
127 | --ds-rgb-orange-450: 250 130 41;
128 | --ds-rgb-orange-500: 249 115 22;
129 | --ds-rgb-orange-550: 241 101 17;
130 | --ds-rgb-orange-600: 234 88 12;
131 | --ds-rgb-orange-650: 214 76 12;
132 | --ds-rgb-orange-700: 194 65 12;
133 | --ds-rgb-orange-750: 174 58 15;
134 | --ds-rgb-orange-800: 154 52 18;
135 | --ds-rgb-orange-850: 139 48 18;
136 | --ds-rgb-orange-900: 124 45 18;
137 | --ds-rgb-orange-950: 67 20 7;
138 | --ds-rgb-amber-50: 255 251 235;
139 | --ds-rgb-amber-100: 254 243 199;
140 | --ds-rgb-amber-150: 253 236 168;
141 | --ds-rgb-amber-200: 253 230 138;
142 | --ds-rgb-amber-250: 252 220 107;
143 | --ds-rgb-amber-300: 252 211 77;
144 | --ds-rgb-amber-350: 251 201 56;
145 | --ds-rgb-amber-400: 251 191 36;
146 | --ds-rgb-amber-450: 248 174 23;
147 | --ds-rgb-amber-500: 245 158 11;
148 | --ds-rgb-amber-550: 231 138 8;
149 | --ds-rgb-amber-600: 217 119 6;
150 | --ds-rgb-amber-650: 198 101 7;
151 | --ds-rgb-amber-700: 180 83 9;
152 | --ds-rgb-amber-750: 163 73 11;
153 | --ds-rgb-amber-800: 146 64 14;
154 | --ds-rgb-amber-850: 133 58 14;
155 | --ds-rgb-amber-900: 120 53 15;
156 | --ds-rgb-amber-950: 69 26 3;
157 | --ds-rgb-yellow-50: 254 252 232;
158 | --ds-rgb-yellow-100: 254 249 195;
159 | --ds-rgb-yellow-150: 254 244 166;
160 | --ds-rgb-yellow-200: 254 240 138;
161 | --ds-rgb-yellow-250: 253 232 104;
162 | --ds-rgb-yellow-300: 253 224 71;
163 | --ds-rgb-yellow-350: 251 214 46;
164 | --ds-rgb-yellow-400: 250 204 21;
165 | --ds-rgb-yellow-450: 242 191 14;
166 | --ds-rgb-yellow-500: 234 179 8;
167 | --ds-rgb-yellow-550: 218 158 6;
168 | --ds-rgb-yellow-600: 202 138 4;
169 | --ds-rgb-yellow-650: 181 118 5;
170 | --ds-rgb-yellow-700: 161 98 7;
171 | --ds-rgb-yellow-750: 147 87 10;
172 | --ds-rgb-yellow-800: 133 77 14;
173 | --ds-rgb-yellow-850: 123 70 16;
174 | --ds-rgb-yellow-900: 113 63 18;
175 | --ds-rgb-yellow-950: 66 32 6;
176 | --ds-rgb-lime-50: 247 254 231;
177 | --ds-rgb-lime-100: 236 252 203;
178 | --ds-rgb-lime-150: 226 250 180;
179 | --ds-rgb-lime-200: 217 249 157;
180 | --ds-rgb-lime-250: 203 245 128;
181 | --ds-rgb-lime-300: 190 242 100;
182 | --ds-rgb-lime-350: 176 236 76;
183 | --ds-rgb-lime-400: 163 230 53;
184 | --ds-rgb-lime-450: 147 217 37;
185 | --ds-rgb-lime-500: 132 204 22;
186 | --ds-rgb-lime-550: 116 183 17;
187 | --ds-rgb-lime-600: 101 163 13;
188 | --ds-rgb-lime-650: 89 143 14;
189 | --ds-rgb-lime-700: 77 124 15;
190 | --ds-rgb-lime-750: 70 111 16;
191 | --ds-rgb-lime-800: 63 98 18;
192 | --ds-rgb-lime-850: 58 90 19;
193 | --ds-rgb-lime-900: 54 83 20;
194 | --ds-rgb-lime-950: 26 46 5;
195 | --ds-rgb-green-50: 240 253 244;
196 | --ds-rgb-green-100: 220 252 231;
197 | --ds-rgb-green-150: 203 249 219;
198 | --ds-rgb-green-200: 187 247 208;
199 | --ds-rgb-green-250: 160 243 190;
200 | --ds-rgb-green-300: 134 239 172;
201 | --ds-rgb-green-350: 104 230 150;
202 | --ds-rgb-green-400: 74 222 128;
203 | --ds-rgb-green-450: 54 209 111;
204 | --ds-rgb-green-500: 34 197 94;
205 | --ds-rgb-green-550: 28 180 84;
206 | --ds-rgb-green-600: 22 163 74;
207 | --ds-rgb-green-650: 21 145 67;
208 | --ds-rgb-green-700: 21 128 61;
209 | --ds-rgb-green-750: 21 114 56;
210 | --ds-rgb-green-800: 22 101 52;
211 | --ds-rgb-green-850: 21 92 48;
212 | --ds-rgb-green-900: 20 83 45;
213 | --ds-rgb-green-950: 5 46 22;
214 | --ds-rgb-emerald-50: 236 253 245;
215 | --ds-rgb-emerald-100: 209 250 229;
216 | --ds-rgb-emerald-150: 188 246 218;
217 | --ds-rgb-emerald-200: 167 243 208;
218 | --ds-rgb-emerald-250: 138 237 195;
219 | --ds-rgb-emerald-300: 110 231 183;
220 | --ds-rgb-emerald-350: 81 221 168;
221 | --ds-rgb-emerald-400: 52 211 153;
222 | --ds-rgb-emerald-450: 34 198 141;
223 | --ds-rgb-emerald-500: 16 185 129;
224 | --ds-rgb-emerald-550: 10 167 117;
225 | --ds-rgb-emerald-600: 5 150 105;
226 | --ds-rgb-emerald-650: 4 135 96;
227 | --ds-rgb-emerald-700: 4 120 87;
228 | --ds-rgb-emerald-750: 5 107 78;
229 | --ds-rgb-emerald-800: 6 95 70;
230 | --ds-rgb-emerald-850: 6 86 64;
231 | --ds-rgb-emerald-900: 6 78 59;
232 | --ds-rgb-emerald-950: 2 44 34;
233 | --ds-rgb-teal-50: 240 253 250;
234 | --ds-rgb-teal-100: 204 251 241;
235 | --ds-rgb-teal-150: 178 248 234;
236 | --ds-rgb-teal-200: 153 246 228;
237 | --ds-rgb-teal-250: 123 240 220;
238 | --ds-rgb-teal-300: 94 234 212;
239 | --ds-rgb-teal-350: 69 223 201;
240 | --ds-rgb-teal-400: 45 212 191;
241 | --ds-rgb-teal-450: 32 198 178;
242 | --ds-rgb-teal-500: 20 184 166;
243 | --ds-rgb-teal-550: 16 166 151;
244 | --ds-rgb-teal-600: 13 148 136;
245 | --ds-rgb-teal-650: 14 133 123;
246 | --ds-rgb-teal-700: 15 118 110;
247 | --ds-rgb-teal-750: 16 106 99;
248 | --ds-rgb-teal-800: 17 94 89;
249 | --ds-rgb-teal-850: 18 86 81;
250 | --ds-rgb-teal-900: 19 78 74;
251 | --ds-rgb-teal-950: 4 47 46;
252 | --ds-rgb-cyan-50: 236 254 255;
253 | --ds-rgb-cyan-100: 207 250 254;
254 | --ds-rgb-cyan-150: 186 246 253;
255 | --ds-rgb-cyan-200: 165 243 252;
256 | --ds-rgb-cyan-250: 134 237 250;
257 | --ds-rgb-cyan-300: 103 232 249;
258 | --ds-rgb-cyan-350: 68 221 243;
259 | --ds-rgb-cyan-400: 34 211 238;
260 | --ds-rgb-cyan-450: 20 196 225;
261 | --ds-rgb-cyan-500: 6 182 212;
262 | --ds-rgb-cyan-550: 7 163 195;
263 | --ds-rgb-cyan-600: 8 145 178;
264 | --ds-rgb-cyan-650: 11 130 161;
265 | --ds-rgb-cyan-700: 14 116 144;
266 | --ds-rgb-cyan-750: 17 105 130;
267 | --ds-rgb-cyan-800: 21 94 117;
268 | --ds-rgb-cyan-850: 21 86 108;
269 | --ds-rgb-cyan-900: 22 78 99;
270 | --ds-rgb-cyan-950: 8 51 68;
271 | --ds-rgb-sky-50: 240 249 255;
272 | --ds-rgb-sky-100: 224 242 254;
273 | --ds-rgb-sky-150: 205 236 253;
274 | --ds-rgb-sky-200: 186 230 253;
275 | --ds-rgb-sky-250: 155 220 252;
276 | --ds-rgb-sky-300: 125 211 252;
277 | --ds-rgb-sky-350: 90 200 250;
278 | --ds-rgb-sky-400: 56 189 248;
279 | --ds-rgb-sky-450: 35 177 240;
280 | --ds-rgb-sky-500: 14 165 233;
281 | --ds-rgb-sky-550: 8 148 216;
282 | --ds-rgb-sky-600: 2 132 199;
283 | --ds-rgb-sky-650: 2 118 180;
284 | --ds-rgb-sky-700: 3 105 161;
285 | --ds-rgb-sky-750: 5 97 147;
286 | --ds-rgb-sky-800: 7 89 133;
287 | --ds-rgb-sky-850: 9 81 121;
288 | --ds-rgb-sky-900: 12 74 110;
289 | --ds-rgb-sky-950: 8 47 73;
290 | --ds-rgb-blue-50: 239 246 255;
291 | --ds-rgb-blue-100: 219 234 254;
292 | --ds-rgb-blue-150: 205 226 254;
293 | --ds-rgb-blue-200: 191 219 254;
294 | --ds-rgb-blue-250: 169 208 253;
295 | --ds-rgb-blue-300: 147 197 253;
296 | --ds-rgb-blue-350: 121 181 251;
297 | --ds-rgb-blue-400: 96 165 250;
298 | --ds-rgb-blue-450: 77 147 248;
299 | --ds-rgb-blue-500: 59 130 246;
300 | --ds-rgb-blue-550: 48 114 240;
301 | --ds-rgb-blue-600: 37 99 235;
302 | --ds-rgb-blue-650: 33 88 225;
303 | --ds-rgb-blue-700: 29 78 216;
304 | --ds-rgb-blue-750: 29 71 195;
305 | --ds-rgb-blue-800: 30 64 175;
306 | --ds-rgb-blue-850: 30 61 156;
307 | --ds-rgb-blue-900: 30 58 138;
308 | --ds-rgb-blue-950: 23 37 84;
309 | --ds-rgb-indigo-50: 238 242 255;
310 | --ds-rgb-indigo-100: 224 231 255;
311 | --ds-rgb-indigo-150: 211 220 254;
312 | --ds-rgb-indigo-200: 199 210 254;
313 | --ds-rgb-indigo-250: 182 195 253;
314 | --ds-rgb-indigo-300: 165 180 252;
315 | --ds-rgb-indigo-350: 147 160 250;
316 | --ds-rgb-indigo-400: 129 140 248;
317 | --ds-rgb-indigo-450: 114 121 244;
318 | --ds-rgb-indigo-500: 99 102 241;
319 | --ds-rgb-indigo-550: 89 86 235;
320 | --ds-rgb-indigo-600: 79 70 229;
321 | --ds-rgb-indigo-650: 73 63 215;
322 | --ds-rgb-indigo-700: 67 56 202;
323 | --ds-rgb-indigo-750: 61 52 182;
324 | --ds-rgb-indigo-800: 55 48 163;
325 | --ds-rgb-indigo-850: 52 47 146;
326 | --ds-rgb-indigo-900: 49 46 129;
327 | --ds-rgb-indigo-950: 30 27 75;
328 | --ds-rgb-violet-50: 245 243 255;
329 | --ds-rgb-violet-100: 237 233 254;
330 | --ds-rgb-violet-150: 229 223 254;
331 | --ds-rgb-violet-200: 221 214 254;
332 | --ds-rgb-violet-250: 208 197 253;
333 | --ds-rgb-violet-300: 196 181 253;
334 | --ds-rgb-violet-350: 181 160 251;
335 | --ds-rgb-violet-400: 167 139 250;
336 | --ds-rgb-violet-450: 153 115 248;
337 | --ds-rgb-violet-500: 139 92 246;
338 | --ds-rgb-violet-550: 131 75 241;
339 | --ds-rgb-violet-600: 124 58 237;
340 | --ds-rgb-violet-650: 116 49 227;
341 | --ds-rgb-violet-700: 109 40 217;
342 | --ds-rgb-violet-750: 100 36 199;
343 | --ds-rgb-violet-800: 91 33 182;
344 | --ds-rgb-violet-850: 83 31 165;
345 | --ds-rgb-violet-900: 76 29 149;
346 | --ds-rgb-violet-950: 46 16 101;
347 | --ds-rgb-purple-50: 250 245 255;
348 | --ds-rgb-purple-100: 243 232 255;
349 | --ds-rgb-purple-150: 238 222 255;
350 | --ds-rgb-purple-200: 233 213 255;
351 | --ds-rgb-purple-250: 224 196 254;
352 | --ds-rgb-purple-300: 216 180 254;
353 | --ds-rgb-purple-350: 204 156 253;
354 | --ds-rgb-purple-400: 192 132 252;
355 | --ds-rgb-purple-450: 180 108 249;
356 | --ds-rgb-purple-500: 168 85 247;
357 | --ds-rgb-purple-550: 157 68 240;
358 | --ds-rgb-purple-600: 147 51 234;
359 | --ds-rgb-purple-650: 136 42 220;
360 | --ds-rgb-purple-700: 126 34 206;
361 | --ds-rgb-purple-750: 116 33 187;
362 | --ds-rgb-purple-800: 107 33 168;
363 | --ds-rgb-purple-850: 97 30 151;
364 | --ds-rgb-purple-900: 88 28 135;
365 | --ds-rgb-purple-950: 59 7 100;
366 | --ds-rgb-fuchsia-50: 253 244 255;
367 | --ds-rgb-fuchsia-100: 250 232 255;
368 | --ds-rgb-fuchsia-150: 247 220 254;
369 | --ds-rgb-fuchsia-200: 245 208 254;
370 | --ds-rgb-fuchsia-250: 242 189 253;
371 | --ds-rgb-fuchsia-300: 240 171 252;
372 | --ds-rgb-fuchsia-350: 236 146 250;
373 | --ds-rgb-fuchsia-400: 232 121 249;
374 | --ds-rgb-fuchsia-450: 224 95 244;
375 | --ds-rgb-fuchsia-500: 217 70 239;
376 | --ds-rgb-fuchsia-550: 204 54 225;
377 | --ds-rgb-fuchsia-600: 192 38 211;
378 | --ds-rgb-fuchsia-650: 177 33 193;
379 | --ds-rgb-fuchsia-700: 162 28 175;
380 | --ds-rgb-fuchsia-750: 148 26 159;
381 | --ds-rgb-fuchsia-800: 134 25 143;
382 | --ds-rgb-fuchsia-850: 123 25 130;
383 | --ds-rgb-fuchsia-900: 112 26 117;
384 | --ds-rgb-fuchsia-950: 74 4 78;
385 | --ds-rgb-pink-50: 253 242 248;
386 | --ds-rgb-pink-100: 252 231 243;
387 | --ds-rgb-pink-150: 251 219 237;
388 | --ds-rgb-pink-200: 251 207 232;
389 | --ds-rgb-pink-250: 250 187 222;
390 | --ds-rgb-pink-300: 249 168 212;
391 | --ds-rgb-pink-350: 246 141 197;
392 | --ds-rgb-pink-400: 244 114 182;
393 | --ds-rgb-pink-450: 240 93 167;
394 | --ds-rgb-pink-500: 236 72 153;
395 | --ds-rgb-pink-550: 227 55 136;
396 | --ds-rgb-pink-600: 219 39 119;
397 | --ds-rgb-pink-650: 204 31 106;
398 | --ds-rgb-pink-700: 190 24 93;
399 | --ds-rgb-pink-750: 173 23 85;
400 | --ds-rgb-pink-800: 157 23 77;
401 | --ds-rgb-pink-850: 144 23 72;
402 | --ds-rgb-pink-900: 131 24 67;
403 | --ds-rgb-pink-950: 80 7 36;
404 | --ds-rgb-rose-50: 255 241 242;
405 | --ds-rgb-rose-100: 255 228 230;
406 | --ds-rgb-rose-150: 254 216 220;
407 | --ds-rgb-rose-200: 254 205 211;
408 | --ds-rgb-rose-250: 253 184 193;
409 | --ds-rgb-rose-300: 253 164 175;
410 | --ds-rgb-rose-350: 252 138 154;
411 | --ds-rgb-rose-400: 251 113 133;
412 | --ds-rgb-rose-450: 247 88 113;
413 | --ds-rgb-rose-500: 244 63 94;
414 | --ds-rgb-rose-550: 234 46 83;
415 | --ds-rgb-rose-600: 225 29 72;
416 | --ds-rgb-rose-650: 207 23 66;
417 | --ds-rgb-rose-700: 190 18 60;
418 | --ds-rgb-rose-750: 174 18 58;
419 | --ds-rgb-rose-800: 159 18 57;
420 | --ds-rgb-rose-850: 147 18 56;
421 | --ds-rgb-rose-900: 136 19 55;
422 | --ds-rgb-rose-950: 76 5 25;
423 | }
424 |
425 | body,
426 | page,
427 | .ds-theme {
428 | --ds-input-height-l: 44px;
429 | --ds-input-height-m: 34px;
430 | --ds-input-height-s: 30px;
431 | --ds-input-height-xs: 26px;
432 | --ds-font-weight-strong: 600;
433 | --ds-font-size-l: 16px;
434 | --ds-line-height-l: 28px;
435 | --ds-font-size-m: 14px;
436 | --ds-line-height-m: 25px;
437 | --ds-font-size-sp: 13px;
438 | --ds-line-height-sp: 23px;
439 | --ds-font-size-s: 12px;
440 | --ds-line-height-s: 21px;
441 | --ds-font-size-xsp: 11px;
442 | --ds-line-height-xsp: 19.5px;
443 | --ds-font-size-xs: 10px;
444 | --ds-line-height-xs: 18px;
445 | --ds-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
446 | --ds-ease-in: cubic-bezier(0.4, 0, 1, 1);
447 | --ds-ease-out: cubic-bezier(0, 0, 0.2, 1);
448 | --ds-font-family-code:
449 | Menlo, "Roboto Mono", "Courier New", Courier, monospace, "Inter", sans-serif;
450 | --ds-transition-duration: 0.2s;
451 | --ds-transition-duration-fast: 0.1s;
452 | --ds-transition-duration-slow: 0.3s;
453 | }
454 |
455 | body,
456 | page {
457 | --ds-rgb-info: var(--ds-rgb-blue-500);
458 | --ds-rgb-primary: var(--ds-rgb-blue-500);
459 | --ds-rgb-primary-foreground: var(--ds-rgb-white);
460 | --ds-rgb-label-1: var(--ds-rgb-neutral-700);
461 | --ds-rgb-label-2: var(--ds-rgb-neutral-450);
462 | --ds-rgb-label-3: var(--ds-rgb-neutral-400);
463 | --ds-rgb-error: var(--ds-rgb-red-500);
464 | --ds-rgb-warning: var(--ds-rgb-amber-500);
465 | --ds-rgb-success: var(--ds-rgb-green-500);
466 | --ds-rgba-transparent: 255 255 255/0;
467 | --ds-rgb-bg-base: var(--ds-rgb-white);
468 | --ds-rgb-elevated: var(--ds-rgb-white);
469 | --ds-rgb-track: var(--ds-rgb-neutral-200);
470 | --ds-rgb-hover: var(--ds-rgb-neutral-100);
471 | --ds-rgb-header: var(--ds-rgb-neutral-50);
472 | --ds-rgb-separator: var(--ds-rgb-neutral-100);
473 | --ds-rgb-separator-strong: var(--ds-rgb-neutral-200);
474 | --ds-rgb-input: var(--ds-rgb-neutral-100);
475 | --ds-rgb-input-strong: var(--ds-rgb-neutral-150);
476 | --ds-rgb-input-focus: var(--ds-rgb-white);
477 | --ds-rgb-link: var(--ds-rgb-primary);
478 | --ds-rgb-tag: var(--ds-rgb-neutral-200);
479 | --ds-rgb-segmented: var(--ds-rgb-neutral-100);
480 | --ds-rgb-segmented-button: var(--ds-rgb-white);
481 | --ds-rgb-segmented-separator: var(--ds-rgb-neutral-300);
482 | --ds-rgb-thumb: var(--ds-rgb-white);
483 | font-family:
484 | Inter,
485 | system-ui,
486 | -apple-system,
487 | BlinkMacSystemFont,
488 | Segoe UI,
489 | Roboto,
490 | Noto Sans,
491 | Ubuntu,
492 | Cantarell,
493 | Helvetica Neue,
494 | Oxygen,
495 | Open Sans,
496 | sans-serif;
497 | }
498 |
--------------------------------------------------------------------------------
/src/defined.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | /**
3 | * 回答类型,思考和回答
4 | */
5 | export type AnswerType = 'answer' | 'thinking';
6 |
7 | /**
8 | * 段落类型
9 | * 段落类型为br时,表示换行
10 | * 段落类型为text时,表示文本
11 | */
12 | export interface IParagraph {
13 | /** 段落内容 */
14 | content?: string;
15 | /** 是否已打字 */
16 | isTyped: boolean;
17 | /** 段落类型 */
18 | type: 'br' | 'text';
19 | /** 回答类型 */
20 | answerType: AnswerType;
21 | }
22 |
23 | export interface MarkdownProps {
24 | /** 打字机效果间隔时间 */
25 | interval: number;
26 | /** 是否关闭匀速打字机效果 */
27 | isClosePrettyTyped?: boolean;
28 | /** 打字完成后回调, */
29 | onEnd?: (data?: { str?: string; answerType?: AnswerType }) => void;
30 | /** 开始打字回调 */
31 | onStart?: (data?: { currentIndex: number; currentChar: string; answerType: AnswerType; prevStr: string }) => void;
32 | /**
33 | * 打字机打完一个字符回调
34 | * @param char 字符
35 | * @param index 字符索引
36 | */
37 | onTypedChar?: (data?: { currentIndex: number; currentChar: string; answerType: AnswerType; prevStr: string }) => void;
38 | }
39 |
--------------------------------------------------------------------------------
/src/index.less:
--------------------------------------------------------------------------------
1 | :root {
2 | --ds-rgb-neutral-50: 250 250 250;
3 | --ds-rgb-neutral-100: 245 245 245;
4 | --ds-rgb-neutral-150: 237 237 237;
5 | --ds-rgb-neutral-200: 229 229 229;
6 | --ds-rgb-neutral-250: 220 220 220;
7 | --ds-rgb-neutral-300: 212 212 212;
8 | --ds-rgb-neutral-350: 187 187 187;
9 | --ds-rgb-neutral-400: 163 163 163;
10 | --ds-rgb-neutral-450: 139 139 139;
11 | --ds-rgb-neutral-500: 115 115 115;
12 | --ds-rgb-neutral-550: 98 98 98;
13 | --ds-rgb-neutral-600: 82 82 82;
14 | --ds-rgb-neutral-650: 73 73 73;
15 | --ds-rgb-neutral-700: 64 64 64;
16 | --ds-rgb-neutral-750: 51 51 51;
17 | --ds-rgb-neutral-800: 38 38 38;
18 | --ds-rgb-neutral-850: 30 30 30;
19 | --ds-rgb-neutral-900: 23 23 23;
20 | --ds-rgb-neutral-950: 10 10 10;
21 |
22 | --ds-rgb-blue-500: 59 130 246;
23 |
24 | --ds-rgb-label-1: var(--ds-rgb-neutral-700);
25 | }
26 |
27 | .ds-markdown {
28 | --ds-font-family-code: Menlo, 'Roboto Mono', 'Courier New', Courier, monospace, 'Inter', sans-serif;
29 | font-family:
30 | Inter,
31 | system-ui,
32 | -apple-system,
33 | BlinkMacSystemFont,
34 | Segoe UI,
35 | Roboto,
36 | Noto Sans,
37 | Ubuntu,
38 | Cantarell,
39 | Helvetica Neue,
40 | Oxygen,
41 | Open Sans,
42 | sans-serif;
43 | }
44 |
45 | .ds-markdown {
46 | --ds-rgb-primary: var(--ds-rgb-blue-500);
47 |
48 | --ds-rgb-label-1: var(--ds-rgb-neutral-700);
49 | --ds-rgb-label-2: var(--ds-rgb-neutral-450);
50 | --ds-rgb-label-3: var(--ds-rgb-neutral-400);
51 | --dsr-border-1: rgb(var(--ds-rgb-neutral-350));
52 | --dsr-border-2: rgb(var(--ds-rgb-neutral-200));
53 | }
54 |
55 | .ds-markdown {
56 | --ds-font-size-m: 14px;
57 | --ds-font-size-xsp: 11px;
58 | --ds-line-height-m: 25px;
59 | --ds-transition-duration: 0.2s;
60 | --ds-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
61 | --ds-rgba-transparent: 255 255 255 / 0;
62 | --ds-rgb-link: var(--ds-rgb-primary);
63 | }
64 |
65 | .ds-markdown.apple {
66 | --ds-font-weight-strong: 500;
67 | }
68 |
69 | .ds-markdown {
70 | --ds-md-zoom: 1.143;
71 | --ds-md-font-size: calc(var(--ds-md-zoom) * var(--ds-font-size-m));
72 | --ds-md-line-height: calc(var(--ds-md-zoom) * var(--ds-line-height-m));
73 | font-size: var(--ds-md-font-size);
74 | min-height: var(--ds-md-font-size);
75 | line-height: var(--ds-md-line-height);
76 | color: rgb(var(--ds-rgb-label-1));
77 | }
78 |
79 | .ds-markdown img {
80 | max-width: 100%;
81 | }
82 |
83 | .ds-markdown h1,
84 | .ds-markdown h2,
85 | .ds-markdown h3,
86 | .ds-markdown h4,
87 | .ds-markdown h5,
88 | .ds-markdown h6 {
89 | font-weight: var(--ds-font-weight-strong);
90 | font-size: var(--ds-md-font-size);
91 | line-height: var(--ds-md-line-height);
92 | margin: calc(var(--ds-md-zoom) * 16px) 0 calc(var(--ds-md-zoom) * 12px) 0;
93 | }
94 |
95 | .ds-markdown h1,
96 | .ds-markdown h2,
97 | .ds-markdown h3,
98 | .ds-markdown h4,
99 | .ds-markdown h5,
100 | .ds-markdown h6 {
101 | font-weight: var(--ds-font-weight-strong);
102 | font-size: var(--ds-md-font-size);
103 | line-height: var(--ds-md-line-height);
104 | margin: calc(var(--ds-md-zoom) * 16px) 0 calc(var(--ds-md-zoom) * 12px) 0;
105 | }
106 |
107 | .ds-markdown h1 {
108 | font-size: calc(var(--ds-md-zoom) * 24px);
109 | line-height: 1.5;
110 | }
111 |
112 | .ds-markdown h2 {
113 | font-size: calc(var(--ds-md-zoom) * 20px);
114 | line-height: 1.5;
115 | }
116 |
117 | .ds-markdown h3 {
118 | font-size: calc(var(--ds-md-zoom) * 16px);
119 | line-height: 1.5;
120 | }
121 |
122 | .ds-markdown h1 .header-anchor,
123 | .ds-markdown h2 .header-anchor,
124 | .ds-markdown h3 .header-anchor,
125 | .ds-markdown h4 .header-anchor,
126 | .ds-markdown h5 .header-anchor,
127 | .ds-markdown h6 .header-anchor {
128 | opacity: 0;
129 | margin-left: 4px;
130 | }
131 |
132 | .ds-markdown h1:hover .header-anchor,
133 | .ds-markdown h2:hover .header-anchor,
134 | .ds-markdown h3:hover .header-anchor,
135 | .ds-markdown h4:hover .header-anchor,
136 | .ds-markdown h5:hover .header-anchor,
137 | .ds-markdown h6:hover .header-anchor {
138 | opacity: 1;
139 | }
140 |
141 | .ds-markdown .ds-markdown-paragraph {
142 | margin: calc(var(--ds-md-zoom) * 12px) 0;
143 | font-size: var(--ds-md-font-size);
144 | line-height: var(--ds-md-line-height);
145 | }
146 | .ds-markdown a:not(.ds-a) {
147 | color: rgb(var(--ds-rgb-link));
148 | transition: box-shadow var(--ds-transition-duration) var(--ds-ease-in-out);
149 | border-radius: calc(var(--ds-md-zoom) * 6px);
150 | border-left: 3px solid rgba(var(--ds-rgba-transparent));
151 | border-right: 3px solid rgba(var(--ds-rgba-transparent));
152 | border-top: 2px solid rgba(var(--ds-rgba-transparent));
153 | border-bottom: 2px solid rgba(var(--ds-rgba-transparent));
154 | margin-left: -3px;
155 | margin-right: -3px;
156 | text-decoration: none;
157 | position: relative;
158 | }
159 |
160 | .ds-markdown a:not(.ds-a):focus {
161 | outline: none;
162 | }
163 |
164 | .ds-markdown a:not(.ds-a):focus-visible {
165 | box-shadow: 0 0 0 2px rgb(var(--ds-rgb-primary));
166 | }
167 |
168 | .ds-markdown li > ul,
169 | .ds-markdown li > ol {
170 | margin-top: 4px;
171 | }
172 |
173 | .ds-markdown ul,
174 | .ds-markdown ol {
175 | margin: calc(var(--ds-md-zoom) * 12px) 0;
176 | padding-left: calc(var(--ds-md-zoom) * 24px);
177 | }
178 |
179 | .ds-markdown li:not(:first-child) {
180 | margin-top: 4px;
181 | }
182 |
183 | .ds-markdown li::marker {
184 | line-height: var(--ds-md-line-height);
185 | color: rgb(var(--ds-rgb-label-2));
186 | }
187 |
188 | .ds-markdown hr {
189 | height: 1px;
190 | margin: 32px 0;
191 | background-color: var(--dsr-border-2);
192 | border: none;
193 | display: block;
194 | }
195 |
196 | .ds-markdown blockquote {
197 | border-left: 2px solid rgb(var(--ds-rgb-label-3));
198 | padding-left: calc(var(--ds-md-zoom) * 16px);
199 | margin: 0;
200 | }
201 |
202 | .ds-markdown table {
203 | border-collapse: collapse;
204 | }
205 |
206 | .ds-markdown th {
207 | color: rgb(var(--ds-rgb-label-1));
208 | padding: calc(var(--ds-md-zoom) * 6px) calc(var(--ds-md-zoom) * 12px);
209 | border-bottom: 1px solid rgb(var(--ds-rgb-label-3));
210 | border-top: 1px solid rgb(var(--ds-rgb-label-3));
211 | font-weight: 600;
212 | }
213 |
214 | .ds-markdown th,
215 | .ds-markdown td {
216 | text-align: left;
217 | }
218 |
219 | .ds-markdown th:first-child {
220 | padding-left: 0;
221 | }
222 |
223 | .ds-markdown td {
224 | padding: calc(var(--ds-md-zoom) * 6px) calc(var(--ds-md-zoom) * 12px);
225 | border-bottom: 1px solid rgb(var(--ds-rgb-label-3));
226 | }
227 |
228 | .ds-markdown td:first-child {
229 | padding-left: 0;
230 | }
231 |
232 | .ds-markdown pre {
233 | margin: calc(var(--ds-md-zoom) * 12px) 0;
234 | font-family: var(--ds-font-family-code);
235 | overflow: auto;
236 | }
237 |
238 | .ds-markdown code {
239 | font-size: 0.875em;
240 | font-weight: var(--ds-font-weight-strong);
241 | font-family: var(--ds-font-family-code);
242 | background-color: #ececec;
243 | border-radius: 4px;
244 | padding: 0.15rem 0.3rem;
245 | }
246 |
247 | .ds-markdown code:before {
248 | margin-left: 4px;
249 | }
250 |
251 | .ds-markdown code:after {
252 | margin-right: 4px;
253 | }
254 |
255 | ds-markdown-math {
256 | text-align: center;
257 | display: block;
258 | overflow-x: auto;
259 | overflow-y: hidden;
260 | }
261 |
262 | .ds-markdown > * .ds-markdown-math {
263 | margin: calc(var(--ds-md-zoom) * 12px) 0;
264 | }
265 |
266 | .ds-markdown > * .ds-markdown-math:first-child {
267 | margin-top: 0;
268 | }
269 |
270 | .ds-markdown > * .ds-markdown-math:last-child {
271 | margin-bottom: 0;
272 | }
273 |
274 | .ds-markdown-code-copy-button {
275 | background-color: rgba(var(--ds-rgba-transparent));
276 | color: inherit;
277 | cursor: pointer;
278 | border: none;
279 | margin: 0;
280 | padding: 0;
281 | }
282 |
283 | .ds-markdown li > .ds-markdown-paragraph {
284 | margin: 4px 0;
285 | }
286 |
287 | .ds-markdown li > :first-child {
288 | margin-top: 0;
289 | }
290 |
291 | .ds-markdown li > :last-child {
292 | margin-bottom: 0;
293 | }
294 |
295 | .ds-markdown .ds-markdown-paragraph:last-child {
296 | margin-bottom: 0 !important;
297 | }
298 |
299 | .ds-markdown > :first-child {
300 | margin-top: 0 !important;
301 | }
302 |
303 | .ds-markdown .ds-typed-thinking {
304 | border-left: 2px solid #e5e5e5;
305 | padding: 0 0 0 12px;
306 | color: #8b8b8b;
307 | line-height: 26px;
308 | font-size: var(--ds-font-size-m);
309 | }
310 |
311 | .ds-markdown.ds-typed-answer {
312 | margin: calc(var(--ds-md-zoom) * 12px) 0;
313 | font-size: var(--ds-md-font-size);
314 | line-height: var(--ds-md-line-height);
315 | }
316 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import MarkdownCMD from './MarkdownCMD/index.js';
2 | import Markdown from './Markdown/index.js';
3 |
4 | export default Markdown;
5 |
6 | export { Markdown, MarkdownCMD };
7 |
--------------------------------------------------------------------------------
/src/style.less:
--------------------------------------------------------------------------------
1 | @import './index.less';
2 |
3 | @import './components/BlockWrap/index.less';
4 | @import './components/HighReactMarkdown/index.less';
5 | @import './components/HighReactMarkdown/highlightTheme.less';
6 |
--------------------------------------------------------------------------------
/tsconfig-base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "lib": ["DOM", "ES2020", "DOM.Iterable"],
5 | "jsx": "react-jsx", // Keep this to allow overriding React with Preact
6 | "module": "ES2020",
7 | "moduleResolution": "Node",
8 | "sourceMap": true,
9 | "declaration": true,
10 | "outDir": "./dist",
11 | "esModuleInterop": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "strict": true,
14 | "alwaysStrict": true,
15 | "skipLibCheck": true,
16 | "noUnusedLocals": false,
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tsconfig-cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-base.json",
3 | "compilerOptions": {
4 | "outDir": "./dist/cjs",
5 | "module": "CommonJS"
6 | },
7 | "include": ["src"],
8 | "exclude": ["**/*.test.*"]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig-esm.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-base.json",
3 | "compilerOptions": {
4 | "outDir": "./dist/esm",
5 | "module": "ES2020"
6 | },
7 | "include": ["src"],
8 | "exclude": ["**/*.test.*"]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig-base.json",
3 | "compilerOptions": {
4 | "resolveJsonModule": true,
5 | "paths": {
6 | "@/test": [
7 | "./test"
8 | ],
9 | "@/test/*": [
10 | "./test/*"
11 | ],
12 | },
13 | "noEmit": true,
14 | "types": [
15 | "node",
16 | "jest",
17 | "@testing-library/jest-dom",
18 | "@types/jest"
19 | ]
20 | },
21 | "include": [
22 | "src",
23 | "test",
24 | "**/*.test.*",
25 | "example"
26 | ],
27 | "exclude": [
28 | "website"
29 | ]
30 | }
--------------------------------------------------------------------------------
/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/vite.build.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | build: {
8 | lib: {
9 | entry: 'src/index.tsx',
10 | name: 'MyLib', // 你的库名
11 | fileName: (format) => `my-lib.${format}.js`,
12 | formats: ['es', 'cjs'], // 输出格式
13 | },
14 | rollupOptions: {
15 | // 确保外部化处理你不想打包进库的依赖
16 | external: ['react', 'react-dom', 'remark-gfm', 'react-markdown', 'react-syntax-highlighter'],
17 | output: {
18 | globals: {
19 | react: 'React',
20 | 'react-dom': 'ReactDOM',
21 | },
22 | },
23 | },
24 | },
25 | })
26 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | base: '/ds-markdown/',
8 | build: {
9 | sourcemap: false, // 或 'inline',
10 | outDir: 'docs',
11 | },
12 | });
13 |
--------------------------------------------------------------------------------