From 999f3f6ec161dbd0bc545a04b9b6811afab12e61 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Mon, 26 Oct 2026 17:45:43 +0800 Subject: [PATCH] =?UTF-8?q?iter(g185):=20=E5=88=86=E5=89=B2=E7=BA=BF?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=94=B9=E8=B5=B0=E7=BB=86=E5=88=86=E8=BE=B9?= =?UTF-8?q?=E6=A1=86=E4=BB=A4=E7=89=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/index.json | 8 ++-- .ua/visual-baseline.json | 9 +++-- llms-full.txt | 8 ++-- llms.txt | 2 +- packages/design/tokens/tokens.css | 2 +- packages/skin/ui/ui.css | 38 ++++++++--------- packages/tools/cli/lib/visual-audit.mjs | 2 +- packages/tools/cli/lib/visual-rules.mjs | 54 +++++++++++++++++++++++-- packages/tools/cli/ua.manifest.json | 6 +-- 9 files changed, 88 insertions(+), 41 deletions(-) diff --git a/.ua/index.json b/.ua/index.json index 4a5488f..015bc55 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-26T09:29:34.397Z", + "generatedAt": "2026-10-26T09:45:15.809Z", "repoRoot": ".", "packages": [ { @@ -3730,7 +3730,7 @@ "ua style:注释规范门禁(导出必须 /*** ***/、接口字段行尾注释、行注释中文),违规退出码 1", "ua verify:清单一致性门禁(id 唯一 / exports 对应 / props 必带 n t d / steps≥3 / demo 锚点存在)", "ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1", - "ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里五类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数", + "ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数", "ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe" ], "keyTypes": [ @@ -3750,9 +3750,9 @@ "装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify → lint-iterations → audit:visual)", "查组件:ua list 看全量,ua search <关键词> 按 id/标题/参数名/参数说明/步骤模糊找(「处理中」这类说明里的词也能搜到,命中非 id 标题时会标出命中字段;拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", "查配方:ua recipes 列全量(编号 / 标题 / 用到的组件),ua recipes <编号> 看骨架代码与要点(--json 给脚本用;MCP 里对应 list_recipes / get_recipe)", - "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset)", + "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十一组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset)", "改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)", - "改完样式跑 npm run audit:visual:五类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌", + "改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌", "在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe" ], "components": [] diff --git a/.ua/visual-baseline.json b/.ua/visual-baseline.json index 1e1d809..929d05c 100644 --- a/.ua/visual-baseline.json +++ b/.ua/visual-baseline.json @@ -1,8 +1,8 @@ { - "$schema": "视觉漂移基线:五类硬编码视觉字面量的允许上限,任一类超出即 ua audit:visual 退出码 1;迁移后用 --baseline 重写", + "$schema": "视觉漂移基线:各类硬编码视觉字面量的允许上限,任一类超出即 ua audit:visual 退出码 1;迁移后用 --baseline 重写", "file": "packages/skin/ui/ui.css", - "commit": "551b2f6", - "generatedAt": "2026-10-26T09:40:14.168Z", + "commit": "ecd24d5", + "generatedAt": "2026-10-26T09:45:21.406Z", "lines": 4780, "total": 40, "counts": { @@ -15,6 +15,7 @@ "control-h-literal": 0, "weight-literal": 0, "leading-literal": 0, - "z-literal": 0 + "z-literal": 0, + "border-literal": 0 } } diff --git a/llms-full.txt b/llms-full.txt index 6cac0f5..e39fcb1 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-26T09:29:34.397Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-26T09:45:15.809Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -43,9 +43,9 @@ 1. 装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify → lint-iterations → audit:visual) 2. 查组件:ua list 看全量,ua search <关键词> 按 id/标题/参数名/参数说明/步骤模糊找(「处理中」这类说明里的词也能搜到,命中非 id 标题时会标出命中字段;拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单) 3. 查配方:ua recipes 列全量(编号 / 标题 / 用到的组件),ua recipes <编号> 看骨架代码与要点(--json 给脚本用;MCP 里对应 list_recipes / get_recipe) -4. 查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset) +4. 查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十一组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset) 5. 改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料) -6. 改完样式跑 npm run audit:visual:五类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌 +6. 改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌 7. 在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe ## @ui-arch/ui(skin-dom)v0.1.0 @@ -2122,7 +2122,7 @@ API 速览: - ua style:注释规范门禁(导出必须 /*** ***/、接口字段行尾注释、行注释中文),违规退出码 1 - ua verify:清单一致性门禁(id 唯一 / exports 对应 / props 必带 n t d / steps≥3 / demo 锚点存在) - ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1 -- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里五类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数 +- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数 - ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe 一行用法: diff --git a/llms.txt b/llms.txt index 811cc1e..39e6cac 100644 --- a/llms.txt +++ b/llms.txt @@ -135,7 +135,7 @@ - ua style:注释规范门禁(导出必须 /*** ***/、接口字段行尾注释、行注释中文),违规退出码 1 - ua verify:清单一致性门禁(id 唯一 / exports 对应 / props 必带 n t d / steps≥3 / demo 锚点存在) - ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1 - - ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里五类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数 + - ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数 - ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe - 用法: `node packages/tools/cli/bin/ua.js |docs|tokens|style|verify|commit-lint|audit:visual|mcp>;仓库内等价于 npm run index / docs / style / verify / audit:visual` - 约定: 零依赖:只用 node 标准库,Node >= 18 diff --git a/packages/design/tokens/tokens.css b/packages/design/tokens/tokens.css index a074a98..83dcc04 100644 --- a/packages/design/tokens/tokens.css +++ b/packages/design/tokens/tokens.css @@ -49,7 +49,7 @@ --ua-leading-normal: 1.55; /* 正文档:多行正文、说明文字 */ /* ---- 边框:细分(比基础边框更淡,用于分割线,避免 1px 线过重) ---- */ - --ua-border-subtle: color-mix(in srgb, var(--ua-color-border) 60%, transparent); /* 细分边框色:表格分割线、卡片内分隔 */ + --ua-border-subtle: color-mix(in srgb, var(--ua-color-border) 60%, transparent); /* 细分边框色(专用于分割线):表格行线 / 卡片页脚 / 弹窗抽屉页头页脚 / 列表项 / 选项卡底边 / 折叠面板与步骤条连接线 / 分隔线组件 */ /* ---- 阴影:四档 elevation,柔和优先(扩散大、透明度低) ---- */ --ua-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.06); /* 1 档:贴地投影,用于卡片 / 输入组 / 轻浮起 */ diff --git a/packages/skin/ui/ui.css b/packages/skin/ui/ui.css index c9dbf28..a9eeba6 100644 --- a/packages/skin/ui/ui.css +++ b/packages/skin/ui/ui.css @@ -102,7 +102,7 @@ gap: var(--ua-space-1); margin-top: var(--ua-space-3); padding-top: var(--ua-space-2); - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } .ua-card-borderless { border: none; @@ -121,7 +121,7 @@ .ua-table th, .ua-table td { padding: var(--ua-space-table-cell); - border-bottom: 1px solid var(--ua-color-border); + border-bottom: 1px solid var(--ua-border-subtle); text-align: left; font-size: var(--ua-font-size-md); white-space: nowrap; @@ -134,7 +134,7 @@ background: var(--ua-color-surface); font-weight: var(--ua-weight-medium); color: var(--ua-color-text-muted); - box-shadow: inset 0 -1px 0 var(--ua-color-border); + box-shadow: inset 0 -1px 0 var(--ua-border-subtle); } .ua-table th.ua-sortable { cursor: pointer; @@ -439,7 +439,7 @@ body.ua-col-resizing { } .ua-option-group:not(:first-child) { margin-top: var(--ua-space-1); - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); padding-top: var(--ua-space-2); } .ua-option { @@ -563,7 +563,7 @@ body.ua-col-resizing { .ua-menu-divider { height: 1px; margin: var(--ua-space-1) var(--ua-space-05); - background: var(--ua-color-border); + background: var(--ua-border-subtle); } /* —— 面包屑 —— */ @@ -666,7 +666,7 @@ body.ua-col-resizing { background: var(--ua-color-bg); } .ua-acc-item + .ua-acc-item { - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } .ua-acc-head { display: flex; @@ -976,7 +976,7 @@ body.ua-col-resizing { top: 30px; bottom: 2px; width: 1px; - background: var(--ua-color-border); + background: var(--ua-border-subtle); } .ua-stepper.is-vertical .ua-step:last-child::after { display: none; /* 末步没有下一段可连 */ @@ -999,7 +999,7 @@ body.ua-col-resizing { height: 1px; margin-top: 13px; margin-right: var(--ua-space-2); - background: var(--ua-color-border); + background: var(--ua-border-subtle); flex-shrink: 0; } .ua-step-dot { @@ -1140,12 +1140,12 @@ button.ua-step-dot:hover { content: ''; flex: 1; height: 1px; - background: var(--ua-color-border); + background: var(--ua-border-subtle); } .ua-divider-dashed::before, .ua-divider-dashed::after { background: none; - border-top: 1px dashed var(--ua-color-border); + border-top: 1px dashed var(--ua-border-subtle); } .ua-divider-label { font-size: var(--ua-font-size-sm); @@ -1165,7 +1165,7 @@ button.ua-step-dot:hover { } .ua-divider-vertical.ua-divider-dashed { background: none; - border-left: 1px dashed var(--ua-color-border); /* 垂直虚线走边框 */ + border-left: 1px dashed var(--ua-border-subtle); /* 垂直虚线走边框 */ } /* —— 徽标 —— */ @@ -1573,7 +1573,7 @@ button.ua-step-dot:hover { gap: var(--ua-space-1); max-width: 100%; overflow-x: auto; - border-bottom: 1px solid var(--ua-color-border); + border-bottom: 1px solid var(--ua-border-subtle); } .ua-tab { padding: 7px var(--ua-space-35); @@ -1820,7 +1820,7 @@ button.ua-step-dot:hover { justify-content: flex-end; gap: var(--ua-space-2); padding: var(--ua-space-3) var(--ua-space-4); - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } /* —— 弹窗 —— */ @@ -1862,7 +1862,7 @@ button.ua-step-dot:hover { justify-content: flex-end; gap: var(--ua-space-2); padding: var(--ua-space-3) var(--ua-space-4); - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } @keyframes ua-fade { from { @@ -1880,7 +1880,7 @@ button.ua-step-dot:hover { align-items: center; justify-content: space-between; padding: var(--ua-space-3) var(--ua-space-4); - border-bottom: 1px solid var(--ua-color-border); + border-bottom: 1px solid var(--ua-border-subtle); } .ua-modal-title { margin: 0; @@ -2157,7 +2157,7 @@ button.ua-step-dot:hover { top: 6px; bottom: 0; width: 1px; - background: var(--ua-color-border); + background: var(--ua-border-subtle); } .ua-tl-right::before { left: auto; @@ -2496,10 +2496,10 @@ button.ua-step-dot:hover { .ua-list-header { font-weight: var(--ua-weight-semibold); color: var(--ua-color-text); - border-bottom: 1px solid var(--ua-color-border); + border-bottom: 1px solid var(--ua-border-subtle); } .ua-list-footer { - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } .ua-list-body { display: flex; @@ -2513,7 +2513,7 @@ button.ua-step-dot:hover { transition: background var(--ua-duration-fast) ease; } .ua-list-item + .ua-list-item { - border-top: 1px solid var(--ua-color-border); + border-top: 1px solid var(--ua-border-subtle); } .ua-list-clickable { cursor: pointer; diff --git a/packages/tools/cli/lib/visual-audit.mjs b/packages/tools/cli/lib/visual-audit.mjs index 446bff6..51ec0fc 100644 --- a/packages/tools/cli/lib/visual-audit.mjs +++ b/packages/tools/cli/lib/visual-audit.mjs @@ -62,7 +62,7 @@ function writeBaseline(root, result) { const file = path.join(root, BASELINE_REL); const total = KIND_META.reduce((sum, meta) => sum + (result.counts[meta.key] ?? 0), 0); const data = { - $schema: '视觉漂移基线:五类硬编码视觉字面量的允许上限,任一类超出即 ua audit:visual 退出码 1;迁移后用 --baseline 重写', + $schema: '视觉漂移基线:各类硬编码视觉字面量的允许上限,任一类超出即 ua audit:visual 退出码 1;迁移后用 --baseline 重写', file: UI_CSS_REL, commit: shortCommit(root), generatedAt: new Date().toISOString(), diff --git a/packages/tools/cli/lib/visual-rules.mjs b/packages/tools/cli/lib/visual-rules.mjs index ae0bd5f..08456ee 100644 --- a/packages/tools/cli/lib/visual-rules.mjs +++ b/packages/tools/cli/lib/visual-rules.mjs @@ -9,7 +9,7 @@ /*** 皮肤包样式表相对仓库根的路径(迁移与审计共同的目标文件) ***/ export const UI_CSS_REL = 'packages/skin/ui/ui.css'; -/*** 作用:五类硬编码的键名、中文名与判定口径,审计输出与报告都按这个顺序 ***/ +/*** 作用:各类硬编码的键名、中文名与判定口径,审计输出与报告都按这个顺序 ***/ export const KIND_META = [ { key: 'font-size-literal', @@ -61,6 +61,11 @@ export const KIND_META = [ label: '层级字面值', hint: 'z-index 写死数字,应改用 --ua-z-* 五档(组件内 1/2 微排序豁免)', }, + { + key: 'border-literal', + label: '边框色字面值', + hint: 'border/border-color 写死色值(rgba()/#hex),组件轮廓用 --ua-color-border、分割线用 --ua-border-subtle', + }, ]; /*** 作用:需要扫描的 CSS 属性白名单(其余属性本轮不动;用白名单顺带避开伪类选择器误判) ***/ @@ -92,6 +97,30 @@ const SCAN_PROPS = new Set([ 'font-weight', 'line-height', 'z-index', + 'border', + 'border-top', + 'border-right', + 'border-bottom', + 'border-left', + 'border-color', + 'border-top-color', + 'border-right-color', + 'border-bottom-color', + 'border-left-color', +]); + +/*** 作用:边框类属性集合(轮廓与分割线共用一套色值口径,禁用字面色) ***/ +const BORDER_PROPS = new Set([ + 'border', + 'border-top', + 'border-right', + 'border-bottom', + 'border-left', + 'border-color', + 'border-top-color', + 'border-right-color', + 'border-bottom-color', + 'border-left-color', ]); /*** 作用:内距类属性集合(迁移与统计共用同一判定) ***/ @@ -217,7 +246,7 @@ export function blankComments(src) { /*** 作用:逐条产出「属性 + 值」及其在原文中的偏移;只认白名单属性且必须以分号收尾(避开伪类选择器) ***/ export function eachDeclaration(code) { - const re = /(padding-top|padding-right|padding-bottom|padding-left|padding|margin-top|margin-right|margin-bottom|margin-left|margin|row-gap|column-gap|gap|font-weight|line-height|z-index|min-height|max-height|height|transition-duration|animation-duration|transition|animation|font-size|box-shadow|border-radius|outline)\s*:\s*([^;{}]*);/gi; + const re = /(border-top-color|border-right-color|border-bottom-color|border-left-color|border-color|border-top|border-right|border-bottom|border-left|border|padding-top|padding-right|padding-bottom|padding-left|padding|margin-top|margin-right|margin-bottom|margin-left|margin|row-gap|column-gap|gap|font-weight|line-height|z-index|min-height|max-height|height|transition-duration|animation-duration|transition|animation|font-size|box-shadow|border-radius|outline)\s*:\s*([^;{}]*);/gi; const list = []; for (const m of code.matchAll(re)) { const prop = m[1].toLowerCase(); @@ -350,6 +379,15 @@ function migrateZ(core) { return { kind: 'z-literal', value: token }; } +/*** 作用:迁移单条边框:纯黑 rgba 边(历史上手写的「深一层黑边」)归到基础边框色;其余留在门禁里由人按轮廓/分割线语义定位 ***/ +function migrateBorder(prop, core) { + if (!BORDER_PROPS.has(prop)) return null; + const value = squash(core); + const next = value.replace(/rgba\(\s*0\s*,\s*0\s*,\s*0\s*,\s*[\d.]+\s*\)/g, 'var(--ua-color-border)'); // 空格宽容,避免只认一种写法 + if (next === value) return null; + return { kind: 'border-literal', value: next }; +} + /*** 作用:判定单条声明是否需要迁移,返回 { kind, value } 或 null(幂等:已是令牌就返回 null) ***/ export function migrateDeclaration(prop, rawValue) { const { core, tail } = splitImportant(rawValue); @@ -374,6 +412,8 @@ export function migrateDeclaration(prop, rawValue) { result = migrateLeading(core); } else if (prop === 'z-index') { result = migrateZ(core); + } else { + result = migrateBorder(prop, core); // 边框类:只收敛纯黑 rgba 边 } if (!result) return null; return { kind: result.kind, value: result.value + tail }; @@ -441,10 +481,16 @@ function detect(prop, rawValue, key) { if (value === '1' || value === '2') return 0; // 组件内微排序(表头压行、水印叠层、头像悬停),不是全局层级 return /^\d+$/.test(value) ? 1 : 0; } + if (key === 'border-literal') { + if (!BORDER_PROPS.has(prop)) return 0; + if (value === 'none' || value === '0' || value === 'inherit') return 0; // 无边框与继承不算色值漂移 + const stripped = value.replace(/var\(--ua-[\w-]+\)/g, ' '); // 已是令牌的色先剔除 + return /(#[0-9a-f]{3,8}\b|rgba?\(|hsla?\()/i.test(stripped) ? 1 : 0; + } return 0; } -/*** 作用:扫描整段 CSS,返回五类硬编码的计数与命中行号(审计命令的唯一统计口径) ***/ +/*** 作用:扫描整段 CSS,返回各类硬编码的计数与命中行号(审计命令的唯一统计口径) ***/ export function scanSource(src) { const code = blankComments(src); const lineAt = (offset) => { @@ -487,7 +533,7 @@ export function migrateSource(src) { return { text: out, counts }; } -/*** 作用:把五类计数拍平成「类别: 数量」数组,供审计报告与基线文件共用同一顺序 ***/ +/*** 作用:把各类计数拍平成「类别: 数量」数组,供审计报告与基线文件共用同一顺序 ***/ export function countRows(counts) { return KIND_META.map((meta) => ({ ...meta, count: counts[meta.key] ?? 0 })); } diff --git a/packages/tools/cli/ua.manifest.json b/packages/tools/cli/ua.manifest.json index cc4145f..7240c37 100644 --- a/packages/tools/cli/ua.manifest.json +++ b/packages/tools/cli/ua.manifest.json @@ -19,7 +19,7 @@ "ua style:注释规范门禁(导出必须 /*** ***/、接口字段行尾注释、行注释中文),违规退出码 1", "ua verify:清单一致性门禁(id 唯一 / exports 对应 / props 必带 n t d / steps≥3 / demo 锚点存在)", "ua commit-lint <文件|message>:提交信息门禁(type 枚举 / scope 规则 / subject 中文祈使 ≤50 字符),规范见 COMMITTING.md,违规退出码 1", - "ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里五类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数", + "ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数", "ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe" ], "keyTypes": [ @@ -39,9 +39,9 @@ "装好依赖后先跑 npm run check 确认基线全绿(build → demo → style → typecheck → smoke → index → verify → lint-iterations → audit:visual)", "查组件:ua list 看全量,ua search <关键词> 按 id/标题/参数名/参数说明/步骤模糊找(「处理中」这类说明里的词也能搜到,命中非 id 标题时会标出命中字段;拼错会回相近 id),ua info <组件id> 看 props/步骤/示例(ua info <包名> 看单包 api 与约定,加 --json 出原始清单)", "查配方:ua recipes 列全量(编号 / 标题 / 用到的组件),ua recipes <编号> 看骨架代码与要点(--json 给脚本用;MCP 里对应 list_recipes / get_recipe)", - "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset)", + "查令牌:ua tokens 打印全部 --ua-* 变量(颜色带暗色值),--json 输出结构化数组;写样式前先查,禁止硬编码。令牌分十一组:颜色 / 圆角 / 间距 / 字号 / 字重行高 / 边框 / 阴影 / 动效 / 焦点环 / 控件高度 / 层级,弹层阴影用 --ua-shadow-1..4,过渡用 --ua-duration-* + --ua-ease,焦点环用 --ua-focus-ring(_inset)", "改完清单跑 npm run docs:同时刷新人类文档站与 llms-full.txt(Agent 首选喂料)", - "改完样式跑 npm run audit:visual:五类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌", + "改完样式跑 npm run audit:visual:各类硬编码计数不得高于 .ua/visual-baseline.json(棘轮只减不增);字面量可用 npm run visual:migrate 幂等改写成令牌", "在 ~/.workbuddy/mcp.json 注册 ua mcp 后,Agent 可直接调 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe" ], "components": []