Skip to content

界面与交互配置 ​

本文介绍如何通过 ui 配置和插槽调整 TrChat 的布局、输入区、消息展示及交互,以及如何单独使用界面组件 TrChatUI。首次接入见 快速上手。

界面配置与定制 ​

按照 快速上手 接入 TrChat 后,可以通过 ui 配置和插槽调整界面。需要直接提供显示数据并自行处理界面事件时,参考仅使用界面组件。

ui 是一个界面配置对象,用于设置布局、文案和各区域的显示方式;只需配置需要调整的部分,其余使用默认值。常用的字段如下,具体配置见后续示例:

ts
import type { ChatUIOptions } from '@opentiny/tiny-robot-chat'

const ui: ChatUIOptions = {
  layout: {}, // 页面布局、侧栏和浮动窗口
  labels: {}, // 按字段修改界面文案
  history: {}, // 历史会话列表
  welcome: {}, // 欢迎区内容
  prompts: {}, // 推荐问题
  sender: {}, // 输入区配置
  bubble: {}, // 消息展示配置
}

将配置好的 ui 传给 TrChat:

vue
<TrChat :runtime="runtime" :ui="ui" />

下面介绍常用配置和示例,完整字段见 界面配置。需要自定义界面内容时,见 插槽定制。

基础布局 ​

调整内容宽度、侧栏显示和空会话输入区的位置,适应不同页面布局。

loading

此演示使用 TrChatUI 展示布局效果,相同的 ui 配置也适用于 TrChat。

通过 ui.layout 设置布局,常用字段如下:

配置用途和可选值默认值
contentMaxWidth消息、欢迎区和输入区的最大内容宽度;支持数字或 CSS 长度,如 640、'80%'980,数字单位为 px
showScrollToBottom是否显示内置回到底部按钮;分享等场景可设为 falsetrue
leftAside左侧栏配置对象;设为 false 隐藏整个左侧栏显示侧栏,默认收起
leftAside.width左侧栏展开宽度,单位为 px300
leftAside.collapsedWidth左侧栏收起宽度,单位为 px56
leftAside.defaultOpen是否默认展开,之后可通过界面按钮切换false
composer.welcome空会话时的输入区位置:'footer' 位于页面底部;'center' 随内置欢迎区居中显示,需保留默认内容布局、欢迎区和输入区'footer'

例如,缩小内容宽度并默认展开左侧栏:

ts
const ui: ChatUIOptions = {
  layout: {
    contentMaxWidth: 640,
    leftAside: { width: 240, collapsedWidth: 48, defaultOpen: true },
    composer: { welcome: 'center' },
  },
}

已有消息或隐藏欢迎区时,输入区仍位于底部。通过插槽自定义主区或空会话内容时,输入区的居中位置需自行设置,详见插槽说明。

完整字段和默认值见 布局配置。

聊天界面默认使用浏览器视口高度。嵌入已有页面时,可将 TrChat 放入高度明确的容器,并设置 --tr-layout-height: 100%,使界面填满该容器。例如,将高度设为 600px:

vue
<template>
  <main class="chat-page">
    <TrChat :runtime="runtime" />
  </main>
</template>

<style scoped>
.chat-page {
  --tr-layout-height: 100%;
  height: 600px;
}
</style>

调整宽度与间距 ​

消息列表与输入框默认对齐,回到底部按钮默认位于右侧。在组件或父容器上设置 CSS 变量,可以调整内容宽度、内边距和按钮位置:

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 变量。

配置欢迎区和推荐问题 ​

设置欢迎内容和推荐问题,帮助用户开始对话。

loading

没有消息时,通过 ui.welcome 设置欢迎标题和描述,通过 ui.prompts 提供推荐问题,帮助用户开始对话。

配置用途默认值
welcome.title欢迎标题,类型为 string'TinyRobot AI 助手'
welcome.description欢迎描述,类型为 string'您好,我是TinyRobot,您专属的 AI 智能专家'
prompts.items推荐问题数组;每项的 label 为必填显示文字,description 为可选补充说明[],不显示推荐问题
prompts.wrap是否允许推荐问题换行,类型为 booleanfalse

例如,设置项目助手的欢迎内容和两个推荐问题:

ts
const ui: ChatUIOptions = {
  welcome: {
    title: '项目助手',
    description: '选择一个问题开始,或输入自己的问题。',
  },
  prompts: {
    wrap: true,
    items: [
      { label: '解释代码', description: '请解释这段代码的作用,并给出使用建议。' },
      { label: '撰写周报', description: '请帮我整理本周工作,生成一份简洁的周报。' },
    ],
  },
}

点击推荐问题会触发 prompt-click,不会自动填入或发送。下面使用 Sender 的模板扩展将问题填入输入框,用户可编辑后发送:

ts
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 }]
}
vue
<TrChat :runtime="runtime" :ui="ui" @prompt-click="handlePromptClick" />

将 ui.welcome 或 ui.prompts 设为 false 可隐藏对应区域。发送首条消息后不再显示欢迎内容;返回新会话可重新查看。

