agentcss 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/01-tokens/README.md +1 -1
  2. package/01-tokens/page.css +11 -0
  3. package/01-tokens/responsive-demo.html +1 -1
  4. package/01-tokens/tokens.core.css +1 -1
  5. package/01-tokens/tokens.css +2 -2
  6. package/02-primitives/date-range/date-range.js +1 -1
  7. package/02-primitives/timeline/demo.html +1 -1
  8. package/03-patterns/drawer/demo.html +1 -1
  9. package/03-patterns/overlay/overlay.css +1 -1
  10. package/03-patterns/states/states.css +1 -1
  11. package/03-patterns/tabs/tabs.css +9 -0
  12. package/04-recipes/app-shell/README.md +1 -1
  13. package/04-recipes/app-shell/demo.html +1 -1
  14. package/04-recipes/longform/content.html +6 -6
  15. package/04-recipes/report/demo.html +2 -2
  16. package/04-recipes/table/README.md +1 -1
  17. package/04-recipes/table/demo.html +1 -1
  18. package/09-assets/bar/bar.css +1 -1
  19. package/09-assets/model-viewer/model-viewer.js +1 -1
  20. package/AGENTS.md +4 -2
  21. package/CHANGELOG.md +126 -5
  22. package/CONTRIBUTING.md +44 -3
  23. package/README.md +7 -4
  24. package/README.zh-CN.md +18 -12
  25. package/START-HERE.md +12 -5
  26. package/ai/START-HERE.md +41 -7
  27. package/ai/cdp.js +152 -0
  28. package/ai/check-html.js +658 -0
  29. package/ai/cli.js +578 -70
  30. package/ai/components.json +1 -1
  31. package/ai/pdf-renderer.js +177 -0
  32. package/ai/pdf.js +125 -80
  33. package/ai/recipes.json +86 -0
  34. package/ai/scaffold.js +195 -0
  35. package/ai/shot.js +237 -0
  36. package/ai/validate.js +65 -6
  37. package/dist/01-tokens/page.css +1 -1
  38. package/dist/03-patterns/tabs/tabs.css +1 -1
  39. package/dist/COSTS.md +12 -12
  40. package/docs/ACCESSIBILITY.md +2 -2
  41. package/docs/ARCHITECTURE.md +116 -0
  42. package/docs/LAYER-BASELINE.md +2 -2
  43. package/docs/PRINT.md +41 -13
  44. package/docs/RELEASE-PROCESS.md +52 -2
  45. package/docs/STABILITY.md +1 -1
  46. package/docs/VERIFY-RELEASE.md +17 -0
  47. package/index.html +3 -3
  48. package/llms-full.txt +19 -4
  49. package/llms.txt +29 -6
  50. package/package.json +2 -1
