Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"resize-observer-polyfill": "^1.5.1",
"standard-version": "^9.5.0",
"stylelint": "^14.9.1",
"ts-jest": "^29.0.3",
"ts-jest": "29.0.3",
"typescript": "~4.5.2"
},
"dependencies": {
Expand All @@ -126,8 +126,9 @@
"react-markdown": "~8.0.6",
"react-resizable": "^3.0.5",
"react-syntax-highlighter": "~15.5.0",
"rehype-raw": "^6.0.0",
"remark-gfm": "~3.0.1",
"shortid": "^2.2.16",
"shortid": "2.2.16",
"showdown": "^1.9.0"
},
"config": {
Expand Down
26,773 changes: 10,513 additions & 16,260 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions src/chat/__tests__/__snapshots__/think.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Think component snapshot renders completed state with children correctly 1`] = `
<div>
<div
class="dtc__custom__think"
>
<section
class="dtc-flex dtc__custom__think__btn"
style="flex-wrap: nowrap; justify-content: normal; align-items: center; gap: 4px;"
>
已完成深度思考
<span
color="#64698B"
data-mock-icon="UpOutlined"
size="12"
style="transform: none;"
/>
</section>
<div
class="dtc__custom__think__content"
>
<div
class="dtc__custom__think__line"
/>
<div
class="dtc__custom__think__text"
>
这里是 Think 的内容示例(支持 Markdown)

- 列表项 A
- 列表项 B
</div>
</div>
</div>
</div>
`;

exports[`Think component snapshot renders loading state correctly 1`] = `
<div>
<div
class="dtc__custom__think"
>
<section
class="dtc-flex dtc__custom__think__btn"
style="flex-wrap: nowrap; justify-content: normal; align-items: center; gap: 4px;"
>
<span
class="gradient-text"
>
思考中...
</span>
<span
color="#64698B"
data-mock-icon="UpOutlined"
size="12"
style="transform: none;"
/>
</section>
<div
class="dtc__custom__think__content"
>
<div
class="dtc__custom__think__line"
/>
<div
class="dtc__custom__think__text"
>
正在思考中,暂无内容。
</div>
</div>
</div>
</div>
`;
26 changes: 26 additions & 0 deletions src/chat/__tests__/content.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ jest.mock('../prompt', () => {
return (props: any) => <pre data-testid="fakePrompt">{JSON.stringify(props)}</pre>;
});

const CustomPrompt = (props: any) => (
<pre className="custom-prompt-replaced" data-testid="fakeCustomPrompt">
{JSON.stringify(props)}
</pre>
);
const CustomMessage = (props: any) => (
<pre className="custom-message-replaced" data-testid="fakeCustomMessage">
{JSON.stringify(props)}
</pre>
);
function generatePrompt() {
const prompt = new BasePrompt({
id: '1',
Expand Down Expand Up @@ -192,4 +202,20 @@ describe('Test Chat Content', () => {
expect(fn).toBeCalledTimes(1);
expect(fn).lastCalledWith({ top: 200, left: 0, behavior: 'instant' });
});
it('Should support replacePrompt and replaceMessage', () => {
const { container, getByTestId } = render(
<Content
data={[generatePrompt()]}
replacePrompt={(promptProps) => <CustomPrompt data={promptProps} />}
replaceMessage={(messageProps) => <CustomMessage data={messageProps} />}
/>
);
expect(getByTestId('fakeCustomPrompt')).toBeInTheDocument();
const ele1 = container.querySelector('.custom-prompt-replaced')!;
expect(ele1.textContent).not.toBeNull();

expect(getByTestId('fakeCustomMessage')).toBeInTheDocument();
const ele2 = container.querySelector('.custom-message-replaced')!;
expect(ele2.textContent).not.toBeNull();
});
});
1 change: 1 addition & 0 deletions src/chat/__tests__/markdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cleanup, render } from '@testing-library/react';
import Markdown from '../markdown';

jest.mock('remark-gfm', () => () => {});
jest.mock('rehype-raw', () => () => {});

const markdown = `
# title
Expand Down
27 changes: 26 additions & 1 deletion src/chat/__tests__/message.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import '@testing-library/jest-dom/extend-expect';

import { Message as MessageEntity, MessageStatus, Prompt as PromptEntity } from '../entity';
import Message from '../message';
import Chat from '..';
import Chat from '../';

jest.mock('remark-gfm', () => () => {});
jest.mock('rehype-raw', () => () => {});

class BasePrompt extends PromptEntity {}
class BaseMessage extends MessageEntity {}
Expand Down Expand Up @@ -301,4 +302,28 @@ describe('Test Chat Message', () => {
expect(ele.dataset.messageid).toBe('1');
expect(ele.dataset.promptid).toBe('1');
});

it('Should support extraRender', () => {
const prompt = generatePrompt();
prompt.messages[0].status = MessageStatus.DONE;
const { container, getByTestId } = render(
<Message
prompt={prompt}
data={prompt.messages}
extraRender={
<div
className="dtc__message__extra__render"
data-testid="fakeMessageExtraRender"
>
ExtraDom
</div>
}
/>
);
expect(getByTestId('fakeMessageExtraRender')).toBeInTheDocument();
const nodeList = container.querySelectorAll<HTMLDivElement>('.dtc__message__extra__render');
const ele = nodeList?.item(nodeList?.length - 1);
expect(ele).not.toBeNull();
expect(ele?.textContent).toBe('ExtraDom');
});
});
21 changes: 20 additions & 1 deletion src/chat/__tests__/prompt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { render } from '@testing-library/react';