更多展示配置见 Welcome 和 Prompts。

控制欢迎页显示 ​

默认在没有可见消息时显示欢迎页。应用也可以通过 is-empty 自行决定,例如仅在没有活动会话时显示:

vue
<TrChat :runtime="runtime" :is-empty="!activeConversation" />

activeConversation 为应用当前选中的会话。已有会话即使尚无消息,也会保持工作区布局。此设置同时影响页头插槽的状态和输入框位置,TrChatUI 同样适用。

配置输入区 ​

为输入框添加提及、可编辑模板和自定义按钮,满足不同输入需求。

loading

示例通过 ui.sender.toolbar 配置“写报告”和“常用问题”,分别填入可编辑模板和预设问题;通过 sender-footer-right 添加“使用说明”,配合 sender-header 展示操作提示。

点击“使用说明”显示或隐藏提示,再点击“写报告”或“常用问题”查看提示更新。输入框上方可按需显示标签或提示,隐藏后不保留空白:

vue
<template v-if="showHelp" #sender-header>
  <p>{{ helpText }}</p>
</template>

通过 ui.sender 调整输入框、配置提及和模板功能;通过 ui.sender.toolbar 或输入区插槽添加自定义按钮,无需重新实现消息发送和取消。

接收拖入和粘贴的文件 ​

TrChat 和 TrChatUI 会将 Sender 上的文件拖拽与粘贴事件交给宿主。宿主需在 sender-drag-over 中对原生 event 调用 preventDefault(),浏览器才会触发放置事件并交付文件。

loading
vue
<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。

loading

TrChat 与 TrChatUI 均支持这些操作,完整说明见 TrChat 组件方法 和 TrChatUI 组件方法。

基础设置 ​

以下默认值为 Chat 的输入区配置,与单独使用 Sender 时不同:

配置用途和可选值默认值
placeholder输入框为空时的提示,类型为 string未发送时为 '请输入你的问题...',发送中为 '思考中...'
mode'single' 单行、'multiple' 多行'multiple'
maxLength最大输入字数,类型为 number;超出后保留内容,但无法提交1000
showWordLimit是否显示字数统计,类型为 booleantrue
clearable有内容时是否显示清空按钮,类型为 booleantrue
defaultActions配置默认发送和清空按钮的提示等选项沿用内置按钮配置
extensions输入扩展数组,如提及、模板和联想[]

例如,限制为 300 字并修改按钮提示:

ts
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 中使用,默认不启用这些扩展。

ts
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 使用相同配置。

loading
  • 追加按钮:使用 (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 回答。

loading

演示中的自定义外观还将 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 设置自定义头像:

ts
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 消息下显示错误提示,帮助用户了解失败原因。

loading

演示使用 useChatRuntime 展示默认错误提示,不包含自定义提示组件;默认提示不提供重试按钮。

通过 ui.bubble.bubbleProvider.errorRenderer 替换错误提示,传入 null 可关闭默认提示。CustomErrorRenderer 表示应用自行实现的错误展示组件。

ts
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 后,可通过目录快速跳转到用户消息。定位后气泡短暂闪动,滚动时同步当前目录项;点击回到底部可返回最新内容。

loading
ts
const ui: ChatUIOptions = {
  anchor: {},
}

目录默认关闭,开启后在浏览器视口宽度小于 768px 时隐藏。应用也可以控制其显示,完整选项见 anchor 配置。

配置历史会话列表 ​

自定义历史会话的排序、筛选和分组,同时保留内置会话操作。

loading

浏览器宽度低于 960px 时,先通过界面按钮展开会话列表。

“置顶”和日期分组使用预设数据,不包含置顶操作或自动按日期归类。演示使用独立的本地存储保存示例会话,刷新后保留修改结果。

TrChat 默认使用 Runtime 的会话生成历史列表,支持切换、重命名和删除,无需额外传入 historyData。需要自定义排序、筛选或分组展示时,可通过 historyData 提供列表数据,继续使用内置列表和会话操作。

historyData 支持会话数组或 { group, items }[] 分组数组,列表按传入顺序展示;传入 [] 时显示空列表。group 是分组标题,items 是该组的会话数组,每项的 id 应对应 Runtime 中已有的会话。

下面以分组展示为例,按会话中的 metadata.group 展示“置顶”“昨天”“30天内”,未设置分组的会话放入“30天内”。metadata.group 是本例约定的字段,不是内置日期或置顶配置:

ts
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),
)
vue
<TrChat class="chat-history" :runtime="runtime" :ui="ui" :history-data="historyData" />

本例通过 computed 根据 Runtime 会话生成分组,列表会随会话新增、重命名或删除更新。

ui.history 用于配置菜单等展示选项,可不传,默认菜单包含重命名和删除;设为 false 可隐藏历史列表。例如,只保留重命名菜单:

ts
const ui: ChatUIOptions = {
  history: { menuItems: [{ id: 'rename', text: '重命名' }] },
}

左侧栏默认使用页面背景色,可通过 --tr-chat-ui-left-aside-bg 调整。选中颜色和圆角可通过 History 的 CSS 变量调整,在 TrChat、TrChatUI 或其父容器上设置即可:

