Skip to content

Commit

Permalink
🐛 fix: improve rehypePlugins and remarkPlugins props
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Sep 15, 2024
1 parent f22bec6 commit 2b15328
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 8 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"@types/react": "18.2.40",
"@types/react-avatar-editor": "^13.0.2",
"@types/react-dom": "^18.3.0",
"@types/unist": "^3.0.3",
"@types/uuid": "^10.0.0",
"@vitest/coverage-v8": "~1.2.2",
"babel-plugin-antd-style": "^1.0.4",
Expand All @@ -160,6 +161,8 @@
"semantic-release": "^21.1.2",
"stylelint": "^15.11.0",
"typescript": "^5.5.4",
"unist-util-is": "^6.0.0",
"unist-util-visit": "^5.0.0",
"vitest": "~1.2.2"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createStyles } from 'antd-style';
import { SparkleIcon } from 'lucide-react';
import { PropsWithChildren, memo } from 'react';
import { Flexbox } from 'react-layout-kit';

import { Icon } from '@/index';

const useStyles = createStyles(({ css, token, isDarkMode }) => ({
container: css`
cursor: pointer;
margin-block-start: 12px;
padding-block: 16px;
padding-inline: 16px;
color: ${token.colorText};
background: ${token.colorFillTertiary};
border-radius: 8px;
&:hover {
background: ${isDarkMode ? '' : token.colorFillSecondary};
}
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-size: 12px;
text-overflow: ellipsis;
`,
}));

const Render = memo<PropsWithChildren>(({ children }) => {
const { styles, theme } = useStyles();

return (
<Flexbox className={styles.container} gap={16} width={'100%'}>
<Flexbox distribution={'space-between'} flex={1} horizontal>
<Flexbox gap={8} horizontal>
<Icon color={theme.purple} icon={SparkleIcon} /> Artifact
</Flexbox>
</Flexbox>
{children}
</Flexbox>
);
});

export default Render;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from './Component';
import rehypePlugin from './rehypePlugin';

const AntArtifactElement = {
Component,
rehypePlugin,
tag: 'antArtifact',
};

export default AntArtifactElement;
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { SKIP, visit } from 'unist-util-visit';

function rehypeAntArtifact() {
return (tree: any) => {
visit(tree, 'element', (node, index, parent) => {
if (node.tagName === 'p' && node.children.length > 0) {
const firstChild = node.children[0];
if (firstChild.type === 'raw' && firstChild.value.startsWith('<antArtifact')) {
// 提取 antArtifact 的属性
const attributes = {};
const attributeRegex = /(\w+)="([^"]*)"/g;
let match;
while ((match = attributeRegex.exec(firstChild.value)) !== null) {
// @ts-ignore
attributes[match[1]] = match[2];
}

// 创建新的 antArtifact 节点
const newNode = {
children: [
{
type: 'text',
value: node.children
.slice(1, -1)
.map((child: any) => {
if (child.type === 'raw') {
return child.value;
} else if (child.type === 'text') {
return child.value;
} else if (child.type === 'element' && child.tagName === 'a') {
return child.children[0].value;
}
return '';
})
.join('')
.trim(),
},
],
properties: attributes,
tagName: 'antArtifact',
type: 'element',
};

// 替换原来的 p 节点
parent.children.splice(index, 1, newNode);
// @ts-ignore
return [SKIP, index];
}
}
});
};
}

export default rehypeAntArtifact;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createStyles } from 'antd-style';
import { ChevronDown, ChevronRight, SparkleIcon } from 'lucide-react';
import { PropsWithChildren, memo, useState } from 'react';
import { Flexbox } from 'react-layout-kit';

import { Icon } from '@/index';

const useStyles = createStyles(({ css, token, isDarkMode }) => ({
container: css`
cursor: pointer;
padding-block: 8px;
padding-inline: 12px;
padding-inline-end: 12px;
color: ${token.colorText};
background: ${token.colorFillTertiary};
border-radius: 8px;
&:hover {
background: ${isDarkMode ? '' : token.colorFillSecondary};
}
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-size: 12px;
text-overflow: ellipsis;
`,
}));

