- 玩法一 数字一笔:规则库、求解器、26 关、竖屏触摸 Web 原型、测试 - 玩法二 连连气球:连消畅玩版核心、Web 原型、Cocos 球槽组件与导出脚本 - 玩法三 六边一笔:规则库、求解器、10 关 + 转墙实验包、Web 原型、测试 - 产品文档、UI 设计规范、各玩法 GDD / HANDOFF - 文档移除全部外部参考作引用;删除玩法二 v0 旧稿
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
// 设计令牌单一来源守门:web/style.css 的 :root 与 src/theme.ts、web/canvas-painter.ts 必须一致。
|
|
// 改任一处而不同步另一处,这里会红。规格文档见仓库 docs/design.md。
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { test } from 'node:test';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { MOTION, THEME } from '../src/theme';
|
|
import { BOARD_FONT_FAMILY } from '../web/canvas-painter';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const css = readFileSync(resolve(here, '../web/style.css'), 'utf8');
|
|
|
|
function rootTokens(source: string): Map<string, string> {
|
|
const m = /:root\s*\{([\s\S]*?)\n\}/.exec(source);
|
|
assert.ok(m, 'style.css 缺少 :root 块');
|
|
const out = new Map<string, string>();
|
|
for (const line of m[1].split('\n')) {
|
|
const t = /^\s*(--[\w-]+)\s*:\s*(.+?);\s*$/.exec(line);
|
|
if (t) out.set(t[1], t[2]);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const tokens = rootTokens(css);
|
|
const hex = (s: string | undefined): string => (s ?? '').trim().toUpperCase();
|
|
|
|
test('颜色令牌:style.css :root 与 THEME 一致', () => {
|
|
const pairs: Array<[string, string]> = [
|
|
['--bg', THEME.bg],
|
|
['--cell', THEME.cell],
|
|
['--ink', THEME.clueInk],
|
|
['--clue-ring', THEME.clueRing],
|
|
['--primary', THEME.visited],
|
|
['--primary-bright', THEME.visitedBright],
|
|
['--on-primary', THEME.visitedInk],
|
|
['--danger', THEME.conflict],
|
|
['--hint', THEME.hint],
|
|
];
|
|
for (const [name, value] of pairs) {
|
|
assert.equal(hex(tokens.get(name)), hex(value), `${name} 与 theme.ts 不一致`);
|
|
}
|
|
});
|
|
|
|
test('动效时长令牌:--t-* 与 MOTION 一致', () => {
|
|
const pairs: Array<[string, number]> = [
|
|
['--t-fast', MOTION.fast],
|
|
['--t-base', MOTION.base],
|
|
['--t-slow', MOTION.slow],
|
|
['--t-hold', MOTION.hold],
|
|
];
|
|
for (const [name, seconds] of pairs) {
|
|
assert.equal(tokens.get(name), `${Math.round(seconds * 1000)}ms`, `${name} 与 theme.ts MOTION 不一致`);
|
|
}
|
|
assert.equal(MOTION.numberFade, MOTION.fast, '步号淡入应等于 fast 档');
|
|
assert.equal(MOTION.bridgeGrow, MOTION.fast, '桥生长应等于 fast 档');
|
|
assert.equal(MOTION.flashTime, MOTION.slow, '闪白应等于 slow 档');
|
|
assert.equal(MOTION.clearedSweep, MOTION.slow, '过关扫光应等于 slow 档');
|
|
assert.equal(MOTION.clearedHold, MOTION.hold, '过关停留应等于 hold 档');
|
|
assert.equal(MOTION.boardEnter, MOTION.base, '进场应等于 base 档');
|
|
});
|
|
|
|
test('字体栈:canvas 与 CSS --font 完全一致', () => {
|
|
assert.equal(tokens.get('--font'), BOARD_FONT_FAMILY);
|
|
});
|