@@ -0,0 +1,116 @@
1
+ # agentcss 是怎么组成的
2
+
3
+ > 写给第一次接触这个项目的人。不需要先懂前端工程,读完能回答三个问题:
4
+ > 这是什么、东西怎么组织、靠什么保证不出乱子。
5
+
6
+ **一句话**:这是一个给手机网页用的**现成零件库**——按钮、表格、抽屉、整页版式都有,
7
+ 拿来就能用。它有三个少见的特点:
8
+
9
+ - **不装任何依赖**。把这个库装进项目,你的 `node_modules` 不会多出任何别的东西。
10
+ - **类名就是说明书**。样式全靠类名控制,不要求写初始化脚本、不绑定任何框架。
11
+ - **专门照顾 AI 编码助手**。每个组件都有一份机器可读的"说明书",AI 先读它再写代码,
12
+ 写完还能用配套命令自己检查。
13
+
14
+ 规模:40 个组件(全部稳定可用)、约 1 MB 的 npm 包、全库样式总量锁死在 172 KB 以内。
15
+
16
+ ## 一张图看懂
17
+
18
+ ```mermaid
19
+ flowchart TD
20
+ subgraph lib["组件本体 —— 依赖自上而下"]
21
+ direction TB
22
+ R["整页配方 recipes · 5 个<br/>拿来就是一页完整版式"]
23
+ P["组合模式 patterns · 14 个<br/>抽屉、弹层、时间线…"]
24
+ B["基础件 primitives · 17 个<br/>按钮、表格、下拉框…"]
25
+ T["设计令牌 tokens + 行为核 · 6 个<br/>颜色字号间距 + 少量交互脚本"]
26
+ R --> P --> B --> T
27
+ end
28
+
29
+ subgraph use["两种用法"]
30
+ H["给人:复制 demo 改内容"]
31
+ A["给 AI:先读机器说明书,写完自检"]
32
+ end
33
+
34
+ subgraph out["三个出口"]
35
+ NPM["npm 包"]
36
+ WEB["文档站(40 个组件页)"]
37
+ REPO["GitHub 仓库(检查系统全部公开)"]
38
+ end
39
+
40
+ H --> NPM
41
+ A --> NPM
42
+ NPM -.-> WEB
43
+ NPM -.-> REPO
44
+ ```
45
+
46
+ 读法:想用一个小按钮,拿 `02-primitives` 里的就行;想要一整页报表,直接拿
47
+ `04-recipes`。上面的层会用到下面的层,反过来不会——所以拆哪儿都拆得动。
48
+
49
+ ## 四层组件,像盖房子
50
+
51
+ | 层 | 是什么 | 打个比方 | 数量 |
52
+ |---|---|---|---|
53
+ | `01-tokens` | 颜色、字号、间距这些"数值表",浅色/深色两套主题;附 6 个小交互脚本(弹窗关闭、焦点归位这类) | 调色盘和刻度尺 | 1 套 |
54
+ | `02-primitives` | 最小的独立组件:按钮、卡片、表格、下拉框 | 砖和预制板 | 17 |
55
+ | `03-patterns` | 需要多个组件配合的场景:抽屉、弹层、标签页、时间线 | 预制房间 | 14 |
56
+ | `04-recipes` | 一整页的成品版式:报表页、应用外壳、财务页 | 样板房 | 5 |
57
+
58
+ 另有 `09-assets`(4 个独立小资产,比如图表底座),不属于这套分层,按需取用。
59
+
60
+ ## 目录地图
61
+
62
+ | 目录 | 放什么 |
63
+ |---|---|
64
+ | `01-tokens/` … `04-recipes/`、`09-assets/` | 上面说的组件本体,每个组件自带可打开的 demo 页和说明 |
65
+ | `ai/` | 给 AI 看的机器说明书(4 份 JSON)和一个命令行工具 |
66
+ | `05-audit/` | 自动检查系统(下节讲),全部公开、任何人可以运行 |
67
+ | `docs/` | 设计与工程文档(本文在这里) |
68
+ | `dist/`、`esm/`、`types/` | 构建产物,由源码自动生成,不许手改 |
69
+
70
+ ## 修改一处,处处自动跟上
71
+
72
+ 这个项目最讲究的一件事:**同一份数据只存一个地方,别的都由它自动生成。**
73
+
74
+ - 40 个组件的清单(类名、状态、无障碍属性)只写在 `ai/components.json` 一处。
75
+ 文档站的 46 个页面、给 AI 的摘要文本,全部由这份清单自动生成——
76
+ 有一个自动检查专门盯着"生成物和清单是否一致",改了清单忘了重新生成就会红。
77
+ - 关键数字同理:文档里写的组件数、检查数,都有自动检查对着源头核。
78
+
79
+ 好处很直接:文档不会和实现"各说各话",因为大部分文档本来就是实现生成的。
80
+
81
+ ## 怎么保证不出乱子:一个自动验收系统
82
+
83
+ 仓库里有一套自动检查(这个项目里叫"门禁"),共 **251 项**,像三道验收:
84
+
85
+ | 什么时候跑 | 跑多少 | 多快 | 查什么 |
86
+ |---|---|---|---|
87
+ | 每次提交前 | 5 项 | 约 7 秒 | 说明书、页面结构、文案、类型有没有被改坏 |
88
+ | 每次推送时 | 128 项 | 约 3.5 分钟 | 以上全部,外加所有不需要浏览器的检查 |
89
+ | 发布之前 | 251 项 | 约 10 分钟 | 再加真浏览器实测、逐像素视觉比对、安装包体检 |
90
+
91
+ 三条规矩让它长期可信:
92
+
93
+ 1. **每个检查先证明自己能抓到问题**——故意弄坏一个组件,检查必须变红;
94
+ 把检查关掉后问题必须重新出现。做不到这一点的检查不算数。
95
+ 2. **"跳过"不等于"通过"**——环境缺浏览器就明确标"本次没查",绝不悄悄变绿。
96
+ 3. **没测过就说没测**——真机只在 5 类环境验证过,其余内核明说"未验证",不写"应该没问题"。
97
+
98
+ ## 给 AI 编码助手怎么用
99
+
100
+ 机器说明书在 `ai/` 目录,四份 JSON 覆盖:每个组件的类名与状态、已知的坑、
101
+ 设计数值、交互脚本的行为。配套命令行工具(装完这个包就能用):
102
+
103
+ ```bash
104
+ npx agentcss ai components --json # 列出全部 40 个组件
105
+ npx agentcss ai check page.html --json # 检查你写出来的页面合不合规范
106
+ npx agentcss ai shot page.html --width=390,1440 # 按真实手机宽度截图
107
+ npx agentcss ai scaffold report/finance # 生成一页报表的最小骨架
108
+ ```
109
+
110
+ 流程就是:**读说明书 → 出骨架 → 填内容 → 自己检查**。最后一步不是可选项——
111
+ "写完了"和"写对了"是两件事。
112
+
113
+ ---
114
+
115
+ *本文数字的出处:组件数来自 `ai/components.json`;检查项数以 `05-audit/check-all.sh`
116
+ 实际注册为准;样式预算见 `docs/BUDGET.md`。均为 2026-11-05 的实测值。*
@@ -34,7 +34,7 @@
34
34
  不是手抄的,也不是推测的:探针页把结果 **POST 回采集端**,落成 JSON 后原样贴进表格。
35
35
  手机访问 `http://<电脑内网地址>:端口` 属于**非安全上下文**,
36
36
  `navigator.clipboard` 在那里根本不存在 —— 所以页面提供的是"发送到电脑"(同源 POST)作为主路径,
37
- 剪贴板只作备用(详见 `ERRORS.md` E47)。
37
+ 剪贴板只作备用。
38
38
 
39
39
  ---