const Render = memo<PropsWithChildren>(({ children }) => {
const { styles, theme } = useStyles();

const [showDetail, setShowDetail] = useState(false);

return (
<Flexbox
className={styles.container}
gap={16}
onClick={() => {
setShowDetail(!showDetail);
}}
width={'100%'}
>
<Flexbox distribution={'space-between'} flex={1} horizontal>
<Flexbox gap={8} horizontal>
<Icon color={theme.purple} icon={SparkleIcon} /> Thinking...
</Flexbox>
<Icon icon={showDetail ? ChevronDown : ChevronRight} />
</Flexbox>
{showDetail && children}
</Flexbox>
);
});

export default Render;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Component from './Component';
import rehypePlugin from './rehypePlugin';

const AntThinkingElement = {
Component,
rehypePlugin,
tag: 'antThinking',
};

export default AntThinkingElement;
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { describe, expect, it } from 'vitest';

import rehypePlugin from './rehypePlugin';

describe('rehypePlugin', () => {
it('should transform <antThinking> tags within paragraphs', () => {
const tree = {
children: [
{
children: [
{ type: 'text', value: 'Before ' },
{ type: 'raw', value: '<antThinking>' },
{ type: 'text', value: 'Thinking content' },
{ type: 'raw', value: '</antThinking>' },
{ type: 'text', value: ' After' },
],
tagName: 'p',
type: 'element',
},
],
type: 'root',
};

const expectedTree = {
children: [
{
children: [{ type: 'text', value: 'Thinking content' }],
properties: {},
tagName: 'antThinking',
type: 'element',
},
],
type: 'root',
};

const plugin = rehypePlugin();
plugin(tree);

expect(tree).toEqual(expectedTree);
});

it('should not transform when only opening tag is present', () => {
const tree = {
children: [
{
children: [
{ type: 'text', value: 'Before ' },
{ type: 'raw', value: '<antThinking>' },
{ type: 'text', value: 'Thinking content' },
],
tagName: 'p',
type: 'element',
},
],
type: 'root',
};

const originalTree = structuredClone(tree);

const plugin = rehypePlugin();
plugin(tree);

expect(tree).toEqual(originalTree);
});

it('should not transform when only closing tag is present', () => {
const tree = {
children: [
{
children: [
{ type: 'text', value: 'Thinking content' },
{ type: 'raw', value: '</antThinking>' },
{ type: 'text', value: ' After' },
],
tagName: 'p',
type: 'element',
},
],
type: 'root',
};

const originalTree = structuredClone(tree);

const plugin = rehypePlugin();
plugin(tree);

expect(tree).toEqual(originalTree);
});

it('should not transform when tags are in wrong order', () => {
const tree = {
children: [
{
children: [
{ type: 'raw', value: '</antThinking>' },
{ type: 'text', value: 'Thinking content' },
{ type: 'raw', value: '<antThinking>' },
],
tagName: 'p',
type: 'element',
},
],
type: 'root',
};

const originalTree = structuredClone(tree);

const plugin = rehypePlugin();
plugin(tree);

expect(tree).toEqual(originalTree);
});

it('should handle multiple paragraphs and transformations', () => {
const tree = {
children: [
{
children: [{ type: 'text', value: 'Normal paragraph' }],
tagName: 'p',
type: 'element',
},
{
children: [
{ type: 'raw', value: '<antThinking>' },
{ type: 'text', value: 'First thinking' },
{ type: 'raw', value: '</antThinking>' },
],
tagName: 'p',
type: 'element',
},
{
children: [
{ type: 'raw', value: '<antThinking>' },
{ type: 'text', value: 'Second thinking' },
{ type: 'raw', value: '</antThinking>' },
],
tagName: 'p',
type: 'element',
},
],
type: 'root',
};

const expectedTree = {
children: [
{
children: [{ type: 'text', value: 'Normal paragraph' }],
tagName: 'p',
type: 'element',
},
{
children: [{ type: 'text', value: 'First thinking' }],
properties: {},
tagName: 'antThinking',
type: 'element',
},
{
children: [{ type: 'text', value: 'Second thinking' }],
properties: {},
tagName: 'antThinking',
type: 'element',
},
],
type: 'root',
};

const plugin = rehypePlugin();
plugin(tree);

expect(tree).toEqual(expectedTree);
});
});
Loading

0 comments on commit 2b15328

Please sign in to comment.