Chat Think · Sources
components can hand over custom tags to existing conversation components. think corresponds to Think, citation lists correspond to Sources, ThoughtChain corresponds to ThoughtChain.
Think
Check the constraint, then answer.
The answer is 42.
Think
renders the think tag as 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>
Hide source
Sources
Sources
maps <sup> in the main text to inline 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>
Hide source
ThoughtChain

Thought Chain Item - 1
status: success
Thought Chain Item - 2
status: error
ThoughtChain
parses the custom thought tag as JSON and then hands it to 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>
Hide source