40
40
 
@@ -163,7 +163,7 @@ Android 上 Chrome ≠ 系统 WebView ≠ 各家 App 内置 WebView。**入口
163
163
 
164
164
  「iOS 16.4 才支持某个特性」这类说法在本文件里没有位置,原因很直接:
165
165
  我没有在真机上验证过它,写下来就会被下一个人当成事实引用
166
- (`ERRORS.md` E13 记过一类同源事故:文档里的结论没人回来复核,最后整份文档都在骗人)。
166
+ (一类同源事故:文档里的结论没人回来复核,最后整份文档都在骗人)。
167
167
 
168
168
  ⇒ 需要版本结论时,**跑一次探针页**,用那一次的 UA 与结果说话。
169
169
  UA 是证据,版本传闻不是。
package/docs/PRINT.md CHANGED
@@ -258,9 +258,9 @@ node 05-audit/print-gate.js --pdf 某文件.pdf
258
258
  | `print-content-audit.js` | 该印的在不在?(屏幕可见 → 纸上消失?) | 它只在浏览器里量,不读 PDF |
259
259
  | `agentcss pdf` + `pdf-export-gate.js` | **导出成文件**这条路能用吗?(§八) | 它不做视觉比对 |
260
260
 
261
- 前四个判的是"浏览器里按 Ctrl+P",最后一个判的是"导出一份文件" ——
262
- **这是两条不同的渲染路径**(CDP `page.pdf()` vs Chrome 命令行 `--print-to-pdf`),
263
- `@page` 的来源与底色默认都不同。所以前者全绿推不出后者能用,两边各有各的判据。
261
+ 前四个判的是"浏览器里按 Ctrl+P",最后一个判的是"导出一份文件"。两者都走 CDP,
262
+ 但 `@page` 来源、就绪条件、底色参数与输出保护不同;打印策略全绿推不出导出链没有
263
+ 旧文件假成功或异步漏内容,所以两边各有各的判据。
264
264
 
265
265
  `printPolicy` 是**有限枚举**,每个值对应一组能在浏览器里量的断言:
266
266
 
@@ -291,8 +291,8 @@ L3 声明→生效 在真浏览器的 print 媒体下量标本页的计算值
291
291
  ```
292
292
 
293
293
  反向控制 7 条(覆盖四条腿),其中两条是历史事故的**逐字复刻**:
294
- E254(去掉 `.tabs ` 前缀,让组件 CSS 同特异性盖掉它)与
295
- E255(只删 `visibility: visible`,display 与高度全对、一个字不印)。
294
+ 去掉 `.tabs ` 前缀,让组件 CSS 同特异性盖掉它;以及
295
+ 只删 `visibility: visible`,display 与高度全对、一个字不印。
296
296
 
297
297
  ### 内容完整性是**硬判据**(2026-11-02 起)
298
298
 
@@ -352,20 +352,36 @@ node 05-audit/print-policy-gate.js --selftest # 反向控制
352
352
  npx agentcss pdf report.html --out report.pdf # 默认 A4 / 18mm 15mm
353
353
  npx agentcss pdf report.html --out report.pdf --paper=letter --margin="20mm"
354
354
  npx agentcss pdf report.html --out report.pdf --max-pages=8
355
+ npx agentcss pdf report.html --out report.pdf --ready-selector="#report-ready" --wait-ms=0
355
356
  ```
356
357
 
357
358
  `--paper` 支持 `a4` / `letter` / `a5` / `a3` / `legal`。边距是 CSS 简写
358
359
  (`18mm`、`"18mm 15mm"`、四段都认)。这两个参数靠**注入一条 `@page` 规则**实现,
359
- 注入位置在使用者样式之后 —— 否则同特异性会被盖掉(E254 的同一个病)。
360
+ 注入位置在使用者样式之后 —— 否则同特异性会被盖掉(同一个病)。
360
361
 
361
- 零运行时依赖:用使用者机器上**已装好的** Chrome / Chromium / Edge。
362
- 找不到就明确报错(退出码 3),不静默降级成空文件。指定位置:
362
+ 零运行时依赖:用使用者机器上**已装好的** Chrome / Chromium / Edge,经共享 CDP
363
+ 管道调用 `Page.printToPDF`,不打包 Puppeteer。找不到浏览器明确报错(退出码 3):
363
364
 
364
365
  ```bash
365
366
  npx agentcss pdf report.html --out report.pdf --chrome="C:/Program Files/Google/Chrome/Application/chrome.exe"
366
367
  # 或环境变量 AGENTCSS_CHROME / CHROME_PATH
367
368
  ```
368
369
 
