@qilin-agent/client-ui-conversation 3.1.5 → 3.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +7 -3
- package/README.zh.md +7 -3
- package/lib/client.js +5147 -454
- package/lib/index.js +13 -9
- package/lib/types/client/contract/composer-submission.d.ts +23 -0
- package/lib/types/client/contract/conversation.d.ts +10 -0
- package/lib/types/client/contract/draft-editor.d.ts +23 -0
- package/lib/types/client/contract/input.d.ts +42 -10
- package/lib/types/client/contract/records.d.ts +8 -3
- package/lib/types/client/contract/slots.d.ts +61 -5
- package/lib/types/client/contract/views.d.ts +2 -1
- package/lib/types/client/conversation/assembler.d.ts +3 -0
- package/lib/types/client/draft.d.ts +22 -0
- package/lib/types/client/index.d.ts +3 -3
- package/lib/types/client/input/decorations.d.ts +1 -1
- package/lib/types/client/input/editor/CommandChip.d.ts +13 -0
- package/lib/types/client/input/editor/command-chip-node.d.ts +87 -0
- package/lib/types/client/input/editor/command-chip-selection.d.ts +20 -0
- package/lib/types/client/input/editor/keymap.d.ts +6 -2
- package/lib/types/client/input/editor/projection.d.ts +3 -0
- package/lib/types/client/input/editor/runtime.d.ts +33 -11
- package/lib/types/client/input/editor/text-ref.d.ts +2 -7
- package/lib/types/client/input/editor/view-binding.d.ts +1 -1
- package/lib/types/client/input/facade.d.ts +74 -18
- package/lib/types/client/input/hub.d.ts +2 -10
- package/lib/types/client/input/machine.d.ts +9 -5
- package/lib/types/client/input/submission-analytics.d.ts +10 -0
- package/lib/types/client/locales.d.ts +12 -16
- package/lib/types/client/skeleton/ConversationSession.d.ts +1 -1
- package/lib/types/client/skeleton/DefaultConversationViews.d.ts +2 -2
- package/lib/types/client/skeleton/InputBar.d.ts +1 -1
- package/lib/types/client/stores.d.ts +8 -1
- package/lib/types/client/view-pins.d.ts +22 -0
- package/lib/types/conversation-settings.d.ts +4 -0
- package/lib/types/index.d.ts +20 -5
- package/package.json +37 -35
- package/lib/types/client/input/editor/claim-decor.d.ts +0 -23
- package/lib/types/client/settings/ContentWidthRow.d.ts +0 -26
package/README.i18n.yaml
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
en: d8ce61119083b24f
|
|
19
19
|
zh: cc48467a09417108
|
|
20
20
|
/qilin-agent-client-ui-conversation/shell-and-standard-props:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: f689d239a47cd93b
|
|
22
|
+
zh: b80f9e6f0e8f8ec6
|
|
23
23
|
/qilin-agent-client-ui-conversation/temporary-composer-entries:
|
|
24
24
|
en: f8c9adabf654cc14
|
|
25
25
|
zh: 67790ddec09a3421
|
package/README.md
CHANGED
|
@@ -48,9 +48,11 @@ The composer registers the File command action and owns its label, availability,
|
|
|
48
48
|
|
|
49
49
|
`SessionInputShell` owns one Lexical editor per Session through its private [DraftEditorRuntime](src/client/input/editor/runtime.ts), while retaining submission, attachment selection, and recovery decisions. [DraftEditor](src/client/input/editor/DraftEditor.tsx) renders the borrowed editor; InputBar retains its Hooks and refs and installs DOM behavior through [view-binding](src/client/input/editor/view-binding.ts). Editor-facing types live in [draft-editor.ts](src/client/contract/draft-editor.ts), with shared input and submission types in [input.ts](src/client/contract/input.ts). This separation does not support simultaneous editable roots for one Session; [the two-stage isolation proposal](../../../.agents/notes/proposed/architecture/2026-09-14-composer-model-and-draft-editor.md) defines the remaining work.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Drafts retain plain text and file, folder, and Session reference chips across Session switching and reload. Public input methods accept text; internal restoration and Workspace transfer preserve structured references. The input model imports saved content before its first published read; React only mounts that editor and binds the existing Conversation store writer. The original storage key accepts legacy strings and writes semantic snapshots without runtime node IDs. A pending skill catalog does not block editing: slash names remain ordinary text until a matching catalog entry arrives. Arrival rematches the current text without changing chips, selection, or Undo history. Browser-owned attachment File objects are not persisted by this mechanism. The [draft initialization decision](../../../.agents/notes/implemented/architecture/2026-09-30-structured-draft-initialization.md) defines ownership and restoration.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
A claimed command is one atomic chip at the start of the draft, and the claim lasts exactly as long as that chip. Deleting the arguments and separator keeps the claim. At the chip's edge, the first Backspace or Delete selects the whole chip and the next one removes it and releases the claim, leaving the arguments as plain text; any other key, a click, or a caret move clears the selection. Text or a line break inserted before the chip turns it back into its literal token and releases the claim, and undo restores a deleted chip together with its claim. The persisted draft records the chip's token, so a reload rebuilds the chip once the command catalog resolves the claim; editing the draft first keeps the literal token, which Enter still submits as the command. The same rules apply to every command and locale, including `/goal`, `/目标`, `/plan`, and `/计划`. Command hints and ordinary placeholders remain hidden throughout IME composition and reappear only after the editor commits the final text and the corresponding input is empty.
|
|
54
|
+
|
|
55
|
+
Workspace selection uses `uiWorkspace.openWorkspace` to prepare the target and commit navigation. Draft content, including structured references, and attachments move in its synchronous preparation callback only while that request is current; later navigation or owner disposal leaves the original draft intact.
|
|
54
56
|
|
|
55
57
|
The package occupies the root-scoped `main` key `conversation`. Its `main.conversation` shell keeps the strict Session Header outside the optional-Session `conversation.content` Component Factory. The Factory owns the shared body and Composer, reads the current Session through its standard Hook, and exposes strict-Session `views` plus root-scoped `widthControls` local positions. Its default adapter renders the existing `conversation.session` entry, while the main occurrence selects the width handles; an embedded occurrence can replace `views` and omit those handles without rendering the main Header. `ctx.uiSession.provide()` materializes the Conversation and input sources from the same Session binding and supplies `inputActions` as a stable standard prop.
|
|
56
58
|
|
|
@@ -58,11 +60,13 @@ The resident `conversation.header` entry keeps the root-scoped `conversation.hea
|
|
|
58
60
|
|
|
59
61
|
View selection is deterministic: a registered persisted selection wins, otherwise registered `chat` wins, otherwise no View renders. It never chooses the first registered View. The roster hides the `trajectory` View while `ctx.configForms.developerTools` is off, and follows that preference so the tab returns when Developer tools are turned back on. Shell phase combines Session lifecycle with the active-target set; no target-specific snapshot is read by the shell.
|
|
60
62
|
|
|
63
|
+
While the root-scoped `conversation.session.header.views.actions` list has registrations, a **More conversation views** button follows the View tabs. It opens a menu of that list's registrations; each receives `entries` (Views outside the header with `id`, `label`, `pinned`, and `active`; Chat is never listed), `onOpen(id)`, `onPinnedChange(id, pinned)`, and `onDismiss()`. The header keeps one in-memory unpinned list for every Session, puts registered Chat first, and keeps the bar and More visible while every other View is unpinned; plugin reload resets the choices and legacy saved values are ignored. The header budgets the row width (48px for More and padding, 120px per tab) and observes the row with one ResizeObserver, falling back to the initial width when none exists; long labels truncate with a full-name tooltip, and the launcher highlights and names a folded active View.
|
|
64
|
+
|
|
61
65
|
The shell reads the persisted View preference before rendering when a Session first binds or a cached Session becomes current, activates the registered preferred View or Chat fallback, and activates later tab or focus selections before committing them to the store. A blank Session still omits the `conversation.view` slot; no unselected target is activated.
|
|
62
66
|
|
|
63
67
|
Active transcripts in the main occurrence expose content-width drag handles in their uncovered side gutters; embedded occurrences omit them. A View that paints into a gutter raises only its concrete painted element above the handle; transparent full-width wrappers stay below so they do not claim empty gutter. This requires the path between that element and the Conversation body to remain outside an intermediate stacking context; the shipped Chromium behavior is pinned by the browser scenario. Chat applies the rule to table elements, while its column-bounded tool cards need no raise. Wheel motion over a handle still scrolls the transcript, while Ctrl+wheel remains a browser zoom gesture. The sticky composer intentionally owns its full footer band, which is not a resize target; an already-captured drag lifts its indicator until release ([decision](../../../.agents/notes/implemented/bug-fix/2026-09-14-transcript-width-handle-layering.md)).
|
|
64
68
|
|
|
65
|
-
The resident composer survives no-Session and Session transitions. Whitespace hides its placeholder; a whitespace-only draft without attachments cannot be sent. The no-Session state keeps the same composer surface mounted but inert while the Workspace picker connects a blank Session. The surface is a shell-owned Lexical editor: reference chips are atomic decorator nodes carrying the owner's serialization identity (submission expands them through the owner codec), claimed slash
|
|
69
|
+
The resident composer survives no-Session and Session transitions. Whitespace hides its placeholder; a whitespace-only draft without attachments cannot be sent. The no-Session state keeps the same composer surface mounted but inert while the Workspace picker connects a blank Session. The surface is a shell-owned Lexical editor: reference chips are atomic decorator nodes carrying the owner's serialization identity (submission expands them through the owner codec), the claimed slash command is an atomic leading chip that carries its claim, folder text references carry the folder glyph as an icon prefix, and the draft's text and semantic references are persisted by the per-Session Conversation store. QueueDock reads `next-turn` directly from the Session `inbox` projection, including cold recovered messages. Queue operations address exact queue occurrences through the scoped `ctx.conversation` service; queue previews render sent text through the shared inline reference projection from `ui-primitives` (wire session forms fold to their label) and show local or durable images and files in original attachment order. An edit exposes the literal sent text in the ui-primitives `InlineEditor`, so line breaks survive re-editing. Images use thumbnails; files use compact name-and-size cards. An edit exposes the literal sent text, and durable thumbnails resolve through the session image URL cache. Busy Enter behavior is stored in the Host-backed `ui-conversation` settings namespace. The composer keymap arbitrates the trigger menu's keys through the slash pipeline — Tab settles the highlighted completion (or drills a drillable one), Escape and Shift+Tab leave the menu without settling — and leaves every other key to the editor. An overlay that takes the keyboard hands it back through `SessionInput.focus()`, which rides Lexical's own focus so the caret returns where the draft left it rather than at the start. Clicking unused space inside an editable composer preserves its current caret or selection when focused, or restores its previous caret or selection when unfocused. Text selection, scrollbars, controls, and embedded overlays keep their own pointer behavior.
|
|
66
70
|
|
|
67
71
|
Default sends commit optimistically: Enter clears the draft, occurrence table, and undo history in the same transaction, keeps the composer in `plain`, and runs the send as a detached attempt, so typing and further sends continue during the flight. `sendSession` registers a Session submission echo (`session.beginSubmission`) with the delivery mode before serializing, preserving selected image and file order in `pendingSubmissions`; Session derives the placement from that mode and its current running state, so idle sends use the transcript, busy Queue sends use QueueDock, and busy Steer sends use the pending-steering surface. It then yields one paint, encodes images through the browser's native `FileReader` data-URL path, and cites staged file receipts. Command submissions use the same receipts for generic files, so sending `/goal` or `/plan` never reads those browser files again. The prompt reuses the submission `requestId`; queue and history observation by that `rpcId` retires the echo once. Concurrent failures are restored together in submission order until the user edits the restored content; command submissions keep the frozen `submitting` phase. Detached attempts retain their attachment ids through admission and Session scope disposal. An observed retirement immediately exposes each image preview through the durable cache, replaces it with the canonical URL after fetching the admitted attachment, revokes each URL after its use ends, and releases file cards. Selected generic files enter one FIFO background-upload queue; `maxConcurrentFileUploads` defaults to two active Worker transports, the Conversation service retains queued and active operations plus byte progress across Session navigation, and removing a draft skips its queued transfer or aborts its active transport. Continuable subagents accept pasted and dropped PNG, JPEG, WebP, and GIF images with the same projected limits; a batch containing a generic file or unsupported image format is refused before intake, and they skip local echoes because their transport does not preserve the browser request id.
|
|
68
72
|
|
package/README.zh.md
CHANGED
|
@@ -48,9 +48,11 @@ target package 通过 declaration merge 扩展 snapshot 与 Location data map,
|
|
|
48
48
|
|
|
49
49
|
`SessionInputShell` 通过私有 [DraftEditorRuntime](src/client/input/editor/runtime.ts) 为每个 Session 持有一个 Lexical editor,同时保留提交、附件选择和恢复决策。[DraftEditor](src/client/input/editor/DraftEditor.tsx) 呈现借用的 editor;InputBar 保留钩子与 refs,并通过 [view-binding](src/client/input/editor/view-binding.ts) 安装 DOM 行为。编辑器类型位于 [draft-editor.ts](src/client/contract/draft-editor.ts),共享输入和提交类型位于 [input.ts](src/client/contract/input.ts)。这一拆分不支持同一 Session 同时挂载多个可编辑 root;[两阶段隔离提案](../../../.agents/notes/proposed/architecture/2026-09-14-composer-model-and-draft-editor.zh.md) 定义剩余工作。
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
草稿中的普通文字及文件、目录、Session 引用胶囊在切换 Session 和刷新后保留。公开输入方法接受文本;内部恢复和工作区携带保留结构化引用。输入模型在首次对外读取前导入保存内容;React 只挂载同一编辑器并绑定现有 Conversation store 写入回调。原存储 key 兼容旧字符串,新写入保存不含运行时节点 ID 的语义快照。等待 skill 目录不会阻塞编辑:斜杠名称保持普通文字,直到匹配的目录项到达。目录到达只重匹配当前文字,不改变胶囊、选区或 Undo 历史。浏览器持有的附件 File 对象不由这一机制持久化。[草稿初始化决策](../../../.agents/notes/implemented/architecture/2026-09-30-structured-draft-initialization.zh.md)规定所有权与恢复流程。
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
已认领的命令是草稿开头的一个原子 chip,认领与该 chip 同生共灭。删除参数和分隔空格不影响认领。光标到达 chip 边缘时,第一次 Backspace 或 Delete 选中整个 chip,再按一次才删除它并释放认领,参数保留为普通文字;按其他键、点击或移动光标会取消选中。在 chip 前插入文字或换行会把它还原为字面 token 并释放认领,撤销会连同认领一起恢复被删除的 chip。持久化的草稿记录该 chip 的 token,因此刷新后会在命令目录解析出认领时重建 chip;先编辑草稿则保留字面 token,Enter 仍会将其作为命令提交。所有命令和语言使用相同规则,包括 `/goal`、`/目标`、`/plan` 和 `/计划`。输入法组合输入期间,命令提示和普通占位文字持续隐藏,直到编辑器提交最终文字且对应输入为空时才重新显示。
|
|
54
|
+
|
|
55
|
+
工作区选择使用 `uiWorkspace.openWorkspace` 准备目标并提交导航。草稿内容(包括结构化引用)和附件仅在该请求仍为当前请求时,通过它的同步准备回调搬移;后续导航或所有者释放会保留原草稿。
|
|
54
56
|
|
|
55
57
|
本包占据 root 作用域 `main` 中的 `conversation` key。其 `main.conversation` shell 将 strict Session Header 保留在 optional-Session `conversation.content` Component Factory 外。Factory 拥有共享正文与 Composer,通过其标准 Hook 读取当前 Session,并公开 strict-Session `views` 与 root-scoped `widthControls` 两个局部位置。默认 adapter 渲染现有 `conversation.session` entry,主 occurrence 选择宽度拖拽条;嵌入式 occurrence 可以替换 `views`、省略拖拽条,且不渲染主 Header。`ctx.uiSession.provide()` 从同一个 Session binding 物化 Conversation 与 input source,并将 `inputActions` 作为稳定标准 prop 提供。
|
|
56
58
|
|
|
@@ -58,11 +60,13 @@ target package 通过 declaration merge 扩展 snapshot 与 Location data map,
|
|
|
58
60
|
|
|
59
61
|
View 选择规则固定:有效且已注册的持久化选择优先,其次是已注册的 `chat`,否则不渲染 View;绝不选择第一个已注册 View。`ctx.configForms.developerTools` 关闭时,roster 隐藏 `trajectory` View,并跟随该偏好,在开发者工具重新打开时让标签页回来。Shell phase 只组合 Session lifecycle 与 active-target set,不读取任何 target-specific 快照。
|
|
60
62
|
|
|
63
|
+
root 作用域的 `conversation.session.header.views.actions` list 有注册项时,View 标签之后出现**更多会话视图**按钮。它打开的菜单以该 list 的注册项为行;每个注册项接收 `entries`(头部之外的 View,含 `id`、`label`、`pinned` 与 `active`;不含 Chat)、`onOpen(id)`、`onPinnedChange(id, pinned)` 与 `onDismiss()`。头部为所有 Session 保存同一份内存已收起列表,把已注册的 Chat 放在首位,并在其余 View 全部收起时仍保留标签栏与更多按钮;插件重载重置选择,不读取旧存储值。头部按行宽预算分配(更多按钮与内边距 48px,每标签 120px),用一个 ResizeObserver 观察标签行;环境没有该观察器时按初始宽度计算。长标题截断并附全名提示;当前 View 被折叠时启动按钮高亮并在提示中显示其名称。
|
|
64
|
+
|
|
61
65
|
Session 首次绑定或缓存的 Session 成为 current 时,shell 会在渲染前读取持久化 View 偏好,激活已注册的偏好 View 或 Chat fallback,并在后续 tab 或 focus 选择写入 store 前先激活对应 target。blank Session 仍不渲染 `conversation.view` slot;未选中的 target 不会激活。
|
|
62
66
|
|
|
63
67
|
主 occurrence 的活跃 transcript 只在未被内容覆盖的两侧沟槽中提供正文宽度拖拽条;嵌入式 occurrence 省略这些拖拽条。View 如果绘制进沟槽,只将具体的可见元素提到拖拽条上方;透明的全宽包装层保持在下方,不会占用空白沟槽。该规则要求此元素与 Conversation body 之间不能引入中间堆叠上下文;浏览器场景固定了交付 Chromium 的行为。Chat 将该规则用于表格元素,其限定在阅读列内的工具卡片无需提高层级。指针位于拖拽条上时,滚轮仍会滚动 transcript,Ctrl+滚轮则保留为浏览器缩放手势。粘滞 composer 刻意拥有完整的底部区带,该区域不是宽度调整目标;已捕获的拖拽会将指示线提高到松开为止([决策](../../../.agents/notes/implemented/bug-fix/2026-09-14-transcript-width-handle-layering.zh.md))。
|
|
64
68
|
|
|
65
|
-
常驻 composer 在无 Session 与有 Session 之间保持挂载。输入空白字符会隐藏占位提示;没有附件的纯空白草稿无法发送。无 Session 时,同一个编辑器表面保持 inert,Workspace picker 连接 blank Session。该表面是 shell 所有的 Lexical 编辑器:引用 chip 是携带 owner 序列化身份的原子 decorator 节点(提交时经 owner codec 展开),已认领的 slash command
|
|
69
|
+
常驻 composer 在无 Session 与有 Session 之间保持挂载。输入空白字符会隐藏占位提示;没有附件的纯空白草稿无法发送。无 Session 时,同一个编辑器表面保持 inert,Workspace picker 连接 blank Session。该表面是 shell 所有的 Lexical 编辑器:引用 chip 是携带 owner 序列化身份的原子 decorator 节点(提交时经 owner codec 展开),已认领的 slash command 是携带认领的行首原子 chip,文件夹文本引用以图标前缀携带文件夹图形,草稿文字和引用语义字段由逐 Session Conversation store 持久化。QueueDock 直接从 Session 的 `inbox` 投影读取 `next-turn`,包含从冷状态恢复的消息。Queue 操作通过 scoped `ctx.conversation` service 寻址准确的 queue occurrence;queue 预览经 `ui-primitives` 的共享行内引用投影渲染已发送文本(wire 会话形式折叠为其标签),并按原始附件顺序展示本地或持久化的图片和文件。图片使用缩略图,文件使用紧凑的名称与大小卡片。编辑在 ui-primitives 的 `InlineEditor` 中展示字面发送文本,因此重新编辑不会丢失换行。持久化缩略图通过会话图片 URL 缓存解析。繁忙时 Enter 行为保存在 Host-backed `ui-conversation` settings namespace。 composer 键盘映射经斜杠流水线裁决触发菜单的按键——Tab 确认高亮补全项(可下钻项则下钻),Escape 与 Shift+Tab 离开菜单且不选定——其余按键交给编辑器自身。 接管键盘的浮层通过 `SessionInput.focus()` 把键盘还回来,该路径走 Lexical 自己的 focus,因此光标回到草稿原来的位置而不是开头。点击可编辑输入框内的留白时,已聚焦的输入框保留当前光标或选区,未聚焦的输入框恢复上次的光标或选区。文本选择、滚动条、控件和内嵌浮层保持各自的指针交互行为。
|
|
66
70
|
|
|
67
71
|
默认发送采用乐观提交:Enter 在同一事务里清空草稿、occurrence 表和撤销历史,composer 保持 `plain`,发送作为 detached attempt 运行,发送期间可以继续输入和提交。`sendSession` 在序列化之前用投递模式注册 Session 提交回显(`session.beginSubmission`),并在 `pendingSubmissions` 中保留图片与文件的选择顺序;Session 根据该模式与当前运行状态推导位置,因此空闲发送进入 transcript(文本记录),繁忙时 Queue 进入 QueueDock,繁忙时 Steer 进入 pending-steering 区域。随后让出一帧,图片经浏览器原生 `FileReader` data-URL 路径编码,文件则引用已暂存凭证。命令提交也用同一凭证表示通用文件,因此发送 `/goal` 或 `/plan` 时不会再次读取这些浏览器文件。提示词复用提交 `requestId`;queue 或历史以同一 `rpcId` 被观察后,回显只退休一次。多个并发发送失败时,在用户编辑还原内容之前按提交顺序合并还原;命令提交保持冻结的 `submitting` 阶段。Detached attempt 持有附件 id,直到 admission 完成或 Session scope 销毁。回显以 observed 退休时,durable 图片缓存立即公开每个预览 URL,读取 admitted 附件后用规范化 URL 替换预览,并在各 URL 停止使用后撤销,同时释放文件卡。选中的通用文件进入同一个先进先出的后台上传队列;`maxConcurrentFileUploads` 默认允许两个 Worker transport 同时运行,Conversation 服务在切换 Session 时继续持有排队和运行中的传输操作及字节进度,移除草稿会跳过排队中的传输或中止正在运行的传输。continuable 子代理允许粘贴和拖入 PNG、JPEG、WebP、GIF 图片,并沿用相同的投影限制;包含通用文件或不支持的图片格式的批次会在进入草稿前整批拒绝。它们不创建本地回显,因为其 transport 不保留浏览器 request id。
|
|
68
72
|
|