import { Prompt as PromptEntity } from '../entity';
import Prompt from '../prompt';
import Chat from '..';
import Chat from '../';

jest.mock('remark-gfm', () => () => {});
jest.mock('rehype-raw', () => () => {});
class BasePrompt extends PromptEntity {}

function generatePrompt() {
Expand Down Expand Up @@ -57,4 +58,22 @@ describe('Test Chat Prompt', () => {

expect(getByTestId('fakeCode').dataset.promptid).toBe('1');
});

it('Should support extraRender', () => {
const data = generatePrompt();
const { container } = render(
<Prompt
data={data}
extraRender={
<div className="dtc__prompt__extra__render" data-testid="fakePromptExtraRender">
PromptExtraDom
</div>
}
/>
);
const nodeList = container.querySelectorAll<HTMLDivElement>('.dtc__prompt__extra__render');
const ele = nodeList?.item(nodeList?.length - 1);
expect(ele).not.toBeNull();
expect(ele?.textContent).toBe('PromptExtraDom');
});
});
19 changes: 19 additions & 0 deletions src/chat/__tests__/think.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import { render } from '@testing-library/react';

import Think from '../think';

describe('Think component snapshot', () => {
it('renders loading state correctly', () => {
const data = { children: '正在思考中,暂无内容。' } as any;
const { container } = render(<Think data={data} loading />);
expect(container).toMatchSnapshot();
});

it('renders completed state with children correctly', () => {
const markdown = `这里是 Think 的内容示例(支持 Markdown)\n\n- 列表项 A\n- 列表项 B`;
const data = { children: markdown } as any;
const { container } = render(<Think data={data} loading={false} />);
expect(container).toMatchSnapshot();
});
});
13 changes: 8 additions & 5 deletions src/chat/codeBlock/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,31 @@ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import classNames from 'classnames';

import Copy from '../../copy';
import { Message as MessageEntity } from '../entity';
import { CopyOptions } from '../useContext';
import './index.scss';

export interface ICodeBlockProps {
export interface ICodeBlockProps<M extends MessageEntity = MessageEntity> {
copy?: boolean | CopyOptions;
className?: string;
style?: React.CSSProperties;
message?: M;
convert?: boolean;
toolbars?: React.ReactNode | (() => React.ReactNode);
toolbars?: React.ReactNode | ((code: string, message?: M) => React.ReactNode);
options?: Partial<SyntaxHighlighterProps>;
children: React.ReactNode & React.ReactNode[];
}

export default function CodeBlock({
export default function CodeBlock<M extends MessageEntity = MessageEntity>({
className,
style,
toolbars,
message,
copy: rawCopy,
convert,
children,
options: { lineNumberStyle = {}, ...rest } = {},
}: ICodeBlockProps) {
}: ICodeBlockProps<M>) {
const { value, language } = useMemo(() => {
const child = children[0] as React.ReactElement;
const match = /language-(\w+)/.exec(child.props.className || '');
Expand Down Expand Up @@ -63,9 +66,9 @@ export default function CodeBlock({
{language.toLocaleLowerCase()}
</span>
<div className="dtc__aigc__codeblock__tool">
{typeof toolbars === 'function' ? toolbars(text, message) : toolbars}
{/* FIXME:Copy 组件后续可以支持一下 disabled 属性 */}
{!copy.disabled && <Copy text={text} {...copy.options} />}
{typeof toolbars === 'function' ? toolbars() : toolbars}
</div>
</div>
<SyntaxHighlighter
Expand Down
Loading
Loading