370
+ #### 就绪、失败与旧文件保护
371
+
372
+ 导出顺序是:页面 `load` → `document.fonts.ready` → 可选可见元素 → 额外等待 →
373
+ 固定动画终态 → PDF。`--wait-ms` 默认 `800`,可设 `0`,上限 `60000`;它只是额外缓冲,
374
+ **不是任意异步数据已经完成的证明**。动态报告应在数据与图表都渲染完后显示一个元素,
375
+ 再传 `--ready-selector="#report-ready"`;只存在但 `display:none`、不可见或没有尺寸不算就绪。
376
+
377
+ 整个导出默认有 60 秒总预算,超时退出码为 `4`。非法 CSS 选择器、空值与非法等待
378
+ 都是用法错误(退出码 `2`)。未捕获的页面 JS 异常会放进 JSON 的 `jsErrors` 供复核,
379
+ 普通 `console.error` 不会擅自让导出失败。
380
+
381
+ 浏览器只写暂存 PDF;确认本次成功后才原子替换输出文件。浏览器崩溃、就绪超时、
382
+ 非法选择器或产物校验失败时,**已有 PDF 保持不变,结论明确为失败**。超页数预算不同:
383
+ 完整的新 PDF 会保留,退出码 `5`,由作者决定如何拆改,绝不静默截断。
384
+
369
385
  ### 8.2 页数预算:超了报错,不替你截断
370
386
 
371
387
  ```bash
@@ -422,8 +438,8 @@ npx agentcss ai check doc.json --medium=paper
422
438
  ### 8.5 这条路谁守着
423
439
 
424
440
  ```bash
