Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
2a00bcb
feat(chat): enhance ChatUI with anchor support and input management
SonyLeo Oct 20, 2026
cb99405
feat(chat): add fixtures and tests for ChatEnhancements and ChatRende…
SonyLeo Oct 20, 2026
2e5a41d
feat(template): enhance template cursor management and backspace logic
SonyLeo Oct 21, 2026
28882e5
feat(anchor): support configurable right inset through CSS variable
SonyLeo Oct 21, 2026
9281fe0
feat(tests): enhance message identity tests and improve auto-scroll f…
SonyLeo Oct 21, 2026
1e1af55
feat(chat): enhance chat draft handling and streaming state management
SonyLeo Oct 21, 2026
3ef31b9
feat(chat): enhance chat sender slots and improve message rendering l…
SonyLeo Oct 22, 2026
c1b3750
feat(chat): add showScrollToBottom option and enhance sender slot han…
SonyLeo Oct 22, 2026
945c87a
feat(chat): add test for restoring draft on send failure after conver…
SonyLeo Oct 22, 2026
5a8f83c
feat(chat): implement file drag-and-drop and paste functionality in c…
SonyLeo Oct 23, 2026
eee2743
feat(chat): update feature icons and add bottom threshold for auto-sc…
SonyLeo Oct 25, 2026
845f49c
feat(chat): enhance mobile responsiveness and add mobile breakpoint o…
SonyLeo Oct 25, 2026
252f6c6
feat(chat): enhance chat runtime with external payloads and improve d…
SonyLeo Oct 26, 2026
87208a3
feat(chat): implement message grouping logic and enhance message iden…
SonyLeo Oct 27, 2026
5258749
feat(chat): enhance message handling with assistant divider and exter…
SonyLeo Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions docs/demos/chat/file-drop-paste.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<script setup lang="ts">
import { shallowRef } from 'vue'
import { TrDragOverlay } from '@opentiny/tiny-robot'
import { TrChatUI, type ChatUIData, type SenderDragPayload } from '@opentiny/tiny-robot-chat'
import '@opentiny/tiny-robot-chat/dist/style.css'

const dragTarget = shallowRef<HTMLElement | null>(null)
const isDragging = shallowRef(false)
const receivedFiles = shallowRef<string[]>([])

const data: ChatUIData = {
sender: { submitDisabled: true },
}

function handleDragEnter({ event, target }: SenderDragPayload) {
if (!event.dataTransfer?.types.includes('Files')) return
dragTarget.value = target
isDragging.value = true
}

function handleDragOver({ event }: SenderDragPayload) {
if (event.dataTransfer?.types.includes('Files')) event.preventDefault()
}

function handleDragLeave({ event, target }: SenderDragPayload) {
if (!target.contains(event.relatedTarget as Node | null)) {
isDragging.value = false
}
}

function handleDrop({ event }: SenderDragPayload) {
isDragging.value = false
receivedFiles.value = Array.from(event.dataTransfer?.files ?? []).map((file) => file.name)
}

function handlePaste(event: ClipboardEvent) {
const files = Array.from(event.clipboardData?.files ?? [])
if (files.length) receivedFiles.value = files.map((file) => file.name)
}
</script>

<template>
<section class="chat-file-drop-demo">
<p class="chat-file-drop-demo__help">将图片拖到输入框,或复制图片后粘贴。此示例只显示文件名,不会上传文件。</p>
<TrChatUI
:data="data"
@sender-drag-enter="handleDragEnter"
@sender-drag-over="handleDragOver"
@sender-drag-leave="handleDragLeave"
@sender-drop="handleDrop"
@sender-paste="handlePaste"
/>
<TrDragOverlay :is-dragging="isDragging" :drag-target="dragTarget">
<template #overlay>
<div class="chat-file-drop-demo__drop-hint">释放图片以添加附件</div>
</template>
</TrDragOverlay>
<p class="chat-file-drop-demo__result" aria-live="polite">
{{ receivedFiles.length ? `收到:${receivedFiles.join('、')}` : '尚未收到文件' }}
</p>
</section>
</template>

<style scoped>
.chat-file-drop-demo {
--tr-layout-height: 100%;
display: flex;
flex-direction: column;
height: min(560px, calc(100vh - 240px));
min-height: 460px;
}

