BUG-698 复发自 BUG-554。四个分区确实都映射到 .settings-modal,CSS 里 width 与 height 也都写了——但 .settings-modal 的 height/max-height 只用 dvh,没有 vh 回退。不支持 dvh 时整条声明作废,height 退回 auto,高度改由 内容决定,正是「切分区就立马缩小」。全文件 18 处 dvh 只有 sidebar-provider 那一处配了回退,本仓自己知道这个写法。 BUG-554 的防复发「设置弹窗必须同时声明 width 与 height」拦不住这次:它 检查声明存在性,不检查声明是否生效;守它的断言也只是一句 CSS 文本匹配。 本单把防线换成「height 用 dvh 必须先写 vh 回退」的全文件契约测试。 首要嫌疑不等于已证实——本会话没有浏览器。任务 1.1 要求执行方先量四个分区 的 getBoundingClientRect 并确认 height 是否被划掉,量不出来就转 investigating 交回产品,不得盲改。 另去掉设置分区菜单的左侧强调条(移动端下边框同理)。删之前必须先拆开选中态 与悬停态——它们共用同一条声明,色条是当前唯一的区分。左侧会话列表的 2px 色条 本轮保留,口径暂时不一致已授权。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
14 KiB
TASK · 设置弹窗尺寸随分区变化(BUG-698,复发自 BUG-554)+ 分区菜单去掉强调条
- 日期:2026-09-15
- 基线 commit:
origin/staging@6c748d86 - 执行分支:
codex/settings-dialog-size-and-nav-20260915 - 工作树:
.worktrees/settings-dialog-size-and-nav-20260915 - 线上状态:staging 当前部署
8d56d0ab(早于本基线两轮实现提交,但晚于 BUG-554 的修复dc6598d7)。用户现象是在已含 BUG-554 修复的版本上复现的。
1. 产品诉求(原话)
设置面板的弹窗大小不一样,选别的就立马缩小了。而且菜单项不要加粗左边框的强调条。
两件事:任务 1 是缺陷(BUG-698),任务 2 是产品决策。
2. 事故实证
行号会漂移,定位以符号名为准。核对于 origin/staging@6c748d86。
2.1 这是 BUG-554 的复发
docs/BUG_HISTORY.md → BUG-554 | 设置弹窗随分区跳变,星盘资料铺开整张表单,账户与点数离开首页(resolved,修复版本 dc6598d7):
- 当时的用户现象第一句就是「三个设置分区弹窗尺寸不同」。
- 当时的根因:
accountDialogClasses给每个分区不同宽度,高度随内容撑开。 - 当时的修复:四个分区共用
.settings-modal固定尺寸。 - 当时的防复发:「设置弹窗必须同时声明 width 与 height」。
现在 frontend/src/lib/home-types.ts → accountDialogClasses 的四个分区确实都是 "settings-modal",globals.css 里也确实同时声明了 width 与 height:
.settings-modal { width: min(100vw - 32px, 880px); height: min(84dvh, 640px); max-height: min(84dvh, 640px); overflow: hidden; display: grid; grid-template-rows: auto 1fr; }
防复发措施仍在,现象仍复现。 说明那条防复发写错了层次——它防的是「有没有写 height」,防不住「写了但没生效」。
守着它的测试也只是一句文本匹配,frontend/tests/account-dialog-overlay.test.ts:
assert.match(styles, /\.settings-modal \{[^}]*width:[^}]*height:/);
它读的是 CSS 源文本,既不渲染也不量盒子,所以声明只要存在就绿。
2.2 首要嫌疑:dvh 没有 vh 回退,不支持时整条 height 被丢弃
.settings-modal 的 height 与 max-height 都只写了 dvh,没有 vh 回退。浏览器遇到不认识的单位会整条声明作废,于是:
height: min(84dvh, 640px)作废 →height回到auto→ 高度由内容决定max-height: min(84dvh, 640px)作废 → 落回.account-modal的max-height: min(84dvh, 760px)→ 同样作废 → 没有任何高度约束
结果正好是用户描述的:内容多的分区高,内容少的分区矮,切分区就立刻变大小。而 width 用的是 100vw,不受影响——所以看上去是「高度在跳,宽度没动」。
这个仓库自己知道要写回退,globals.css 里就有一处标准写法:
.group\/sidebar-provider[data-viewport] { height: 100vh; height: 100dvh; min-height: 0; overflow: hidden; }
全文件 18 处 dvh 声明,只有这一处配了 vh 回退。.settings-modal 是其中后果最严重的一处,因为它是唯一一个「固定高度就是功能本身」的面板。
⚠️ 这是首要嫌疑,不是已证实的根因。 会话环境没有浏览器,无法实测。执行方必须先复现并确认,见任务 1.1。若实测发现 dvh 正常生效、尺寸仍在变,按 1.2 继续排查,不得直接把回退当作修复交付。
2.3 分区菜单的强调条
frontend/src/app/globals.css:
.settings-dialog-nav-item:hover,
.settings-dialog-nav-item[aria-current="page"] { background: var(--color-canvas-muted); color: var(--color-ink); }
.settings-dialog-nav-item[aria-current="page"] { box-shadow: inset 2px 0 0 var(--color-action); }
移动端(@media (max-width: 767px),导航变成顶部四栏)是同一条的下边框版本:
.settings-dialog-nav-item[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--color-action); }
产品要去掉的就是这两条 box-shadow。
去掉之后有一个必须同时解决的问题:选中态与悬停态共用同一条规则(上面第一条),强调条是当前唯一区分它们的东西。直接删 box-shadow 会让「鼠标划过某项」和「当前就在某项」长得一模一样。任务 2 必须把这两态拆开。
3. 决策记录(产品已授权)
- 去掉设置分区菜单的左侧强调条(移动端的下边框同理)。选中态改用面(背景)与墨色等级表达,不用色条。
- 这条只针对设置弹窗的分区菜单。左侧会话列表当前会话的 2px 色条(
--sidebar-ring,见frontend/DESIGN.md「Where the action color appears」第 2 条)本轮不动。改完两处观感会不一致,产品知情并接受;若之后也要去掉,另开一轮。 - 不要用加粗(
font-weight)来区分选中态。 产品原话就是不要"加粗"的强调;frontend/DESIGN.md也写明导航层级来自墨色等级而不是色相。 - 尺寸问题按缺陷处理,占 BUG-698,并在记录里写明复发自 BUG-554、旧防复发为何没拦住。
4. 硬红线
./node_modules/.bin/tsc --noEmit0 错;npm run lint0 error。- 测试总数不得低于开工时
origin/staging的实测;改既有断言写「原值 / 新值 / 原因」三栏。 - BUG-554 的其余防复发条款继续有效,不得顺手破坏:星盘资料默认视图不得无条件渲染
<form;/保持○ Static,不得useSearchParams;收银台仍window.open;硬跳转清单不得变长。 - 不得改
accountDialogClasses让四个分区重新用不同的类——那正是 BUG-554 的原始根因。 - 不得动 admin(antd + Refine)。
- 不得顺手升级依赖。
- 改 UI 的同一提交内更新
frontend/DESIGN.md。
5. 任务分解
任务 1 · BUG-698:设置弹窗尺寸随分区变化
1.1 先复现并确认机制(不得跳过)
- 起本地 dev,登录,打开设置弹窗,在四个分区之间来回切:个人资料 → 星盘资料 → 账户与点数 → 通用设置。
- 用 DevTools 量
section.account-modal.settings-modal的getBoundingClientRect(),四个分区各记一次。 - 在 Computed 面板看
height与max-height的计算值,以及 Styles 面板里.settings-modal的height声明有没有被划掉。 - 把四组数字和结论写进 PROGRESS。
- 若
height被划掉 / 计算值为auto→ §2.2 成立,做 1.2。 - 若
height正常生效、盒子四个分区一致 → 说明用户看到的是别的东西(例如内容在固定盒子里塌成一小块、或某个分区的内容把overflow: hidden顶破)。此时不要硬改 CSS,把量到的数字和截图写进 PROGRESS,本任务转investigating,由产品补充现象(哪两个分区、什么浏览器、窗口多大)后再开一轮。
1.2 修复:给固定高度补 vh 回退
按仓库既有写法(.group\/sidebar-provider[data-viewport] 那一行)补成两条声明,新值写在旧值之后:
.settings-modal {
width: min(100vw - 32px, 880px);
height: min(84vh, 640px);
height: min(84dvh, 640px);
max-height: min(84vh, 640px);
max-height: min(84dvh, 640px);
…
}
同样处理同一批里高度即功能的几处,其余只写 max-height 的可以不动(作废后退化成无上限,不会让盒子随分区跳变):
.account-modal的max-height: min(84dvh, 760px)- 移动端
.settings-modal { height: 100dvh }与.account-modal { max-height/min-height: 100dvh }
1.3 把防复发从「有没有写」升级成「量不量得到」
现有断言只是文本匹配,挡不住本次复发。新增两条契约测试:
- 回退契约:
globals.css中凡是在height(非max-height)里用了dvh的规则,同一规则内必须存在一条vh版本的同名属性声明,且vh在前。这条可以纯文本实现,覆盖全文件而不只是设置弹窗——它才是真正能防住下一次的那条。 - 同尺寸契约:断言
accountDialogClasses的四个设置分区映射到同一个类名(现有测试只验了dialogClass: "settings-modal"这一个分区的渲染)。
原有的 assert.match(styles, /\.settings-modal \{[^}]*width:[^}]*height:/) 保留,但在它上面加注释说明它只是存在性检查、真正的防线是上面两条。
验收标准(任务 1)
- 1.1 的四组
getBoundingClientRect()数字写进 PROGRESS,四个分区高度与宽度完全一致。 - 1.3 的两条新测试通过;故意把某处
height: …dvh的回退删掉,回退契约测试必须变红(执行方本地验一次,结论写进 PROGRESS,不用把破坏提交上去)。 - 桌面与 375px 两种宽度下四个分区逐一切换,弹窗外框不跳变——浏览器项,写进
docs/testing/。
任务 2 · 分区菜单去掉强调条,并拆开选中态与悬停态
2.1 删除这两条:
.settings-dialog-nav-item[aria-current="page"] { box-shadow: inset 2px 0 0 var(--color-action); } /* 桌面 */
.settings-dialog-nav-item[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--color-action); } /* 移动端 */
2.2 把现在共用的那条规则拆成两态。目标:选中比悬停"重"一档,且两者都不用强调色条、不用 font-weight。建议值(执行方可在同族 token 内微调,但必须给出选择理由):
.settings-dialog-nav-item:hover {
background: color-mix(in srgb, var(--color-canvas-muted) 55%, transparent);
color: var(--color-ink-secondary);
}
.settings-dialog-nav-item[aria-current="page"] {
background: var(--color-canvas-muted);
color: var(--color-ink);
}
即:悬停是一层淡的面 + 次级墨;选中是实的面 + 主墨。 与 frontend/DESIGN.md「Hierarchy inside the nav comes from ink rank, not hue」一致。
2.3 深色主题必须同样成立。--color-canvas-muted 与两级墨色在深色下都有定义,但 55% 混透明后与深色底的对比要复查;frontend/tests/dark-theme-contract.test.ts 若涉及新增 token 需同步。不得引入新的字面色值。
2.4 键盘可达性不受影响::focus-visible 的描边规则不动。选中态不能只靠颜色——aria-current="page" 已经在 DOM 上,屏幕阅读器不受本改动影响,这一点在 PROGRESS 里写明即可。
2.5 frontend/DESIGN.md 同提交更新:在导航/强调色一节写明「设置弹窗分区菜单不使用强调色条,选中与悬停靠面与墨色等级区分」,并写明左侧会话列表的 2px 色条本轮保留,两处口径暂时不同是已知且授权的。
验收标准(任务 2)
grep -n "inset 2px 0 0 var(--color-action)\|inset 0 -2px 0 var(--color-action)" frontend/src/app/globals.css在.settings-dialog-nav-item上下文无结果。- 新增契约测试:
.settings-dialog-nav-item[aria-current="page"]规则内不含box-shadow,且:hover与[aria-current="page"]不再共用同一条声明(两者必须是各自独立的规则块)。 - 浅色与深色各看一遍,悬停与选中肉眼可区分——浏览器项,写进
docs/testing/。
任务 3 · 测试与文档
3.1 开工先量基线:
cd .worktrees/settings-dialog-size-and-nav-20260915/frontend
npm ci
npm test 2>&1 | tail -20 # pass / fail / tests 三个数字
3.2 文档:
docs/BUG_HISTORY.md新增 BUG-698,字段齐全。「复发自」一栏必须写BUG-554,并在「根因」里写清旧防复发(「必须同时声明 width 与 height」)为何没拦住——它检查的是声明存在性,不是声明是否生效。同时回到 BUG-554 那条记录,在「防复发」末尾补一句指向 BUG-698 的修正。frontend/DESIGN.md:任务 2.5 的内容;另加一条 CSS 规则——凡是height用dvh的地方必须先写vh回退,并说明为什么(不支持时整条声明作废,固定高度会退化成内容高度)。docs/testing/settings-dialog-20260915.md:把任务 1 与 2 里标了「浏览器项」的条目写成可照做的清单。CHANGELOG.md:一条日期 + 一句话标题 + 要点。docs/tasks/PROGRESS-settings-dialog-size-and-nav-20260915.md。
6. 让步顺序
- 任务 1.1 若复现不出尺寸变化,转
investigating并停在那里,把量到的数字交回产品。不得为了"有交付"而盲改 CSS。 - 任务 1.3 的回退契约测试若在全文件范围内误伤了合理写法(例如某处确实只能用
dvh),缩小到height属性且排除已注释说明的例外,例外清单写进测试文件头部注释。不得因为难写就退回原来的存在性断言。 - 任务 2.2 的具体数值可调,但必须让选中比悬停重一档;调完在浅色与深色下都要能分辨。
- 绝不让步:不得让四个分区重新使用不同的类;不得用
font-weight做选中态;不得引入字面色值;BUG-554 的其余防复发条款不得破坏。
7. 开工前置命令
cd /workspace/Jyotisha
git status -sb | head -1
git fetch origin --prune
git worktree add -b codex/settings-dialog-size-and-nav-20260915 \
.worktrees/settings-dialog-size-and-nav-20260915 origin/staging
cd .worktrees/settings-dialog-size-and-nav-20260915/frontend
npm ci
npm test 2>&1 | tail -20
./node_modules/.bin/tsc --noEmit
npm run lint
纯前端改动,不要求 scripts/pre_work_check.py。
交付:git push origin HEAD:staging,推完核对远端 SHA。
8. 串行与依赖
- 本单改
frontend/src/app/globals.css。TASK-chat-reading-load-20260915与TASK-mobile-touch-and-breakpoints-20260915的实现已合入staging(8144fca2、6c748d86),以6c748d86为基线即可,无需再等。 - 与那两轮改的是同一文件的不同区段,但它们已经合入,所以本单不与任何在途轮次并行。开工前仍按
AGENTS.md§3 确认状态板上没有新的「执行中」项在动这个文件。
9. BUG 编号
- 本单占用 BUG-698(任务 2 是产品决策,不占编号)。
origin/staging@6c748d86的当前最大号是 BUG-697(BUG-695~697 由触控与断点那轮占用)。- 开工时仍需核对
docs/BUG_HISTORY.md的当前最大号。