@deepseek-ai/dsh-client-ui-conversation 0.1.7-alpha.1 → 0.1.7-alpha.2

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 CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md
5
- README.md: a58ae14a450a18ae4bc89dc0dc5cc6fb664d48e0
6
- README.zh.md: 20d6cab01e7b5fc4c9b075a993783c9df8121413
5
+ README.md: 4823aa0a1ba5975c4efc8b6bdddd7ca8c57c068f
6
+ README.zh.md: 5ef8a1b6621bf8bab7e98cb18722a9aedda29101
package/README.md CHANGED
@@ -68,9 +68,9 @@ Active transcripts in the main occurrence expose content-width drag handles in t
68
68
 
69
69
  The width-handle indicator follows the pointer only during a captured drag; ordinary hover leaves its position unchanged.
70
70
 
71
- 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 commands stay styled leading text, folder text references carry the folder glyph as an icon prefix, and the draft's clipboard projection is mirrored into 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. 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.
71
+ 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 commands stay styled leading text, folder text references carry the folder glyph as an icon prefix, and the draft's clipboard projection is mirrored into the per-Session Conversation store. QueueDock reads `next-turn` from the Session `inbox` projection, including cold recovered messages, and excludes only requestIds still owned by local transcript submissions. Other queued rows retain their normal display and actions. 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. Images use thumbnails; files use compact name-and-size cards. An edit exposes the literal sent text in a textarea that grows with its content, so line breaks survive re-editing; Enter saves, Shift+Enter breaks the line, and Escape cancels. 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.
72
72
 
