/** * 票 102 静态闸门(二):一级菜单的位置不得由秒针决定,同时保留票 37 的溢出兜底。 * * 维护者 2026-11-02:「按钮在居中偏左(略微左移,不与背景装饰线条重合);目前由于时间字体 * 长短不平均,时间跳动会导致按钮位置左右移动,需避免。」 * * 关键认知:让菜单移动的真凶是**时钟读数的不定宽**,不是右列的 min-content 兜底本身。 * 所以正确解是「固定时钟宽度」+「保留 min-content 下限」这对组合: * - 时钟进固定数字槽位后,右列的 min-content 只剩常量,不再随秒针变 → 位置稳定; * - min-content 下限还在 → 窄视口内容超宽时不会像票 37 那样向左溢出压住「预警处置」。 * 只把右列改成 minmax(0, 1fr) 也能消除抖动,但会撤掉溢出兜底(上一版就犯了这个错), * 本闸门同时锁这两条,改回 0 或删掉槽位都会转红。 * 断言一律落在对应选择器的规则体内,不用全文件子串(见 t102-shell-source 的说明)。 * 用法:node verify/t102-topbar-position.mjs [源码路径]。 */ import { readShell, ruleBody, declaration, check, assertModeNavIntact, report } from './t102-shell-source.mjs' const source = readShell(process.argv[2]) // 入口一:中列(菜单)的位置不得由右列内容决定。 // 票 102 当时靠「右列 min-content 变成常量 + 时钟固定槽位」;票 103 起状态栏已脱流 // (.cockpit-shell__top-meta 绝对定位),右列不再承载任何内容,两列等分即可, // 中列位置与内容彻底无关。此处只锁「两列等分、中列真居中」这一结构事实, // 原先那条 min-content 下限的断言随设计变更一并退役(票 103 的闸门接管碰撞校验)。 const columns = declaration(source, 'grid-template-columns') check(columns.includes('minmax(240px, 1fr) auto minmax(240px, 1fr)'), '顶栏三列须两列等分(中列位置与右列内容无关)') // 入口二:一级菜单整组平移,位置与内容彻底解耦。用 transform 不用 margin。 const modes = ruleBody(source, 'cockpit-shell__modes') check(/transform:\s*translateX\(/.test(modes), '一级菜单必须整组 translateX 左移(居中偏左)') check(!/margin-left/.test(modes.split('\n').filter((l) => /^\s*[a-z-]+\s*:/.test(l)).join('\n')), '左移不得用 margin-left(会被 grid 再分配,等于把裁决权交回内容)') // 入口三:时钟读数宽度恒由声明决定,而不是由「这一刻几点几分几秒」决定。 // 菜单位置虽已与右列无关,但时钟自身仍不许随秒针改变占位(否则状态栏内部会左右抖)。 const clock = ruleBody(source, 'cockpit-shell__clock') check(/display:\s*inline-block/.test(clock), '时钟槽位要 display:inline-block 才吃得到 min-width') check(/flex:\s*0 0 auto/.test(clock), '时钟槽位不参与挤压') check(/text-align:\s*right/.test(clock), '时钟槽位内右对齐(左缘固定,只右侧字形变化)') check(/font-variant-numeric:\s*tabular-nums/.test(clock), '时钟数字必须等宽') const slot = clock.match(/min-width:\s*calc\((\d+(?:\.\d+)?)\s*\*\s*var\(--u\)\)/) check(Boolean(slot), '时钟必须有固定 min-width 数字槽位(右列下限靠它保持常量)') if (slot) { // 最宽一次读数(2384×1160 实测 HH:MM:SS 占 ~101u)必须塞得进槽位,否则 min-width 形同虚设。 check(Number(slot[1]) >= 101, `时钟槽位 ${slot[1]}u 不足以容纳最宽读数(实测 ~101u)`) } // 位置改动只许动位置:导航语义、归位/返回全球按钮、时钟本体都不变。 assertModeNavIntact(source) check(source.includes('cockpit-shell__home'), '归位按钮保持原样') check(source.includes('cockpit-shell__return-global'), '返回全球按钮保持原样') check(source.includes('cockpit-shell__clock'), '时钟保持原样') report('t102-topbar-position')