css
.chat-history {
  --tr-history-item-selected-bg: #e4edfd;
  --tr-history-item-selected-color: #3964fe;
  --tr-history-item-border-radius: 8px;
}

完整展示配置见 History 属性 和 样式变量,程序调用见 会话管理。

插槽定制 ​

在页头、消息和输入区添加自定义内容。

loading

本示例使用以下插槽:

  • layout-header:自定义页头。
  • bubble-content-footer:添加消息反馈按钮。
  • composer-after:添加输入框下方的提示。

额度提醒等页头内容也可放在 layout-header 中,由应用控制显示和排版。

完整插槽列表及参数见 插槽。

右侧面板 ​

在聊天界面旁展示引用资料或结果预览。

loading

需要在会话旁展示引用资料、预览结果等内容时,通过 ui.layout.rightAside.panels 配置面板列表,再用 layout-right-aside-panel 插槽提供内容。

panels 是面板配置对象数组,默认为空数组 [],每项包含以下字段:

字段用途必填
id面板唯一标识,与 layout-right-aside-panel 插槽中的 panelId 对应。是
title面板标题,省略时显示 ui.labels.rightAsideTitle(默认 '详情')。否

id 不能重复,也不能使用内置保留标识 mcp。右侧栏默认宽度为 320px,初始关闭;没有自定义面板或 MCP 数据时不会显示。

ts
const ui: ChatUIOptions = {
  layout: {
    rightAside: {
      panels: [{ id: 'preview', title: '结果预览' }],
    },
  },
}

仅需设置初始打开状态时,使用 defaultRightAsideOpen 属性(默认 false);defaultActiveRightAsidePanelId 属性指定初始面板,未设置时选择第一个可用面板:

vue
<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 和应用页签会显示在同一右侧面板中。

loading

扩展页签通过以下接口处理交互和自定义内容:

  • 事件:组件触发 extension-action 后,应用需要写回安装状态或开关状态。
  • 页头插槽:通过 extension-header-actions 添加导入等页头操作。
  • 二级面板:将 ui.extensions.overlayOpen 设为 true,并通过 extension-overlay 插槽提供导入表单或详情;显示和关闭状态由应用管理。

mcp 是内置保留页签 ID,不能作为应用页签 ID。MCP 服务、工具和自定义添加的配置方式见 ChatUIData.mcp。

完整配置、事件和插槽说明见 ui.extensions。

浮动聊天窗口 ​

将聊天界面显示为可拖动、缩放的浮动窗口。

loading

ui.layout.surface 用于配置聊天界面的显示方式,可在父容器中显示,也可作为浮动窗口显示。它包含以下两个属性:

  • mode:选择显示方式,默认 'normal',显示在父容器中;设为 'floating' 时显示为浮动窗口。
  • floatingOptions:配置浮动窗口的拖动、缩放和尺寸限制,仅在 mode 为 'floating' 时生效。

floatingOptions 的常用字段如下:

字段用途默认值或行为
draggable是否允许拖动窗口true
resizable是否允许拖动边缘缩放窗口false
minWidth / minHeight最小宽度、最小高度,单位为 px320 / 240,同时受视口尺寸限制
maxWidth / maxHeight最大宽度、最大高度,单位为 px不超过当前视口宽度、高度

窗口默认支持拖动,下面开启缩放:

ts
const ui: ChatUIOptions = {
  layout: {
    surface: {
      mode: 'floating',
      floatingOptions: { resizable: true },
    },
  },
}

需要自定义窗口的位置和尺寸时,设置 floatingState 属性:

ts
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,
})
vue
<TrChat :runtime="runtime" :ui="ui" v-model:floating-state="floatingState" />

上例将窗口放在右上角,距右侧 24px、顶部 72px,宽高均为 520px。

未设置 floatingState 时,窗口默认居中,尺寸为 420px × 560px;实际尺寸不会超过浏览器可视区域。

传入 floatingState 时,需同步更新状态,建议使用 v-model:floating-state。

完整字段及默认值见 拖动与缩放配置和窗口位置与尺寸。

响应式布局与侧栏缩放 ​

切换桌面和移动端布局,并在桌面布局下调整侧栏宽度。

loading

演示切换的是布局效果,不改变浏览器宽度;浏览器可视区域宽度低于 960px 时,即使选择桌面布局,也会自动使用抽屉。

侧栏支持两种布局,通过 ui.layout.leftAside.mode 或 ui.layout.rightAside.mode 设置,默认是 'dock':

  • 桌面布局('dock'):侧栏与消息区并排显示。
  • 移动端布局('drawer'):侧栏以抽屉形式覆盖消息区。

浏览器可视区域宽度低于 960px 时,组件会自动使用抽屉布局。

仅缩小父容器不会触发自动切换;需要在较窄容器中使用抽屉时,将侧栏的 mode 设为 'drawer'。

桌面布局下,将侧栏配置中的 resizable 设为 true,可拖动边缘调整宽度,默认不启用。通过 minWidth、maxWidth 设置调整范围,单位为 px;抽屉布局不支持调整宽度。例如:

