Skip to content

Commit 6c4432e

Browse files
authored
feat: add the markdown components (#655)
* chore: upate the gitignore * chore: remove useless fiel * Optimize the style building and increase style scope. * chore: updage the git ignore * fix: fix the style building * chore: fix the style building * feat: add the markdown components * chore: update the doc * style: tweak the them of the Markdown * chore: remove the pro-editor * fix: fix the markdown * chore: update the assistant_start * chore: update the icon * chore: release @petercatai/[email protected] * chore: fix ci
1 parent af08e82 commit 6c4432e

32 files changed

+4064
-3986
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ next-env.d.ts
6262
.pytest_cache
6363

6464
dist/
65-
lui/src/style.css
65+
assistant/src/style/global.css

assistant/package.json

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@petercatai/assistant",
3-
"version": "2.0.12",
3+
"version": "2.0.15",
44
"description": "PeterCat Assistant Application",
55
"repository": "https://github.com/petercat-ai/petercat.git",
66
"license": "MIT",
@@ -12,16 +12,16 @@
1212
"import": "./dist/esm/index.js"
1313
},
1414
"./style": {
15-
"import": "./dist/esm/style.css"
15+
"import": "./dist/esm/style/global.css"
1616
}
1717
},
1818
"module": "dist/esm/index.js",
1919
"browser": "dist/umd/assistant.min.js",
2020
"types": "dist/esm/index.d.ts",
21-
"style": "dist/esm/style.css",
21+
"style": "dist/esm/style/global.css",
2222
"scripts": {
23-
"build": "npm run build:css && cp ./src/style.css ./.dumi/tmp/style.css && father build",
24-
"build:css": "tailwindcss -i tailwind.css -o ./src/style.css",
23+
"build": "npm run build:css && cp ./src/style/global.css ./.dumi/tmp/global.css && father build",
24+
"build:css": "tailwindcss -i tailwind.css -o ./src/style/global.css",
2525
"build:watch": "father dev",
2626
"dev": "dumi dev",
2727
"docs:build": "dumi build",
@@ -57,16 +57,21 @@
5757
},
5858
"dependencies": {
5959
"@ant-design/icons": "^5.3.5",
60-
"@ant-design/pro-editor": "^1.1.1",
6160
"@ant-design/x": "^1.0.0",
6261
"@babel/runtime": "^7.18.0",
6362
"antd": "^5.15.3",
6463
"antd-style": "^3.6.1",
6564
"axios": "^1.6.7",
6665
"classnames": "^2.5.1",
6766
"dotenv": "^16.4.5",
67+
"latest": "^0.2.0",
6868
"lodash": "^4.17.21",
6969
"lottie-react": "^2.4.0",
70+
"react-markdown": "^8.0.7",
71+
"react-syntax-highlighter": "^15.6.1",
72+
"rehype-highlight": "6.0.0",
73+
"remark-gfm": "^3.0.1",
74+
"remark-html": "^16.0.1",
7075
"swr": "^2.2.5",
7176
"tailwindcss": "^3.4.1",
7277
"tailwindcss-scoped-preflight": "^3.2.6"
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module.exports = (content) => {
2-
return `import '../.dumi/tmp/style.css';\n${content}`;
2+
return `import '../.dumi/tmp/global.css';\n${content}`;
33
}

assistant/src/Assistant/index.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default () => {
2828
<a onClick={() => setToken('1234')}>点我变token{token}</a>
2929
<Assistant
3030
editBotId ={token}
31-
apiUrl='/api/chat/stream_builder'
31+
apiUrl='/api/chat/stream_qa'
3232
token='0553365a-edb1-435c-b69c-4c645290b86e'
3333
clearMessage={true}
3434
/>
@@ -52,4 +52,4 @@ export default () => {
5252
| `disabled` | `boolean` | `false` | 是否禁用聊天输入区域,禁用后用户无法输入消息。 |
5353
| `disabledPlaceholder` | `string` | `undefined` | 当聊天输入区域被禁用时显示的占位符文本。 |
5454
| `getToolsResult` | `(response: any) => void` | `undefined` | 用于接收工具处理结果的回调函数。 |
55-
| `bottom` | `number` | `120` | 聊天助手距离底部的距离,单位为像素。 |
55+
| `bottom` | `number` | `120` | 聊天助手距离底部的距离,单位为像素。 |

assistant/src/Assistant/index.tsx

+9-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
import { CloseCircleFilled } from '@ant-design/icons';
2-
import { ActionIcon } from '@ant-design/pro-editor';
32
import classnames from 'classnames';
43
import React, { useEffect, useState } from 'react';
54
import { createRoot } from 'react-dom/client';
65
import Chat, { ChatProps } from '../Chat';
76
import BubbleIcon from '../icons/BubbleIcon';
87

98
export interface AssistantProps extends ChatProps {
10-
showBubble: boolean;
11-
isVisible: boolean;
9+
showBubble?: boolean;
10+
isVisible?: boolean;
1211
onClose?: () => void;
1312
bottom?: number;
1413
}
1514

1615
const Assistant = (props: AssistantProps) => {
1716
const {
1817
showBubble = true,
19-
isVisible,
18+
isVisible = false,
2019
onClose,
2120
drawerWidth = 500,
2221
bottom = 120,
@@ -69,11 +68,8 @@ const Assistant = (props: AssistantProps) => {
6968
},
7069
);
7170

72-
if (typeof window === 'undefined') {
73-
return;
74-
}
7571
return (
76-
<div className="petercat-lui-assistant">
72+
<div className="petercat-assitant">
7773
<div
7874
className={cls}
7975
style={{
@@ -91,12 +87,11 @@ const Assistant = (props: AssistantProps) => {
9187
{...props}
9288
drawerWidth={drawerWidth}
9389
/>
94-
<div className="absolute top-0 right-0 m-1">
95-
<ActionIcon
96-
icon={<CloseCircleFilled />}
97-
onClick={toggleDrawer}
98-
className="w-6 h-6 text-black"
99-
/>
90+
<div
91+
className="absolute top-0 right-0 m-1 flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 hover:bg-gray-300 cursor-pointer transition-all duration-300"
92+
onClick={toggleDrawer}
93+
>
94+
<CloseCircleFilled className="w-4 h-4 text-black" />
10095
</div>
10196
</>
10297
)}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
/* eslint-disable react/no-danger */
2-
import { Markdown } from '@ant-design/pro-editor';
31
import React from 'react';
2+
import Markdown from '../../Markdown';
43

54
interface IProps {
65
className?: string;
@@ -9,10 +8,8 @@ interface IProps {
98

109
const MarkdownRender = React.memo((props: IProps) => (
1110
<Markdown
12-
className="ant-pro-chat-list-item-message-content"
1311
style={{ overflowX: 'hidden', overflowY: 'auto' }}
14-
>
15-
{props.content}
16-
</Markdown>
12+
text={props.content}
13+
/>
1714
));
1815
export default MarkdownRender;

assistant/src/Chat/components/UserContent.tsx

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Markdown } from '@ant-design/pro-editor';
21
import { Image } from 'antd';
32
import React, { type FC } from 'react';
43
import { ImageURLContentBlock } from '../../interface';
4+
import MarkdownRender from './MarkdownRender';
55

66
interface IProps {
77
images: ImageURLContentBlock[];
@@ -23,16 +23,7 @@ const UserContent: FC<IProps> = ({ images, text }) => {
2323
}}
2424
/>
2525
))}
26-
{text && (
27-
<Markdown
28-
style={{
29-
overflowX: 'hidden',
30-
overflowY: 'auto',
31-
}}
32-
>
33-
{text}
34-
</Markdown>
35-
)}
26+
{text && <MarkdownRender content={text} />}
3627
</div>
3728
);
3829
};

assistant/src/Chat/index.css

-37
This file was deleted.

assistant/src/Chat/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ atomId: Chat
44

55
# Chat
66

7-
Chat 是一个基于 @ant-design/x @ant-design/pro-editor 构建的聊天组件,提供了丰富的功能和配置选项。它支持与一个基于 @ant-design/x 的聊天服务进行交互,并且可以渲染多种消息类型。
7+
Chat 是一个基于 @ant-design/x 构建的聊天组件,提供了丰富的功能和配置选项。它支持与一个基于 @ant-design/x 的聊天服务进行交互,并且可以渲染多种消息类型。
88

99
## 安装
1010

assistant/src/Chat/index.tsx

+21-22
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import LoadingStart from './components/LoadingStart';
2323
import MarkdownRender from './components/MarkdownRender';
2424
import MySpinner from './components/MySpinner';
2525
import UserContent from './components/UserContent';
26-
import './index.css';
2726
import { UITemplateRender } from './template';
2827

2928
const CANCEL_REASON = 'petercat user cancel';
@@ -299,26 +298,6 @@ const Chat: FC<ChatProps> = memo(
299298
};
300299
}, []);
301300