73
- 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. Where the browser shell exposes `__DSH_HOST_PATHS__` (the Desktop application), dropped or pasted folders and dropped, picked, or pasted non-image files with a real path become `@path` chips; images keep uploading. Drag-and-drop and paste identify directories through the browser entry API; when that API is absent or returns no entry, pasted items retain ordinary file handling. The file picker cannot select directories. References require the `ui-reference` plugin and the original paths to remain readable by the model's file tools. Paths inside the workspace are relative; others remain absolute. The whole batch is validated before insertion, retains source order and selected text, and uses whitespace-separated, closed-quote mentions. A browser without the bridge refuses dropped or pasted folders; a Desktop folder without a reported path is rejected separately. Continuable subagents disable attachment intake and skip local echoes because their transport does not preserve the browser request id.
73
+ 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`; Session retires the echo once at its display handoff, correlated by that `rpcId`. 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. Where the browser shell exposes `__DSH_HOST_PATHS__` (the Desktop application), dropped or pasted folders and dropped, picked, or pasted non-image files with a real path become `@path` chips; images keep uploading. Drag-and-drop and paste identify directories through the browser entry API; when that API is absent or returns no entry, pasted items retain ordinary file handling. The file picker cannot select directories. References require the `ui-reference` plugin and the original paths to remain readable by the model's file tools. Paths inside the workspace are relative; others remain absolute. The whole batch is validated before insertion, retains source order and selected text, and uses whitespace-separated, closed-quote mentions. A browser without the bridge refuses dropped or pasted folders; a Desktop folder without a reported path is rejected separately. Continuable subagents disable attachment intake and skip local echoes because their transport does not preserve the browser request id.
74
74
 
75
75
  Queued submission echoes show “Sending…” beside disabled edit, remove, and steer buttons; a collapsed dock keeps the sending status in its header. A matching Host queue row replaces the echo and enables each action according to its normal text-content and running-state requirements. Prompt acknowledgement alone does not enable queue actions. A failed submission removes its echo and displays an error; the composer restores the failed draft when it is empty or still contains the previous automatic restoration, preserving subsequently typed text.
76
76
 
package/README.zh.md CHANGED
@@ -68,9 +68,9 @@ Session 首次绑定或缓存的 Session 成为 current 时,shell 会在渲染
68
68
 
69
69
  宽度拖拽条的指示线只在已捕获指针的拖拽期间跟随指针,普通悬停不改变其位置。
70
70
 
71
- 常驻 composer 在无 Session 与有 Session 之间保持挂载。输入空白字符会隐藏占位提示;没有附件的纯空白草稿无法发送。无 Session 时,同一个编辑器表面保持 inert,Workspace picker 连接 blank Session。该表面是 shell 所有的 Lexical 编辑器:引用 chip 是携带 owner 序列化身份的原子 decorator 节点(提交时经 owner codec 展开),已认领的 slash command 保持为带样式的行首文本,文件夹文本引用以图标前缀携带文件夹图形,草稿的剪贴板投影镜像到逐 Session Conversation store。QueueDock 直接从 Session 的 `inbox` 投影读取 `next-turn`,包含从冷状态恢复的消息。Queue 操作通过 scoped `ctx.conversation` service 寻址准确的 queue occurrence;queue 预览经 `ui-primitives` 的共享行内引用投影渲染已发送文本(wire 会话形式折叠为其标签),并按原始附件顺序展示本地或持久化的图片和文件。图片使用缩略图,文件使用紧凑的名称与大小卡片。编辑态展示字面发送文本,持久化缩略图通过会话图片 URL 缓存解析。繁忙时 Enter 行为保存在 Host-backed `ui-conversation` settings namespace。 composer 键盘映射经斜杠流水线裁决触发菜单的按键——Tab 确认高亮补全项(可下钻项则下钻),Escape 与 Shift+Tab 离开菜单且不选定——其余按键交给编辑器自身。 接管键盘的浮层通过 `SessionInput.focus()` 把键盘还回来,该路径走 Lexical 自己的 focus,因此光标回到草稿原来的位置而不是开头。
71
+ 常驻 composer 在无 Session 与有 Session 之间保持挂载。输入空白字符会隐藏占位提示;没有附件的纯空白草稿无法发送。无 Session 时,同一个编辑器表面保持 inert,Workspace picker 连接 blank Session。该表面是 shell 所有的 Lexical 编辑器:引用 chip 是携带 owner 序列化身份的原子 decorator 节点(提交时经 owner codec 展开),已认领的 slash command 保持为带样式的行首文本,文件夹文本引用以图标前缀携带文件夹图形,草稿的剪贴板投影镜像到逐 Session Conversation store。QueueDock Session 的 `inbox` 投影读取 `next-turn`,包含从冷状态恢复的消息,仅排除仍由本地 transcript 提交承接的 requestId。其他排队行保留正常展示和操作。Queue 操作通过 scoped `ctx.conversation` service 寻址准确的 queue occurrence;queue 预览经 `ui-primitives` 的共享行内引用投影渲染已发送文本(wire 会话形式折叠为其标签),并按原始附件顺序展示本地或持久化的图片和文件。图片使用缩略图,文件使用紧凑的名称与大小卡片。编辑态在可随内容增高的 textarea 中展示字面发送文本,因此重新编辑不会丢失换行;Enter 保存,Shift+Enter 换行,Escape 取消。持久化缩略图通过会话图片 URL 缓存解析。繁忙时 Enter 行为保存在 Host-backed `ui-conversation` settings namespace。 composer 键盘映射经斜杠流水线裁决触发菜单的按键——Tab 确认高亮补全项(可下钻项则下钻),Escape 与 Shift+Tab 离开菜单且不选定——其余按键交给编辑器自身。 接管键盘的浮层通过 `SessionInput.focus()` 把键盘还回来,该路径走 Lexical 自己的 focus,因此光标回到草稿原来的位置而不是开头。
72
72
 
73
- 默认发送采用乐观提交: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 时继续持有排队和运行中的传输操作及字节进度,移除草稿会跳过排队中的传输或中止正在运行的传输。浏览器 shell 暴露 `__DSH_HOST_PATHS__` 时(桌面应用),拖入或粘贴的文件夹以及拖入、选择或粘贴的带真实路径的非图片文件会成为 `@路径` chip;图片仍然上传。拖放和粘贴通过浏览器 entry API 识别目录;该 API 不可用或没有返回 entry 时,粘贴项沿用普通文件处理。文件选择器不能选择目录。引用需要启用 `ui-reference` 插件,原路径也必须仍可由模型的文件工具读取。工作区内的路径使用相对形式,其他路径保留绝对形式。整批文件先校验再插入,保留来源顺序和已选中的文字,引用之间有空白分隔,含空格的路径使用闭合引号。没有该桥的浏览器会拒绝拖入或粘贴的文件夹,桌面端无法获取文件夹路径时单独报错。continuable 子代理禁用附件入口,也不创建本地回显,因为其 transport 不保留浏览器 request id。
73
+ 默认发送采用乐观提交:Enter 在同一事务里清空草稿、occurrence 表和撤销历史,composer 保持 `plain`,发送作为 detached attempt 运行,发送期间可以继续输入和提交。`sendSession` 在序列化之前用投递模式注册 Session 提交回显(`session.beginSubmission`),并在 `pendingSubmissions` 中保留图片与文件的选择顺序;Session 根据该模式与当前运行状态推导位置,因此空闲发送进入 transcript(文本记录),繁忙时 Queue 进入 QueueDock,繁忙时 Steer 进入 pending-steering 区域。随后让出一帧,图片经浏览器原生 `FileReader` data-URL 路径编码,文件则引用已暂存凭证。命令提交也用同一凭证表示通用文件,因此发送 `/goal` 或 `/plan` 时不会再次读取这些浏览器文件。提示词复用提交 `requestId`;Session 按同一 `rpcId` 关联展示接管,并仅退休回显一次。多个并发发送失败时,在用户编辑还原内容之前按提交顺序合并还原;命令提交保持冻结的 `submitting` 阶段。Detached attempt 持有附件 id,直到 admission 完成或 Session scope 销毁。回显以 observed 退休时,durable 图片缓存立即公开每个预览 URL,读取 admitted 附件后用规范化 URL 替换预览,并在各 URL 停止使用后撤销,同时释放文件卡。选中的通用文件进入同一个先进先出的后台上传队列;`maxConcurrentFileUploads` 默认允许两个 Worker transport 同时运行,Conversation 服务在切换 Session 时继续持有排队和运行中的传输操作及字节进度,移除草稿会跳过排队中的传输或中止正在运行的传输。浏览器 shell 暴露 `__DSH_HOST_PATHS__` 时(桌面应用),拖入或粘贴的文件夹以及拖入、选择或粘贴的带真实路径的非图片文件会成为 `@路径` chip;图片仍然上传。拖放和粘贴通过浏览器 entry API 识别目录;该 API 不可用或没有返回 entry 时,粘贴项沿用普通文件处理。文件选择器不能选择目录。引用需要启用 `ui-reference` 插件,原路径也必须仍可由模型的文件工具读取。工作区内的路径使用相对形式,其他路径保留绝对形式。整批文件先校验再插入,保留来源顺序和已选中的文字,引用之间有空白分隔,含空格的路径使用闭合引号。没有该桥的浏览器会拒绝拖入或粘贴的文件夹,桌面端无法获取文件夹路径时单独报错。continuable 子代理禁用附件入口,也不创建本地回显,因为其 transport 不保留浏览器 request id。
74
74
 
75
75
  排队提交的本地回显在禁用的编辑、删除、插话按钮旁显示“发送中…”;折叠后的队列在标题栏保留发送状态。匹配的 Host 队列行替换回显后,各操作按原有的纯文本内容和运行状态要求启用。仅收到提示词确认不会启用队列操作。提交失败会移除回显并显示错误;输入框为空或仍保留上一次自动恢复的内容时,composer 恢复失败草稿,保留用户随后输入的文字。
76
76
 
package/lib/client.js CHANGED
@@ -14677,8 +14677,6 @@ window.__ModuleLoader__.load({
14677
14677
  "tool.autoReviewRejected": "Auto review 已拒绝",
14678
14678
  "tool.autoReviewNotExecuted": "工具未执行。原因:{reason}",
14679
14679
  "tool.autoReviewReasonFallback": "Auto review 未授权此次操作",
14680
- "diff.files.one": "{count} 个文件",
14681
- "diff.files.other": "{count} 个文件",
14682
14680
  "diff.collapseAria": "收起差异",
14683
14681
  "diff.expandAria": "展开其余 {count} 行差异",
14684
14682
  "diff.expandRest": "… 其余 {count} 行",
@@ -15027,8 +15025,6 @@ window.__ModuleLoader__.load({
15027
15025
  "tool.autoReviewRejected": "Rejected by Auto review",
15028
15026
  "tool.autoReviewNotExecuted": "Tool was not executed. Reason: {reason}",
15029
15027
  "tool.autoReviewReasonFallback": "Auto review did not authorize this action",
15030
- "diff.files.one": "{count} file",
15031
- "diff.files.other": "{count} files",
15032
15028
  "diff.collapseAria": "Collapse diff",
15033
15029
  "diff.expandAria": "Expand {count} more diff lines",
15034
15030
  "diff.expandRest": "… {count} more lines",
@@ -15079,7 +15075,7 @@ window.__ModuleLoader__.load({
15079
15075
  };
15080
15076
  //#endregion
15081
15077
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-conversation/src/client/queue/QueueDock.module.css.mjs
15082
- const css$6 = "._7yHdaG_dock{box-sizing:border-box;width:calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));max-width:calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));margin:0 auto calc(0px - var(--dsh-composer-stack-gap) - 3px);padding:0 var(--dsh-composer-dock-inset);flex:none}._7yHdaG_panel{isolation:isolate;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px 12px 0 0;width:100%;padding:2px 0;position:relative;overflow:hidden}._7yHdaG_panel:before{z-index:-1;border-radius:inherit;background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter);content:\"\";pointer-events:none;position:absolute;inset:0}._7yHdaG_panel:after{border:.5px solid var(--dsw-alias-border-l1);border-radius:inherit;content:\"\";pointer-events:none;border-bottom:none;position:absolute;inset:0}._7yHdaG_header{box-sizing:border-box;width:100%;height:36px;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:10px;padding:4px 12px;display:flex}._7yHdaG_header:focus-visible{outline:2px solid var(--dsw-alias-label-tertiary);outline-offset:-2px}._7yHdaG_header:disabled{cursor:default}._7yHdaG_lead{color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}._7yHdaG_count{min-width:0;font-family:Inter, var(--dsw-font-family);flex:auto;font-size:13px;font-weight:500;line-height:24px}._7yHdaG_chevron{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}._7yHdaG_list{max-height:180px;margin:0;padding:0;list-style:none;overflow-y:auto}._7yHdaG_row{box-sizing:border-box;border-radius:8px;align-items:center;gap:10px;width:100%;height:36px;padding:4px 5px 4px 12px;display:flex}._7yHdaG_row+._7yHdaG_row{box-shadow:inset 0 1px 0 var(--dsw-alias-border-l1)}._7yHdaG_attachments{flex:none;gap:4px;min-width:0;max-width:55%;display:flex;overflow:hidden}._7yHdaG_pendingRow ._7yHdaG_attachments{flex-shrink:1}._7yHdaG_file{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-sizing:border-box;border-radius:6px;flex:0 180px;align-items:center;gap:4px;min-width:74px;height:24px;padding:0 6px;display:inline-flex;overflow:hidden}._7yHdaG_fileIcon{flex:none;width:16px;height:16px;display:inline-flex}._7yHdaG_fileName{min-width:0;color:var(--dsw-alias-label-primary-dimmed);font:var(--dsw-font-xs-13);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._7yHdaG_fileSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;flex:none;font-size:10px}._7yHdaG_thumb{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);object-fit:cover;border-radius:4px;width:24px;height:24px}._7yHdaG_preview,._7yHdaG_editor{min-width:0;font:var(--dsw-font-xs-13);font-family:Inter, var(--dsw-font-family);flex:auto}._7yHdaG_preview{color:var(--dsw-alias-label-primary-dimmed);text-overflow:ellipsis;white-space:nowrap;word-break:break-word;overflow:hidden}._7yHdaG_editor{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-base);height:28px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;padding:0 8px}._7yHdaG_editor:focus{border-color:var(--dsw-alias-state-business-primary)}._7yHdaG_actions{flex:none;align-items:center;gap:10px;display:flex}._7yHdaG_status{color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xs-13);white-space:nowrap;flex:none}._7yHdaG_action{corner-shape:round;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:999px;flex:none;place-items:center;padding:0;display:grid}._7yHdaG_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._7yHdaG_action:focus-visible{outline:2px solid var(--dsw-alias-label-tertiary);outline-offset:-2px}._7yHdaG_action:disabled{cursor:default;opacity:.45}";
15078
+ const css$6 = "._7yHdaG_dock{box-sizing:border-box;width:calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));max-width:calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));margin:0 auto calc(0px - var(--dsh-composer-stack-gap) - 3px);padding:0 var(--dsh-composer-dock-inset);flex:none}._7yHdaG_panel{isolation:isolate;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px 12px 0 0;width:100%;padding:2px 0;position:relative;overflow:hidden}._7yHdaG_panel:before{z-index:-1;border-radius:inherit;background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter);content:\"\";pointer-events:none;position:absolute;inset:0}._7yHdaG_panel:after{border:.5px solid var(--dsw-alias-border-l1);border-radius:inherit;content:\"\";pointer-events:none;border-bottom:none;position:absolute;inset:0}._7yHdaG_header{box-sizing:border-box;width:100%;height:36px;color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:10px;padding:4px 12px;display:flex}._7yHdaG_header:focus-visible{outline:2px solid var(--dsw-alias-label-tertiary);outline-offset:-2px}._7yHdaG_header:disabled{cursor:default}._7yHdaG_lead{color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}._7yHdaG_count{min-width:0;font-family:Inter, var(--dsw-font-family);flex:auto;font-size:13px;font-weight:500;line-height:24px}._7yHdaG_chevron{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;place-items:center;display:grid}._7yHdaG_list{max-height:180px;margin:0;padding:0;list-style:none;overflow-y:auto}._7yHdaG_row{box-sizing:border-box;border-radius:8px;align-items:center;gap:10px;width:100%;min-height:36px;padding:4px 5px 4px 12px;display:flex}._7yHdaG_row+._7yHdaG_row{box-shadow:inset 0 1px 0 var(--dsw-alias-border-l1)}._7yHdaG_attachments{flex:none;gap:4px;min-width:0;max-width:55%;display:flex;overflow:hidden}._7yHdaG_pendingRow ._7yHdaG_attachments{flex-shrink:1}._7yHdaG_file{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);box-sizing:border-box;border-radius:6px;flex:0 180px;align-items:center;gap:4px;min-width:74px;height:24px;padding:0 6px;display:inline-flex;overflow:hidden}._7yHdaG_fileIcon{flex:none;width:16px;height:16px;display:inline-flex}._7yHdaG_fileName{min-width:0;color:var(--dsw-alias-label-primary-dimmed);font:var(--dsw-font-xs-13);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}._7yHdaG_fileSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;flex:none;font-size:10px}._7yHdaG_thumb{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);object-fit:cover;border-radius:4px;width:24px;height:24px}._7yHdaG_preview,._7yHdaG_editor{min-width:0;font:var(--dsw-font-xs-13);font-family:Inter, var(--dsw-font-family);flex:auto}._7yHdaG_preview{color:var(--dsw-alias-label-primary-dimmed);text-overflow:ellipsis;white-space:nowrap;word-break:break-word;overflow:hidden}._7yHdaG_editor{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-base);min-height:28px;max-height:127px;color:var(--dsw-alias-label-primary);resize:none;border-radius:6px;outline:none;padding:3px 8px;overflow-y:auto}._7yHdaG_editor:focus{border-color:var(--dsw-alias-state-business-primary)}._7yHdaG_actions{flex:none;align-items:center;gap:10px;display:flex}._7yHdaG_status{color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xs-13);white-space:nowrap;flex:none}._7yHdaG_action{corner-shape:round;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:999px;flex:none;place-items:center;padding:0;display:grid}._7yHdaG_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._7yHdaG_action:focus-visible{outline:2px solid var(--dsw-alias-label-tertiary);outline-offset:-2px}._7yHdaG_action:disabled{cursor:default;opacity:.45}";
15083
15079
  const tagId$6 = "@deepseek-ai/dsh-client-ui-conversation/QueueDock.module.css";
15084
15080
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
15085
15081
  const tag = document.createElement("style");
@@ -15198,13 +15194,55 @@ window.__ModuleLoader__.load({
15198
15194
  });
15199
15195
  }
15200
15196
  /**
15197
+ * Inline editor for one queued row. A textarea rather than an input: HTML
15198
+ * strips newlines from single-line input values, so editing a multi-line
15199
+ * queued message through one rewrites it as a single line. It grows with its
15200
+ * content up to the CSS cap, then scrolls. Enter saves, Shift+Enter breaks the
15201
+ * line, Escape cancels.
15202
+ */
15203
+ function QueueEditor({ text, label, onChange, onSave, onCancel }) {
15204
+ const ref = (0, react.useRef)(null);
15205
+ (0, react.useLayoutEffect)(() => {
15206
+ const node = ref.current;
15207
+ /* v8 ignore next -- the ref is attached before layout effects run. */
15208
+ if (node === null) return;
15209
+ node.style.height = "auto";
15210
+ node.style.height = `${node.scrollHeight + node.offsetHeight - node.clientHeight}px`;
15211
+ }, [text]);
15212
+ return (0, react_jsx_runtime.jsx)("textarea", {
15213
+ ref,
15214
+ autoFocus: true,
15215
+ rows: 1,
15216
+ className: QueueDock_module_css_default.editor,
15217
+ "aria-label": label,
15218
+ value: text,
15219
+ onChange: (event) => {
15220
+ onChange(event.currentTarget.value);
15221
+ },
15222
+ onKeyDown: (event) => {
15223
+ if (event.key === "Escape") {
15224
+ onCancel();
15225
+ return;
15226
+ }
15227
+ if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) return;
15228
+ event.preventDefault();
15229
+ onSave();
15230
+ }
15231
+ });
15232
+ }
15233
+ /**
15201
15234
  * Queue strip: one item renders directly; multiple items default to a
15202
- * collapsible count header; an empty queue renders nothing. Local submissions
15235
+ * collapsible count header; an empty queue renders nothing. Local queued submissions
15203
15236
  * show sending status and disabled actions until their Host queue rows arrive.
15204
15237
  */
15205
15238
  function QueueDock({ useSession, useProjection, updateQueue, notify, loadImage, t }) {
15206
- const queue = useProjection("inbox")?.["next-turn"] ?? EMPTY_QUEUE;
15239
+ const inbox = useProjection("inbox");
15207
15240
  const pendingSubmissions = useSession((s) => s.pendingSubmissions);
15241
+ const queue = (0, react.useMemo)(() => {
15242
+ const rows = inbox?.["next-turn"] ?? EMPTY_QUEUE;
15243
+ const inChat = new Set(pendingSubmissions.filter((item) => item.placement === "transcript").map((item) => item.requestId));
15244
+ return inChat.size === 0 ? rows : rows.filter(({ source }) => source.kind !== "user" || !("rpcId" in source) || !inChat.has(source.rpcId));
15245
+ }, [inbox, pendingSubmissions]);
15208
15246
  const pendingQueue = (0, react.useMemo)(() => {
15209
15247
  const admitted = new Set(queue.flatMap(({ source }) => source.kind === "user" && "rpcId" in source ? [source.rpcId] : []));
15210
15248
  return pendingSubmissions.filter((submission) => submission.placement === "queued" && !admitted.has(submission.requestId));
@@ -15302,26 +15340,20 @@ window.__ModuleLoader__.load({
15302
15340
  "aria-hidden": true,
15303
15341
  children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutlineRegular, {})
15304
15342
  }),
15305
- editing?.id === row.id ? (0, react_jsx_runtime.jsx)("input", {
15306
- autoFocus: true,
15307
- className: QueueDock_module_css_default.editor,
15308
- "aria-label": t("queue.edit"),
15309
- value: editing.text,
15310
- onChange: (event) => {
15343
+ editing?.id === row.id ? (0, react_jsx_runtime.jsx)(QueueEditor, {
15344
+ text: editing.text,
15345
+ label: t("queue.edit"),
15346
+ onChange: (text) => {
15311
15347
  setEditing({
15312
15348
  id: row.id,
15313
- text: event.currentTarget.value
15349
+ text
15314
15350
  });
15315
15351
  },
15316
- onKeyDown: (event) => {
15317
- if (event.key === "Escape") {
15318
- setEditing(null);
15319
- return;
15320
- }
15321
- if (event.key === "Enter" && !event.nativeEvent.isComposing) {
15322
- event.preventDefault();
15323
- saveEdit();
15324
- }
15352
+ onSave: () => {
15353
+ saveEdit();
15354
+ },
15355
+ onCancel: () => {
15356
+ setEditing(null);
15325
15357
  }
15326
15358
  }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [attachments.length > 0 && (0, react_jsx_runtime.jsx)("span", {
15327
15359
  className: QueueDock_module_css_default.attachments,
@@ -301,8 +301,6 @@ export declare const zh: {
301
301
  'tool.autoReviewRejected': string;
302
302
  'tool.autoReviewNotExecuted': string;
303
303
  'tool.autoReviewReasonFallback': string;
304
- 'diff.files.one': string;
305
- 'diff.files.other': string;
306
304
  'diff.collapseAria': string;
307
305
  'diff.expandAria': string;
308
306
  'diff.expandRest': string;
@@ -653,8 +651,6 @@ export declare const en: {
653
651
  'tool.autoReviewRejected': string;
654
652
  'tool.autoReviewNotExecuted': string;
655
653
  'tool.autoReviewReasonFallback': string;
656
- 'diff.files.one': string;
657
- 'diff.files.other': string;
658
654
  'diff.collapseAria': string;
659
655
  'diff.expandAria': string;
660
656
  'diff.expandRest': string;
@@ -14,7 +14,7 @@ export interface QueueDockInjected {
14
14
  export type QueueDockProps = PropsRuntime<'conversation.input.dock'> & QueueDockInjected & PropsLocale<'conversation'>;
15
15
  /**
16
16
  * Queue strip: one item renders directly; multiple items default to a
17
- * collapsible count header; an empty queue renders nothing. Local submissions
17
+ * collapsible count header; an empty queue renders nothing. Local queued submissions
18
18
  * show sending status and disabled actions until their Host queue rows arrive.
19
19
  */
20
20
  export declare function QueueDock({ useSession, useProjection, updateQueue, notify, loadImage, t }: QueueDockProps): import("react").JSX.Element | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-conversation",
3
3
  "description": "Target-neutral Conversation assembly, shell, composer, queue, and view navigation",
4
- "version": "0.1.7-alpha.1",
4
+ "version": "0.1.7-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -42,10 +42,10 @@
42
42
  },
43
43
  "license": "MIT",
44
44
  "dependencies": {
45
- "@deepseek-ai/schemastery": "^3.18.3"
45
+ "@deepseek-ai/schemastery": "~3.18.4"
46
46
  },
47
47
  "peerDependencies": {
48
- "@deepseek-ai/cordis": "^4.0.3"
48
+ "@deepseek-ai/cordis": "~4.0.4"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/react": "~18.3.1",
@@ -60,38 +60,38 @@
60
60
  "@lexical/text": "^0.49.0",
61
61
  "@lexical/plain-text": "^0.49.0",
62
62
  "@lexical/history": "^0.49.0",
63
- "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
64
- "@deepseek-ai/dsh-api-session-controller": "^0.1.7-alpha.1",
65
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.7-alpha.1",
66
- "@deepseek-ai/cordis": "^4.0.3",
67
- "@deepseek-ai/dsh-attachment": "^0.1.7-alpha.1",
68
- "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
69
- "@deepseek-ai/dsh-client-file-upload": "^0.1.7-alpha.1",
70
- "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
71
- "@deepseek-ai/dsh-brand": "^0.1.7-alpha.1",
72
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
73
- "@deepseek-ai/dsh-client-ui-layout": "^0.1.7-alpha.1",
74
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
75
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
76
- "@deepseek-ai/dsh-client-ui-session": "^0.1.7-alpha.1",
77
- "@deepseek-ai/dsh-client-ui-workspace": "^0.1.7-alpha.1",
78
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
79
- "@deepseek-ai/dsh-commands": "^0.1.7-alpha.1",
80
- "@deepseek-ai/dsh-goal": "^0.1.7-alpha.1",
81
- "@deepseek-ai/dsh-llm": "^0.1.7-alpha.1",
82
- "@deepseek-ai/dsh-llm-retry": "^0.1.7-alpha.1",
83
- "@deepseek-ai/dsh-plan-mode": "^0.1.7-alpha.1",
84
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
85
- "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
86
- "@deepseek-ai/dsh-util-values": "^0.1.7-alpha.1",
87
- "@deepseek-ai/dsh-token-meter": "^0.1.7-alpha.1",
88
- "@deepseek-ai/dsh-tool-todo": "^0.1.7-alpha.1",
89
- "@deepseek-ai/dsh-util-crypto": "^0.1.7-alpha.1",
90
- "@deepseek-ai/dsh-workspace": "^0.1.7-alpha.1",
91
- "@deepseek-ai/dsh-file-reference": "^0.1.7-alpha.1",
92
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.7-alpha.1",
93
- "@deepseek-ai/dsh-agent": "^0.1.7-alpha.1",
94
- "@deepseek-ai/dsh-settings": "^0.1.7-alpha.1"
63
+ "@deepseek-ai/cordis": "~4.0.4",
64
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-alpha.2",
65
+ "@deepseek-ai/dsh-api-workspace-controller": "0.1.7-alpha.2",
66
+ "@deepseek-ai/dsh-attachment": "0.1.7-alpha.2",
67
+ "@deepseek-ai/dsh-api-session-controller": "0.1.7-alpha.2",
68
+ "@deepseek-ai/dsh-brand": "0.1.7-alpha.2",
69
+ "@deepseek-ai/dsh-client-locale": "0.1.7-alpha.2",
70
+ "@deepseek-ai/dsh-client-file-upload": "0.1.7-alpha.2",
71
+ "@deepseek-ai/dsh-client-store": "0.1.7-alpha.2",
72
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.7-alpha.2",
73
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-alpha.2",
74
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-alpha.2",
75
+ "@deepseek-ai/dsh-client-ui-session": "0.1.7-alpha.2",
76
+ "@deepseek-ai/dsh-client-test-runtime": "0.1.7-alpha.2",
77
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-alpha.2",
78
+ "@deepseek-ai/dsh-client-ui-workspace": "0.1.7-alpha.2",
79
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-alpha.2",
80
+ "@deepseek-ai/dsh-commands": "0.1.7-alpha.2",
81
+ "@deepseek-ai/dsh-goal": "0.1.7-alpha.2",
82
+ "@deepseek-ai/dsh-llm": "0.1.7-alpha.2",
83
+ "@deepseek-ai/dsh-plan-mode": "0.1.7-alpha.2",
84
+ "@deepseek-ai/dsh-session": "0.1.7-alpha.2",
85
+ "@deepseek-ai/dsh-util-values": "0.1.7-alpha.2",
86
+ "@deepseek-ai/dsh-token-meter": "0.1.7-alpha.2",
87
+ "@deepseek-ai/dsh-tool-todo": "0.1.7-alpha.2",
88
+ "@deepseek-ai/dsh-util-crypto": "0.1.7-alpha.2",
89
+ "@deepseek-ai/dsh-workspace": "0.1.7-alpha.2",
90
+ "@deepseek-ai/dsh-file-reference": "0.1.7-alpha.2",
91
+ "@deepseek-ai/dsh-util-workspace-path": "0.1.7-alpha.2",
92
+ "@deepseek-ai/dsh-agent": "0.1.7-alpha.2",
93
+ "@deepseek-ai/dsh-settings": "0.1.7-alpha.2",
94
+ "@deepseek-ai/dsh-llm-retry": "0.1.7-alpha.2"
95
95
  },
96
96
  "files": [
97
97
  "lib/index.js",