# 侧边栏项目树 + 消息区对齐 + 空态清理 — 实施计划 配套需求:用户 7 条前端优化(侧边栏信息架构重构 + 气泡对齐 + 空态清理) 目标文件:`src/genesis/server/static/{chat.html,chat.css,chat.js,chat_state.js}` 前端无框架、无构建;后端零改动。 ## 已确认决策 - D1 气泡对齐到**文本输入框**:左 16px;右留发送按钮宽度(40+10=50px);**删除 assistant 头像点**。 - D2 点击侧边栏项目名 = **选中该项目(设为当前项目)+ 展开/收起**。 - D3 抽屉左侧项目列表**保留**(仅删除「+新建项目」项)。 - D4 侧边栏折叠态**隐藏「项目/其他会话」两段**。 - D5 req2 的「提示语及提示词」= 空态主标题 + 建议卡,发送后一并清除。 ## 目标侧边栏 ``` [G Genesis 概要设计书做成Agent] [+ 新会话] 项目 + ← 仅"+",hover「追加项目」 ▾ 股票系统 ⚙ ← 展开显示其会话;⚙ hover「配置」 · 需求A · 需求B ▾ 人事系统 ⚙ · 需求C 其他会话 · 需求D(project="") ``` --- ## Task 1:chat_state.js 分组纯函数 + node 测试 - 新增 `groupSessionsByProject(sessions)` → `{ byProject: { "": [s...] }, other: [s...] }`(`project` 为空者入 `other`)。 - `tests/test_chat_state.js` 增加断言:空项目归 other;两项目分别归组。 - 运行 `node --test tests/test_chat_state.js`。 ## Task 2:chat.html 侧边栏结构 + 移除底部新建项目按钮 - 删除 `

会话历史

` 与 `
`。 - 新增 `#sec-projects`(`side-head`:标题「项目」+ `#side-add-project`「+」)与 `#project-tree`。 - 新增 `#sec-others`(标题「其他会话」)与 `#other-session-list`。 - 删除 `#tb-newproject` 按钮。 ## Task 3:chat.css 侧边栏树 + 消息区对齐 + 折叠态 - 新增 `.side-section/.side-head/.side-title/.side-add/.proj-group/.proj-row/.proj-caret/.proj-name/.proj-config/.proj-sessions` 样式;嵌套 `.sess` 缩进。 - 折叠态:`#sidebar.collapsed .side-section { display: none; }`。 - 消息区:`#chat { max-width: none; margin: 0; padding: var(--sp-6) var(--sp-4); }`;`.msg { padding-right: 50px; }`;`.msg .bubble { max-width: 100%; }`;**删除 `.msg.assistant::before`**。 ## Task 4:chat.js 分组渲染 + 绑定调整 + 空态清理 - `refreshSessions()` 改拉全量并按 `groupSessionsByProject` 分组 → `renderProjectTree()` + `renderOtherSessions()`。 - 抽 `renderSessItem(s)`(复用现有会话行逻辑)。 - `renderProjectTree()`:项目行点击选中+展开/收起(`collapsedProjects` Set,localStorage 持久化);`⚙` → `openProjectDrawer(name)`。 - `#side-add-project` → `openProjectDrawer(null)`(带脏守卫)。 - `renderProjectSwitcher()`:删除 `ps-manage`「项目管理(打开抽屉)」项及绑定。 - `renderDrawerList()`:删除 `di-create`「+新建项目」项。 - `send()`:发消息前 `chatEl.querySelector('.empty-state')?.remove()`。 - 删除 `#tb-newproject` 绑定。 ## Task 5:验证与提交 - `node --check`、`node --test`、`python -m pytest`(≥99%)。 - 手动核对:气泡对齐、空态清理、项目树/⚙/+、底部无新建项目、未选项目时新会话入「其他会话」。 - 更新 `_AI_USAGE_LOG.md` 并提交。 ## 风险 - `#sidebar.collapsed` 下 `.sess` 旧样式成为死代码(无害)。 - 分组渲染依赖 `GET /api/sessions` 全量返回(已支持 `project` 可选)。 - 若某会话的 project 已从项目列表删除,仍会以其 project 名建组渲染(保证不丢会话)。 --- ## 评审结论与计划调整(plan-eng-review) ### 采纳的修正 - **F1(正确性,req 5)显式「不选择项目」持久化** - `chat_state.js`:`autoBindProject(draftProject, projects, explicitNone = false)` —— `explicitNone` 为真时原样返回 `draftProject`(不移除用户选择);否则维持原自动绑定逻辑。新增 node 断言。 - `chat.js`:读写 `localStorage['genesis_project_explicit_none']`;`loadProjects()` 传入该标记;选任意项目 → 清除标记;选「不选择项目」→ 设标记。保证「无项目选择 → 新会话进『其他会话』」成立。 - **F2(UX)打开会话所在分组高亮** - `renderProjectTree()`:组内任一会话 `session_id === sid` 时给 `.proj-group` 加 `.has-active`(与 `.current` = 选中项目 区分)。 - **F3(DRY)对齐余量单一来源** - 新增 CSS 变量:`--send-size: 40px`、`--composer-gap: 10px`、`--composer-gutter: calc(var(--send-size) + var(--composer-gap))`。 - `#send { width/height: var(--send-size) }`、`#composer-inner { gap: var(--composer-gap) }`、`.msg { padding-right: var(--composer-gutter) }`。 - **F6(清理)删除折叠态死样式** - 移除 `#sidebar.collapsed .sess*` 与 `#sidebar h2` 相关规则(折叠态整段隐藏、h2 已不存在)。 ### 接受现状(标注,不改) - F4 project 已删的会话以原 project 名成组渲染(不丢会话)。 - F5 DOM 渲染层无自动化测试;靠纯函数 `groupSessionsByProject` 断言 + `node --check` + 手动。 - F7 项目行沿用 `div.onclick`(非键盘可达),与会话行一致。 - F8 `refreshSessions` 每次拉全量;规模增大时再考虑限制。 ### 受影响任务更新 - Task 1 追加:`autoBindProject` 第三参数 + `groupSessionsByProject` + node 断言。 - Task 3 追加:`--send-size/--composer-gap/--composer-gutter` 变量;删除折叠死样式。 - Task 4 追加:`explicit_none` 读写与联动;`.has-active` 渲染。 ## GSTACK REVIEW REPORT - STATUS: DONE_WITH_CONCERNS - REVIEWED: docs/superpowers/plans/2026-09-12-sidebar-project-tree.md - FINDINGS: F1 正确性(已采纳修复) / F2 UX(已采纳) / F3 DRY(已采纳) / F6 清理(已采纳);F4/F5/F7/F8 接受 - KEY DECISION: 显式「不选择项目」持久化修复(否则 req 5 不成立) - CONCERN: 渲染层无自动化测试(无 DOM harness);折叠态样式清理后需回归核对 - NEXT: 待用户批准后实施