设置 · Agent 与用户身份

头像设置

Agent 与用户头像共用一个 AvatarPicker。先选三种类型,再进入该类型自己的控件。生成类型的变体只属于当前 DiceBear 风格。

问题与边界

当前缺陷

把每一种 DiceBear 风格和 Letter / Upload 排成同一行源选择;变体网格再覆盖全部风格,点选会改掉刚刚选的 style。

目标

类型(Generated / Letter / Upload)是唯一入口。Generated 先选风格,再从该风格的变体里挑 seed。Letter 的字符和颜色在选择器内编辑。

不变范围

AvatarConfig 的 kind 名称(dicebear / letter / upload)、持久化字段、Avatar 渲染、Agent/User profile store 和 DiceBear 依赖均不改变。

统一数据流

1 · TYPE生成 / 字母 / 上传

左对齐分段控件,不是 16 风格瓷砖行

2 · STYLE当前 DiceBear 风格

换风格尽量保留当前 seed,否则用显示名

3 · VARIANTS只含当前风格

固定 48px 轨道,在右侧 cluster 内换行,不拉伸

4 · SAVEAvatarConfig

Generated 写入 kind: dicebear、当前 style 与 seed

布局

设置卡片里每一行都是两段式:名称在左,不伸展;控件在右,按内容收缩。Avatar 与 Preview、Display name 相同——标签在左,选择器以自然瓷砖尺寸靠右成组,不拉满卡片。字母模式的字符和颜色仍在选择器内,不作为卡片上的独立行。

决策

方案结论原因
源选择 = 每种风格 + Letter + Upload拒绝把类型和风格混在一层,Letter 还要再到卡片外改字符。
变体网格覆盖全部已发布风格拒绝点选变体会改掉当前 style,和「先选风格」冲突。
类型 →(风格 + 同风格变体 | 字母字段 | 上传)采用三条路径互不交叉;Regenerate 只换当前风格的 seed。
变体网格 1fr 拉满卡片拒绝瓷砖保持固定 48px,作为右侧 cluster 换行,不拉伸。
为 Agent 和用户分别实现拒绝两个设置入口已共用 ProfileEditor 与 AvatarPicker。

验收合同

  1. Agent 和用户设置使用同一选择器。
  2. 变体全部属于当前风格;Regenerate 不改 style。
  3. 点击变体后写入 kind: "dicebear"、当前 style 与该 seed。
  4. Letter 的字符和颜色在选择器内编辑,仍落在 profile 的 initial / color。
  5. 变体网格是固定 48px 轨道,落在行右侧的 cluster 里,不是 1fr 拉满。
  6. 已保存的 AvatarConfig 继续按原 kind / style / seed 显示。

实现状态

已实现。 AvatarPicker 以三种类型为入口;Generated 用 AVATAR_STYLES 选风格,variants.ts 只为当前风格生成 seed。Letter 控件从 ProfileEditor 迁入选择器。设置卡片把 Avatar 做成与其它行相同的两段式:左侧标签,右侧自然尺寸选择器。apps/web/tests/settings/avatar-randomization.test.mjs 与 theme-appearance.test.mjs 校验同风格变体、Regenerate 不改风格、Letter 控件位置和固定轨道网格。