- 四窗口固定顺序占位布局,空时收起到 0 宽;动画区分「运行中新出现」与「切换/加载」 - 文件记录:edit/write 埋点写入对话 metadata.edited_files,预览走既有 /api/file/content - 详情面板:lastDetail/lastStatus 快照、首次填充静态+瞬间到底、终态四色分类 - 数据通道全部走 REST 轮询(带 conversation_id 对话级隔离),含 demo 与设计文档
133 lines
10 KiB
Markdown
133 lines
10 KiB
Markdown
# 对话区快捷窗口栈 · 设计定稿(2026-07-21)
|
||
|
||
> 状态:demo 已验收,正式接入 `static/src` 中。
|
||
> Demo 位置:`demo/todo_float_window/`(目录名为历史遗留,内含全部 4 个窗口;浏览器直接打开 `index.html`)。
|
||
> 本文档是唯一设计依据;压缩对话后凭本文档 + demo 继续。
|
||
|
||
## 0.1 正式接入变更(2026-07-21 用户补充,覆盖 demo 的悬浮定位)
|
||
|
||
1. **命名**:该功能正式名称为「**快捷窗口**」(quick dock / quick window)。
|
||
2. **布局:占位而非悬浮**:快捷窗口**不是** `position: fixed` 悬浮,而是在**对话区域右侧空白区域占位显示**的一列,常驻时会**把对话区域向左挤压**(与 git/终端 right-panels 同为 `chat-container` 的 flex 兄弟节点,位于 `</main>` 之后、right-panels 之前)。有内容时才显示该列。
|
||
3. **设置项**:个人空间(PersonalizationDrawer)新增「**隐藏快捷窗口**」开关,**默认关闭**(即默认显示)。字段 `hide_quick_dock: boolean` 存 personalization form。
|
||
4. 详情面板(子智能体/后台指令)保持 fixed 悬浮,从快捷窗口列左侧滑出;文件预览侧边栏在快捷窗口列右侧(git/终端面板左侧)占位/滑出。
|
||
5. 移动端(isMobileViewport)第一版先隐藏。
|
||
|
||
## 0. Demo 文件地图
|
||
|
||
| 文件 | 内容 |
|
||
|------|------|
|
||
| `index.html` | 页面结构:模拟对话区 + 悬浮栈(4 窗口)+ 详情面板 + 预览侧边栏 + 菜单 + toast |
|
||
| `style.css` | 变量(配色/尺寸/缓动)+ 窗口基础 + 待办样式与全部 keyframes |
|
||
| `app.js` | 待办逻辑(创建/完成/替换/滚动划线) |
|
||
| `runner.css` / `runner.js` | 子智能体 + 后台指令窗口、左侧详情面板、⋯ 菜单(强制关闭) |
|
||
| `file.css` / `file.js` | 文件记录窗口、右侧预览侧边栏、⋯ 菜单(下载/打开/复制路径)、toast |
|
||
|
||
## 1. 总体布局
|
||
|
||
- 位置:对话区域右侧**占位列**(非悬浮):`chat-container` 的 flex 兄弟节点,宽 294px(270 + 左右 padding 12),`flex: none`,整列上下排布。对话区被自然向左挤压。
|
||
- 窗口顺序(从上到下,固定):**待办事项 → 子智能体 → 后台指令 → 文件**。
|
||
- 窗口上下排布,右对齐,`gap: 12px`。
|
||
- **整列可滚动**:列高 100%,`overflow-y: auto`,滚动条隐藏。
|
||
- 防阴影被 overflow 裁剪:栈 `padding: 24px; margin: -24px`。
|
||
- 栈滚动时关闭所有打开的 ⋯ 菜单(菜单 fixed 定位不随栈滚动)。
|
||
- 各窗口初始隐藏,有内容时淡入出现(fade + translateY(-6px),0.3s)。
|
||
|
||
## 2. 窗口通用规格
|
||
|
||
- **宽度固定 270px**(= 最大宽度 300px × 90%,变量 `--window-max-w` 联动)。
|
||
- **列表区高度固定 5 行**(行高 36px → 180px),不足留空,超出内部滚动,滚动条隐藏。
|
||
- 窗口 `flex: none`(防止被栈 flex 压缩变矮,已踩坑)。
|
||
- 圆角 12px;背景不透明(实体面板禁半透明);中性阴影(无发光);1px 细边框。
|
||
- 标题栏 38px:SVG 图标(accent 色)+ 标题(12.5px 次要色)+ 右侧计数(11.5px 三级色)。
|
||
- **列表无上下 padding**:首行 hover 紧贴标题栏分隔线,末行紧贴窗口底边。
|
||
- 行:固定 36px 高,直接铺在窗口上(无嵌套卡片/背景/边框),超长文字省略号。
|
||
- 正式接入时配色全部换成三主题语义 token(demo 为经典主题暖色写死)。
|
||
|
||
## 3. 各窗口设计
|
||
|
||
### 3.1 待办事项
|
||
|
||
- 行结构:`[状态点] 任务文字`;**无 hover、不可点击**。
|
||
- 计数:`done/total`。
|
||
- 动画:
|
||
- **创建**:逐行从左向右(位移 14px + 淡入,0.34s,stagger 60ms)。
|
||
- **完成**:横线从左往右划过文字(260ms,`strike-in` scaleX 0→1,origin left)→ 文字延迟 120ms 变灰 + 状态点变色弹跳(`dot-pop`)。静态完成态(整表渲染时)不播划线,直接呈现。
|
||
- **整表替换**(收到新 todo):旧行从上到下逐行从右向左移出(0.26s,stagger 45ms,**带高度收拢**让下方行平滑上移)→ 新行逐行进入。
|
||
- **区域外条目**:要完成的行未完全显示时,先 `scrollIntoView({behavior:'smooth', block:'nearest'})` 滚出,滚动停稳 90ms 后再播划线(800ms 兜底)。
|
||
|
||
### 3.2 子智能体 / 3.3 后台指令(两者同构,仅详情内容不同)
|
||
|
||
- 行结构:`[●状态点] 名称 [⋯]`;**有 hover,整行可点击**;后台指令名称用等宽字体。
|
||
- 状态点:运行中 = accent 色呼吸(`status-pulse` 1.6s);完成/终止 = 灰,名称同步变灰。
|
||
- 计数:`running/total`。
|
||
- 新条目:进入动画同待办;**区域外先滚动露出再播进入动画**(先隐身插入,滚动到位后播)。
|
||
- **无删除**(条目永久保留)。
|
||
- ⋯ 菜单:仅「强制关闭」(警示红色,运行完/已终止置灰);菜单与按钮**右对齐**向下展开;两菜单(本菜单与文件菜单)互斥;Esc / 点空白关闭。
|
||
- **点击行 → 左侧展开详情面板**(480×460,`right: calc(20px + 270px + 12px)`,滑入 0.24s):
|
||
- 子智能体:工具调用 + 文本输出流,**扁平行式无嵌套卡片**;工具行:运行中 spinner(旋转)→ 完成变 ✓ + 右侧落定结果;文本行次要色。
|
||
- 后台指令:等宽终端行,首行 `$ 命令` 加粗。
|
||
- 新进度出现动画:淡入 + translateY(5px) 上浮(0.25s)。
|
||
- **自动滚动**:距底 <80px 时新内容平滑滚到底;用户上翻则不打断。
|
||
- 再点同一行 / ✕ / Esc 关闭;点其他行切换(内容区快速淡入)。
|
||
- ⚠️ **实际 API 颗粒度提醒**:子智能体进度 API 没有细化的"运行中流式状态",**每个步骤完成后才推送一次进度**。demo 里的流式效果仅为演示,正式接入时的显示颗粒度届时再定。
|
||
|
||
### 3.4 文件
|
||
|
||
- 行结构:`[⋯] 文件名`;**无状态点**;有 hover,整行可点击。
|
||
- 计数:文件总数。
|
||
- 显示逻辑(正式版):**文件被编辑/创建过 + 文件仍存在**才显示;同一文件按 path 去重只出现一次。
|
||
- ⋯ 菜单(与按钮**左对齐**向下展开):
|
||
1. **下载**
|
||
2. **在文件管理器中打开**——复用 git 状态侧边栏已有逻辑,用默认应用打开;**docker 模式下无此选项**
|
||
3. **复制路径**——工作区内相对路径
|
||
- **点击行 → 右侧滑出预览侧边栏**(非左侧详情面板):
|
||
- 440px 宽、全高(top/bottom 20px),`translateX(100%+24px)` ↔ `0`,0.3s。
|
||
- 打开时**悬浮窗栈与详情面板同步左移让位**(栈 `right: 20+440+12`,均带 0.3s transition)。
|
||
- 头部:文件图标 + 文件名(等宽加粗)+ 目录(灰小字)+ 关闭按钮。
|
||
- 内容:等宽 12px + 行号(44px 灰),行 hover 有底色,滚动条隐藏。
|
||
- 再点同一文件 / ✕ / Esc 关闭;点其他文件直接切换。
|
||
- 仅支持特定种类 UTF-8 文本文件(正式版按扩展名白名单)。
|
||
|
||
## 4. 后端配合点(正式接入)
|
||
|
||
1. **文件记录**:每次 `write_file` / `edit_file` 成功后,把文件**相对路径**(含时间戳、操作类型)写入**对话文件(对话 JSON)**;同一 path 更新而非追加重复项。
|
||
2. **前端读取**:前端从对话数据中读取文件记录列表渲染文件窗口。
|
||
3. **文件内容**:预览用**现有文件内容 API**(用户确认已有,无需新做)。
|
||
4. 待办 / 子智能体 / 后台指令:接各自现有数据流与事件推送。
|
||
|
||
## 5. 已踩过的实现坑(接入时避开)
|
||
|
||
1. `display: flex` 会覆盖 `hidden` 属性的 `display: none` → 必须补 `.panel[hidden] { display: none; }`。
|
||
2. flex 容器会压缩子元素(窗口变矮)→ 窗口加 `flex: none`。
|
||
3. `animationend` 会冒泡(feed 行 → 详情面板),用 `{once:true}` 监听会被提前消费 → 时序控制改用 `setTimeout`。
|
||
4. `overflow` 裁剪窗口阴影 → 滚动容器 `padding + 负 margin` 扩大滚动盒。
|
||
5. 菜单 fixed 定位不随栈滚动 → 栈滚动时主动关闭菜单。
|
||
6. 两个 ⋯ 菜单 + Esc 多层浮层:菜单互斥;Esc 按 菜单 → 详情面板 → 预览栏 顺序逐个关(用 `e.preventDefault()` + `e.defaultPrevented` 串联)。
|
||
|
||
## 6. 正式接入任务清单(2026-07-21 已全部完成)
|
||
|
||
- [x] 后端:编辑文件路径记录写入对话 JSON(write/edit 钩子)+ 对话数据带出该列表
|
||
- [x] 前端:`static/src` 快捷窗口占位列容器(非悬浮,挤压对话区)
|
||
- [x] 前端:4 个窗口组件 + 详情面板 + 文件预览侧边栏(配色走语义 token 三主题)
|
||
- [x] 前端:接入待办 / 子智能体 / 后台指令 / 文件记录的真实数据与事件推送
|
||
- [x] 前端:文件预览内容接现有文件内容 API(扩展名白名单)
|
||
- [x] 动画参数按 demo 定稿迁移(位移 14px、stagger 60/45ms、划线 260ms 等)
|
||
|
||
### 接入实现位置(2026-07-21)
|
||
|
||
**后端**
|
||
- `core/main_terminal_parts/tools_execution.py`:`_record_edited_file` / `_remove_edited_file` / `_rename_edited_file` + `_mutate_edited_files`(写入对话 `metadata.edited_files`,同 path 去重;广播 `edited_files_updated`)
|
||
- `server/conversation.py`:`GET /api/conversations/<id>/messages` 返回 `edited_files`(过滤不存在文件)
|
||
- `server/app_legacy.py`:`terminal_broadcast` 白名单加 `edited_files_updated`
|
||
- `modules/personalization_manager.py`:`hide_quick_dock` 字段(默认 False)
|
||
|
||
**前端**
|
||
- `static/src/components/chat/quickdock/`:`QuickDock.vue`(占位列容器+菜单+Esc 分层+轮询)、`TodoWindow.vue`、`RunnerWindow.vue`(子智能体/后台指令通用)、`RunnerDetailPanel.vue`、`FileWindow.vue`、`FilePreviewPanel.vue`、`quickdock.css`(全部 keyframes 与语义 token 配色)
|
||
- `static/src/stores/quickDock.ts`:editedFiles / detail / previewPath / menu 状态
|
||
- `static/src/composables/useLegacySocket.ts`:监听 `edited_files_updated`(按 conversation_id 过滤)
|
||
- `static/src/app/methods/history.ts`:messages 加载后回填 editedFiles
|
||
- `static/src/App.vue`:`</main>` 后挂载 `QuickDock` + `FilePreviewPanel`(移动端与 hide_quick_dock 时不渲染)
|
||
- `static/src/components/personalization/PersonalizationDrawer.vue`:「隐藏快捷窗口」设置行(默认隐藏工作区之后)
|
||
|
||
**验证状态**:py_compile ✓ / 6 项后端冒烟 ✓ / tsc+stylelint+vite build ✓ / 新文件 eslint 0 问题,存量文件 lint 与基线持平。8091 服务重启后的实际运行验证待手动进行。
|