.chat-file-drop-demo__help,
.chat-file-drop-demo__result {
margin: 0;
padding: 10px 14px;
color: var(--tr-text-secondary, #575d6c);
font-size: 13px;
}

.chat-file-drop-demo :deep(.tr-chat-ui) {
flex: 1;
min-height: 0;
}

.chat-file-drop-demo :deep(.tr-drag-overlay) {
padding: 0;
overflow: hidden;
background: transparent;
backdrop-filter: none;
}

.chat-file-drop-demo__drop-hint {
display: grid;
width: 100%;
height: 100%;
box-sizing: border-box;
place-items: center;
border: 1px dashed rgb(64 158 255 / 75%);
border-radius: 16px;
background: rgb(64 158 255 / 12%);
color: var(--tr-text-primary, #252b3a);
font-size: 14px;
font-weight: 600;
}
</style>
51 changes: 51 additions & 0 deletions docs/demos/chat/input-controls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import { shallowRef } from 'vue'
import { TrChatUI, type ChatInputActions, type ChatUIOptions } from '@opentiny/tiny-robot-chat'
import '@opentiny/tiny-robot-chat/dist/style.css'

const chat = shallowRef<ChatInputActions | null>(null)
const ui: ChatUIOptions = {
layout: { leftAside: false, rightAside: false },
header: false,
welcome: { title: '编辑提示词', description: '点击上方按钮回填、清空或聚焦,也可以直接编辑文本。' },
sender: { toolbar: [] },
}
</script>

<template>
<section class="input-controls-demo">
<div class="input-controls-demo__actions">
<button type="button" @click="chat?.setInputValue('请介绍聊天布局')">仅回填</button>
<button type="button" @click="chat?.clearInput()">清空文本</button>
<button type="button" @click="chat?.focusInput()">聚焦</button>
</div>
<TrChatUI
ref="chat"
default-input-value="请帮我整理一份项目周报。"
:ui="ui"
:data="{ sender: { submitDisabled: true } }"
/>
</section>
</template>

<style scoped>
.input-controls-demo {
--tr-layout-height: 420px;
}

.input-controls-demo__actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
}

.input-controls-demo__actions button {
padding: 4px 10px;
border: 1px solid var(--tr-border-color-default);
border-radius: 6px;
color: var(--tr-text-primary);
background: var(--tr-container-bg-default);
cursor: pointer;
}
</style>
51 changes: 51 additions & 0 deletions docs/demos/chat/message-anchor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import { TrChatUI, type ChatMessageItem, type ChatUIOptions } from '@opentiny/tiny-robot-chat'
import '@opentiny/tiny-robot-chat/dist/style.css'

const ui: ChatUIOptions = {
layout: { leftAside: false, rightAside: false },
anchor: {},
}
const messages: ChatMessageItem[] = [
{ id: 'user-1', role: 'user', content: '上线前需要准备哪些材料?' },
{
id: 'assistant-1',
role: 'assistant',
content:
'建议提前准备以下材料:\n\n1. **发布说明**:列出本次新增功能、修复的问题及影响范围,方便使用者了解变化。\n2. **验证记录**:整理主要使用流程的验证结果,并记录尚未解决的问题。\n3. **操作手册**:说明发布步骤、负责人和预计完成时间,确保参与人员了解各自分工。\n4. **回退方案**:明确异常时的判断条件、恢复步骤及需要保留的数据。',
},
{ id: 'user-2', role: 'user', content: '如何安排上线当天的人员分工?' },
{
id: 'assistant-2',
role: 'assistant',
content:
'可以按职责分配,并在发布前确认联系方式:\n\n- **发布负责人**:统一确认发布条件,决定开始、暂停或回退。\n- **执行人员**:按操作手册完成部署,记录每一步的结果与时间。\n- **验证人员**:发布后检查登录、核心业务和数据展示,及时反馈异常。\n- **支持人员**:关注用户反馈,整理问题并同步给负责人。\n\n发布期间保持沟通渠道畅通,避免多个角色同时修改同一项配置。',
},
{ id: 'user-3', role: 'user', content: '发布后应该关注哪些指标?' },
{
id: 'assistant-3',
role: 'assistant',
content:
'建议同时观察系统运行和实际使用情况:\n\n1. **可用性**:页面能否打开,登录和核心操作是否正常。\n2. **响应时间**:对比发布前后的页面加载和主要操作耗时。\n3. **错误情况**:关注异常数量、失败比例及重复出现的问题。\n4. **业务结果**:检查关键记录是否正确保存,统计结果是否符合预期。\n\n提前约定观察周期和异常阈值,发现明显变化时及时核对,必要时暂停扩大使用范围。',
},
{ id: 'user-4', role: 'user', content: '请整理一份发布结束后的检查清单。' },
{
id: 'assistant-4',
role: 'assistant',
content:
'发布结束后,可逐项确认:\n\n- [ ] 核心业务流程已经验证,结果符合预期。\n- [ ] 监控指标保持正常,没有持续增加的异常。\n- [ ] 发布记录已补全,包含版本、时间和执行结果。\n- [ ] 使用说明已更新,并同步给相关人员。\n- [ ] 遗留问题已明确负责人和处理时间。\n- [ ] 已约定下一次复查时间,并保留必要的回退材料。\n\n完成检查后,再向相关人员同步发布结果和后续安排。',
},
]
</script>

<template>
<div class="message-anchor-demo">
<TrChatUI :ui="ui" :data="{ conversation: { activeId: 'release', title: '项目上线讨论' }, bubble: { messages } }" />
</div>
</template>

<style scoped>
.message-anchor-demo {
--tr-layout-height: 560px;
}
</style>
8 changes: 7 additions & 1 deletion docs/demos/chat/sender-options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import { useChatCaseRuntime } from './shared/createChatRuntime'

const showHelp = shallowRef(false)
const currentTemplate = shallowRef<TemplateItem[]>([])
const helpText = computed(() => {
if (currentTemplate.value.length > 1)
return '报告模板已填入,文本块可以编辑,下拉字段可以选择。内容超过 300 字时无法提交。'
if (currentTemplate.value.length) return '常用问题已填入,可以修改后发送。内容超过 300 字时无法提交。'
return '@ 用于插入对象名称,不会切换模型;点击“写报告”或“常用问题”填入内容。'
})
const mentions: MentionItem[] = [
{ label: '产品组', value: 'product' },
{ label: '研发组', value: 'engineering' },
Expand Down Expand Up @@ -86,7 +92,7 @@ function fillCommonQuestion() {
<TrChat :runtime="runtime" :ui="ui">
<template v-if="showHelp" #sender-header>
<p id="sender-options-help" class="sender-options-demo__help">
@ 用于插入对象名称,不会切换模型;模板中的文本块可以编辑,下拉字段可以选择。内容超过 300 字时无法提交。
{{ helpText }}
</p>
</template>
<template #sender-footer-right>
Expand Down
5 changes: 3 additions & 2 deletions docs/src/components/bubble.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ BubbleList 支持多种分组策略。分组时,连续的 `hidden` 消息会

#### useAutoScroll

`useAutoScroll` 是 BubbleList 内部使用的公开组合式函数。新代码推荐使用对象参数,并分别传入滚动容器和承载全部内容的内部元素:
`useAutoScroll` 用于在内容增长时自动跟随到底部,向上浏览时暂停跟随。分别传入滚动容器和承载全部内容的元素:

```ts
import { useAutoScroll } from '@opentiny/tiny-robot'
Expand Down Expand Up @@ -203,7 +203,8 @@ const { scrollToBottom, arrivedState } = useAutoScroll({

| 返回值 | 类型 | 说明 |
| ---------------- | ---------------------------------------------- | --------------------------------------- |
| `scrollToBottom` | `(behavior?: ScrollBehavior) => Promise<void>` | 命令式滚动到底部;不受 `enabled` 限制。 |
| `pause` | `() => void` | 暂停自动跟随,适用于跳转到指定消息。 |
| `scrollToBottom` | `(behavior?: ScrollBehavior) => Promise<void>` | 滚动到底部并恢复跟随;不受 `enabled` 限制。 |
| `arrivedState` | `UseScrollReturn['arrivedState']` | 当前是否到达各滚动边界的响应式状态。 |

旧的位置参数签名仍为兼容性保留,但已弃用:
Expand Down
2 changes: 2 additions & 0 deletions docs/src/components/sender.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ Sender 提供了多个插槽位置,方便扩展功能:
- **`footer`** - 底部左侧区域,可添加功能按钮
- **`footer-right`** - 底部右侧区域,可添加操作按钮

`header` 内容为空时不显示顶部留白和分隔线。条件显示时,请在插槽声明或直接子节点上使用 `v-if`;使用 `v-show` 或仅在子组件内部隐藏内容仍可能占位。

:::info 当前插槽作用域
当前版本只有 `content` 插槽提供 `editor`。`actions-inline`、`footer` 和 `footer-right` 只负责放置内容,不提供作用域参数;需要操作输入内容时,请通过 Sender 实例公开的方法接入。
:::
Expand Down
Loading
Loading