302-
useEffect(() => {
303-
if (isEmpty(botDetail)) {
304-
return;
305-
}
306-
try {
307-
// @ts-ignore
308-
const info = botDetail?.[0] as any;
309-
setCurrentBotInfo({
310-
assistantMeta: {
311-
avatar: info.avatar,
312-
title: info.name,
313-
},
314-
helloMessage: info.hello_message,
315-
starters: info.starters || [],
316-
});
317-
} catch (e) {
318-
console.error('botDetail effect', e);
319-
}
320-
}, [botDetail]);
321-
322301
useEffect(() => {
323302
if (isEqual(currentBotInfo, currentBotInfoRef.current)) {
324303
return;
@@ -342,6 +321,26 @@ const Chat: FC<ChatProps> = memo(
342321
});
343322
}, [assistantMeta, helloMessage, starters]);
344323

324+
useEffect(() => {
325+
if (isEmpty(botDetail)) {
326+
return;
327+
}
328+
try {
329+
// @ts-ignore
330+
const info = botDetail?.[0] as any;
331+
setCurrentBotInfo({
332+
assistantMeta: {
333+
avatar: info.avatar,
334+
title: info.name,
335+
},
336+
helloMessage: info.hello_message,
337+
starters: info.starters || [],
338+
});
339+
} catch (e) {
340+
console.error('botDetail effect', e);
341+
}
342+
}, [botDetail]);
343+
345344
// ============================ Roles =============================
346345
const roles: GetProp<typeof Bubble.List, 'roles'> = React.useMemo(() => {
347346
const {
@@ -509,7 +508,7 @@ const Chat: FC<ChatProps> = memo(
509508
// ============================ Render ============================
510509
return (
511510
<div
512-
className="petercat-lui bg-[#FCFCFC] pt-2"
511+
className="petercat-assitant bg-[#FCFCFC] pt-2"
513512
style={{
514513
...style,
515514
minWidth: drawerWidth,

assistant/src/GitInsight/index.tsx

+15-13
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,24 @@ const GitInsight = (props: GitInsightProps) => {
3131
}, []);
3232

3333
return (
34-
<div className="flex justify-start items-center">
35-
<div className="opacity-0 transform transition-opacity duration-500 delay-200 animate-fade-in">
36-
<MemoizedCountCard type="star" count={starCount} />
37-
</div>
38-
39-
{num0End && (
34+
<div className="petercat-assitant">
35+
<div className="flex justify-start items-center">
4036
<div className="opacity-0 transform transition-opacity duration-500 delay-200 animate-fade-in">
41-
<MemoizedCountCard type="fork" count={forkCount} />
37+
<MemoizedCountCard type="star" count={starCount} />
4238
</div>
43-
)}
4439

45-
{num1End && (
46-
<div className="opacity-0 transform transition-opacity duration-500 delay-200 animate-fade-in">
47-
<MemoizedCountCard type="commit" count={commitCount} />
48-
</div>
49-
)}
40+
{num0End && (
41+
<div className="opacity-0 transform transition-opacity duration-500 delay-200 animate-fade-in">
42+
<MemoizedCountCard type="fork" count={forkCount} />
43+
</div>
44+
)}
45+
46+
{num1End && (
47+
<div className="opacity-0 transform transition-opacity duration-500 delay-200 animate-fade-in">
48+
<MemoizedCountCard type="commit" count={commitCount} />
49+
</div>
50+
)}
51+
</div>
5052
</div>
5153
);
5254
};

assistant/src/Markdown/CodeBlock.tsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
3+
// @ts-ignore
4+
import { vs } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5+
6+
import CopyButton from './CopyButton';
7+
8+
const CodeBlock: React.FC<{
9+
inline?: boolean;
10+
className?: string;
11+
children?: React.ReactNode;
12+
}> = ({ inline, className, children }) => {
13+
const language = className?.replace('language-', '') || ''; // 提取语言信息
14+
const codeContent = String(children).trim();
15+
16+
if (inline) {
17+
return <code className="bg-gray-200 px-1 rounded">{children}</code>;
18+
}
19+
20+
return (
21+
<div className="relative group">
22+
<SyntaxHighlighter language={language} style={vs} className="rounded-md">
23+
{codeContent}
24+
</SyntaxHighlighter>
25+
<CopyButton content={codeContent} />
26+
</div>
27+
);
28+
};
29+
30+
export default CodeBlock;

assistant/src/Markdown/CopyButton.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, { useState } from 'react';
2+
3+
const CopyButton: React.FC<{ content: string }> = ({ content }) => {
4+
const [copied, setCopied] = useState(false);
5+
6+
const handleCopy = async () => {
7+
try {
8+
await navigator.clipboard.writeText(content);
9+
setCopied(true);
10+
setTimeout(() => setCopied(false), 2000);
11+
} catch (error) {
12+
console.error('copy', error);
13+
}
14+
};
15+
16+
return (
17+
<button
18+
className="absolute top-2 right-2 text-xs bg-gray-800 text-white p-1 rounded-md hover:bg-gray-700"
19+
onClick={handleCopy}
20+
>
21+
{copied ? 'copied' : 'copy'}
22+
</button>
23+
);
24+
};
25+
26+
export default CopyButton;

0 commit comments

Comments
 (0)