ts
const ui: ChatUIOptions = {
  layout: {
    leftAside: {
      defaultOpen: true,
      resizable: true,
      minWidth: 240,
      maxWidth: 420,
    },
  },
}

完整侧栏配置及默认值见 布局配置。

仅使用界面组件 ​

TrChatUI 是 TrChat 使用的界面组件,只负责展示数据和触发事件。需要自行处理消息请求、会话保存和数据更新时,可单独使用,前面的 ui 配置和插槽同样适用。

复用已有会话或请求逻辑,也可以继续使用 TrChat,详见高级用法。

loading

数据与事件处理 ​

通过 data 提供会话列表、消息和发送状态。用户发送消息、取消请求或切换会话时,在对应事件中执行项目的操作,并更新显示数据。

ts
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 同步输入内容:

vue
<TrChatUI :data="data" v-model:input-value="inputValue" @submit="handleSubmit" @cancel="handleCancel" />

submit 和 cancel 只通知项目处理操作,不会自动请求或停止服务。发送后是否清空输入、何时追加回答,由对应的事件处理方法决定。

输入区的清空按钮会清空输入;使用 v-model 时,会同步更新绑定值。清空输入不会删除消息或取消请求。

完整数据字段见 ChatUIData,属性和事件见 TrChatUI API。

受控与非受控 ​

输入内容、侧栏开闭等界面状态支持两种管理方式:

  • 受控:由项目传入当前值并同步更新,适合填入预设问题、发送后清空输入或从外部打开侧栏。
  • 非受控:由组件维护状态,无需从外部同步更新,可按需设置初始值。

以输入内容为例,受控方式通过 v-model:input-value 绑定当前内容,inputValue 可初始化为 '':

vue
<TrChatUI v-model:input-value="inputValue" @submit="handleSubmit" />

非受控方式不传入 inputValue,由组件维护输入。可通过 defaultInputValue 设置初始内容,默认 '';后续修改该属性不会重置当前输入:

vue
<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组件管理右栏开闭时的初始值。booleanfalse否
active-right-aside-panel-id由应用管理的当前右栏面板,需处理更新事件。ChatRightAsidePanelId—否
default-active-right-aside-panel-id组件管理当前面板时的初始值。ChatRightAsidePanelId第一个可用面板否

事件 ​

TrChat 会直接处理提交、取消、会话切换、模型选择和 MCP 开关操作,调用 Runtime 对应方法;这些事件不会再次向外发出。

Sender 的文件拖拽与粘贴事件会透传给应用,由应用处理文件,详见接收拖入和粘贴的文件。

事件参数触发时机
runtime-action-errorChatRuntimeActionErrorPayloadRuntime 操作失败;send 错误详情仍从所属消息读取。
model-feature-value-changeChatModelFeatureValueChangePayload用户修改当前模型的设置时触发。随后 TrChat 会请求 Runtime 更新该设置。
history-actionChatHistoryActionPayload历史菜单操作;删除操作可调用 preventDefault() 阻止默认 Runtime 删除。
prompt-clickChatPromptClickPayload点击提示项。
mcp-create-serverChatMcpCreateServerPayload请求创建 MCP 服务;Runtime 不处理创建表单。
extension-actionExtensionManagerActionEvent自定义扩展页签中的操作被触发。
extension-name-clickExtensionManagerNameClickEvent点击自定义扩展页签中的条目名称。
extension-tab-changeExtensionManagerTabChangeEvent扩展管理器切换页签。
bubble-state-changeChatBubbleStateChangePayload气泡内部状态变化。
bubble-eventChatBubbleEventPayload气泡内容发出自定义事件。
sender-drag-enter / sender-drag-over / sender-drag-leave / sender-dropSenderDragPayloadSender 上对应的原生文件拖拽事件;放置文件需在 sender-drag-over 中调用 event.preventDefault()。
sender-pasteClipboardEventSender 收到粘贴事件。
left-aside-open-change / right-aside-open-changeChatAsideOpenChangePayload侧栏状态变化。
update:right-aside-openboolean通知应用更新右栏开闭状态。
update:active-right-aside-panel-idChatRightAsidePanelId | undefined通知应用更新当前右栏面板。
update:floating-stateLayoutFloatingState通知应用更新浮动窗口的位置和尺寸。
floating-drag-start / floating-drag / floating-drag-endLayoutFloatingDragDetail浮动窗口开始拖动、拖动中或拖动结束。
floating-resize-start / floating-resize / floating-resize-endLayoutFloatingResizeDetail浮动窗口开始缩放、缩放中或缩放结束。

组件方法 ​

方法签名说明
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组件管理右栏开闭时的初始值。booleanfalse否
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 时,不会自动创建会话,需由回调自行创建或选择会话,见 自定义发送流程。

以下事件需由项目自行处理:

