把每一种 DiceBear 风格和 Letter / Upload 排成同一行源选择;变体网格再覆盖全部风格,点选会改掉刚刚选的 style。
头像设置
Agent 与用户头像共用一个 AvatarPicker。先选三种类型,再进入该类型自己的控件。生成类型的变体只属于当前 DiceBear 风格。
问题与边界
类型(Generated / Letter / Upload)是唯一入口。Generated 先选风格,再从该风格的变体里挑 seed。Letter 的字符和颜色在选择器内编辑。
AvatarConfig 的 kind 名称(dicebear / letter / upload)、持久化字段、Avatar 渲染、Agent/User profile store 和 DiceBear 依赖均不改变。
统一数据流
左对齐分段控件,不是 16 风格瓷砖行
换风格尽量保留当前 seed,否则用显示名
固定 48px 轨道,在右侧 cluster 内换行,不拉伸
AvatarConfigGenerated 写入 kind: dicebear、当前 style 与 seed
布局
设置卡片里每一行都是两段式:名称在左,不伸展;控件在右,按内容收缩。Avatar 与 Preview、Display name 相同——标签在左,选择器以自然瓷砖尺寸靠右成组,不拉满卡片。字母模式的字符和颜色仍在选择器内,不作为卡片上的独立行。
决策
| 方案 | 结论 | 原因 |
|---|---|---|
| 源选择 = 每种风格 + Letter + Upload | 拒绝 | 把类型和风格混在一层,Letter 还要再到卡片外改字符。 |
| 变体网格覆盖全部已发布风格 | 拒绝 | 点选变体会改掉当前 style,和「先选风格」冲突。 |
| 类型 →(风格 + 同风格变体 | 字母字段 | 上传) | 采用 | 三条路径互不交叉;Regenerate 只换当前风格的 seed。 |
变体网格 1fr 拉满卡片 | 拒绝 | 瓷砖保持固定 48px,作为右侧 cluster 换行,不拉伸。 |
| 为 Agent 和用户分别实现 | 拒绝 | 两个设置入口已共用 ProfileEditor 与 AvatarPicker。 |
验收合同
- Agent 和用户设置使用同一选择器。
- 变体全部属于当前风格;Regenerate 不改 style。
- 点击变体后写入
kind: "dicebear"、当前 style 与该 seed。 - Letter 的字符和颜色在选择器内编辑,仍落在 profile 的
initial/color。 - 变体网格是固定 48px 轨道,落在行右侧的 cluster 里,不是
1fr拉满。 - 已保存的
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 控件位置和固定轨道网格。