使用场景
Chat 组件可用于普通会话,AI 会话等场景。
对话内容渲染基于 MarkdownRender 组件,支持 Markdown 和 MDX (注:Chat 中的 MarkdownRender 的默认 format 模式是 md,如果需要使用 MDX 格式,可通过 markdownRenderProps API 设置),可实现图片,表格,链接,加粗,代码区等常用富文本功能。更加复杂的定制化需求通过作用域插槽实现。代码区的高亮语言需先通过 CodeHighlight 的 CodeHighlight.register 注册。
代码演示
如何引入
<script setup lang="ts">
import { Chat } from '@fibrous/semi-ai-vue';
</script>
基本用法
通过 v-model:chats 绑定对话列表,@messageSend 监听发送实现基础对话显示和交互。v-model:chats 在列表变化时先触发 update:chats,再触发 chatsChange;只用单向的 :chats 而不回写时,列表保持不变。
附件支持通过点击上传按钮,输入框粘贴,拖拽文件至 Chat 区域上传。通过 uploadProps 设置上传参数,详情参考 Upload。
上传按钮的提示文案通过 #uploadTip 插槽设置,Tooltip 的其他属性通过 uploadTipProps 设置(不含 content),详情参考 Tooltip。
对话是多方参与,多轮交互的场景。可通过 roleConfig 传入角色信息(包括名称,头像等),具体参数细节 RoleConfig。
使用 align 属性可以设置对话的布局,支持左右分布(leftRight, 默认)和左对齐(leftAlign)。
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage, type ChatProps } from '@fibrous/semi-ai-vue';
import { Radio, RadioGroup, type RadioValue } from '@fibrous/semi-ui-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
margin: '8px 16px',
height: '550px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const message = ref<ChatMessage[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: '给一个 Semi Design 的 Button 组件的使用示例',
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: "以下是一个 Semi 代码的使用示例:\n```jsx \nimport React from 'react';\nimport { Button } from '@fibrous/semi-ui-react';\n\nconst MyComponent = () => {\n return (\n <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n```\n",
},
]);
const mode = ref<RadioValue | undefined>('bubble');
const align = ref<RadioValue | undefined>('leftRight');
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
createAt: Date.now(),
content: '这是一条 mock 回复信息',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
const onMessageReset = () => {
setTimeout(() => {
const lastMessage = message.value[message.value.length - 1];
const newLastMessage: ChatMessage = {
...lastMessage,
status: 'complete',
content: 'This is a mock reset message.',
};
message.value = [...message.value.slice(0, -1), newLastMessage];
}, 200);
};
</script>
<template>
<span style="display: flex; flex-direction: column; row-gap: 8px">
<span style="display: flex; align-items: center; column-gap: 10px">
模式
<RadioGroup v-model="mode" type="button">
<Radio value="bubble">气泡</Radio>
<Radio value="noBubble">非气泡</Radio>
<Radio value="userBubble">用户会话气泡</Radio>
</RadioGroup>
</span>
<span style="display: flex; align-items: center; column-gap: 10px">
会话布局方式
<RadioGroup v-model="align" type="button">
<Radio value="leftRight">左右分布</Radio>
<Radio value="leftAlign">左对齐</Radio>
</RadioGroup>
</span>
</span>
<Chat :key="`${align}${mode}`" v-model:chats="message" :align="align as ChatProps['align']" :mode="mode as ChatProps['mode']" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageReset="onMessageReset" @messageSend="onMessageSend">
<template #uploadTip>自定义上传按钮提示信息</template>
</Chat>
</template>消息状态
chats 类型为 Message[], Message 包含对话的各种信息,如角色(role)、内容(content)、附件(attachment)、状态(status)、唯一标识(id)、创建时间(createAt)等,具体见 Message。其中 status 不同,会话样式不同。
请求成功
请求错误
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage as Message } from '@fibrous/semi-ai-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const messages = ref<Message[]>([
{
role: 'assistant',
id: '1',
createAt: 1715676751919,
content: '请求成功',
},
{
id: 'loading',
role: 'assistant',
status: 'loading',
},
{
role: 'assistant',
id: 'error',
content: '请求错误',
status: 'error',
},
]);
const onMessageSend = () => {
const newAssistantMessage: Message = {
role: 'assistant',
id: getId(),
createAt: Date.now(),
content: '这是一条 mock 回复信息',
};
setTimeout(() => {
messages.value = [...messages.value, newAssistantMessage];
}, 200);
};
</script>
<template>
<Chat v-model:chats="messages" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend" />
</template>动态更新数据
对于后台返回 Serve Side Event 数据情况,可将获取到的数据用于更新 chats,对话内容将实时更新。
showStopGenerate 参数可用于设置是否展示停止生成按钮,默认为 false。 可以在 @stopGenerator 中处理停止生成逻辑。
Hello, I'm your AI assistant.
介绍一下 Semi design
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户[1]。
Semi Design的特点包括:
- 设计简洁、现代化。
- 提供主题方案,可深度样式定制。
- 提供明暗色两套模式,切换方便。
- 国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
- 采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
Learn more:
代码
<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue';
import { Chat, type ChatMessage as Message } from '@fibrous/semi-ai-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '600px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const messages = ref<Message[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: '介绍一下 Semi design',
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: `
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。作为一个全面、易用、优质的现代应用UI解决方案,Semi Design从字节跳动各业务线的复杂场景中提炼而来,目前已经支撑了近千个平台产品,服务了内外部超过10万用户[[1]](https://semi.design/zh-CN/start/introduction)。
Semi Design的特点包括:
1. 设计简洁、现代化。
2. 提供主题方案,可深度样式定制。
3. 提供明暗色两套模式,切换方便。
4. 国际化,覆盖了简/繁体中文、英语、日语、韩语、葡萄牙语等20+种语言,日期时间组件提供全球时区支持,全部组件可自动适配阿拉伯文RTL布局。
5. 采用 Foundation 和 Adapter 跨框架技术方案,方便扩展。
---
Learn more:
1. [Introduction 介绍 - Semi Design](https://semi.design/zh-CN/start/introduction)
2. [Getting Started 快速开始 - Semi Design](https://semi.design/zh-CN/start/getting-started)
3. [Semi D2C 设计稿转代码的演进之路 - 知乎](https://zhuanlan.zhihu.com/p/667189184)
`,
},
]);
let timer: ReturnType<typeof setInterval> | null = null;
const stopTimer = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
};
const replaceLast = (update: (last: Message) => Message) => {
messages.value = [...messages.value.slice(0, -1), update(messages.value[messages.value.length - 1])];
};
const generateMockResponse = (content: string) => {
timer = setInterval(() => {
const last = messages.value[messages.value.length - 1];
if (last.status === 'loading') {
replaceLast((m) => ({ ...m, content: `mock Response for ${content} \n`, status: 'incomplete' }));
} else if (last.status === 'incomplete') {
const next = `${last.content} mock stream message`;
if ((last.content?.length ?? 0) > 200) {
stopTimer();
replaceLast((m) => ({ ...m, content: next, status: 'complete' }));
} else {
replaceLast((m) => ({ ...m, content: next }));
}
}
}, 400);
};
const onMessageSend = (content: string) => {
messages.value = [...messages.value, { role: 'assistant', status: 'loading', createAt: Date.now(), id: getId() }];
generateMockResponse(content);
};
const onStopGenerator = () => {
if (!timer) {
return;
}
stopTimer();
const last = messages.value[messages.value.length - 1];
if (last.status && last.status !== 'complete') {
replaceLast((m) => ({ ...m, status: 'complete' }));
}
};
onBeforeUnmount(stopTimer);
</script>
<template>
<Chat v-model:chats="messages" :roleConfig="roleInfo" showStopGenerate :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend" @stopGenerator="onStopGenerator" />
</template>清除上下文
通过 showClearContext 可以开启在输入框中显示清除上下文按钮,默认为 false。也可以通过模板 ref 调用 clearContext 方法清除上下文。
Hello, I'm your AI assistant.
介绍一下 semi design
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage } from '@fibrous/semi-ai-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
margin: '8px 16px',
height: '550px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const message = ref<ChatMessage[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: '介绍一下 semi design',
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
},
]);
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
createAt: Date.now(),
content: '这是一条 mock 回复信息',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
const onMessageReset = () => {
setTimeout(() => {
const lastMessage = message.value[message.value.length - 1];
const newLastMessage: ChatMessage = {
...lastMessage,
status: 'complete',
content: 'This is a mock reset message.',
};
message.value = [...message.value.slice(0, -1), newLastMessage];
}, 200);
};
</script>
<template>
<Chat v-model:chats="message" :roleConfig="roleInfo" showClearContext :style="commonOuterStyle" :uploadProps="uploadProps" @messageReset="onMessageReset" @messageSend="onMessageSend">
<template #uploadTip>自定义上传按钮提示信息</template>
</Chat>
</template>自定义渲染会话框
通过作用域插槽自定义会话框的各个部分,插槽参数类型如下。插槽参数里的 class 是该部分默认的类名,自定义的根元素需要绑定它。
export interface ChatTitleSlotProps {
message: ChatMessage;
role: ChatMetadata;
defaultTitle: VNode;
}
export interface ChatAvatarSlotProps {
message: ChatMessage;
role: ChatMetadata;
defaultAvatar: VNode;
}
/* 用于没有图片 URL 的角色,渲染头像里的内容 */
export interface ChatRoleAvatarSlotProps {
message: ChatMessage;
role: ChatMetadata;
}
export interface ChatContentSlotProps {
message: ChatMessage;
role: ChatMetadata;
defaultContent: VNode;
class: string;
}
/* 与默认操作栏显示的按钮一致,按钮不显示时(如消息未完成)对应的键不存在 */
export interface ChatDefaultActionNodeObj {
copyNode?: VNode;
likeNode?: VNode;
dislikeNode?: VNode;
resetNode?: VNode;
deleteNode?: VNode;
}
export interface ChatActionSlotProps {
message: ChatMessage;
defaultActions: VNode[];
defaultActionsObj: ChatDefaultActionNodeObj;
class: string;
}
/* 每个部分都是消息默认渲染的结果,已包含 avatar、title、content、action 插槽的自定义 */
export interface ChatFullChatBoxNodes {
avatar: VNode;
title: VNode;
content: VNode;
action: VNode;
}
export interface ChatFullChatBoxSlotProps {
message: ChatMessage;
role: ChatMetadata;
defaultNodes: ChatFullChatBoxNodes;
class: string;
}
自定义渲染头像和标题,可通过 #avatar 和 #title 插槽实现。提供插槽但内容为空时,对应的头像或标题不显示;roleConfig 的 avatar 只接受图片 URL,需要节点头像时使用 #roleAvatar 插槽。
Hello, I'm your AI assistant.
这张图片里有什么?
图片中是一个有卡通画像装饰的黄色背包。
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage } from '@fibrous/semi-ai-vue';
import { Avatar, Radio, RadioGroup, type RadioValue } from '@fibrous/semi-ui-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const formatTime = (createAt = Date.now()) => {
const date = new Date(createAt);
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
};
const message = ref<ChatMessage[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: [
{
type: 'text',
text: '这张图片里有什么?',
},
{
type: 'image_url',
image_url: {
url: '/assets/root-web-sites/edit-bag.jpeg',
},
},
],
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: '图片中是一个有卡通画像装饰的黄色背包。',
},
]);
const title = ref<RadioValue | undefined>('null');
const avatar = ref<RadioValue | undefined>('null');
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
content: 'This is a mock response',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
</script>
<template>
<span style="display: flex; flex-direction: column; row-gap: 8px; margin-bottom: 5px">
<span style="display: flex; align-items: center; column-gap: 10px">
头像渲染模式
<RadioGroup v-model="avatar" type="button">
<Radio value="default">默认头像</Radio>
<Radio value="null">无头像</Radio>
<Radio value="custom">自定义头像</Radio>
</RadioGroup>
</span>
<span style="display: flex; align-items: center; column-gap: 10px">
标题渲染模式
<RadioGroup v-model="title" type="button">
<Radio value="default">默认标题</Radio>
<Radio value="null">无标题</Radio>
<Radio value="custom">自定义标题</Radio>
</RadioGroup>
</span>
</span>
<Chat :key="`${avatar}${title}`" v-model:chats="message" class="component-chat-demo-custom-render" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend">
<template v-if="avatar === 'custom'" #avatar="{ role }">
<Avatar shape="square" size="extra-small" style="flex-shrink: 0">{{ role.name }}</Avatar>
</template>
<template v-else-if="avatar === 'null'" #avatar />
<template v-if="title === 'custom'" #title="{ role, message: item }">
<span class="title">
{{ role.name }}
<span class="time">{{ formatTime(item.createAt) }}</span>
</span>
</template>
<template v-else-if="title === 'null'" #title />
</Chat>
</template>鼠标移动到会话上,即可显示会话操作区,通过 #action 插槽自定义渲染操作区
Hello, I'm your AI assistant.
介绍一下 semi design
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage } from '@fibrous/semi-ai-vue';
import { IconForward, IconMoreStroked } from '@fibrous/semi-icons-vue';
import { Button, Dropdown } from '@fibrous/semi-ui-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const message = ref<ChatMessage[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: '介绍一下 semi design',
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
},
]);
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
content: 'This is a mock response',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
</script>
<template>
<Chat v-model:chats="message" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend">
<template #action="{ defaultActions, class: actionClass }">
<span :class="actionClass">
<component :is="action" v-for="(action, index) in defaultActions" :key="index" />
<Dropdown position="top" trigger="click">
<Button aria-label="更多" class="semi-chat-chatBox-action-btn" theme="borderless" type="tertiary">
<template #icon>
<IconMoreStroked />
</template>
</Button>
<template #content>
<Dropdown.Menu>
<Dropdown.Item>
<template #icon>
<IconForward />
</template>
分享
</Dropdown.Item>
</Dropdown.Menu>
</template>
</Dropdown>
</span>
</template>
</Chat>
</template>通过 #content 插槽自定义内容区域
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage } from '@fibrous/semi-ai-vue';
import { IconChevronUp } from '@fibrous/semi-icons-vue';
import { Avatar, AvatarGroup, MarkdownRender } from '@fibrous/semi-ui-vue';
interface Source {
avatar: string;
url: string;
title: string;
subTitle: string;
content: string;
}
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '500px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const sourcesOf = (item: ChatMessage): Source[] | undefined => item.source;
const message = ref<ChatMessage[]>([
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。它作为全面、易用、优质的现代应用 UI 解决方案,从字节跳动各业务线的复杂场景提炼而来,支撑近千计平台产品,服务内外部 10 万+ 用户。',
source: [
{
avatar: '/assets/placeholder/logo.svg',
url: '/zh-CN/start/introduction',
title: 'semi Design',
subTitle: 'Semi design website',
content: 'Semi Design 是由抖音前端团队,MED 产品设计团队设计、开发并维护的设计系统。',
},
{
avatar: '/assets/placeholder/logo.svg',
url: '/dsm/landing',
subTitle: 'Semi DSM website',
title: 'Semi 设计系统',
content: '从 Semi Design,到 Any Design 快速定义你的设计系统,并应用在设计稿和代码中',
},
{
avatar: '/assets/placeholder/logo.svg',
url: '/code/zh-CN/start/introduction',
subTitle: 'Semi D2C website',
title: '设计稿转代码',
content: 'Semi 设计稿转代码(Semi Design to Code,或简称 Semi D2C),是由抖音前端 Semi Design 团队推出的全新的提效工具',
},
],
},
]);
// Only the first message carries sources, so one open state serves the whole list
const collapsed = ref(true);
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
content: 'This is a mock response',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
const buttonReset = {
border: 'none',
background: 'var(--semi-color-tertiary-light-hover)',
borderRadius: '16px',
fontFamily: 'inherit',
lineHeight: 'inherit',
};
</script>
<template>
<Chat v-model:chats="message" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend">
<template #content="{ message: item, class: contentClass }">
<div :class="contentClass">
<div
v-if="sourcesOf(item)"
:style="{
transition: collapsed ? 'height 0.4s ease, width 0.4s ease' : 'height 0.4s ease',
height: collapsed ? '30px' : '200px',
width: collapsed ? '190px' : '100%',
background: 'var(--semi-color-tertiary-light-hover)',
borderRadius: '16px',
boxSizing: 'border-box',
marginBottom: '10px',
}"
>
<button
v-show="collapsed"
:style="{
...buttonReset,
display: 'flex',
width: 'fit-content',
columnGap: '10px',
padding: '5px 10px',
cursor: 'pointer',
fontSize: '14px',
color: 'var(--semi-color-text-1)',
}"
type="button"
@click="collapsed = false"
>
<span>基于{{ sourcesOf(item)?.length }}个搜索来源</span>
<AvatarGroup size="extra-extra-small">
<Avatar v-for="(s, index) in sourcesOf(item)" :key="index" alt="" :src="s.avatar" />
</AvatarGroup>
</button>
<button
v-show="!collapsed"
:style="{
...buttonReset,
width: '100%',
height: '100%',
boxSizing: 'border-box',
display: 'flex',
flexDirection: 'column',
padding: '12px',
textAlign: 'start',
fontSize: 'inherit',
color: 'inherit',
}"
type="button"
@click="collapsed = true"
>
<span style="display: flex; align-items: center; justify-content: space-between; padding: 5px 10px; column-gap: 10px; color: var(--semi-color-text-1)">
<span style="font-size: 14px; font-weight: 500">Source</span>
<IconChevronUp />
</span>
<span style="display: flex; flex-wrap: wrap; gap: 10px; overflow: scroll; padding: 5px 10px">
<span v-for="s in sourcesOf(item)" :key="s.url" style="display: flex; flex-direction: column; row-gap: 5px; flex-basis: 150px; flex-grow: 1; border: 1px solid var(--semi-color-border); border-radius: 12px; padding: 12px; font-size: 12px">
<span style="display: flex; column-gap: 5px; align-items: center">
<Avatar alt="" shape="square" :src="s.avatar" style="width: 16px; height: 16px; flex-shrink: 0" />
<span style="color: var(--semi-color-text-2); text-overflow: ellipsis">
{{ s.title }}
</span>
</span>
<span style="color: var(--semi-color-primary); font-size: 12px">{{ s.subTitle }}</span>
<span style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; text-overflow: ellipsis; overflow: hidden; color: var(--semi-color-text-2)">
{{ s.content }}
</span>
</span>
</span>
</button>
</div>
<MarkdownRender :raw="typeof item.content === 'string' ? item.content : ''" />
</div>
</template>
</Chat>
</template>使用 #fullChatBox 插槽自定义渲染整个会话框
Hello, I'm your AI assistant.
介绍一下 semi design
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage } from '@fibrous/semi-ai-vue';
import { Avatar } from '@fibrous/semi-ui-vue';
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
const titleStyle = {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
columnGap: '10px',
padding: '5px 0px',
width: 'fit-content',
};
let id = 0;
const getId = () => `id-${id++}`;
const message = ref<ChatMessage[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createAt: 1715676751919,
content: '介绍一下 semi design',
},
{
role: 'assistant',
id: '3',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
},
]);
const onMessageSend = () => {
const newAssistantMessage: ChatMessage = {
role: 'assistant',
id: getId(),
content: 'This is a mock response',
};
setTimeout(() => {
message.value = [...message.value, newAssistantMessage];
}, 200);
};
</script>
<template>
<Chat v-model:chats="message" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend">
<template #fullChatBox="{ role, message: item, defaultNodes, class: boxClass }">
<div :class="boxClass">
<div
:style="{
display: 'flex',
flexDirection: 'column',
rowGap: '4px',
alignItems: item.role === 'user' ? 'end' : undefined,
}"
>
<span v-if="item.role !== 'user'" :style="titleStyle">
<Avatar alt="" shape="square" size="extra-small" :src="role.avatar" />
<component :is="defaultNodes.title" />
</span>
<div style="width: fit-content">
<component :is="defaultNodes.content" />
</div>
<component :is="defaultNodes.action" />
</div>
</div>
</template>
</Chat>
</template>自定义渲染输入框
可通过 #inputArea 插槽自定义渲染输入框,插槽参数如下
export interface ChatInputAreaSlotProps {
/* 默认节点 */
defaultNode: VNode;
/* 清除上下文按钮 */
clearContextNode: VNode;
/* 上传按钮,上传方式全部关闭时为 null */
uploadNode: VNode | null;
/* 文本输入框 */
inputNode: VNode;
/* 发送按钮 */
sendNode: VNode;
/* 如果自定义输入框,发送消息时需调用 */
onSend: (content?: string, attachment?: FileItem[]) => void;
/* 如果自定义清除上下文按钮,点击清除上下文时需调用 */
onClear: () => void;
/* 点击触发聚焦文本输入框的处理函数 */
onClick: () => void;
}
各部分节点的使用示例如下
<template>
<Chat v-model:chats="chats">
<template #inputArea="{ uploadNode, inputNode, sendNode, onClick }">
<div
style="
margin: 8px 16px;
display: flex;
flex-direction: row;
align-items: flex-end;
border-radius: 16px;
padding: 10px;
border: 1px solid var(--semi-color-border);
"
@click="onClick"
>
<component :is="uploadNode" />
<component :is="inputNode" />
<component :is="sendNode" />
</div>
</template>
</Chat>
</template>
其他使用示例如下
Hello, I'm your AI assistant.
代码
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import { Chat, type ChatMessage as Message } from '@fibrous/semi-ai-vue';
import { IconUpload } from '@fibrous/semi-icons-vue';
import { Button, Form, FormInput, FormUpload, type FileItem, type FormApi } from '@fibrous/semi-ui-vue';
interface InputValues {
name?: string;
file?: FileItem[];
}
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '500px',
};
const inputStyle = {
display: 'flex',
flexDirection: 'column',
border: '1px solid var(--semi-color-border)',
margin: '8px 16px',
borderRadius: '8px',
padding: '8px',
} as const;
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const messages = ref<Message[]>([
{
role: 'system',
id: '1',
createAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
]);
type Send = (content?: string, attachment?: FileItem[]) => void;
const formApi = shallowRef<FormApi<InputValues>>();
const setFormApi = (api: FormApi<InputValues>) => {
formApi.value = api;
};
const onMessageSend = () => {
const newAssistantMessage: Message = {
role: 'assistant',
id: getId(),
content: `This is a mock response`,
};
setTimeout(() => {
messages.value = [...messages.value, newAssistantMessage];
}, 200);
};
const submit = (onSend: Send) => {
const api = formApi.value;
if (!api) {
return;
}
const values = api.getValues();
if (values.name?.length || values.file?.length) {
onSend(values.name, values.file);
api.reset();
}
};
</script>
<template>
<Chat v-model:chats="messages" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @messageSend="onMessageSend">
<template #inputArea="area">
<div :style="inputStyle">
<Form @getFormApi="setFormApi">
<strong>输入信息</strong>
<FormInput field="name" label="名称(Input)" :style="{ width: '250px' }" trigger="blur" />
<FormUpload :action="uploadProps.action" field="file" label="文档">
<Button theme="light">
<template #icon>
<IconUpload />
</template>
点击上传
</Button>
</FormUpload>
</Form>
<Button :style="{ width: 'fit-content' }" @click="submit(area.onSend)">提交</Button>
</div>
</template>
</Chat>
</template>提示信息
通过 hints 可设置提示区域内容, 点击提示内容后,提示内容将成为新的用户输入内容,并触发 hintClick 事件。
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage as Message } from '@fibrous/semi-ai-vue';
const hintsExample = ['告诉我更多', 'Semi Design 的组件有哪些?', '我能够通过 DSM 定制自己的主题吗?'];
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const messages = ref<Message[]>([
{
role: 'assistant',
id: '1',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
]);
const hints = ref(hintsExample);
const onMessageSend = () => {
const newAssistantMessage: Message = {
role: 'assistant',
id: getId(),
createAt: Date.now(),
content: '这是一条 mock 回复信息',
};
setTimeout(() => {
messages.value = [...messages.value, newAssistantMessage];
}, 200);
};
</script>
<template>
<Chat v-model:chats="messages" :hints="hints" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @clear="hints = []" @hintClick="hints = []" @messageSend="onMessageSend" />
</template>自定义提示信息渲染
通过 #hintBox 插槽自定义提示区域内容, 参数如下
export interface ChatHintBoxSlotProps {
content: string;
index: number;
onHintClick: () => void;
}
使用示例如下:
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Chat, type ChatMessage as Message } from '@fibrous/semi-ai-vue';
import { IconArrowRight } from '@fibrous/semi-icons-vue';
const hintsExample = ['告诉我更多', 'Semi Design 的组件有哪些?', '我能够通过 DSM 定制自己的主题吗?'];
const roleInfo = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const commonOuterStyle = {
border: '1px solid var(--semi-color-border)',
borderRadius: '16px',
height: '400px',
};
const commonHintStyle = {
border: '1px solid var(--semi-color-border)',
padding: '10px',
borderRadius: '10px',
background: 'none',
color: 'var( --semi-color-text-1)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
textAlign: 'left',
cursor: 'pointer',
fontFamily: 'inherit',
fontSize: '14px',
lineHeight: 'inherit',
} as const;
const uploadProps = { action: 'https://api.semi.design/upload' };
let id = 0;
const getId = () => `id-${id++}`;
const messages = ref<Message[]>([
{
role: 'assistant',
id: '1',
createAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
]);
const hints = ref(hintsExample);
const onMessageSend = () => {
const newAssistantMessage: Message = {
role: 'assistant',
id: getId(),
createAt: Date.now(),
content: '这是一条 mock 回复信息',
};
setTimeout(() => {
messages.value = [...messages.value, newAssistantMessage];
}, 200);
hints.value = [];
};
</script>
<template>
<Chat v-model:chats="messages" :hints="hints" :roleConfig="roleInfo" :style="commonOuterStyle" :uploadProps="uploadProps" @clear="hints = []" @hintClick="hints = []" @messageSend="onMessageSend">
<template #hintBox="{ content, onHintClick }">
<button :style="commonHintStyle" type="button" @click="onHintClick">
{{ content }}
<IconArrowRight :style="{ marginLeft: '10px' }" />
</button>
</template>
</Chat>
</template>API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| chats | 对话列表,配合 v-model:chats 使用。只有整体替换数组才视为变化,就地 push 不会触发跟随滚动。 | Message[] | semiGlobalDefault("Chat", "chats") |
| align | 对话布局方式,可选 leftRight(左右分布)或 leftAlign(左对齐)。 | ChatAlign | semiDefault("Chat", "align", "leftRight") |
| mode | 对话模式,可选 bubble、noBubble 或 userBubble。 | ChatMode | semiDefault("Chat", "mode", "bubble") |
| roleConfig | 角色信息,键为消息的 role。avatar 只接受图片 URL,节点头像使用 roleAvatar 插槽;color 传给 Avatar。 | ChatRoleConfig | semiGlobalDefault("Chat", "roleConfig") |
| hints | 提示信息列表。点击某项后触发 hintClick,并将该项作为新的用户消息。 | string[] | semiGlobalDefault("Chat", "hints") |
| sendHotKey | 发送快捷键,可选 enter 或 shift+enter。enter 时单独按 Enter 发送,Shift+Enter 换行;shift+enter 相反。 | SendHotKey | semiDefault("Chat", "sendHotKey", "enter") |
| placeholder | 输入框占位符。 | string | semiGlobalDefault("Chat", "placeholder") |
| showStopGenerate | 是否在最后一条消息生成中时显示停止生成按钮。生成中发送按钮不可用,点击停止按钮触发 stopGenerator。 | boolean | semiDefault("Chat", "showStopGenerate", false) |
| showClearContext | 是否在输入框中显示清除上下文按钮。 | boolean | semiDefault("Chat", "showClearContext", false) |
| enableUpload | 是否启用上传。布尔值同时控制粘贴、点击和拖拽上传;传对象时分别设置 pasteUpload、clickUpload、dragUpload,未设置的项为 true。 | boolean | EnableUploadProps | semiDefault("Chat", "enableUpload", true) |
| canSend | 发送按钮是否可用。通常由内部逻辑决定,设置后以此值为准。 | boolean | semiGlobalDefault("Chat", "canSend") |
| escapeHtml | 是否转义用户消息中的 HTML 标签,防止被 Markdown 解析器当作 HTML 处理导致内容丢失。 | boolean | semiDefault("Chat", "escapeHtml", true) |
| uploadProps | 传给 Upload 的属性,fileList 与 defaultFileList 由 Chat 管理,Upload 的事件以 onXxx 形式传入。添加或移除附件时 Chat 自己处理 onChange。 | ChatUploadProps | semiGlobalDefault("Chat", "uploadProps") |
| uploadTipProps | 上传按钮提示 Tooltip 的属性,不含 content;提示文字使用 uploadTip 插槽。 | TypeOmit<TooltipProps, "content"> | semiGlobalDefault("Chat", "uploadTipProps") |
| markdownRenderProps | 传给消息内容所用 MarkdownRender 的属性,默认 format 为 md。raw 与 components 不可设置,自定义渲染组件使用 customMarkDownComponents。 | ChatMarkdownRenderProps | semiGlobalDefault("Chat", "markdownRenderProps") |
| customMarkDownComponents | 自定义 Markdown 渲染组件,键为元素名,传给消息内容的 MarkdownRender。 | MarkdownRenderComponents | semiGlobalDefault("Chat", "customMarkDownComponents") |
| inputBoxCls | 输入框的类名。 | string | semiGlobalDefault("Chat", "inputBoxCls") |
| inputBoxStyle | 输入框的样式。 | StyleValue | semiGlobalDefault("Chat", "inputBoxStyle") |
| hintCls | 提示区最外层的类名。 | string | semiGlobalDefault("Chat", "hintCls") |
| hintStyle | 提示区最外层的样式。 | StyleValue | semiGlobalDefault("Chat", "hintStyle") |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| update:chats | 对话列表变化时触发,供 v-model:chats 回写;先于 chatsChange。 | [chats: Message[]] |
| chatsChange | 对话列表变化时触发。 | [chats: Message[]] |
| messageSend | 发送消息时触发,参数为文本内容和附件。 | [content: string, attachment?: FileItem[] | undefined] |
| messageCopy | 复制消息时触发。 | [message: Message] |
| messageGoodFeedback | 消息正向反馈时触发。 | [message: Message] |
| messageBadFeedback | 消息负向反馈时触发。 | [message: Message] |
| messageReset | 重置消息时触发;由 resetMessage 方法触发时参数为 null。 | [message: Message | null] |
| messageDelete | 删除消息时触发。 | [message: Message] |
| clear | 清除上下文时触发。 | [] |
| hintClick | 点击提示信息时触发。 | [hint: string] |
| inputChange | 输入内容或附件变化时触发,参数为 { inputValue, attachment }。 | [props: ChatInputChangeProps] |
| stopGenerator | 点击停止生成按钮时触发。 | [event: MouseEvent] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| topSlot | 对话区域顶部的内容。 | any |
| bottomSlot | 输入框下方的内容。 | any |
| title | 自定义消息标题,参数为 message、role 和默认标题 defaultTitle。提供但内容为空时不显示标题。 | ChatTitleSlotProps |
| avatar | 自定义消息头像,参数为 message、role 和默认头像 defaultAvatar。提供但内容为空时不显示头像。 | ChatAvatarSlotProps |
| roleAvatar | 没有图片 URL 的角色的头像内容,参数为 message 和 role。 | ChatRoleAvatarSlotProps |
| content | 自定义消息内容区域,参数为 message、role、默认内容 defaultContent 和类名 class。 | ChatContentSlotProps |
| action | 自定义消息操作栏,参数为 message、默认按钮数组 defaultActions、按名称索引的 defaultActionsObj 和类名 class。 | ChatActionSlotProps |
| fullChatBox | 完全自定义整个消息框,参数为 message、role、各部分默认节点 defaultNodes 和类名 class。 | ChatFullChatBoxSlotProps |
| inputArea | 自定义输入区域,参数为 defaultNode、各部分节点(clearContextNode、uploadNode、inputNode、sendNode)以及需要自行调用的 onSend、onClear、onClick。 | ChatInputAreaSlotProps |
| hintBox | 自定义提示项,参数为 content、index 和点击处理函数 onHintClick。 | ChatHintBoxSlotProps |
| divider | 自定义清除上下文后的分割线,参数为 message。 | ChatDividerSlotProps |
| uploadButton | 替换上传按钮。 | any |
| uploadTip | 上传按钮的提示文字。 | any |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| resetMessage | 将最后一条消息重置为空内容的 loading 状态并触发 messageReset,参数为 null。 | Type() => void |
| clearContext | 在对话末尾追加分割线以清除上下文并触发 clear;末尾已是分割线时无效果。 | Type() => void |
| scrollToBottom | 滚动到最底部,animation 为 true 时平滑滚动,否则直接跳转。 | Type(animation?: boolean | undefined) => void |
| sendMessage | 发送消息,不传 attachment 时消息的 content 为字符串。 | Type(content: string, attachment?: FileItem[] | undefined) => void |
class、style 和其他未声明的属性落在根元素上,根元素带有 aria-label 或 aria-labelledby 且没有传 role 时,自动加上 role="group"。dragover、dragstart、dragend 监听同样落在根元素上,Chat 自己的拖拽上传处理先于使用方的监听执行。
RoleConfig
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| user | 用户信息 | ChatMetadata | - |
| assistant | 助手信息 | ChatMetadata | - |
| system | 系统信息 | ChatMetadata | - |
Metadata
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| name | 名称 | string | - |
| avatar | 头像图片 URL。节点头像(如 Semi 图标)使用 #roleAvatar 插槽 | string | - |
| color | 头像背景色,同 Avatar 组件的 color 参数, 支持 amber、 blue、 cyan、 green、 grey、 indigo、 light-blue、 light-green、 lime、 orange、 pink、 purple、 red、 teal、 violet、 yellow | AvatarColor | grey |
Message
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| role | 角色 | string | - |
| name | 名称 | string | - |
| id | 唯一标识 | string | - |
| content | 文本内容 | string | Content[] | - |
| parentId | 父节点id | string | - |
| createAt | 创建时间 | number | - |
| status | 消息状态,可选值为 loading | incomplete | complete | error | string | complete |
Content
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| type | 类型, 可选值text | image_url | file_url | string | - |
| text | 当类型为 text 时的内容数据 | string | - |
| image_url | 当类型为 image_url 时的内容数据 | { url: string } | - |
| file_url | 当类型为 file_url 时的内容数据 | { url: string; name: string; size: string; type: string } | - |
Accessibility
键盘和焦点
- 输入框中按
Enter或Shift+Enter发送消息,由sendHotKey决定:enter时Enter发送、Shift+Enter换行,shift+enter相反。 - 在输入框按
Shift+Tab回到上传按钮。默认的上传按钮是原生按钮,Enter或Space打开文件选择;设置了上传提示时,提示文字是该按钮的描述,按钮获得焦点时显示。 - 停止生成按钮仅在生成中出现。生成结束、按钮卸载时,如果焦点在它上面,焦点回到输入框,不会落到页面开头。
- 除最后一条已完成的消息外,消息操作栏平时隐藏,仍在
Tab顺序中,焦点进入其中的按钮时显示。删除按钮打开的确认框按Esc关闭,焦点回到删除按钮;在 Modal 中只关闭确认框,Modal 保持打开。 - 消息列表是带名称的
role="log"区域,最后一条消息生成中时带aria-busy="true",读屏在回复完成后再播报,不逐段朗读。
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $radius-chat_chatBox_content | var(--semi-border-radius-large) | 聊天框内容圆角 |
| $radius-chat_inputBox_container | 16px | 输入框容器圆角 |
| $radius-chat_attachment_img | var(--semi-border-radius-medium) | 附件图片圆角 |
| $radius-chat_attachment_file | var(--semi-border-radius-medium) | 附件文件圆角 |
| $radius-chat_hint_item | var(--semi-border-radius-large) | 提示条圆角 |
| $radius-chat_chatBox_content_code | var(--semi-border-radius-large) | 代码块圆角 |
| $radius-chat_chatBox_content_code_topSlot_copy | var(--semi-border-radius-large) | 代码块顶部复制按钮圆角 |
| $radius-chat_dropArea | 16px | 拖拽上传区域圆角 |
| $color-chat_action_content-bg | var(--semi-color-bg-0) | 返回按钮/停止生成内容按钮背景颜色 |
| $color-chat_action_content-border | var(--semi-color-border) | 返回按钮/停止生成按钮描边颜色 |
| $color-chat_divider | var(--semi-color-text-2) | 分割线文字颜色 |
| $color-chat_chatBox_title | var(--semi-color-text-0) | 聊天框标题颜色 |
| $color-chat_chatBox_action_icon | var(--semi-color-text-2) | 聊天框操作区域按钮图标颜色 |
| $color-chat_chatBox_action_icon-hover | var(--semi-color-text-0) | 聊天框操作区域按钮图标hover颜色 |
| $color-chat_chatBox_action-bg-hover | transparent | 聊天框操作区域按钮hover背景颜色 |
| $color-chat_chatBox_content_text | var(--semi-color-text-0) | 聊天框内容文字颜色 |
| $color-chat_chatBox_content_bg | var(--semi-color-fill-0) | 聊天框内容背景颜色 |
| $color-chat_chatBox_content_user-bg | var(--semi-color-primary) | 聊天框内容用户背景颜色 |
| $color-chat_chatBox_content_user-text | var(--semi-color-white) | 聊天框内容用户文字颜色 |
| $color-chat_chatBox_content_error-bg | var(--semi-color-danger-hover) | 聊天框内容错误背景颜色 |
| $color-chat_chatBox_content_error-text | var(--semi-color-white) | 聊天框内容错误文字颜色 |
| $color-chat_inputBottom_clearButton_icon | var(--semi-color-text-2) | 清空按钮图标颜色 |
| $color-chat_inputBottom_uploadButton_icon | var(--semi-color-text-0) | 上传按钮图标颜色 |
| $color-chat_inputBottom_sendButton_icon-disable | var(--semi-color-primary-disabled) | 发送按钮禁用态图标颜色 |
| $color-chat_inputBox_container-border | var(--semi-color-border) | 输入框容器边框颜色 |
| $color-chat_attachment_clear_icon | var(--semi-color-text-2) | 附件清除图标颜色 |
| $color-chat_attachment_file-bg | var(--semi-color-fill-0) | 附件文件背景颜色 |
| $color-chat_attachment_fail | var(--semi-color-danger) | 附件上传失败的提示图标颜色 |
| $color-chat_chatBox_user_attachment_file-bg | var(--semi-color-bg-0) | 用户聊天框附件文件背景颜色 |
| $color-chat_chatBox_other_attachment_file-bg | var(--semi-color-fill-2) | 聊天框附件文件背景颜色 |
| $color-chat_attachment_file_icon | var(--semi-color-text-2) | 附件文件图标颜色 |
| $color-chat_attachment_file_title | var(--semi-color-text-0) | 附件文件标题颜色 |
| $color-chat_attachment_file_metadata_text | var(--semi-color-text-2) | 附件文件元数据文字颜色 |
| $color-chat_hint_item-border | var(--semi-color-border) | 提示条边框颜色 |
| $color-chat_hint_item-bg | transparent | 提示条背景颜色 |
| $color-chat_hint_item-bg-hover | var(--semi-color-fill-0) | 提示条hover背景颜色 |
| $color-chat_hint_content_text | var(--semi-color-text-1) | 提示条文字颜色 |
| $color-chat_hint_icon | var(--semi-color-text-2) | 提示条图标颜色 |
| $color-chat_chatBox_loading-bg | var(--semi-color-text-0) | 聊天内容加载图标圆圈颜色 |
| $color-chat_chatBox_code_topSlot | var(--semi-white) | 代码块顶部字体颜色 |
| $color-chat_chatBox_code_topSlot-bg | var(--semi-grey-4) | 代码块顶部背景色 |
| $color-chat_chatBox_code_topSlot_toCopy-bg-hover | var(--semi-grey-5) | 代码块顶部复制按钮hover背景色 |
| $color-chat_chatBox_code_content | var(--semi-color-bg-0) | 代码块内容背景色 |
| $color-chat_action_content-bg-hover | var(--semi-color-tertiary-light-hover) | 返回按钮/停止生成按钮hover背景颜色 |
| $color-chat_dropArea-bg | rgb(from var(--semi-grey-2) r g b / 0.9) | 拖拽区域文字颜色 |
| $color-chat_dropArea-border | var(--semi-color-border) | 拖拽区域边框颜色 |
| $spacing-chat_paddingY | 12px | chat组件上下内边距 |
| $spacing-chat_container-paddingX | 16px | 消息框水平内边距 |
| $spacing-chat_action_content-bottom | 0 | 返回按钮/停止生成按钮底部边距 |
| $spacing-chat_chatBox-marginY | 8px | 聊天框上下外边距 |
| $spacing-chat_chatBox-columnGap | 12px | 聊天框内容列间距 |
| $spacing-chat_chatBox_action-columnGap | 10px | 聊天框操作区域按钮列间距 |
| $spacing-chat_chatBox_action-marginX | 10px | 聊天框操作区域左右外边距 |
| $spacing-chat_chatBox_action_btn-padding | 0 | 聊天框操作区域按钮内边距 |
| $spacing-chat_chatBox_content-paddingY | 8px | 聊天框内容上下内边距 |
| $spacing-chat_chatBox_content-paddingX | 12px | 聊天框内容左右内边距 |
| $spacing-chat_inputBox-paddingTop | 8px | 输入框顶部内边距 |
| $spacing-chat_inputBox-paddingBottom | 8px | 输入框底部内边距 |
| $spacing-chat_inputBox-paddingX | 16px | 输入框左右内边距 |
| $spacing-chat_inputBox_container-padding | 11px | 输入框容器内边距 |
| $spacing-chat_inputBox_inner-columnGap | 4px | 输入框容器列间距 |
| $spacing-chat_inputBox-marginY | 4px | |
| $spacing-chat_attachment-columnGap | 10px | 附件列间距 |
| $spacing-chat_attachment-RowGap | 5px | 附件行间距 |
| $spacing-chat_attachment-marginX | 12px | 附件左右外边距 |
| $spacing-chat_attachment_clear-top | 8px | 附件清除图标顶部间距 |
| $spacing-chat_attachment_clear-right | 8px | 附件清除图标右内边距 |
| $spacing-chat_attachment_file-columnGap | 5px | 文件附件列间距 |
| $spacing-chat_attachment_file-padding | 5px | 文件附件内边距 |
| $spacing-chat_chatBox_loading_item-gap | 15px | 聊天内容加载图标间距 |
| $spacing-chat_divider-marginY | 12px | 分割线上下外边距 |
| $spacing-chat_chatBox_content_attachment-marginY | 4px | 聊天框内容文件/图片上下外间距 |
| $spacing-chat_chatBox_content_attachment-marginRight | 4px | 聊天框内容文件/图片上下右间距 |
| $spacing-chat_chatBox_content_code_topSlot-paddingX | 5px | 聊天框代码块顶部上下内边距 |
| $spacing-chat_chatBox_content_code_topSlot-paddingY | 8px | 聊天框代码块顶部左右内边距 |
| $spacing-chat_chatBox_content_code_topSlot_copy-columnGap | 5px | 聊天框代码块顶部复制按钮列间距 |
| $spacing-chat_chatBox_content_code_topSlot_copy-padding | 5px | 聊天框代码块顶部复制按钮列间距 |
| $spacing-chat_chatBox_wrap | 8px | 聊天框外层间距 |
| $spacing-chat_hint-rowGap | 10px | 提示条行间距 |
| $spacing-chat_hint-marginY | 12px | 提示条容器上下外边距 |
| $spacing-chat_hint-marginLeft | 34px | 提示条容器左外边距 |
| $spacing-chat_hint_item-marginY | 8px | 提示条上下外边距 |
| $spacing-chat_hint_item-marginX | 12px | 提示条左右外边距 |
| $spacing-chat_hint_item-columnGap | 20px | 提示条内容列间距 |
| $spacing-chat_chatBox_loading-item-marginX | 18px | 聊天内容加载图标中心圆圈左右外边距 |
| $spacing-chat_chatBox_loading-item-marginY | 6px | 聊天内容加载图标中心圆圈上下外边距 |
| $width-chat_backBottom_wrapper | 42px | 返回按钮宽度 |
| $width-chat_action_content-border | 1px | 返回按钮/停止生成按钮描边宽度 |
| $width-chat_inputBottom_clearButton | 48px | 清空按钮宽度 |
| $width-chat_inputBottom_uploadButton | 32px | 上传按钮宽度 |
| $width-chat_inputBottom_sendButton | 32px | 发送按钮宽度 |
| $width-chat_inputBox_container-border | 1px | 输入框容器边框宽度 |
| $width-chat_hint_item-border | 1px | 提示条边框宽度 |
| $width-chat_chatBox_loading | 8px | 加载中单个圆圈图标宽度 |
| $width-chat_attachment_file_title | 90px | 附件文件标题最大宽度 |
| $width-chat_max | 800px | chat组件最大宽度 |
| $width-chat_dropArea-border | 5px | 拖拽上传边框宽度 |
| $width-chat_chatBox_content_code_topSlot_copy | 150px | 聊天框代码块顶部复制按钮最小宽度 |
| $width-chat_chatBox_avatar | 24px | 聊天框头像宽度 |
| $height-chat_action_stop | 42px | 停止生成按钮高度 |
| $font-chat_divider-fontWeight | $font-weight-regular | 分割线字重 |
| $font-chat_divider-fontSize | $font-size-small | 分割线字体大小 |
| $font-chat_chatBox_title-lineHeight | 20px | 聊天框标题行高 |
| $font-chat_chatBox_title-fontSize | $font-size-header-6 | 聊天框标题字体大小 |
| $font-chat_chatBox_title-fontWeight | $font-weight-regular | 聊天框标题字重 |
| $font-chat_inputBottom_clearButton_icon-fontSize | 30px | 输入区清空上下文按钮图标大小 |
| $font-chat_attachment_file_title-fontSize | $font-size-header-6 | 附件文件标题字体大小 |
| $font-chat_attachment_file_metadata-fontSize | $font-size-regular | 附件文件元数据字体大小 |
| $font-chat_hint_content-fontSize | $font-size-regular | 提示条文字大小 |
| $font-chat_chatBox_code_topSlot | 12px | 代码块顶部字体大小 |
| $font-chat_chatBox_code_topSlot-lineHeight | 16px | 代码块顶部区域字体行高 |
| $font-chat_dropArea_text | 48px | 拖拽上传区域文字大小 |
| $z-chat_dropArea | 10 | 拖拽上传区域z-index |
| $z-chat_action | 1 | 返回按钮/停止生成按钮z-index |
