Skip to content

Commit 81f275c

Browse files
author
zhaoge
committed
feat(chat): support AI quick command feat and upgrade lock file
1 parent 3b47ed7 commit 81f275c

23 files changed

Lines changed: 10955 additions & 16307 deletions

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
"resize-observer-polyfill": "^1.5.1",
108108
"standard-version": "^9.5.0",
109109
"stylelint": "^14.9.1",
110-
"ts-jest": "^29.0.3",
110+
"ts-jest": "29.0.3",
111111
"typescript": "~4.5.2"
112112
},
113113
"dependencies": {
@@ -126,8 +126,9 @@
126126
"react-markdown": "~8.0.6",
127127
"react-resizable": "^3.0.5",
128128
"react-syntax-highlighter": "~15.5.0",
129+
"rehype-raw": "^6.0.0",
129130
"remark-gfm": "~3.0.1",
130-
"shortid": "^2.2.16",
131+
"shortid": "2.2.16",
131132
"showdown": "^1.9.0"
132133
},
133134
"config": {

pnpm-lock.yaml

Lines changed: 10513 additions & 16260 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Think component snapshot renders completed state with children correctly 1`] = `
4+
<div>
5+
<div
6+
class="dtc__custom__think"
7+
>
8+
<section
9+
class="dtc-flex dtc__custom__think__btn"
10+
style="flex-wrap: nowrap; justify-content: normal; align-items: center; gap: 4px;"
11+
>
12+
已完成深度思考
13+
<span
14+
color="#64698B"
15+
data-mock-icon="UpOutlined"
16+
size="12"
17+
style="transform: none;"
18+
/>
19+
</section>
20+
<div
21+
class="dtc__custom__think__content"
22+
>
23+
<div
24+
class="dtc__custom__think__line"
25+
/>
26+
<div
27+
class="dtc__custom__think__text"
28+
>
29+
这里是 Think 的内容示例(支持 Markdown)
30+
31+
- 列表项 A
32+
- 列表项 B
33+
</div>
34+
</div>
35+
</div>
36+
</div>
37+
`;
38+
39+
exports[`Think component snapshot renders loading state correctly 1`] = `
40+
<div>
41+
<div
42+
class="dtc__custom__think"
43+
>
44+
<section
45+
class="dtc-flex dtc__custom__think__btn"
46+
style="flex-wrap: nowrap; justify-content: normal; align-items: center; gap: 4px;"
47+
>
48+
<span
49+
class="gradient-text"
50+
>
51+
思考中...
52+
</span>
53+
<span
54+
color="#64698B"
55+
data-mock-icon="UpOutlined"
56+
size="12"
57+
style="transform: none;"
58+
/>
59+
</section>
60+
<div
61+
class="dtc__custom__think__content"
62+
>
63+
<div
64+
class="dtc__custom__think__line"
65+
/>
66+
<div
67+
class="dtc__custom__think__text"
68+
>
69+
正在思考中,暂无内容。
70+
</div>
71+
</div>
72+
</div>
73+
</div>
74+
`;

src/chat/__tests__/message.test.tsx

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import '@testing-library/jest-dom/extend-expect';
77

88
import { Message as MessageEntity, MessageStatus, Prompt as PromptEntity } from '../entity';
99
import Message from '../message';
10-
import Chat from '..';
10+
import Chat from '../';
1111

1212
jest.mock('remark-gfm', () => () => {});
1313

@@ -232,12 +232,24 @@ describe('Test Chat Message', () => {
232232
prompt.messages[0].status = MessageStatus.DONE;
233233
const onRegenerate = jest.fn();
234234
const { container, getByText } = render(
235-
<Message
236-
prompt={prompt}
237-
regenerate
238-
data={prompt.messages}
239-
onRegenerate={onRegenerate}
240-
/>
235+
<Chat
236+
chat={{} as any}
237+
messageHeader={
238+
<div
239+
className="dtc__message__extra__render"
240+
data-testid="fakeMessageExtraRender"
241+
>
242+
ExtraDom
243+
</div>
244+
}
245+
>
246+
<Message
247+
prompt={prompt}
248+
regenerate
249+
data={prompt.messages}
250+
onRegenerate={onRegenerate}
251+
/>
252+
</Chat>
241253
);
242254

243255
const nodeList = container
@@ -301,4 +313,29 @@ describe('Test Chat Message', () => {
301313
expect(ele.dataset.messageid).toBe('1');
302314
expect(ele.dataset.promptid).toBe('1');
303315
});
316+
317+
it('Should support extraRender', () => {
318+
const prompt = generatePrompt();
319+
prompt.messages[0].status = MessageStatus.DONE;
320+
const { container, getByTestId } = render(
321+
<Chat
322+
chat={{} as any}
323+
messageHeader={
324+
<div
325+
className="dtc__message__extra__render"
326+
data-testid="fakeMessageExtraRender"
327+
>
328+
ExtraDom
329+
</div>
330+
}
331+
>
332+
<Message prompt={prompt} data={prompt.messages} />
333+
</Chat>
334+
);
335+
expect(getByTestId('fakeMessageExtraRender')).toBeInTheDocument();
336+
const nodeList = container.querySelectorAll<HTMLDivElement>('.dtc__message__extra__render');
337+
const ele = nodeList?.item(nodeList?.length - 1);
338+
expect(ele).not.toBeNull();
339+
expect(ele?.textContent).toBe('ExtraDom');
340+
});
304341
});

src/chat/__tests__/prompt.test.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { render } from '@testing-library/react';
44

55
import { Prompt as PromptEntity } from '../entity';
66
import Prompt from '../prompt';
7-
import Chat from '..';
7+
import Chat from '../';
88

99
jest.mock('remark-gfm', () => () => {});
1010
class BasePrompt extends PromptEntity {}
@@ -57,4 +57,24 @@ describe('Test Chat Prompt', () => {
5757

5858
expect(getByTestId('fakeCode').dataset.promptid).toBe('1');
5959
});
60+
61+
it('Should support extraRender', () => {
62+
const data = generatePrompt();
63+
const { container } = render(
64+
<Chat
65+
chat={{} as any}
66+
promptFooter={
67+
<div className="dtc__prompt__extra__render" data-testid="fakePromptExtraRender">
68+
PromptExtraDom
69+
</div>
70+
}
71+
>
72+
<Prompt data={data} />
73+
</Chat>
74+
);
75+
const nodeList = container.querySelectorAll<HTMLDivElement>('.dtc__prompt__extra__render');
76+
const ele = nodeList?.item(nodeList?.length - 1);
77+
expect(ele).not.toBeNull();
78+
expect(ele?.textContent).toBe('PromptExtraDom');
79+
});
6080
});

src/chat/__tests__/think.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
4+
import Think from '../think';
5+
6+
describe('Think component snapshot', () => {
7+
it('renders loading state correctly', () => {
8+
const data = { children: '正在思考中,暂无内容。' } as any;
9+
const { container } = render(<Think data={data} loading />);
10+
expect(container).toMatchSnapshot();
11+
});
12+
13+
it('renders completed state with children correctly', () => {
14+
const markdown = `这里是 Think 的内容示例(支持 Markdown)\n\n- 列表项 A\n- 列表项 B`;
15+
const data = { children: markdown } as any;
16+
const { container } = render(<Think data={data} loading={false} />);
17+
expect(container).toMatchSnapshot();
18+
});
19+
});

src/chat/codeBlock/index.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,31 @@ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
44
import classNames from 'classnames';
55

66
import Copy from '../../copy';
7+
import { Message as MessageEntity } from '../entity';
78
import { CopyOptions } from '../useContext';
89
import './index.scss';
910

10-
export interface ICodeBlockProps {
11+
export interface ICodeBlockProps<M extends MessageEntity = MessageEntity> {
1112
copy?: boolean | CopyOptions;
1213
className?: string;
1314
style?: React.CSSProperties;
15+
message?: M;
1416
convert?: boolean;
15-
toolbars?: React.ReactNode | (() => React.ReactNode);
17+
toolbars?: React.ReactNode | ((code: string, message?: M) => React.ReactNode);
1618
options?: Partial<SyntaxHighlighterProps>;
1719
children: React.ReactNode & React.ReactNode[];
1820
}
1921

20-
export default function CodeBlock({
22+
export default function CodeBlock<M extends MessageEntity = MessageEntity>({
2123
className,
2224
style,
2325
toolbars,
26+
message,
2427
copy: rawCopy,
2528
convert,
2629
children,
2730
options: { lineNumberStyle = {}, ...rest } = {},
28-
}: ICodeBlockProps) {
31+
}: ICodeBlockProps<M>) {
2932
const { value, language } = useMemo(() => {
3033
const child = children[0] as React.ReactElement;
3134
const match = /language-(\w+)/.exec(child.props.className || '');
@@ -63,9 +66,9 @@ export default function CodeBlock({
6366
{language.toLocaleLowerCase()}
6467
</span>
6568
<div className="dtc__aigc__codeblock__tool">
69+
{typeof toolbars === 'function' ? toolbars(text, message) : toolbars}
6670
{/* FIXME:Copy 组件后续可以支持一下 disabled 属性 */}
6771
{!copy.disabled && <Copy text={text} {...copy.options} />}
68-
{typeof toolbars === 'function' ? toolbars() : toolbars}
6972
</div>
7073
</div>
7174
<SyntaxHighlighter

src/chat/demos/basic.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export default function () {
5757
</Button>
5858
),
5959
}}
60+
messageHeader={<div>这是自定义header</div>}
61+
promptFooter={<div>这是自定义footer</div>}
6062
>
6163
<Chat.Content
6264
data={chat.conversation.get()?.prompts || []}

src/chat/demos/markdown.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
2+
import { Components } from 'react-markdown';
23
import { Chat } from 'dt-react-component';
4+
import rehypeRaw from 'rehype-raw';
35

46
const children = `
57
# 大标题
@@ -35,7 +37,35 @@ SELECT * FROM table_name;
3537
| 单元格 | 单元格 | 单元格 |
3638
| 单元格 | 单元格 | 单元格 |
3739
`;
40+
const childrenTsx = `<customtsx>
41+
<span class=\"command-tag-Lynton\">这是自定义的标签</span>
42+
</customtsx>`;
3843

3944
export default function () {
40-
return <Chat.Markdown>{children}</Chat.Markdown>;
45+
return (
46+
<Chat
47+
chat={{} as any}
48+
components={
49+
{
50+
customtsx: ({ children }: any) => {
51+
return <div className="hhhh">{children}</div>;
52+
},
53+
} as Components
54+
}
55+
rehypePlugins={[rehypeRaw]}
56+
>
57+
<Chat.Markdown>{children}</Chat.Markdown>
58+
<Chat.Content
59+
data={[
60+
{
61+
messages: [
62+
{
63+
content: childrenTsx,
64+
},
65+
],
66+
},
67+
]}
68+
/>
69+
</Chat>
70+
);
4171
}

src/chat/demos/message.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { Message, MessageStatus, Prompt } from 'dt-react-component/chat/entity';
55

66
class BasicPrompt extends Prompt {}
77
class BasicMessage extends Message {}
8-
8+
const CustomRender = () => {
9+
return <div style={{ color: 'red' }}>这是自定义header</div>;
10+
};
911
export default function () {
1012
const [status, setStatus] = useState<MessageStatus>(MessageStatus.DONE);
1113

@@ -36,13 +38,15 @@ export default function () {
3638
置为完成
3739
</Button>
3840
</Space>
39-
<Chat.Message
40-
prompt={data}
41-
data={data.messages}
42-
regenerate
43-
onStop={() => setStatus(MessageStatus.STOPPED)}
44-
onRegenerate={() => console.log('regenerate')}
45-
/>
41+
<Chat chat={{} as any} messageHeader={<CustomRender />}>
42+
<Chat.Message
43+
prompt={data}
44+
data={data.messages}
45+
regenerate
46+
onStop={() => setStatus(MessageStatus.STOPPED)}
47+
onRegenerate={() => console.log('regenerate')}
48+
/>
49+
</Chat>
4650
</>
4751
);
4852
}

0 commit comments

Comments
 (0)