Files
Agent-System-Documents/docs/proposals/2026-09-30-ui-redesign-and-auth.md
weichen 8e2657c824 Record the chat-order and artifact-files fix round in the proposal
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.
2026-09-30 15:22:33 +00:00

20 KiB
Raw Permalink Blame History

界面重构与多用户认证 —— 设计提案(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):

  1. 创建入口分裂且错位:新建项目在左上角 nav-header 的 ✚,新会话在左下角 nav-footer 的「+ 新会话」。两个高频动作一个在最上一个在最下,且都是小图标/文字按钮,无主次。
  2. 项目树导航繁琐:ProjectNav 只渲染 currentProjectId 的会话(open && p.id === currentProjectId),切项目才能看会话;项目折叠态与会话列表耦合,浏览成本高。
  3. 无用户与租户界面:系统定位是多用户、多租户(D17 已落 users/api_keys 表),但界面只有一个密钥输入登录门(App.tsx LoginGate),没有账户、身份、成员、租户切换的任何表面。
  4. 视觉密度与专业度不足: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):

  1. 消息顺序保真(新设计约定:渲染顺序 = 落库顺序)。后端 llm.go 按时间交错落库 parts (text → tool_call → tool_result → artifact_ref → 下一轮 text…),前端曾按 p.process 布尔二分重排(过程全前置),工具调用脱离真实位置。修复:groupParts 只把"连续的"过程 部件就地聚成折叠块,内容部件原位保留;live 流式改为 LiveState.parts 按事件到达序维护 (artifact.created 即时上 chip)。该约定写入 UI 层默认要求:任何 parts 分组展示不得改变原始顺序。
  2. 下载通道。后端 artifactContent 始终回 Content-Disposition(默认 inline, ?download=1 → attachment,RFC 5987 filename* 支持中文名)+ Office/PDF 扩展名 MIME 表; 前端 chip / 工件面板行 / DocViewer 三处都有下载动作。
  3. 工件面板 = 工作区文件管理(浏览/显示/下载/回看/删除):搜索 + kind 过滤 + 上传 + 行内 预览/下载/删除(二次确认);DocViewer 重构为单层 OverlayHost(pdf 内嵌 iframe、 csv/json 等宽、二进制信息卡 + 下载 CTA);收件箱结果卡经 docBridge 可直接打开工件。
  4. 设置界面级联修复。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 工具/目录)。