事件参数说明
submitChatSendPayload发送请求并更新消息、请求状态和输入值。
update:input-valuestring由应用管理输入时,更新输入内容。
cancel / clear无中止请求或清空输入;组件不会修改外部请求。
create-conversation无清空当前会话或创建新会话。
switch-conversationChatSwitchConversationPayload切换数据源并更新 data.conversation.activeId。
rename-conversationChatRenameConversationPayload保存新标题并更新会话列表。
history-actionChatHistoryActionPayload处理历史菜单操作;TrChatUI 本身不会执行删除。
prompt-clickChatPromptClickPayload决定填充输入、直接提交或执行其他操作。
bubble-state-change / bubble-event对应 payload更新消息状态或处理自定义气泡事件。
model-selectChatModelSelectPayload更新 data.model.selectedId。
model-feature-value-changeChatModelFeatureValueChangePayload用户修改设置时触发。由应用根据 featureValues 更新 data.model.featureValues。
sender-drag-enter / sender-drag-over / sender-drag-leave / sender-dropSenderDragPayloadSender 上对应的原生文件拖拽事件;放置文件需在 sender-drag-over 中调用 event.preventDefault()。
sender-pasteClipboardEventSender 收到粘贴事件。
mcp-add-server / mcp-remove-serverChatMcpAddServerPayload / ChatMcpRemoveServerPayload更新 MCP 服务列表。
mcp-create-serverChatMcpCreateServerPayload创建并接入自定义 MCP 服务。
mcp-server-enabled-changeChatMcpServerEnabledChangePayload更新 MCP 服务启用状态。
mcp-tool-enabled-changeChatMcpToolEnabledChangePayload更新工具启用状态。
extension-actionExtensionManagerActionEvent处理自定义扩展页签中的安装、开关等操作。
extension-name-clickExtensionManagerNameClickEvent处理自定义扩展条目的名称点击。
extension-tab-changeExtensionManagerTabChangeEvent处理扩展管理器页签切换。
left-aside-open-change / right-aside-open-changeChatAsideOpenChangePayload由应用管理侧栏时更新开闭状态;source 区分用户操作和浏览器窗口变化。
update:right-aside-openboolean更新应用管理的右栏开闭状态。
update:active-right-aside-panel-idChatRightAsidePanelId | undefined更新应用管理的当前面板。
update:floating-stateLayoutFloatingState更新应用管理的浮动窗口位置和尺寸。
floating-drag-start / floating-drag / floating-drag-endLayoutFloatingDragDetail处理窗口开始拖动、拖动中或拖动结束。
floating-resize-start / floating-resize / floating-resize-endLayoutFloatingResizeDetail处理窗口开始缩放、缩放中或缩放结束。

组件方法 ​

方法签名说明
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。

字段类型对应区域默认行为
conversationChatConversationView页头标题、会话列表和当前选中空列表、无选中,标题为“新对话”。
bubbleChatBubbleView消息主区消息为空时显示欢迎布局;传入 is-empty 时以该值为准。
senderChatSenderView输入区可用性与加载反馈三个布尔状态均为 false。
modelChatModelView输入区模型选择器与字段配置不显示模型选择器。
mcpChatMcpView输入区 MCP 入口与内置右栏不显示 MCP 入口或面板。
requestChatRequestView插槽参数、消息流状态推断undefined;不单独显示请求状态。

conversation ​

字段类型默认值说明
itemsChatConversationInfo[][]原始会话列表;未提供 history 时按当前数组顺序生成默认历史数据。
activeIdstring | nullnull当前选中会话 ID;应用处理切换事件后更新。
titlestring新对话页头标题;空字符串会原样显示。
historyChatHistoryData—应用提供的排序或分组结果;提供后优先于根据 items 生成的默认历史。

ChatConversationInfo、ChatMessageItem 及消息内容结构见 数据与逻辑配置:会话、消息与发送。

bubble 与 sender ​

字段类型默认值说明
bubble.messagesChatMessageItem[][]消息列表;组件不会追加、删除或保存消息。
sender.loadingbooleanfalse显示停止按钮,点击触发 cancel。
sender.disabledbooleanfalse禁用整个输入区。
sender.submitDisabledbooleanfalse仅禁止提交;输入仍可编辑,loading 时仍可取消。

单独使用 TrChatUI 时,AI 消息的 state.error 用于显示错误提示,需要由应用写入。

model ​

字段类型说明
optionsChatModelOptionView[]可选模型列表;为空时显示没有可选模型的提示。
selectedIdstring | null当前模型;选择后应用通过 model-select 写回。
featureValuesModelSelectorFeatureValues当前模型字段值;与模型的 features 配合使用。
renderTriggerTextModelSelectorProps['renderTriggerText']自定义模型选择器触发器文本。
pendingFeatureKeysreadonly string[]当前模型正在异步更新的字段 key。
featureValuesDisabledboolean禁止字段修改。
selectingboolean模型切换中的整体等待状态。

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 ​

