iter: d06 新增 ua verify 门禁(清单/文档/演示锚点一致性)
This commit is contained in:
@@ -96,6 +96,14 @@
|
|||||||
"acceptance": "新增 breadcrumb.ts:nav + ol 语义结构,末项自动为当前页(不可点、aria-current=page),条目有 href 渲染 <a>、无 href 渲染 button 走 onNavigate,分隔符可配;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
|
"acceptance": "新增 breadcrumb.ts:nav + ol 语义结构,末项自动为当前页(不可点、aria-current=page),条目有 href 渲染 <a>、无 href 渲染 button 走 onNavigate,分隔符可配;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
|
||||||
"status": "done",
|
"status": "done",
|
||||||
"notes": "API breadcrumb({ items: {label, href?}[], separator?, onNavigate? });分隔符 aria-hidden;reduced-motion 列表补 .ua-crumb-link;demo 新增 sec-breadcrumb('›' 分隔 + toast 反馈跳转);首轮 style 门禁拦下多行 /*** 注释(检查器只认紧邻单行块),已改单行"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-23T08:49:44.245Z",
|
"generatedAt": "2026-10-23T08:50:48.366Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -630,7 +630,8 @@
|
|||||||
"example": "badge({ label: '成功', variant: 'success' })",
|
"example": "badge({ label: '成功', variant: 'success' })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"选择语义色:default 中性、success 成功、warning 警告、danger 危险",
|
"选择语义色:default 中性、success 成功、warning 警告、danger 危险",
|
||||||
"传入 label 即可,纯展示组件无状态"
|
"传入 label 即可,纯展示组件无状态",
|
||||||
|
"状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -654,7 +655,8 @@
|
|||||||
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
|
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
|
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
|
||||||
"传入 message 即可,入场自带下滑动画"
|
"传入 message 即可,入场自带下滑动画",
|
||||||
|
"需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -770,7 +772,8 @@
|
|||||||
"example": "spinner(); spinner({ size: 26 })",
|
"example": "spinner(); spinner({ size: 26 })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"数据加载时插入 spinner(),加载完成移除",
|
"数据加载时插入 spinner(),加载完成移除",
|
||||||
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)"
|
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)",
|
||||||
|
"与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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: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: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 走 <a>、无 href 走 onNavigate,分隔符可配;manifest/llms/demo 同步,check 全绿(中途被 style 门禁拦下多行注释,已改单行)"}
|
{"time": "2026-10-23T16:49:45+08:00", "id": "d05", "result": "done", "summary": "新增面包屑 breadcrumb:nav/ol 语义结构 + 末项 aria-current,href 走 <a>、无 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 全绿"}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||||
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
- `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 查询组件库的三种方式
|
## Agent 查询组件库的三种方式
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
可用工具:`list_components` / `get_component` / `search_components`。
|
可用工具:`list_components` / `get_component` / `search_components`。
|
||||||
|
|
||||||
2. **CLI**:`node packages/tools/cli/bin/ua.js list | info <name> | index`
|
2. **CLI**:`node packages/tools/cli/bin/ua.js list | info <name> | index | docs | style | verify`
|
||||||
|
|
||||||
3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌,无图形纯文本,喂给 Agent 首选)**。
|
3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌,无图形纯文本,喂给 Agent 首选)**。
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @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)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -319,6 +319,7 @@ badge({ label: '成功', variant: 'success' })
|
|||||||
|
|
||||||
1. 选择语义色:default 中性、success 成功、warning 警告、danger 危险
|
1. 选择语义色:default 中性、success 成功、warning 警告、danger 危险
|
||||||
2. 传入 label 即可,纯展示组件无状态
|
2. 传入 label 即可,纯展示组件无状态
|
||||||
|
3. 状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-feedback
|
现场演示:apps/docs/demo.html#sec-feedback
|
||||||
|
|
||||||
@@ -339,6 +340,7 @@ alert({ message: '操作可能存在风险', variant: 'warning' })
|
|||||||
|
|
||||||
1. 按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误
|
1. 按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误
|
||||||
2. 传入 message 即可,入场自带下滑动画
|
2. 传入 message 即可,入场自带下滑动画
|
||||||
|
3. 需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-feedback
|
现场演示:apps/docs/demo.html#sec-feedback
|
||||||
|
|
||||||
@@ -429,6 +431,7 @@ spinner(); spinner({ size: 26 })
|
|||||||
|
|
||||||
1. 数据加载时插入 spinner(),加载完成移除
|
1. 数据加载时插入 spinner(),加载完成移除
|
||||||
2. 用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)
|
2. 用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)
|
||||||
|
3. 与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-feedback
|
现场演示:apps/docs/demo.html#sec-feedback
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -12,8 +12,9 @@
|
|||||||
"style": "node packages/tools/cli/bin/ua.js style",
|
"style": "node packages/tools/cli/bin/ua.js style",
|
||||||
"index": "node packages/tools/cli/bin/ua.js index",
|
"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",
|
"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",
|
"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"
|
"ua": "node packages/tools/cli/bin/ua.js",
|
||||||
|
"verify": "node packages/tools/cli/bin/ua.js verify"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.6.0"
|
"typescript": "^5.6.0"
|
||||||
|
|||||||
@@ -501,7 +501,8 @@
|
|||||||
"example": "badge({ label: '成功', variant: 'success' })",
|
"example": "badge({ label: '成功', variant: 'success' })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"选择语义色:default 中性、success 成功、warning 警告、danger 危险",
|
"选择语义色:default 中性、success 成功、warning 警告、danger 危险",
|
||||||
"传入 label 即可,纯展示组件无状态"
|
"传入 label 即可,纯展示组件无状态",
|
||||||
|
"状态语义靠 variant 承载,不要自造颜色;纯展示不可交互,需要操作请配 button"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -525,7 +526,8 @@
|
|||||||
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
|
"example": "alert({ message: '操作可能存在风险', variant: 'warning' })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
|
"按信息级别选 variant:info 提示、success 成功、warning 警告、danger 错误",
|
||||||
"传入 message 即可,入场自带下滑动画"
|
"传入 message 即可,入场自带下滑动画",
|
||||||
|
"需要可关闭或带操作按钮时,外层用 h() 组合 button 自行拼装,alert 只负责语义底色"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -641,7 +643,8 @@
|
|||||||
"example": "spinner(); spinner({ size: 26 })",
|
"example": "spinner(); spinner({ size: 26 })",
|
||||||
"steps": [
|
"steps": [
|
||||||
"数据加载时插入 spinner(),加载完成移除",
|
"数据加载时插入 spinner(),加载完成移除",
|
||||||
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)"
|
"用 size 匹配上下文尺寸(按钮内用小号,整页 loading 用大号)",
|
||||||
|
"与 skeleton 分工:已知内容结构用 skeleton 占位,只有『在跑』的短等待用 spinner"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -560,6 +560,47 @@ function cmdStyle() {
|
|||||||
console.log(`style OK (${files.length} files)`);
|
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);
|
const [cmd, ...args] = process.argv.slice(2);
|
||||||
@@ -583,10 +624,13 @@ switch (cmd) {
|
|||||||
case 'style':
|
case 'style':
|
||||||
cmdStyle();
|
cmdStyle();
|
||||||
break;
|
break;
|
||||||
|
case 'verify':
|
||||||
|
cmdVerify();
|
||||||
|
break;
|
||||||
case 'mcp':
|
case 'mcp':
|
||||||
cmdMcp();
|
cmdMcp();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('usage: ua <index|list|info <name>|docs|style|mcp>');
|
console.log('usage: ua <index|list|info <name>|docs|style|verify|mcp>');
|
||||||
process.exit(cmd ? 1 : 0);
|
process.exit(cmd ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user