425
- node 05-audit/pdf-export-gate.js # E1–E12 + 三条反向,约 11s,不需要静态服务
426
- node 05-audit/pdf-export-gate.js --selftest # 只跑"必须红"的几条,约 0.3s
441
+ node 05-audit/pdf-export-gate.js # E1–E20,不需要静态服务,约 40s
442
+ node 05-audit/pdf-export-gate.js --selftest # 旧文件保护 / 超时 / 崩溃结论 / 非法参数
427
443
  ```
428
444
 
429
445
  | 判据 | 它问什么 | 实测抓到过什么 |
@@ -436,15 +452,27 @@ node 05-audit/pdf-export-gate.js --selftest # 只跑"必须红"的几条,约 0
436
452
  | E7 | `--margin` 真的影响页数吗 | 不注入 `@page` 时 60mm 与 10mm 页数相同 |
437
453
  | E8/E9″ | 没有浏览器时说人话吗;报错回显原始输入吗 | 退出码不对 / `abc` 被报成 `null` |
438
454
  | E11 | 产物本身合格吗(字体链 / 每页有文本) | 交给 `print-gate --pdf` 的分析器判,不重复实现解析 |
455
+ | E13 | 页树 `/Count` 与 `/Type /Page` 数一致吗 | 分段页树按"第一个"取 ⇒ 50 页读成 8 页 |
456
+ | E14 | ready-selector 等到的是内容,不只是耗时吗 | 延迟新增行的文字 runs 必须增加(48 → 143) |
457
+ | E15 | 真实金融长文每页都有内容吗 | 透明面板与全空白尾页;现为 7 页、每页有文字 |
458
+ | E16 | 浏览器失败会冒用旧 PDF 吗 | 旧文件 SHA-256 必须不变,结论必须失败 |
459
+ | E17 | 就绪条件永不出现会无限等吗 | 有界 timeout,且不碰旧文件 |
460
+ | E18 | 新参数写错会静默按默认值导出吗 | 非法 wait / 空值 / 非法 CSS 都是退出码 2 |
461
+ | E19 | 浏览器起不来时给的是结论还是堆栈 | 去掉管道 error 监听 ⇒ 进程以 EPIPE 崩溃 |
462
+ | E20 | 强杀留下的临时 HTML 会一直堆着吗 | 下次导出清掉 6 小时前的残留,刚生成的不碰 |
439
463
 
440
464
  🔴 E3 与 E7 是同一件事的两面:`@page` 注入是这套东西唯一的技术前提。
441
465
  把它改坏,门禁必须红 —— 实测过:注释掉注入那行,E3 报
442
466
  `a4=612×792pt`(回落成 Letter),E7 报 `10mm=2 页 / 60mm=2 页`。
443
467
 
468
+ E14–E20 则保证“导出链说成功”与“这份产物真是本次、完整、可用”不会脱节。
469
+
444
470
  ### 8.6 不覆盖什么
445
471
 
446
472
  - **远程 URL 不注入 `@page`**:拿不到远程页面的字节,硬注入等于假装改了它。
447
473
  这类导出会明确打印"(远程页面:未注入 @page)",纸幅由页面自己的 CSS 决定。
448
- - **视觉正确**:底色位置、边框粗细、图片裁切 —— 要栅格化比对,没做。
449
- - **页眉页脚 / 页码**:`--no-pdf-header-footer` 已关掉 Chrome 的默认页眉页脚,
450
- 但本工具不生成自己的。需要页码请用 `@page` + `counter(page)` 自己写。
474
+ - **跨机器像素基线**:库内不把字体光栅化差异当成回归;正式交付仍需对实际 PDF
475
+ 逐页栅格化,检查空白、裁切、边框与图表,并做一次实体打印打样。
476
+ - **页眉页脚 / 页码**:导出器显式 `displayHeaderFooter:false`,本工具不生成自己的。
477
+ 需要页码时用浏览器打印对话框的页眉页脚选项,或在自有 CDP/Puppeteer 层提供
478
+ `headerTemplate` / `footerTemplate`;不要假设 `@page + counter(page)` 在 Chrome 可用。
@@ -132,7 +132,7 @@ pack-smoke.sh # npm pack → 装进空项目 → 解析入口
132
132
 
133
133
  工作流按这个顺序落地:
134
134
 
135
- 1. 校验 tag 名 == `package.json.version`,检查 `NPM_TOKEN`;
135
+ 1. 校验 tag 名 == `package.json.version`,并确认 npm 足够新(OIDC 需 ≥ 11.5.1);
136
136
  2. `npm ci`;
137
137
  3. **probe**(只读):输出 `npm=… release=… provenance=…`;
138
138
  4. `publish:check`;
@@ -156,7 +156,7 @@ pack-smoke.sh # npm pack → 装进空项目 → 解析入口
156
156
 
157
157
  ⚠️ 第 8 步的回执必须**等 registry 索引**。`npm publish` 返回 0 之后,版本端点还要几十秒才可见;
158
158
  回执在发布后立刻跑,会拿到 `npm=absent` 并把一次成功的发布报成「未完成」。
159
- `release-run.sh verify` 现在对 `absent` 重试(默认 18 次 × 10 秒),
159
+ `release-run.sh verify` 现在对 `absent` 重试(默认 42 次 × 10 秒 = 420 秒),
160
160
  对 `unreadable` **不重试**(那是接口/网络问题,等下去也不会变)。
161
161
 
162
162
  这条不是推演,是 `v0.24.2` 第三次运行的现场:npm 与 Release 都建好了,
@@ -164,6 +164,56 @@ pack-smoke.sh # npm pack → 装进空项目 → 解析入口
164
164
  registry `integrity` 与 attestation `subject` 全部一致——产物是对的,
165
165
  错的是**回执比索引快**。
166
166
 
167
+ ⚠️ 预算必须**明显大于**实测最大值,不能贴着它设。`v0.26.0`(2026-11-05)是第二次:
168
+ `npm publish` 在 `08:15:34` 返回成功,registry 到 `08:18:40` 才索引到——**186 秒**,
169
+ 而当时的上界是 `17 × 10 = 170` 秒,回执在 `08:18:38` 放弃,比真相早 2 秒。
170
+ 后果:包已经发出去了,回执却写 `npm=absent`,要再跑一遍 14 分钟的门禁才能拿到绿。
171
+ ⇒ 现在的 420 秒是实测量级的 6 倍:慢只是多等,错判是回执说谎。
172
+
173
+ ### 5.1.1 事后改包(dist-tag 等):走 trusted publishing,不要走令牌
174
+
175
+ 发布完成后要改**包管理类**的状态(例如删一个 dist-tag),**不要**想着用发布令牌去做:
176
+
177
+ - 勾了「绕过两步验证」的细粒度令牌(GAT)自 2026-07 起只能发布写,做包管理删除会被
178
+ `403 Granular access tokens that bypass two-factor authentication may not perform this action` 挡住;
179
+ - npm 网页里**没有** dist-tag 管理入口(`/package/<pkg>/dist-tags` 是 404,
180
+ Versions 的 Current Tags 只有只读表格);
181
+ - 剩下唯一合法的口子是 **trusted publishing**:它的 Allowed actions 里明确有 `npm dist-tag`。
182
+
183
+ 本仓的落地方式(一次性运维,不影响发版):
184
+
185
+ 1. npm 包设置 → Trusted Publisher → GitHub Actions,
186
+ 仓库标识(`owner/repo`,见 `package.json` 的 `repository`)与 `release.yml`,
187
+ 勾 `npm publish` 与 `npm dist-tag`;
188
+ 2. `release.yml` 里的 `dist-tag-cleanup` job:只在 `workflow_dispatch` 且填了
189
+ `dist_tag_rm` 时跑;`id-token: write` 换 OIDC 凭证;先 `npm i -g npm@latest`
190
+ (OIDC 需 ≥ 11.5.1);**不要**给 `setup-node` 传 `registry-url`
191
+ (它会写一条空的 `_authToken` 行,值指向 `NODE_AUTH_TOKEN`,先撞 ENEEDAUTH,轮不到 OIDC);
192
+ 3. 触发:
193
+
194
+ ```bash
195
+ gh workflow run release.yml -f dist_tag_rm=<tag 名>
196
+ ```
197
+
198
+ 2026-11-05 实测:run 41997717147 success,`probe-2fa` 删除后只剩 `latest`。
199
+ 这条作业**不会**被 tag 推送触发(条件是 `github.event_name == 'workflow_dispatch'` 且输入非空)。
200
+
201
+ ### 5.1.2 发版本身的凭据:OIDC,没有任何长期令牌
202
+
203
+ 发版与上面的包管理动作共用同一套信任关系:npm 侧把 `release.yml` 登记为
204
+ trusted publisher(`Status: Valid`),工作流用 `id-token: write` 现换一次短期凭证。
205
+ **仓库里不再有任何 npm 令牌 secret**,也没有 7 天/90 天到期的东西会悄悄断掉发布。
206
+
207
+ 两条硬约束(都实测踩过,`release-gate.py` 的 `workflow_problems` 逐条守着):
208
+
209
+ 1. `setup-node` **不传** `registry-url` —— 它会写一行空的 `_authToken`(值指向
210
+ `NODE_AUTH_TOKEN`),npm 先撞 `ENEEDAUTH`,根本轮不到 OIDC;
211
+ 2. 必须 `npm install -g npm@latest` —— Node 22 自带 npm 10.x,OIDC 需要 ≥ 11.5.1。
212
+
213
+ `workflow_problems` 同时**反向**断言:谁把 `NPM_TOKEN` / `NODE_AUTH_TOKEN` /
214
+ `registry-url` 加回来,门禁立刻红。这不是风格检查——长期令牌模型在 2027-01
215
+ 会连直接发布权一起失效,留着它只是把断点推迟到下一次发版。
216
+
167
217
  ### 5.2 站点链(主分支触发)
168
218
 
169
219
  | 态 | 含义 | 探测方式 |
package/docs/STABILITY.md CHANGED
@@ -30,7 +30,7 @@
30
30
  | JS 成员不消失 | `Select.open()`、`AccordionOptions.duration` 这类名字 | `deprecation-gate.py` ⑦ + `api-contract-gate.py`(d.ts ↔ 运行时**实测**比对) |
31
31
  | 声明的名字真的存在 | 不许出现"文档里有、代码里没有"的幻觉 API | `gen-ai-contract.py --check`、`dead-class-gate.py` |
32
32
  | 行为事件名不消失 | `fl:select` 这类事件名 | 只记在基线里,**没有运行时探针**(见 §6) |
33
- | CLI 子命令与 `--json` 形状 | `agentcss ai <cmd> --json` 的 11 个子命令、失败也是 JSON | `ai-layer-gate.js` ⑥′⑥″ |
33
+ | CLI 子命令与 `--json` 形状 | `agentcss ai <cmd> --json` 的 14 个子命令、失败也是 JSON | `ai-layer-gate.js` ⑥′⑥″、`facts-gate.py`(计数从 `ai/cli.js` 现算) |
34
34
  | 契约文件字段 | `ai/components.json` / `pitfalls*.json` / `behaviors.json` | `gen-ai-contract --check`、`ai-layer-gate` ⑦ |
35
35
  | 文件路径 | `02-primitives/button/button.css` 这类 `<link>` 直接指向的路径 | `dist-parity-check`、`pack-smoke.sh`、`assets-catalog-gate.py` |
36
36
  | 体积承诺 | 全库生效 CSS ≤ 172 KB,常规增长不涨价 | `perf-gate.py` + `budget-selftest.py`(见 [BUDGET.md](BUDGET.md)) |
@@ -170,3 +170,20 @@ a3d36f35ee748632ba05705e669ce950260dd81a3d32b5f3f46fadae0bd084ef # npm 包内
170
170
  ## 6. 更早的版本
171
171
 
172
172
  provenance 不是从第一个版本就有的。实测 `0.10.0` 的 attestation 接口返回 404 —— 这类版本只能核对 `gitHead`(L0),不要把它当作签名证据。
173
+
174
+ ---
175
+
176
+ ## 7. 已知偏差:2026-11-05 的历史重写
177
+
178
+ | 项 | 内容 |
179
+ |---|---|
180
+ | 发生了什么 | 仓库提交历史被重写一次,用于移除早期版本里误留的真实项目代号与本机用户名 |
181
+ | 受影响范围 | **`0.26.0` 及更早**的 provenance 里记录的 `gitCommit` |
182
+ | 具体表现 | 那个 SHA 在重写后的仓库里**不再是任何分支的祖先**;`git cat-file -p <sha>` 会报 `Not a valid object` |
183
+ | 仍然成立的部分 | npm 上的 attestation 本身**没有被改动**(sigstore 签名是不可变的)。它仍然能证明:这个 tarball 由本仓库的 `release.yml` 在某个提交上构建 |
184
+ | 从什么时候恢复正常 | `0.27.0` 起,provenance 的 `gitCommit` 与仓库历史一致 |
185
+ | GitHub 侧 | 旧提交会以**不可达对象**状态保留一段时间,知道 SHA 仍能按地址访问;之后由平台 GC。仓库已无任何 ref 指向它们 |
186
+
187
+ 核验旧版本时正确的读法:**签名证明"构建来源与工作流",不证明"那个提交现在还在仓库里"**。
188
+ 这两件事在重写前是重合的,重写后必须分开看 —— 本文件不打算把这一点藏起来。
189
+
package/index.html CHANGED
@@ -39,7 +39,7 @@ li a:active { background: var(--surface-sunken); }
39
39
  <li><a href="02-primitives/choice/demo.html">checkbox / radio / select</a><span class="r__p">单选/多选/原生 select 外观的统一控件</span></li>
40
40
  <li><a href="02-primitives/combobox/demo.html">combobox · 组合框 / 标签输入</a><span class="r__p">可搜索 + 可多选标签的输入控件</span></li>
41
41
  <li><a href="02-primitives/date-range/demo.html">date-range · 日期区间</a><span class="r__p">日期区间选择,含预设快捷范围</span></li>
42
- <li><a href="02-primitives/delta/demo.html">delta · 变化量</a><span class="r__p">变化量:涨跌方向 + 符号 + 颜色。只描述往哪边变,不判断好坏(等于好恶是 badge 的事,会计正负是 ta</span></li>
42
+ <li><a href="02-primitives/delta/demo.html">delta · 变化量</a><span class="r__p">变化量:涨跌方向 + 符号 + 颜色。</span></li>
43
43
  <li><a href="02-primitives/input/demo.html">input / field</a><span class="r__p">文本输入及其标签/帮助/错误三件套</span></li>
44
44
  <li><a href="02-primitives/metric/demo.html">metric · 指标块</a><span class="r__p">指标块:一个标签 + 一个值 + 一句上下文。数字等宽、单位分离、缺数是一等状态</span></li>
45
45
  <li><a href="02-primitives/popover/demo.html">popover · 气泡卡片</a><span class="r__p">锚定到触发元素的浮层</span></li>
@@ -48,7 +48,7 @@ li a:active { background: var(--surface-sunken); }
48
48
  <li><a href="02-primitives/separator/demo.html">separator · 分隔线</a><span class="r__p">分隔线,可带中间文字</span></li>
49
49
  <li><a href="02-primitives/skeleton/demo.html">skeleton · 骨架屏</a><span class="r__p">加载占位骨架</span></li>
50
50
  <li><a href="02-primitives/switch/demo.html">switch · 开关</a><span class="r__p">开关切换</span></li>
51
- <li><a href="02-primitives/timeline/demo.html">timeline · 时间轴</a><span class="r__p">时间轴:按时间排的事件序列(公告 / 大事记 / 版本记录)。顺序即信息,用 ol;状态交给 badge,颜色在</span></li>
51
+ <li><a href="02-primitives/timeline/demo.html">timeline · 时间轴</a><span class="r__p">时间轴:按时间排的事件序列(公告 / 大事记 / 版本记录)。</span></li>
52
52
  <li><a href="02-primitives/upload/demo.html">upload · 文件上传</a><span class="r__p">文件上传:零 JS 的原生形态 + 拖放/多文件/校验的增强形态</span></li>
53
53
  </ul>
54
54
 
@@ -97,7 +97,7 @@ li a:active { background: var(--surface-sunken); }
97
97
  </p>
98
98
  </main>
99
99
  <footer class="wrap">
100
- <p class="note">0.26.0 · 零第三方运行时依赖</p>
100
+ <p class="note">0.27.1 · 零第三方运行时依赖</p>
101
101
  </footer>
102
102
  </body>
103
103
  </html>
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # agentcss — full reference (one file)
2
2
 
3
- > Generated from `ai/components.json` + `ai/pitfalls*.json` + `ai/behaviors.json` at version **0.26.0**.
3
+ > Generated from `ai/components.json` + `ai/pitfalls*.json` + `ai/behaviors.json` at version **0.27.1**.
4
4
  > Do not edit by hand: run `python 05-audit/gen-llms.py` (freshness is gated).
5
5
  > Short version: `llms.txt`. Machine contract: `ai/*.json`.
6
6
 
@@ -16,10 +16,25 @@ npx agentcss ai pitfalls --json --component=table
16
16
  npx agentcss ai tokens --json --set=dark
17
17
  npx agentcss ai tree --json --set=dark --cat=color
18
18
  npx agentcss ai validate <file> --schema=components --json
19
- npx agentcss ai check <doc.json> --profile=strict --json
19
+ npx agentcss ai check <doc.json> --profile=strict --json # the plan
20
+ npx agentcss ai check <page.html> --json # the artifact
21
+ npx agentcss ai scaffold report/finance --out report.html # skeleton only
22
+ npx agentcss ai shot <page.html> --out shot-{width}.png --width=390,1440 --full
23
+ npx agentcss ai pdf <page.html> --out page.pdf --wait-ms=800 # zero deps: your Chrome
24
+ [--ready-selector="#report-ready"] # wait for async content
20
25
  ```
21
26
 
22
- Every subcommand accepts `--json`; failures print `{"ok":false,...}` and exit non-zero.
27
+ Every subcommand accepts `--json`. On failure it prints *one* JSON document —
28
+ `{"ok":false,"command":"…","error":{"kind":"…","message":"…"}}` — so a pipeline can
29
+ never mistake an error for data. Exit codes are one table for the whole CLI:
30
+ `0` pass · `1` the thing you checked is bad (a verdict, not a tool failure) ·
31
+ `2` bad flag or value (unknown flag, unknown enum value, empty value, repeated flag) ·
32
+ `3` environment missing · `4` render failed · `5` over budget · `6` tool bug.
33
+ `check <page.html>` is static (no network, no browser) and reports undefined class names,
34
+ stylesheets that were never linked, missing `img alt` / table name / `th scope`, skipped
35
+ heading levels and the per-layout obligations in `ai/recipes.json`. `shot` drives your
36
+ installed Chrome over a debugging pipe: the viewport is exactly the width you asked for,
37
+ and it reports horizontal overflow plus JS errors.
23
38
 
24
39
  ## 2 · Components (40)
25
40
 
@@ -42,7 +57,7 @@ Every subcommand accepts `--json`; failures print `{"ok":false,...}` and exit no
42
57
  | `pagination` | `pagination` | pattern | stable | 7.7 KB | `pagination.js` | 分页控件 | — |
43
58
  | `reveal` | `reveal` | pattern | stable | 4.0 KB | `reveal.js` | 入场动效:一个 data-reveal 属性六个方向,无 JS 时内容全可见 | — |
44
59
  | `states` | `state` | pattern | stable | 10.9 KB | — | 空/加载/错误/成功四种整块状态 | `empty-state-no-decoration` `text-injection-xss` |
45
- | `tabs` | `tabs` | pattern | stable | 5.9 KB | `tabs.js` | 选项卡切换 | — |
60
+ | `tabs` | `tabs` | pattern | stable | 6.1 KB | `tabs.js` | 选项卡切换 | — |
46
61
  | `tooltip` | `tooltip` | pattern | stable | 3.5 KB | `tooltip.js` | 悬停/聚焦提示 | `popover-measure-after-render` `fixed-element-inventory` |
47
62
  | `tree` | `tree` | pattern | stable | 4.0 KB | `tree.js` | 可展开树形结构 | — |
48
63
  | `badge` | `badge` | primitive | stable | 6.1 KB | — | 状态标签:计数、分类、结果标记 | — |
package/llms.txt CHANGED
@@ -21,22 +21,45 @@ npx agentcss ai tokens --json --set=dark
21
21
  npx agentcss ai pitfalls --json --component=table
22
22
  npx agentcss ai validate ai/components.json --schema=components --json
23
23
  npx agentcss ai check page.json --profile=strict --json
24
- npx agentcss ai pdf page.html --out page.pdf --max-pages=8 # HTML → PDF (zero deps: uses your Chrome)
24
+ npx agentcss ai check page.html --json # check the page you actually wrote
25
+ npx agentcss ai scaffold report/finance --out report.html # minimal shell (skeleton, no content)
26
+ npx agentcss ai shot page.html --out shot-{width}.png --width=390,1440 --full
27
+ # PNG at exact viewport widths
28
+ npx agentcss ai pdf page.html --out page.pdf --max-pages=8 --ready-selector="#report-ready"
29
+ # waits for load + fonts + visible async content;
30
+ # zero deps; failed renders preserve an old PDF
25
31
  ```
26
32
 
33
+ `check <doc.json>` and `check <page.html>` answer **different** questions: the JSON one
34
+ validates the *plan* (node kinds, component maturity, paper obligations), the HTML one
35
+ validates the *artifact* — class names that the library does not define, stylesheets that
36
+ were never linked, missing `img alt` / table name / `th scope`, skipped heading levels,
37
+ and the per-layout obligations in `ai/recipes.json`. Both are static: no network, no browser.
38
+
39
+ `shot` drives your installed Chrome over a debugging pipe (no port, no bundled browser) so
40
+ the viewport is **exactly** the width you asked for, and reports horizontal overflow plus
41
+ JS errors. Same machine + same flags ⇒ byte-identical PNG (that is what the gate checks).
42
+
27
43
  Print / PDF: `--medium=paper` is the obligation switch — only then are `printPolicy`
28
44
  and `pageBudget` enforced. Per-component paper behaviour lives in
29
45
  `ai/components.json` → `printPolicy`. `@page` is owned by the exporter, not the library.
30
46
 
31
- Every subcommand accepts `--json`. On failure it prints
32
- `{"ok":false,"command":"…","error":{"kind":"…","message":"…"}}` and exits non-zero,
33
- so a pipeline can never mistake an error for data.
47
+ Every subcommand accepts `--json`. On failure it prints *one* JSON document —
48
+ `{"ok":false,"command":"…","error":{"kind":"…","message":"…"}}` — and exits non-zero,
49
+ so a pipeline can never mistake an error for data. There is exactly one failure shape;
50
+ the `kind` values map to exit codes: `1` verdict-negative (`findings`), `2` bad usage,
51
+ `3` environment missing, `4` render failed, `5` over budget, `6` tool bug.
52
+
53
+ Enum flags (`--set`, `--cat`, `--maturity`, `--profile`, `--medium`, `--recipe`) reject
54
+ an unknown value, an empty value, and a repeated occurrence with exit `2`. Flags a
55
+ subcommand does not understand are rejected too — they are never silently ignored.
34
56
 
35
57
  `pitfalls` needs `agentcss@0.21.0+`: `npx` resolves the *published* version, which can lag
36
58
  this repository. If a subcommand is missing, check `npx agentcss --version` first.
37
59
 
38
- There is **no MCP server**: the CLI is the whole interface (read-only queries, no daemon,
39
- no second copy of the contract to keep in sync). See `ai/START-HERE.md` §7.
60
+ There is **no MCP server**: the CLI is the whole interface (no daemon, no network,
61
+ no second copy of the contract to keep in sync). `scaffold` and `shot` write only the file
62
+ you name; every other subcommand is a read-only query. See `ai/START-HERE.md` §7.
40
63
 
41
64
  ## Use it in a page
42
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcss",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
4
4
  "description": "移动 Web 组件库:CSS + 可选行为脚本,零第三方运行时依赖。样式用 bundler 引入,交互行为用 script 标签引入。",
5
5
  "keywords": [
6
6
  "css",
@@ -94,6 +94,7 @@
94
94
  "scripts": {
95
95
  "test": "node --test 05-audit/unit.test.mjs 05-audit/behavior-cores.test.mjs",
96
96
  "gate": "bash 05-audit/check-all.sh",
97
+ "gate:pre-commit": "bash 05-audit/check-all.sh --pre-commit",
97
98
  "gate:fast": "bash 05-audit/check-all.sh --fast",
98
99
  "gate:timing": "bash 05-audit/check-all.sh --timing",
99
100
  "ci:observe": "python3 05-audit/nightly-observe.py",