# 0.2.0 Desktop 功能复核与交互审查 2026-11-04 复核结论:当前客户端能正常加载,基本导航、置顶和真实头像显示通过;会话页折叠后的标签可见性不通过。含用户反馈后的补充复核,共发现 6 项 P2 问题和 1 项 P3 文档问题,尚不能判定全部需求通过。本轮只记录验收与审查结果,未修改实现。 ## 环境与方法 | 项 | 本轮环境 | | --- | --- | | 插件 | `@skylerfee/dsh-sidebar-rail@0.2.0`,提交 `0e1a6dd3cb4df5143776b781f4de00da1e103ae3` | | 宿主 | 已安装的 macOS Desktop `0.2.2-rc.1`,`dsh-app://app/` | | 安装来源 | Desktop profile 通过 `link:` 指向当前插件仓库;本轮未重新安装或验证 npm tarball | | 界面 | 中文、深色外观、Catppuccin 皮肤 | | 真机方法 | Computer Use 原生窗口操作、可访问性树和截图观察;键盘使用 `Tab`、`Shift+Tab`、`Escape`、`super+b`、`super+comma` | | 受控探针 | 从当前 `client.js` 提取实际加载逻辑和归一化函数,以可控 Promise、定时器及 effect 清理替身验证异步状态;不修改运行中应用、不发真实失败请求 | | 自动检查 | `npm run check` 通过:仓库规则、两个入口语法、20 项测试 | ## 已通过的真实用户路径 | 路径 | 观察结果 | | --- | --- | | 当前加载与组件重挂载 | 窗口正常显示;停用后浮轨消失,重新启用后恢复;详情显示 `0.2.0`、1 个组件运行中 | | 图标轨显隐 | 折叠时出现四个顶部入口及底部账号;展开后浮轨消失,宿主工作区树保留;会话页标签遮挡另见问题 6 | | 宿主入口 | 搜索会话、视图选项、添加工作区入口仍在;`super+comma` 能打开原生设置 | | 工作区与最近会话 | 9 个工作区显示单行名称;最近 5 条显示中文相对时间;点击已有会话后标题与正文切到对应会话 | | 置顶 | 图钉使工作区进入「置顶项目」;停用再启用后仍保留;测试结束取消置顶,分组恢复 | | 任务与插件 | 点击图标能打开原生管理页;正常请求下任务浮层显示空态,插件浮层列出 8 个用户插件,没有 `@deepseek-ai/*` 内置插件 | | 账号 | 底部显示真实头像;账号浮层靠近头像底边;「展开侧栏」可用 | | 关闭浮层 | `Escape` 可关闭浮层 | ## 发现的问题 ### 1. P2:请求期间切换浮层后,返回原浮层会永久加载 定位:[client.js](../client.js) 的 `loadRemote`(850、862 行)及 `[open]` effect(872–885 行)。切换浮层会清理旧请求的接收标记和超时,但 `started.current[key]` 仍为 `true`;回到原入口时不重发请求,旧请求的结果也不会再写入。 受控复现:令任务请求保持 pending → 打开任务 → 切到插件 → 回到任务,全程不关闭浮层 → 让旧任务请求成功。结果为 `tasks.phase = 'loading'`、任务调用次数 `1`、活动超时数 `0`。即使旧请求成功,也没有结果或超时能结束加载。插件共用这段加载逻辑,同样存在该路径。 建议:清理当前请求时同步恢复可重试状态,或让每个列表独立持有请求生命周期;必须保留对过期结果的隔离。回归需覆盖直接切换以及关闭后重开的区别。 ### 2. P2:RemoteResult 失败和服务缺失被显示成空列表 定位:[client.js](../client.js) 的成功分支(859 行)、`normalizeTasks`(631 行)、`normalizePlugins`(664 行)和可选服务包装(1192–1202 行)。Promise resolve 就会进入 `ready`,没有判断 `{ ok: false, error }`;归一化随后返回空数组。服务不存在返回 `null`,也会进入同一个空态。 受控复现:任务 loader resolve `{ ok: false, error: { code: 'gateway/peer-disposed', message: 'offline' } }`,实际得到 `{ phase: 'ready', rows: [] }`,未进入失败态。该探针代表结构化失败帧,不表示本轮真实 Desktop 出现了此错误码。 宿主源码佐证:`packages/api/gateway/src/client/index.ts:461` 将 RPC 失败作为 `ok: false` 返回;宿主任务 `catalog-source.ts:111` 和插件 `manager-store.ts:1111` 都显式判断 `ok`。不能只依赖 Promise rejection。建议区分成功空列表、结构化失败和服务不可用,再显示对应失败与重试入口。 ### 3. P2:矮窗口的工作区浮层越出底部,最近会话被截住 定位:[client.js](../client.js) 的浮层样式(198–210 行)和定位(974 行)。`top` 只按固定 200px 预留空间,`max-height: calc(100vh - 32px)` 没有减去实际 `top`;最近会话位于独立 footer,上方 body 的滚动不能解决 footer 越出窗口的问题。 真机复现:通过 macOS「窗口 → 移动与调整大小 → 顶部」把窗口放到屏幕上半区,再打开包含 9 个工作区、5 条最近会话的工作区浮层。浮层下边界越出窗口,最近会话尾部被截住。恢复大窗口后列表完整。 建议:按实际锚点和窗口剩余高度限制浮层总高度,并让长内容在可见区域内滚动;回归时覆盖工作区较多、最近会话满额和矮窗口的组合。 ### 4. P2:常规 Tab 导航会换掉浮层,无法顺序进入原列表 定位:[client.js](../client.js) 的图标 `onFocus`(1085 行)和 portal 顺序(1098–1116 行)。所有轨道图标排在浮层列表之前,每个图标获得焦点又会打开自身浮层,因此列表在焦点走到它之前已被替换。 真机复现:聚焦工作区图标并等待工作区浮层出现,逐次按 `Tab`,让各入口浮层出现。焦点顺序为工作区 → 定时任务 → 插件 → 更多 → 账号 → 账号浮层的「展开侧栏」。工作区列表随切换卸载,无法按这条正常 Tab 路径进入工作区、最近会话或图钉。 建议:提供明确的键盘进入动作,例如方向键把焦点移入当前浮层,同时处理 Escape 后回到触发按钮;验收需覆盖列表内容,而不只是轨道图标可聚焦。 ### 5. P2:行内透明背景覆盖当前、悬停和键盘焦点的底色 定位:[client.js](../client.js) 的 `Row`(401 行)和 CSS(216、221 行)。`Row` 始终写入行内 `background: 'transparent'`,优先级高于 `.dsr-row:hover`、`:focus-visible` 和 `[data-dsr-current="true"]` 的背景。没有图钉 wrapper 的最近会话、插件等行因此失去这些背景反馈;焦点规则还移除了 outline。 这是源码确认的独立样式问题。本轮不把「当前会话判定为 true」当作真机已验证项:该宿主版本的 `retainedBy.mainView` 数据限制仍需单独处理。但即使当前标记正确,现有行内背景也会覆盖选中底色,因此第 12 轮记录的「宿主计数恢复后两侧会一起亮起」不足以保证结果。 建议:由 CSS 统一管理行状态背景;仅在确实需要的工作区 wrapper 内限制透明背景。分别验收 hover、focus-visible 和 current 状态。 ### 6. P2:折叠导航栏后,图标轨遮住会话页「对话」标签 用户反馈:在会话界面展开、折叠导航栏,会隐藏「对话」标签,导致无法切换。补充真机复核确认:本次表现为折叠时首个「对话」标签被遮挡;「上下文」「编队」仍可见,展开后「对话」恢复显示。 复现步骤与结果: 1. 在当前会话的「对话」视图保持导航栏展开,确认「对话 / 上下文 / 编队」标签均可见。 2. 点击宿主「收起侧边栏」。图标轨出现在左侧,「对话」文字和选中下划线不再可见,「上下文」「编队」仍在。 3. 切换到仍可见的「上下文」,内容正常切换,但「对话」仍被遮挡,无法直接用鼠标点击标签返回;上下文页左侧部分内容也被轨遮住。 4. 点击宿主「打开侧边栏」。「对话」重新可见,点击后能回到原对话正文。 可访问性树在折叠前后均保留「对话」标签,切到上下文后还显示其未选中状态。因此本轮确认的是视觉覆盖与鼠标入口受阻,不能描述成标签被卸载或所有标签一起消失。临时可通过展开侧栏恢复入口。 定位线索:[client.js](../client.js) 第 179–183 行把浮轨固定在窗口左侧 56px、顶部 48px 以下,`z-index: 60`,没有为宿主正文预留横向空间。宿主 `packages/client/ui-layout/src/client/AppFrame.tsx:188` 在 macOS 折叠时将侧栏宽度设为 0;`packages/client/ui-conversation/src/client/skeleton/ConversationRoot.module.css:74` 只对标题行应用左侧避让,206–218 行的标签行没有对应避让。已只读核对安装包 `0.2.2-rc.1` 的相应 CSS 规则,与本地源码一致。这些规则与真机看到的首标签遮挡相符;本轮未做 DOM 命中测试或停用插件的 A/B 对照,不记录精确标签边界。 本次仅补充报告,不修改插件或宿主实现。补充复核开始时侧栏为展开、当前为「对话」;结束时已恢复这两个状态,插件保持启用,未发送消息或保存临时截图。 ### 7. P3:设计说明保留了已修复错误的旧 API 示例 [design.md](design.md) 第 134 行仍写 `remote.pluginInventory.list()`,实际代码已使用 `remote.pluginManager.listBundles()`;第 139 行仍写 `openSession({ kind: 'session', sessionId })`,实际第 951 行正确传裸 `SessionId`。后者正是既往会话跳转问题的触发写法。 建议:在实现修复时同步更新设计说明,并核对相应历史验收记录与当前说明的区别,避免维护者依据旧例重新引入错误。 ## 验证边界与结束状态 本轮未重启整个 Electron/Host 进程,不能据此宣称冷启动回归通过;只验证了当前客户端加载及插件停用、启用。未验证 Web、Windows、Linux、浅色主题、视口宽度小于 768px 的真机行为、真实网络故障或精确的 120/200ms 时序。异步两个问题来自受控源码探针,矮窗口、Tab 和会话标签遮挡问题来自真实 Desktop;行状态背景覆盖问题来自源码审查。 首次复核结束时恢复原会话、原有插件启用状态、侧栏折叠和大窗口,撤销本轮测试置顶;后续标签问题的补充复核恢复其开始时的侧栏展开与「对话」视图。未发送消息、创建任务、重新安装插件、修改用户 profile 或生成临时截图文件。