字段类型说明
serversChatMcpServerView[]MCP 服务的安装、启用、加载和错误状态;应用处理对应事件后写回。
toolsChatMcpToolMap以 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 的作用
layoutChatLayoutOptions未配置的布局字段使用默认值;窗口、输入区和左右侧栏可分别配置。mobileBreakpoint 默认 960,视口宽度低于该值启用移动布局;非正数或非有限值回退到 960。不支持;使用 leftAside / rightAside。
brandChatBrandOptions提供的选项覆盖对应的默认名称或图标,默认名称为 TinyRobot。不支持。
labelsPartial<ChatLabels>按字段覆盖内置中文文案;也会更新默认欢迎文案和历史菜单文案。不支持。
headerfalse省略时显示默认 Header。移除 Header。
historyfalse | ChatHistoryOptions与默认 History 选项合并;menuItems 整体替换默认重命名、删除菜单。隐藏历史列表,左栏品牌和操作仍在。
welcomefalse | ChatWelcomeOptions与默认欢迎区配置合并;未提供标题和描述时使用 labels 中的文案。空会话不显示欢迎区。
promptsfalse | ChatPromptsOptions与默认 Prompts 选项合并;items 整体替换,默认 []。不显示提示项。
bubbleChatBubbleOptions合并气泡渲染和列表配置;autoScroll 默认 true,system 消息默认隐藏。不支持。
senderfalse | ChatSenderOptions提供的选项覆盖对应的默认输入区选项。移除输入区及其插槽。
modelfalse | ChatModelOptions默认 {};只有 data.model 存在时显示。即使存在模型数据也隐藏选择器。
mcpfalse | ChatMcpOptions默认 {};当前没有额外配置字段。即使存在 MCP 数据也隐藏入口和面板。
extensionsfalse | ChatExtensionsOptions注册应用扩展页签,可设置默认页签及二级面板状态。不注册应用扩展页签。
anchorfalse | 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 会被忽略。

ts
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' 随内置欢迎区居中显示,需保留默认内容布局、欢迎区和输入区。
contentMaxWidthstring | number980消息、欢迎区和输入区的最大内容宽度。
panelPadding / panelGapstring | number12 / 12消息内容纵向内边距与区域间距;水平内边距通过 CSS 变量设置。
leftAsidefalse | ChatAsideOptionsDock,300 / 56,关闭左栏;open 由应用管理,defaultOpen 仅设置组件管理开闭时的初始值。
rightAsidefalse | ChatRightAsideOptionsDock,宽 320,不可缩放右栏;只有注册应用面板或存在可见 MCP 数据时才渲染。

leftAside 和 rightAside 的配置字段如下,尺寸字段的单位均为 px:

字段类型或可选值默认值或未配置行为用途
mode'dock' | 'drawer''dock';浏览器视口低于 960px 时自动使用 'drawer'侧栏与消息区并排,或以抽屉覆盖消息区
widthnumber左栏 300;右栏 320展开宽度
collapsedWidthnumber左栏 56;右栏 0收起宽度;移动端使用 0
resizablebooleanfalse允许拖动调整展开宽度,仅桌面端并排模式生效
minWidthnumber左栏 200;右栏 240最小展开宽度
maxWidthnumber左栏 560;右栏 640最大展开宽度
openboolean未设置时由组件管理仅左栏;由项目控制开闭时,需处理 left-aside-open-change 并更新值
defaultOpenbooleanfalse仅左栏;由组件管理开闭时的初始值
showClosebooleantrue仅右栏;是否显示关闭按钮
panelsChatRightAsidePanelOptions[][]仅右栏;每项必填 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 时不显示目录。

字段类型默认值说明
visiblebooleantrue是否显示目录,可用于分享等场景下临时隐藏。
minWidthnumber768显示目录所需的最小浏览器视口宽度,单位为 px;不按聊天容器宽度判断。
hoverExpandDelaynumber300鼠标悬停后展开目录的延迟,单位为 ms。设为 0 立即展开。

点击目录或向上浏览会暂停自动跟随,点击回到底部可恢复已启用的自动跟随。

各区域配置 ​

配置项说明相关组件
brandname?: string、logo?: unknown。—
labels会话创建/重命名/删除、侧栏展开/收起、输入占位、模型、MCP、Welcome、右栏和滚动到底部等文案。—
history默认菜单为重命名和删除;Chat 固定管理 data、selected 与事件。History
welcome默认标题与描述来自 labels.welcomeTitle、labels.welcomeDescription。Welcome
promptsitems?: PromptProps[],其余展示选项继承 Prompts。Prompts
bubbleautoScroll、bubbleProvider、bubbleList;消息错误提示配置见消息错误提示配置。Bubble
sender默认 mode: 'multiple'、clearable: true、maxLength: 1000、showWordLimit: true;值和禁用状态由 Chat 管理。Sender
modelappendTo?: ModelSelectorProps['appendTo'],默认跟随 floating Layout 的 overlay host。ModelSelector
mcpRecord<string, never>,当前没有配置字段。—
extensionstabs 注册 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 仍用于无障碍名称和悬浮提示。

