# Conflicts: # packages/client/ui-chat/src/client/chat/MessageItem.module.css # packages/client/ui-conversation/package.json # packages/client/ui-input-trigger/README.i18n.yaml # packages/client/ui-input-trigger/README.md # packages/client/ui-input-trigger/README.zh.md # packages/client/ui-reference/README.i18n.yaml # packages/client/ui-reference/README.md # packages/client/ui-reference/README.zh.md # pnpm-lock.yaml
7.5 KiB
description, kind
| description | kind |
|---|---|
| Target-neutral 对话装配与浏览器 shell:事件和视图注册表、逐会话 binding、输入状态、slot 与临时 composer takeover。 | package-reference |
@deepseek-ai/dsh-client-ui-conversation
English | 中文
概述
ui-conversation 拥有与 target 无关的 Conversation 组装和共享浏览器 shell。它消费 Session Controller 的 SessionEventLikeEntry feed,通过 ctx.uiConversation 暴露不依赖 React 的 registry 与逐 Session binding,并通过 ctx.uiSession 提供 useConversation、useInput 和 inputActions 标准 props。它还拥有按会话的持久化图片 URL 缓存:ctx.uiConversation.imageUrl(sessionId, attachment) 为每个附件解析一个经会话授权的浏览器 URL,并随 Session binding 释放而撤销,因此所有 Conversation target 共享一次 session.attachment 读取。Chat 等具体 target 位于独立 package,由各自 package 注册 Definition、snapshot builder、View 和 renderer。
目录
Conversation 组装
UiConversation.events 是 event Definition 的唯一 registry,UiConversation.views 是 target snapshot builder 的唯一 registry。两者都拒绝重复 key、保持注册顺序、返回幂等 disposer,并在 contribution roster 变化时重建现有 binding。UiConversation.binding(bindingOrSessionId) 为当前 Session Controller binding 返回 identity 稳定的 Conversation binding,不会另开 event source。
adapter 把每个 SessionEventLikeEntry 直接交给 assembler。外层 type 区分 scalar 与 packed record,内部 event 则统一公开 type、seq、time 与 data;Definition 接收这个内部 SessionEventLike。历史 replace 与 prepend 接受两种 entry,实时 append 只接受 SessionLiveEventEntry。两种 event 都使用 Definition 的同一组 match 与 update 方法,start 则只接收标准 event,assembler 会拒绝 packed start。不消费 Assistant delta 的 Definition 对 packed tag 返回 null。replace window 或 revision 断档从完整已加载窗口重建;连续 revision 的 append 和 prepend 使用增量组装,并且不展开 packed member。assembler 拥有 Context 匹配、Turn/Step location、target node 物化、target activity 和稳定 target source。ConversationSnapshot 只包含与 target 无关的 View 与 active-target 事实;Session lifecycle 状态仍属于 SessionSnapshot。
target package 通过 declaration merge 扩展 snapshot 与 Location data map,再调用 ctx.uiConversation.events.register(...) 和 ctx.uiConversation.views.register(...)。target 通过 ctx.uiConversation.binding(binding).target(targetId) 读取其 Session-owned source。注册属于 Cordis effect,返回的 disposer 从同一个 registry 移除 contribution。
Shell 与标准 props
本包注册 optional-Session conversation shell、strict Session header/body、View list、composer chain 与 bar、输入区域、Hero 区域、queue dock、草稿持久化和 phase 计算。ctx.uiSession.provide() 从同一个 Session binding 物化 Conversation 与 input source,并将 inputActions 作为稳定标准 prop 提供。
View 选择规则固定:有效且已注册的持久化选择优先,其次是已注册的 chat,否则不渲染 View;绝不选择第一个已注册 View。Shell phase 只组合 Session lifecycle 与 active-target set,不读取任何 target-specific snapshot。
常驻 composer 在无 Session 与有 Session 之间保持挂载。无 Session 时,同一个编辑器表面保持 inert,Workspace picker 连接 blank Session。该表面是 shell 所有的 Lexical 编辑器:引用 chip 是携带 owner 序列化身份的原子 decorator 节点(提交时经 owner codec 展开),已认领的 slash command 保持为带样式的行首文本,文件夹文本引用以图标前缀携带文件夹图形,草稿的剪贴板投影镜像到逐 Session Conversation store。Queue 操作通过 scoped ctx.conversation service 寻址准确的 queue occurrence;queue 预览经 ui-primitives 的共享行内引用投影渲染已发送文本(wire 会话形式折叠为其标签),编辑态则展示字面发送文本。繁忙时 Enter 行为保存在 Host-backed ui-conversation settings namespace。
普通 composer 运行时,如果草稿为空或输入不可用,主指针操作保持为 Stop。可提交的文字或附件会把同一位置切换为 Queue Send;清空或成功提交草稿后恢复 Stop。繁忙态 Enter 设置继续选择 Queue 或 Steer 键盘操作。可继续 subagent 保留独立的 Send 与 Stop 操作(决策)。
临时 composer entry
conversation.composer 是通用 chain,其完整 owner currency 为:
/** Owner values used to elect a composer takeover. */
interface ComposerChainProps {
/** Current Session identity used by temporary business-owned entries. */
sessionId: SessionId | undefined
/** Current Session lifecycle state, absent without a selected Session. */
session: SessionSnapshot | undefined
/** Effective business-owned interaction awaiting the user in this Session. */
pendingInteraction: SessionPendingInteraction | undefined
}
业务 package 可仅在一个 Remote waterfall request pending 期间安装 entry:
import type { ComposerChainProps } from '@deepseek-ai/dsh-client-ui-conversation/client'
import type { ChainSelect, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
import type { SessionId } from '@deepseek-ai/dsh-session/types'
interface Request {
readonly sessionId: SessionId
}
type RequestComposerProps =
PropsRuntime<'conversation.composer'> & { matched: Request }
const select: ChainSelect<ComposerChainProps, Request> = owner =>
owner.sessionId === request.sessionId ? request : null
const dispose = ctx.slots.register(
{ name: 'conversation.composer', select },
RequestComposer,
)
try {
return await request.result
} finally {
dispose()
}
selector 必须是 owner currency 的纯函数。非 null 返回值作为 matched 传给组件;PropsRuntime<'conversation.composer'> 提供标准 Session 与 global props。Chain 顺序仍按 priority 升序,再按注册顺序;首个返回非 null 的 selector 获选。Shell 会在 takeover 下保持默认 composer 挂载。Request 状态、listener、response encoding 和任何 request-specific child slot 都属于业务 package,不进入 SessionSnapshot,也不由 core package 声明。
模型体验
无,因为本包渲染浏览器状态,并通过 Session Controller API 发送用户确认提交的输入,而不构造模型请求。
KV Cache 影响
无;Conversation 组装和浏览器输入状态不会改变提供方侧的 prompt cache。
已知限制与暂缓事项
- 只有已注册 target 可以渲染——除已注册的
chat偏好外,shell 刻意不提供隐式 fallback target。
开发备注
维护者工作上下文——点击展开
无。