Appearance
界面与交互配置
本文介绍如何通过 ui 配置和插槽调整 TrChat 的布局、输入区、消息展示及交互,以及如何单独使用界面组件 TrChatUI。首次接入见 快速上手。
界面配置与定制
按照 快速上手 接入 TrChat 后,可以通过 ui 配置和插槽调整界面。需要直接提供显示数据并自行处理界面事件时,参考仅使用界面组件。
ui 是一个界面配置对象,用于设置布局、文案和各区域的显示方式;只需配置需要调整的部分,其余使用默认值。常用的字段如下,具体配置见后续示例:
import type { ChatUIOptions } from '@opentiny/tiny-robot-chat'
const ui: ChatUIOptions = {
layout: {}, // 页面布局、侧栏和浮动窗口
labels: {}, // 按字段修改界面文案
history: {}, // 历史会话列表
welcome: {}, // 欢迎区内容
prompts: {}, // 推荐问题
sender: {}, // 输入区配置
bubble: {}, // 消息展示配置
}将配置好的 ui 传给 TrChat:
<TrChat :runtime="runtime" :ui="ui" />下面介绍常用配置和示例,完整字段见 界面配置。需要自定义界面内容时,见 插槽定制。
基础布局
调整内容宽度、侧栏显示和空会话输入区的位置,适应不同页面布局。
此演示使用
TrChatUI展示布局效果,相同的ui配置也适用于TrChat。
通过 ui.layout 设置布局,常用字段如下:
| 配置 | 用途和可选值 | 默认值 |
|---|---|---|
contentMaxWidth | 消息、欢迎区和输入区的最大内容宽度;支持数字或 CSS 长度,如 640、'80%' | 980,数字单位为 px |
showScrollToBottom | 是否显示内置回到底部按钮;分享等场景可设为 false | true |
leftAside | 左侧栏配置对象;设为 false 隐藏整个左侧栏 | 显示侧栏,默认收起 |
leftAside.width | 左侧栏展开宽度,单位为 px | 300 |
leftAside.collapsedWidth | 左侧栏收起宽度,单位为 px | 56 |
leftAside.defaultOpen | 是否默认展开,之后可通过界面按钮切换 | false |
composer.welcome | 空会话时的输入区位置:'footer' 位于页面底部;'center' 随内置欢迎区居中显示,需保留默认内容布局、欢迎区和输入区 | 'footer' |
例如,缩小内容宽度并默认展开左侧栏:
const ui: ChatUIOptions = {
layout: {
contentMaxWidth: 640,
leftAside: { width: 240, collapsedWidth: 48, defaultOpen: true },
composer: { welcome: 'center' },
},
}已有消息或隐藏欢迎区时,输入区仍位于底部。通过插槽自定义主区或空会话内容时,输入区的居中位置需自行设置,详见插槽说明。
完整字段和默认值见 布局配置。
聊天界面默认使用浏览器视口高度。嵌入已有页面时,可将 TrChat 放入高度明确的容器,并设置 --tr-layout-height: 100%,使界面填满该容器。例如,将高度设为 600px:
<template>
<main class="chat-page">
<TrChat :runtime="runtime" />
</main>
</template>
<style scoped>
.chat-page {
--tr-layout-height: 100%;
height: 600px;
}
</style>调整宽度与间距
消息列表与输入框默认对齐,回到底部按钮默认位于右侧。在组件或父容器上设置 CSS 变量,可以调整内容宽度、内边距和按钮位置:
.business-chat {
--tr-chat-ui-content-max-width: 800px;
--tr-chat-ui-content-padding-inline: 20px;
--tr-chat-ui-header-padding: 16px 20px 8px;
--tr-chat-ui-footer-padding: 8px 20px 16px;
--tr-chat-scroll-button-align: center;
}完整变量和默认值见 CSS 变量。
配置欢迎区和推荐问题
设置欢迎内容和推荐问题,帮助用户开始对话。
没有消息时,通过 ui.welcome 设置欢迎标题和描述,通过 ui.prompts 提供推荐问题,帮助用户开始对话。
| 配置 | 用途 | 默认值 |
|---|---|---|
welcome.title | 欢迎标题,类型为 string | 'TinyRobot AI 助手' |
welcome.description | 欢迎描述,类型为 string | '您好,我是TinyRobot,您专属的 AI 智能专家' |
prompts.items | 推荐问题数组;每项的 label 为必填显示文字,description 为可选补充说明 | [],不显示推荐问题 |
prompts.wrap | 是否允许推荐问题换行,类型为 boolean | false |
例如,设置项目助手的欢迎内容和两个推荐问题:
const ui: ChatUIOptions = {
welcome: {
title: '项目助手',
description: '选择一个问题开始,或输入自己的问题。',
},
prompts: {
wrap: true,
items: [
{ label: '解释代码', description: '请解释这段代码的作用,并给出使用建议。' },
{ label: '撰写周报', description: '请帮我整理本周工作,生成一份简洁的周报。' },
],
},
}点击推荐问题会触发 prompt-click,不会自动填入或发送。下面使用 Sender 的模板扩展将问题填入输入框,用户可编辑后发送:
import { shallowRef } from 'vue'
import { TrSender, type TemplateItem } from '@opentiny/tiny-robot'
import type { ChatPromptClickPayload } from '@opentiny/tiny-robot-chat'
const inputTemplate = shallowRef<TemplateItem[]>([])
ui.sender = { extensions: [TrSender.template(inputTemplate)] }
function handlePromptClick({ item }: ChatPromptClickPayload) {
inputTemplate.value = [{ type: 'text', content: item.description || item.label }]
}<TrChat :runtime="runtime" :ui="ui" @prompt-click="handlePromptClick" />将 ui.welcome 或 ui.prompts 设为 false 可隐藏对应区域。发送首条消息后不再显示欢迎内容;返回新会话可重新查看。
控制欢迎页显示
默认在没有可见消息时显示欢迎页。应用也可以通过 is-empty 自行决定,例如仅在没有活动会话时显示:
<TrChat :runtime="runtime" :is-empty="!activeConversation" />activeConversation 为应用当前选中的会话。已有会话即使尚无消息,也会保持工作区布局。此设置同时影响页头插槽的状态和输入框位置,TrChatUI 同样适用。
配置输入区
为输入框添加提及、可编辑模板和自定义按钮,满足不同输入需求。
示例通过 ui.sender.toolbar 配置“写报告”和“常用问题”,分别填入可编辑模板和预设问题;通过 sender-footer-right 添加“使用说明”,配合 sender-header 展示操作提示。
点击“使用说明”显示或隐藏提示,再点击“写报告”或“常用问题”查看提示更新。输入框上方可按需显示标签或提示,隐藏后不保留空白:
<template v-if="showHelp" #sender-header>
<p>{{ helpText }}</p>
</template>通过 ui.sender 调整输入框、配置提及和模板功能;通过 ui.sender.toolbar 或输入区插槽添加自定义按钮,无需重新实现消息发送和取消。
接收拖入和粘贴的文件
TrChat 和 TrChatUI 会将 Sender 上的文件拖拽与粘贴事件交给宿主。宿主需在 sender-drag-over 中对原生 event 调用 preventDefault(),浏览器才会触发放置事件并交付文件。
<TrChatUI
@sender-drag-over="({ event }) => event.preventDefault()"
@sender-drop="({ event }) => handleFiles(event.dataTransfer?.files)"
@sender-paste="(event) => handleFiles(event.clipboardData?.files)"
/>拖拽事件参数包含原生 event 和 Sender 根节点 target。可在 sender-drag-enter 与 sender-drag-leave 控制拖拽提示层;粘贴事件收到 ClipboardEvent。上传校验、状态和请求由宿主负责。
回填、清空与聚焦
可从外部填入提示词、清空文本或聚焦输入框。回填不会自动发送,清空文本不会移除附件或已选 Skill。
TrChat 与 TrChatUI 均支持这些操作,完整说明见 TrChat 组件方法 和 TrChatUI 组件方法。
基础设置
以下默认值为 Chat 的输入区配置,与单独使用 Sender 时不同:
| 配置 | 用途和可选值 | 默认值 |
|---|---|---|
placeholder | 输入框为空时的提示,类型为 string | 未发送时为 '请输入你的问题...',发送中为 '思考中...' |
mode | 'single' 单行、'multiple' 多行 | 'multiple' |
maxLength | 最大输入字数,类型为 number;超出后保留内容,但无法提交 | 1000 |
showWordLimit | 是否显示字数统计,类型为 boolean | true |
clearable | 有内容时是否显示清空按钮,类型为 boolean | true |
defaultActions | 配置默认发送和清空按钮的提示等选项 | 沿用内置按钮配置 |
extensions | 输入扩展数组,如提及、模板和联想 | [] |
例如,限制为 300 字并修改按钮提示:
const ui: ChatUIOptions = {
sender: {
placeholder: '请输入问题,最多 300 字...',
maxLength: 300,
showWordLimit: true,
defaultActions: {
submit: { tooltip: '发送给项目助手' },
clear: { tooltip: '清空输入内容' },
},
},
}输入值、发送中状态和禁用状态由 Chat 管理,不在 ui.sender 中设置。需要禁止输入或提交时,见 禁用输入与提交。
提及与模板
TrSender.mention() 为输入框提供提及选择,默认输入 @ 触发;TrSender.template() 用于填入包含可编辑字段的模板。将它们放入 ui.sender.extensions 即可在 TrChat 中使用,默认不启用这些扩展。
import { shallowRef } from 'vue'
import { TrSender, type MentionItem, type TemplateItem } from '@opentiny/tiny-robot'
const mentions: MentionItem[] = [
{ label: '产品组', value: 'product' },
{ label: '研发组', value: 'engineering' },
]
const currentTemplate = shallowRef<TemplateItem[]>([])
ui.sender = {
...ui.sender,
extensions: [TrSender.mention(mentions), TrSender.template(currentTemplate)],
}
function fillReportTemplate() {
currentTemplate.value = [
{ type: 'text', content: '请帮我写一份关于' },
{ type: 'block', content: '项目进展' },
{ type: 'text', content: '的报告。' },
]
}提及项中的 label 是显示名称,value 是对应标识;模板中的 'text' 为普通文本,'block' 为可编辑字段。更新 currentTemplate 会替换输入框内容。
提及功能不会自动切换模型或助手。默认发送使用输入内容的纯文本;需要处理提及标识或模板字段时,见 结构化提交数据 和 发送前检查。
工具栏与自定义按钮
ui.sender.toolbar 统一配置左侧高频操作按钮,支持数组或函数。TrChat 与 TrChatUI 使用相同配置。
- 追加按钮:使用
(defaults) => [...defaults, customItem]保留内置按钮;自定义按钮配置type: 'custom'、label、icon和onClick,通过active控制高亮。 - 调整顺序:函数配置可基于
defaults排序;数组配置完整替换按钮列表,未列出的按钮不显示。 - 控制显隐:
visible: false隐藏按钮。 - 控制禁用:
disabled: true禁止点击;设为false仍受模型能力和输入区禁用状态限制。 - 适配窄屏:带图标的按钮在窄输入区自动隐藏文字、保留图标;
label仍用于无障碍名称和悬浮提示。
显隐和禁用不改变功能开关状态。清空、发送和停止按钮通过默认按钮配置管理。
需要自定义渲染时,使用 sender-footer 在左侧工具栏后追加内容,或使用 sender-footer-right 在右侧操作按钮前追加内容,详见插槽。
完整字段和规则见 Toolbar 配置。
配置消息展示
通过 ui.bubble 调整消息展示:bubbleList 设置消息列表外观,bubbleProvider 设置内容渲染和错误提示,autoScroll 控制自动滚动(默认 true);未配置时使用默认展示方式。
消息外观
按消息角色设置头像、位置和气泡形状,区分用户消息与 AI 回答。
演示中的自定义外观还将 AI 气泡背景设为透明;继续发送消息,新的消息会沿用当前配置。
通过 ui.bubble.bubbleList.roleConfigs 按消息角色设置外观。例如,分别配置用户消息(user)和 AI 回答(assistant)的头像、位置和气泡形状。
| 配置 | 用途和可选值 | 默认值 |
|---|---|---|
avatar | 自定义头像,例如通过 Vue 的 h() 创建的节点 | 用户和 AI 分别使用内置头像 |
placement | 'start' 左侧、'end' 右侧 | user 为 'end',assistant 为 'start' |
shape | 'corner' 带角气泡、'rounded' 圆角气泡、'none' 不使用预设气泡形状 | 'corner' |
例如,给用户设置圆角气泡,给 AI 设置自定义头像:
import { h } from 'vue'
const ui: ChatUIOptions = {
bubble: {
bubbleList: {
roleConfigs: {
user: { placement: 'end', avatar: h('span', '我'), shape: 'rounded' },
assistant: { placement: 'start', avatar: h('span', 'AI'), shape: 'none' },
},
},
},
}更多配置见 头像和位置、气泡形状 和 BubbleList 属性。
需要自定义消息内容时,参考 Bubble 自定义渲染器。渲染器也可获取当前会话、消息状态和滚动容器,见自定义消息渲染上下文。
消息错误提示配置
请求失败时,在对应的 AI 消息下显示错误提示,帮助用户了解失败原因。
演示使用 useChatRuntime 展示默认错误提示,不包含自定义提示组件;默认提示不提供重试按钮。
通过 ui.bubble.bubbleProvider.errorRenderer 替换错误提示,传入 null 可关闭默认提示。CustomErrorRenderer 表示应用自行实现的错误展示组件。
const customErrorUI: ChatUIOptions = {
bubble: {
bubbleProvider: { errorRenderer: CustomErrorRenderer },
},
}
const disabledErrorUI: ChatUIOptions = {
bubble: {
bubbleProvider: { errorRenderer: null },
},
}errorRenderer 只控制提示的显示,不会改变请求的失败状态。
默认错误提示支持以下 CSS 变量:
--tr-bubble-error-color:文字颜色。--tr-bubble-error-bg:背景颜色。--tr-bubble-error-border-radius:圆角大小。
更多样式配置见 Bubble CSS 变量。
请求异常处理和操作失败通知见 请求失败时的提示。
用户消息目录
开启 ui.anchor 后,可通过目录快速跳转到用户消息。定位后气泡短暂闪动,滚动时同步当前目录项;点击回到底部可返回最新内容。
const ui: ChatUIOptions = {
anchor: {},
}目录默认关闭,开启后在浏览器视口宽度小于 768px 时隐藏。应用也可以控制其显示,完整选项见 anchor 配置。
配置历史会话列表
自定义历史会话的排序、筛选和分组,同时保留内置会话操作。
浏览器宽度低于 960px 时,先通过界面按钮展开会话列表。
“置顶”和日期分组使用预设数据,不包含置顶操作或自动按日期归类。演示使用独立的本地存储保存示例会话,刷新后保留修改结果。
TrChat 默认使用 Runtime 的会话生成历史列表,支持切换、重命名和删除,无需额外传入 historyData。需要自定义排序、筛选或分组展示时,可通过 historyData 提供列表数据,继续使用内置列表和会话操作。
historyData 支持会话数组或 { group, items }[] 分组数组,列表按传入顺序展示;传入 [] 时显示空列表。group 是分组标题,items 是该组的会话数组,每项的 id 应对应 Runtime 中已有的会话。
下面以分组展示为例,按会话中的 metadata.group 展示“置顶”“昨天”“30天内”,未设置分组的会话放入“30天内”。metadata.group 是本例约定的字段,不是内置日期或置顶配置:
import { computed } from 'vue'
import type { ChatHistoryData } from '@opentiny/tiny-robot-chat'
const groups = ['置顶', '昨天', '30天内']
const historyData = computed<ChatHistoryData>(() =>
groups
.map((group) => ({
group,
items: runtime.conversations.value.filter((item) => (item.metadata?.group ?? '30天内') === group),
}))
.filter(({ items }) => items.length > 0),
)<TrChat class="chat-history" :runtime="runtime" :ui="ui" :history-data="historyData" />本例通过 computed 根据 Runtime 会话生成分组,列表会随会话新增、重命名或删除更新。
ui.history 用于配置菜单等展示选项,可不传,默认菜单包含重命名和删除;设为 false 可隐藏历史列表。例如,只保留重命名菜单:
const ui: ChatUIOptions = {
history: { menuItems: [{ id: 'rename', text: '重命名' }] },
}左侧栏默认使用页面背景色,可通过 --tr-chat-ui-left-aside-bg 调整。选中颜色和圆角可通过 History 的 CSS 变量调整,在 TrChat、TrChatUI 或其父容器上设置即可:
.chat-history {
--tr-history-item-selected-bg: #e4edfd;
--tr-history-item-selected-color: #3964fe;
--tr-history-item-border-radius: 8px;
}完整展示配置见 History 属性 和 样式变量,程序调用见 会话管理。
插槽定制
在页头、消息和输入区添加自定义内容。
本示例使用以下插槽:
layout-header:自定义页头。bubble-content-footer:添加消息反馈按钮。composer-after:添加输入框下方的提示。
额度提醒等页头内容也可放在 layout-header 中,由应用控制显示和排版。
完整插槽列表及参数见 插槽。
右侧面板
在聊天界面旁展示引用资料或结果预览。
需要在会话旁展示引用资料、预览结果等内容时,通过 ui.layout.rightAside.panels 配置面板列表,再用 layout-right-aside-panel 插槽提供内容。
panels 是面板配置对象数组,默认为空数组 [],每项包含以下字段:
| 字段 | 用途 | 必填 |
|---|---|---|
id | 面板唯一标识,与 layout-right-aside-panel 插槽中的 panelId 对应。 | 是 |
title | 面板标题,省略时显示 ui.labels.rightAsideTitle(默认 '详情')。 | 否 |
id 不能重复,也不能使用内置保留标识 mcp。右侧栏默认宽度为 320px,初始关闭;没有自定义面板或 MCP 数据时不会显示。
const ui: ChatUIOptions = {
layout: {
rightAside: {
panels: [{ id: 'preview', title: '结果预览' }],
},
},
}仅需设置初始打开状态时,使用 defaultRightAsideOpen 属性(默认 false);defaultActiveRightAsidePanelId 属性指定初始面板,未设置时选择第一个可用面板:
<TrChat :runtime="runtime" :ui="ui" :default-right-aside-open="true" default-active-right-aside-panel-id="preview">
<template #layout-right-aside-panel="{ panelId }">
<p v-if="panelId === 'preview'">在这里放置预览内容。</p>
</template>
</TrChat>通过 v-model 绑定 rightAsideOpen 和 activeRightAsidePanelId,分别控制右栏开闭和当前面板。未设置这两个属性时,由组件自行管理。
完整配置及默认值见 布局配置,面板控制属性见 TrChat 属性。
注册更多扩展
通过 ui.extensions.tabs 可以向内置扩展管理器注册 Skills 等更多应用页签。已有的 mcp 配置无需修改;同时使用时,MCP 和应用页签会显示在同一右侧面板中。
扩展页签通过以下接口处理交互和自定义内容:
- 事件:组件触发
extension-action后,应用需要写回安装状态或开关状态。 - 页头插槽:通过
extension-header-actions添加导入等页头操作。 - 二级面板:将
ui.extensions.overlayOpen设为true,并通过extension-overlay插槽提供导入表单或详情;显示和关闭状态由应用管理。
mcp 是内置保留页签 ID,不能作为应用页签 ID。MCP 服务、工具和自定义添加的配置方式见 ChatUIData.mcp。
完整配置、事件和插槽说明见 ui.extensions。
浮动聊天窗口
将聊天界面显示为可拖动、缩放的浮动窗口。
ui.layout.surface 用于配置聊天界面的显示方式,可在父容器中显示,也可作为浮动窗口显示。它包含以下两个属性:
mode:选择显示方式,默认'normal',显示在父容器中;设为'floating'时显示为浮动窗口。floatingOptions:配置浮动窗口的拖动、缩放和尺寸限制,仅在mode为'floating'时生效。
floatingOptions 的常用字段如下:
| 字段 | 用途 | 默认值或行为 |
|---|---|---|
draggable | 是否允许拖动窗口 | true |
resizable | 是否允许拖动边缘缩放窗口 | false |
minWidth / minHeight | 最小宽度、最小高度,单位为 px | 320 / 240,同时受视口尺寸限制 |
maxWidth / maxHeight | 最大宽度、最大高度,单位为 px | 不超过当前视口宽度、高度 |
窗口默认支持拖动,下面开启缩放:
const ui: ChatUIOptions = {
layout: {
surface: {
mode: 'floating',
floatingOptions: { resizable: true },
},
},
}需要自定义窗口的位置和尺寸时,设置 floatingState 属性:
import { shallowRef } from 'vue'
import type { LayoutFloatingState } from '@opentiny/tiny-robot-chat'
const floatingState = shallowRef<LayoutFloatingState>({
placement: 'top-right',
offsetX: 24,
offsetY: 72,
width: 520,
height: 520,
})<TrChat :runtime="runtime" :ui="ui" v-model:floating-state="floatingState" />上例将窗口放在右上角,距右侧
24px、顶部72px,宽高均为520px。
未设置 floatingState 时,窗口默认居中,尺寸为 420px × 560px;实际尺寸不会超过浏览器可视区域。
传入 floatingState 时,需同步更新状态,建议使用 v-model:floating-state。
响应式布局与侧栏缩放
切换桌面和移动端布局,并在桌面布局下调整侧栏宽度。
演示切换的是布局效果,不改变浏览器宽度;浏览器可视区域宽度低于
960px时,即使选择桌面布局,也会自动使用抽屉。
侧栏支持两种布局,通过 ui.layout.leftAside.mode 或 ui.layout.rightAside.mode 设置,默认是 'dock':
- 桌面布局(
'dock'):侧栏与消息区并排显示。 - 移动端布局(
'drawer'):侧栏以抽屉形式覆盖消息区。
浏览器可视区域宽度低于 960px 时,组件会自动使用抽屉布局。
仅缩小父容器不会触发自动切换;需要在较窄容器中使用抽屉时,将侧栏的
mode设为'drawer'。
桌面布局下,将侧栏配置中的 resizable 设为 true,可拖动边缘调整宽度,默认不启用。通过 minWidth、maxWidth 设置调整范围,单位为 px;抽屉布局不支持调整宽度。例如:
const ui: ChatUIOptions = {
layout: {
leftAside: {
defaultOpen: true,
resizable: true,
minWidth: 240,
maxWidth: 420,
},
},
}完整侧栏配置及默认值见 布局配置。
仅使用界面组件
TrChatUI 是 TrChat 使用的界面组件,只负责展示数据和触发事件。需要自行处理消息请求、会话保存和数据更新时,可单独使用,前面的 ui 配置和插槽同样适用。
复用已有会话或请求逻辑,也可以继续使用
TrChat,详见高级用法。
数据与事件处理
通过 data 提供会话列表、消息和发送状态。用户发送消息、取消请求或切换会话时,在对应事件中执行项目的操作,并更新显示数据。
import { computed, shallowRef } from 'vue'
import type { ChatMessageItem, ChatUIData } from '@opentiny/tiny-robot-chat'
const inputValue = shallowRef('')
const messages = shallowRef<ChatMessageItem[]>([])
const sending = shallowRef(false)
const data = computed<ChatUIData>(() => ({
conversation: { title: '应用助手' },
bubble: { messages: messages.value },
sender: { loading: sending.value },
}))| 字段 | 用途 | 默认值 |
|---|---|---|
conversation.title | 设置页头标题 | 默认文案为“新对话” |
bubble.messages | 提供消息列表 | [] |
sender.loading | 显示发送中状态,并切换为取消操作 | false |
常见操作通过以下事件通知项目处理:
| 用户操作 | 事件 | 项目需要处理 |
|---|---|---|
| 发送消息 | submit | 发起请求,更新消息和发送状态 |
| 取消发送 | cancel | 停止请求,恢复发送状态 |
| 新建会话 | create-conversation | 清空当前选择或创建会话,更新显示数据 |
| 切换会话 | switch-conversation | 更新选中会话、标题和消息 |
| 重命名会话 | rename-conversation | 保存新标题并更新列表 |
| 删除会话 | history-action | 判断 action.id === 'delete',删除并更新数据 |
例如,监听 submit 和 cancel 处理发送与取消,通过 v-model:input-value 同步输入内容:
<TrChatUI :data="data" v-model:input-value="inputValue" @submit="handleSubmit" @cancel="handleCancel" />submit 和 cancel 只通知项目处理操作,不会自动请求或停止服务。发送后是否清空输入、何时追加回答,由对应的事件处理方法决定。
输入区的清空按钮会清空输入;使用 v-model 时,会同步更新绑定值。清空输入不会删除消息或取消请求。
完整数据字段见 ChatUIData,属性和事件见 TrChatUI API。
受控与非受控
输入内容、侧栏开闭等界面状态支持两种管理方式:
- 受控:由项目传入当前值并同步更新,适合填入预设问题、发送后清空输入或从外部打开侧栏。
- 非受控:由组件维护状态,无需从外部同步更新,可按需设置初始值。
以输入内容为例,受控方式通过 v-model:input-value 绑定当前内容,inputValue 可初始化为 '':
<TrChatUI v-model:input-value="inputValue" @submit="handleSubmit" />非受控方式不传入 inputValue,由组件维护输入。可通过 defaultInputValue 设置初始内容,默认 '';后续修改该属性不会重置当前输入:
<TrChatUI defaultInputValue="请介绍一下 TinyRobot" @submit="handleSubmit" />两种方式都能通过 submit 获取文本,但不会自动发送请求或清空输入。
输入管理方式应在初始化时确定,不要在使用过程中切换。
右栏和浮动窗口也支持这两种方式:
| 状态 | 受控方式 | 非受控方式 |
|---|---|---|
| 右栏开闭 | v-model:right-aside-open | 使用 defaultRightAsideOpen 设置初始值,默认 false |
| 当前右栏面板 | v-model:active-right-aside-panel-id | 使用 defaultActiveRightAsidePanelId;未指定有效面板时选择第一个可用面板 |
| 浮动窗口位置和尺寸 | v-model:floating-state | 不传入当前值,由组件维护内置窗口状态 |
左栏通过 ui.layout.leftAside.open 设置当前开闭状态,并在 left-aside-open-change 中更新;不设置当前值时,可通过 ui.layout.leftAside.defaultOpen 设置初始状态,默认 false。
完整属性和事件见 TrChatUI API,相关布局配置见 右侧面板 和 浮动聊天窗口。
TrChat API
属性
| 属性名 | 说明 | 类型 | 默认值 | 必填 |
|---|---|---|---|---|
runtime | 提供会话、输入区状态和操作方法。 | ChatRuntime | — | 是 |
is-empty | 是否显示欢迎布局;传入时优先于消息数判断,同时影响页头插槽状态与输入框位置。 | boolean | 按可见消息数判断 | 否 |
ui | 配置页面布局、文案和区域。 | ChatUIOptions | 默认界面配置 | 否 |
title | 覆盖当前会话提供的页面标题。 | string | — | 否 |
history-data | 覆盖 Runtime 会话生成的历史列表或分组。 | ChatHistoryData | — | 否 |
floating-state | 由应用管理的浮动窗口位置和尺寸。 | LayoutFloatingState | — | 否 |
right-aside-open | 由应用管理的右栏开闭状态。 | boolean | — | 否 |
default-right-aside-open | 组件管理右栏开闭时的初始值。 | boolean | false | 否 |
active-right-aside-panel-id | 由应用管理的当前右栏面板,需处理更新事件。 | ChatRightAsidePanelId | — | 否 |
default-active-right-aside-panel-id | 组件管理当前面板时的初始值。 | ChatRightAsidePanelId | 第一个可用面板 | 否 |
事件
TrChat 会直接处理提交、取消、会话切换、模型选择和 MCP 开关操作,调用 Runtime 对应方法;这些事件不会再次向外发出。
Sender 的文件拖拽与粘贴事件会透传给应用,由应用处理文件,详见接收拖入和粘贴的文件。
| 事件 | 参数 | 触发时机 |
|---|---|---|
runtime-action-error | ChatRuntimeActionErrorPayload | Runtime 操作失败;send 错误详情仍从所属消息读取。 |
model-feature-value-change | ChatModelFeatureValueChangePayload | 用户修改当前模型的设置时触发。随后 TrChat 会请求 Runtime 更新该设置。 |
history-action | ChatHistoryActionPayload | 历史菜单操作;删除操作可调用 preventDefault() 阻止默认 Runtime 删除。 |
prompt-click | ChatPromptClickPayload | 点击提示项。 |
mcp-create-server | ChatMcpCreateServerPayload | 请求创建 MCP 服务;Runtime 不处理创建表单。 |
extension-action | ExtensionManagerActionEvent | 自定义扩展页签中的操作被触发。 |
extension-name-click | ExtensionManagerNameClickEvent | 点击自定义扩展页签中的条目名称。 |
extension-tab-change | ExtensionManagerTabChangeEvent | 扩展管理器切换页签。 |
bubble-state-change | ChatBubbleStateChangePayload | 气泡内部状态变化。 |
bubble-event | ChatBubbleEventPayload | 气泡内容发出自定义事件。 |
sender-drag-enter / sender-drag-over / sender-drag-leave / sender-drop | SenderDragPayload | Sender 上对应的原生文件拖拽事件;放置文件需在 sender-drag-over 中调用 event.preventDefault()。 |
sender-paste | ClipboardEvent | Sender 收到粘贴事件。 |
left-aside-open-change / right-aside-open-change | ChatAsideOpenChangePayload | 侧栏状态变化。 |
update:right-aside-open | boolean | 通知应用更新右栏开闭状态。 |
update:active-right-aside-panel-id | ChatRightAsidePanelId | undefined | 通知应用更新当前右栏面板。 |
update:floating-state | LayoutFloatingState | 通知应用更新浮动窗口的位置和尺寸。 |
floating-drag-start / floating-drag / floating-drag-end | LayoutFloatingDragDetail | 浮动窗口开始拖动、拖动中或拖动结束。 |
floating-resize-start / floating-resize / floating-resize-end | LayoutFloatingResizeDetail | 浮动窗口开始缩放、缩放中或缩放结束。 |
组件方法
| 方法 | 签名 | 说明 |
|---|---|---|
send | (payload: ChatSendPayload) => Promise<boolean> | 通过 Runtime 发送消息。 |
setInputValue | (value: string) => void | 仅回填文本,不发送。 |
clearInput | () => void | 仅清空文本。 |
focusInput | () => Promise<boolean> | 聚焦当前默认输入器,返回是否成功。 |
openRightAside | (panel?: ChatRightAsidePanelId) => void | 打开右栏,可同时指定面板。 |
closeRightAside | () => void | 关闭右栏。 |
toggleRightAside | (panel?: ChatRightAsidePanelId) => void | 切换右栏,可同时指定面板。 |
activateRightAsidePanel | (panel: ChatRightAsidePanelId) => boolean | 激活存在的面板并返回是否成功。 |
setInputValue 和 clearInput 只更新文本,不自动发送,也不移除附件或已选 Skill。focusInput 返回是否聚焦成功;无默认输入框或输入框不可用时返回 false,自定义输入框的聚焦由应用处理。
TrChatUI API
属性
| 属性名 | 说明 | 类型 | 默认值 | 必填 |
|---|---|---|---|---|
data | 应用提供的显示数据;组件不会修改该对象。 | ChatUIData | 空展示数据 | 否 |
ui | 配置页面布局、文案和区域。 | ChatUIOptions | 默认界面配置 | 否 |
input-value | 应用管理的输入内容,需处理更新事件。 | string | — | 否 |
default-input-value | 组件管理输入时的初始内容。 | string | '' | 否 |
is-empty | 是否显示欢迎布局;传入时优先于消息数判断,同时影响页头插槽状态与输入框位置。 | boolean | 按可见消息数判断 | 否 |
floating-state | 由应用管理的浮动窗口位置和尺寸。 | LayoutFloatingState | — | 否 |
right-aside-open | 由应用管理的右栏开闭状态。 | boolean | — | 否 |
default-right-aside-open | 组件管理右栏开闭时的初始值。 | boolean | false | 否 |
active-right-aside-panel-id | 由应用管理的当前面板,需处理更新事件。 | ChatRightAsidePanelId | — | 否 |
default-active-right-aside-panel-id | 组件管理当前面板时的初始值。 | ChatRightAsidePanelId | 第一个可用面板 | 否 |
input-value 与 default-input-value 二选一,使用期间不切换输入管理方式。由应用管理左栏开闭时,通过 ui.layout.leftAside.open 设置状态,并在 left-aside-open-change 事件中更新该值。
事件
点击“新会话”时,TrChatUI 只触发 create-conversation,由项目决定清空当前选择还是立即创建会话。TrChat 会返回新会话页面;使用默认发送流程时,在发送首条非空消息时创建会话。
配置自定义
send时,不会自动创建会话,需由回调自行创建或选择会话,见 自定义发送流程。
以下事件需由项目自行处理:
| 事件 | 参数 | 说明 |
|---|---|---|
submit | ChatSendPayload | 发送请求并更新消息、请求状态和输入值。 |
update:input-value | string | 由应用管理输入时,更新输入内容。 |
cancel / clear | 无 | 中止请求或清空输入;组件不会修改外部请求。 |
create-conversation | 无 | 清空当前会话或创建新会话。 |
switch-conversation | ChatSwitchConversationPayload | 切换数据源并更新 data.conversation.activeId。 |
rename-conversation | ChatRenameConversationPayload | 保存新标题并更新会话列表。 |
history-action | ChatHistoryActionPayload | 处理历史菜单操作;TrChatUI 本身不会执行删除。 |
prompt-click | ChatPromptClickPayload | 决定填充输入、直接提交或执行其他操作。 |
bubble-state-change / bubble-event | 对应 payload | 更新消息状态或处理自定义气泡事件。 |
model-select | ChatModelSelectPayload | 更新 data.model.selectedId。 |
model-feature-value-change | ChatModelFeatureValueChangePayload | 用户修改设置时触发。由应用根据 featureValues 更新 data.model.featureValues。 |
sender-drag-enter / sender-drag-over / sender-drag-leave / sender-drop | SenderDragPayload | Sender 上对应的原生文件拖拽事件;放置文件需在 sender-drag-over 中调用 event.preventDefault()。 |
sender-paste | ClipboardEvent | Sender 收到粘贴事件。 |
mcp-add-server / mcp-remove-server | ChatMcpAddServerPayload / ChatMcpRemoveServerPayload | 更新 MCP 服务列表。 |
mcp-create-server | ChatMcpCreateServerPayload | 创建并接入自定义 MCP 服务。 |
mcp-server-enabled-change | ChatMcpServerEnabledChangePayload | 更新 MCP 服务启用状态。 |
mcp-tool-enabled-change | ChatMcpToolEnabledChangePayload | 更新工具启用状态。 |
extension-action | ExtensionManagerActionEvent | 处理自定义扩展页签中的安装、开关等操作。 |
extension-name-click | ExtensionManagerNameClickEvent | 处理自定义扩展条目的名称点击。 |
extension-tab-change | ExtensionManagerTabChangeEvent | 处理扩展管理器页签切换。 |
left-aside-open-change / right-aside-open-change | ChatAsideOpenChangePayload | 由应用管理侧栏时更新开闭状态;source 区分用户操作和浏览器窗口变化。 |
update:right-aside-open | boolean | 更新应用管理的右栏开闭状态。 |
update:active-right-aside-panel-id | ChatRightAsidePanelId | undefined | 更新应用管理的当前面板。 |
update:floating-state | LayoutFloatingState | 更新应用管理的浮动窗口位置和尺寸。 |
floating-drag-start / floating-drag / floating-drag-end | LayoutFloatingDragDetail | 处理窗口开始拖动、拖动中或拖动结束。 |
floating-resize-start / floating-resize / floating-resize-end | LayoutFloatingResizeDetail | 处理窗口开始缩放、缩放中或缩放结束。 |
组件方法
| 方法 | 签名 | 说明 |
|---|---|---|
setInputValue | (value: string) => void | 仅回填文本;受控时请求更新。 |
clearInput | () => void | 仅清空文本;受控时请求更新。 |
focusInput | () => Promise<boolean> | 聚焦当前默认输入器,返回是否成功。 |
openRightAside | (panel?: ChatRightAsidePanelId) => void | 打开右栏,可同时指定面板。 |
closeRightAside | () => void | 关闭右栏。 |
toggleRightAside | (panel?: ChatRightAsidePanelId) => void | 切换右栏,可同时指定面板。 |
activateRightAsidePanel | (panel: ChatRightAsidePanelId) => boolean | 激活存在的面板并返回是否成功。 |
输入方法的行为与 TrChat 相同。绑定 input-value 时,需使用 v-model:input-value 或处理 update:inputValue,回填和清空才会同步到输入框。
显示数据
ChatUIData 是应用传给 TrChatUI 的显示数据,组件不会修改它。以下字段均为可选,对应区域见下表。
未配置的字段使用下表默认值。无需模型或 MCP 入口时,省略 model / mcp,或将 ui.model / ui.mcp 设为 false。
| 字段 | 类型 | 对应区域 | 默认行为 |
|---|---|---|---|
conversation | ChatConversationView | 页头标题、会话列表和当前选中 | 空列表、无选中,标题为“新对话”。 |
bubble | ChatBubbleView | 消息主区 | 消息为空时显示欢迎布局;传入 is-empty 时以该值为准。 |
sender | ChatSenderView | 输入区可用性与加载反馈 | 三个布尔状态均为 false。 |
model | ChatModelView | 输入区模型选择器与字段配置 | 不显示模型选择器。 |
mcp | ChatMcpView | 输入区 MCP 入口与内置右栏 | 不显示 MCP 入口或面板。 |
request | ChatRequestView | 插槽参数、消息流状态推断 | undefined;不单独显示请求状态。 |
conversation
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items | ChatConversationInfo[] | [] | 原始会话列表;未提供 history 时按当前数组顺序生成默认历史数据。 |
activeId | string | null | null | 当前选中会话 ID;应用处理切换事件后更新。 |
title | string | 新对话 | 页头标题;空字符串会原样显示。 |
history | ChatHistoryData | — | 应用提供的排序或分组结果;提供后优先于根据 items 生成的默认历史。 |
ChatConversationInfo、ChatMessageItem 及消息内容结构见 数据与逻辑配置:会话、消息与发送。
bubble 与 sender
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
bubble.messages | ChatMessageItem[] | [] | 消息列表;组件不会追加、删除或保存消息。 |
sender.loading | boolean | false | 显示停止按钮,点击触发 cancel。 |
sender.disabled | boolean | false | 禁用整个输入区。 |
sender.submitDisabled | boolean | false | 仅禁止提交;输入仍可编辑,loading 时仍可取消。 |
单独使用 TrChatUI 时,AI 消息的 state.error 用于显示错误提示,需要由应用写入。
model
| 字段 | 类型 | 说明 |
|---|---|---|
options | ChatModelOptionView[] | 可选模型列表;为空时显示没有可选模型的提示。 |
selectedId | string | null | 当前模型;选择后应用通过 model-select 写回。 |
featureValues | ModelSelectorFeatureValues | 当前模型字段值;与模型的 features 配合使用。 |
renderTriggerText | ModelSelectorProps['renderTriggerText'] | 自定义模型选择器触发器文本。 |
pendingFeatureKeys | readonly string[] | 当前模型正在异步更新的字段 key。 |
featureValuesDisabled | boolean | 禁止字段修改。 |
selecting | boolean | 模型切换中的整体等待状态。 |
ChatModelOptionView 至少包含 id 和 label,还可提供 description、icon、disabled、group、features 和 metadata。功能定义、默认值和触发器文本见 ModelSelector。在 Floating Layout 中,选择器默认挂载到当前窗口的 overlay host;ui.model.appendTo 可显式覆盖挂载目标。
使用 TrChatUI 时,用户切换模型会触发 model-select,修改当前模型的设置会触发 model-feature-value-change。应用监听事件并更新 data.model.selectedId 或 data.model.featureValues。
使用 TrChat 时,组件直接调用 Runtime 切换模型。用户修改设置时,TrChat 会发出 model-feature-value-change,再调用 Runtime 的 setFeatureValue({ modelId, key, value })。初始化、应用直接赋值或切换模型都不会触发设置修改事件。请求参数的转换方式见 模型功能。
mcp
| 字段 | 类型 | 说明 |
|---|---|---|
servers | ChatMcpServerView[] | MCP 服务的安装、启用、加载和错误状态;应用处理对应事件后写回。 |
tools | ChatMcpToolMap | 以 MCP 服务 ID 为键的工具数组;工具包含 id、name、enabled 等。 |
ChatMcpServerView 要求 id、name、installed 和 enabled;可选 description、icon、category、loading、error 与 metadata。ChatMcpToolView 要求 id、name 和 enabled,可选 description 与 loading。只有 ui.mcp 与 ui.layout.rightAside 都未设为 false 时,MCP 入口和内置面板才可见。
内置右栏使用 ExtensionManager 将 MCP 服务按“已安装”和“可安装”分区;点击已安装项名称进入 MCP 详情切换工具,点击“自定义添加”进入 MCP 表单。表单验证通过后,mcp-create-server 发出 { config, source };config 是整理后的单个 MCP 服务连接配置,source 为 'form' 或 'code'。Chat 不保存自定义 MCP 服务,应用收到事件后负责保存并更新 mcp.servers。服务的 loading 状态会禁用其列表操作,工具的 loading 状态会禁用对应开关。
request
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
state | 'idle' | 'processing' | 'completed' | 'paused' | 'aborted' | 'error' | 是 | 请求状态。 |
processingState | 'requesting' | 'completing' | string | 否 | 应用定义的处理中阶段。 |
request 会传给 layout-main 和 layout-empty-state,也用于推断最新消息的流状态,见自定义消息渲染上下文。输入区的发送中反馈读取 sender.loading,消息错误读取 message.state.error。
公共配置
以下界面配置和插槽均适用于 TrChat 和 TrChatUI。
界面配置
TrChat 和 TrChatUI 都通过 ui 接收 ChatUIOptions,配置布局、文案和各区域的显示方式。
未配置时使用默认界面。支持 false 的区域可通过它隐藏,其他选项见下表。
| 字段 | 类型 | 配置说明 | false 的作用 |
|---|---|---|---|
layout | ChatLayoutOptions | 未配置的布局字段使用默认值;窗口、输入区和左右侧栏可分别配置。mobileBreakpoint 默认 960,视口宽度低于该值启用移动布局;非正数或非有限值回退到 960。 | 不支持;使用 leftAside / rightAside。 |
brand | ChatBrandOptions | 提供的选项覆盖对应的默认名称或图标,默认名称为 TinyRobot。 | 不支持。 |
labels | Partial<ChatLabels> | 按字段覆盖内置中文文案;也会更新默认欢迎文案和历史菜单文案。 | 不支持。 |
header | false | 省略时显示默认 Header。 | 移除 Header。 |
history | false | ChatHistoryOptions | 与默认 History 选项合并;menuItems 整体替换默认重命名、删除菜单。 | 隐藏历史列表,左栏品牌和操作仍在。 |
welcome | false | ChatWelcomeOptions | 与默认欢迎区配置合并;未提供标题和描述时使用 labels 中的文案。 | 空会话不显示欢迎区。 |
prompts | false | ChatPromptsOptions | 与默认 Prompts 选项合并;items 整体替换,默认 []。 | 不显示提示项。 |
bubble | ChatBubbleOptions | 合并气泡渲染和列表配置;autoScroll 默认 true,system 消息默认隐藏。 | 不支持。 |
sender | false | ChatSenderOptions | 提供的选项覆盖对应的默认输入区选项。 | 移除输入区及其插槽。 |
model | false | ChatModelOptions | 默认 {};只有 data.model 存在时显示。 | 即使存在模型数据也隐藏选择器。 |
mcp | false | ChatMcpOptions | 默认 {};当前没有额外配置字段。 | 即使存在 MCP 数据也隐藏入口和面板。 |
extensions | false | ChatExtensionsOptions | 注册应用扩展页签,可设置默认页签及二级面板状态。 | 不注册应用扩展页签。 |
anchor | false | ChatAnchorOptions | 默认关闭;传入对象开启,见 anchor 配置。 | 隐藏用户消息目录。 |
ui.layout.mobileBreakpoint 控制移动布局断点,默认 960(像素);视口宽度小于该值时启用移动布局。非正数或非有限数值回退到 960。layout-header 插槽参数 ChatHeaderSlotProps.isMobileViewport 可用于同步自定义页头布局状态。桌面视口中,如果浮窗宽度不足以容纳当前左侧栏、主区最小宽度和展开的右侧栏,右侧栏会从 dock 切换为 Drawer。空间恢复后会切回 dock,并保留打开状态和当前面板。
ui.extensions
通过 ui.extensions.tabs 向内置扩展管理器注册 Skills 等应用能力。若同时提供 mcp,MCP 和应用页签会在同一面板中展示。原有 MCP 事件保持不变。mcp 是保留页签 ID,重复 ID 会被忽略。
import type { ChatUIOptions } from '@opentiny/tiny-robot-chat'
const ui: ChatUIOptions = {
extensions: {
defaultActiveTab: 'skills',
tabs: [
{
id: 'skills',
label: 'Skills',
items: [
{
id: 'concise-answer',
name: 'Concise Answer',
installed: true,
actions: [{ id: 'toggle', type: 'switch', label: '启用简洁回答', checked: true }],
},
],
},
],
},
}扩展页签相关接口分为以下三类:
- 事件:
extension-action处理安装、卸载和开关等条目操作;extension-name-click处理名称点击;extension-tab-change处理页签切换。 - 插槽:
extension-header-actions按当前页签添加“导入”等页头操作;extension-overlay在同一右栏提供导入表单或详情等二级面板内容。 - 受控配置:
ui.extensions.overlayOpen控制是否显示二级面板。设为true时,扩展列表会暂时移出可访问树;应用负责将其设回false以关闭二级面板。
layout
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
surface.mode | 'normal' | 'floating' | 'normal' | 页面或浮动窗口;浮动时读取 floatingOptions 与 floating-state。 |
emptyState | 'start' | 'center' | 'start' | 空状态在主区起始位置或居中。 |
composer.welcome | 'footer' | 'center' | 'footer' | 空会话时的输入区位置:'footer' 位于页面底部;'center' 随内置欢迎区居中显示,需保留默认内容布局、欢迎区和输入区。 |
contentMaxWidth | string | number | 980 | 消息、欢迎区和输入区的最大内容宽度。 |
panelPadding / panelGap | string | number | 12 / 12 | 消息内容纵向内边距与区域间距;水平内边距通过 CSS 变量设置。 |
leftAside | false | ChatAsideOptions | Dock,300 / 56,关闭 | 左栏;open 由应用管理,defaultOpen 仅设置组件管理开闭时的初始值。 |
rightAside | false | ChatRightAsideOptions | Dock,宽 320,不可缩放 | 右栏;只有注册应用面板或存在可见 MCP 数据时才渲染。 |
leftAside 和 rightAside 的配置字段如下,尺寸字段的单位均为 px:
| 字段 | 类型或可选值 | 默认值或未配置行为 | 用途 |
|---|---|---|---|
mode | 'dock' | 'drawer' | 'dock';浏览器视口低于 960px 时自动使用 'drawer' | 侧栏与消息区并排,或以抽屉覆盖消息区 |
width | number | 左栏 300;右栏 320 | 展开宽度 |
collapsedWidth | number | 左栏 56;右栏 0 | 收起宽度;移动端使用 0 |
resizable | boolean | false | 允许拖动调整展开宽度,仅桌面端并排模式生效 |
minWidth | number | 左栏 200;右栏 240 | 最小展开宽度 |
maxWidth | number | 左栏 560;右栏 640 | 最大展开宽度 |
open | boolean | 未设置时由组件管理 | 仅左栏;由项目控制开闭时,需处理 left-aside-open-change 并更新值 |
defaultOpen | boolean | false | 仅左栏;由组件管理开闭时的初始值 |
showClose | boolean | true | 仅右栏;是否显示关闭按钮 |
panels | ChatRightAsidePanelOptions[] | [] | 仅右栏;每项必填 id,可选 title |
右栏开闭和当前面板通过组件属性设置,见 TrChat 属性。浮动窗口的配置结构、尺寸限制和状态绑定见 浮动聊天窗口。
panels 需要配合 layout-right-aside 或 layout-right-aside-panel 插槽提供内容;面板 ID 不能重复,也不能使用内置保留 ID mcp。layout-right-aside-title 和 layout-right-aside-panel 只用于应用添加的面板,不替换 MCP 面板。
anchor
传入 ui.anchor: {} 开启用户消息目录,按用户消息的展示文本生成目录项。没有文本的消息不生成目录项;欢迎布局或自定义 layout-main 时不显示目录。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
visible | boolean | true | 是否显示目录,可用于分享等场景下临时隐藏。 |
minWidth | number | 768 | 显示目录所需的最小浏览器视口宽度,单位为 px;不按聊天容器宽度判断。 |
hoverExpandDelay | number | 300 | 鼠标悬停后展开目录的延迟,单位为 ms。设为 0 立即展开。 |
点击目录或向上浏览会暂停自动跟随,点击回到底部可恢复已启用的自动跟随。
各区域配置
| 配置项 | 说明 | 相关组件 |
|---|---|---|
brand | name?: string、logo?: unknown。 | — |
labels | 会话创建/重命名/删除、侧栏展开/收起、输入占位、模型、MCP、Welcome、右栏和滚动到底部等文案。 | — |
history | 默认菜单为重命名和删除;Chat 固定管理 data、selected 与事件。 | History |
welcome | 默认标题与描述来自 labels.welcomeTitle、labels.welcomeDescription。 | Welcome |
prompts | items?: PromptProps[],其余展示选项继承 Prompts。 | Prompts |
bubble | autoScroll、bubbleProvider、bubbleList;消息错误提示配置见消息错误提示配置。 | Bubble |
sender | 默认 mode: 'multiple'、clearable: true、maxLength: 1000、showWordLimit: true;值和禁用状态由 Chat 管理。 | Sender |
model | appendTo?: ModelSelectorProps['appendTo'],默认跟随 floating Layout 的 overlay host。 | ModelSelector |
mcp | Record<string, never>,当前没有配置字段。 | — |
extensions | tabs 注册 ExtensionManager 页签;defaultActiveTab 设置初始页签;overlayOpen 切换二级面板。 | ExtensionManager |
ChatLabels 的字段为 newConversationTitle、createConversation、renameConversation、deleteConversation、expandConversationList、collapseConversationList、composerPlaceholder、composerLoadingPlaceholder、selectModel、searchModel、modelEmptyText、mcp、extensions、mcpInstallServer、mcpRemoveServer、welcomeTitle、welcomeDescription、rightAsideTitle、openRightAside、closeRightAside 和 scrollToBottom,字段值均为 string。
sender.toolbar
ui.sender.toolbar 用于配置左侧工具栏,支持 readonly ChatSenderToolbarItem[] 或 (defaults: readonly ChatSenderToolbarItem[]) => readonly ChatSenderToolbarItem[];交互示例见工具栏与自定义按钮。
| 字段 | 内置项 | 自定义项 |
|---|---|---|
type | 'builtin' | 'custom' |
id | 'model' | 'mcp' | 唯一业务 ID,不能使用内置 ID |
visible | 可选,默认 true | 同左 |
disabled | 可选,默认 false | 同左 |
label | 使用 labels 中的文案 | 必填,按钮文案 |
icon | 使用内置图标 | 可选,Vue 组件或图片地址 |
active | 读取当前功能状态 | 可选,由业务控制高亮 |
onClick | 使用现有内置操作 | 必填,() => void |
未提供 toolbar 时,默认顺序为 model → mcp,模型特性在选择器面板内配置。数组配置完整替换默认列表,未列出的项隐藏。函数配置接收尚未按数据和可见性过滤的只读默认列表,同步返回最终列表,可追加、筛选或排序;请勿修改 defaults 或在回调中执行副作用。两种配置均在确定列表后统一应用显示和禁用规则,返回 [] 时隐藏整个工具栏。带图标的按钮在窄输入区自动隐藏文字、保留图标;label 仍用于无障碍名称和悬浮提示。
toolbar: (defaults) => [
...defaults,
{
type: 'custom',
id: 'gui',
label: '生成式 UI',
icon: IconGenerativeUi,
onClick: handleGui,
},
]visible: false 隐藏单项,全部隐藏时移除工具栏容器。ID 必须唯一;重复 ID 仅保留第一项,使用内置 ID 的自定义项不显示。
模型特性统一通过模型的 features 配置,并在选择器面板中操作。业务快捷操作应配置为自定义工具栏项,调用 runtime.composer.model.setFeatureValue({ modelId, key, value }) 更新同一份字段状态。ui.model: false 或没有模型数据时,模型选择器隐藏;自定义项不依赖模型或 MCP 数据。
data.sender.disabled、单项 disabled 和内置限制共同决定禁用状态,点击时同样检查;loading 不自动禁用整个工具栏。隐藏和禁用不改变功能开关,也不写入请求配置。自定义项由业务回调更新状态;动态配置可使用 computed。
sender-footer 插槽仍追加在工具栏之后,需要参与混排的按钮应配置到 toolbar。清空、发送和停止按钮继续由原有 Sender 管理。工具栏用于默认多行输入框;替换 layout-footer 时由业务自行渲染。
CSS 变量
在 TrChat、TrChatUI 或父容器上设置以下变量。长度使用 CSS 长度值,如 20px 或 0。表中宽度指浏览器视口宽度。
| 变量 | 默认值(≥960px) | 默认值(<960px) | 用途 |
|---|---|---|---|
--tr-chat-ui-content-max-width | 980px | 980px | 页头、消息区和输入区的内容最大宽度。 |
--tr-chat-ui-content-padding-inline | 24px | 16px | 各区域共享的水平内边距。 |
--tr-chat-ui-panel-padding | 12px | 12px | 消息内容纵向内边距。 |
--tr-chat-ui-panel-gap | 12px | 12px | 内容容器的子项间距。 |
--tr-chat-ui-header-padding | 24px 24px 0 | 16px 16px 0 | 页头内边距,包含自定义页头。 |
--tr-chat-ui-main-padding | 12px 24px | 12px 16px | 消息区内边距。 |
--tr-chat-ui-footer-padding | 0 24px 24px | 0 16px 16px | 底部输入区内边距。 |
--tr-chat-ui-header-gap | 8px | 8px | 内置页头子项间距,自定义页头自行排版。 |
--tr-chat-ui-message-list-padding | 8px 0 | 8px 0 | 气泡列表内边距。 |
--tr-chat-ui-composer-gap | 0px | 0px | 输入框与 composer-before、composer-after 内容的间距。 |
--tr-chat-ui-welcome-composer-gap | 16px | 16px | 内置居中欢迎区与输入框的间距。 |
--tr-chat-scroll-button-align | center | center | 回到底部按钮的位置:flex-start 左侧、center 居中、flex-end 右侧。 |
- 设置
ui.layout.contentMaxWidth、panelPadding、panelGap后,对应默认值随之调整;同项 CSS 变量优先。 - 页头、消息区和输入区的左右内边距跟随共享水平内边距,也可分别覆盖;消息区上下内边距跟随
--tr-chat-ui-panel-padding。 - 视口宽度 ≥960px 且使用
composer-after时,底部输入区下内边距默认为8px。
回到底部按钮以底部输入区为基准对齐;没有底部输入区时,使用共享内容宽度和水平内边距,底边距跟随 --tr-chat-ui-panel-padding。
插槽
以下插槽适用于 TrChat 和 TrChatUI。插槽参数中的发送、会话、模型和 MCP 操作,在 TrChat 中调用当前 Runtime;在 TrChatUI 中触发事件,需要项目处理事件并更新数据。
| 插槽 | 插槽参数 | 说明 |
|---|---|---|
layout-header | ChatHeaderSlotProps | 替换页头。 |
layout-left-aside | ChatLeftAsideSlotProps | 替换左侧展开面板。 |
layout-left-aside-brand / layout-left-aside-actions / layout-left-aside-rail | ChatLeftAsideSlotProps | 分别替换左栏品牌、操作和收起时的操作区域。 |
layout-left-aside-footer | ChatLeftAsideSlotProps | 在左栏底部添加内容。 |
layout-left-aside-content | ChatLeftAsideContentSlotProps | 替换左栏内容区,包括默认会话列表。 |
layout-left-aside-history-item-prefix | ChatHistoryItemPrefixSlotProps | 在历史项前添加内容。 |
layout-right-aside | ChatRightAsidePanelSlotProps | 替换整个右栏及其面板。 |
layout-right-aside-title | ChatRightAsideTitleSlotProps | 替换应用添加的面板标题。 |
layout-right-aside-panel | ChatRightAsidePanelSlotProps | 提供应用添加的面板内容。 |
layout-main | ChatMainSlotProps | 替换消息区和空状态内容,保留外层滚动区域;优先于 layout-empty-state。 |
layout-empty-state | ChatEmptyStateSlotProps | 替换没有消息时的内容;需要显示默认输入区时,调用 renderComposer()。 |
layout-footer | ChatSenderSlotProps | 完整替换默认 Sender;使用后默认输入框及其内部插槽不渲染,需自行连接输入和提交操作;保留 composer-before 和 composer-after。 |
composer-before | ChatSenderSlotProps | 在输入框外上方添加内容,不替换输入框;随输入区显示,ui.sender: false 时隐藏。 |
composer-after | ChatSenderSlotProps | 在输入框外下方添加提示、链接等内容,不替换输入框;随输入区显示,ui.sender: false 时隐藏。 |
sender-header | 无 | 在默认输入框内部上方添加内容。 |
input-prefix | 无 | 在默认多行输入框首行前添加内容。 |
sender-content | { editor } | 替换默认输入框的编辑区域。 |
sender-actions-inline | SenderSlotScope | 在默认行内操作区、默认操作之前添加内容。 |
sender-footer | SenderSlotScope | 在默认多行输入框内部底部左侧添加内容,与模型和工具操作同一区域;提供插入、聚焦和状态方法。 |
sender-footer-right | SenderSlotScope | 在默认多行输入框内部底部右侧、默认操作按钮前添加内容;提供插入、聚焦和状态方法。 |
welcome-footer / prompts-footer | 无 | 扩展对应区域。 |
extension-header-actions / extension-overlay | ChatExtensionSlotProps | 为注册的应用扩展页签添加页头操作或二级面板内容。 |
bubble-prefix / bubble-suffix / bubble-after | ChatBubbleSlotProps | 在消息周围添加内容。 |
bubble-content-footer | ChatBubbleContentFooterSlotProps | 在消息内容下方添加内容。 |
sender-header、input-prefix、sender-content、sender-actions-inline、sender-footer、sender-footer-right 支持动态显示和更新。控制 input-prefix 显隐时,应将 v-if 放在插槽声明上;仅在直接子节点上使用 v-if 会隐藏内容但保留首行缩进。其他插槽可将 v-if 放在插槽声明或直接子节点上;v-show 或子组件内部返回空内容仍可能占位。
自定义消息渲染上下文
自定义消息渲染器可通过 useChatRenderContext(props) 获取当前会话、消息状态和滚动容器。以下代码位于渲染器的 <script setup> 中:
import type { BubbleContentRendererProps } from '@opentiny/tiny-robot'
import { useChatRenderContext } from '@opentiny/tiny-robot-chat'
const props = defineProps<BubbleContentRendererProps>()
const context = useChatRenderContext(props)返回值为 ChatRenderContext | undefined,在 Chat 消息渲染区域之外调用时返回 undefined。状态字段为响应式引用:
| 字段 | 类型 | 含义 |
|---|---|---|
scrollRoot | ComputedRef<HTMLElement | null> | 消息滚动容器,挂载前为 null。 |
conversationId | ComputedRef<string | null | undefined> | 当前会话标识。 |
messageKey | ComputedRef<string> | 当前消息的稳定标识。 |
contentIndex | ComputedRef<number> | 当前内容索引。 |
streamState | ComputedRef<'streaming' | 'finished' | 'unknown'> | 流式生成中、已结束或状态未知。 |
pauseAutoScroll | () => void | 临时暂停自动跟随,在修改展开或收起状态之前同步调用。 |
消息对象和服务端 ID 都可能变化时,请提供唯一且稳定的 renderKey。使用 messageKey 和 contentIndex 可区分渲染内容,无需解析 messageKey。
message.streamState 可显式指定流状态。未指定时根据当前请求的可追踪归属判断:历史消息默认按已结束处理;当前请求中的最新消息状态不足时为 unknown。loading 不决定流状态。
渲染器的定义与注册见 Bubble 自定义渲染器 和 Provider 配置。
类型索引
组件类型
| 类型 | 类别 | 说明 |
|---|---|---|
ChatUIProps | interface | TrChatUI 的属性类型,属性名采用小驼峰写法。 |
ChatUIEmits | interface | TrChatUI 的事件及参数类型。 |
ChatUISlots | interface | TrChat 和 TrChatUI 的插槽及参数类型。 |
ChatUIData | interface | 显示数据;字段行为见 ChatUIData。 |
ChatUIOptions | interface | 界面配置;字段行为见 ChatUIOptions。 |
ChatAnchorOptions | interface | 用户消息目录的显示条件,见 anchor 配置。 |
ChatInputActions | interface | 两入口共用的文本回填、清空与聚焦方法。 |
ChatRenderContext | interface | 自定义消息渲染上下文。 |
ChatCssSize | type | string | number。 |
ChatWelcomeComposerPlacement | type | 'footer' | 'center'。 |
ChatRightAsidePanelId | type | string。 |
ChatRightAsidePanelContext | interface | 当前 panelId 与可选的面板配置。 |
ChatRequestState | type | 请求状态类型。 |
ChatProcessingState | type | 'requesting' | 'completing' | string。 |
会话、消息和操作相关类型见 数据与逻辑配置 API。
展示数据类型
| 类型 | 说明 |
|---|---|
ChatConversationView | 会话列表、当前 ID、标题和可选历史分组。 |
ChatHistoryData | ChatConversationInfo[] | ChatHistoryGroup[]。 |
ChatHistoryGroup | group: string | symbol 与 items。 |
ChatBubbleView | 消息列表容器。 |
ChatSenderView | 输入区 loading、disabled 与 submitDisabled 状态。 |
ChatRequestView | 请求 state 与可选 processingState。 |
ChatModelView | 模型列表、选中值、字段值和加载状态。 |
ChatModelOptionView | 单个模型的名称、字段配置与附加信息。 |
ChatMcpView | MCP 服务列表与工具映射。 |
ChatMcpServerView | 单个 MCP 服务的安装、启用、加载和错误状态。 |
ChatMcpToolView | 单个工具的名称、启用和加载状态。 |
ChatMcpToolMap | Partial<Record<string, readonly ChatMcpToolView[]>>,键为 MCP 服务 ID。 |
界面配置类型
| 类型 | 说明 |
|---|---|
ChatLayoutOptions | 页面显示方式、空状态、内容宽度、间距和两侧栏。 |
ChatSurfaceOptions | 普通页面或浮动窗口配置。 |
ChatComposerLayoutOptions | 欢迎区中输入区的位置。 |
ChatBrandOptions | 品牌名称与图标。 |
ChatLabels | Chat 所有内置中文文案字段。 |
ChatAsideOptions | 左栏显示方式、宽度、拖动调整与开闭状态。 |
ChatRightAsideOptions | 右栏模式、宽度、缩放与面板注册。 |
ChatRightAsidePanelOptions | id 与可选 title。 |
ChatHistoryOptions | 支持 History 的配置,不支持 data、selected 和 onItemAction。 |
ChatBubbleOptions | 气泡渲染、列表和自动滚动配置。 |
ChatBubbleListOptions | 支持 BubbleList 的配置,不支持 messages 和 autoScroll。 |
ChatWelcomeOptions | 支持 Welcome 的配置,所有字段均可选。 |
ChatPromptsOptions | 支持 Prompts 的配置,items?: PromptProps[] 用于设置推荐问题。 |
ChatSenderOptions | 支持 Sender 的配置,不支持 modelValue、defaultValue、loading 和 disabled;defaultActions 使用 ChatSenderDefaultActions。 |
ChatSenderDefaultActions | 输入区默认按钮配置,支持 DefaultActions 中除 submit.disabled 外的配置;提交禁用状态通过 data.sender.submitDisabled 设置。 |
ChatSenderToolbarItem | 左侧工具栏内置项或自定义项,配置顺序、显示和禁用状态,见完整配置。 |
ChatModelOptions | 模型选择器弹出面板所在容器的配置。 |
ChatMcpOptions | 当前为空对象配置。 |
ChatExtensionsOptions | 应用扩展页签、默认页签和二级面板状态。 |
插槽参数
| 类型 | 字段 |
|---|---|
ChatHeaderSlotProps | title;isEmpty;conversation;createConversation();isLeftAsideOpen;openLeftAside();closeLeftAside();toggleLeftAside();openRightAside(panel?);closeRightAside() |
ChatLeftAsideSlotProps | conversation;isOpen;isDock;createConversation();switchConversation(id);renameConversation(id, title);deleteConversation(id);openLeftAside();closeLeftAside();toggleLeftAside() |
ChatLeftAsideContentSlotProps | 包含 ChatLeftAsideSlotProps 的全部参数,另有 history?: ChatHistoryData。 |
ChatHistoryItemPrefixSlotProps | item: ChatConversationInfo |
ChatRightAsidePanelSlotProps | panelId?;panel?;panels;右栏打开、关闭、切换和激活方法;isRightAsideOpen |
ChatRightAsideTitleSlotProps | panelId?;panel? |
ChatSenderSlotProps | value;loading;disabled;submitDisabled;输入更新、提交、取消和清空方法 |
ChatExtensionSlotProps | activeTabId?: string |
ChatMainSlotProps | messages;request?;conversation |
ChatEmptyStateSlotProps | messages;request?;conversation;isEmpty: true;renderComposer() |
ChatBubbleSlotProps | messages;role?;messageIndexes |
ChatBubbleContentFooterSlotProps | messages;role?;messageIndexes;contentIndex?: number |
事件参数
| 类型 | 字段 |
|---|---|
ChatSendPayload | text: string;structuredData?: ChatStructuredData;externalPayloads?: SenderExternalPayload[] |
ChatStructuredData | ChatStructuredDataItem[] |
ChatStructuredDataItem | type: string 用于标识数据种类;其他字段按该种类提供,字段值类型为 unknown。 |
ChatHistoryActionPayload | action: HistoryMenuItem;conversation: ChatConversationInfo;defaultPrevented;preventDefault() |
ChatSwitchConversationPayload | conversationId: string |
ChatRenameConversationPayload | conversationId: string;title: string |
ChatPromptClickPayload | event: MouseEvent;item: PromptProps |
SenderDragPayload | event: DragEvent;target: HTMLElement |
ChatModelSelectPayload | modelId: string | null |
ChatModelFeatureValueChangePayload | modelId: string;featureKey: string;previous: boolean | string | undefined;next: boolean | string;featureValues: ModelSelectorFeatureValues |
ChatMcpAddServerPayload / ChatMcpRemoveServerPayload | serverId: string |
ChatMcpCreateServerPayload | config: McpExtensionFormValue;source: McpExtensionFormMode |
ChatMcpServerEnabledChangePayload | serverId: string;enabled: boolean |
ChatMcpToolEnabledChangePayload | serverId: string;toolId: string;enabled: boolean |
ChatAsideOpenChangePayload | open: boolean;source: 'user' | 'viewport' |
ChatBubbleStateChangePayload | key;value;messageIndex;contentIndex |
ChatBubbleEventPayload | name;payload?;messageIndex;contentIndex |
LayoutFloatingState、LayoutFloatingDragDetail、LayoutFloatingResizeDetail、HistoryMenuItem、PromptProps、BubbleMessage、ModelSelectorField、ModelSelectorFeatureValues、McpExtensionFormValue 和 McpExtensionFormMode 来自 @opentiny/tiny-robot。气泡状态、事件和渲染器见 Bubble。
常见问题
页面没有高度或消息区不滚动
聊天界面默认使用浏览器视口高度。嵌入已有页面时,检查父容器是否有明确高度,以及是否设置了 --tr-layout-height: 100%,见 基础布局。使用百分比高度时,祖先容器也需要有明确高度;使用 flex 布局时,检查消息区所在的子项是否允许收缩(如设置 min-height: 0)。