ts
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-width980px980px页头、消息区和输入区的内容最大宽度。
--tr-chat-ui-content-padding-inline24px16px各区域共享的水平内边距。
--tr-chat-ui-panel-padding12px12px消息内容纵向内边距。
--tr-chat-ui-panel-gap12px12px内容容器的子项间距。
--tr-chat-ui-header-padding24px 24px 016px 16px 0页头内边距,包含自定义页头。
--tr-chat-ui-main-padding12px 24px12px 16px消息区内边距。
--tr-chat-ui-footer-padding0 24px 24px0 16px 16px底部输入区内边距。
--tr-chat-ui-header-gap8px8px内置页头子项间距,自定义页头自行排版。
--tr-chat-ui-message-list-padding8px 08px 0气泡列表内边距。
--tr-chat-ui-composer-gap0px0px输入框与 composer-before、composer-after 内容的间距。
--tr-chat-ui-welcome-composer-gap16px16px内置居中欢迎区与输入框的间距。
--tr-chat-scroll-button-aligncentercenter回到底部按钮的位置: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-headerChatHeaderSlotProps替换页头。
layout-left-asideChatLeftAsideSlotProps替换左侧展开面板。
layout-left-aside-brand / layout-left-aside-actions / layout-left-aside-railChatLeftAsideSlotProps分别替换左栏品牌、操作和收起时的操作区域。
layout-left-aside-footerChatLeftAsideSlotProps在左栏底部添加内容。
layout-left-aside-contentChatLeftAsideContentSlotProps替换左栏内容区,包括默认会话列表。
layout-left-aside-history-item-prefixChatHistoryItemPrefixSlotProps在历史项前添加内容。
layout-right-asideChatRightAsidePanelSlotProps替换整个右栏及其面板。
layout-right-aside-titleChatRightAsideTitleSlotProps替换应用添加的面板标题。
layout-right-aside-panelChatRightAsidePanelSlotProps提供应用添加的面板内容。
layout-mainChatMainSlotProps替换消息区和空状态内容,保留外层滚动区域;优先于 layout-empty-state。
layout-empty-stateChatEmptyStateSlotProps替换没有消息时的内容;需要显示默认输入区时,调用 renderComposer()。
layout-footerChatSenderSlotProps完整替换默认 Sender;使用后默认输入框及其内部插槽不渲染,需自行连接输入和提交操作;保留 composer-before 和 composer-after。
composer-beforeChatSenderSlotProps在输入框外上方添加内容,不替换输入框;随输入区显示,ui.sender: false 时隐藏。
composer-afterChatSenderSlotProps在输入框外下方添加提示、链接等内容,不替换输入框;随输入区显示,ui.sender: false 时隐藏。
sender-header无在默认输入框内部上方添加内容。
input-prefix无在默认多行输入框首行前添加内容。
sender-content{ editor }替换默认输入框的编辑区域。
sender-actions-inlineSenderSlotScope在默认行内操作区、默认操作之前添加内容。
sender-footerSenderSlotScope在默认多行输入框内部底部左侧添加内容,与模型和工具操作同一区域;提供插入、聚焦和状态方法。
sender-footer-rightSenderSlotScope在默认多行输入框内部底部右侧、默认操作按钮前添加内容;提供插入、聚焦和状态方法。
welcome-footer / prompts-footer无扩展对应区域。
extension-header-actions / extension-overlayChatExtensionSlotProps为注册的应用扩展页签添加页头操作或二级面板内容。
bubble-prefix / bubble-suffix / bubble-afterChatBubbleSlotProps在消息周围添加内容。
bubble-content-footerChatBubbleContentFooterSlotProps在消息内容下方添加内容。

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> 中:

ts
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。状态字段为响应式引用:

字段类型含义
scrollRootComputedRef<HTMLElement | null>消息滚动容器,挂载前为 null。
conversationIdComputedRef<string | null | undefined>当前会话标识。
messageKeyComputedRef<string>当前消息的稳定标识。
contentIndexComputedRef<number>当前内容索引。
streamStateComputedRef<'streaming' | 'finished' | 'unknown'>流式生成中、已结束或状态未知。
pauseAutoScroll() => void临时暂停自动跟随,在修改展开或收起状态之前同步调用。

消息对象和服务端 ID 都可能变化时,请提供唯一且稳定的 renderKey。使用 messageKey 和 contentIndex 可区分渲染内容,无需解析 messageKey。

message.streamState 可显式指定流状态。未指定时根据当前请求的可追踪归属判断:历史消息默认按已结束处理;当前请求中的最新消息状态不足时为 unknown。loading 不决定流状态。

渲染器的定义与注册见 Bubble 自定义渲染器 和 Provider 配置。

类型索引 ​

组件类型 ​

类型类别说明
ChatUIPropsinterfaceTrChatUI 的属性类型,属性名采用小驼峰写法。
ChatUIEmitsinterfaceTrChatUI 的事件及参数类型。
ChatUISlotsinterfaceTrChat 和 TrChatUI 的插槽及参数类型。
ChatUIDatainterface显示数据;字段行为见 ChatUIData。
ChatUIOptionsinterface界面配置;字段行为见 ChatUIOptions。
ChatAnchorOptionsinterface用户消息目录的显示条件,见 anchor 配置。
ChatInputActionsinterface两入口共用的文本回填、清空与聚焦方法。
ChatRenderContextinterface自定义消息渲染上下文。
ChatCssSizetypestring | number。
ChatWelcomeComposerPlacementtype'footer' | 'center'。
ChatRightAsidePanelIdtypestring。
ChatRightAsidePanelContextinterface当前 panelId 与可选的面板配置。
ChatRequestStatetype请求状态类型。
ChatProcessingStatetype'requesting' | 'completing' | string。

