对话 Think · Sources
components 可以把自定义标签交给已经有的对话组件。think 对 Think,引用列表对 Sources,思考链对 ThoughtChain。
Think
Check the constraint, then answer.
The answer is 42.
Think
把 think 标签渲染成 Think。
ts
<script setup lang="tsx">
defineOptions({ name: 'AXXMarkdownThinkV2' });
import { Think } from 'ant-design-x-vue';
import { XMarkdown } from '@ant-design-x-vue/x-markdown';
const content = '<think>Check the constraint, then answer.</think>\n\nThe answer is 42.';
defineRender(() => (
<XMarkdown content={content} components={{ think: Think }} />
));
</script>
隐藏源代码
Sources
Sources
把正文里的 <sup> 映射成内联 Sources。
ts
<script setup lang="tsx">
defineOptions({ name: 'AXXMarkdownSourcesV2' });
import { onUnmounted, ref, watchEffect } from 'vue';
import { Bubble, Sources } from 'ant-design-x-vue';
import { XMarkdown, type ComponentProps } from '@ant-design-x-vue/x-markdown';
import { Button, Flex } from 'ant-design-vue';
import { textOf } from '../_shared/markdown-code';
const text = `Ant Financial has a large number of enterprise-level products.<sup>1</sup> With complex scenarios, designers and developers often need to respond fast due to frequent changes in product demands and concurrent R & D workflow.<sup>2</sup> Many similar contents exist in the process. Through abstraction, we could obtain some stable and highly reusable components and pages.<sup>3</sup>`;
const items = [
{
title: '1. Data source',
key: 1,
url: 'https://x.ant.design/components/overview',
description:
'Artificial Intelligence, often abbreviated as AI, is a broad branch of computer science concerned with building smart machines capable of performing tasks that typically require human intelligence.',
},
{
title: '2. Data source',
key: 2,
url: 'https://x.ant.design/components/overview',
},
{
title: '3. Data source',
key: 3,
url: 'https://x.ant.design/components/overview',
},
];
const SupSource = (props: ComponentProps) => {
const title = textOf(props.children) || '0';
return (
<Sources
activeKey={parseInt(title, 10)}
title={title}
items={items}
inline
/>
);
};
const index = ref(0);
const isServer = import.meta.env.SSR;
watchEffect((onCleanup) => {
if (isServer || index.value >= text.length) return;
const timerId = setTimeout(() => {
index.value = Math.min(text.length, index.value + 5);
}, 20);
onCleanup(() => clearTimeout(timerId));
});
onUnmounted(() => {
index.value = text.length;
});
defineRender(() => (
<Flex vertical gap="small">
<Button style={{ alignSelf: 'flex-end' }} onClick={() => (index.value = 0)}>
Re-Render
</Button>
<Bubble
content={text.slice(0, index.value)}
contentRender={(content) => (
<XMarkdown
content={content as string}
components={{ sup: SupSource }}
paragraphTag="div"
/>
)}
variant="outlined"
/>
</Flex>
));
</script>
隐藏源代码
ThoughtChain

Thought Chain Item - 1
status: success
Thought Chain Item - 2
status: error
ThoughtChain
自定义 thought 标签解析 JSON,再交给 ThoughtChain。
ts
<script setup lang="tsx">
defineOptions({ name: 'AXXMarkdownThoughtChainV2' });
import { CheckCircleOutlined, InfoCircleOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import type { ThoughtChainItemType } from 'ant-design-x-vue';
import { Bubble, ThoughtChain } from 'ant-design-x-vue';
import { XMarkdown, type ComponentProps } from '@ant-design-x-vue/x-markdown';
import { Avatar, Button, Card, Flex, Spin } from 'ant-design-vue';
import { defineComponent, ref, type PropType } from 'vue';
import { textOf } from '../_shared/markdown-code';
function getStatusIcon(status: ThoughtChainItemType['status']) {
switch (status) {
case 'success':
return <CheckCircleOutlined />;
case 'error':
return <InfoCircleOutlined />;
case 'loading':
return <LoadingOutlined />;
default:
return undefined;
}
}
const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
const ThoughtPanel = defineComponent({
name: 'XMarkdownThoughtPanel',
props: {
items: { type: Array as PropType<ThoughtChainItemType[]>, required: true },
},
setup(props) {
const items = ref<ThoughtChainItemType[]>([...props.items]);
const loading = ref(false);
const onClick = async () => {
loading.value = true;
items.value = [
...items.value,
{
title: `Thought Chain Item - ${items.value.length + 1}`,
status: 'loading',
icon: getStatusIcon('loading'),
description: 'status: loading',
},
];
for (const status of ['error', 'loading', 'success'] as const) {
await delay(800);
const next = [...items.value];
const last = { ...next[next.length - 1] };
last.status = status;
last.icon = getStatusIcon(status);
last.description = `status: ${status}`;
next[next.length - 1] = last;
items.value = next;
}
loading.value = false;
};
return () => (
<Card style={{ width: 500 }}>
<Button onClick={onClick} style={{ marginBottom: 16 }} loading={loading.value}>
{loading.value ? 'Running' : 'Run Next'}
</Button>
<ThoughtChain items={items.value} />
</Card>
);
},
});
const text = `<div><thought>[{"title":"Thought Chain Item - 1","status":"success","description":"status: success"},{"title":"Thought Chain Item - 2","status":"error","description":"status: error"}]</thought></div>`;
const ThoughtComponent = (props: ComponentProps) => {
if (props.streamStatus === 'loading') {
return <Spin />;
}
const raw = textOf(props.children);
if (!raw) return null;
const data = JSON.parse(raw) as ThoughtChainItemType[];
const withIcons = data.map((item) => ({
...item,
icon: getStatusIcon(item.status),
}));
return <ThoughtPanel items={withIcons} />;
};
defineRender(() => (
<Flex vertical gap="small">
<Bubble
content={text}
contentRender={(content) => (
<XMarkdown
content={content as string}
components={{ thought: ThoughtComponent }}
/>
)}
avatar={
<Avatar src="https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*2Q5LRJ3LFPUAAAAAAAAAAAAADmJ7AQ/fmt.webp" />
}
variant="outlined"
/>
</Flex>
));
</script>
隐藏源代码