feat(theme): 语义变量层基座 --ot-*(深色锁现值/浅色按 WCAG 派生)+ 表测试

This commit is contained in:
Bill committed 2026-10-16 20:21:40 +08:00
1 parent 0a7bdda2d3
commit 2136221f2c
4 files changed
+218 -39

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
/**
* semantic.ts self-test. 该模块必须零 import(Node 原生类型剥离直连 .ts,
* 同 tests/terminal-cwd.mjs 的约束)。锁三件事:
* 1) isLightTheme 边界(>0.5 亮度为浅);
* 2) 深色分支 = SEMANTIC 锚点原值(零回归锁定);
* 3) 浅色分支全部达标:正文/fg ≥4.5,语义基色 ≥3.0。
* Run: node tests/semantic-theme.mjs (must exit 0)
*/
import { isLightTheme, semanticVars, SEMANTIC } from '../src/renderer/src/theme/semantic.ts'
let failed = 0
const ok = (cond, msg) => {
console.log(` ${cond ? 'ok' : 'FAIL'}: ${msg}`)
if (!cond) failed += 1
}
console.log('isLightTheme:')
ok(isLightTheme('#ffffff') === true, '#ffffff is light')
ok(isLightTheme('#fafafa') === true, '#fafafa is light')
ok(isLightTheme('#fdf6e3') === true, '#fdf6e3 is light')
ok(isLightTheme('#1e1e1e') === false, '#1e1e1e is dark')
ok(isLightTheme('#282a36') === false, '#282a36 is dark')
console.log('dark branch locks anchors (zero regression):')
const dark = semanticVars({ background: '#1e1e1e', foreground: '#cccccc' }, false)
ok(dark['muted'] === SEMANTIC.muted, 'muted = anchor')
ok(dark['success'] === SEMANTIC.success, 'success = anchor')
ok(dark['danger'] === SEMANTIC.danger, 'danger = anchor')
ok(dark['warning'] === SEMANTIC.warning, 'warning = anchor')
ok(dark['info'] === SEMANTIC.info, 'info = anchor')
ok(dark['scrollbar'] === SEMANTIC.scrollbar, 'scrollbar = anchor')
ok(dark['shadow'] === SEMANTIC.shadow, 'shadow = anchor')
ok(dark['colorScheme'] === 'dark', 'colorScheme dark')
console.log('light branch meets ratios:')
for (const bg of ['#f6f8fa', '#fafafa', '#fdf6e3']) {
const v = semanticVars({ background: bg, foreground: '#383a42' }, true)
const L = (h) => {
const c = [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16) / 255)
.map((s) => (s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4))
return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]
}
const cr = (a, b) => {
const x = L(a), y = L(b)
return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05)
}
for (const key of ['muted', 'textSecondary', 'successFg', 'dangerFg', 'warningFg', 'infoFg']) {
ok(cr(v[key], bg) >= 4.5, `${bg} ${key} ${v[key]} >= 4.5 (${cr(v[key], bg).toFixed(2)})`)
}
for (const key of ['success', 'danger', 'warning', 'info']) {
ok(cr(v[key], bg) >= 3, `${bg} ${key} ${v[key]} >= 3.0 (${cr(v[key], bg).toFixed(2)})`)
}
ok(v['colorScheme'] === 'light', `${bg} colorScheme light`)
}
if (failed > 0) {
console.error(`${failed} check(s) failed`)
process.exit(1)
}
console.log('semantic-theme: all ok')