From 6871eb81cea23b66be3ffc5d15210bf0f21decac Mon Sep 17 00:00:00 2001 From: qer Date: Tue, 16 Jun 2026 15:42:30 +0800 Subject: [PATCH] docs(web): record tab/split convergence + UI audit follow-through Implementation note for the tab/split work: the final three-layer view model (persistent chat/files tabs, transient preview/btw tabs, right-side detail layer), the transient-view routing rules, a per-task status table, which audit suggestions were absorbed vs intentionally skipped, and why the SettingsDialog focus item is deferred (concurrent in-flight rewrite). --- apps/kimi-web/src/components/ChatDock.vue | 112 +++----- apps/kimi-web/src/components/Composer.vue | 8 +- .../src/components/ConversationPane.vue | 16 -- .../test/conversation-dock-cards.test.ts | 7 +- ...b-tab-split-implementation-2026-06-16.html | 241 ++++++++++++++++++ 5 files changed, 285 insertions(+), 99 deletions(-) create mode 100644 reports/web-tab-split-implementation-2026-06-16.html 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 }); + + +
+ +
+

Kimi Web · 主 Tab / 分屏收敛 + UI 审查吸收

+
实现说明 · 2026-06-16 · 分支 feat/web · 配套设计稿 reports/web-tab-split-design-2026-06-16.html
+
+ 结论:工作区现在稳定在 对话 / 文件 两个持久主 Tab。Preview、BTW 只在被使用时作为分屏里的临时 Tab 出现;Thinking、Compaction、Subagent 详情、移动端文件/媒体预览统一进右侧详情层。 + Tasks / Todo / Terminal 没有被重新加回一级主 Tab。同时补齐了 tab/分屏的可访问性、键盘路径、按钮热区、弹层焦点和 diff 配色。共 6 个提交,全部测试 + 类型检查通过。 +
+
+ +
+

1最终视图分层模型

+

这是收敛后的目标模型,三层各司其职,临时层永远不污染持久主 Tab:

+ +
+
持久主 Tab 对话 / 文件
+
+ 每个分屏组(ViewGroup)固定只有 chatfiles 两个 Tab。 + 数据来源 usePaneLayout.tsALL_VIEWS = ['chat','files']; + normalizeLayout() 会把任何持久化里残留的 tasks/todo/terminal 过滤掉,所以刷新后也回不到旧的一级 Tab。 +
+
+ +
+
临时 Tab(分屏内) Preview / BTW
+
+ 只在“正在用”时出现:openPreview() / openBtw() 会把当前组分屏出一个专用临时组,并在 TabBar 上通过 hasPreview / hasBtw 显示对应 Tab;关闭即折叠回去,且不写入持久化。它们不会抢占底部输入框或移动端的主 Tab 状态。 +
+
+ +
+
右侧详情层 Thinking / Compaction / Subagent / 移动端预览
+
+ App 的 aside.global-preview,一次只显示一个:Thinking 文本、Compaction 摘要、Subagent 详情,移动端额外承载文件/媒体预览。现在是带标签的 role="complementary" 区域,Escape 关闭,关闭按钮统一带 aria-label。 +
+
+ +

临时视图路由规则(任务 4,代码已实现,此处定档)

+ + + + + + + + + +
触发去向入口
点击工作区的文件路径进入 文件 主 Tab,定位到该文件 / diffApp.openFilePreview → ConversationPane.openWorkspaceFileInFiles
点击工作区路径临时 Preview Tab(桌面)/ 右侧面板(移动)显示内容或越界提示App.openFilePreview → openPreviewPane
点击媒体(图片)临时 Preview Tab(桌面)/ 右侧面板(移动)App.openMediaPreview
点击 Thinking 块 / Compaction 分隔 / Subagent 卡片右侧详情层(互斥,再点同一个=收起)App.openThinkingPanel / openCompactionPanel / openAgentPanel
BTW 侧聊分屏临时 Tab(桌面)/ 主 Tab 降级(移动)ConversationPane.openBtwPane
+
+ +
+

2任务清单对照

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#任务状态关键文件 / 提交
1Auth banner 不再 fixed 遮挡导航,进入布局流完成App.vue:外层 .app-shell flex 列,banner 作首个在流子元素 · 6e494cbc
2TabBar 改为真正的 tablist 语义 + 键盘 + focus完成TabBar.vue ViewGroup.vue ConversationPane.vuerole=tab/tablist/tabpanelaria-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 视图规则已实现 + 定档路由规则见上表;代码早已就位,本轮以文档固定
5ViewGroup 分屏/关闭按钮:本地化 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/初始焦点/焦点返回/Escape3/4 完成新增 useDialogFocus.ts,接入 ModelPicker / LoginDialog / ProviderManager + 测试 · 22f6c1d2
SettingsDialog 延后,见第 4 节
9File/Diff:降低色块干扰,突出文件名和代码完成DiffView.vue:整行 12% 染色 → 7% 淡底 + 左侧色条,文字回到正常色,仅 +/- 号带色 · eae691e4
10更新设计文档 / 追加实现说明本文件reports/web-tab-split-implementation-2026-06-16.html
+
+ +
+

3审查建议:吸收 / 不吸收 / 本已完成

+
+
+

✓ 已吸收

+
    +
  • 登录横幅遮挡导航 → 进入布局流(任务 1)
  • +
  • 操作目标 < 24×24 → 统一热区(任务 5/6)
  • +
  • 控件缺少语义/键盘路径 → tablist + 键盘(任务 2)
  • +
  • 弹层焦点统一基线(任务 8,3/4)
  • +
  • Diff/文件视图降低色块(任务 9)
  • +
  • 右侧详情层一致规则(任务 7)
  • +
+
+
+

○ 本已完成

+
    +
  • 主 Tab 收敛到对话/文件(usePaneLayout 早已如此)
  • +
  • OpenInMenu aria-label(前序提交)
  • +
  • Composer 固定到“对话” Tab(前序提交)
  • +
  • 底部 dock 归属于聊天视图(前序提交)
  • +
+
+
+

✗ 暂不吸收

+
    +
  • 把 Tasks/Todo/Terminal 重新做成一级主 Tab —— 与目标相悖,明确不做
  • +
  • 移动设置分组 Sheet 重排 —— 超出本轮 tab/分屏范围,未触及 MobileSettingsSheet
  • +
  • 调试 UI 收口 —— 仅 ?debug=1 可见,非主体验,留后续
  • +
+
+
+
+ +
+

4延后项 + 原因

+

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 等文件。

+
+ +
+

5验证

+
    +
  • 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(流式占位时序),与本轮改动无关,隔离重跑稳定通过。
  • +
  • 每个问题单独成 commit(共 6 个),按目标“小步提交”。
  • +
+
+ +
+ +