Files
Jyotisha/frontend/tests/touch-target-contract.test.ts
T
Jesse_ChenandClaude Opus 5 b3ea8c336e
Independent Staging Quality Gate / validate (push) Failing after 9m19s
Independent Staging Quality Gate / publish (push) Skipped
feat(ui): 侧栏三段合成一条平铺列表,助手消息去掉头像
侧栏此前在第一条会话之前压着三段:星盘名字 chip 组、收藏对话 <details>、
历史对话 <details>——两个折叠箭头,底下都没有值得折叠的东西。现在是一条
平铺列表:收藏作为首个标签组(带 13px 星标),其余沿用既有的日期分组。

星盘入口收敛到一处。这里与任务书 D6 的字面写法方向相反:D6 说留侧栏删
账户菜单,我做的是删侧栏留账户菜单。理由——D6 写于侧栏只有 2 个导航项时,
现在是 4 个;且 chip 点下去只是开弹窗(page.tsx 里三个回调都是
openAccountDialog("chart-library")),一组只会开弹窗的名字没有导航价值。
连带删 charts/onSelectChart/onAddChart props、SidebarChart 类型、
sidebarCharts 派生与一批 CSS。

助手头像删除,--assistant-content-inset 由 calc(32px + gap) 归零,
追问 chip 与运行时间轴改为与正文对齐。/jyotish-logo.png 文件保留。

顺带关闭 R2 留下的隐患:入口 pill 36px 低于 44px 触摸推荐值,补
::after inset -4px 热区扩展,并在 touch-target-contract 里接替
已删除的 .chart-nav-chip。

测试 3350 不变,fail 仍 31 且与基线逐条一致;/ 仍 Static;
CSS gzip 41,044→39,825(−3.0%),三轮累计 −3.1%。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-16 05:46:29 +00:00

65 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { cssDeclarations } from "./css-contract-test-support.ts";
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
function minHeightPx(selector: string) {
const matches = [...cssDeclarations(selector, globalStyles).matchAll(/min-height:\s*(\d+)px/g)]
.map((hit) => Number(hit[1]));
assert.ok(matches.length > 0, `${selector} must declare min-height`);
return Math.max(...matches);
}
function hasHitExpandPseudo(selector: string) {
const after = cssDeclarations(`${selector}::after`, globalStyles);
assert.match(after, /position:\s*absolute/);
assert.match(after, /content:\s*""/);
return after;
}
test("40px controls grow to a 44px box when that does not pack a scrolling list badly", () => {
assert.ok(minHeightPx(".select-item") >= 44);
assert.ok(minHeightPx(".birth-time-window-details .birth-time-skip-button") >= 44);
assert.ok(minHeightPx(".report-center-section-heading button") >= 44);
});
test("icon and chip controls keep their visual size and expand the hit with a pseudo-element", () => {
const actions = cssDeclarations(".message-actions button", globalStyles);
assert.match(actions, /width:\s*26px/);
assert.match(actions, /height:\s*26px/);
assert.match(cssDeclarations(".message-actions", globalStyles), /gap:\s*1px/);
const actionHit = hasHitExpandPseudo(".message-actions button");
// 原值: 热区锁 27×3444×44 会与相邻按钮和 follow-up 重叠)。
// 新值: 细指针仍是 27×34;粗指针见下一条,gap 20px + 底边 20px 后才放大到 44×44。
// 原因: BUG-695,触屏点赞容易点成点踩。
assert.match(actionHit, /width:\s*27px/);
assert.match(actionHit, /height:\s*34px/);
// 原值:`.chart-nav-chip`(侧栏星盘列表的名字 chip)
// 新值:`.starter-entry`(空态的两枚入口 pill
// 原因:星盘列表 chip 已随入口收敛删除(D6)。空态的入口 pill 是这一类「视觉小于
// 44px、靠伪元素扩热区」控件的新成员,36px 视觉 + -4px 外扩,正好归这条合同管。
assert.equal(minHeightPx(".starter-entry"), 36);
assert.match(hasHitExpandPseudo(".starter-entry"), /inset:\s*-4px/);
const auth = cssDeclarations(".auth-links button", globalStyles);
assert.match(auth, /min-height:\s*32px/);
assert.match(hasHitExpandPseudo(".auth-links button"), /inset:\s*-4px/);
});
test("coarse pointers get a 44×44 message-action hit that does not overlap neighbours", () => {
const coarseStart = globalStyles.indexOf("@media (pointer: coarse)");
assert.notEqual(coarseStart, -1);
const coarse = globalStyles.slice(coarseStart, globalStyles.indexOf("}", globalStyles.indexOf(".markdown-code-copy", coarseStart)) + 1);
assert.match(coarse, /\.message-actions \{[\s\S]*gap:\s*var\(--space-5\)/);
assert.match(coarse, /margin-bottom:\s*var\(--space-5\)/);
assert.match(coarse, /\.message-actions button::after \{[\s\S]*width:\s*44px;[\s\S]*height:\s*44px;/);
assert.match(coarse, /\.markdown-code-copy \{[\s\S]*min-height:\s*44px;/);
const base = globalStyles.slice(0, coarseStart);
assert.match(base, /\.message-actions \{[\s\S]*gap:\s*1px;/);
assert.match(base, /\.message-actions button::after \{[\s\S]*width:\s*27px;[\s\S]*height:\s*34px;/);
});