会话、消息和操作相关类型见 数据与逻辑配置 API。

展示数据类型 ​

类型说明
ChatConversationView会话列表、当前 ID、标题和可选历史分组。
ChatHistoryDataChatConversationInfo[] | ChatHistoryGroup[]。
ChatHistoryGroupgroup: string | symbol 与 items。
ChatBubbleView消息列表容器。
ChatSenderView输入区 loading、disabled 与 submitDisabled 状态。
ChatRequestView请求 state 与可选 processingState。
ChatModelView模型列表、选中值、字段值和加载状态。
ChatModelOptionView单个模型的名称、字段配置与附加信息。
ChatMcpViewMCP 服务列表与工具映射。
ChatMcpServerView单个 MCP 服务的安装、启用、加载和错误状态。
ChatMcpToolView单个工具的名称、启用和加载状态。
ChatMcpToolMapPartial<Record<string, readonly ChatMcpToolView[]>>,键为 MCP 服务 ID。

界面配置类型 ​

类型说明
ChatLayoutOptions页面显示方式、空状态、内容宽度、间距和两侧栏。
ChatSurfaceOptions普通页面或浮动窗口配置。
ChatComposerLayoutOptions欢迎区中输入区的位置。
ChatBrandOptions品牌名称与图标。
ChatLabelsChat 所有内置中文文案字段。
ChatAsideOptions左栏显示方式、宽度、拖动调整与开闭状态。
ChatRightAsideOptions右栏模式、宽度、缩放与面板注册。
ChatRightAsidePanelOptionsid 与可选 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应用扩展页签、默认页签和二级面板状态。

插槽参数 ​

类型字段
ChatHeaderSlotPropstitle;isEmpty;conversation;createConversation();isLeftAsideOpen;openLeftAside();closeLeftAside();toggleLeftAside();openRightAside(panel?);closeRightAside()
ChatLeftAsideSlotPropsconversation;isOpen;isDock;createConversation();switchConversation(id);renameConversation(id, title);deleteConversation(id);openLeftAside();closeLeftAside();toggleLeftAside()
ChatLeftAsideContentSlotProps包含 ChatLeftAsideSlotProps 的全部参数,另有 history?: ChatHistoryData。
ChatHistoryItemPrefixSlotPropsitem: ChatConversationInfo
ChatRightAsidePanelSlotPropspanelId?;panel?;panels;右栏打开、关闭、切换和激活方法;isRightAsideOpen
ChatRightAsideTitleSlotPropspanelId?;panel?
ChatSenderSlotPropsvalue;loading;disabled;submitDisabled;输入更新、提交、取消和清空方法
ChatExtensionSlotPropsactiveTabId?: string
ChatMainSlotPropsmessages;request?;conversation
ChatEmptyStateSlotPropsmessages;request?;conversation;isEmpty: true;renderComposer()
ChatBubbleSlotPropsmessages;role?;messageIndexes
ChatBubbleContentFooterSlotPropsmessages;role?;messageIndexes;contentIndex?: number

事件参数 ​

类型字段
ChatSendPayloadtext: string;structuredData?: ChatStructuredData;externalPayloads?: SenderExternalPayload[]
ChatStructuredDataChatStructuredDataItem[]
ChatStructuredDataItemtype: string 用于标识数据种类;其他字段按该种类提供,字段值类型为 unknown。
ChatHistoryActionPayloadaction: HistoryMenuItem;conversation: ChatConversationInfo;defaultPrevented;preventDefault()
ChatSwitchConversationPayloadconversationId: string
ChatRenameConversationPayloadconversationId: string;title: string
ChatPromptClickPayloadevent: MouseEvent;item: PromptProps
SenderDragPayloadevent: DragEvent;target: HTMLElement
ChatModelSelectPayloadmodelId: string | null
ChatModelFeatureValueChangePayloadmodelId: string;featureKey: string;previous: boolean | string | undefined;next: boolean | string;featureValues: ModelSelectorFeatureValues
ChatMcpAddServerPayload / ChatMcpRemoveServerPayloadserverId: string
ChatMcpCreateServerPayloadconfig: McpExtensionFormValue;source: McpExtensionFormMode
ChatMcpServerEnabledChangePayloadserverId: string;enabled: boolean
ChatMcpToolEnabledChangePayloadserverId: string;toolId: string;enabled: boolean
ChatAsideOpenChangePayloadopen: boolean;source: 'user' | 'viewport'
ChatBubbleStateChangePayloadkey;value;messageIndex;contentIndex
ChatBubbleEventPayloadname;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)。