From 582318adf10fc06493bfbe0ad1bfbec654295aa6 Mon Sep 17 00:00:00 2001 From: ui-arch-bot Date: Fri, 23 Oct 2026 16:50:49 +0800 Subject: [PATCH] =?UTF-8?q?iter:=20d06=20=E6=96=B0=E5=A2=9E=20ua=20verify?= =?UTF-8?q?=20=E9=97=A8=E7=A6=81=EF=BC=88=E6=B8=85=E5=8D=95/=E6=96=87?= =?UTF-8?q?=E6=A1=A3/=E6=BC=94=E7=A4=BA=E9=94=9A=E7=82=B9=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ua/backlog.json | 8 ++++++ .ua/index.json | 11 +++++--- .ua/iterations.log | 1 + AGENTS.md | 4 +-- llms-full.txt | 5 +++- package.json | 5 ++-- packages/skin/ui/ua.manifest.json | 9 ++++-- packages/tools/cli/bin/ua.js | 46 ++++++++++++++++++++++++++++++- 8 files changed, 76 insertions(+), 13 deletions(-) diff --git a/.ua/backlog.json b/.ua/backlog.json index d11d8c7..79871d5 100644 --- a/.ua/backlog.json +++ b/.ua/backlog.json @@ -96,6 +96,14 @@ "acceptance": "新增 breadcrumb.ts:nav + ol 语义结构,末项自动为当前页(不可点、aria-current=page),条目有 href 渲染 、无 href 渲染 button 走 onNavigate,分隔符可配;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿", "status": "done", "notes": "API breadcrumb({ items: {label, href?}[], separator?, onNavigate? });分隔符 aria-hidden;reduced-motion 列表补 .ua-crumb-link;demo 新增 sec-breadcrumb('›' 分隔 + toast 反馈跳转);首轮 style 门禁拦下多行 /*** 注释(检查器只认紧邻单行块),已改单行" + }, + { + "id": "d06", + "title": "门禁新增 ua verify:清单 ↔ 文档 ↔ 演示锚点一致性校验", + "motivation": "新增组件靠人工同步 manifest/docs/demo,易漏(本轮已出现 badge/alert/spinner 手把手只有 2 步);把不变量机器化,防回归", + "acceptance": "CLI 新增 verify 子命令:docs.components 的 id 唯一且 api.exports 有 `${id}(` 或以 `${id}.` 开头条目、props 条目必备 n/t/d、steps ≥3、example 必填、demo 锚点必须存在于 apps/demo/main.ts;纳入 npm run check 链条;AGENTS.md 同步;npm run check 全绿", + "status": "done", + "notes": "锚点校验改看演示源码而非打包产物(demo.html 是 esbuild 产物,锚点在运行时生成);顺带补齐 badge/alert/spinner 的第 3 条手把手(说明与 button/skeleton 的分工);check 链变为 build→style→typecheck→smoke→index→verify" } ] } diff --git a/.ua/index.json b/.ua/index.json index 169713c..7f3ec4d 100644 --- a/.ua/index.json +++ b/.ua/index.json @@ -1,6 +1,6 @@ { "$schema": "ui-arch-registry-index/0.1", - "generatedAt": "2026-10-23T08:49:44.245Z", + "generatedAt": "2026-10-23T08:50:48.366Z", "repoRoot": ".", "packages": [ { @@ -630,7 +630,8 @@ "example": "badge({ label: '成功', variant: 'success' })", "steps": [ "选择语义色:default 中性、success 成功、warning 警告、danger 危险", - "传入 label 即可,纯展示组件无状态" + "传入 label 即可,纯展示组件无状态", + "状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button" ] }, { @@ -654,7 +655,8 @@ "example": "alert({ message: '操作可能存在风险', variant: 'warning' })", "steps": [ "按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误", - "传入 message 即可,入场自带下滑动画" + "传入 message 即可,入场自带下滑动画", + "需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色" ] }, { @@ -770,7 +772,8 @@ "example": "spinner(); spinner({ size: 26 })", "steps": [ "数据加载时插入 spinner(),加载完成移除", - "用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)" + "用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)", + "与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner" ] }, { diff --git a/.ua/iterations.log b/.ua/iterations.log index 0ab4210..941a2d5 100644 --- a/.ua/iterations.log +++ b/.ua/iterations.log @@ -10,3 +10,4 @@ {"time": "2026-10-23T16:47:56+08:00", "id": "d03", "result": "done", "summary": "新增折叠面板 accordion:单开/多开 + active 受控 + 禁用面板,grid 行高 0fr→1fr 过渡免测量,aria 关联完整;manifest/llms/demo 同步,check 全绿"} {"time": "2026-10-23T16:48:36+08:00", "id": "d04", "result": "done", "summary": "dataTable 新增 loading(布尔|信号):表体换骨架行、筛选与翻页禁用、aria-busy;demo 加开关,manifest/llms 同步,check 全绿"} {"time": "2026-10-23T16:49:45+08:00", "id": "d05", "result": "done", "summary": "新增面包屑 breadcrumb:nav/ol 语义结构 + 末项 aria-current,href 走 、无 href 走 onNavigate,分隔符可配;manifest/llms/demo 同步,check 全绿(中途被 style 门禁拦下多行注释,已改单行)"} +{"time": "2026-10-23T16:50:49+08:00", "id": "d06", "result": "done", "summary": "新增 ua verify 门禁:清单 id/exports/props/steps/example 与 demo 锚点一致性,纳入 npm run check;顺带补齐 badge/alert/spinner 手把手第三条,check 全绿"} diff --git a/AGENTS.md b/AGENTS.md index b9072b0..efae679 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ - `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html - `apps/docs`:文档站,纯产物(ua docs 生成) -**门禁一条龙**:`npm run check` = style + typecheck + smoke + index,提交前必过。 +**门禁一条龙**:`npm run check` = build + style + typecheck + smoke + index + verify,提交前必过。`ua verify` 校验清单 ↔ 文档 ↔ 演示锚点一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、demo 锚点必须在 `apps/demo/main.ts` 中存在)。 ## Agent 查询组件库的三种方式 @@ -46,7 +46,7 @@ 可用工具:`list_components` / `get_component` / `search_components`。 -2. **CLI**:`node packages/tools/cli/bin/ua.js list | info | index` +2. **CLI**:`node packages/tools/cli/bin/ua.js list | info | index | docs | style | verify` 3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌,无图形纯文本,喂给 Agent 首选)**。 diff --git a/llms-full.txt b/llms-full.txt index f4fab03..427f5a3 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,6 +1,6 @@ # @ui-arch 组件库 Agent 文档(无图形版) -> 自动生成于 2026-10-23T08:49:44.245Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 +> 自动生成于 2026-10-23T08:50:48.366Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 ## 快速上手 @@ -319,6 +319,7 @@ badge({ label: '成功', variant: 'success' }) 1. 选择语义色:default 中性、success 成功、warning 警告、danger 危险 2. 传入 label 即可,纯展示组件无状态 +3. 状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button 现场演示:apps/docs/demo.html#sec-feedback @@ -339,6 +340,7 @@ alert({ message: '操作可能存在风险', variant: 'warning' }) 1. 按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误 2. 传入 message 即可,入场自带下滑动画 +3. 需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色 现场演示:apps/docs/demo.html#sec-feedback @@ -429,6 +431,7 @@ spinner(); spinner({ size: 26 }) 1. 数据加载时插入 spinner(),加载完成移除 2. 用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号) +3. 与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner 现场演示:apps/docs/demo.html#sec-feedback diff --git a/package.json b/package.json index aaad274..7696a19 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "style": "node packages/tools/cli/bin/ua.js style", "index": "node packages/tools/cli/bin/ua.js index", "docs": "node packages/tools/cli/bin/ua.js index && node packages/tools/cli/bin/ua.js docs", - "check": "npm run build && npm run style && npm run typecheck && npm run smoke && npm run index", - "ua": "node packages/tools/cli/bin/ua.js" + "check": "npm run build && npm run style && npm run typecheck && npm run smoke && npm run index && npm run verify", + "ua": "node packages/tools/cli/bin/ua.js", + "verify": "node packages/tools/cli/bin/ua.js verify" }, "devDependencies": { "typescript": "^5.6.0" diff --git a/packages/skin/ui/ua.manifest.json b/packages/skin/ui/ua.manifest.json index 5fd5859..ec0bcfb 100644 --- a/packages/skin/ui/ua.manifest.json +++ b/packages/skin/ui/ua.manifest.json @@ -501,7 +501,8 @@ "example": "badge({ label: '成功', variant: 'success' })", "steps": [ "选择语义色:default 中性、success 成功、warning 警告、danger 危险", - "传入 label 即可,纯展示组件无状态" + "传入 label 即可,纯展示组件无状态", + "状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button" ] }, { @@ -525,7 +526,8 @@ "example": "alert({ message: '操作可能存在风险', variant: 'warning' })", "steps": [ "按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误", - "传入 message 即可,入场自带下滑动画" + "传入 message 即可,入场自带下滑动画", + "需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色" ] }, { @@ -641,7 +643,8 @@ "example": "spinner(); spinner({ size: 26 })", "steps": [ "数据加载时插入 spinner(),加载完成移除", - "用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)" + "用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)", + "与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner" ] }, { diff --git a/packages/tools/cli/bin/ua.js b/packages/tools/cli/bin/ua.js index 6ff5502..e7c5b0b 100644 --- a/packages/tools/cli/bin/ua.js +++ b/packages/tools/cli/bin/ua.js @@ -560,6 +560,47 @@ function cmdStyle() { console.log(`style OK (${files.length} files)`); } +/*** 清单一致性校验:组件文档块的 id / props 说明 / demo 锚点必须与代码和演示产物对齐 ***/ +function cmdVerify() { + const issues = []; + const demoPath = path.join(ROOT, 'apps', 'demo', 'main.ts'); // 演示区块定义在源码里(产物是打包后的 JS) + const demoSource = fs.existsSync(demoPath) ? fs.readFileSync(demoPath, 'utf8') : ''; + const anchors = new Set(demoSource.match(/sec-[a-z0-9-]+/g) ?? []); // 已定义的区块锚点 + if (!demoSource) console.log('verify: 未找到 apps/demo/main.ts,跳过 demo 锚点校验'); + + for (const pkg of collectManifests()) { + const comps = pkg.docs?.components ?? []; // 清单已被 collectManifests 摊平 + const exports = pkg.api?.exports ?? []; + if (comps.length && !exports.length) issues.push(`${pkg.name}: docs.components 有 ${comps.length} 项但 api.exports 为空`); + + const seen = new Set(); // 组件 id 去重(同一清单内不允许重复) + for (const c of comps) { + const tag = `${pkg.name}#${c.id ?? '?'}`; + if (!c.id || !c.title) issues.push(`${tag}: 缺少 id 或 title`); + if (seen.has(c.id)) issues.push(`${tag}: id 重复`); + seen.add(c.id); + if (!c.example) issues.push(`${tag}: 缺少 example`); + if ((c.steps ?? []).length < 3) issues.push(`${tag}: steps 少于 3 条`); + if (!exports.some((e) => e.startsWith(`${c.id}(`) || e.startsWith(`${c.id}.`))) { + issues.push(`${tag}: api.exports 中没有以「${c.id}(」或「${c.id}.」开头的条目`); + } + for (const prop of c.props ?? []) { + if (!prop.n || !prop.t || !prop.d) issues.push(`${tag}: props 条目 ${prop.n ?? '?'} 缺 n/t/d`); + } + if (c.demo && demoSource && !anchors.has(c.demo)) { + issues.push(`${tag}: demo 锚点 ${c.demo} 在 apps/demo/main.ts 中不存在(缺对应 section)`); + } + } + } + + if (issues.length) { + for (const it of issues) console.error('verify: ' + it); + console.error(`${issues.length} violation(s)`); + process.exit(1); + } + console.log('verify OK (manifest ↔ docs ↔ demo 一致)'); +} + // ---- 入口 ---- const [cmd, ...args] = process.argv.slice(2); @@ -583,10 +624,13 @@ switch (cmd) { case 'style': cmdStyle(); break; + case 'verify': + cmdVerify(); + break; case 'mcp': cmdMcp(); break; default: - console.log('usage: ua |docs|style|mcp>'); + console.log('usage: ua |docs|style|verify|mcp>'); process.exit(cmd ? 1 : 0); }