Why: the delivery log for the 2026-09-30 afternoon fixes (part-order fidelity, download channel, artifact file management, settings cascade) belongs with the redesign proposal it extends, including the two durable conventions it produced. Evidence: adds section 9 with root causes, the render-order-equals- storage-order rule, the compound-selector lesson for @ui shells on host generic classes, and the full verification record (vitest/go suites plus the 12/12 headless run). Impact: future UI work inherits both conventions without rediscovering the failure modes. Refs: task feature/chat-order-artifact-files.
20 KiB
界面重构与多用户认证 —— 设计提案(2026-09-30)
状态:accepted(方向 + R2 范围)——2026-09-30 用户裁定采用方案 A 任务中心;同日裁定认证阶段 2 改为管理员发放制:无自助注册,账号仅管理员(或 CLI 引导)创建并设定初始密码,创建后与重置后的首次登录均强制改密(§5.1 邮箱+自助注册方案作废)。R1 已实施:Dev 仓 feature/task-sidebar-r1(Core +pinned/archived、Backend 迁移 v9 与任务列表 API、Frontend TaskSidebar)。R2 已实施:Dev 仓 feature/auth-admin-accounts(Backend 迁移 v10 auth_accounts:users 密码/must_change/is_admin + sessions 表;/api/auth/login|logout|password、/api/admin/users*;argon2id;登录失败退避;中间件接受 Bearer 密钥或会话 cookie 且豁免静态与登录入口;CLI user create|reset-password|list;Frontend 登录页双 tab、强制改密门、用户管理弹层。验证:store/e2e 测试 + 无头全流程走查)。R4 已实施:feature/visual-refresh-r4(令牌 v2 + 线性图标集 + 全组件重造型)。UI 能力平台已实施(2026-09-30 用户裁定:头像/卡片/设置页为系统基本能力,插件直接复用):feature/ui-capability-platform——Core 增 avatar 词表(UINode/UIAvatarSpec 含叠放组);@ui 入 Icon/Avatar(名字派色/语义色/状态点/叠放)/primitives(Btn/Input/Chip/EmptyState/SettingRow)/SettingsShell(侧导航设置外壳);UIRenderer 补齐 D28 七词表(progress/compare/tree/table/steps/tabs/timeline——此前仅类型声明、卡片内被静默丢弃);前端消费(消息头像列、设置中心上外壳、管理弹层头像);react 类型单副本钉扎(tsconfig paths,修 make frontend 双 @types/react 名义冲突)。owner:weichen。
前置:界面交互底座(D25–D33)、双档存储、多租户与规模化(D17–D24)。
配套 demo:ui-redesign-demos/(index.html 为入口,本地浏览器直接打开)。
1. 背景与问题
用户对现有界面的判定(2026-09-30):界面与交互设计不够好。具体事实(对照 v0.1.0 截图与 modules/frontend/src/components/ProjectNav.tsx):
- 创建入口分裂且错位:新建项目在左上角
nav-header的 ✚,新会话在左下角nav-footer的「+ 新会话」。两个高频动作一个在最上一个在最下,且都是小图标/文字按钮,无主次。 - 项目树导航繁琐:
ProjectNav只渲染currentProjectId的会话(open && p.id === currentProjectId),切项目才能看会话;项目折叠态与会话列表耦合,浏览成本高。 - 无用户与租户界面:系统定位是多用户、多租户(D17 已落
users/api_keys表),但界面只有一个密钥输入登录门(App.tsx LoginGate),没有账户、身份、成员、租户切换的任何表面。 - 视觉密度与专业度不足:emoji 图标(✚ ☰ ⚙ 📦 🤖)、无统一设计令牌,观感偏离"办公工作台"。
2. ZCode 桌面端研究结论
研究对象:github.com/zai-org/ZCode(Apache-2.0,Electron 桌面 + Web + CLI/TUI 三形态)。证据为仓库源码与 DESIGN.md(以下文件路径均在该仓)。
2.1 布局范式(packages/ui/src/app-shell/WorkspaceShellLayout.tsx)
- 四区:左侧栏(任务列表)+ 中栏会话 + 底部终端 + 右侧 Side Pane(浏览器/子代理会话/工作流运行/工件,Tab 化)。
- 尺寸用 CSS 变量驱动(
--workspace-sidebar-width等),拖拽中只写变量不进 React state;宽度持久化 localStorage。 - 自动收起策略:窗口变窄(去抖 300ms)时若会话列 < 480px 收 Side Pane、< 360px 收侧栏;只在窗口 resize 时触发,避免把用户手动打开的面板又关回去。
- 侧栏隐藏时由
DesktopTopOverlay浮出 New Task,保证创建入口永不丢失。
2.2 侧栏组织(packages/ui/src/WorkspaceSidebar.tsx 等)
- 顶部动作区:第一颗按钮就是 New Task(带快捷键),随后是 Command Center 搜索(⌘K)、Automations、插件商店。创建入口在视觉与逻辑的第一位,不在角落。
- 任务列表四种组织模式:
grouped(自定义分组)/workspace(按项目)/timeline(按时间线)/archived,pill 切换 + 过滤菜单(排序、归档)。项目是任务的组织维度,不是必须展开导航的树。 - 置顶区全局置顶(
WorkspacePinnedTasksSection:归档态也保持置顶区可见)。 - 任务行支持拖拽排序、分组展开态持久化、右键菜单(
TaskListItemContextMenu)。 - Footer = 账户区:主题/语言、设置、用量与套餐徽章、登录/登出与用户信息(
WorkspaceSidebarFooter)。 - 收起后变 52px 图标轨道(
WorkspaceSidebarCollapsedRail)。
2.3 会话头与中栏(WorkspaceHeaderSections/)
- 中栏会话头:上下文路径(项目/目录)+ 最近活动时间 + 动作区;无全局顶栏(与我方 D26 一致)。
2.4 设计系统(DESIGN.md)
- 定位:"calm, dense, operational rather than decorative";桌面优先。
- 硬规则:统一
text-ui-*字号阶(默认 14px);语义色只用于真实语义态;所有等待确认类交互(提问/权限/计划批准)统一绿色确认;圆角按嵌套层级递减(外 12 → 内 10/8/6);4px 间距基;高度克制(先靠背景对比与描边,不堆阴影)。 - 键盘优先:可聚焦、可见焦点、快捷键全程可触达(新建任务、⌘K、侧栏拖拽支持方向键步进 16px)。
2.5 认证表面
login/LoginApiKeyForm.tsx:API 密钥表单;onboarding/:首跑引导;OAuth 相关在root/与packages/web/src/auth/。
2.6 对本系统的可迁移结论
| ZCode 实践 | 迁移判据 |
|---|---|
| New Task 置顶 + 快捷键 + 隐藏侧栏时浮出 | 直接采纳:解决"创建入口分裂"这一首要痛点 |
| 项目/时间线/分组/归档是列表的视图模式 | 直接采纳:项目从"树"降为"组织维度",浏览免切项目 |
| 侧栏 Footer 承载账户/租户/用量 | 直接采纳:多租户身份常驻但低噪声 |
| 底部终端 + Side Pane Tab | 远期:我们的代理/工件已有 ⌘1–9 弹层(D25/R4),不重复建面 |
| CSS 变量驱动分隔条、自动收起策略 | 实现层采纳(现有 divider 逻辑升级) |
| 绿色统一确认 | 与 InlineConfirmCard 语义合并考虑 |
3. 设计原则(继承 + 新增)
继承界面五原则(D25–D33 提案 §1.2:中栏做空、消息是事件流卡片是状态对象、弹层是加速器、待办不沉没、确认留痕)与 D26(无全局顶栏)。新增:
- P6 单一主创建入口:创建动作只有一个主按钮(新建任务=会话),其余创建(项目等)收进它的次级菜单或组织区的显式 +。位置固定在侧栏顶部第一位,快捷键 ⌘N。
- P7 身份常驻:用户(头像/名/租户/角色)常驻侧栏 Footer;一切跨租户资源不可见性由服务端保证(D18 不变)。
- P8 组织即视图:项目/时间线/归档是任务列表的切换视图,不改变数据模型(
conversations.project_id不变),切换是本端偏好。
4. 三个方案
demo:ui-redesign-demos/demo-a-task-centric.html / demo-b-activity-rail.html / demo-c-project-portal.html(含明暗主题切换;方案 A 含完整交互演示)。
方案 A:任务中心(ZCode 同构)——推荐
┌────────────────────┬──────────────────────────────┬─────────────────┐
│ Agent-System │ [项目徽章] 会话标题 ⋯ │ 收件箱 2 │
│ [+ 新建任务 ⌘N ▾] │ 状态 · 模型 · 更新于 3 分钟前 │ [待办][全部] │
│ [🔍 搜索 ⌘K] ├──────────────────────────────┤ ⚠ 发布前确认 │
│ 组织 [项目|时间线] ⋯│ │ ▷ 记忆库索引 68% │
│ 📌 置顶 │ 消息流 + 内联确认卡 │ ✓ 周报已生成 │
│ ▾ 研发体系文档 (4) │ │ − 已确认(折叠) │
│ • 任务… ├──────────────────────────────┤─────────────────│
│ ▸ 真实模型测试 (2) │ [附件] 输入框… [发送] │ 代理 2 · 工件 5 │
│ 👤 weichen · 个人 │ │ │
│ 租户 ⚙ 🌓 │ │ │
└────────────────────┴──────────────────────────────┴─────────────────┘
- 新建任务(主按钮,split menu):默认在"当前项目上下文"建会话;▾ 展开新建项目/新建任务于指定项目/导入。解决入口分裂:一个位置、有主次、带快捷键。
- 组织切换:按项目(分组可折叠,组头 hover 出「在此项目新建」)/ 按时间线(今天/昨天/本周)。置顶区恒在顶部;过滤菜单含归档视图与排序。
- 任务行:状态点(运行中/待我确认/空闲/已完成)+ 相对时间 + 所属项目徽章(时间线模式)。
- Footer 账户区:用户 chip(点头像弹账户菜单:信息/切换租户/API 密钥/退出)+ 设置 + 主题。
- 中栏头沿用 D26:项目徽章 + 会话标题 + 状态;右栏纯收件箱(D25 不变),底部快捷链接进 ⌘1–9 弹层。
- 优点:创建/浏览/身份三大痛点一次解决;与 ZCode(以及 Linear/ChatGPT 侧栏)这类已验证范式同构;纯前端改造,不动数据模型。
- 代价:需重写
ProjectNav(约一个组件的量)+ 新增账户菜单;左栏语义从"项目树"变为"任务列表",用户有一周适应期。
方案 B:活动栏 + 上下文列表(VS Code 式)
┌──┬─────────────────┬──────────────────────┬─────────────┐
│▣ │ 研发体系文档 ▾ │ 会话(同 A 中栏) │ 收件箱 │
│+│ [+ 新会话] │ │ │
│🔍│ ── 会话列表 │ │ │
│📭│ • … │ │ │
│🤖│ │ │ │
│📦│ │ │ │
│⚙ │ │ │ │
│👤│ │ │ │
└──┴─────────────────┴──────────────────────┴─────────────┘
- 最左 48px 图标轨切换领域(任务/收件箱/代理/工件/设置/账户),第二栏随活动渲染列表。
- 优点:导航可无限扩展(插件域友好);账户/设置有恒定图标位。
- 代价:创建仍是二级动作(先选项目再新建),痛点 1 只部分解决;多一列常驻(1280px 屏占 6%);与 D25"代理/工件走弹层"重复建面。判定:适合域数量 ≥ 6 时再启用。
方案 C:项目门户(Notion/Linear 式)
┌──────────────┬────────────────────────────────┐
│ [+ 新项目] │ 研发体系文档 12 会话 · 3 运行│
│ ▸ 文档智能体 │ ┌─────┐┌─────┐┐─────┐ │
│ ▸ 记忆库建设 │ │会话卡││会话卡││ + 新│ │
│ ▸ 模型测试 │ └─────┘└─────┘└─────┘ │
│ 👤 weichen │ 点会话卡 → 进入对话视图 │
└──────────────┴────────────────────────────────┘
- 项目是页面:首页是会话卡片网格 + 统计 + 项目内新建(入口在上下文中,最贴任务)。
- 优点:项目级总览最好;新建会话离上下文最近。
- 代价:对话-first 被破坏(打开高频会话多一跳);卡片网格信息密度低;与"中栏做空"张力最大。判定:适合项目数少(<10)且以交付物为中心的用法。
对比与推荐
| 维度 | A 任务中心 | B 活动栏 | C 项目门户 |
|---|---|---|---|
| 解决创建入口分裂 | 完全 | 部分 | 部分(上下文内) |
| 跨项目浏览 | 优(视图切换) | 中 | 中 |
| 身份/租户常驻 | Footer 账户区 | 图标位 | Footer |
| 与既有决策兼容(D25/D26/D31) | 完全 | 需调 D25 | 张力大 |
| 改造量 | 中(左栏+账户菜单) | 大 | 大(含视图路由) |
| 扩展性(未来插件域) | 中 | 优 | 中 |
推荐 A,并以 A 的 Footer 账户区 + 账户菜单吸收 B 的"账户恒定入口"优点。B 的活动栏留作域数量增长后的演进项(远期不否决)。
5. 多用户与多租户设计(认证阶段 2)
demo:ui-redesign-demos/demo-auth.html(登录/注册/账户与租户三屏)。
5.1 身份模型演进(延续 D17 编号段,新决策从 D34 起,本文只提案)
- 阶段 1(现状,保留):API key(
ask_)即身份,user 即租户 owner,嵌入式档默认关闭认证。 - 阶段 2(本设计):
users增email、password_hash(argon2id)、status;organizations(租户)与memberships(user × org × role: owner/admin/member)分离——user 不再天然等于租户,一个用户可属多租户。- 登录:邮箱 + 密码 → 服务端会话(
sessions表,HttpOnly+SameSite=Lax cookie,滑动过期);API key 保留为程序化访问凭证,挂到 user,权限同其成员角色。 - 阶段 1 过换路径:每个既有 owner user 自动建同名个人租户并设 owner;API key 归属不变。
- SSE 认证升级:会话 cookie 使 EventSource 同源自动携带,移除
?key=URL 传钥(消除 D17 §3 记录的日志泄露权衡);API key 客户端仍可用 header。
5.2 界面表面
| 表面 | 内容 |
|---|---|
| 登录页 | 品牌 + 双 tab(访问密钥 / 邮箱密码)+ 注册入口;错误内联;宽屏右侧产品信息面板 |
| 注册页 | 姓名/邮箱/密码/组织名(即建租户,成为 owner) |
| 账户菜单(侧栏 Footer) | 头像、显示名、邮箱;当前租户 + 角色徽章;切换租户(多租户成员);API 密钥管理;偏好(主题/语言);退出 |
| 成员管理(设置内) | 成员表(邮箱/角色/状态)、邀请(邮件链接或邀请码)、角色变更;仅 owner/admin 可见 |
| 项目可见性(远期) | 项目级成员范围(默认租户内全员);跨租户访问仍一律 NotFound(D18) |
5.3 服务端边界(不变项)
- 权威在执行端(baseline 硬边界 4):角色检查在服务端每路由执行,UI 只做可见性降级。
- 密码哈希 argon2id、会话 cookie HttpOnly;不引入第三方 OAuth(远期)。
- 限流沿用 D24 租户固定窗口;登录路由加 IP 级失败退避。
6. 落地路线(裁定方向后另立实施提案,此处仅示意切分)
| 期 | 内容 | 验收要点 |
|---|---|---|
| R1 | 左栏重构(方案 A 任务列表 + 新建任务 + 组织视图 + 置顶/归档)+ 设计令牌接入(modules/ui tokens.css) | 创建入口唯一性走查;对话完备性剧本不回退 |
| R2 | 登录页 + 阶段 2 认证(orgs/memberships/sessions)+ 侧栏 Footer 账户区 + 迁移 | auth_e2e 扩容;?key= 移除后 SSE 全链路 |
| R3 | 成员管理与邀请、租户切换、API 密钥管理页 | 角色负例(member 越权 NotFound) |
| R4 | 视觉系统全面收敛(字号阶/语义色/圆角嵌套/绿色统一确认) | 明暗主题截图对比 + 密度走查 |
7. Demo 清单
| 文件 | 内容 | 交互 |
|---|---|---|
index.html |
方案总览与链接、对比表 | — |
demo-a-task-centric.html |
方案 A 全应用 | 组织切换、新建菜单、账户菜单、收起轨道、主题、收件箱筛选 |
demo-b-activity-rail.html |
方案 B | 活动切换、二级列表 |
demo-c-project-portal.html |
方案 C | 项目切换、进入会话 |
demo-auth.html |
登录/注册/账户与租户 | 双 tab 登录、注册表单校验、账户菜单、成员管理 |
demo 为静态设计稿(无后端),数据为演示数据;视觉规格(色板/字号/圆角)即 R4 设计令牌的目标值来源。
8. 边界与不做
- 本提案不含实施;实施前须用户裁定方案(默认 A)与认证阶段 2 范围。
- 不做计费/套餐(Footer 用量区留位即可)、不做 SSO/OAuth、不做项目级 ACL(远期,见 5.2)。
- 中栏与右栏结构不在本次重构范围(D25/D26 已定且未被推翻);底部终端面板不做(我们非终端工作台)。
9. 交付补记(2026-09-30 下午:消息顺序保真 + 工件文件管理)
用户验收 UI 能力平台后反馈四项,本轮修复(任务 feature/chat-order-artifact-files):
- 消息顺序保真(新设计约定:渲染顺序 = 落库顺序)。后端 llm.go 按时间交错落库 parts
(text → tool_call → tool_result → artifact_ref → 下一轮 text…),前端曾按
p.process布尔二分重排(过程全前置),工具调用脱离真实位置。修复:groupParts只把"连续的"过程 部件就地聚成折叠块,内容部件原位保留;live 流式改为LiveState.parts按事件到达序维护 (artifact.created 即时上 chip)。该约定写入 UI 层默认要求:任何 parts 分组展示不得改变原始顺序。 - 下载通道。后端
artifactContent始终回Content-Disposition(默认 inline,?download=1→ attachment,RFC 5987 filename* 支持中文名)+ Office/PDF 扩展名 MIME 表; 前端 chip / 工件面板行 / DocViewer 三处都有下载动作。 - 工件面板 = 工作区文件管理(浏览/显示/下载/回看/删除):搜索 + kind 过滤 + 上传 + 行内 预览/下载/删除(二次确认);DocViewer 重构为单层 OverlayHost(pdf 内嵌 iframe、 csv/json 等宽、二进制信息卡 + 下载 CTA);收件箱结果卡经 docBridge 可直接打开工件。
- 设置界面级联修复。SettingsShell 根元素同挂宿主
.modal,被后加载的宿主样式以同 特异性覆盖(column/860px)压坏左右布局——改为.modal.ui-settings-shell复合选择器 (0,2,0)免疫加载顺序;设置行统一 @ui SettingRow,保存按钮进固定 actions 底栏。 教训入库:@ui 组件挂宿主通用类时,外壳样式必须用复合选择器高于宿主单类。
验证:ui 37 + frontend 20 vitest 全绿;backend go test ./... 全绿(含 disposition/MIME 新测); Playwright 头链 12/12(mock 后端 + SQLite 直插交错 parts 探针消息): DOM 序 [text→process-collapse→text→artifact-chip]、设置外壳 computed flex-direction=row 且 侧栏/主区并排、?download=1 → attachment+RFC5987 文件名、面板工具栏/过滤/上传/行内下载。 docx 在线预览为非目标(下载为主);工作区文件即 artifacts(不新增 fs 工具/目录)。