agentcss 0.0.0-stage → 0.11.3
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 +236 -0
- package/01-tokens/behavior/dismissable.js +132 -0
- package/01-tokens/behavior/emit.js +76 -0
- package/01-tokens/behavior/focus-return.js +81 -0
- package/01-tokens/behavior/roving.js +152 -0
- package/01-tokens/behavior/typeahead.js +124 -0
- package/01-tokens/focus-ring-demo.html +133 -0
- package/01-tokens/focus-ring.css +102 -0
- package/01-tokens/page.css +244 -0
- package/01-tokens/polyfill.js +116 -0
- package/01-tokens/responsive-demo.html +171 -0
- package/01-tokens/scrollbar-demo.html +194 -0
- package/01-tokens/scrollbar.css +179 -0
- package/01-tokens/theme-toggle.js +103 -0
- package/01-tokens/tokens.css +972 -0
- package/01-tokens/typography.css +156 -0
- package/02-primitives/badge/README.md +71 -0
- package/02-primitives/badge/badge.css +141 -0
- package/02-primitives/badge/demo.html +101 -0
- package/02-primitives/button/README.md +99 -0
- package/02-primitives/button/button.css +353 -0
- package/02-primitives/button/demo.html +200 -0
- package/02-primitives/card/README.md +145 -0
- package/02-primitives/card/card.css +246 -0
- package/02-primitives/card/demo.html +170 -0
- package/02-primitives/choice/README.md +161 -0
- package/02-primitives/choice/choice.css +395 -0
- package/02-primitives/choice/demo.html +277 -0
- package/02-primitives/combobox/README.md +79 -0
- package/02-primitives/combobox/combobox.css +223 -0
- package/02-primitives/combobox/combobox.js +485 -0
- package/02-primitives/combobox/demo.html +109 -0
- package/02-primitives/date-range/README.md +100 -0
- package/02-primitives/date-range/date-range.css +206 -0
- package/02-primitives/date-range/date-range.js +301 -0
- package/02-primitives/date-range/demo.html +104 -0
- package/02-primitives/input/README.md +170 -0
- package/02-primitives/input/demo.html +246 -0
- package/02-primitives/input/input.css +239 -0
- package/02-primitives/popover/README.md +80 -0
- package/02-primitives/popover/demo.html +150 -0
- package/02-primitives/popover/popover.css +187 -0
- package/02-primitives/popover/popover.js +331 -0
- package/02-primitives/progress/README.md +82 -0
- package/02-primitives/progress/demo.html +154 -0
- package/02-primitives/progress/progress.css +172 -0
- package/02-primitives/select/README.md +95 -0
- package/02-primitives/select/demo.html +121 -0
- package/02-primitives/select/select.css +151 -0
- package/02-primitives/select/select.js +549 -0
- package/02-primitives/separator/README.md +63 -0
- package/02-primitives/separator/demo.html +84 -0
- package/02-primitives/separator/separator.css +73 -0
- package/02-primitives/skeleton/README.md +69 -0
- package/02-primitives/skeleton/demo.html +110 -0
- package/02-primitives/skeleton/skeleton.css +96 -0
- package/02-primitives/switch/README.md +65 -0
- package/02-primitives/switch/demo.html +115 -0
- package/02-primitives/switch/switch.css +370 -0
- package/03-patterns/accordion/README.md +121 -0
- package/03-patterns/accordion/accordion.css +453 -0
- package/03-patterns/accordion/accordion.js +369 -0
- package/03-patterns/accordion/demo.html +223 -0
- package/03-patterns/content/README.md +103 -0
- package/03-patterns/content/content.css +286 -0
- package/03-patterns/content/demo.html +88 -0
- package/03-patterns/drawer/README.md +79 -0
- package/03-patterns/drawer/demo.html +101 -0
- package/03-patterns/drawer/drawer.css +129 -0
- package/03-patterns/drawer/drawer.js +258 -0
- package/03-patterns/dropdown/README.md +78 -0
- package/03-patterns/dropdown/demo.html +101 -0
- package/03-patterns/dropdown/dropdown.css +135 -0
- package/03-patterns/dropdown/dropdown.js +451 -0
- package/03-patterns/form-validation/README.md +147 -0
- package/03-patterns/form-validation/demo.html +280 -0
- package/03-patterns/form-validation/form-validation.css +263 -0
- package/03-patterns/list/README.md +150 -0
- package/03-patterns/list/demo.html +225 -0
- package/03-patterns/list/flip.js +201 -0
- package/03-patterns/list/list.css +162 -0
- package/03-patterns/nav/README.md +225 -0
- package/03-patterns/nav/demo.html +131 -0
- package/03-patterns/nav/nav.css +314 -0
- package/03-patterns/nav/nav.js +430 -0
- package/03-patterns/nav/toc.js +111 -0
- package/03-patterns/overlay/README.md +196 -0
- package/03-patterns/overlay/demo.html +241 -0
- package/03-patterns/overlay/overlay.css +319 -0
- package/03-patterns/overlay/overlay.js +563 -0
- package/03-patterns/pagination/README.md +114 -0
- package/03-patterns/pagination/demo.html +165 -0
- package/03-patterns/pagination/pagination.css +195 -0
- package/03-patterns/pagination/pagination.js +395 -0
- package/03-patterns/reveal/README.md +91 -0
- package/03-patterns/reveal/demo.html +106 -0
- package/03-patterns/reveal/reveal.css +93 -0
- package/03-patterns/reveal/reveal.js +172 -0
- package/03-patterns/states/README.md +141 -0
- package/03-patterns/states/demo.html +240 -0
- package/03-patterns/states/states.css +252 -0
- package/03-patterns/tabs/README.md +84 -0
- package/03-patterns/tabs/demo.html +139 -0
- package/03-patterns/tabs/tabs.css +172 -0
- package/03-patterns/tabs/tabs.js +307 -0
- package/03-patterns/tooltip/README.md +96 -0
- package/03-patterns/tooltip/demo.html +78 -0
- package/03-patterns/tooltip/tooltip.css +89 -0
- package/03-patterns/tooltip/tooltip.js +288 -0
- package/03-patterns/tree/README.md +94 -0
- package/03-patterns/tree/demo.html +138 -0
- package/03-patterns/tree/tree.css +113 -0
- package/03-patterns/tree/tree.js +456 -0
- package/04-recipes/analysis-report/README.md +80 -0
- package/04-recipes/analysis-report/analysis-report.css +187 -0
- package/04-recipes/analysis-report/demo.html +274 -0
- package/04-recipes/data-showcase/README.md +45 -0
- package/04-recipes/data-showcase/demo.html +247 -0
- package/04-recipes/detail-page/README.md +93 -0
- package/04-recipes/detail-page/demo.html +55 -0
- package/04-recipes/detail-page/detail-page.css +190 -0
- package/04-recipes/detail-page/full.html +60 -0
- package/04-recipes/detail-page/null.html +60 -0
- package/04-recipes/detail-page/partial.html +60 -0
- package/04-recipes/detail-page/verify-skeleton.py +19 -0
- package/04-recipes/longform/README.md +93 -0
- package/04-recipes/longform/agc-memory.svg +4 -0
- package/04-recipes/longform/content.html +610 -0
- package/04-recipes/longform/content.md +51 -0
- package/04-recipes/longform/longform.html +627 -0
- package/04-recipes/report/README.md +176 -0
- package/04-recipes/report/demo.html +355 -0
- package/04-recipes/report/report.css +364 -0
- package/04-recipes/table/README.md +103 -0
- package/04-recipes/table/demo.html +455 -0
- package/04-recipes/table/table.css +372 -0
- package/04-recipes/tierA-tokens.css +189 -0
- package/09-assets/README.md +74 -0
- package/09-assets/bar/README.md +94 -0
- package/09-assets/bar/bar.css +166 -0
- package/09-assets/bar/bar.js +320 -0
- package/09-assets/bar/demo.html +176 -0
- package/09-assets/echarts-adapter/demo.html +192 -0
- package/09-assets/echarts-adapter/echarts-adapter.js +277 -0
- package/09-assets/model-viewer/README.md +155 -0
- package/09-assets/model-viewer/demo.html +105 -0
- package/09-assets/model-viewer/model-viewer.css +143 -0
- package/09-assets/model-viewer/model-viewer.js +423 -0
- package/09-assets/scientific-plot/README.md +94 -0
- package/09-assets/scientific-plot/demo.html +252 -0
- package/09-assets/scientific-plot/scientific-plot.css +141 -0
- package/09-assets/scientific-plot/scientific-plot.js +610 -0
- package/09-assets/sparkline/README.md +153 -0
- package/09-assets/sparkline/demo.html +163 -0
- package/09-assets/sparkline/sparkline.css +158 -0
- package/09-assets/sparkline/sparkline.js +508 -0
- package/API.md +332 -0
- package/CHANGELOG.md +3138 -0
- package/CODE_OF_CONDUCT.md +64 -0
- package/CONTRIBUTING.md +89 -0
- package/ERRORS.md +1658 -0
- package/LICENSE +21 -0
- package/README.md +497 -2
- package/SECURITY.md +72 -0
- package/START-HERE.md +164 -0
- package/SUPPORT.md +109 -0
- package/adapters/presentation/README.md +113 -0
- package/adapters/presentation/check.js +101 -0
- package/adapters/presentation/deck.schema.json +74 -0
- package/adapters/presentation/to-doc.js +138 -0
- package/ai/START-HERE.md +104 -0
- package/ai/behaviors.json +380 -0
- package/ai/behaviors.schema.json +61 -0
- package/ai/capabilities.json +56 -0
- package/ai/cli.js +387 -0
- package/ai/components.json +1944 -0
- package/ai/components.meta.json +168 -0
- package/ai/components.schema.json +220 -0
- package/ai/contract.schema.json +147 -0
- package/ai/deprecations.json +55 -0
- package/ai/deprecations.schema.json +85 -0
- package/ai/motion-policy.json +22 -0
- package/ai/patch.js +323 -0
- package/ai/patch.schema.json +63 -0
- package/ai/profiles.json +92 -0
- package/ai/tokens.audience.json +55 -0
- package/ai/tokens.audience.schema.json +73 -0
- package/ai/tokens.json +762 -0
- package/ai/tokens.schema.json +58 -0
- package/ai/tokens.tree.json +752 -0
- package/ai/tokens.tree.schema.json +56 -0
- package/ai/validate.js +383 -0
- package/dist/01-tokens/behavior/dismissable.js +60 -0
- package/dist/01-tokens/behavior/emit.js +21 -0
- package/dist/01-tokens/behavior/focus-return.js +31 -0
- package/dist/01-tokens/behavior/roving.js +77 -0
- package/dist/01-tokens/behavior/typeahead.js +61 -0
- package/dist/01-tokens/focus-ring.css +1 -0
- package/dist/01-tokens/page.css +1 -0
- package/dist/01-tokens/polyfill.js +78 -0
- package/dist/01-tokens/scrollbar.css +1 -0
- package/dist/01-tokens/theme-toggle.js +61 -0
- package/dist/01-tokens/tokens.css +1 -0
- package/dist/01-tokens/typography.css +1 -0
- package/dist/02-primitives/badge/badge.css +1 -0
- package/dist/02-primitives/button/button.css +1 -0
- package/dist/02-primitives/card/card.css +1 -0
- package/dist/02-primitives/choice/choice.css +1 -0
- package/dist/02-primitives/combobox/combobox.css +1 -0
- package/dist/02-primitives/combobox/combobox.js +439 -0
- package/dist/02-primitives/date-range/date-range.css +1 -0
- package/dist/02-primitives/date-range/date-range.js +267 -0
- package/dist/02-primitives/input/input.css +1 -0
- package/dist/02-primitives/popover/popover.css +1 -0
- package/dist/02-primitives/popover/popover.js +257 -0
- package/dist/02-primitives/progress/progress.css +1 -0
- package/dist/02-primitives/select/select.css +1 -0
- package/dist/02-primitives/select/select.js +449 -0
- package/dist/02-primitives/separator/separator.css +1 -0
- package/dist/02-primitives/skeleton/skeleton.css +1 -0
- package/dist/02-primitives/switch/switch.css +1 -0
- package/dist/03-patterns/accordion/accordion.css +1 -0
- package/dist/03-patterns/accordion/accordion.js +219 -0
- package/dist/03-patterns/content/content.css +1 -0
- package/dist/03-patterns/drawer/drawer.css +1 -0
- package/dist/03-patterns/drawer/drawer.js +229 -0
- package/dist/03-patterns/dropdown/dropdown.css +1 -0
- package/dist/03-patterns/dropdown/dropdown.js +365 -0
- package/dist/03-patterns/form-validation/form-validation.css +1 -0
- package/dist/03-patterns/list/flip.js +117 -0
- package/dist/03-patterns/list/list.css +1 -0
- package/dist/03-patterns/nav/nav.css +1 -0
- package/dist/03-patterns/nav/nav.js +322 -0
- package/dist/03-patterns/nav/toc.js +76 -0
- package/dist/03-patterns/overlay/overlay.css +1 -0
- package/dist/03-patterns/overlay/overlay.js +398 -0
- package/dist/03-patterns/pagination/pagination.css +1 -0
- package/dist/03-patterns/pagination/pagination.js +273 -0
- package/dist/03-patterns/reveal/reveal.css +1 -0
- package/dist/03-patterns/reveal/reveal.js +138 -0
- package/dist/03-patterns/states/states.css +1 -0
- package/dist/03-patterns/tabs/tabs.css +1 -0
- package/dist/03-patterns/tabs/tabs.js +242 -0
- package/dist/03-patterns/tooltip/tooltip.css +1 -0
- package/dist/03-patterns/tooltip/tooltip.js +213 -0
- package/dist/03-patterns/tree/tree.css +1 -0
- package/dist/03-patterns/tree/tree.js +383 -0
- package/dist/04-recipes/analysis-report/analysis-report.css +1 -0
- package/dist/04-recipes/detail-page/detail-page.css +1 -0
- package/dist/04-recipes/report/report.css +1 -0
- package/dist/04-recipes/table/table.css +1 -0
- package/dist/04-recipes/tierA-tokens.css +1 -0
- package/dist/09-assets/bar/bar.css +1 -0
- package/dist/09-assets/bar/bar.js +222 -0
- package/dist/09-assets/echarts-adapter/echarts-adapter.js +185 -0
- package/dist/09-assets/model-viewer/model-viewer.css +1 -0
- package/dist/09-assets/model-viewer/model-viewer.js +320 -0
- package/dist/09-assets/scientific-plot/scientific-plot.css +1 -0
- package/dist/09-assets/scientific-plot/scientific-plot.js +568 -0
- package/dist/09-assets/sparkline/sparkline.css +1 -0
- package/dist/09-assets/sparkline/sparkline.js +265 -0
- package/dist/COSTS.md +56 -0
- package/docs/AI-CONTRACT.md +197 -0
- package/docs/BACKLOG.md +127 -0
- package/docs/BENCHMARK.md +252 -0
- package/docs/GUIDANCE.md +154 -0
- package/docs/INVARIANT.md +372 -0
- package/docs/LAYER-BASELINE.md +158 -0
- package/docs/RELEASING.md +311 -0
- package/docs/RFC-THEME-SCOPE.md +141 -0
- package/docs/ROADMAP.md +182 -0
- package/index.html +82 -0
- package/package.json +107 -4
- package/types/index.d.ts +310 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "减少动效(prefers-reduced-motion)下的保留清单。默认一律关掉;只有列在 keep 里的组件允许保留动画,且必须写明为什么 —— 判定见 05-audit/motion.py --reduced-audit。⚠️ 不登记却私自保留 = 红:这正是本文件存在的意义。",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"keep": {
|
|
5
|
+
"button": {
|
|
6
|
+
"file": "02-primitives/button/button.css",
|
|
7
|
+
"reason": "按钮里的 spinner 是「提交正在进行」的唯一证据。停掉它,用户会以为没点上,从而重复提交。"
|
|
8
|
+
},
|
|
9
|
+
"states": {
|
|
10
|
+
"file": "03-patterns/states/states.css",
|
|
11
|
+
"reason": "加载态停住 ⇒ 用户看到一个永远不动的界面,比有动画更糟。骨架屏的静态占位保留、扫光停止。"
|
|
12
|
+
},
|
|
13
|
+
"form-validation": {
|
|
14
|
+
"file": "03-patterns/form-validation/form-validation.css",
|
|
15
|
+
"reason": "校验中的小 spinner:没有它就没有「正在检查」的反馈,用户会重复提交表单。"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"shorten": {
|
|
19
|
+
"_comment": "颜色/透明度类允许保留但缩短到 --dur-fast。这一档**不强制**:本库没有为它逐文件改造(20 多个文件只为一个观感细节),所以不做机器判定 —— 写了却没判据的规则比没有规则更糟。",
|
|
20
|
+
"note": "组件若在 reduced 块里用 transition-duration: var(--dur-fast) 保留颜色过渡,审计不报错。"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/ai/patch.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* patch.js — 补丁 API:作用对象是**语义 id**,不是 HTML
|
|
3
|
+
*
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* 🔴 为什么是 id 而不是别的
|
|
6
|
+
* ----------------------------------------------------------------------------
|
|
7
|
+
* 三种"指向要改哪里"的方式:
|
|
8
|
+
*
|
|
9
|
+
* ① HTML 路径 「第 3 个 div 的第 2 个子节点」
|
|
10
|
+
* ⇒ 任何一次结构变动都会让它失效。改 A 的时候 B 的引用就悄悄变了。
|
|
11
|
+
* ② CSS 选择器 「.page-3 h1」
|
|
12
|
+
* ⇒ 作用域扩散到整个文档(INVARIANT I-1 / I-2)。这是事故的直接形态。
|
|
13
|
+
* ③ 语义 id
|
|
14
|
+
* ⇒ 稳定、可比、可 diff、可回放。**改别的地方不影响它。**
|
|
15
|
+
*
|
|
16
|
+
* ============================================================================
|
|
17
|
+
* 🔴 move 是**结构上不可能出错**的操作
|
|
18
|
+
* ----------------------------------------------------------------------------
|
|
19
|
+
* "改布局把内容弄没了" 这个故障,靠提醒 AI 小心是治不好的 ——
|
|
20
|
+
* 因为整篇重写时,"内容"和"位置"混在同一个字符串里。
|
|
21
|
+
*
|
|
22
|
+
* ⇒ 解法是把它们拆开:move 只改 children 数组,**一个字节的内容都不碰**。
|
|
23
|
+
* 这不是"小心一点",是**让那类错误无法被表达出来**。
|
|
24
|
+
*
|
|
25
|
+
* ============================================================================
|
|
26
|
+
* 五种操作
|
|
27
|
+
* ----------------------------------------------------------------------------
|
|
28
|
+
* create { op, node, parent, index } 新建节点并挂到 parent
|
|
29
|
+
* update { op, id, set, unset } 改属性/文本(不动结构)
|
|
30
|
+
* move { op, id, parent, index } 只改挂载位置,内容保持不变
|
|
31
|
+
* delete { op, id } 删除节点及其子树
|
|
32
|
+
* replace { op, id, node } 整体替换(保留挂载位置与 id)
|
|
33
|
+
* ============================================================================
|
|
34
|
+
*/
|
|
35
|
+
'use strict';
|
|
36
|
+
|
|
37
|
+
var OPS = ['create', 'update', 'move', 'delete', 'replace'];
|
|
38
|
+
|
|
39
|
+
// 每种操作的必填字段。与 ai/patch.schema.json 的 opRequirements 保持一致。
|
|
40
|
+
var REQUIRED = {
|
|
41
|
+
create: ['op', 'node'],
|
|
42
|
+
update: ['op', 'id'],
|
|
43
|
+
move: ['op', 'id', 'parent'],
|
|
44
|
+
delete: ['op', 'id'],
|
|
45
|
+
replace: ['op', 'id', 'node']
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function clone(v) {
|
|
49
|
+
return v === undefined ? v : JSON.parse(JSON.stringify(v));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fail(path, msg) {
|
|
53
|
+
return { path: path, msg: msg };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 校验补丁本身(不碰文档)。返回错误数组,空数组 = 合法。
|
|
58
|
+
*/
|
|
59
|
+
function validatePatch(patch) {
|
|
60
|
+
var errors = [];
|
|
61
|
+
if (!patch || typeof patch !== 'object') {
|
|
62
|
+
return [fail('', '补丁必须是一个对象')];
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(patch.ops)) {
|
|
65
|
+
return [fail('/ops', '补丁必须含 ops 数组')];
|
|
66
|
+
}
|
|
67
|
+
patch.ops.forEach(function (op, i) {
|
|
68
|
+
var p = '/ops/' + i;
|
|
69
|
+
if (!op || typeof op !== 'object') {
|
|
70
|
+
errors.push(fail(p, '操作必须是对象'));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (OPS.indexOf(op.op) < 0) {
|
|
74
|
+
errors.push(fail(p + '/op', '未知操作 ' + JSON.stringify(op.op) +
|
|
75
|
+
'(可用:' + OPS.join(' / ') + ')'));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
REQUIRED[op.op].forEach(function (k) {
|
|
79
|
+
if (op[k] === undefined) {
|
|
80
|
+
errors.push(fail(p, op.op + ' 操作缺少字段 ' + k));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (op.op === 'create' && op.node && !op.node.id) {
|
|
84
|
+
errors.push(fail(p + '/node/id', 'create 的节点必须有 id'));
|
|
85
|
+
}
|
|
86
|
+
if (op.op === 'create' && op.node && !op.node.kind) {
|
|
87
|
+
errors.push(fail(p + '/node/kind', 'create 的节点必须有 kind'));
|
|
88
|
+
}
|
|
89
|
+
if (op.parent !== undefined && op.parent !== null &&
|
|
90
|
+
typeof op.parent !== 'string') {
|
|
91
|
+
errors.push(fail(p + '/parent', 'parent 必须是节点 id 或 null(表示顶层)'));
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return errors;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function detachFromParent(nodes, id) {
|
|
98
|
+
Object.keys(nodes).forEach(function (pid) {
|
|
99
|
+
var ch = nodes[pid].children;
|
|
100
|
+
if (!Array.isArray(ch)) return;
|
|
101
|
+
var at = ch.indexOf(id);
|
|
102
|
+
while (at >= 0) {
|
|
103
|
+
ch.splice(at, 1);
|
|
104
|
+
at = ch.indexOf(id);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
var root = nodes.__root || [];
|
|
108
|
+
var at2 = root.indexOf(id);
|
|
109
|
+
while (at2 >= 0) {
|
|
110
|
+
root.splice(at2, 1);
|
|
111
|
+
at2 = root.indexOf(id);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function subtree(nodes, id) {
|
|
116
|
+
var out = [id];
|
|
117
|
+
(nodes[id].children || []).forEach(function (c) {
|
|
118
|
+
if (nodes[c]) out = out.concat(subtree(nodes, c));
|
|
119
|
+
});
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function attach(nodes, id, parent, index) {
|
|
124
|
+
if (parent === null || parent === undefined) {
|
|
125
|
+
if (!Array.isArray(nodes.__root)) nodes.__root = [];
|
|
126
|
+
if (index === undefined || index < 0 || index > nodes.__root.length) {
|
|
127
|
+
nodes.__root.push(id);
|
|
128
|
+
} else {
|
|
129
|
+
nodes.__root.splice(index, 0, id);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (!nodes[parent]) {
|
|
134
|
+
return fail('', '父节点不存在:' + parent);
|
|
135
|
+
}
|
|
136
|
+
if (!Array.isArray(nodes[parent].children)) nodes[parent].children = [];
|
|
137
|
+
var ch = nodes[parent].children;
|
|
138
|
+
if (index === undefined || index < 0 || index > ch.length) ch.push(id);
|
|
139
|
+
else ch.splice(index, 0, id);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 应用补丁。**不修改传入的 doc** —— 成功才返回新文档,失败原文档不受影响。
|
|
145
|
+
* @returns {{ok:boolean, doc:object, errors:Array, applied:number}}
|
|
146
|
+
*/
|
|
147
|
+
function apply(doc, patch) {
|
|
148
|
+
var errs = validatePatch(patch);
|
|
149
|
+
if (errs.length) return { ok: false, doc: doc, errors: errs, applied: 0 };
|
|
150
|
+
|
|
151
|
+
var work = clone(doc);
|
|
152
|
+
work.nodes = work.nodes || {};
|
|
153
|
+
// root 用内部键承载,apply 结束时写回 doc.root
|
|
154
|
+
work.nodes.__root = Array.isArray(work.root) ? work.root.slice() : [];
|
|
155
|
+
delete work.root;
|
|
156
|
+
|
|
157
|
+
var applied = 0;
|
|
158
|
+
var errors = [];
|
|
159
|
+
|
|
160
|
+
for (var i = 0; i < patch.ops.length; i++) {
|
|
161
|
+
var op = patch.ops[i];
|
|
162
|
+
var p = '/ops/' + i;
|
|
163
|
+
|
|
164
|
+
if (op.op === 'create') {
|
|
165
|
+
if (work.nodes[op.node.id]) {
|
|
166
|
+
errors.push(fail(p, '节点 id 已存在:' + op.node.id +
|
|
167
|
+
'(create 不覆盖;要覆盖请用 replace)'));
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
work.nodes[op.node.id] = clone(op.node);
|
|
171
|
+
var e = attach(work.nodes, op.node.id, op.parent, op.index);
|
|
172
|
+
if (e) { errors.push({ path: p, msg: e.msg }); delete work.nodes[op.node.id]; continue; }
|
|
173
|
+
applied++;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (op.op === 'update') {
|
|
178
|
+
var n = work.nodes[op.id];
|
|
179
|
+
if (!n) { errors.push(fail(p, '节点不存在:' + op.id)); continue; }
|
|
180
|
+
if (op.set) {
|
|
181
|
+
Object.keys(op.set).forEach(function (k) {
|
|
182
|
+
if (k === 'id') { errors.push(fail(p + '/set/id', '不允许改 id(等于换了一个节点)')); return; }
|
|
183
|
+
n[k] = clone(op.set[k]);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
(op.unset || []).forEach(function (k) {
|
|
187
|
+
if (k === 'id') { errors.push(fail(p + '/unset/id', '不允许删除 id')); return; }
|
|
188
|
+
delete n[k];
|
|
189
|
+
});
|
|
190
|
+
applied++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (op.op === 'move') {
|
|
195
|
+
if (!work.nodes[op.id]) { errors.push(fail(p, '节点不存在:' + op.id)); continue; }
|
|
196
|
+
// 🔴 内容保持不变:只改挂载关系
|
|
197
|
+
detachFromParent(work.nodes, op.id);
|
|
198
|
+
var e2 = attach(work.nodes, op.id, op.parent, op.index);
|
|
199
|
+
if (e2) { errors.push({ path: p, msg: e2.msg }); continue; }
|
|
200
|
+
applied++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (op.op === 'delete') {
|
|
205
|
+
if (!work.nodes[op.id]) { errors.push(fail(p, '节点不存在:' + op.id)); continue; }
|
|
206
|
+
var doomed = subtree(work.nodes, op.id);
|
|
207
|
+
detachFromParent(work.nodes, op.id);
|
|
208
|
+
doomed.forEach(function (d) { delete work.nodes[d]; });
|
|
209
|
+
applied++;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (op.op === 'replace') {
|
|
214
|
+
var old = work.nodes[op.id];
|
|
215
|
+
if (!old) { errors.push(fail(p, '节点不存在:' + op.id)); continue; }
|
|
216
|
+
var parentId = null, idx = -1;
|
|
217
|
+
Object.keys(work.nodes).forEach(function (pid) {
|
|
218
|
+
if (pid === '__root') return;
|
|
219
|
+
var at = (work.nodes[pid].children || []).indexOf(op.id);
|
|
220
|
+
if (at >= 0) { parentId = pid; idx = at; }
|
|
221
|
+
});
|
|
222
|
+
if (idx < 0) {
|
|
223
|
+
idx = (work.nodes.__root || []).indexOf(op.id);
|
|
224
|
+
parentId = null;
|
|
225
|
+
}
|
|
226
|
+
var kept = clone(op.node);
|
|
227
|
+
kept.id = op.id; // id 必须保持
|
|
228
|
+
subtree(work.nodes, op.id).forEach(function (d) {
|
|
229
|
+
if (d !== op.id) delete work.nodes[d];
|
|
230
|
+
});
|
|
231
|
+
work.nodes[op.id] = kept;
|
|
232
|
+
detachFromParent(work.nodes, op.id);
|
|
233
|
+
var e3 = attach(work.nodes, op.id, parentId, idx);
|
|
234
|
+
if (e3) { errors.push({ path: p, msg: e3.msg }); continue; }
|
|
235
|
+
applied++;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (errors.length) {
|
|
241
|
+
return { ok: false, doc: doc, errors: errors, applied: applied };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
var root = work.nodes.__root || [];
|
|
245
|
+
delete work.nodes.__root;
|
|
246
|
+
work.root = root;
|
|
247
|
+
return { ok: true, doc: work, errors: [], applied: applied };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 确定性检查:同一份文档 + 同一份补丁,跑两次结果必须逐字节相同。
|
|
252
|
+
* ⚠️ 这是"可回放"的可验证形式 —— 不是声称,是能跑出来的断言。
|
|
253
|
+
*/
|
|
254
|
+
function isDeterministic(doc, patch) {
|
|
255
|
+
var a = apply(doc, patch);
|
|
256
|
+
var b = apply(doc, patch);
|
|
257
|
+
if (a.ok !== b.ok) return false;
|
|
258
|
+
return JSON.stringify(a.doc) === JSON.stringify(b.doc);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 反向补丁(撤销用)。
|
|
263
|
+
* ⚠️ 只对**已成功应用**的补丁有意义;失败补丁无法求逆。
|
|
264
|
+
*/
|
|
265
|
+
function invert(doc, patch) {
|
|
266
|
+
var ops = [];
|
|
267
|
+
var errs = validatePatch(patch);
|
|
268
|
+
if (errs.length) return { ok: false, ops: [], errors: errs };
|
|
269
|
+
|
|
270
|
+
var nodes = doc.nodes || {};
|
|
271
|
+
for (var i = patch.ops.length - 1; i >= 0; i--) {
|
|
272
|
+
var op = patch.ops[i];
|
|
273
|
+
if (op.op === 'create') {
|
|
274
|
+
ops.push({ op: 'delete', id: op.node.id });
|
|
275
|
+
} else if (op.op === 'delete') {
|
|
276
|
+
var n = nodes[op.id];
|
|
277
|
+
if (!n) return { ok: false, ops: [], errors: [fail('/ops/' + i, '撤销失败:原节点已不存在')] };
|
|
278
|
+
var parentId = null, idx = 0;
|
|
279
|
+
Object.keys(nodes).forEach(function (pid) {
|
|
280
|
+
var at = (nodes[pid].children || []).indexOf(op.id);
|
|
281
|
+
if (at >= 0) { parentId = pid; idx = at; }
|
|
282
|
+
});
|
|
283
|
+
if (parentId === null) idx = (doc.root || []).indexOf(op.id);
|
|
284
|
+
ops.push({ op: 'create', node: clone(n), parent: parentId, index: idx });
|
|
285
|
+
} else if (op.op === 'move') {
|
|
286
|
+
var parent0 = null, idx0 = 0;
|
|
287
|
+
Object.keys(nodes).forEach(function (pid) {
|
|
288
|
+
var at = (nodes[pid].children || []).indexOf(op.id);
|
|
289
|
+
if (at >= 0) { parent0 = pid; idx0 = at; }
|
|
290
|
+
});
|
|
291
|
+
if (parent0 === null) idx0 = (doc.root || []).indexOf(op.id);
|
|
292
|
+
ops.push({ op: 'move', id: op.id, parent: parent0, index: idx0 });
|
|
293
|
+
} else if (op.op === 'update' || op.op === 'replace') {
|
|
294
|
+
var before = nodes[op.id];
|
|
295
|
+
if (!before) {
|
|
296
|
+
return { ok: false, ops: [], errors: [fail('/ops/' + i, '撤销失败:原节点已不存在')] };
|
|
297
|
+
}
|
|
298
|
+
if (op.op === 'update') {
|
|
299
|
+
var set = {}, unset = [];
|
|
300
|
+
(op.set ? Object.keys(op.set) : []).forEach(function (k) {
|
|
301
|
+
if (before[k] === undefined) unset.push(k);
|
|
302
|
+
else set[k] = clone(before[k]);
|
|
303
|
+
});
|
|
304
|
+
(op.unset || []).forEach(function (k) {
|
|
305
|
+
if (before[k] !== undefined) set[k] = clone(before[k]);
|
|
306
|
+
});
|
|
307
|
+
ops.push({ op: 'update', id: op.id, set: set, unset: unset });
|
|
308
|
+
} else {
|
|
309
|
+
ops.push({ op: 'replace', id: op.id, node: clone(before) });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return { ok: true, ops: ops, errors: [] };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
module.exports = {
|
|
317
|
+
OPS: OPS,
|
|
318
|
+
REQUIRED: REQUIRED,
|
|
319
|
+
validatePatch: validatePatch,
|
|
320
|
+
apply: apply,
|
|
321
|
+
invert: invert,
|
|
322
|
+
isDeterministic: isDeterministic
|
|
323
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://frontend-lib.dev/ai/patch.schema.json",
|
|
4
|
+
"title": "frontend-lib 补丁操作契约",
|
|
5
|
+
"$comment": [
|
|
6
|
+
"🔴 补丁的作用对象是【语义 id】,不是 HTML、也不是 CSS 选择器。",
|
|
7
|
+
"",
|
|
8
|
+
"为什么:",
|
|
9
|
+
" · HTML 路径(第 3 个 div 的第 2 个子节点)—— 结构一变就失效",
|
|
10
|
+
" · CSS 选择器(.page-3 h1)—— 会把作用域扩散到整个文档(I-1/I-2)",
|
|
11
|
+
" · 语义 id —— 稳定、可比、可回放、可 diff",
|
|
12
|
+
"",
|
|
13
|
+
"五种操作:create / update / move / delete / replace",
|
|
14
|
+
"其中 **move 是内容保持操作**:只改挂载位置,一个字节的内容都不碰。",
|
|
15
|
+
"这是对『改布局把内容改没了』这个具体故障的直接回答 ——",
|
|
16
|
+
"把它做成一种**结构上不可能出错**的操作,而不是靠提醒 AI 小心。"
|
|
17
|
+
],
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["schemaVersion", "ops"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"schemaVersion": { "type": "string" },
|
|
22
|
+
"ops": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": { "$ref": "#/definitions/op" }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"definitions": {
|
|
28
|
+
"op": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["op"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"op": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"enum": ["create", "update", "move", "delete", "replace"]
|
|
35
|
+
},
|
|
36
|
+
"id": { "type": "string" },
|
|
37
|
+
"node": { "type": "object" },
|
|
38
|
+
"parent": { "type": ["string", "null"] },
|
|
39
|
+
"index": { "type": "number" },
|
|
40
|
+
"set": { "type": "object" },
|
|
41
|
+
"unset": { "type": "array", "items": { "type": "string" } },
|
|
42
|
+
"$comment": { "type": "string" }
|
|
43
|
+
},
|
|
44
|
+
"allOf": [
|
|
45
|
+
{
|
|
46
|
+
"$comment": "五种操作各自的必需字段(用 allOf 表达,便于校验器逐条给出原因)"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"opRequirements": {
|
|
52
|
+
"$comment": [
|
|
53
|
+
"这张表不是 JSON Schema 标准键,是我们的**元信息**,",
|
|
54
|
+
"由 ai/validate.js 读取执行(表达力比 allOf/if-then 强,且报错可读)。",
|
|
55
|
+
"放在这里而不是写在代码里,是为了让『每种操作需要什么』这件事可被外部读取。"
|
|
56
|
+
],
|
|
57
|
+
"create": ["op", "node"],
|
|
58
|
+
"update": ["op", "id"],
|
|
59
|
+
"move": ["op", "id", "parent"],
|
|
60
|
+
"delete": ["op", "id"],
|
|
61
|
+
"replace": ["op", "id", "node"]
|
|
62
|
+
}
|
|
63
|
+
}
|
package/ai/profiles.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": [
|
|
3
|
+
"三档 Profile:约束的【松紧】是可以选的,但 Invariant 在任何档位下都必须成立。",
|
|
4
|
+
"",
|
|
5
|
+
"🔴 这为什么重要(架构悖论的解)",
|
|
6
|
+
" 恐惧是:今天为防 AI 犯错加的约束,明天变成更强 AI 的天花板。",
|
|
7
|
+
" 解法不是'以后再说',是**把松紧做成可选项 + 把边界分层**:",
|
|
8
|
+
" · Invariant(客观事实) —— 任何档位都不能违反",
|
|
9
|
+
" · Contract(版本化约定) —— 任何档位都不能违反,但可以升版本",
|
|
10
|
+
" · Guidance(工作建议) —— creative 档位下只是提示",
|
|
11
|
+
" · 组件成熟度门槛 —— 三档不同,这是松紧的来源",
|
|
12
|
+
" ⇒ 模型变强 ⇒ 用户切到 creative,而不是我们去删约束。",
|
|
13
|
+
" ⇒ 约束永远留着给需要的人,不成为任何人的天花板。"
|
|
14
|
+
],
|
|
15
|
+
"schemaVersion": "1.0.0",
|
|
16
|
+
"profiles": {
|
|
17
|
+
"creative": {
|
|
18
|
+
"label": "放开了做",
|
|
19
|
+
"allowedMaturity": ["alpha", "beta", "stable"],
|
|
20
|
+
"allowedUnknownComponent": true,
|
|
21
|
+
"allowedUnknownVariant": true,
|
|
22
|
+
"allowedUnknownState": true,
|
|
23
|
+
"allowedExtensions": true,
|
|
24
|
+
"guidanceLevel": "advisory",
|
|
25
|
+
"$comment": [
|
|
26
|
+
"适合:探索期、原型、模型很强且你能自己看结果。",
|
|
27
|
+
"未知组件/变体/状态都放行 —— 因为本库的组件集合本来就不完整,",
|
|
28
|
+
"不让用新东西等于让使用者为我们的缺口买单。"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"standard": {
|
|
32
|
+
"label": "默认",
|
|
33
|
+
"allowedMaturity": ["beta", "stable"],
|
|
34
|
+
"allowedUnknownComponent": true,
|
|
35
|
+
"allowedUnknownVariant": true,
|
|
36
|
+
"allowedUnknownState": false,
|
|
37
|
+
"allowedExtensions": true,
|
|
38
|
+
"guidanceLevel": "warn",
|
|
39
|
+
"$comment": [
|
|
40
|
+
"适合:日常。不允许用**没在契约里登记过的状态取值**,",
|
|
41
|
+
"因为状态取值写错 = 组件静默停在错误状态(I-8/I-9),",
|
|
42
|
+
"这是最难靠肉眼发现的一类错误。",
|
|
43
|
+
"但允许没登记过的组件/变体 —— 我们的清单不全,不该卡住使用者。"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"strict": {
|
|
47
|
+
"label": "只许用验证过的",
|
|
48
|
+
"allowedMaturity": ["stable"],
|
|
49
|
+
"allowedUnknownComponent": false,
|
|
50
|
+
"allowedUnknownVariant": false,
|
|
51
|
+
"allowedUnknownState": false,
|
|
52
|
+
"allowedExtensions": false,
|
|
53
|
+
"guidanceLevel": "error",
|
|
54
|
+
"$comment": [
|
|
55
|
+
"适合:交付物、要交给别人维护的东西、无人值守的自动化流水线。",
|
|
56
|
+
"只允许成熟度 = stable 的组件(名单/数量以 ai/components.json 的 maturity 字段为准,",
|
|
57
|
+
"不要信这里的数字 —— 数字会漂移,机器文件不会)。",
|
|
58
|
+
"",
|
|
59
|
+
"⚠️ 诚实说明:这一档位下 **beta 组件不可用**(0.7.6 实测 7 个 / 共 27 个)。",
|
|
60
|
+
" 这不是缺陷,是信息 —— 它告诉你这批组件还有**行为欠账**:",
|
|
61
|
+
" 行为矩阵里标着 manual(手写副本未收编)或 gap(该做没做),",
|
|
62
|
+
" ⇒ 改一处不会全库跟着变(I-14 的病)。",
|
|
63
|
+
" 欠账清零后档位会自动升 stable(maturity 是算出来的,见 components.meta.json)。",
|
|
64
|
+
"",
|
|
65
|
+
"🔴 0.7.6(M8)之前这里写的是「11 个不可用、现已全部升完」—— 那时 27 个全 stable,",
|
|
66
|
+
" ladder 等于没有鉴别力。补上第 4 条判据(行为欠账)后,7 个回到 beta。**这是**",
|
|
67
|
+
" **判据变严了,不是组件变差了**:同一批组件,以前被算作 stable 只因为判据没问到点上。"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"alwaysEnforced": {
|
|
72
|
+
"$comment": [
|
|
73
|
+
"无论哪一档都必须成立 —— 这些是 Invariant,不是偏好。",
|
|
74
|
+
"",
|
|
75
|
+
"🔴 适用范围:以下规则约束的是【采用本库契约】的场景",
|
|
76
|
+
" (用本库组件,或明确按本库范式产出页面)。",
|
|
77
|
+
" 它们不是对所有 HTML / 网站任务的普遍禁令 ——",
|
|
78
|
+
" 改一个已有网站时,宿主项目原有的体系优先;",
|
|
79
|
+
" 本库组件不适用时,可以用别的实现方式。",
|
|
80
|
+
"",
|
|
81
|
+
"措辞与 docs/INVARIANT.md 保持同步:",
|
|
82
|
+
" I-5 区分内容色与效果色;I-2 说明的是代价而非绝对禁止;",
|
|
83
|
+
" I-8 说的是「状态的存在依赖 JS 执行过」,不是「class 不可读」。"
|
|
84
|
+
],
|
|
85
|
+
"rules": [
|
|
86
|
+
"不得写裸标签选择器 —— 裸标签的作用域是整个文档,会改掉宿主页面(I-1)",
|
|
87
|
+
"本该跟随主题的内容色与间距不得绕过令牌;效果色(阴影等)不受此约束(I-5)",
|
|
88
|
+
"用页面级 override 表达局部改动时特异性只增不减 —— 这是代价,不是绝对禁止(I-2)",
|
|
89
|
+
"状态不得只存在于由 JS 维护的类名里 —— 否则其存在依赖 JS 执行过(I-8)"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"source": "01-tokens/tokens.css",
|
|
4
|
+
"$comment": "手写,不是生成的。生成出来的『契约』只跟再生成一遍比,没有引入新信息(见 docs/INVARIANT.md I-10 实证 9)。这个文件回答的问题是:每个令牌是**给谁用的**。",
|
|
5
|
+
|
|
6
|
+
"audiences": {
|
|
7
|
+
"public": "契约的一部分:文档承诺使用者可以直接引用。改名/改值算破坏性变更。",
|
|
8
|
+
"internal": "库自己的实现细节:使用者不该依赖。可以改名改值,不必公告。",
|
|
9
|
+
"reference": "给人/脚本读的常量:CSS 里**物理上不可能**被 var() 引用(断点进不了 media query 条件),所以『没人引用』是它的本分,不是病。"
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"families": {
|
|
13
|
+
"--accent": { "audience": "public", "why": "品牌色三档(--accent / -hover / -soft):换主题色的第一入口" },
|
|
14
|
+
"--border": { "audience": "public", "why": "描边三档:控件 / 装饰 / 强装饰" },
|
|
15
|
+
"--bp-": { "audience": "reference", "why": "断点:CSS media query 条件里用不了 var(),只能当锚点给流体计算和脚本查询" },
|
|
16
|
+
"--danger": { "audience": "public", "why": "危险色(--danger / -hover / -bg)" },
|
|
17
|
+
"--dur-": { "audience": "public", "why": "时长阶梯:快 / 基准 / 慢" },
|
|
18
|
+
"--ease-": { "audience": "public", "why": "缓动阶梯:默认 / 进出 / 滑入" },
|
|
19
|
+
"--fs-": { "audience": "public", "why": "字号阶梯:阶梯允许有暂未使用的档位(那是阶梯,不是死代码)" },
|
|
20
|
+
"--info": { "audience": "public", "why": "信息色(--info / -bg)" },
|
|
21
|
+
"--lh-": { "audience": "public", "why": "行高阶梯:紧 / 基准 / 松" },
|
|
22
|
+
"--lift-": { "audience": "public", "why": "动效的位移量(translate 的距离),不是阴影 —— 同样时长下幅度不统一就会感觉乱" },
|
|
23
|
+
"--measure-": { "audience": "public", "why": "版心阶梯:正文 / 标题 / 导语 / 引言 / 边注 / 页面列宽" },
|
|
24
|
+
"--mono": { "audience": "public", "why": "等宽字族" },
|
|
25
|
+
"--paper": { "audience": "public", "why": "页面底色" },
|
|
26
|
+
"--popover-": { "audience": "internal", "why": "气泡组件自己的尺寸与时长,是组件调参不是主题令牌" },
|
|
27
|
+
"--progress": { "audience": "internal", "why": "进度条内部参数(--progress 是数值本身,不是族前缀)" },
|
|
28
|
+
"--r-": { "audience": "public", "why": "圆角阶梯" },
|
|
29
|
+
"--raw-": { "audience": "internal", "why": "L1 原始值层(D5 分层):**当前配色的取值本身**,由语义令牌 var() 引用。组件只该用语义名;换一套品牌配色时这一层整体替换 ⇒ 使用者依赖它就是依赖实现细节。它不进 ai/tokens.tree.json(对外契约只承诺语义名)" },
|
|
30
|
+
"--ring-": { "audience": "internal", "why": "焦点环尺寸,只由 focus-ring.css 消费" },
|
|
31
|
+
"--sans": { "audience": "public", "why": "无衬线字族" },
|
|
32
|
+
"--scrim": { "audience": "internal", "why": "遮罩底色,overlay 用" },
|
|
33
|
+
"--scrollbar-": { "audience": "internal", "why": "滚动条外观,scrollbar.css 用" },
|
|
34
|
+
"--serif": { "audience": "public", "why": "衬线字族" },
|
|
35
|
+
"--shadow-": { "audience": "public", "why": "阴影阶梯 1 / 2" },
|
|
36
|
+
"--skeleton-":{ "audience": "internal", "why": "骨架屏内部参数" },
|
|
37
|
+
"--sp-": { "audience": "public", "why": "间距阶梯 1–8" },
|
|
38
|
+
"--success": { "audience": "public", "why": "成功色(--success / -bg)" },
|
|
39
|
+
"--surface": { "audience": "public", "why": "表面色(--surface / -sunken / -raised)" },
|
|
40
|
+
"--switch-": { "audience": "internal", "why": "开关轨道色,只由 switch 消费" },
|
|
41
|
+
"--sz-": { "audience": "public", "why": "触控尺寸(--sz-touch / -sm):点按目标最小边" },
|
|
42
|
+
"--text-": { "audience": "public", "why": "文字色五档(primary / secondary / tertiary / on-accent / on-solid)" },
|
|
43
|
+
"--tooltip-": { "audience": "internal", "why": "气泡的一对反色面(bg / fg / border),只由 tooltip 消费。它替代了原先把 `prefers-color-scheme` 写进组件的那段分支(RFC-THEME-SCOPE D5)—— 是组件实现细节,不承诺改名" },
|
|
44
|
+
"--tracking-":{ "audience": "public", "why": "字距阶梯(标题 / 常规)" },
|
|
45
|
+
"--warning": { "audience": "public", "why": "警告色(--warning / -bg)" },
|
|
46
|
+
"--z-": { "audience": "internal", "why": "层级栈:库内部层序,使用者不该依赖具体数值(依赖了就会和以后的层打架)" }
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"tokens": {},
|
|
50
|
+
|
|
51
|
+
"budget": {
|
|
52
|
+
"orphan": 4,
|
|
53
|
+
"$comment": "棘轮:孤儿令牌总数只能降。降了就把这个数字一起改小,不许偷偷升。2026-10-10:--lh-loose / --fs-xl 被新增 recipe 引用,7→5;同日 04-recipes/report 改在自身层内重定义边注宽 ⇒ --measure-aside 由『被引用』回到『无引用』,5→5→再核为 4。"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://frontend-lib.dev/ai/tokens.audience.schema.json",
|
|
4
|
+
"title": "frontend-lib 令牌受众登记",
|
|
5
|
+
"$comment": [
|
|
6
|
+
"描述 ai/tokens.audience.json 的形状:**每个令牌是给谁用的**。",
|
|
7
|
+
"",
|
|
8
|
+
"🔴 为什么这份登记要存在:『声明了 108 个令牌、9 个从没被引用』早就量出来过,",
|
|
9
|
+
" 但它只是一句观察,不是判据 ⇒ 没人被它拦住过。而那 9 个根本不是同一种东西:",
|
|
10
|
+
" · 断点 物理上不可能被 var() 引用(进不了 media query 条件)",
|
|
11
|
+
" · 阶梯档位 没人用是本分(那是阶梯)",
|
|
12
|
+
" · 为某组件造的 造它的人以为接上了,其实没有 ⇒ 真缺陷",
|
|
13
|
+
" · 被取代的遗留 死重量 ⇒ 真缺陷",
|
|
14
|
+
" 在『只数孤儿个数』的视角下它们完全一样 ⇒ 必须先登记受众,判据才有鉴别力。",
|
|
15
|
+
"",
|
|
16
|
+
"🔴 三种受众:",
|
|
17
|
+
" public 契约的一部分:文档承诺使用者可以直接引用;改名/改值算破坏性变更",
|
|
18
|
+
" internal 库自己的实现细节:使用者不该依赖",
|
|
19
|
+
" reference 给人/脚本读的常量:CSS 里物理上不可能被 var() 引用",
|
|
20
|
+
"",
|
|
21
|
+
"⚠️ 本文件是**手写**的,不是生成的。生成出来的『契约』只跟再生成一遍比,",
|
|
22
|
+
" 没有引入任何新信息(docs/INVARIANT.md I-10 实证 9)。",
|
|
23
|
+
"⚠️ 本 schema 只用了 ai/validate.js 白名单里的关键字(propertyNames 不在白名单)。"
|
|
24
|
+
],
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["schemaVersion", "audiences", "families", "tokens", "budget"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"schemaVersion": { "type": "string" },
|
|
29
|
+
"source": { "type": "string" },
|
|
30
|
+
"$comment": { "type": "string" },
|
|
31
|
+
"audiences": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"required": ["public", "internal", "reference"],
|
|
34
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
35
|
+
},
|
|
36
|
+
"families": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^--[a-z][a-z0-9-]*$": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["audience", "why"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"audience": { "enum": ["public", "internal", "reference"] },
|
|
44
|
+
"why": { "type": "string", "minLength": 1 }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": false
|
|
49
|
+
},
|
|
50
|
+
"tokens": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"patternProperties": {
|
|
53
|
+
"^--[a-z][a-z0-9-]*$": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": ["audience", "why"],
|
|
56
|
+
"properties": {
|
|
57
|
+
"audience": { "enum": ["public", "internal", "reference"] },
|
|
58
|
+
"why": { "type": "string", "minLength": 1 }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"additionalProperties": false
|
|
63
|
+
},
|
|
64
|
+
"budget": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"required": ["orphan"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"orphan": { "type": "integer", "minimum": 0 },
|
|
69
|
+
"$comment": { "type": "string" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|