diff --git a/apps/kimi-web/src/components/ChatDock.vue b/apps/kimi-web/src/components/ChatDock.vue index 4d9c5ac04..a5c59a9f8 100644 --- a/apps/kimi-web/src/components/ChatDock.vue +++ b/apps/kimi-web/src/components/ChatDock.vue @@ -150,14 +150,12 @@ defineExpose({ loadForEdit }); :aria-pressed="dockPanel === 'bash'" @click="emit('toggle-dock-panel', 'bash')" > - - {{ t('tasks.dockBash') }} - {{ bashTasks.length }} + + {{ t('tasks.dockBash') }} + ({{ bashTasks.length }}) @@ -252,6 +246,8 @@ defineExpose({ loadForEdit }); + +
+feat/web · 配套设计稿 reports/web-tab-split-design-2026-06-16.html这是收敛后的目标模型,三层各司其职,临时层永远不污染持久主 Tab:
+ +ViewGroup)固定只有 chat 与 files 两个 Tab。
+ 数据来源 usePaneLayout.ts 的 ALL_VIEWS = ['chat','files'];
+ normalizeLayout() 会把任何持久化里残留的 tasks/todo/terminal 过滤掉,所以刷新后也回不到旧的一级 Tab。
+ openPreview() / openBtw() 会把当前组分屏出一个专用临时组,并在 TabBar 上通过 hasPreview / hasBtw 显示对应 Tab;关闭即折叠回去,且不写入持久化。它们不会抢占底部输入框或移动端的主 Tab 状态。
+ aside.global-preview,一次只显示一个:Thinking 文本、Compaction 摘要、Subagent 详情,移动端额外承载文件/媒体预览。现在是带标签的 role="complementary" 区域,Escape 关闭,关闭按钮统一带 aria-label。
+ | 触发 | 去向 | 入口 |
|---|---|---|
| 点击工作区内的文件路径 | 进入 文件 主 Tab,定位到该文件 / diff | App.openFilePreview → ConversationPane.openWorkspaceFileInFiles |
| 点击工作区外路径 | 临时 Preview Tab(桌面)/ 右侧面板(移动)显示内容或越界提示 | App.openFilePreview → openPreviewPane |
| 点击媒体(图片) | 临时 Preview Tab(桌面)/ 右侧面板(移动) | App.openMediaPreview |
| 点击 Thinking 块 / Compaction 分隔 / Subagent 卡片 | 右侧详情层(互斥,再点同一个=收起) | App.openThinkingPanel / openCompactionPanel / openAgentPanel |
| BTW 侧聊 | 分屏临时 Tab(桌面)/ 主 Tab 降级(移动) | ConversationPane.openBtwPane |
| # | 任务 | 状态 | 关键文件 / 提交 |
|---|---|---|---|
| 1 | +Auth banner 不再 fixed 遮挡导航,进入布局流 | +完成 | +App.vue:外层 .app-shell flex 列,banner 作首个在流子元素 · 6e494cbc |
+
| 2 | +TabBar 改为真正的 tablist 语义 + 键盘 + focus | +完成 | +TabBar.vue ViewGroup.vue ConversationPane.vue:role=tab/tablist/tabpanel、aria-selected、roving tabindex、←/→/Home/End、aria-controls↔aria-labelledby · 测试 test/tabbar.test.ts · f62c7ac3 |
+
| 3 | +主 Tab 只保留对话/文件,不回收 Tasks/Todo/Terminal | +本已满足 | +usePaneLayout.ts 已是 ALL_VIEWS=['chat','files'] 且过滤旧视图;本轮确认 + 定档,未改代码 |
+
| 4 | +明确 transient 视图规则 | +已实现 + 定档 | +路由规则见上表;代码早已就位,本轮以文档固定 | +
| 5 | +ViewGroup 分屏/关闭按钮:本地化 aria-label + ≥28×28 | +完成 | +ViewGroup.vue + workspace.ts(en/zh):splitRight/splitDown/closeGroup,按钮 24→28 · f62c7ac3 |
+
| 6 | +统一 Header/Sidebar/TabBar/ViewGroup 图标按钮热区 | +完成 | +ChatHeader kebab 28×28 + aria-label;Sidebar .gh-more 24×24 + 键盘可见、.gh-add 增大;多处 focus-visible · 172456a9 / f62c7ac3 |
+
| 7 | +右侧详情层一致规则:标题/关闭/aria-label/Escape/移动端 | +完成 | +App.vue(Escape 捕获阶段关闭、区域 role/label)+ ThinkingPanel/AgentDetailPanel/FilePreview(aria-label + focus 环)· f5caf4cc |
+
| 8 | +弹层焦点基线:aria-modal/初始焦点/焦点返回/Escape | +3/4 完成 | +新增 useDialogFocus.ts,接入 ModelPicker / LoginDialog / ProviderManager + 测试 · 22f6c1d2SettingsDialog 延后,见第 4 节 |
+
| 9 | +File/Diff:降低色块干扰,突出文件名和代码 | +完成 | +DiffView.vue:整行 12% 染色 → 7% 淡底 + 左侧色条,文字回到正常色,仅 +/- 号带色 · eae691e4 |
+
| 10 | +更新设计文档 / 追加实现说明 | +本文件 | +reports/web-tab-split-implementation-2026-06-16.html |
+
usePaneLayout 早已如此)MobileSettingsSheet?debug=1 可见,非主体验,留后续SettingsDialog 的弹层焦点接入被有意延后。本轮工作期间,SettingsDialog.vue 正被另一项“设置侧边 Tab(settings side tabs)”改动重写(约 360 增 / 251 删,未提交)。在它之上编辑并提交,会把那份未完成的大改一并打进本轮 commit,污染历史。
共享的 useDialogFocus() 已经就绪,等 side-tabs 落地后,给 SettingsDialog 接入只是“一个 ref + aria-modal/tabindex + 一行调用”,是低成本 follow-up。其余三个优先弹层(ModelPicker / LoginDialog / ProviderManager)本轮已干净覆盖。
同样原因,本轮全程未触碰并行在改的 ChatDock.vue / Composer.vue / settings.ts 等文件。
vue-tsc --noEmit:通过(每个提交前均跑)。vitest run:39 个测试文件全部通过;新增 test/tabbar.test.ts(tablist/键盘/tabpanel 关联)与 test/model-picker.test.ts 焦点用例(开启聚焦搜索框、关闭返回开启者、aria-modal)。test/markdown-streaming-placeholders.test.ts 偶发 flaky(流式占位时序),与本轮改动无关,隔离重跑稳定通过。