docs(superpowers): 补录前端美化与交互改造的设计规范与实施计划

- docs/superpowers/specs/2026-08-30-frontend-beautify-design.md
  前端美化与交互改造设计(深色指挥中心 + 文档工作台混合风格,
  单一青绿 #5EEAD4 强调色,chat.html 拆为 chat.html + chat.css + chat.js,
  8 项交互改善,用户决策 10 项)
- docs/superpowers/plans/2026-08-30-frontend-beautify.md
  前端美化与交互改造 Implementation Plan(三步走:机械拆分 → 视觉重塑 →
  交互改造,Task 独立 commit,浏览器手测验证)

按项目文档规则存入 docs/superpowers/ 目录。
This commit is contained in:
lhl
2026-08-31 22:33:46 +08:00
parent 19d214039c
commit b14c2213bc
2 changed files with 2146 additions and 0 deletions
@@ -0,0 +1,367 @@
# 前端美化与交互改造设计(2026-08-30)
## 背景与目标
当前前端(`src/genesis/server/static/chat.html`,单文件 1215 行内联 CSS+JS)沿用 Material/Google 风
`#1a73e8` 蓝、Inter、胶囊按钮、扁平阴影),功能完整但视觉同质化严重,与"概要设计书自动生成
Agent"的项目定位缺少记忆点。
本次迭代在**保持现有功能、API 协议、localStorage 键、所有 DOM id 完全不变**的前提下,对整套前端
做视觉重塑 + 8 项交互改善,达成:
1. 视觉差异化(深色指挥中心 + 文档工作台混合风格,单一青绿强调色)
2. 体感现代化(拖拽、多行输入、消息气泡操作、骨架屏、空状态快捷指令)
3. 架构可维护(chat.html 拆为 chat.html + chat.css + chat.jschat_state.js / chat_ws.js 不动)
## 用户决策(brainstorming 已确认)
| # | 决策项 | 选定 |
|---|---|---|
| 1 | 视觉风格 | 深色指挥中心 + 文档工作台混合 |
| 2 | 强调色 | 青绿 `#5EEAD4` |
| 3 | 字体策略 | 系统字体栈(不引入 Google Fonts / JetBrains Mono |
| 4 | 抽屉遮罩 | `backdrop-filter: blur(8px)` |
| 5 | 文件拆分 | B2chat.html 拆为 chat.html + chat.css + chat.js |
| 6 | 交互改造 | 本轮做 8 项(详见 §四) |
| 7 | 快捷键 | 只做 Esc + Cmd/Ctrl+K |
| 8 | 移动端改造 | **不做**(留到下个迭代) |
| 9 | 消息气泡"重新生成" | **不做**(避免触发后端协议变更) |
| 10 | 主题切换 | 单主题(无 dark/light 切换) |
## 范围
### 做
- 视觉重塑(六大区域,颜色 / 字体 / 间距 / 圆角 / 动效)
- chat.html 拆分为 chat.html + chat.css + chat.js
- 8 项交互改善(见 §四)
- 同步 `docs/design.md` 增加"前端 UI 设计规范"一节
- 追加 `_AI_USAGE_LOG.md` 三条记录(设计 / 实现 / 文档同步)
### 不做
- 移动端侧边栏抽屉化(断点 < 768px 的改造)
- 快捷键 `Cmd+Shift+O` / `Cmd+Shift+S`
- 消息气泡"重新生成"按钮
- 主题切换(dark/light
- 后端 API 协议变更
- localStorage / sessionStorage 键重命名
- DOM id 变更
## 架构
### 文件结构(B2 拆分后)
```
src/genesis/server/static/
├── chat.html # 仅 DOM 结构
├── chat.css # 新增:所有视觉样式
├── chat.js # 新增:原 chat.html 中 <script> 段(约 700 行)
├── chat_state.js # 不动
└── chat_ws.js # 不动
```
### chat.html 改动
- 头部:删除 `<link rel="preconnect">` 与 Google Fonts 的 `<link>`
- 头部:新增 `<link rel="stylesheet" href="/chat.css">`
- 头部末尾(`<body>` 之前):保持空白
- 主体:删除第 10-392 行 `<style>...</style>` 整段
- 主体:保留所有 DOM 节点 id(业务逻辑依赖)
- 末尾:删除第 509-1213 行 `<script>...</script>` 整段
- 末尾:新增 `<script src="/chat.js" defer></script>`
- 末尾:保留 `<script src="/chat_state.js">``<script src="/chat_ws.js">` 在 chat.js 之前
### chat.js 改造
- 整体结构保持不变(DOMContentLoaded 后绑定事件 + 启动恢复)
- 新增函数:`groupSessions``bindUploadDrag``attachBubbleOps``autoResizeTextarea`
`renderProjectMismatchBadge``renderSkeleton``renderEmptyQuickActions`
`addInfo``addWarn``bindKeyboardShortcuts`
- 保持函数签名与原内联版本一致(避免 chat_state.js 协议变化)
## 一、设计令牌(chat.css 顶部 `:root`
```css
:root {
/* === 颜色 === */
--bg-base: #0B0F17;
--bg-surface: #141A24;
--bg-elevated: #1B2330;
--bg-input: #0F141C;
--border-subtle: rgba(255, 255, 255, 0.08);
--border-strong: rgba(255, 255, 255, 0.14);
--text-primary: #E5EAF0;
--text-secondary: #8B95A7;
--text-muted: #5C6677;
--accent: #5EEAD4;
--accent-soft: rgba(94, 234, 212, 0.15);
--success: #34D399;
--warning: #FBBF24;
--danger: #F87171;
--info: #60A5FA;
/* === 字体 === */
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Microsoft YaHei", "Noto Sans CJK SC", system-ui, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
"Liberation Mono", monospace;
/* === 间距 === */
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px;
--sp-4: 16px; --sp-5: 20px; --sp-6: 24px;
/* === 圆角 === */
--r-sm: 6px; --r-md: 10px; --r-lg: 14px;
/* === 动效 === */
--ease: cubic-bezier(.2, 0, 0, 1);
--dur: .18s;
}
```
## 二、各区域视觉规约
### 2.1 侧边栏(`#sidebar`
- 底色 `--bg-surface`,右 1px `--border-subtle`
- 折叠按钮:从右外侧浮出的小竖条,hover 时变 `--accent`**不再旋转 180°**
改为左右 chevron`«` / `»`)切换
- 会话项 hover:背景 `--bg-elevated`,左侧 2px `--accent` 竖条
- 会话项 active:左侧 2px `--accent` 竖条 + 背景 `--bg-elevated`
- 折叠态:去掉头像逻辑,改成 4px 圆点(项目色 hash);折叠态下分组标题 `display:none`
### 2.2 顶栏(`header`
- 底色 `--bg-surface` + `backdrop-filter: blur(12px)` + 1px `--border-subtle` 底边
- 项目切换器:胶囊改为左 6px 圆点(项目色 hash+ 名字 + chevronhover 出现 1px `--accent`
- RAG 胶囊:去掉背景底色,改成 `🔘 0 片段` 风格的等宽小标签
- 已索引:实心圆点 + `--accent` 色 + 等宽数字
- 未索引:空心圆点 + `--text-muted`
- 会话徽标:窄屏下不再 `display:none`,限制 `max-width: 32%` + `text-overflow: ellipsis`
### 2.3 聊天区(`#chat`
- 主区底色 `--bg-base`,内边距 `var(--sp-6)`
- 气泡:去掉阴影,改用 1px 细边 + 内部留白
- **用户消息**`--bg-surface` + 左侧 2px `--accent` 竖条 + 字号 14,行高 1.65
- **助手消息**`--bg-surface` + 1px `--border-subtle` + 顶头 6px 圆点(`--accent` 静态)
- **进度消息**`--bg-input` + dashed 边 `--accent-soft`,等宽字体
- 思考中态:3 个等宽方块循环亮起(替代当前 `思考中…` 文案),用 `--accent`
- 消息出现动画:保留 `zoomIn`,幅度从 0.97 → 0.98
### 2.4 上传条(`#upload-bar`
- 底色 `--bg-surface`,与主区用 1px `--border-subtle` 分隔
- 升级为可拖入区域(详见 §四-2):dragover 时整条出现 dashed `--accent` 边 + `accent-soft` 底色
- select / button 深色样式:底色 `--bg-input`focus 时 `0 0 0 2px var(--accent-soft)`
- 项目提示(`#proj-hint`)改为左侧 4px 三角指示条 + inline note
### 2.5 输入区(`#composer`
- 底色 `--bg-surface`,顶 1px `--border-subtle`
- `<input id="input">` 升级为 `<textarea id="input" rows="1">`(详见 §四-4
- send 按钮:底色 `--accent`,文字 `--bg-base`hover 加 `accent-soft` 外发光
### 2.6 项目抽屉(`#proj-drawer`
- 整体 `--bg-surface`,背后遮罩 `rgba(11,15,23,.6)` + `backdrop-filter: blur(8px)`
- 左侧列表项:active 时左侧 2px `--accent` 竖条 + 背景 `--bg-elevated`
- 表单输入:深色底 `--bg-input`focus 时 `0 0 0 2px var(--accent-soft)`
- 「高级」details 改为纯线框展开,无背景填充
- 关闭按钮:hover 红色描边(保留现有语义)
## 三、关键不变量(兼容性保证)
### 3.1 必须保留的 DOM id(业务逻辑依赖)
```
sidebar, sidebar-toggle, brand-logo, brand-name, new-chat, session-list,
main, rag-bar, rag-enabled, rag-stats, impact-btn, sid-badge,
proj-switcher, ps-current, ps-name, ps-menu, chat, upload-bar, file-type,
file-input, upload-btn, upload-status, proj-hint, composer, input, send,
proj-drawer, drawer-list, drawer-form, drawer-close, pf-name, pf-display,
pf-template, pf-write, pf-rules, pf-code, pf-design, pf-save, pf-delete
```
### 3.2 必须保留的全局符号
- `window.GenesisState`(由 chat_state.js 提供,chat.js 通过回退默认值兜底)
- `window.GenesisWS`(由 chat_ws.js 提供,chat.js 通过 `connectProgressWs` / `applyProgressEvent` 消费)
### 3.3 必须保留的 localStorage / sessionStorage 键
- `genesis_sidebar_collapsed`
- `genesis_session`
- `genesis_rag_enabled`
- `genesis_skipped_project_hint`sessionStorage
新增键(无冲突):
- `genesis_session_group_collapsed`(分组折叠状态,本轮默认全展开,不实际写入)
### 3.4 必须保留的 API 端点
所有 `/api/...` 调用与请求体格式不变。
## 四、交互改造(8 项)
### 4.1 会话列表按时间分组
- **位置**`refreshSessions()` 内,对 `list.sort(...)` 之后插入分组
- **分组规则**:用 `updated_at` 字符串前缀(`YYYY-MM-DD`)与今天/昨天日期比较
- 今天 / 昨天 / 本周(7 天内)/ 本月 / 更早
- **DOM**:在 `.sess` 之间插入 `<div class="sess-group">今天</div>`,样式:
`font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; color: var(--text-muted)`
- **不影响**:折叠态下分组标题 `display:none`;分组内 `.sess` 仍可正常点击
- **行为**:分组标题不可点击,不参与折叠按钮 toggle
### 4.2 拖拽上传
- **位置**`#upload-bar` 监听 `dragenter / dragover / dragleave / drop`
- **行为**
- `dragover``upload-bar``.drag-active` 类(dashed `--accent` 边 + `accent-soft` 底色)
- `dragleave``upload-bar` 移除 `.drag-active`
- `drop`:取 `e.dataTransfer.files[0]`,写入 `#file-input.files`,并**按后缀自动选 `file-type`**
- `.xlsx``requirements`
- `.docx``template`(缺省兜底)
- `.zip``existing_system`
- **安全**:复用现有 `projectHasPrefixedType``UPLOAD_EXT_RULES` 校验
- **拖入整个文件夹**`webkitGetAsEntry` 拒绝并提示(仅取第一个文件)
### 4.3 消息气泡操作
- **触发**
- **用户气泡**:hover 时右上角浮出 `↺ 引用` 按钮(点击把消息文本填到 `#input` 并 focus
- **助手气泡**:hover 时右上角浮出 `⧉ 复制` 按钮
- **不实现**`↻ 重新生成` 按钮(见 §零-9 决策)
- **实现**
- CSS`.msg { position: relative; } .msg .ops { position: absolute; top: -10px; opacity: 0; ... }`
`:hover .ops { opacity: 1; }`
- JS:扩展 `addMsg()`,内部 appendChild `<div class="ops">...</div>`;用户/助手不同
- **复制**:用 `navigator.clipboard.writeText(text)`,无 HTTPS 时 fallback
`document.execCommand('copy')`(同步临时 textarea
- **不存储历史**:仅影响本次会话 UI
### 4.4 输入区多行 + 自动高度
- **位置**`<input id="input">` 改为 `<textarea id="input" rows="1">`
- **行为**
- `input` 事件:`element.style.height = 'auto'; element.style.height = Math.min(scrollHeight, 160) + 'px'`
- `keydown``Enter`(无 Shift 且无 IME 组合中)= 发送;`Shift+Enter` = 换行
- 发送后自动重置高度为 `auto`(下一帧设回 24px
- **保留**:现有 `maxlength=2000`
- **IME 兼容**keydown 中检查 `e.isComposing`(中文输入法组合中按 Enter 不应发送)
### 4.5 顶栏项目不匹配徽章化
- **位置**:在 `#rag-bar` 之前插入 `<span id="proj-mismatch-badge" hidden>`
- **行为**
- 替代现有 `renderProjectMismatchHint()` 的聊天区插入逻辑
- 徽章样式:黄色 1px 边 `--warning` + 圆角 `--r-md` + `⚠` 前缀 +
"项目: stock ≠ 草稿: foo" 文本
- 点击展开一个迷你 popover(不打开抽屉),提供"切到该项目"和"保留"两个按钮
- "保留"逻辑沿用现有 `sessionStorage` 标记(`genesis_skipped_project_hint`
- **不删除旧函数**:保留 `renderProjectMismatchHint` 但改为只调用新徽章渲染函数
### 4.6 快捷键(仅 2 个)
- **Esc**:已有,扩展为同时关闭项目切换器 + 抽屉 + 移动端侧边栏(本轮不做移动端,等价无变化)
- **Cmd/Ctrl + K**:切换 `#ps-menu`(与点击 `#ps-current` 行为一致),焦点在输入框时也能用
- **实现位置**`document.addEventListener('keydown', ...)`,沿用现有 Escape 监听并扩展
- **可视提示**:项目切换器 hover 时显示 `⌘K`CSS `::after`,仅 mac 显示,win/linux 省略)
### 4.7 骨架屏
- **位置**:启动时(`localStorage``genesis_session` 时)替换
`addMsg('assistant', '正在恢复上次会话…')`
- **样式**:3 条消息形状的占位(圆形 8px + 矩形条 `--bg-surface`),用
`linear-gradient``@keyframes shimmer` 1.4s 循环(亮色 `--bg-elevated`
`--bg-surface` 交替)
- **逻辑**`loadSession()` 完成后 `skeleton.remove()`,最多 5 秒超时兜底
- **超时处理**`setTimeout(5000)` 强制 remove 并显示错误提示
### 4.8 空状态快捷指令
- **位置**`renderEmptyOrWelcome()` 中,无 `sid``draftProject` 已选时
- **内容**:欢迎气泡 + 3 个胶囊按钮:
- "上传要件定义"(点击触发 `#file-input` click
- "查看项目状态"(直接 `send('现在什么状态?')`
- "开始生成概要设计书"(`send('生成概要设计书')`
- **样式**:胶囊 `--bg-elevated` 底 + 1px `--border-subtle` 边,
hover 出现 `--accent`
- **不显示**`draftProject``null` 时(保持现有欢迎语)
## 五、错误分级样式(4.10
- **位置**:扩展 `addMsg()`,新增可选 `level` 参数:`'info' | 'warn' | 'error'`
- **样式**
- `info``--info` 1px 边 + `rgba(96,165,250,.12)`
- `warn``--warning` 1px 边 + `rgba(251,191,36,.12)`
- `error``--danger` 1px 边 + `rgba(248,113,113,.12)`
- **兼容性**:现有所有 `addMsg('error', ...)` 调用不变(默认仍走 error 样式)
- **新增助手函数**`addInfo(text)` / `addWarn(text)`,内部调用 `addMsg('info' / 'warn', text)`
- **本轮替换点**(仅本轮改造涉及的错误,不全量替换):
- 上传类型不匹配 → `addWarn`(客户端校验,非后端失败)
- 项目名不能为空 → `addWarn`(客户端校验)
- 加载会话失败 → `addError`(现有)
- 网络/服务器错误 → `addError`(现有)
## 六、错误处理
| 场景 | 行为 |
|---|---|
| chat.css / chat.js 加载失败(404/网络) | 页面降级显示,肉眼可见(无样式白板)。可在 README 提示开发者 |
| Google Fonts 已无 link,不会再失败 | N/A |
| localStorage 不可用(隐私模式) | 沿用现有 try/catch,输出到 `console.warn` 而非 `addError` |
| 拖拽 API 不可用(旧浏览器) | `if (!('draggable' in document.createElement('div')) return;` 静默退出 |
| IME 中文输入中按 Enter | 走 `e.isComposing` 检查,**不发送**,保持原换行 |
| 快捷键冲突(浏览器原生 Cmd+K) | 始终 `e.preventDefault()`,已知会拦截 Firefox 切搜索栏 |
## 七、测试策略
由于本轮为**纯前端样式与交互改造**,且不新增可被 pytest 覆盖的逻辑,采用:
1. **手动回归清单**(见 `docs/superpowers/specs/2026-08-30-frontend-beautify-checklist.md`
本 spec 不展开)
2. **截图对比**(实施前用浏览器截 5 张关键页面对照,实施后比对)
3. **关键流程脚本测试**(用户手动跑通):
- 新建会话 → 选项目 → 上传要件 → 发送"生成概要设计书" → 收到下载链接
- 切换项目 → 不匹配徽章出现 → 点击"切到该项目" → 徽章消失
- 拖拽上传 xlsx → 自动选 `requirements` → 上传成功
- 输入多行(Shift+Enter)→ 发送(Enter)→ 高度重置
- hover 助手气泡 → 复制 → 粘贴验证
- Esc 关闭抽屉;Cmd+K 打开/关闭项目切换器
- 启动恢复会话时显示骨架屏 → 加载完成消失
## 八、实施步骤(写作计划阶段会细化)
1. **建文件骨架**chat.html 拆分为 chat.html + chat.css + chat.js,机械搬移不改样式
2. **写 chat.css 令牌**`:root` 全部变量
3. **重写 chat.css 样式**:六大区域按 §二 改造
4. **chat.js 加 8 项交互**:按 §四 / §五 顺序(不依赖外网)
5. **本地手动回归**:见 §七
6. **同步 docs/design.md**:新增"前端 UI 设计规范"一节
7. **更新 _AI_USAGE_LOG.md**:三条记录
## 九、风险与回滚
| 风险 | 缓解 |
|---|---|
| 内联 CSS/JS 拆分影响加载顺序 | `<link>``<head>``<script defer>``<body>` 末尾;chat_state.js / chat_ws.js 保持同步加载 |
| 改 class 破坏后端 | 后端不读 class;仅核对 id 列表(§三-1 |
| 系统字体在 win / linux 上回退差异 | 字体栈已含 PingFang / Microsoft YaHei / Noto Sans CJK 兜底 |
| backdrop-filter 性能 | 限定在抽屉遮罩 + 顶栏,不滥用 |
| localStorage 新增键冲突 | 全部以 `genesis_` 前缀 |
| 拖入文件夹行为 | 拒绝并 toast 提示"请拖入单个文件" |
**回滚**`git checkout` 上一版本,删除 `chat.css` / `chat.js` 即可。
## 十、里程碑外(后续)
- 移动端侧边栏抽屉化(< 768px 断点)
- 快捷键 `Cmd+Shift+O`(项目抽屉)/ `Cmd+Shift+S`(侧边栏折叠)
- 消息气泡"重新生成"按钮(需后端协议扩展)
- 深色 / 浅色主题切换
- 国际化(i18n)文案
- 设计系统独立文件(`static/design-tokens.css`)供未来复用