agentcss 0.25.6 → 0.27.0
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.
- package/01-tokens/README.md +1 -1
- package/01-tokens/behavior/labels.js +44 -0
- package/01-tokens/responsive-demo.html +1 -1
- package/01-tokens/tokens.core.css +1 -1
- package/01-tokens/tokens.css +2 -2
- package/02-primitives/combobox/combobox.js +26 -3
- package/02-primitives/date-range/date-range.js +28 -6
- package/02-primitives/popover/popover.js +22 -2
- package/02-primitives/select/select.js +23 -2
- package/02-primitives/timeline/demo.html +1 -1
- package/02-primitives/upload/upload.js +50 -17
- package/03-patterns/content/content.css +13 -1
- package/03-patterns/content/demo.html +1 -1
- package/03-patterns/drawer/demo.html +1 -1
- package/03-patterns/drawer/drawer.js +44 -6
- package/03-patterns/dropdown/dropdown.js +21 -1
- package/03-patterns/overlay/overlay.css +1 -1
- package/03-patterns/overlay/overlay.js +39 -2
- package/03-patterns/pagination/pagination.js +37 -10
- package/03-patterns/states/states.css +1 -1
- package/03-patterns/tooltip/tooltip.js +17 -2
- package/03-patterns/tree/tree.js +21 -1
- package/04-recipes/analysis-report/demo.html +1 -1
- package/04-recipes/app-shell/README.md +1 -1
- package/04-recipes/app-shell/demo.html +1 -1
- package/04-recipes/data-showcase/demo.html +2 -2
- package/04-recipes/longform/content.html +993 -78
- package/04-recipes/longform/longform.html +3 -3
- package/04-recipes/report/demo.html +3 -3
- package/04-recipes/table/README.md +23 -1
- package/04-recipes/table/demo.html +7 -7
- package/09-assets/bar/bar.css +1 -1
- package/09-assets/bar/bar.js +28 -4
- package/09-assets/model-viewer/model-viewer.js +45 -13
- package/09-assets/scientific-plot/demo.html +6 -6
- package/AGENTS.md +3 -1
- package/API.md +32 -0
- package/CHANGELOG.md +174 -5
- package/CONTRIBUTING.md +44 -3
- package/README.md +19 -0
- package/README.zh-CN.md +18 -0
- package/SECURITY.md +16 -4
- package/START-HERE.md +25 -18
- package/ai/START-HERE.md +40 -6
- package/ai/check-html.js +658 -0
- package/ai/cli.js +557 -67
- package/ai/components.json +9 -4
- package/ai/pdf.js +52 -10
- package/ai/recipes.json +86 -0
- package/ai/scaffold.js +195 -0
- package/ai/shot.js +345 -0
- package/ai/validate.js +65 -6
- package/dist/01-tokens/behavior/labels.js +11 -0
- package/dist/02-primitives/combobox/combobox.js +25 -3
- package/dist/02-primitives/date-range/date-range.js +26 -5
- package/dist/02-primitives/popover/popover.js +14 -1
- package/dist/02-primitives/select/select.js +22 -2
- package/dist/02-primitives/upload/upload.js +47 -17
- package/dist/03-patterns/content/content.css +1 -1
- package/dist/03-patterns/drawer/drawer.js +32 -4
- package/dist/03-patterns/dropdown/dropdown.js +20 -1
- package/dist/03-patterns/overlay/overlay.js +25 -1
- package/dist/03-patterns/pagination/pagination.js +34 -10
- package/dist/03-patterns/tooltip/tooltip.js +14 -2
- package/dist/03-patterns/tree/tree.js +20 -1
- package/dist/09-assets/bar/bar.js +23 -4
- package/dist/09-assets/model-viewer/model-viewer.js +38 -12
- package/dist/COSTS.md +20 -20
- package/docs/ACCESSIBILITY.md +3 -3
- package/docs/ARCHITECTURE.md +116 -0
- package/docs/L10N.md +92 -0
- package/docs/LAYER-BASELINE.md +2 -2
- package/docs/PRINT.md +3 -3
- package/docs/RELEASE-PROCESS.md +59 -3
- package/docs/STABILITY.md +1 -1
- package/docs/VERIFY-RELEASE.md +24 -2
- package/index.html +3 -3
- package/llms-full.txt +18 -4
- package/llms.txt +26 -5
- package/package.json +4 -2
- package/types/index.d.ts +218 -13
|
@@ -479,7 +479,7 @@ th {
|
|
|
479
479
|
哪怕会覆盖掉别人写的东西。
|
|
480
480
|
</p>
|
|
481
481
|
|
|
482
|
-
<div class="table-wrap">
|
|
482
|
+
<div class="table-wrap" tabindex="0" role="region" aria-label="对照表(可横向滚动)">
|
|
483
483
|
<table>
|
|
484
484
|
<colgroup>
|
|
485
485
|
<col style="width: 34%">
|
|
@@ -520,7 +520,7 @@ th {
|
|
|
520
520
|
这套机制加上"准入控制",让关键任务(姿态、点火)不会被普通计算饿死。
|
|
521
521
|
</p>
|
|
522
522
|
|
|
523
|
-
<pre><code><span class="c-cmt"># 不是原文,是按手册重述的伪代码</span>
|
|
523
|
+
<pre tabindex="0"><code><span class="c-cmt"># 不是原文,是按手册重述的伪代码</span>
|
|
524
524
|
if computer_overload():
|
|
525
525
|
issue(1202) <span class="c-cmt"># 告警编号写入 AGC 计数区</span>
|
|
526
526
|
<span class="c-cmt"># 不停机:停机会丢掉姿态信息,地面将失去唯一的实时数据源</span>
|
|
@@ -540,7 +540,7 @@ if computer_overload():
|
|
|
540
540
|
包括读取真实的操作数、写入真实的寄存器地址。
|
|
541
541
|
</p>
|
|
542
542
|
|
|
543
|
-
<pre><code><span class="c-cmt"># 一段能真正跑起来的最小例子</span>
|
|
543
|
+
<pre tabindex="0"><code><span class="c-cmt"># 一段能真正跑起来的最小例子</span>
|
|
544
544
|
<span class="c-out">$</span> python3 -m agc --help
|
|
545
545
|
<span class="c-out">$</span> ./yaAGC -t demo.agc <span class="c-cmt"># 载入磁带并执行</span>
|
|
546
546
|
<span class="c-out">AGC 0000 GO</span></code></pre>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<!-- ======================================================================== -->
|
|
33
33
|
<h2 class="rule-top">先看取舍:三套版式各自放弃了什么</h2>
|
|
34
34
|
|
|
35
|
-
<div class="table"><table>
|
|
35
|
+
<div class="table" tabindex="0" role="region" aria-label="三种 profile 的结构差异(可横向滚动)"><table>
|
|
36
36
|
<caption class="table__cap">表 A · 三种 profile 的结构差异(判据可验证,不是文案差异)</caption>
|
|
37
37
|
<thead><tr>
|
|
38
38
|
<th scope="col">profile</th>
|
|
@@ -289,8 +289,8 @@
|
|
|
289
289
|
<!-- ======================================================================== -->
|
|
290
290
|
<h2 class="rule-top">4 · 🔴 本轮明确不做(不是"还没做",是"这一版不管")</h2>
|
|
291
291
|
<ol>
|
|
292
|
-
<li><strong>分页 PDF / 印刷分页</strong
|
|
293
|
-
孤行寡行控制 —— 全部不在本版范围内,需要
|
|
292
|
+
<li><strong>分页 PDF / 印刷分页</strong>:<code>@page</code> 页边距、页眉页脚、页码、跨页不断行、
|
|
293
|
+
孤行寡行控制 —— 全部不在本版范围内,需要 <code>@page</code>、实物打样与 PDF 引擎验证。
|
|
294
294
|
CSS 里只保留了"打印时不更糟"的最小保障(隐藏边注、图表不跨撕裂)。</li>
|
|
295
295
|
<li><strong>自动目录、尾注/脚注、交叉引用、参考文献样式</strong>:图/表编号用 CSS counter
|
|
296
296
|
做到"写第几就显示第几",再往上的引用图(「详见图 3」这种自动维护)不支持。</li>
|
|
@@ -65,6 +65,28 @@ var COLS = [
|
|
|
65
65
|
|
|
66
66
|
## 无障碍
|
|
67
67
|
|
|
68
|
+
- **滚动容器必须能用键盘进入。** `.table` 是 `overflow:auto` 的二维滚动区域,
|
|
69
|
+
窄屏下会横向滚动;**纯 CSS 无法让它被 Tab 到**,必须在标记上给入口:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<div class="table table--financial" tabindex="0" role="region"
|
|
73
|
+
aria-label="A 股样例数据(可横向滚动)">
|
|
74
|
+
<table>…</table>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
三个属性缺一不可:`tabindex="0"`(进 Tab 序列)、`role="region"`(成为可命名地标)、
|
|
79
|
+
`aria-label`(读屏要念出"这是什么表")。少了它们的后果是:只用键盘的用户
|
|
80
|
+
和部分读屏用户**看不到超出可视区的列**(WCAG 2.1.1)。
|
|
81
|
+
名字里带上 **「(可横向滚动)」**:只说"什么表"不告诉用户这里能滚,
|
|
82
|
+
焦点停上来时读屏不会提示还有更多列 —— 那正是这条规则要解决的问题。
|
|
83
|
+
图/图表容器同理,但名字应来自图注(`aria-labelledby` 指向 `<figcaption>`),
|
|
84
|
+
不要再手写一份同名文案(同一事实不留第二出处)。
|
|
85
|
+
> 这条由两道门禁守着:`table-check.js` ⑮ 在 320px 下逐个量「会滚的容器有没有入口」,
|
|
86
|
+
> `a11y-scan.js` 在同一个页面上补跑 320px 的 `scrollable-region-focusable`。
|
|
87
|
+
> 桌面宽(1280)下表格不溢出,那条 axe 规则根本不会触发 —— 所以窄屏必须单独判。
|
|
88
|
+
> ⚠️ 只有**真的会滚**的容器需要 `tabindex`;不滚的容器加上去只是多一个空的 Tab 停靠点。
|
|
89
|
+
|
|
68
90
|
- 数据表用 `<th scope="col">` / `<th scope="row">`;多级表头第一层用 `scope="colgroup"`。
|
|
69
91
|
复杂表格可用 `id` + `headers` 建立显式关联(本示例未用到,但结构支持)。
|
|
70
92
|
- 排序状态放在 `<th aria-sort="ascending|descending">`,**不是**放在按钮上。
|
|
@@ -163,7 +185,7 @@ python -m http.server 8000
|
|
|
163
185
|
会让同一格同时受两套颜色支配,谁赢取决于选择器权重 —— 那是最难查的一类 bug。
|
|
164
186
|
`table-check` 的判据 ⑬ 会挡住给 `table.css` 添 `num--up` / `num--down` 的做法。
|
|
165
187
|
|
|
166
|
-
### 零值:两边都不许有符号(0.12.1
|
|
188
|
+
### 零值:两边都不许有符号(0.12.1 修)
|
|
167
189
|
|
|
168
190
|
`format: 'signed-percent'` 的正号由类名触发,而旧实现判的是 `v < 0` ⇒
|
|
169
191
|
`v === 0` 落进 else 分支得到 `num--pos`,渲染成「**+0.0%**」——
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
<strong>空格子和 0 是两件事</strong>:空格子读者分不清"没有数据"还是"漏了",
|
|
132
132
|
0 会被当成真实数值参与心算。
|
|
133
133
|
</p>
|
|
134
|
-
<div class="table table--compact">
|
|
134
|
+
<div class="table table--compact" tabindex="0" role="region" aria-label="银行对账明细(可横向滚动)">
|
|
135
135
|
<table>
|
|
136
136
|
<caption class="table__cap">表 G · 银行对账明细(示例,含缺失值)</caption>
|
|
137
137
|
<thead>
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
/* 符号:负数总是有 −(数值语义 + 无障碍第二线索);
|
|
261
261
|
正数只有 signed-* 才带 + —— 这就是"按列语义处理"。
|
|
262
262
|
|
|
263
|
-
🔴 但符号必须**跟打印出来的数字同号**(0.12.1
|
|
263
|
+
🔴 但符号必须**跟打印出来的数字同号**(0.12.1 修):
|
|
264
264
|
旧实现判的是 `v < 0`,于是 `v === 0` 掉进 else 分支拿到 `num--pos`,
|
|
265
265
|
渲染成「+0.0%」—— 一个没变化的数被念成"涨了"。
|
|
266
266
|
同一条错还会在**四舍五入**处复现:0.0004 在 1 位精度下打印成
|
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
-
var h = '<div class="table table--financial"><table>' +
|
|
356
|
+
var h = '<div class="table table--financial" tabindex="0" role="region" aria-label="A 股样例数据(可横向滚动)"><table>' +
|
|
357
357
|
'<caption class="table__cap">A 股样例数据(非真实财报,量级参照公开年报)· ' +
|
|
358
358
|
'金额单位:万元 · 比率为比例值</caption>' +
|
|
359
359
|
'<thead><tr>';
|
|
@@ -425,7 +425,7 @@
|
|
|
425
425
|
{ label: '时效 (150 °C)', key: 'h150' }
|
|
426
426
|
];
|
|
427
427
|
|
|
428
|
-
var a = '<div class="table table--academic"><table>' +
|
|
428
|
+
var a = '<div class="table table--academic" tabindex="0" role="region" aria-label="时效温度对铝合金拉伸性能的影响(可横向滚动)"><table>' +
|
|
429
429
|
'<caption class="table__cap">表 1 · 不同时效温度下铝合金的拉伸性能(示例数据)</caption>' +
|
|
430
430
|
'<thead><tr><th scope="col" rowspan="2">合金牌号</th>';
|
|
431
431
|
GROUPS.forEach(function (g) {
|
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
return fixed(p, 3).replace(/^0\./, '.');
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
-
var s = '<div class="table table--stats"><table>' +
|
|
475
|
+
var s = '<div class="table table--stats" tabindex="0" role="region" aria-label="OLS 估计结果(可横向滚动)"><table>' +
|
|
476
476
|
'<caption class="table__cap">表 2 · 处理效应的 OLS 估计(示例数据,n = 412)</caption>' +
|
|
477
477
|
'<thead><tr>' +
|
|
478
478
|
'<th scope="col">项</th>' +
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
why: '缺失 ⇒ 显示 —,不是 0,也不带符号' }
|
|
513
513
|
];
|
|
514
514
|
|
|
515
|
-
var p = '<div class="table"><table>' +
|
|
515
|
+
var p = '<div class="table" tabindex="0" role="region" aria-label="同一个数在不同列定义下的显示(可横向滚动)"><table>' +
|
|
516
516
|
'<caption class="table__cap">同一个数、不同的列定义 ⇒ 不同的显示</caption>' +
|
|
517
517
|
'<thead><tr><th scope="col">列定义</th><th scope="col">输入</th>' +
|
|
518
518
|
'<th scope="col">渲染结果</th><th scope="col">为什么</th></tr></thead><tbody>';
|
|
@@ -547,7 +547,7 @@
|
|
|
547
547
|
expect: '显示为字面字符' }
|
|
548
548
|
];
|
|
549
549
|
|
|
550
|
-
var e2 = '<div class="table"><table>' +
|
|
550
|
+
var e2 = '<div class="table" tabindex="0" role="region" aria-label="转义演示:动态文本按文本渲染(可横向滚动)"><table>' +
|
|
551
551
|
'<caption class="table__cap">动态文本按**文本**渲染,不当 HTML 解释</caption>' +
|
|
552
552
|
'<thead><tr><th scope="col">列定义</th><th scope="col">原始数据</th>' +
|
|
553
553
|
'<th scope="col">渲染结果</th><th scope="col">验收点</th></tr></thead><tbody>';
|
package/09-assets/bar/bar.css
CHANGED
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
这里原来有一段针对 `prefers-color-scheme: dark` 的媒体查询块,
|
|
168
168
|
里面只有两条:`.bar` 的 background 与 `.bar__label` 的 stroke。
|
|
169
169
|
⚠️ 那段文字在这里**故意不照抄**:多个门禁拿这个字符串当锚点,
|
|
170
|
-
|
|
170
|
+
注释里出现字面量会把它们带偏(踩过一次)。
|
|
171
171
|
它做的只有一件事:把 `var()` 的**兜底值**从 #fff 换成暗色值。
|
|
172
172
|
令牌在的时候(本库的正式用法)两条声明解析结果完全相同 ⇒ 纯冗余。
|
|
173
173
|
|
package/09-assets/bar/bar.js
CHANGED
|
@@ -95,9 +95,35 @@
|
|
|
95
95
|
* 并且只画(画布内的那一部分),不能画到坐标系外面去
|
|
96
96
|
* ③ **标注跟着堆叠顶端走**:标签写的是**该列合计**,不是第一个系列的值
|
|
97
97
|
*/
|
|
98
|
+
/* 可见文案默认值(F2 / 0.26.0)。
|
|
99
|
+
⚠️ 这里用 `opt.text` 而不是 `labels` —— `bar.draw` 的 `labels`
|
|
100
|
+
早就是「X 轴标签」这个**数据**参数,改名是破坏性变更。
|
|
101
|
+
同类情况全库只此一处,已在 docs/L10N.md 写明。 */
|
|
102
|
+
var DEFAULT_TEXT = {
|
|
103
|
+
series: function (i) { return '系列 ' + i; },
|
|
104
|
+
clipped: function (n, isStack, max) {
|
|
105
|
+
return '有 ' + n + ' 个' + (isStack ? '类目的合计' : '数据')
|
|
106
|
+
+ '超出量程(上限 ' + max + '),已按上限截断显示。';
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/* ==== BEHAVIOR INJECT BEGIN: labels ==== */
|
|
111
|
+
var flLabels = function (defaults, src) {
|
|
112
|
+
src = src || {};
|
|
113
|
+
var out = {};
|
|
114
|
+
var k;
|
|
115
|
+
for (k in defaults) {
|
|
116
|
+
if (Object.prototype.hasOwnProperty.call(defaults, k)) {
|
|
117
|
+
out[k] = (src[k] === undefined || src[k] === null) ? defaults[k] : src[k];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
};
|
|
122
|
+
/* ==== BEHAVIOR INJECT END: labels ==== */
|
|
98
123
|
function draw(opt) {
|
|
99
124
|
opt = opt || {};
|
|
100
125
|
var host = opt.host;
|
|
126
|
+
var T = flLabels(DEFAULT_TEXT, opt && opt.text);
|
|
101
127
|
if (!host) throw new Error('bar.draw: 缺少 host');
|
|
102
128
|
if (opt.max === undefined || opt.max === null) {
|
|
103
129
|
/* 🔴 铁律三:不替调用方决定量程。
|
|
@@ -286,7 +312,7 @@
|
|
|
286
312
|
/* 🔴 用 textContent,不用 innerHTML —— 系列名来自调用方的数据 */
|
|
287
313
|
li.appendChild(sw);
|
|
288
314
|
var tx = document.createElement('span');
|
|
289
|
-
tx.textContent = se.name || (
|
|
315
|
+
tx.textContent = se.name || T.series(si + 1);
|
|
290
316
|
li.appendChild(tx);
|
|
291
317
|
legendEl.appendChild(li);
|
|
292
318
|
});
|
|
@@ -302,9 +328,7 @@
|
|
|
302
328
|
} else if (clipped.length) {
|
|
303
329
|
var p2 = document.createElement('p');
|
|
304
330
|
p2.className = 'bar__note';
|
|
305
|
-
p2.textContent =
|
|
306
|
-
(mode === 'stack' ? '类目的合计' : '数据') +
|
|
307
|
-
'超出量程(上限 ' + max + '),已按上限截断显示。';
|
|
331
|
+
p2.textContent = T.clipped(clipped.length, mode === 'stack', max);
|
|
308
332
|
host.appendChild(p2);
|
|
309
333
|
}
|
|
310
334
|
|
|
@@ -110,33 +110,33 @@
|
|
|
110
110
|
n.appendChild(this.ui);
|
|
111
111
|
|
|
112
112
|
// 加载按钮
|
|
113
|
-
this.btn = el('button', 'mv__btn',
|
|
113
|
+
this.btn = el('button', 'mv__btn', LABELS.load);
|
|
114
114
|
this.btn.type = 'button';
|
|
115
115
|
this.btn.addEventListener('click', function () { self.load(); });
|
|
116
116
|
this.ui.appendChild(this.btn);
|
|
117
117
|
|
|
118
118
|
this.hint = el('p', 'mv__hint');
|
|
119
|
-
/* 🔴 文件名来自 `src`,可能是调用方拼出来的 URL
|
|
119
|
+
/* 🔴 文件名来自 `src`,可能是调用方拼出来的 URL。
|
|
120
120
|
原来用 innerHTML 直接拼 ⇒ `<img onerror>` 这类值会被解析成节点。
|
|
121
121
|
改成 textContent 组装:`<code>` 与 `<b>` 仍然存在,但文本永远只是文本。 */
|
|
122
122
|
var fname = el('code', null, this.src.split('/').pop() || '—');
|
|
123
|
-
var strong = el('b', null,
|
|
124
|
-
this.hint.appendChild(document.createTextNode(
|
|
123
|
+
var strong = el('b', null, LABELS.hintStrong);
|
|
124
|
+
this.hint.appendChild(document.createTextNode(LABELS.hintBefore));
|
|
125
125
|
this.hint.appendChild(fname);
|
|
126
|
-
this.hint.appendChild(document.createTextNode(
|
|
126
|
+
this.hint.appendChild(document.createTextNode(LABELS.hintBetween));
|
|
127
127
|
this.hint.appendChild(strong);
|
|
128
|
-
this.hint.appendChild(document.createTextNode(
|
|
128
|
+
this.hint.appendChild(document.createTextNode(LABELS.hintTail));
|
|
129
129
|
this.ui.appendChild(this.hint);
|
|
130
130
|
|
|
131
131
|
if (!hasWebGL()) {
|
|
132
|
-
this.fail(
|
|
132
|
+
this.fail(LABELS.noWebGL);
|
|
133
133
|
this.btn.disabled = true;
|
|
134
134
|
}
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
Viewer.prototype.makeIdle = function () {
|
|
138
138
|
var box = el('div', 'mv__stage');
|
|
139
|
-
box.appendChild(el('div', 'mv__ph',
|
|
139
|
+
box.appendChild(el('div', 'mv__ph', LABELS.placeholder));
|
|
140
140
|
return box;
|
|
141
141
|
};
|
|
142
142
|
|
|
@@ -146,14 +146,14 @@
|
|
|
146
146
|
this.state = 'error';
|
|
147
147
|
this.ui.innerHTML = '';
|
|
148
148
|
var s = el('div', 'mv__stage mv__stage--err');
|
|
149
|
-
s.appendChild(el('div', 'mv__ph',
|
|
149
|
+
s.appendChild(el('div', 'mv__ph', LABELS.placeholder));
|
|
150
150
|
var p = el('p', 'mv__err', msg);
|
|
151
151
|
s.appendChild(p);
|
|
152
152
|
this.ui.appendChild(s);
|
|
153
153
|
if (this.btn) this.ui.appendChild(this.btn);
|
|
154
154
|
if (this.btn) {
|
|
155
155
|
this.btn.disabled = false;
|
|
156
|
-
this.btn.textContent =
|
|
156
|
+
this.btn.textContent = LABELS.retry;
|
|
157
157
|
}
|
|
158
158
|
};
|
|
159
159
|
|
|
@@ -169,12 +169,12 @@
|
|
|
169
169
|
|
|
170
170
|
this.ui.innerHTML = '';
|
|
171
171
|
var stage = el('div', 'mv__stage');
|
|
172
|
-
this.statusNode = el('p', 'mv__status',
|
|
172
|
+
this.statusNode = el('p', 'mv__status', LABELS.preparing);
|
|
173
173
|
stage.appendChild(this.statusNode);
|
|
174
174
|
this.ui.appendChild(stage);
|
|
175
175
|
|
|
176
176
|
var t0 = (window.performance && performance.now) ? performance.now() : Date.now();
|
|
177
|
-
this.status(
|
|
177
|
+
this.status(LABELS.preparingEngine);
|
|
178
178
|
this.boot(null, t0, 0);
|
|
179
179
|
|
|
180
180
|
/* 🔴🔴 **超时兜底**(实测:服务器上会卡死在 loading)
|
|
@@ -296,7 +296,7 @@
|
|
|
296
296
|
xhr.onprogress = function (e) {
|
|
297
297
|
if (e.lengthComputable && e.total) {
|
|
298
298
|
self1.lastBytes = e.total;
|
|
299
|
-
self1.status(
|
|
299
|
+
self1.status(LABELS.downloading(Math.round(e.loaded / e.total * 100)));
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
302
|
xhr.onload = function () {
|
|
@@ -396,6 +396,36 @@
|
|
|
396
396
|
|
|
397
397
|
/* ---------- 对外 API ---------- */
|
|
398
398
|
|
|
399
|
+
/* 可见 / 读屏文案(F2 / 0.26.0)。
|
|
400
|
+
本组件由 `[data-mv]` 自动初始化、没有逐实例 opts ⇒ 用**模块级**设置:
|
|
401
|
+
`ModelViewer.setLabels({ load: 'Load 3D model', ... })`
|
|
402
|
+
⚠️ 已知仍未覆盖的(列在 docs/L10N.md,不假装已做完):
|
|
403
|
+
`mv__cap` 的说明句、`fail()` 的几条错误句 ——
|
|
404
|
+
它们含 `<b>` 结构与多个动态量,要改成节点拼装才能安全本地化。 */
|
|
405
|
+
var LABELS = {
|
|
406
|
+
load: '加载 3D 模型',
|
|
407
|
+
placeholder: '3D 模型',
|
|
408
|
+
retry: '重试',
|
|
409
|
+
preparing: '正在准备…',
|
|
410
|
+
preparingEngine: '正在准备 3D 引擎…',
|
|
411
|
+
hintBefore: '模型 ',
|
|
412
|
+
hintBetween: ' 会',
|
|
413
|
+
hintStrong: '点击后才下载',
|
|
414
|
+
hintTail: ',首屏不占流量。',
|
|
415
|
+
downloading: function (pct) { return '正在下载模型 ' + pct + '%'; },
|
|
416
|
+
noWebGL: '这台设备的浏览器不支持 WebGL,无法显示 3D 模型。'
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
function setLabels(patch) {
|
|
420
|
+
if (!patch) return;
|
|
421
|
+
for (var k in LABELS) {
|
|
422
|
+
if (Object.prototype.hasOwnProperty.call(LABELS, k)
|
|
423
|
+
&& patch[k] !== undefined && patch[k] !== null) {
|
|
424
|
+
LABELS[k] = patch[k];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
399
429
|
var ModelViewer = {
|
|
400
430
|
/** 扫描并初始化页面里的所有查看器 */
|
|
401
431
|
update: function (root) {
|
|
@@ -421,6 +451,8 @@
|
|
|
421
451
|
},
|
|
422
452
|
};
|
|
423
453
|
|
|
454
|
+
ModelViewer.setLabels = setLabels;
|
|
455
|
+
|
|
424
456
|
if (typeof window !== 'undefined') window.ModelViewer = ModelViewer;
|
|
425
457
|
|
|
426
458
|
// @autoinit: [data-mv] —— 扫属性自动 update()(详见 API.md「自动初始化」)
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
本模块<strong>不做表达式求值</strong>。
|
|
40
40
|
</p>
|
|
41
41
|
<figure class="fig">
|
|
42
|
-
<div class="fig__box" id="fig1"></div>
|
|
43
|
-
<figcaption class="fig__cap">
|
|
42
|
+
<div class="fig__box" id="fig1" tabindex="0" role="region" aria-labelledby="fig1-cap"></div>
|
|
43
|
+
<figcaption class="fig__cap" id="fig1-cap">
|
|
44
44
|
图 1 · 阻尼振荡位移随时间的变化。A = 1.00 m,λ = 0.35 s⁻¹,f = 1.20 Hz。
|
|
45
45
|
</figcaption>
|
|
46
46
|
</figure>
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
也不据此推断显著性 —— 这两件事必须由测量者说清楚。
|
|
53
53
|
</p>
|
|
54
54
|
<figure class="fig">
|
|
55
|
-
<div class="fig__box" id="fig2"></div>
|
|
56
|
-
<figcaption class="fig__cap">
|
|
55
|
+
<div class="fig__box" id="fig2" tabindex="0" role="region" aria-labelledby="fig2-cap"></div>
|
|
56
|
+
<figcaption class="fig__cap" id="fig2-cap">
|
|
57
57
|
图 2 · 五种催化剂在不同温度下的转化率(示例数据,误差棒为 ±1 个测量标准差)。
|
|
58
58
|
</figcaption>
|
|
59
59
|
</figure>
|
|
@@ -105,11 +105,11 @@
|
|
|
105
105
|
后者画出的是一段<strong>不存在的趋势</strong>。
|
|
106
106
|
</p>
|
|
107
107
|
<figure class="fig">
|
|
108
|
-
<div class="fig__box" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:var(--sp-3)">
|
|
108
|
+
<div class="fig__box" tabindex="0" role="region" aria-labelledby="figgap-cap" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:var(--sp-3)">
|
|
109
109
|
<div id="figGap"></div>
|
|
110
110
|
<div id="figZero"></div>
|
|
111
111
|
</div>
|
|
112
|
-
<figcaption class="fig__cap">
|
|
112
|
+
<figcaption class="fig__cap" id="figgap-cap">
|
|
113
113
|
图 5 · 同一组数据,两种缺失处理。左:缺失断开(正确)。右:缺失当 0(错误,仅用于对照)。
|
|
114
114
|
</figcaption>
|
|
115
115
|
</figure>
|
package/AGENTS.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
- **Known failure modes:** `ai/pitfalls.json`, or `npx agentcss ai pitfalls --json --component=<id>`.
|
|
5
5
|
- **Print / PDF:** `npx agentcss ai pdf <page.html> --out page.pdf [--paper=a4|letter|a5] [--margin="18mm 15mm"] [--max-pages=N]`. `@page` belongs to the exporter, never to the library. To hold a document to paper standards use `ai check <doc.json> --medium=paper` — it then requires `pageBudget` and refuses `pagination:"server"` components.
|
|
6
6
|
- **Query:** `npx agentcss ai components --json [--maturity=stable]` · `ai tokens --json [--set=dark]` · `ai tree --json`.
|
|
7
|
-
- **Validate:** `npx agentcss ai validate <file> --schema=components --json`. Every subcommand accepts `--json`; failures print `{"ok":false
|
|
7
|
+
- **Validate, then branch on the exit code:** `npx agentcss ai validate <file> --schema=components --json`. Every subcommand accepts `--json`; failures print exactly one document `{"ok":false,"command","error":{"kind","message"}}`. Codes are one table for the whole CLI: `0` pass · `1` what you checked is bad (a verdict) · `2` bad flag or value — unknown flag, unknown enum value, empty value and a repeated flag all land here · `3` environment missing (no browser / no `node`) · `4` render failed · `5` over budget · `6` tool bug. A typo can never degrade into "empty result, exit 0".
|
|
8
|
+
- **Check the page you actually wrote:** `npx agentcss ai check <page.html> [--recipe=report/finance]`. Static — no network, no browser. It reports class names the library does not define, stylesheets that were never linked (naming the file that defines the class), missing `img alt` / table name / `th scope`, skipped heading levels, and the per-layout obligations in `ai/recipes.json`. `npx agentcss ai scaffold <recipe> --out <page.html>` gives a minimal shell that already passes.
|
|
9
|
+
- **See it before shipping:** `npx agentcss ai shot <page.html> --out shot-{width}.png --width=390,1440 --full`. The viewport is exactly the width you ask for (a cropped window is not the same thing), and it reports horizontal-overflow culprits plus JS errors.
|
|
8
10
|
- **Markup ground truth:** each component's `demo.html`. Restyle through tokens (CSS variables); never rewrite component selectors.
|
|
9
11
|
- **Docs are Chinese:** `ai/START-HERE.md` (navigation). English entry points: `README.md`, `llms.txt`, `llms-full.txt`.
|
package/API.md
CHANGED
|
@@ -376,6 +376,38 @@ python3 05-audit/prefix-build.py --prefix fl
|
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
|
379
|
+
## 文案本地化(0.26.0 起)
|
|
380
|
+
|
|
381
|
+
**库默认输出中文;`labels` 可覆盖用户可见 / 读屏会念到的文案。**
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
Pagination.create(node, { labels: { prev: 'Previous', next: 'Next' } });
|
|
385
|
+
Overlay.toast({ title: 'Saved', labels: { closeToast: 'Dismiss' } });
|
|
386
|
+
Tooltip.setLabels({ region: 'Tooltip' }); // 模块级(属性初始化,无逐实例 opts)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
- 值可以是字符串或函数(动态量用函数:`page(p, isCurrent)`、`uploading(pct)`)。
|
|
390
|
+
- **不传时输出与 0.25.x 逐字相同** —— 升级补丁版本不会换掉界面语言。
|
|
391
|
+
- `bar.draw` 是唯一例外:`labels` 已是「X 轴标签」这个**数据**参数,
|
|
392
|
+
它的文案覆盖叫 `text`。
|
|
393
|
+
- 开发期错误信息(`throw new Error`)**不在覆盖范围**,它们不经过 UI。
|
|
394
|
+
|
|
395
|
+
逐组件的键名、以及**已知仍未覆盖**的几处,见 `docs/L10N.md`。
|
|
396
|
+
门禁:`05-audit/i18n-labels-gate.js`(静态禁硬编码 + 真浏览器验证覆盖生效、默认值不变)。
|
|
397
|
+
|
|
398
|
+
### HTML 净化钩子
|
|
399
|
+
|
|
400
|
+
`popover` / `drawer` 的字符串 HTML 入口可以接净化器:
|
|
401
|
+
|
|
402
|
+
```js
|
|
403
|
+
Popover.setSanitizer(function (html) { return DOMPurify.sanitize(html); });
|
|
404
|
+
Drawer.setSanitizer(function (html) { return DOMPurify.sanitize(html); });
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
默认**不改变行为**(仍按"调用方已净化"处理),理由与责任边界见 `SECURITY.md`。
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
379
411
|
## 破坏性变更怎么走
|
|
380
412
|
|
|
381
413
|
公开**名字**(令牌名 / 类名 / 全局与类型成员 / 事件名)的移除走**登记制**:
|
package/CHANGELOG.md
CHANGED
|
@@ -12,9 +12,178 @@
|
|
|
12
12
|
- **次版本**:新增组件 / 新增能力(下游不改代码也能升级)
|
|
13
13
|
- **修订号**:修 bug、修文案(下游无需任何改动)
|
|
14
14
|
|
|
15
|
+
> **关于历史条目里的 `E123` 这类编号**:那是 0.26.0 及更早版本沿用的
|
|
16
|
+
> 内部失误台账索引,台账本身不随包分发。从 0.27.0 起,本文件只描述
|
|
17
|
+
> **改了什么、影响谁**,不再引用内部编号 —— 读者不需要一份查不到的索引。
|
|
18
|
+
|
|
15
19
|
## [Unreleased]
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
|
|
22
|
+
## [0.27.0] - 2026-11-06
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`ai check <page.html>` —— 校验产物,而不只是计划。**
|
|
27
|
+
此前 `ai check` 只吃 `doc.json`(节点类型、组件档位),管不到"写出来的那一页"。
|
|
28
|
+
新增 16 条判据,查的都是**不会报错、只会静默失效**的东西:
|
|
29
|
+
类名库里没有 / 类名库里有但这页没引到定义它的样式表(指名到文件)/
|
|
30
|
+
引的样式表一个都读不到 / `<img>` 缺 `alt` / 表没有可访问名称 /
|
|
31
|
+
`th` 缺 `scope` / id 重复 / 标题跳级 / 表单控件没有可读名称 /
|
|
32
|
+
`target="_blank"` 缺 `rel="noopener"` / 缺 `lang`、`charset`、`viewport`。
|
|
33
|
+
纯静态:不联网、不启动浏览器。按扩展名分派,`check x.json` 的行为不变。
|
|
34
|
+
- **`ai shot <page.html> --out shot-{width}.png` —— 出 PNG。**
|
|
35
|
+
用 Chrome 的调试管道(不开端口、不打包浏览器)把视口**设成**要的宽度,
|
|
36
|
+
再截图。为什么不用 `--window-size`:Windows 上窗口有最小宽度,
|
|
37
|
+
`--window-size=390` 实际按 500px 排版、只按 390 裁一刀 —— 图是好的,
|
|
38
|
+
内容不对,且不报错。输出里同时给出横向溢出(指名元素)、内容高度与 JS 报错。
|
|
39
|
+
同一台机器、同样参数两次逐字节一致(门禁验的就是这条)。
|
|
40
|
+
- **`ai scaffold <版式> --out page.html` —— 三个最小壳**(`report/finance`、
|
|
41
|
+
`report/technical`、`report/magazine`):只给正确的骨架与必有件的位置,
|
|
42
|
+
不含内容。三个壳都由门禁跑一遍 `ai check --recipe=<自己>`。
|
|
43
|
+
- **`ai/recipes.json`** —— 版式"必有件"的机器可读事实:能从标记判定的写进
|
|
44
|
+
`requires`(少一件就报错),判不了的写进 `unchecked`(只打印,不判过)。
|
|
45
|
+
- **`prose-gate`** —— 检查页面的**可见文本**:未渲染的 `**加粗**` / 反引号 / `](链接)`、
|
|
46
|
+
整页括号不配平(通常是把 `(…)` 切断),以及生成器是否切在语义边界上。
|
|
47
|
+
自带反向控制:四类注入必须全部抓到,注释与代码块里的同样字符不许误报。
|
|
48
|
+
- **诊断可分辨**:`npm pack` 失败时 `pack-closure` 会打印 npm/node 版本、cwd、
|
|
49
|
+
stdout/stderr 尾,并区分"产品失败(package.json 坏了)"与"清单没拿到(无法判定)";
|
|
50
|
+
`check-all.sh` 在 `node` 不在 PATH 时一次性说明并整体退出 `3`,
|
|
51
|
+
而不是让 5 道 python 门禁各抛一个 `WinError 2` 堆栈。
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **退出码统一成一套,并且可以按码分支。** 以前同一个 `--profile` 写错,
|
|
56
|
+
`pdf` 给 2、其它子命令给 1;而 `1` 同时表示"页面有错"和"工具崩了"。
|
|
57
|
+
现在全 CLI 一张表:
|
|
58
|
+
|
|
59
|
+
| 码 | 含义 |
|
|
60
|
+
|---|---|
|
|
61
|
+
| 0 | 通过 |
|
|
62
|
+
| **1** | **判定不通过**(页面/文档确实有错 —— 是结论,不是工具坏了) |
|
|
63
|
+
| **2** | 用法错误(开关名、取值、重复、空值) |
|
|
64
|
+
| 3 | 环境不具备(没有浏览器 / 没有 node) |
|
|
65
|
+
| 4 | 渲染失败 |
|
|
66
|
+
| 5 | 超出预算(产物完整) |
|
|
67
|
+
| 6 | 工具内部错误 |
|
|
68
|
+
|
|
69
|
+
失败的 JSON 也统一成**一种形状**:`{"ok":false,"command","error":{"kind","message"}}`
|
|
70
|
+
(`pdf` 以前发的是顶层 `code`/`message`,调用方得写两个分支)。
|
|
71
|
+
- **枚举开关的非法值一律报错,不再退化成"空结果"。** 以前:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
ai tokens --set=not-a-theme --json → stdout 是字面量 undefined,退出码 0
|
|
75
|
+
ai tree --set=not-a-theme --json → 同上
|
|
76
|
+
ai components --maturity=not-a-level → [],退出码 0
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
对 Agent 这不是"没输出",而是**错误的控制流**:它会把拼错的参数读成
|
|
80
|
+
"这个库没有 beta 组件",然后据此写结论。现在非法值 / 空值 / 同一个开关给两次
|
|
81
|
+
/ 开关后面吞掉另一个开关,全部 `2` 退出,并在 message 里列出合法取值。
|
|
82
|
+
合法取值**从数据现算**(`profiles.json` 的 `allowedMaturity`、`tokens.json` 的
|
|
83
|
+
`sets`、`tokens.tree.json` 的 `meta.categories`、`recipes.json` 的版式),
|
|
84
|
+
不在这里另抄一份。
|
|
85
|
+
- **与本命令无关的开关不再被静默忽略。** `ai components --set=dark` 以前退出码 0、
|
|
86
|
+
什么都不说 —— 调用方会以为"按 dark 过滤过了"。现在每个子命令声明自己认识的开关
|
|
87
|
+
(`ai/cli.js` 的 `FLAGS` 表),不认识的给 `2`。
|
|
88
|
+
- **`ai check <page.html> --profile` / `--medium` 明确报错**:这两个开关只对
|
|
89
|
+
`doc.json` 有效。以前静默忽略 ⇒ 调用方以为"按 strict 档查过了"。
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- **Schema 校验器实现了 `uniqueItems` 与 `not`。** 这两个关键字一直挂在
|
|
94
|
+
"已实现"白名单里却没有实现:`validate({type:'array',uniqueItems:true},[1,1])`
|
|
95
|
+
返回 `ok:true`,`validate({type:'number',not:{const:1}},1)` 同样。
|
|
96
|
+
白名单只保证"允许出现",不保证"真的校验" —— 现在逐个关键字都有正/负契约用例,
|
|
97
|
+
新加关键字却不写用例,门禁直接红。
|
|
98
|
+
- **能力预扫从 4 份 schema 扩到全部 8 份。** 以前只扫 contract/components/tokens/patch,
|
|
99
|
+
`behaviors` / `deprecations` / `tokensAudience` / `tokensTree` 从来没被扫过 ——
|
|
100
|
+
它们里面出现未知关键字也不会有人知道。扩上去当场撞出一个真 bug:
|
|
101
|
+
`patternProperties` 的**键是正则**,被当成关键字检查,于是
|
|
102
|
+
`tokensAudience` 报"用了没实现的关键字"(schema 本身完全合法)。
|
|
103
|
+
- **生成器不再按字符硬切句子。** 首页摘要以前是 `purpose[:56]`,于是
|
|
104
|
+
`delta` 结尾是「会计正负是 ta」、`timeline` 是「颜色在」,首页还因此括号不配平。
|
|
105
|
+
现在按语义边界切(句末标点 > 分句边界 > 硬切 + 省略号),并且括号必须配平。
|
|
106
|
+
`aria-label` 里的小节名走同一条规则 —— 读屏会把它逐字念出来。
|
|
107
|
+
- **可见文本里不再有未渲染的 Markdown。** 修了 5 处:
|
|
108
|
+
`timeline` / `drawer` / `app-shell` 的 `**加粗**`,`responsive-demo` 与
|
|
109
|
+
`report` 页里的反引号代码;另有 `guide/app-shell.html` 的一个孤立 `**`
|
|
110
|
+
(源头是 `04-recipes/app-shell/README.md` 里一处没闭合的加粗)。
|
|
111
|
+
- **`md-render.py` 产出的表格补 `scope="col"` 与可访问名称**
|
|
112
|
+
(取最近一节的小节标题作 `aria-label`)。此前每一份从 Markdown 出来的页面
|
|
113
|
+
都缺这两件 —— GFM 的表格语法里没有它们可写,而缺了不会报错,
|
|
114
|
+
只会让读屏用户听到一串没有表头的数字。
|
|
115
|
+
- `04-recipes/longform/content.html` 随之重生成(差 4 行,纯属性)。
|
|
116
|
+
|
|
117
|
+
## [0.26.0] - 2026-11-04
|
|
118
|
+
|
|
119
|
+
这一版有两类改动:**修外部审计确认的缺陷**,以及**新增三个公开能力**
|
|
120
|
+
(文案覆盖、HTML 净化钩子、toast 上限)。升级不需要改代码 ——
|
|
121
|
+
新增能力都有默认值,且默认值与 0.25.6 行为一致。
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
- **文案本地化**:`labels` 可以覆盖用户可见 / 读屏会念到的文案。
|
|
126
|
+
逐实例入口 `Overlay.toast({ labels })`、`Drawer.open({ labels })`、
|
|
127
|
+
`Pagination.create(node, { labels })`、`new Upload(root, { labels })`、
|
|
128
|
+
`Select/Combobox/Dropdown/Tree/DateRange.create(root, { labels })`;
|
|
129
|
+
属性自动初始化的 `Tooltip` / `ModelViewer` 用模块级 `setLabels()`。
|
|
130
|
+
**不传时输出与 0.25.6 逐字相同**。`bar.draw` 的文案覆盖叫 `text`
|
|
131
|
+
(它的 `labels` 早就是 X 轴标签这个数据参数)。清单与已知剩余项见 `docs/L10N.md`。
|
|
132
|
+
- **HTML 净化钩子**:`Popover.setSanitizer(fn)` / `Drawer.setSanitizer(fn)`,
|
|
133
|
+
也可逐次传 `sanitize`。默认**不改变行为**(仍按"调用方已净化"处理)——
|
|
134
|
+
静默自动净化会让既有页面里的 `<b>` / `<svg>` 突然消失。边界见 `SECURITY.md`。
|
|
135
|
+
- **toast 同屏上限**:`Overlay.toast({ maxVisible })`,默认 4,`0` = 不限。
|
|
136
|
+
超出时摘掉**最旧的**一条(此前无上限,20 条并发提示会把屏幕铺满)。
|
|
137
|
+
- `types/index.d.ts`:新增上列文案类型与 `ToastOptions` / `DialogOptions` /
|
|
138
|
+
`PopoverOptions` / `DrawerOpenOptions` / `UploadOptions` / `DropdownOptions` /
|
|
139
|
+
`NavOptions`;把 `ModelViewer` 从"构造函数"改正为 `{ update, refresh, setLabels }`
|
|
140
|
+
(按旧声明写 `new window.ModelViewer(el)` 会在运行时抛 TypeError)。
|
|
141
|
+
|
|
142
|
+
### Fixed
|
|
143
|
+
|
|
144
|
+
- `longform` 配方页:`.prose > img` 是**直接子代**选择器,而 Markdown 渲染出的图片
|
|
145
|
+
在 `<p>` 里 ⇒ `max-width:100%` 从未生效,600px 宽的图在 375px 视口横向溢出
|
|
146
|
+
246px、320px 溢出 301px。改为后代选择器(`> a` 在 0.9 踩过同一个坑,这次同一个坑换了标签)。
|
|
147
|
+
- 可滚动的 `.table` / `.table-wrap` / `<pre>` / 图框在窄屏下**键盘进不去**
|
|
148
|
+
(WCAG 2.1.1)。补 `tabindex="0"` + `role="region"` + 「内容名(可横向滚动)」为名的
|
|
149
|
+
可访问名;图类用 `aria-labelledby` 指向 `<figcaption>`,不手写第二份文案。
|
|
150
|
+
- 浏览器自动发现:Playwright 1.6x 的缓存目录名(`chrome-headless-shell-win64/` 等)
|
|
151
|
+
与代码里的候选**一个都不匹配**,此前靠系统 Chrome 兜底才跑得通。改为按可执行文件名
|
|
152
|
+
有界递归 + 读 `chromium.executablePath()`,并补 `%LOCALAPPDATA%` 路径。
|
|
153
|
+
- 三处判据把"文本里出现过的字样"当成"当前生效的现状":`contrast.py` 把注释里的旧色值
|
|
154
|
+
当真值、把 `@media dark` 块之后的浅色定义卷进暗色范围;`wiring-check.py` 把
|
|
155
|
+
`<style>` 里的 CSS 选择器当接线;`delta-check.js` 把 `<pre>` 里的用法示例当成
|
|
156
|
+
页面真的用了 `data-market`。三处都改成只看**会渲染 / 会执行**的结构。
|
|
157
|
+
|
|
158
|
+
### Changed
|
|
159
|
+
|
|
160
|
+
- 开发依赖 `puppeteer-core` 23 → 25(`npm audit` 从 9 high 到 0),CI 增加
|
|
161
|
+
`npm audit --omit=dev --audit-level=high`。
|
|
162
|
+
- 根导出补显式 `import` 条件;README 讲清"运行时是 ESM、`engines>=18` 只代表语法可用,
|
|
163
|
+
`require()` 需要 Node ≥ 22.12"。
|
|
164
|
+
|
|
165
|
+
### 新增 / 加强的门禁
|
|
166
|
+
|
|
167
|
+
- `i18n-labels`:静态禁止「中文字面量直接进 `aria-label` / `textContent` / `el()` 文本位」,
|
|
168
|
+
并在真浏览器里传 `labels` 断言 DOM 真的变了、不传时仍是中文。
|
|
169
|
+
- `md-inline-gate`:`md-render.py` 生成的单文件产物会把 `tokens.css` / `content.css`
|
|
170
|
+
整份内联成第二份副本,此前没有任何门禁守着它漂移(实测已落后 586 字符)。
|
|
171
|
+
- `chrome-discovery-gate`:七种 Playwright 目录布局 × 两个入口 + 空目录不回落 + 旧写法元反向。
|
|
172
|
+
- `a11y-scan` 增加 320px 的 `scrollable-region-focusable` 补扫 —— 一上线就抓出
|
|
173
|
+
`content` / `longform` / `analysis-report` / `scientific-plot` 四页真实违规。
|
|
174
|
+
- `zoom-reflow` 的页面清单改为从文件系统枚举 `04-recipes/**/*.html`(43 → 49 页)——
|
|
175
|
+
上面那个 `.prose` 缺陷就活在不属于任何组件的配方页里。
|
|
176
|
+
- `gen-behavior` 的扫描范围补上 `09-assets`:`bar` 的注入块否则是空的,
|
|
177
|
+
`flLabels is not defined` 只在浏览器门禁才炸。
|
|
178
|
+
|
|
179
|
+
### 关于外部审计报告里的三条
|
|
180
|
+
|
|
181
|
+
F8(demo 对比度)、F10(model-viewer 小溢出)、F12(缺 SemVer/废弃政策)经复核
|
|
182
|
+
**不成立**:`.bad-err` / `.btn--naive` 都在 `.cmp__bad` 豁免容器内;本机 320px
|
|
183
|
+
实测溢出 0;`STABILITY.md` / `DEPRECATIONS.md` / `deprecation-gate` 早已存在。
|
|
184
|
+
没有为"关闭报告项"去改这三处。
|
|
185
|
+
|
|
186
|
+
---
|
|
18
187
|
|
|
19
188
|
## [0.25.6] - 2026-11-04
|
|
20
189
|
|
|
@@ -70,7 +239,7 @@
|
|
|
70
239
|
按真值更正,历史段落只加注记、不改写。
|
|
71
240
|
|
|
72
241
|
上述每一项都跑了正向、反向控制与真实发布包验证;发布前全量门禁 0 FAIL。
|
|
73
|
-
|
|
242
|
+
新增的失误与纠正规则登记在内部失误台账(不随包分发)。
|
|
74
243
|
|
|
75
244
|
---
|
|
76
245
|
|
|
@@ -264,7 +433,7 @@ README 里「想知道我们踩过哪些坑」的指向,从内部文件改到
|
|
|
264
433
|
|
|
265
434
|
### 修复:9 处指向不存在文件的引用
|
|
266
435
|
|
|
267
|
-
|
|
436
|
+
内部失误台账(不入库)、`docs/ROADMAP.md`、`docs/GUIDANCE.md` 被
|
|
268
437
|
README / CONTRIBUTING / 05-audit/README 引用,但文件不存在。
|
|
269
438
|
现在全部改指向真实文件,或去掉编号(内部条号对外部读者没有意义)。
|
|
270
439
|
|
|
@@ -1268,7 +1437,7 @@ G0d 前提:G4 的替换真的改到了文件
|
|
|
1268
1437
|
现在:
|
|
1269
1438
|
|
|
1270
1439
|
- 词表统一走 `terms`(原来 outsider-audit 自己抄了一份,与 leak-scan 口径不同)
|
|
1271
|
-
-
|
|
1440
|
+
- 真实代号与用户名模式移出公开树
|
|
1272
1441
|
(`.gitignore` 里,也不在 npm `files` 里 ⇒ 不进包)
|
|
1273
1442
|
- 读不到时**不假装扫过**:`terms.private_status()` 由三道扫描器打印
|
|
1274
1443
|
「私有词表未加载 ⇒ 本次未覆盖」
|
|
@@ -2013,7 +2182,7 @@ Actions → release → Run workflow → 勾 `shoot_baseline`,
|
|
|
2013
2182
|
### 文档对齐
|
|
2014
2183
|
|
|
2015
2184
|
- `CONTRIBUTING.md`:补「全量为什么**不**在 CI 上跑」的完整理由与责任边界;
|
|
2016
|
-
补 `rc=3`
|
|
2185
|
+
补 `rc=3` 三态表;补「失败时先读本地门禁日志」。
|
|
2017
2186
|
- `docs/A11Y-MANUAL-PROTOCOL.md`:结果表补 `accordion` / `drawer` / `dropdown`
|
|
2018
2187
|
三行 —— 三个 beta 组件此前一个都不在表内,而「beta 升 stable」的判据之一
|
|
2019
2188
|
就是行为零欠账,人工读屏是唯一的用户侧证据。**状态仍写「未执行」**:
|