Files
aurora-admin/CHANGELOG.md
T
aurora-admin f1fbfc2ddb
Regression / regression (push) Canceled after 0s
feat(品牌标识): 几何 K 图标(favicon/顶栏标记/theme-color) + 并行会话成果入库
## 品牌标识(本次会话)

起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」,
那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」,
移动端站已是「K」;移动端文档站则完全没有 favicon)。

- 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边;
  描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」
- 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark);
  顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28))
- 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg)
- 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留)
- 移动端 7 个模板补 favicon(此前计数 0)

验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/
theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致;
两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。

## 并行会话成果(本次一并入库)

- 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具
  + IconPreview 预览页 + ICON-SPEC.md 冻结规格
- 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁
- PC 组件:103 个大后台组件 / 组件11 批次
- uni-app:PC 端试点 + 移动端端实现 + 真实编译验证

## 工程

- .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库)
- CHANGELOG 补品牌标识条目
- ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
2026-09-21 10:05:48 +08:00

1681 lines
188 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
所有显著改动按 [SemVer](https://semver.org/) 记录于此。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
## [Unreleased]
### 品牌标识 · 几何 K 图标(favicon / 顶栏标记 / theme-color)
> 起因:品牌此前**没有任何图形标识** —— 唯一 favicon 是内联 data-URI 里的字母「A」,
> 那是 v2.0.0「Aurora Admin → Kole UI」改名时漏掉的一处(PC 顶栏也是「A」,
> 而移动端站已是「K」;移动端文档站则完全没有 favicon)。
> 落地:几何 K(三笔画断笔描边),两端同源,含 favicon / 顶栏标记 / theme-color。
#### 设计
- **几何**:24 网格上三个**互不接触**的笔画(竖笔 + 上斜 + 下斜),圆头描边。
笔画不接触既表达「独立组件拼组成系统」,也保证 16px 浏览器标签页尺寸下不糊。
- **描边 2.25**:在 24 网格下,16px 渲染时正好是 **1.5px** —— 等于规范原文
「线性图标,描边1.5px」(`.design_library/kole-ui/specs/组件1.txt`)。
实测比对了 1.5 / 1.875 / 2.25 三档在 20px 顶栏与 16px 标签页下的观感。
- **圆角 rx=6**(24 网格)= 32px 下 8px,即令牌 `--kole-radius-large`。
#### 取色分两套(刻意,不是不一致)
- **favicon 硬编码** `#2F54EB` + `#FFFFFF`:它渲染在**浏览器标签栏**,不继承 `html.kole-dark`。
若走令牌,暗色下 `--kole-color-text-inverse` = `#14161C`,会变成「亮蓝底 + 近黑标记」,
在浅色标签栏上反而发闷。
- **两端顶栏标记走 `currentColor`**:实测暗色下自动解析为 `rgb(20,22,28)`、
底色转 `rgb(107,140,255)`(令牌化行为保真,与同页其它图标一致)。
#### 新增
- `theme-color` 双条(light `#FFFFFF` / dark `#1C1F26`,取 `--kole-color-card-bg`,
即 `.topbar` / `.m-top` 的 background,色值与视觉连续)。PC 写在 `site/index.html`;
移动端由新增的 `__BRAND_HEAD__` 占位符经 `tools/build-mobile.mjs` 注入 7 个模板。
#### 修正
- `site/index.html` 的 favicon 字母 `A` → 几何 K;顶栏 `.logo-mark` 的 `A` → 几何 K
(**类名与 32×32 盒模型不变** —— ≤1366px 时它是顶栏唯一品牌标识)。
- `tools/build-mobile.mjs` 移动端顶栏标记 `K` 文字 → 几何 K 图形(保留 `class="m-logo-mark"`,
`verify-mobile-docs` 会逐页断言它存在)。
- `site/app.js` 首页 hero 标语 `KOLE ADMIN DESIGN SYSTEM` → `KOLE UI DESIGN SYSTEM`
(同一改名的变形残留)。
- 移动端文档站 7 个模板补 favicon(此前 `rel="icon"` 计数为 0,标签页显示默认地球图标)。
#### 验收
- 门禁:`verify-site-routing` OK(103 / 412 / 521)· `verify-site-routes` OK(零 4xx)·
`verify-mobile-docs` OK(12 条 · 53 页)· `verify-mobile-site` OK(263 条 · 50 页,
控制台 0 错误)· `verify:isolation` OK(31 条)· `verify:theme` / `verify:nav` /
`verify:i18n` / `verify-icons` 全 OK。
- 回归:PC **1464/1464** · 移动端 **807/807**,**各连跑 8 次一致**。
- 浏览器实测:PC 与移动端 favicon **405 字节逐字节一致**;页面 DOM 中 `>A<` 清零;
PC 站控制台错误从 1(favicon 404)降为 **0**。
- 已知边界:`og:image` / `apple-touch-icon` / web manifest 未做(需位图管线,
见 ROADMAP S8-P4)。
### 图标系统(PC 5 端 + 移动端 6 端 + 全量图标库 + 预览页)
> 起因:用户要求「图标组件参考 TDesign / Element 等主流组件库,看他们的 icon 是怎么专门设计的、
> 设计内容有哪些,覆盖他们全部拥有的 icon 图标和功能」。
> 落地:**2576 个图标**(TDesign 线性 1334 + 面性 1020 + Element Plus 293,均 MIT),
> 11 个端实现全部接通,新增 5 个构建/门禁工具与 1 个图标预览页。
> 冻结规格:`.design_library/kole-ui/icons/ICON-SPEC.md`。
#### 新增:图标数据管线(离线可复现,上游校验和钉死)
- `tools/fetch-icon-sources.mjs` — 拉取并**归一化**上游 SVG → `sources.json`。
逐包校验 sha256(防「某天图标悄悄变了」);纯函数可单独 import(无副作用)。
- `tools/build-icons.mjs` — 编译出 `manifest.json`(清单)/ `registry.json`(渲染数据)/
`viewbox.js`(viewBox 例外表)/ `core.js`(内联运行时)/ `ATTRIBUTION.md`(MIT 归属,分发必带)。
- `tools/build-icon-aliases.mjs` — 别名层,让 **Element UI 全部 282 个历史图标名**与
移动端既有 9 个字形名都能落到真实图标(**旧调用点零破坏**)。
- `tools/inject-icon-table.mjs` — 把 core 表注入 11 个端实现的标记块,杜绝「11 份手抄表必然漂移」。
- `tools/verify-icons.mjs` — **11 条静态断言**(产物一致 / 无 `view-box` 残留 / 描边全 1.5 /
core 可渲染 / 别名不遮蔽 / 各端都能查到名 / 分组覆盖率 / 路径数字与源逐值一致)。
#### 修正:两个上游缺陷在构建期抹平(此处是本仓库的增量,不是照抄)
1. **`view-box` 拼写错误**:TDesign **全部 2356 个** SVG 把属性写成 `view-box`(无效属性,
浏览器忽略 → 图标按默认视口缩放)。构建期两种拼写都读,产物统一写回规范的 `viewBox`。
2. **描边宽度 2 → 1.5**:上游为 `stroke-width="2"`(24 网格),本仓库规范
(`组件1.txt`「图标规范:线性图标,描边1.5px,尺寸16/20/24px」)要求 1.5,构建期归一。
另丢弃 `fill="transparent"` 命中区路径(那是热区不是视觉内容)。
#### 修正:自测发现的数据损坏缺陷(构建期把坐标改错)
- **现象**:归一化把路径数字四舍五入到 2 位小数,看似无害;但 SVG path 允许「隐式分隔」——
`q-13.005.48-22.5` 是**三个**数(`-13.005`、`.48`、`-22.5`,靠 `-` 和 `.` 当分隔符)。
把 `-13.005` 舍成 `-13` 后,后面的 `.48` **粘成** `-13.48` —— 那是另一个坐标,
图形画错**且不报任何错**。实测踩到 Element Plus 的 `quartz-watch`(靠浏览器控制台才发现)。
- **修法**:原本有小数位的数字,输出**至少保留 1 位小数**(`-13.005` → `-13.0`),
隐式相连的 `.48` 不可能再被吸收;并在 `optimizePath` 内**复核数字序列**,不一致就原样返回。
- **防复发**:新增门禁断言 **V8「路径数字与源逐值一致」**,比对全部 2576 个图标。
已做**变异测试**:把该 bug 重新注入 → V8 当场 FAIL(`数字个数 461 ≠ 源 462`),判据有区分力。
- 代价:`registry.json` 707KB(修前 677KB,+30KB 换正确性)。
#### 修正:非 `<path>` 图形被静默丢弃(覆盖率审计发现)
- **现象**:解析器只认 `<path d>`,而上游有 **36 个文件**用 `<circle cx cy r>` / `<ellipse>`
表达图形(TDesign 的 `circle` / `round` / `brightness` / `image` 等)。这些图形被**静默丢掉** ——
图标页面上"渲染成功"却是空的,构建与回归都不报错。TDesign 的 `circle` 与 `round` 两个图标
因此整个消失,是**覆盖率审计**(对三个参考库逐名比对)才暴露的。
- **修法**:解析器同时处理 `<path>` / `<circle>` / `<ellipse>`,把圆与椭圆等价换算成
path 的两段 arc(`M cx cy-r A r r 0 1 1 cx cy+r A r r 0 1 1 cx cy-r Z`)。
- **结果**:图标数 2574 → **2576**;对三家参考库的覆盖率现为
**TDesign 2356/2356、Element Plus 293/293(各 100%)**、Element UI 280/282(余 2 个是解析碎片名)。
- 校验:两个恢复的图标在浏览器实测有真实几何(path 长度 63 / 44,分别对应 r=10 与 r=7 的圆)。
#### 分层与体积(按实测决定,不是拍脑袋)
- **core 151 个内联**(`core.js` 35KB),`standard` 1989 / `extended` 434 按需加载。
- **为何不全内联**:全量 670KB,内联等于给每一页都加 670KB,直接违背 ROADMAP 的性能预算精神。
#### 组件 API(PC 5 端 + 移动端 6 端,语义统一)
- props:`name` / `size`(关键字 `small|default|large|xlarge` **或任意 CSS 长度**,对齐 TDesign 双模式)/
`tone`(`default|brand|secondary|danger|success|warning`)/ `spin` / `label`。
- **向后兼容**:PC 既有 `icon` prop(字形字符,默认 `'★'`)与移动端既有 9 名字形表全部保留,`name` 优先。
- 无障碍:`label` 有值 → `role="img"` + `aria-label`,无值 → `aria-hidden`;
`spin` 在 `prefers-reduced-motion: reduce` 下停转;语义色对比度 ≥ 3:1(实测 5.10–5.87:1)。
- **viewBox 按「路径串 → 网格」反查而非按名**:别名指向的 1024 网格图标若按名查会取错网格
(实测影响 160 个名字,会导致图标拉伸变形)。
#### 新增:图标预览页 `site/icon-preview.html`
规范 `组件5.txt`「图标预览 IconPreview:展示系统所有图标 / 网格布局,支持搜索 /
点击图标复制名称或代码 / 可调整图标大小」声明已久但从未实现。本次补齐:
2576 图标网格、搜索、点击复制名称、Shift+点击复制内联 SVG、四档尺寸切换、仅线性过滤;
分两段加载(索引 51KB 先出骨架,路径数据按需);
入口挂在图标组件页代码条 footer,i18n 双语(`图标预览` / `Icon gallery`)。
#### 已知未覆盖 / 遗留
- 移动端 uni-app 端用 **CSS mask(data-URI SVG)** 承载字形(小程序无 DOM、无内联 SVG)。
H5 目标已实测;**mp-weixin / app-plus 的遮罩渲染未在真机验证**,已登记进契约 `unknowns`。
- `verify-emits-parse.mjs` 的文件数期望值陈旧(158,应为 103×2=206)—— **本次之前就存在**
(`git show HEAD:` 确认),已登记为 ROADMAP `S8-P1`。
- `verify-cross-platform.mjs` 把注入的图标数据表误判为组件类名(540 条里 533 条是噪声)——
已登记为 ROADMAP `S8-P2`(判据不放宽,让门禁学会跳过数据块)。
- `组件5.txt` 声明的 47 个工具/模板组件里 **46 个仍未实现**(本次只补了 IconPreview)——
已登记为 ROADMAP `S8-P3`,含「走 `frameworks/` 正式组件还是 `site/` 工具页」的决策点。
### Mobile · 顶栏对齐 PC + 平台入口去重(移动端站导航栏改造)
> 起因:用户要求「移动端组件的导航栏参考 PC 端组件导航栏优化(上面跳转 PC 和移动的下拉框也重复了)」。
> 实测确认两条问题,逐项修掉。
- **去重:平台切换从顶栏移除**。移动端站顶栏原有一个 `[PC 端] [移动端]` 分段胶囊,与同一页左栏的
「平台」组指向同一跳转(实测 DOM:`a.mp-item` 与 `a.m-side-link.m-side-platform` 的 href 均为
`../index.html`)—— 同一件事两处入口。PC 站顶栏从来没有这个控件(只有左栏一处),
故删顶栏那份,两端现在都是**左栏唯一入口**。改动同时断言「删的是重复的那个,不是删光」:
`verify-mobile-docs` 的 E7 从「断言顶栏有平台切换」改为「断言左栏有平台入口」。
- **顶栏逐项对齐 PC**(此前只有结构同名,控件实现不同源):
- 高度 56px → **60px**;内容加 **1180px 居中容器**(`.m-top-inner`,与 `.m-body` 同宽,
logo 与左栏左边缘对齐;此前内容铺满视口、与下方内容左边缘错位)。
- 激活项补 **2px 底部品牌指示条**(`.m-nav a.active::after`,PC `.topnav a.active::after` 同款);
此前只有文字变色。
- 版本角标 → **可点版本下拉**:`<span class="m-ver">` 换成 `#m-ver-trigger` + `#m-ver-menu`,
清单从部署根 `versions.json` / `site/versions.json` 两份按序取(不并行 —— 快照页那份必然 404,
并行会把 404 记进控制台,而站点门禁的零控制台错误是硬断言);拿不到清单时
`data-single="1"` 退回不可点角标(与 PC 同一语义)。路径校验只放行 `".."` 与 `x.y.z`,防清单把人带去外站。
- 主题触发器补 **三态图标**(月亮 / 太阳 / 显示器,PC `.theme-icon` 同款):显式选择时按时态给图标,
自动模式恒为显示器图标;此前只有文字标签。
- **零运行时依赖不变**:新增的版本/主题逻辑是构建期内联脚本(`COPY_SCRIPT`),不引包。
- **顺手修掉一处过期硬编码**:移动端站文案里的 PC 组件数写死为「79」,
而 S6-P21 组件族参数化后 PC 索引已是 **103**(实测 `components/index.json`)。改为构建期从 PC 索引实测读取
(模板占位符由 `build-mobile.mjs` 灌入),拿不到索引时退回中性表述而非编造数字。
涉及:总览页、FAQ、平台与端页的正文与覆盖矩阵、以及 `dist/mobile/README.md` 与 manifest 的 `isolatedFrom`。
**验收(原样)**:
```
node tools/verify-mobile-docs.mjs → [OK] 12 条断言 · 53 页(E7 判据已同步去重)
REG_BASE=http://127.0.0.1:3457 node tools/verify-mobile-site.mjs → [OK] 263 条断言 · 50 页 · 控制台错误 0 · 演示帧 281/281
REG_BASE=http://127.0.0.1:3457 node tools/run-mobile-regression.mjs → passRate 100% | pages 47 (all-pass 47) | assertions 804/804 | N/A 10
REG_BASE=http://127.0.0.1:3457 node tools/run-regression.mjs → passRate 100% | pages 103 (all-pass 103) | assertions 1405/1405 | N/A 50
PC 与移动端回归各连跑 8 次,结果逐次一致(40/40 全绿)
```
浏览器实测(Playwright,1440px):顶栏 60px / 内容容器实测 1180px / 激活项 `::after` 实测
`2px rgb(47, 84, 235)` / 顶栏内 `.m-platform` 计数 0 / 版本下拉实测 1 个选项且 `data-single="0"` /
切夜间后图标 `m-icon-sun:block` 且 `html.kole-dark` 生效;320–768px 四档无横向溢出、零控制台错误。
四道移动端门禁全绿:隔离 31 条 / uni-app 9 条 × 50 SFC / 文档 12 条 · 53 页 / 站点 263 条 · 50 页。
### Mobile · 批次 E/F:移动端组件 36 → 47(两批子 agent 并行 + 新增「磁盘↔索引」双向门禁)
> 继续 `/son` 派活推进 TDesign 清单覆盖。批次 E(typography / segmented / sticky / overlay / popover / message)
> 与批次 F(picker / cascader / colorpicker / upload / table)并行产出 11 个组件 × 8 文件 = 88 文件,
> 规格 §37–§47。索引 36 → **47**,实现 216 → **282**,移动端回归 597 → **804 条断言**。
- **给每个组件预分配规格编号**(§37–§47):上一轮两个子 agent 并存时存在编号撞号风险(撞号会被合并脚本按幂等规则整节跳过),
本轮在派单里逐组件指定编号,合并时零冲突、规格 §1–47 连续。
- **新增反向门禁 B1b(两个子 agent 独立报的同一条缺口)**:`verify-mobile-isolation.mjs` 原先只查
「索引里的组件有没有实现文件」,**不查「磁盘上的实现文件有没有进索引」**——于是子 agent 产出的组件在合并前
完全不被任何门禁覆盖(不在索引里 = 不被检查),属静默缺口。现补反向断言:磁盘前缀集合必须等于索引前缀集合,
不一致直接报「未登记:X, Y(跑 node tools/merge-mobile-batch.mjs 合并)」。断言数 30 → **31**。
- **修掉两处探针误判(都是工具侧,不是内容缺陷)**:
- `verify-mobile-site.mjs` 的总览页在 47 个卡片时只渲染 43 个 —— 预览帧 `loading="lazy"`,
**一次 `scrollTo` 到底会跳过中间帧**。改为逐段滚动(每 800px 一次)后再判定,实测 281/281。
- 同一脚本把收集器的 `net::ERR_ABORTED` 记为控制台错误 —— 那是 `_collect.html` 在帧装载完成后
**主动** `removeAttribute('src')` 释放帧造成的在途请求中断,是设计行为。判据改为忽略 `ERR_ABORTED`,
其余(404 / ERR_INVALID_URL 等)仍然计错。
- **子 agent 自修的真实缺陷(都带证据)**:`Sticky` 的 `is-stuck` 判定在默认偏移下滚动 0 就已贴合(改为与容器 padding box + 偏移量比较,含 1px 边框补偿);
`Message.jsx` 把 `onClose` 写进 `useEffect` 依赖 → 宿主传内联箭头函数时计时器永远不到点(回调移到 `useRef`);
`Sticky`/`Message` 演示页缺实际执行的监听器致 `data-behavior` 静默失效;
`Table` 可点行不可聚焦(真实引擎报 `matrix:keyboard-reachable`)→ 补 `tabindex`/`role`/`aria-selected`/Enter 键路径;
`ColorPicker` 的色板 hex 写在行内 style 被 `no-hardcode-hex` 判据扫到 → 改走行内自定义属性(CSS 零 hex,色值只存数据层)。
- **规划偏差(子 agent 主动对齐既有约定)**:`disabled` 从变体维度降为状态类 —— 全仓 47 份契约无一把它当变体,
同意;`picker`/`cascader`/`segmented` 的初判分类经我核对后修正为 input/navigation(PC 同源六分类口径)。
**验收(原样)**:
```
node tools/verify-mobile-isolation.mjs → [OK] 31 条断言(含新增 B1b 反向断言)
node tools/verify-uniapp.mjs → [OK] 9 条断言 · 50 个 SFC
node tools/verify-mobile-docs.mjs → [OK] 12 条断言 · 53 页
REG_BASE=… verify-mobile-site.mjs → [OK] 263 条断言 · 50 页(0 控制台错误、演示帧 281/281)
node tools/run-mobile-regression.mjs → 100% | 804/804 | 47/47 页 | N/A 10 | 0 超时
node tools/run-regression.mjs → 100% | 1405/1405 | 103/103 页(PC 侧未被污染)
node tools/pack-deploy.mjs --tar → OK(282 实现 / 47 文档页 / 47 测试页)
```
**部署**:暂存树哈希与本地一致(`936742e98d061a30`)→ 替换重建 → 五项验收全过、11 个新增组件页全 200、容器 0 error;
公网 `data.mobile.json` 报 **47 个组件**。
**剩余 23 个**(已写进 ROADMAP S7-P23):config-provider / Fab / BackTop / Drawer / Indexes / SideBar / Tabs /
Calendar / DateTimePicker / TreeSelect / CountDown / Empty / Footer / Image / ImageViewer / QRCode / Result /
Skeleton / Swiper / Watermark / DropdownMenu / Guide / PullDownRefresh。
### Docs · 模板页库 1 → 5(S4-P11:把组件组合成可复制的后台页面)
> 起因:用户要求「为组件库设置一个合适的 UI」。定位到 ROADMAP 中唯一「🟢 可开工」的任务 —— S4-P11 模板页库。
> 其依据写得很直白:「模板页是『能否直接用』的关键 —— 用户要的不是组件,是页面」。此前只有 1 个场景页。
- **新增 4 个模板页**(`site/scenario/`,纯 HTML + 原生 JS、零依赖):
- `login.html` — 输入类组件 + 表单校验。字段级错误提示(指出字段、说明原因、给修正方向)、
密码显隐切换、提交加载态 → 成功态、账号锁定等业务错误分支。
- `dashboard.html` — 侧边导航 + 4 张指标卡 + SVG 趋势图 + 渠道占比进度条 + 待办表格 + 分页。
时间范围切换会重算指标与图表;含折叠面板。
- `order-list.html` — 筛选表单 + 表格 + 批量操作条 + 分页 + 详情抽屉。
37 条示范订单;筛选/全选/批量审核/抽屉(Esc 关闭、焦点归还)全部可用。
- `settings.html` — 侧边分区 + 表单 + 开关组 + 单选/复选组 + 危险操作二次确认。
- **接入首页**:`site/app.js` 首页新增「模板页库」区块(5 张卡片,新窗口打开);`site/style.css` 补 `.home-note`。
- **sitemap**:`build-site.ps1` 把 5 个模板页写入 sitemap(521 URL)。新增行全为 ASCII,守住 PS5.1 的 ASCII-only 约束。
- **i18n**:`site/i18n.js` 补 13 条英文(模板页库标题与 5 张卡片),中文源串作键的既有约定不变;`verify:i18n` 316 键全覆盖。
- **新增门禁 `npm run verify:templates`**(`tools/verify-templates.mjs`,54 条浏览器断言,已接入 CI):
令牌生效 / 非白屏 / 零控制台错误 / 零 4xx / 逐页交互 / 375–1024px 无横向溢出。
- **判据修正(`tools/verify-site-routing.mjs`)**:sitemap URL 总数原为硬编码 `1 + 组件数×5`,
现改为「**按 `site/scenario/` 磁盘上实际存在的 .html 逐个核对 + 计入总数**」。
理由:硬编码在新增模板页时只会静默漏检(sitemap 少了页面而断言仍绿);
改后「新增/删除模板页却忘记同步 `build-site.ps1`」会直接红。**这是加强而非放宽** —— 新增了 5 条逐文件断言。
**模板页实测(原样)**:
```
node tools/verify-templates.mjs → [templates] OK — 54 checks passed
node tools/verify-site-routing.mjs → [routing] OK: 103 components / 412 platform shells / 521 sitemap URLs
node tools/verify-i18n.mjs → [i18n] OK — 16 checks passed(316 keys covered)
node tools/run-regression.mjs → 100% | 1405/1405 | 103/103 页 | N/A 50(八次连跑一致)
```
**过程中被测试逮到并修掉的 4 处真实缺陷**(都不是判据问题):
1. **进度条高度渲染为 0** —— `components.css` 只聚合 6 个**核心**组件,`ProgressVariants` / `DashboardCard`
这类扩展组件的样式根本没被加载(类名在、样式缺,页面不报错、只是画不出来)。
修法:4 个模板页按需 `<link>` 各自的 `frameworks/*.css`(这些文件均为纯令牌自包含),
而不是在模板页里重画一遍组件。
2. **指标卡标题与图标未两端对齐**(`display:block` 而非 `flex`)—— 与上条同根因。
3. **设置页脏检查存在永久闩锁** —— 原写法 `if (saved) return` 导致「保存过一次」之后表单再也不会被标记为脏;
且「放弃修改」只硬编码还原 5 个字段,IP 白名单与通知勾选漏在还原之外。
修法:改为**快照式**脏检查(基线 = 上次保存值,逐字段比对),于是「改回原值自动回到干净态」,
日后新增字段也不必再补还原代码。
4. **保存竞态** —— 保存有 700ms 模拟延迟,期间「放弃修改」仍可点,会拿到上一份基线,表现为数据被还原成旧值。
修法:保存进行中两个按钮都禁用。
### Mobile · 批次推进:移动端组件 5 → 36(三批子 agent + 合并脚本 + 修行为断言静默缺口)
> 起因:用户要求「参考 TDesign 移动端组件清单,按批次补全缺的组件」,随后 `/son`(派子 agent)、`/agi`(全自主)。
> 参考清单 67 个组件,本仓库已覆盖 36 个(含此前的 18 个);流程按 `PLATFORMS.md` §四「六步」固化。
- **三批子 agent 产出 18 个组件**(每个 = 6 端实现 + 规格片段 + 契约 = 8 文件,共 144 文件):
- 批次 A:`mobile-icon` / `mobile-layout` / `mobile-link` / `mobile-loading`
- 批次 B:`mobile-avatar` / `mobile-list` / `mobile-collapse` / `mobile-progress`
- 批次 C:`mobile-input` / `mobile-search` / `mobile-switch` / `mobile-stepper` / `mobile-textarea`
- 批次 D:`mobile-form` / `mobile-radio` / `mobile-checkbox` / `mobile-slider` / `mobile-rate`
规格从 §19 连续到 §36(共 36 节);索引 18 → **36 个组件**,实现文件 108 → **216**。
- **新增可复用的合并脚本 `tools/merge-mobile-batch.mjs`**:子 agent 一律不写 `index.json`(避免并发写同一文件),
由主 agent 用该脚本统一合并 —— 它做三件事且幂等:① 按编号把 `spec/parts/*.md` 拼进规格原文(同编号则只归档不拼接);
② 把 `frameworks-mobile/` 里未登记的组件(校验 6 端文件齐全 + 契约存在)写进索引;③ 已拼接的片段归档到 `parts/_merged/`。
- **修掉一个静默缺口(子 agent 复核时发现,价值最高的一条)**:`tests/mobile/_behaviors.js` 的 `PILOTS` 是**硬编码白名单**
(只有最初 5 个组件),于是批次新增组件即使在演示页写了 `data-behavior`,引擎也直接 `return []` ——
**行为断言静默不跑,断言总数悄悄少一截**。修法:作用范围改为「演示页里**真的**写了 `[data-behavior]` 的组件」
∪ 兜底名单,由 `build-mobile.mjs` 在生成测试页时把派生结果注入 `window.__koleBehaviorSlugs`。
实测:行为断言覆盖 5 → **31 个组件**,断言条数 8 → **34 条**(全部 pass)。
- **修掉一处判据过窄(上一轮遗留)**:`pack-deploy.mjs` 的「frameworks-mobile 实现文件数」按索引动态算,
但打包时批次文件已在磁盘而尚未合并进索引 → 断言误报。现在按「索引组件数 × 端数」与磁盘实际双向核对,
不一致时直接指出差额(本次实测:156 vs 186,正是未合并的批次 C)。
- **修掉一处探针误判**:`verify-mobile-site.mjs` 统计总览页演示帧时只看到 20/26 ——
预览帧是 `loading="lazy"`,**视口外的帧不会加载**(浏览器行为,不是缺陷)。判据改为先滚到底再判定,
实测 26/26、现在 36/36 全部正常渲染。
- **子 agent 自修的真实缺陷**(都在报告里给了证据):`<button>` 嵌在 `<label>` 里(非法嵌套);
Stepper 根 `overflow:hidden` 裁掉小尺寸档的热区外扩(命中测试证实);同一状态块挂两条 `data-behavior` 互相污染;
Textarea 计数示例是死数据;Slider 根元素 `cursor:pointer` 让数值文案被判「鼠标专用交互」(真实引擎报的 `matrix:keyboard-reachable`)。
**验收(原样)**:
```
node tools/verify-mobile-isolation.mjs → [OK] 30 条断言
node tools/verify-uniapp.mjs → [OK] 9 条断言 · 39 个 SFC
node tools/verify-mobile-docs.mjs → [OK] 12 条断言 · 42 页(站内链接全可解析)
REG_BASE=… verify-mobile-site.mjs → [OK] 208 条断言 · 39 页(0 控制台错误、演示帧 220/220、320–768px 无溢出)
node tools/run-mobile-regression.mjs → 100% | 597/597 | 36/36 页 | N/A 10 | 0 超时
node tools/run-regression.mjs → 100% | 1405/1405 | 103/103 页(PC 侧未被污染)
node tools/pack-deploy.mjs --tar → OK(2349 文件 / 36 组件 × 6 端 = 216 实现 / 文档页 36 / 测试页 36)
```
**部署**:暂存树哈希与本地一致(`719d66f2fce6875a`)→ 替换重建 → 服务器端五项验收全过、容器 0 error;
公网 `data.mobile.json` 报 **36 个组件**,抽查 8 个新增组件页全 200。
### Tooling · uni-app **真实编译**验证 + 回归可追溯性(S7-P25 / S5-P17 / S6-P30)
> 起因:按批次继续推进,处理三个「一直没做但可立即做」的缺口 —— 前两项都是**只能靠真跑才能回答**的问题,
> 此前一直以「静态门禁通过」代替,属于典型的「绿灯证明不了什么」。
- **S7-P25 · uni-app 真实编译验证(新增 `tools/verify-uniapp-build.mjs`,11 条断言)**:
在隔离目录装 `@dcloudio` 工具链(主仓库 `dependencies` 仍为空),搭最小 uni-app 工程,
把 **21 个 SFC**(移动端 18 + PC×uni-app 3)全部真实挂载并编译到 **H5 与微信小程序**:
- 实测结果:两目标编译通过;产物含 **148 / 211 个唯一 `kole-m-` 类名**;
**18/18 组件的契约声明类名都进了产物**;小程序产物 **0 处 DOM 操作**。
- **为什么必须做**:静态门禁只能证明「源码长得合规」,证明不了「编译器接受它」。
本仓库已有两次「静态全绿却编译不过」的实例(`RangeQuickPicker` 的 const 重赋值、
`CodeInput` 的 emit 遮蔽),都是真编译才暴露的 —— 而 uni-app 端的 18 个 SFC 此前**从未被编译器碰过**。
- **调通过程中抓到的三个真实坑(都写进了脚本注释)**:
① **`package.json` 必须声明 `@dcloudio/*` 进 `dependencies`** —— 只写 `{name, private, version}` 时
编译器照样打印 `DONE Build complete.`、退出码 0,但产物只有一个 **704 字节的 modulepreload polyfill**,
页面与 21 个组件**全部静默丢失**。这是本次最危险的失败模式。
② **移动端 `Button` 与 PC×uni-app `Button` 同名**(两个平台各有一份实现)→ 绑定名冲突互相覆盖。
消歧规则:PC 侧加 `Pc` 前缀,并把消歧结果用于**落盘文件名**与 `import` 绑定名(否则后拷的覆盖先拷的)。
③ 页面模板用 `<C0 />` 这类索引式绑定名会被 uni-app 编译器当未识别元素丢弃 —— 必须用组件自身的 Pascal 名。
- **判据做了两轮变异测试才定稿**(判据修正附理由 + 反例,符合仓库铁律):
初版「从 `frameworks-mobile/<X>.css` 取类名」→ 假通过(uni-app 端样式是**内联**的,查的是另一份文件);
二版「从 uni-app 端源码取类名」→ 假通过(**改名后判据与产物同步变化,两边一致**,实测
`kole-m-grid` 全量改名后仍 PASS);定稿版**拿契约(`variantClasses`)里声明的类名去产物里找** ——
契约与实现是两条独立写入路径,实现悄悄改名/丢组件时契约不会跟着变。
定稿后同一变异立刻抓到:FAIL `mobile-grid(契约类名 kole-m-grid--2 未进产物)`。
- **App 端(app-plus)未覆盖**:需 HBuilderX 云端打包,命令行无法完成(脚本输出里已注明)。
- **S5-P17 · 回归偶发失败的可追溯性**:`tools/run-regression.mjs` 在**非满分时把上一次报告留档为
`tests/report-prev.json`**(`report.json` 仍只有一份,供首页读取),失败现场不再被下次运行覆盖。
实测(真实注入,非模拟):把 `tests/button.html` 移走制造 404 → 输出
`kept: tests/report-prev.json(本次非满分,已保留上一次报告供回放)` 且文件确实生成;还原后满分运行不再留档。
`report-prev.json` 已加进 `.gitignore`(诊断中间产物,不入库)。
- **S6-P30 · 采集偶发 vs 真断言失败**:新增 `isCollectionFailure()`,采集失败(`no-result` / `timeout` / `page-error`)
**单列 `collectionFailures` 且不计入 passRate 分母**;`failedPages` 只留真断言失败
(此前 `button: no-result` 会被读成「button 的断言坏了」,两类事实混在一张表里)。
实测同一注入:`passRate 100% | pages 103 (all-pass 102) | assertions 1378/1378 | N/A 50 | 采集失败 1 (button:no-result)`
—— 分母正确扣掉该页(1378 而非 1405),且 **`EXIT=1`**(不静默吞掉)。
**判据反例**:5 条用例逐条验证 —— 采集三类为真 / 真断言失败(`matrix:contrast>=4.5`)为假 / 满分页为假。
- **顺带修掉**:`AGENTS.md` 门禁清单里三处陈旧断言数(隔离 29→30 / 文档 8→12 / 站点 53→118,均为实测值)。
**验收(原样)**:
```
node tools/verify-uniapp-build.mjs
→ [OK] uni-app 真实编译验证通过(11 条断言 · 21 个 SFC · h5 + mp-weixin)
PASS B0 待编译 SFC 全部存在于源目录 — 21 个(无缺失)
PASS B-h5 编译通过 — 9488 ms PASS B-mp-weixin 编译通过 — 7747 ms
PASS B-h5 产物含 kole-m- 类名 — 148 个唯一类名
PASS B-mp-weixin 产物含 kole-m- 类名 — 211 个唯一类名
PASS B-h5 每个移动端组件(按契约声明的类名)都进了产物 — 18/18
PASS B-mp-weixin 每个移动端组件(按契约声明的类名)都进了产物 — 18/18
PASS B-mp-weixin 产物无 DOM 操作 — 0
node tools/verify-mobile-isolation.mjs → [OK] 隔离门禁全部通过(30 条断言)
node tools/verify-uniapp.mjs → [OK] uni-app 门禁全部通过(9 条断言 · 21 个 SFC)
node tools/verify-mobile-docs.mjs → [OK] 文档完整性门禁全部通过(12 条断言 · 24 页)
node tools/verify-i18n.mjs → [OK] — 16 checks passed
REG_BASE=… node tools/run-regression.mjs → passRate 100% | pages 103 (all-pass 103) | assertions 1405/1405 | N/A 50
REG_BASE=… node tools/run-mobile-regression.mjs → passRate 100% | pages 18 (all-pass 18) | assertions 270/270 | N/A 6
# 判据反例(变异测试,各自当场还原)
改掉 Grid 的契约声明类名 → FAIL mobile-grid(契约类名 kole-m-grid--2 未进产物)
注入 404 制造采集失败 → report-prev.json 生成 + EXIT=1 + 分母扣掉该页(1378/1378)
isCollectionFailure 5 条用例 → 采集三类为真 / 真断言失败为假 / 满分页为假(全 PASS)
```
**未执行**:App 端(app-plus)真实编译 —— 需 HBuilderX 云端打包,命令行无法完成。
**依赖说明**:`@dcloudio` 工具链装在 `.tmp/uniapp-build`(已 gitignore),主仓库 `dependencies` **仍为空**(零运行时依赖不变);
CI 里该步骤在工具链未缓存时**跳过并告警**(环境问题非代码问题,脚本自身退出码 2)。
### Fix · 演示帧内「覆盖型浮层」只在演示框里全屏(点击图片 / 打开弹窗不满屏)
- **缺陷**:文档站把演示页嵌在按内容高度撑开的 iframe 里,而组件里 `position:fixed; inset:0`
的覆盖层以**帧视口**为包含块 —— 于是「点击图片全屏」只在演示框内铺开。实测
`imagepreview` 的遮罩 724×134,宿主视口 1241×1233。命中的是全部 8 个「覆盖视口」语义的
组件:imagepreview / modal / alertmodal / confirmmodal / formmodal / fullscreenmodal /
bottomsheet / loadingoverlay。这是**承载容器的形态问题**,不是组件缺陷 —— 这些演示页
单独打开(或点「新窗口打开」)时覆盖层本来就铺满视口,故组件源码与令牌一律不动。
- **修法**(站点侧):`site/app.js` 新增演示帧提升桥 —— 帧内出现覆盖型浮层时给演示舞台
(`.demo-stage` / `.ex-stage`)加 `.is-lifted`(`position:fixed; inset:0`,见 `site/style.css`),
舞台成为全视口浮层的包含块后帧视口随之变成宿主视口,覆盖层自然铺满;同时锁宿主滚动
(`html.kole-stage-locked`)。帧内显隐由演示脚本改 style/class 驱动,宿主无法感知,
故在帧 document 上挂 MutationObserver 跟随。
- **判据只认「覆盖视口 + 真的渲染出来了」**:贴边的 fixed(backtotop 右下角 / anchornav 右侧 /
messagepro 顶部 / tabbar 底部)不提升,它们的语义本来就是贴着视口某一边;
`position:absolute` 的局部块(`loadingoverlay.is-inline`、popconfirm 弹泡)不提升;
水印 `.is-fullscreen` 是装饰层不是浮层,也不提升。
- **实现期实测踩到并修掉的三个真缺陷**(都由新增门禁的反向/边界用例逼出):
① **淡入过渡漏判** —— BottomSheet 的遮罩 opacity 0→1 走 `.25s`,突变那一刻量到 opacity 仍是 0
被判成「未显示」,而过渡本身不再产生新突变 → 没有补测就永远不提升。修法:突变后隔 450ms 再判一次;
② **不可见遮罩被误提升** —— 祖先 `display:none` 时自身 computed display 仍是 flex,
只看自身样式会把「已挂载但看不见」的遮罩当浮层,舞台升到全屏却没有任何内容(整屏空白)。
修法:加矩形非零判据(文档站逐示例预览会隐藏场景外节点,modal 的 `#mount` 实测命中);
③ **撤销时帧高不回收** —— 提升期间 `fitFrame()` 被早退跳过,而遮罩打开期间帧内容不再变化、
ResizeObserver 不回调,帧停在全屏高度。修法:撤销时显式补量一次。
- **新增门禁** `npm run verify:stage-overlay`(`tools/verify-stage-overlay.mjs`,77 条断言):
A 组 8 个组件逐一点开 → 浮层尺寸必须≈宿主视口(容差 2px)+ 舞台已提升 + 滚动已锁,
关闭 → 提升撤销 + 滚动锁解除 + 帧回到内容高度;B 组 6 条反向用例(水印 / backtotop /
anchornav / button / messagepro 不得提升,移动端静态站不得泄漏 PC 提升逻辑)。
已接入 `regression.yml`。
### Mobile · 移动端组件第二批收尾(弹出层系 + 展示系 + 输入系):Toast / Dialog / Grid / Steps / NoticeBar / NumberKeyboard / DatePicker(索引 11 → 18)
> 起因:按批次继续开工,把 ROADMAP S7-P23 的剩余 7 个组件做完 —— 规格 §12~§18 本轮新写
> (契约的唯一授权来源必须先于实现),再按 `PLATFORMS.md` §四 六步流程落地为 6 端实现。
> 索引 11 → **18**,`frameworks-mobile/` 66 → **108** 个文件(18 × 6 端)。
- **七个新组件**(规格小节 → slug / 前缀):
- **轻提示 Toast**(§12 → `mobile-toast`):`tone` 五语气 + `position` 三位置 + `mask`;
容器 `role="status"` + `aria-live="polite"`(结果朗读一次不反复打断);`tone=loading` 时图标旋转并标 `aria-busy`。
- **对话框 Dialog**(§13 → `mobile-dialog`):`variant` = confirm(两按钮等宽)/ alert(单按钮铺满)+ `tone` = danger + `round`;
`closeOnMask` 可关;`role="dialog"` + `aria-modal` + `aria-labelledby`;按钮热区 ≥44px。
- **宫格 Grid**(§14 → `mobile-grid`):`columns` 2/3/4 + `border` + `square`;可点格子是**原生 button**(整块热区 + 键盘可达),
`static` 格子是 div 且不绑点击 —— 直接规避「鼠标专用交互」这类无障碍缺口;按下反馈一律 `:active`(触屏没有悬停)。
- **步骤条 Steps**(§15 → `mobile-steps`):`direction` 横/纵 + `status` 四态;`role="list"` / `role="listitem"` + `aria-current="step"`;
**状态不只靠颜色**(进行中加粗、已完成用勾选字符、失败用感叹号)。
- **通知栏 NoticeBar**(§16 → `mobile-noticebar`):四种语气 + `scrollable` 跑马灯 + `closable`;
**无障碍硬要求落地**:`prefers-reduced-motion: reduce` 下停止滚动并改为换行显示;滚动内容**不用** `aria-live`(反复朗读会干扰)。
- **数字键盘 NumberKeyboard**(§17 → `mobile-numberkeyboard`):`type` = number/digit + `showDelete` + `showConfirm` + `confirmDisabled`;
键盘**不持有输入值** —— 只 emit 按键事件(数字键回传该字符 / 删除键回传 `'delete'` / 确认键回传 `'confirm'`),写入哪个输入框由宿主决定。
- **日期选择器 DatePicker**(§18 → `mobile-datepicker`):`mode` = date/month + `round` + `closeOnMask`;三列 `role="listbox"` / `role="option"` + `aria-selected`;
年份范围由宿主传入(`years` / `months` / `days`),**不发明**可用年份区间(契约 unknowns 已登记)。
- **配色零发明**(全部既有令牌,脚本实测亮/暗两态):Toast 深底 + 反色文字 15.78:1 / 15.00:1;
Dialog 卡片底 + 正文色 15.13:1 / 10.34:1;Grid / Steps / NoticeBar 沿用既有语义对(浅底 + 同族深字,均 ≥4.5:1)。
- **判据有效性用变异探针验证(5/5 当场捕获,不是「跑了绿灯」就算数)**:
① 向 CSS 注入 `#FF0000` → B3 报出文件与色值;② 契约加一个源码没有的 prop → E4 逐端报「源码里没有」;
③ 把有默认值的 prop 标成必传 → E4b 逐端报「应相反」;④ uni-app 端注入 `document.querySelectorAll` → U5 报出两条规则;
⑤ uni-app 端去掉全部 `rpx` → U9 报「未使用 rpx」。每个探针都当场还原并复跑确认绿灯。
- **过程中发现并修掉三处真问题**:
① 契约 `related` 里写了裸名(`toast` / `noticebar`)而非真实 slug → E6c 逐条报「不存在」,已补 `mobile-` 前缀(5 处);
② `tools/pack-deploy.mjs` 输出文案与 `site/m/_design_template.html` 里**写死了组件数**(前一批 8 个组件落地时漏改)
→ 分别改为读索引与构建期占位符注入;
③ `Dialog` 初版的圆角写在基础类上(`round` 变体于是无意义)→ 改为由 `--round` 类控制,契约 `variantClasses` 与 CSS 双向对上。
**验收(原样)**:
```
node tools/build-mobile.mjs → 18 组件 × 6 端(data.mobile.json 412 KB)· 18 测试页 · 18 文档页 · dist/mobile 全端
node tools/verify-mobile-isolation.mjs → [OK] 隔离门禁全部通过(30 条断言)— B1 18 × 6 端 108 文件 / B8 报告 18 = 索引 18
node tools/verify-uniapp.mjs → [OK] uni-app 门禁全部通过(9 条断言 · 21 个 SFC)
node tools/verify-mobile-docs.mjs → [OK] 文档完整性门禁全部通过(12 条断言 · 24 页)
REG_BASE=… verify-mobile-site.mjs → [OK] 移动端站点全部通过(118 条断言 · 21 页;演示帧 101/101 正常,0 控制台错误)
REG_BASE=… node tools/run-mobile-regression.mjs → passRate 100% | pages 18 (all-pass 18) | assertions 270/270 | N/A 6
node tools/run-regression.mjs → passRate 100% | pages 103 (all-pass 103) | assertions 1405/1405 | N/A 50(PC 零污染)
node tools/pack-deploy.mjs → OK — mobile 108 个实现文件(18 组件 × 6 端)/ 文档页 18 / 测试页 18
```
**未执行**:部署(只改工作区,未走 AGENTS §九 发布流程);uni-app 真实编译(既有状况 S7-P25,需 `@dcloudio` 依赖)。
### Mobile · 移动端组件第二批(弹出层基座 + 展示系):Badge / Tag / Popup(索引 8 → 11)
> 起因:批次推进。移动端规格已写到 §9 / §10 / §11(上一轮写好但未实现),本轮按 `PLATFORMS.md` §四
> 的六步流程把三节规格落地为 6 端实现 —— 组件数 8 → 11,`frameworks-mobile/` 48 → 66 个文件。
- **三个新组件**(规格小节 → slug / 前缀):
- **徽标 Badge**(§9 → `mobile-badge`):`shape` = `dot` / `number` / `text` + `standalone`;
状态 `overflow`(超过 `max` 显示 `99+`)与 `hidden`(值为 0 且未开 `showZero` 时不渲染)。
角标绝对定位、不改变被包裹元素的布局尺寸;红点 `aria-hidden`,数字角标带 `aria-label`。
- **标签 Tag**(§10 → `mobile-tag`):`tone` 五语气(default / primary / success / warning / danger)
+ `size`(default / small)+ `closable`。关闭按钮是**原生 button**、24×24 热区(小尺寸标签内用负外边距扩展),
`aria-label` 带上标签文字(否则一串「✕」读屏无法区分);`disabled` 时置灰且不响应。
- **弹出层 Popup**(§11 → `mobile-popup`):`placement` 五方向(center / bottom / top / left / right)+ `round`
(贴边方向在靠内容一侧切圆角,用逻辑属性,RTL 安全);`closeOnMask` 可关;内容超长时 **body 内部滚动**、
遮罩不滚;滑入 240ms `cubic-bezier(.32,.72,0,1)`;浮层 `role="dialog"` + `aria-modal`,遮罩 `aria-hidden`。
- **配色零发明(全部既有令牌,脚本实测两态)**:实心底 + `--kole-color-text-inverse`
亮色 5.57–5.87:1 / 暗色 5.64–8.24:1;Tag 的浅底 + 同族字色亮色 4.68–5.38:1 / 暗色 4.92–6.91:1。
**未新增任何色值**,因此不需要碰 PC 令牌文件。
- **过程发现并修掉两处陈旧计数**(都是前一批 8 个组件落地时漏改的):
① `tools/pack-deploy.mjs` 的输出文案写死「5 组件 × 6 端」(数字本身是算出来的,只有说明文字陈旧)→ 改为读索引;
② `site/m/_design_template.html` 的「移动端 5 个组件」写死在 HTML 里 → 改为构建期占位符(由 `build-mobile.mjs` 注入实际组件数)。
> 注:本条初版把占位符原文写进了日志正文,而 `changelog.html` 是**从本文件正文提取**渲染的,
> 于是页面里出现了未替换的占位符 —— 被 `verify-mobile-docs.mjs` 的 E7(无残留占位符)当场拦下。
> 同一条纪律:日志正文里不要出现占位符形状的字面量。
- **修掉一处误报 skip**:Badge 的「数值为 0(hidden)」演示块把 `data-assert` 放在**被隐藏的角标**上,
断言引擎按「默认隐藏(交互后可见)」记为 skip —— 但该块本身就是「隐藏态+可见对照」的设计。
已把断言移到可见的容器上,skip 3 → 且该页断言 16 条全 pass(skip 只剩两条静态组件固有的 N/A)。
- **顺带修正 ROADMAP S7-P23 的失败判据**:其中写的 PC 断言总数是 1017,实际当前为 **1405**(103 组件 × 5 端后未更新)。
**验收(原样)**:
```
node tools/build-mobile.mjs → 11 组件 × 6 端(data.mobile.json 238 KB)· 11 测试页 · 11 文档页 · dist/mobile 全端
node tools/verify-mobile-isolation.mjs → [OK] 隔离门禁全部通过(30 条断言)— B1 11 × 6 端 66 文件 / B8 报告 11 = 索引 11
node tools/verify-uniapp.mjs → [OK] uni-app 门禁全部通过(9 条断言 · 14 个 SFC)
node tools/verify-mobile-docs.mjs → [OK] 文档完整性门禁全部通过(12 条断言 · 17 页;E8 站内链接 657 条)
REG_BASE=… verify-mobile-site.mjs → [OK] 移动端站点全部通过(83 条断言 · 14 页;演示帧 64/64 正常,0 控制台错误)
REG_BASE=… node tools/run-mobile-regression.mjs → passRate 100% | pages 11 (all-pass 11) | assertions 172/172 | N/A 4
node tools/run-regression.mjs → passRate 100% | pages 103 (all-pass 103) | assertions 1405/1405 | N/A 50(PC 零污染)
node tools/pack-deploy.mjs → OK — mobile 66 个实现文件(11 组件 × 6 端)/ 文档页 11 / 测试页 11
浏览器实测(1280×1000) → 三页各 14 个小节 / 演示块 6·5·6 / 预览帧全部有内容(0 空帧)/ 0 控制台错误
几何:Badge 角标 absolute + translate(50%,-50%)(不占位);Tag 24px 高;Popup fixed 被展示框 transform 包含块约束(关闭态位移 143.6px)
```
### PC 常用组件补齐:103 个组件 / 组件11批次
- 对照 Element UI 2.15.14 左侧组件目录,新增 24 个 PC 常用组件:Layout、Container、Typography、Icon、Link、Radio、Checkbox、InputNumber、Switch、TimePicker、DatePicker、DateTimePicker、Form、Pagination、Badge、Avatar、Descriptions、Alert、PageHeader、Tooltip、Popover、Divider、InfiniteScroll、Drawer。
- 每个新增组件均登记设计契约、批次 11 规格、H5/CSS/React/Vue 2/Vue 3 五端源码、站点详情和测试页;PC 清单由 79 扩展为 103,frameworks 源文件由 395 扩展为 515。
- 构建、预计算、分发、族层、跨端、API 文档和路由门禁改为从 PC 索引读取组件数;不改变移动端 5 组件与 PC × uni-app 3 个试点边界。
- 新增组件使用场景覆盖后台布局、表单输入、列表分页、状态提示、详情描述、浮层和抽屉;日期/时间组件明确采用零运行时依赖的原生控件边界。
### PC 组件文档改为「逐示例用法代码」——一个使用场景 = 一块预览 + 一块调用代码
> 起因:用户对照 Element 的 *Radio 单选框* 文档页指出 —— 组件文档应当直接给出
> `<el-radio disabled v-model="radio" label="选中且禁用">备选项</el-radio>` 这样的**调用写法**,
> 而不是把整份实现文件摊给使用者;并且「单个使用场景对应单个代码块」。
> 原形态是「一个整页 iframe + 展开看整份实现源码(Button.html 117 行 / Button.jsx 66 行)」,
> 使用者要自己从实现里反推调用方式。
- **每个使用场景一块卡片**(`site/app.js` 的 `buildExampleCard`):标题(演示页的 `h2` /
> `.hint` / 契约代表变体名)→ 预览 → **只属于这个场景的代码块**(H5 HTML / React JSX /
> Vue 2 / Vue 3 四端可切,默认跟顶栏「框架」选择器一致,可复制)。示例数据不可用时退回
> 「整页演示 + 整份源码」,功能不消失。
- **预览不重排**:iframe 仍加载**原始演示页**,只把其它场景 `display:none`(隐藏计划在构建期
按 body 的元素下标算好)。不切 HTML 是因为演示脚本对节点有索引依赖 —— `resultvariants` 的
`DATA[i]`、`modal` 的 `#mount` 一旦拆开就会渲染出错的内容。
- **片段出处三分**(`site/examples/<slug>.json` 的 `source` 字段):
`demo-mapped`(演示页逐字映射:`btn-primary` → `type="primary"`)、
`demo-data`(演示数据 + API 表:挂载点型组件的 `options` / `v-model`)、
`api-derived`(演示页无调用标记,依据 API 表推导最小调用)。卡片代码栏右侧标注出处。
- **不发明**:片段里的 prop 名必须能指到 API 表 / 源码参数表,class 必须在该组件 CSS 里真实存在
(`npm run verify:examples` 的 9 条断言逐条复核;预览隐藏路径也必须能解析回演示页 DOM)。
- **数据分层**:`site/examples/<slug>.json` 按组件懒加载,`data.js` 只留
`{ id, title, source }` 目录 + `examplesRef`;`data.json` 保持**完整示例**(对外承诺不变)。
- **顺带修掉**:①代码区常显后暴露的暗色对比度问题 —— 代码块借 `--kole-color-tooltip-bg`
而该令牌暗色下是浅色,补 `html.kole-dark .ex-code` 深底并把语法高亮调色板按深底实测重选
(全部 ≥4.5:1,原先 `hl-tag` 只有 2.70:1);②演示脚本钩子 `id`(`#demo-toggle`)与
`data-behavior`/`data-assert` 不再混进用法代码;③`<input>` 等 void 元素不再序列化成 `</input>`。
### Mobile · 组件批次推进:18 个组件 × 6 端(108 实现文件)+ 文档站 24 页,全部门禁与回归通过
> 起因:用户要求「参考 TDesign 移动端的组件清单,按批次补全当前组件库缺的组件」。
> 参考页给出 72 个组件的清单,本仓库按「规格 → 契约 → 6 端实现 → 文档页 → 门禁」的既有流水线分批推进。
- **组件从 5 扩到 18**(`frameworks-mobile/` 108 个文件 = 18 × 6 端),规格 19 节、契约 18 份:
按 PC 同源分类:导航 4 · 反馈 7 · 通用 2 · 数据展示 3 · 数据录入 2。完整清单见 `site/m/index.html` 的覆盖矩阵与侧栏。
- **本会话直接产出的一批(A)**:`mobile-button`(三档高度 44/36/28、loading 阻止重复触发、`aria-busy`)、
`cell`(整行热区 ≥56px、按行分隔线、可点用原生 button / 纯展示用 div)、`mobile-divider`(水平/垂直/虚线/
三种文字对齐,纯装饰 `role=separator + aria-hidden`)。三者的 6 端实现、契约(含演示分组、相似组件、
必传列、CSS 变量)与规格 §6–§8 均由本会话写入;其余批次与并发会话合并完成。
- **合并时修掉两处判据问题(都是「判据太窄」而不是放宽)**:
- **slug 允许与 PC 同名**:`build-mobile.mjs` 与 `verify-mobile-isolation.mjs` 原先要求「移动端 slug 不得与
PC 撞名」,实际两个平台各自有「按钮」「分割线」是正常的 —— 真正要证明的是「同名也各自独立」。
改为:报告同名清单,并断言同名组件的实现文件**不在** `frameworks/` 下。
- **`dist/mobile/manifest.json` 的判据**从「无 PC slug」改为「slug 集合与索引完全一致」——
后者能同时抓到漏项与串入,比原来更严。
- **移动端令牌层补一条全局焦点环**:`:focus-visible` 落在移动端令牌层(不能只依赖 PC 令牌文件里那条 ——
断言引擎读的是**直接 link 的样式表**的 `cssRules`,`@import` 进来的规则不在其中)。
这既是让静态组件过 `focus-visible-defined` 断言,也是真实的 a11y 改进。
- **门禁全绿**:隔离 30 条 · uni-app 9 条(21 个 SFC)· 文档完整性 12 条(24 页,站内链接 1121 条全可解析)·
移动端站点 **118 条**(21 页浏览器实测:0 控制台错误、演示帧 101/101 正常渲染、320–768px 无溢出)。
- **回归**:移动端 **100%(270 通过 / N/A 6 / 18 页)** · PC **100%(1405 通过 / N/A 50 / 103 页)**。
**部署(AGENTS §九)**:`pack-deploy --tar`(2187 文件 / 1.65 MB gzip)→ 暂存树哈希与本地一致
(`a962d7c4caa853b2`)→ 替换重建 → 服务器端 specs 404 / 根 302 / sitemap 200 / healthz / SPA 深链全过、
容器 0 error;公网抽查 5 个页面全 200,`site/m/index.html`、`site/m/component/mobile-button.html`、
`site/m/data.mobile.json` 逐字节与本地一致。
### Mobile docs · 顶栏与左栏对齐 PC(平台 / 开发指南 / 组件 N + 主题三态跨站生效)+ 补常见问题与更新日志页
> 起因:用户指出「导航栏需要和 PC 一致,左侧栏也是」。移动端文档站此前是自成一体的简版顶栏
> (品牌 + 平台分段 + 3 个链接)与自创分组的左栏(开发指南 5 项自定名 + 手势/反馈分类),
> 与 PC 文档站的信息架构不一致。
- **顶栏与 PC 同结构**:`logo(标记块 + Kole UI + 副标题)` + `版本角标 v1.0.0`(读 `data.mobile.json` 的
`meta.version`)+ `平台切换` + `主导航 5 项`(**组件总览 / 快速开始 / 设计规范 / 常见问题 / 更新日志**,
与 PC 同名同序,组件页高亮「组件总览」= PC 在组件页高亮「组件」的同一口径)+ `主题模式三态`。
PC 特有的三件**没有搬**,原因写明在 `site/m/style.css` 注释里:搜索框(PC 79 个组件才需要)、
技术栈选择器(PC 5 端各自成页;移动端 6 端同页展示)、语言选择器(移动端无 i18n 字典)。
- **主题三态与 PC 完全同一约定**:`localStorage['kole-mode'] ∈ light|dark|auto`、`auto` 跟随系统、
反色靠 `html.kole-dark`(令牌两端共用)。实测**跨站生效**:在移动端站选夜间 → FAQ 页仍是夜间 →
PC 站同一键也是夜间;`<head>` 里有同步 bootstrap,避免首帧先白后黑(PC 的 S2-P5 同款处理)。
- **左侧栏三段式与 PC 侧栏一致**:「**平台**」组(PC 端组件 / 移动端组件【当前】,各带副标题)→
「**开发指南**」组(与 PC 同名同序的 5 项;移动端特有的「平台与端 / 测试与回归」置后并标「移动端」)→
「**组件 N**」按 **PC 的六分类**分组与计数(通用 / 导航 / 数据录入 / 数据展示 / 反馈 / 工具与系统)。
组件重新归类:navbar、tabbar → 导航;actionsheet、pullrefresh、swipecell → 反馈;
分类口径写进移动端索引的 `categories` 块(真源),构建脚本与文档站都读它。
- **补两页**:
- `faq.html` 常见问题:6 组 20 条(接入 / 令牌 / 触控 / uni-app / 排障 / 口径),每条对应仓库里可复现的
实测或门禁断言;含「uni-app 端真的编译过吗 → 没有」这类如实回答。
- `changelog.html` 更新日志:**构建时从仓库根 `CHANGELOG.md` 提取**标题含「Mobile / 移动端」的段落
(自带一个极小的 Markdown 渲染:标题 / 列表 / 引用 / 代码块 / 行内 code 与 strong),不手抄 ——
站点与仓库两处说法不一致是这类页面的典型失败模式。
- **改名对齐**:`设计令牌` → `设计规范`(`tokens.html` → `design.html`),与 PC 的「设计规范」同名。
- **门禁 11 → 12 条**:E3 从「侧栏列全组件」扩为「**顶栏与左栏与 PC 同结构**」(平台组与「当前」角标、
开发指南 5 项同名同序、顶栏 7 个必需元素、主导航 5 项);新增 **E8 站内文件链接全部可解析**(332 条)。
- **E8 当场抓到两个真问题(已修)**:① 改名后 `_index_template.html` 与 `_guide_template.html` 还指向
`tokens.html`(快速开始卡片与页脚);② `changelog.html` 的测试链接少退一级(`../tests/…` 应为 `../../tests/…`)。
E8 已双向验证(临时改坏一处链接 → FAIL)。
- **修**:更新日志页在窄屏横向溢出(375px 溢出 88px、414px 溢出 49px)—— 提取的正文含 64 位哈希、
长路径这类不可断词的串;给正文/列表/行内 code 补 `overflow-wrap: anywhere`(表格此前已有同类规则)。
修后 7 页 × 4 宽度(320/375/414/768)**0 溢出**。
**验收(原样)**:
```
node tools/verify-mobile-docs.mjs → [OK] 12 条断言 · 11 页
node tools/verify-mobile-isolation.mjs → [OK] 29 条断言 node tools/verify-uniapp.mjs → [OK] 9 条(8 SFC)
REG_BASE=… verify-mobile-site.mjs → [OK] 53 条断言(8 页 + 窄屏) npm run verify:i18n → OK(16 checks)
浏览器实测:顶栏 = Kole UI 移动端 / v1.0.0 / [PC 端][移动端] / 组件总览·快速开始·设计规范·常见问题·更新日志 / 自动模式
左栏 = 平台(PC 端组件、移动端组件【当前】)/ 开发指南 5 项 + 2 项移动端特有 / 组件 5(导航 2 · 反馈 3)
主题:选夜间 → html.kole-dark + localStorage[kole-mode]=dark;跳 FAQ 仍夜间;PC 站同键亦夜间;0 控制台错误
node tools/run-mobile-regression.mjs → 100% | 77/77 | 5/5 页
node tools/run-regression.mjs → 100% | 1026/1026 | N/A 34 | 79/79 页
node tools/pack-deploy.mjs --tar → OK(39 条必需项,含 6 张移动端根页;组件数改为从索引读取)
```
### Deploy · 移动端组件页(TDesign 范式)已上线
按 AGENTS §九 发布:`pack-deploy --tar` → 上传 → 暂存**归一化树哈希与本地一致**(`f00d3c4ac2e50861`,1531 文件)→ 替换 → `docker compose build && up -d`。
验收:specs 404 / 根 302→`/site/` / sitemap 200 / healthz 有 content-type / SPA 深链 200 / 缺快照 404 / 容器日志 0 error;
公网 `https://kole-ui.mymoyu.top` 抽查 6 个文件(`site/app.js`、组件薄壳、`site/data.json`、移动端文档页与演示页)**逐字节与本地一致**。
> 并发提示:本轮期间另一会话把 `package.json` 版本重置为 **1.0.0** 并重建站点(`data.json` 的 `generated=2026-09-20 07:36`、
> `versions.json` 单版本)。线上现已与该状态一致 —— 版本号本身不是本次改动;如需回到 2.0.0,请改 `package.json`
> 后重跑 `npm run build:site` 并再发布一次。
> 另:本轮第一次发布(07:26 打包)后实测到线上落后工作区 906 个文件(并发会话 07:36 重建了薄壳与 `app.js`),
> 已按 §九 的约定「等稳定后重发」补发一次,现线上与工作区逐字节一致。
### Mobile docs · 组件页对齐 TDesign 范式(演示分组 / 独立预览 + 原文代码 / 必传 / CSS 变量 / 相似组件)
> 起因:用户给了参考页 <https://tdesign.tencent.com/mobile-vue/components/link>。用浏览器渲染后提取其结构:
> 演示按 `01 组件类型` / `02 组件状态` 分组;每个演示独立成块(标题 + 预览 + 代码);
> API 分 Props(**名称 / 类型 / 默认值 / 描述 / 必传**)、Events、**CSS Variables**(名称 / 默认值 / 描述);
> 另有「何时使用 / 组件搭配使用 / 推荐慎用示例 / 相似组件」。按这套范式重排了移动端组件页。
- **演示改为「分组 + 独立块」**:契约新增 `demos`(id / group / title / desc / variant),5 个组件共 **20 个演示块**
(navbar 3、tabbar 4、actionsheet 4、pullrefresh 4、swipecell 4),按 `01 组件类型` / `02 组件状态` 分组。
每块含:标题 + 变体标记 + 说明 + **375 宽单演示预览帧** + 「查看代码」(演示页**原文**,行数标注,可复制)。
- **演示页支持 `?demo=<id>` 单块模式**:5 个演示页的每个演示包成
`<section class="demo-block" data-demo="…">`,并加过滤脚本(只显示该块、隐藏帧内标题行、取消 `min-height:100vh`)。
**无参数路径完全不变** —— 测试页与回归走无参数,实测移动端回归仍 100%(77/77)。
- **预览帧高度自适应**:按 `body.scrollHeight` 收紧到 140–360px。
> 踩坑:先用 `max(body.scrollHeight, documentElement.scrollHeight)` 量,结果**四个演示都锁在 308px** ——
> `documentElement.scrollHeight` 等于**帧视口高度**(html 撑满视口),于是「帧高 → 视口高 → 量到的高度」
> 形成反馈环,永远收敛在初始值。只量 body 后:swipecell 140 / navbar 140–166 / tabbar 222 / pullrefresh 242 /
> actionsheet 262,**0 个演示被裁**。
- **API 补两列/一表**:Props 增「**必传**」列(严格定义:实现里**没有默认值**时才为 Y,门禁逐条核对 4 个框架端);
新增「**CSS 变量**」表(从组件样式表扫描组件级变量:`--kole-m-swipecell-offset` /
`--kole-m-swipecell-action-width` / `--kole-m-pullrefresh-threshold` / `--kole-m-pullrefresh-offset`)。
- **新增「相似组件」表**(契约 `related`:组件 + 「何时用它而不是本组件」的区分说明,共 10 条),
并把「用法要点」按参考页改名为「**何时使用**」。
- **组件页新顺序**:演示 → API(Props / 事件 / 插槽 / CSS 变量)→ 何时使用 → 交互与触控 → 无障碍 →
相似组件 → 规格未定 → 结构 → 变体类名映射 → 代表变体 → 令牌 → 6 端源码 → 测试与回归 → 契约。
另在 hero 下加「引入」代码块(令牌 + 本组件样式)。
- **门禁从 8 条扩到 11 条**:新增 E4b(必传 ⇔ 实现默认值)、E6b(CSS 变量表 ↔ 组件 CSS 双向)、
E6c(相似组件 slug 真实存在 + 说明非空);E5 扩为「每个演示块 ↔ 演示页 `data-demo` 双向一致 +
预览帧带 `?demo=` + 代码区含**转义后**的演示页原文 + 每个演示代码块非空」。
**验收(原样)**:
```
node tools/verify-mobile-docs.mjs → [OK] 11 条断言 · 9 页
node tools/verify-mobile-isolation.mjs → [OK] 29 条断言 node tools/verify-uniapp.mjs → [OK] 9 条(8 SFC)
REG_BASE=… verify-mobile-site.mjs → [OK] 53 条断言(8 页 + 窄屏)
npm run verify:i18n → OK(16 checks)
浏览器实测(1280×1000):分组 01/02 ✓ · 20 个演示块 ✓ · 每块预览帧只显示对应演示(4/4)✓ ·
每块有代码折叠 ✓ · 帧高自适应 0 裁剪 ✓ · 0 控制台错误
node tools/run-mobile-regression.mjs → 100% | 77/77 | 5/5 页(演示页改动后复跑)
node tools/run-regression.mjs ×2 → 100% | 1026/1026 | N/A 34 | 79/79 页
```
### Deploy · 公网站更新到当前构建(移动端文档站四页 + 逐组件页 + PC 顶栏改动)
- 按 AGENTS §九 流程发布:`SITE_URL_BASE=https://kole-ui.mymoyu.top/ npm run build:site` → `build-mobile` + `build-uniapp`
→ `pack-deploy --tar`(1531 文件 / 5.8 MB,1.0 MB gzip)→ 上传 → 备份(目录 `/opt/aurora-admin.prev-20260920-0712`
+ 镜像 `kole-ui-showcase:pre-20260920-0710`)→ 暂存目录**逐文件哈希对账 0 差异(1531/1531)** → 替换 → `docker compose build && up -d`。
- **验收(服务器 127.0.0.1:3311)**:specs 目录 404 ✓ / 根 302→`/site/` ✓ / sitemap 200 ✓ / healthz 有 content-type ✓ /
SPA 深层路由 `/site/component/button/h5` 200 ✓;移动端与 uni-app 路径 8 条全 200(含此前 404 的
`site/m/guide.html`、`platform.html`、`tokens.html`);安全响应头 3 条在位。
- **公网 `https://kole-ui.mymoyu.top`**:`/site/`、`/site/m/`、`/site/m/guide.html`、
`/site/m/component/actionsheet.html`、`/versions.json`、`/sitemap.xml` 全 200;
`data.json` → `lib=kole-ui` / `version=2.0.0` / `generated=2026-09-20 07:09`(= 本次构建);
抽查 `site/m/guide.html` 与 `site/m/component/actionsheet.html` 的公网字节哈希与本地包**逐字节一致**。
- **过程发现并修掉一个会让构建直接失败的仓库状态问题**:本地 `Dockerfile` 曾带 `COPY 1.4.1 /usr/share/nginx/html/1.4.1`
行,而仓库根已无 `1.4.1/` 快照目录(并发改动期间快照被清理)——`docker compose build` 报
`failed to calculate checksum of ref …: "/1.4.1": not found`。该行与其后注释块("以后归档后在补一行")
的自洽性由 `tools/verify-versions.mjs` 双向卡住(磁盘快照 ↔ COPY 行),现已通过(33 checks)。
> 注:本轮第一次发布正是**卡在这条**(打包与 Dockerfile 之间差了几十秒的并发改动),已重新打包发布成功。
- **修掉发布过程中暴露的 500**:快照被移除后 `/1.4.1/site/` 由「归档站」变成 **500**(容器日志:
`rewrite or internal redirection cycle while internally redirecting to "/1.4.1/site/index.html"`)——
`nginx.conf` 里版本段的 `try_files` 回落目标又匹配回同一 location,形成内部重定向环;
且深链 `/1.4.1/site/component/button/h5` 同样 500。修法:末位补 `=404`,前三个参数退化为文件存在性检查
(快照在 → 发它自己的壳;不在 → 干净 404)。**并给这条失败模式加了门禁**:`tools/verify-versions.mjs`
新增「versioned fallback terminates with =404」,双向验证过(改动后 34 checks 全过;把 `=404` 去掉立刻 FAIL)。
实测:`/1.4.1/site/` 404、`/1.4.1/` 302(再 404)、其余验收全绿、容器日志 0 条 error。
> 归档的 1.4.1 快照内容本身没有被删除——它仍在服务器上的 `/opt/aurora-admin.prev-20260920-0712/1.4.1/`
> 与 `kole-ui-showcase:pre-20260920-0710` 镜像里;仓库当前按「不归档任何历史版本」的状态(`versions.json`
> 只列 2.0.0)发布,这是并发会话清理快照后的既定状态。
### Mobile docs · 补全左侧栏与页面内容(API / 6 端源码 / 令牌 / 回归状态)
> 起因:用户指出「移动端的左侧栏内容不完整,页面内容也不够完整」。移动端文档站此前只有顶部导航
> 与 6 个小节的组件页,没有左侧导航,也没有 API、源码、令牌、回归状态这些"读文档的人真正要用的东西"。
- **左侧栏**:所有页面共用一份 IA —— 「开发指南」5 项(移动端总览 / 快速开始 / 平台与端 / 设计令牌 /
测试与回归)+「组件 5」按分类分组(导航 / 手势 / 反馈)与计数,当前页/当前组件高亮并带 `aria-current`。
桌面恒定展开;**≤1000px 自动收起为可折叠面板**(点标题展开)。
> 踩坑记录:左栏用 `<details>` 承载时,**不能**用"去掉 `open` + CSS 强制展开"的写法 ——
> 不带 `open` 的 `<details>` 高度按关闭态算成 **0**,`overflow:auto` 会把里面的 nav 整块裁掉
> (DOM 里有 10 条链接、屏幕上一片空白,实测截图抓到)。改为 `open` 默认展开 + 断点脚本切换。
- **新增三页**:
- `guide.html` 快速开始:三步接入、按端引入路径表(6 端)、6 端最小示例(React / Vue 3 / Vue 2 / uni-app
可拷贝代码)、与 PC 端的边界、5 条常见问题(样式不生效 / `--kole-m-*` 取不到值 / 安全区 / 手势不触发 / 多 fixed 叠层)。
- `platform.html` 平台与端:两轴覆盖矩阵、uni-app 两列对照、12 行目录与命名映射、6 条隔离规则、
6 条门禁命令(各自检查什么)、新增组件的六步流程。
- `tokens.html` 设计令牌:15 个 `--kole-m-*` 表(名 / 值 / 用途)+ 组件实际引用到的继承令牌表(构建时扫描)、
单位与安全区与明暗模式三条约定、"改令牌的正确姿势"示例。
- **组件页 6 节 → 14 节**:新增 API(props / 事件 / 插槽三张表)、6 端源码(每端一个折叠块 + 复制按钮)、
用到的令牌(构建时从该组件 CSS 扫描,区分自有 / 继承 / 组件级变量)、交互与触控、无障碍、
变体维度 × 类名映射、测试与回归(**逐组件断言数**,取自回归报告的 `pageResults`)、设计契约(内嵌原始 JSON)、
相关组件。
- **契约补 4 类字段**:`interaction` / `accessibility`(逐字取自规格 §x.5 / §x.6)、
`api`(props / events / slots,写明"实现接口"的来源)、`variantClasses`(变体取值 → 类名/变量)。
- **新门禁 `npm run verify:mobile-docs`(8 条断言)**:页面齐全 / 组件页 14 小节 / 侧栏列全组件且恰好一个高亮 /
**契约 API 与 4 个框架端源码逐名一致(正向 + 反向)** / 每个声明的端都有非空代码块 /
`variantClasses` 的类与变量在组件 CSS 里真实存在 / 页面壳完整且无残留占位符。
Rationale:本站是生成物,生成器的 bug 只会让页面**悄悄少一块**,不会报错 —— 这类"缺内容"必须靠断言抓。
- **门禁当场抓到的真实漂移(已修)**:契约声明 `PullRefresh.threshold`,但 **uni-app 端没有这个 prop**(它用的是
模块常量)。已给 uni-app 端补上 `threshold`(rpx 默认 120),并把「浏览器端 px / 本端 rpx」的单位差异写进契约描述。
- **回归报告新增 `pageResults`**(逐页 total/pass/fail):组件页据此显示「断言 N 条 · 全部通过」。
PC 侧报告缺这一块(正是 S5-P17「失败落不了盘」的痛点),移动端先补。
- **令牌计数统一为 15**:安全区两条在 `@supports` 里各出现两次(0px 默认 + `env()` 覆盖),
此前构建日志/数据显示 17 而映射表显示 15;现按**唯一令牌名**计,并给这两条补上用途说明。
**验收(原样)**:
```
node tools/verify-mobile-docs.mjs → [OK] 8 条断言 · 9 页(含 API ↔ 源码逐名一致)
node tools/verify-mobile-isolation.mjs → [OK] 29 条断言
node tools/verify-uniapp.mjs → [OK] 9 条断言(8 个 SFC)
REG_BASE=… node tools/verify-mobile-site.mjs → [OK] 53 条断言(8 页 + 窄屏 3 页 × 4 宽度)
npm run smoke:site / verify:i18n → OK(16 checks)
浏览器实测:桌面左栏高度 500px / 10 条链接 / 恰好 1 个高亮;窄屏自动收起、点开可见、放大自动展开;
复制按钮写入剪贴板 2233 字符且文案变「已复制」;#api 锚点可定位;0 控制台错误
node tools/run-regression.mjs ×2 → 100% | 1026/1026 | N/A 34 | 79/79 页 | 0 超时
node tools/run-mobile-regression.mjs → 100% | 77/77 | 5/5 页 | 0 超时(逐页 14/15/16/15/17)
```
### Design system · SideMenu 花屏修复(nav-menu 族 `side` 方向)+ 演示页命名统一 + 布局守卫断言(S6-P42 / S6-P44 / S6-P43)
> 起因:用户报告「当前的项目有花屏 bug」。定位后是 v2.0.0 族重构(`eb25fee`)引入的回归,
> 只在 `direction=side` 上暴露;同批把「为什么 100% 回归没拦住它」「Linux 侧的命名分裂」一起收掉。
- **花屏现象与根因(S6-P42)**:`/site/component/sidemenu` 与 `frameworks/SideMenu.html` 的「代码演示」里,
二级项(全部订单 / 待发货 / 商品列表 / 分类管理)渲染成侧栏**右侧一列浮块**并压住同级项
(`工作台` 的 label 盒与 `全部订单` 交叠 52×14)。两处叠加:①族模板 `menu.html.tpl` 把子级拼在
`.kole-menu-item` **内部**,而该项是 `display:flex; height:44px` → 子级成了横向 flex 子项;
②`menu.css.tpl` 在 `side` 方向**没有折叠规则**(只有 `top` 有 `display:none` + hover 展开)。
重构前的手写实现是「子级挂在菜单根下当兄弟节点 + `.collapsed .children{display:none}`」,两条都在改写时丢了。
- **修法(唯一真源 `tools/lib/family-impl/nav-menu/menu.css.tpl`,5 端共用一份 CSS)**:side 一级项
`flex-wrap: wrap; height:auto; min-height:44px; row-gap:0`;`> .kole-menu-children { flex: 0 0 calc(100% + 32px); margin: 0 -16px; display:none }`,
`.is-open` 时 `display:block`;折叠态用**同权重且靠后**的选择器压住已点开的项。改模板后重跑
`node tools/gen-family-impl.mjs --only=nav-menu`(三个文件的 CSS 仍逐字相同)。
- **演示页命名统一到 Pascal(S6-P44)**:`git ls-files` 跟踪的 21 个演示页是小写
(`button.html` / `input.html` / … / `sidemenu.html`),而 `index.json` 的 `frameworksPrefix` 79/79 全 Pascal、
族生成器按 `<Prefix>.html` 写入、测试页 iframe 也写 Pascal → **Linux 下 21 个测试页 iframe 404**
(Windows 大小写不敏感把生成物折叠到小写文件上,本地完全看不出)。`git mv` 两步改名统一后,
大小写敏感审计(`data.json` 与测试页引用逐一与磁盘名**逐字**比对)**不一致处 = 0**;
`frameworks/` 仍 395 文件;`pack-deploy` 的样本断言 `frameworks/button.html` → `frameworks/Button.html`。
- **布局守卫断言(S6-P43)**:`tests/_behaviors.js` 新增 verb `layout-menu-rows`(纯几何、无交互)——
可见叶子文本两两重叠不得超过较小者的 55% / 可见子级不得越出菜单横向边界 ±2px / 折叠态不得露出子级;
三个导航族成员加入 `PILOTS`,族模板三个 `<nav>` 各声明一条 → **新增 9 条断言**。A/B 反例:注入旧 CSS 后
`sidemenu` 两条 fail 且报错与原始花屏逐字一致(`文本交叠 kole-menu-label「工作台」 × kole-menu-label「全部订单」 重叠 52×14px`)。
- **顶栏窄屏溢出**(同批发现):版本选择器 122px 在 375px 把汉堡挤出右边界 23px(英文 19px、360px 34px);
`site/style.css` 在 ≤600px 收紧为 68px(与并行会话的 ≤560px 收起并存互补,覆盖 561–593px 那一档)。
逐像素扫描 320–900px 中英双语溢出量全为 0;`npm run verify:nav` 349 项全绿。
- **判据修正(`tools/verify-versions.mjs`)**:①路径校验改为**按行为**验证(抽函数体跑输入矩阵),
原按写法匹配的检查在实现改写后误报;②硬编码版本号扫描**先剥注释**——原判据把文档注释里的路由示例
`1.4.1` 当成了硬编码(反例已验证:代码里写 `var v = 'v2.0.0'` 仍会失败)。
- **同批修掉的构建/环境问题**:`site/dev-server.js` 的白名单缺 `frameworks-mobile` / `frameworks-uniapp-pc`
(生产 nginx 走 `location /` 全放行,只有本地预览 403,移动端文档页本地打不开);
打包前 `SITE_URL_BASE=https://kole-ui.mymoyu.top/` 重建(否则 sitemap 落占位域名,`verify:site-routing` 红灯)。
- **发布链缺陷(S6-P48,发布验收时才暴露)**:`Dockerfile` 用 `COPY [0-9]*.[0-9]*.[0-9]*/ /usr/share/nginx/html/`
打包历史快照 —— COPY 源带结尾斜杠时复制的是**目录内容**,于是 `1.4.1/` 快照的 `site/`、`frameworks/`、
`sitemap.xml` **原地合并进站点根**,用快照那份旧构建覆盖当次构建(镜像里 `data.js` 的 `generated` 是
快照那次的 05:27、`frameworks/` 多出改名前的 21 个小写演示页),且 `/<版本>/` 目录根本没建出来 →
`/1.4.1/site/**` 全 **500**(S6-P45 的真因)。改为逐版本显式 `COPY 1.4.1 /usr/share/nginx/html/1.4.1`;
`tools/verify-versions.mjs` 加三条门禁(禁通配形 / 每个快照必须有 COPY 行 / 不许留已删版本的行),
反例已验证。修复后线上 `/1.4.1/site/index.html` 由 500 → **200**。
- **发布**(2026-09-20 06:00 构建,部署 `/opt/aurora-admin`):`pack-deploy --tar` 2914 文件 →
`ssh_upload`(sha256 校验)→ 暂存目录与本地发布集**逐字节核对 0 差异** → 目录替换 + `--no-cache` 重建 →
五条验收全中(specs 404 / 根 302→`/site/` / sitemap 200 / healthz 有 content-type / 深层路由 200)+ 版本 2.0.0;
**公网实测** `https://kole-ui.mymoyu.top`:`data.json` 的 `lib=kole-ui`、`version=2.0.0`、
`generated=2026-09-20 06:00`,`/site/style.css` 含新规则、`/frameworks/SideMenu.html` 200、
`/1.4.1/site/index.html` 200,演示帧几何实测「子级宽 220 = 侧栏宽 221、落在栏内、父项 44→140px」。
- **归档入口 302 目标修正**:`nginx.conf` 的版本裸入口规则注释写着「送到**该版**站点」,
代码却是 `return 302 /site/`(现行版本),正则也没捕获版本号 → `/1.4.1/` 与 `/1.4.1/index.html`
会把访问者带到现行文档,归档进不去归档。改为 `return 302 /$1/site/`(与 dev-server 的 `verRoot` 一致);
线上实测 `/1.4.1/` → `Location: /1.4.1/site/`。同时修 `verify-deploy-consistency.mjs` 的**假滞后**:
它直接比对响应字节,而 nginx 故意 302 的路径(根 `/index.html`、`/<x.y.z>/index.html`)拿到的是
302 响应体,与磁盘文件天然不同 → 这类路径改为跳过并单独计数。
- **回归**:PC 100%(1026/1026,79 页全过,0 超时);移动端 100%(77/77)。
- **并发写入说明(§九 的纪律)**:本轮发布期间另一会话持续改写 `site/app.js`、`site/style.css`、
`site/m/**`、`tests/mobile/**`、`ROADMAP/CHANGELOG/AGENTS`(实测 06:24 又改了 `app.js` 并重做快照,
晚于本次打包 06:21)。线上快照 = 本次打包时刻的工作树;之后工作树的改动不在线上。
已登记 S6-P49(把「打包后工作树再被改写」变成出包时的硬判据)。
### Docs site · 平台入口打通(PC ↔ 移动端)+ 技术栈选择器美化 + 本地访问方式
> 起因:用户要求「做一个合理的访问方式」与「当前的组件框架选择的样式需要美化一下」。
> 前者指:移动端文档站上线后没有入口,且本地起服务时端口被旧实例占用会静默失败(实测 403);
> 后者指顶栏那个裸 `<select>`(系统原生下拉、无悬停/展开态,与旁边的语言/主题选择器不同源)。
- **技术栈选择器改为「胶囊触发器 + 卡片菜单」**(`site/index.html` + `site/style.css` + `site/app.js`):
图标(层次符号)+ 当前值 + 箭头;菜单沿用语言选择器的卡片样式(标题分隔线、每项带**端文件名角标**
`html / jsx / vue2 / vue3`、选中项对勾、头部右侧显示当前端文件名)。键盘全路径:`↓/↑` 移动、
`Home/End` 首尾、`Enter/Space` 选中、`Esc` 关闭并回焦、`Tab` 关闭不抢焦点、点击外部关闭;
展开态旋转箭头 + 品牌色焦点环;暗色与 `prefers-reduced-motion` 均适配。
- **原生 `<select>` 保留为状态真源**(`.fw-native`:1px 视觉隐藏,不进 Tab 序列):
选中项写回它的 `value` 并派发 `change`,既有的「状态更新 / localStorage / 路由跳转」管道
与既有验证脚本(`run-site-smoke` 读 `inputValue()` + `selectOption()`)**一行未改**。
- **侧栏新增「平台」分组**:`PC 端组件`(带「当前」角标与副标题)/ `移动端组件`(副标题写清
5 组件 × 6 端含 uni-app)。移动端入口是**整页跳转**(指向站点根下的 `m/index.html`)——
SPA 的路由拦截器只接管无扩展名的地址,带 `.html` 的链接交回浏览器,两个站各自独立加载。
- **移动端文档站顶栏加平台切换** `[PC 端] [移动端]`(当前侧实心品牌底),与 PC 侧形成双向入口。
- **本地访问方式**(`site/dev-server.js`):端口被占用时**自动向上找空闲端口**(最多 10 档)并说明,
不再 `EADDRINUSE` 直接退出;`KOLE_PORT` 显式指定时不回退(CI/脚本依赖固定端口)。
启动即打印 6 个入口:PC 文档站 / 移动端文档站 / 组件总览 / PC 测试总览 / 移动端测试总览 /
两个回归收集器,并给出 `REG_BASE=` 提示。
> 实测踩到过:3311 上挂着**改动前启动**的实例时,新目录一律 403,日志里只有一句 `EADDRINUSE`,
> 极易误判成「构建没产出」。
- **修:移动端文档站在小屏上横向溢出**(实测 375px 溢出 128px、414px 溢出 89px)—— 一个讲移动端的
文档站自己在手机上横向滚动说不过去。元凶两处:表格里**不可断行的长路径**
(`.design_library/kole-ui-mobile/components/` 把表格最小宽度顶到 462px)与组件页固定 375px 的设备帧。
修法:单元格与 `<code>` 允许长 token 断行、设备帧取 `min(375px, 100%)`、顶栏 ≤720px 换行并把副导航移到第二行。
修后实测 320 / 375 / 414 / 768 / 1280 全部 0 溢出、0 控制台错误。
- **新增窄屏门禁**:`tools/verify-mobile-site.mjs` 加「窄屏无横向溢出」一节(3 类页面 × 4 个宽度 + 控制台错误),
断言数 38 → **53**。只靠"在桌面宽度看一眼"发现不了这类问题。
- **修**:移动端文档站与规格文件里对 `MOBILE.md` 的失效引用 → `PLATFORMS.md`(4 处源码,
生成物已重跑)。该文档在交付时最终命名为 PLATFORMS.md。
- **验证脚本同步**:`verify-nav-responsive.mjs` 的字体容忍度元素清单随 UI 更新
(`.fw-label`/`#fw-select` → `.fw-cur`/`.fw-menu .fw-opt`)—— 清单不跟着改会把余量报得偏乐观。
实测余量 5.8%(英文 · 1367px)。
**验收(原样)**:
```
浏览器功能断言(Chromium 1440×900) 25/25 PASS,全程 0 控制台错误
├ 触发器/菜单/4 选项/端文件名角标/选中态/aria-expanded
├ 选择 Vue 3 → 触发器文案 + #fw-select value + localStorage + 路由 /component/button/vue3 同步
├ 键盘:Esc 关闭、↓ 打开并聚焦当前项、↓ 环形移动、Enter 生效
├ 侧栏平台入口 2 项、href=/site/m/index.html、整页跳转到移动端站、移动端站回链 ../index.html
└ 暗色模式菜单底色 rgb(28,31,38)(跟随主题)
node tools/verify-mobile-isolation.mjs → [OK] 29 条断言
node tools/verify-uniapp.mjs → [OK] 9 条断言(8 个 SFC)
REG_BASE=… node tools/verify-mobile-site.mjs → [OK] 53 条断言(8 页 + 3 页窄屏 × 4 宽度)
npm run smoke:site → OK — all checks passed
npm run verify:nav → OK — all checks passed(余量 5.8%)
npm run verify:playground → OK — all checks passed
node tools/run-regression.mjs ×3 → 100% | 1026/1026 | N/A 34 | 79/79 页 | 0 超时
node tools/run-mobile-regression.mjs → 100% | 77/77 | 5/5 页 | 0 超时
```
### Tooling · 发布快照一致性核对(升级验收)+ 验证脚本的跨平台/远程修正
> 起因:站点发布后做升级验收,要回答「线上跑的到底是工作树的哪个状态」。此前只有 AGENTS §九 里
> 一句**手工步骤**("打包前后各查一次产物哈希")—— 本轮实测线上 2908 个可比对文件里
> **1883 个与工作树不一致**(滞后 917 / 缺失 966),手工核对不可能发现。
- **新增 `tools/verify-deploy-consistency.mjs`**(`npm run verify:deploy`):按发布集枚举工作树里
会随包出去的文件,逐个拉远端同路径比对 sha256,报「一致 / 滞后 / 线上缺失」;生成物
(`tests/report*.json|xml`、`site/data.js`)单独计数不算失败。零依赖(node 内置 fetch)。
`--base=` 指向任意实例(本地 dev-server 或公网),`--only=` 限定子串快速跑。
- **发布集抽成 `tools/lib/publish-set.mjs`**:`.dockerignore` 的解析与匹配语义、内容目录清单、
历史版本快照目录原先只存在于 `tools/pack-deploy.mjs`。核对脚本必须与打包器看**同一份清单**
(否则又会出现"打包器说发了、核对脚本说没发"),故抽成模块供两处 import;
`pack-deploy` 重构前后输出逐字节一致(复用其自带断言:2914 文件 / 排除 26 / 断言 13+34 全过)。
- **`tools/verify-playground.mjs` 两处跨平台/延迟修正**(都只在**对远程部署**跑时暴露):
① mock 里的演示文件路径原先写死小写(`/frameworks/card.html`)—— Windows 不区分大小写照样过,
到 Linux 容器就是 404,改为一律从 `data.json` 的 `files.html` 派生(各组件大小写不同:
`Card.html` / `button.html`);② 远程下 `data.json`(1.36 MB)与令牌样式表比文档慢,
预检超时放宽到 30 s,暗色令牌改「等到真生效再读」,不再把"还没加载完"当成"没反色"。
- **发布集同步覆盖 `frameworks-mobile` / `frameworks-uniapp-pc`**(新平台上线后核对不漏检)。
### 深检修复批次:暗色对比度(站点 chrome)+ 两处测试侧缺陷
> 起因:S6-P37 交付后的深度检测(数据层审计 / 79 页巡检 / 侧栏不变量 / 暗色对比度 / 构建确定性)
> 报出 3 个问题(ROADMAP S6-P39/P40/P41)。本轮全部修掉,并把判据固化进验收脚本。
- **暗色对比度(S6-P39)**:`site/app.js` 的 `applyTheme()` 把暗色品牌色族的混白比例从 0.25 提到
**0.45**(悬停 0.6 / 按下 0.3 保持「悬停更亮、按下更暗」的次序)—— 品牌色文字常压在品牌浅底
(`--kole-color-brand-bg` = 同色 18% 叠卡片底)上,只混 0.25 时实测 **3.59:1**,低于 AA 正文 4.5:1;
提到 0.45 后 **7.27:1**。注意这几条是**内联**写在根元素上的,样式表规则压不过它们。
- **同时把「站点 chrome 的暗色对比度」纳入 `tools/verify-dark.mjs` 第 4 组**(7 条路由,正文 4.5:1 /
大字与图标 3:1,含侧栏选中态与含必填徽章的组件页)。该组一上线即抓出**另外四处既有缺陷**并修掉:
行内 `code` 2.88:1、`.radius-demo` 3.44:1、`.callout.note` 4.14:1(三者用品牌色族偏暗档 → 暗色改用基色)、
`.code-inline` 2.88:1(代码块借用的 `--kole-color-tooltip-bg` 在暗色下是**浅色**,与固定浅色的代码文字撞车
→ 暗色改用 `table-header-bg`,11.1:1)、`.badge-opt` 3.08:1 与 `.badge-req` 3.71:1(**亮色模式也不达标**
→ 改用令牌,4.88 / 5.57:1)、`.callout.warn` 4.14:1(亮色不达标 → 文字换规格定义的 `#8C5A00` 5.51:1,
暗色另给低透明底 6.49:1)。`node tools/verify-dark.mjs` 现 **DARK VERIFY OK**。
- **`verify-nav-responsive` 红灯(S6-P40)**:脚本原固定在 390px 点 `#lang-trigger`,而工作区一度加了
「≤420px 语言选择器让位」的规则 → 元素不可见、30s 超时崩溃。改为**与规则解耦**:叠层检查
(语言菜单 vs 抽屉)固定放在语言选择器可见的 **480px** 视口,390px 段只覆盖抽屉交互本身 ——
规则在或不在都能跑。
- **对比度断言采到过渡中间帧(S6-P41)**:`tests/_runtime.js` 的对比度遍历前后临时注入/移除
`#kole-assert-settle`(`*{transition:none!important;animation:none!important}`)——**阈值不动**(仍 4.5:1),
只是不再把过渡中间帧当结果(反例:DensitySwitcher 选项按钮过渡中途 1.74:1,稳定态 7.00/5.85:1)。
另把 `tests/_runtime.js` 已算好的 `failureDetails` 落进 `report.json` 与 JUnit(此前只存断言 ID,
偶发失败事后无法回溯)。**验证**:断言总数仍 1017,并发压测由「180 次加载失败 1 次」变为 **300 次 0 失败**。
- **亮色模式也补了同一类检查**:把对比度探针抽成 `tools/lib/contrast-probe.mjs`(半透明底沿父链合成 +
正文 4.5:1 / 大字与图标 3:1 判据),`verify-dark.mjs` 第 4 组与 `verify-theme.mjs` 新增的
`chromeContrastCase` 共用它,亮/暗各跑 3 条路由。亮色一上线又抓出**两处既有缺陷**:搜索框占位文字
`span.st-text` 4.23:1、搜索面板副标题 `span.sp-sub` 4.16:1(都是 `text-placeholder #767676` 压在浅底上)
→ 改用 `text-secondary`(4.75:1)。`node tools/verify-theme.mjs` 现 **THEME VERIFY OK**。
- **公网部署复核(S6-P38)**:公网 `https://kole-ui.mymoyu.top/site/` 已是 `lib: kole-ui` / `version: 2.0.0`
(`generated 2026-09-20 05:27`,标题 `Kole UI · 组件库文档`),AGENTS §九 四条验收全过;
§九 里残留的 `/opt/kole-ui.staged` 暂存目录名与部署目录 `/opt/aurora-admin` 对齐。
### Design system · 修 SideMenu 花屏:nav-menu 族 `direction=side` 子菜单渲染错位(S6-P42)
> 起因:用户报告「当前的项目有花屏 bug」。定位后是 **v2.0.0 族重构(`eb25fee`)引入的回归**,
> 只在 `direction=side` 上暴露,文档站组件页与演示页同时可见。
- **现象(2026-09-20 实测)**:`/site/component/sidemenu` 与 `frameworks/sidemenu.html` 的「代码演示」里,
二级项(全部订单 / 待发货 / 商品列表 / 分类管理)渲染成侧栏**右侧一列浮块**,横向越出 220px 侧栏,
并压住同级项(`工作台` 的 label 盒与 `全部订单` 交叠 52×14)。三个演示块(默认态 / 带徽章 / 折叠态)全中。
- **根因(两处叠加)**:①族模板 `menu.html.tpl` 的 `itemHtml()` 把子级拼在 `.kole-menu-item` **内部**,
而该项是 `display:flex; height:44px` → 子级被当成横向 flex 子项挤在同一行、纵向溢出;
重构前的手写实现是把子级 `appendChild` 到**菜单根下当兄弟节点**,故无此问题。
②`menu.css.tpl` 在 `side` 方向**没有折叠规则**(只有 `top` 方向有 `display:none` + hover 展开),
子级常驻渲染;重构前有 `.aa-sidemenu.collapsed .aa-menu-children { display: none }`。
- **修法(单点在唯一真源,5 端共用一个 CSS,HTML/JSX/Vue 模板无需改)**:
`tools/lib/family-impl/nav-menu/menu.css.tpl` —— side 一级项改为
`flex-wrap: wrap; height: auto; min-height: 44px; row-gap: 0`;`> .kole-menu-children` 为
`flex: 0 0 calc(100% + 32px); margin: 0 -16px; display: none`,`.is-open` 时 `display: block`;
折叠态用**同权重且靠后**的规则压住已点开的项(否则 `.is-open` 权重更高会胜出)。
改模板后重跑 `node tools/gen-family-impl.mjs --only=nav-menu`(三个文件的 CSS 仍逐字相同)。
- **验证**:几何实测 —— 未展开 `display:none`;展开后子级宽 220 = 侧栏宽且完整落在栏内;父项 44→140px;
可见 label 两两无交叠。`top`(hover 展开)与 `mixed` 未回归。回归 100%(1017/1017,79 页全过,0 超时)。
- **同批登记、未在本任务修**:S6-P43(测试矩阵缺「布局包含性」断言,故本次花屏被判为全绿)、
S6-P44(`frameworks/sidemenu.html` 大小写命名分裂,Linux 下测试页 iframe 指向不存在的文件)。
### Mobile · 移动端平台上线:与 PC 端物理隔离(平台 × 端两轴,含 uni-app 端)
> 起因:用户要求「在组件库额外添加移动端组件,移动端和 PC 端隔离」,并明确「PC 和移动是两个大分类,
> 移动端后面是 H5 移动端、Vue 移动端,而且 uni-app 也会经常用到(uni-app 移动端 / uni-app PC 端)」。
> 据此把组件库组织成**两个正交轴**:平台 `pc | mobile` × 端 `css | html | jsx | vue2 | vue3 | uniapp`。
> 新增 **`PLATFORMS.md`**(两轴定义、目录与命名映射、隔离规则、新增组件六步流程)。
- **移动端 5 个组件 × 6 端 = 30 个实现文件**(`frameworks-mobile/`):`navbar`(顶部导航栏)、
`tabbar`(底部标签栏)、`actionsheet`(动作面板)、`pullrefresh`(下拉刷新)、`swipecell`(滑动单元格)。
每组件六端:CSS / H5 演示页 / React / Vue 2 / Vue 3 / **uni-app**。类名前缀 `kole-m-`,导出名 `KoleM*`。
- **移动端规格原文(本仓库自撰)**:`.design_library/kole-ui-mobile/spec/移动端规格.md`。
PC 端规格来自外部交付的 `组件1~10.txt`;移动端**没有**外部规范,故契约 `sourceKind: authored-spec`、
`provenance: authored-in-repo`——不冒认外部来源;契约的 `doNotInvent` / `unknowns` 逐条对应规格条目。
规格共 5 节(另有 §〇 隔离总则),5 份契约全覆盖。
- **令牌层**:`.design_library/kole-ui-mobile/colors_and_type.css` = `@import` PC 令牌(颜色/字体/圆角/阴影同源)
+ 15 个 `--kole-m-*`(触控 44px 最小热区、`@supports (env())` 安全区、移动端字号、手势动效时值)。
**PC 令牌文件零改动**,改一处令牌两端同时生效。
- **隔离边界**(每一条都有断言,见下):实现目录(`frameworks-mobile/` vs `frameworks/`)、契约目录、
类名前缀、令牌前缀、测试页(`tests/mobile/` vs `tests/`)、回归报告(`tests/mobile-report.json` vs
`tests/report.json`)、文档站(`site/m/` 静态站 vs `site/` SPA,移动端**不进** PC 路由表)、
分发产物(`dist/mobile/*` vs `dist/*`)。
- **uni-app 端两处**:移动端 × uni-app(5 个,目标 `app-plus` / `mp-weixin` / `h5`,用 uni 基础组件 +
`rpx` + **touch 事件**——小程序与 App 端无 PointerEvent);**PC × uni-app 试点 3 个**
(`frameworks-uniapp-pc/{Button,Input,Card}.uniapp.vue`,类名与 PC 的 `frameworks/*.css` 逐字一致、
`--kole-*` 令牌、px 尺寸、目标 H5/PC 容器),覆盖率 3 / 79 并登记为 ROADMAP S7-P26。
- **构建(Node,跨平台,不碰 Windows-only 的 PS 链)**:`tools/build-mobile.mjs`(导出 `site/m/data.mobile.json`
自包含 102 KB = 5 组件 × 6 端源码、`site/m/index.html` 总览、`site/m/component/<slug>.html` 逐组件页、
`tests/mobile/**`、`dist/mobile/**`)与 `tools/build-uniapp.mjs`(`dist/uniapp-pc/**`)。
两个脚本都带**写入守卫**:越界路径直接抛错(移动端脚本只允许写 `site/m/` `tests/mobile/` `dist/mobile/`)。
- **测试**:移动端测试页以 **375×640 设备帧** iframe 载真实演示页;断言引擎与 PC **共用** `tests/_runtime.js`;
行为库为移动端专属 `tests/mobile/_behaviors.js`,新增 3 个触控动词 `swipe-sets-class` / `swipe-sets-attr` /
`pull-triggers`(合成 `pointerdown → pointermove → pointerup` 手势,断言类/属性真实变化)。
**两端行为库互不加载**,PC 侧 6 试点组件不受影响。
- **门禁与 CI**:`tools/verify-mobile-isolation.mjs`(**28 断言**:PC 零污染 / 移动端自洽 / 分发隔离)、
`tools/verify-uniapp.mjs`(**9 断言**:SFC 三段 / `node --check` 语法 / 标签配平 / 禁 DOM API /
手势必须 touch / 只用 uni 基础组件 / 前缀隔离 / 单位策略)、`tools/run-mobile-regression.mjs`。
`regression.yml` 新增:构建分发产物 → **移动端生成物可复现性断言**(`git diff --quiet` 卡"改了索引忘重跑构建")
→ 两条门禁 → 移动端回归 → 报告上传与摘要(PC 与移动端两段)。
- **部署**:`tools/pack-deploy.mjs` 的 `CONTENT_DIRS` 增 `frameworks-mobile`、`frameworks-uniapp-pc`,
新增 6 条硬断言(移动端 30 实现 / 5 文档页 / 5 测试页 / 3 试点);`Dockerfile` 增两条 `COPY`;
`.dockerignore` 显式排除 `.design_library/kole-ui-mobile/spec`(移动端规格原文与 PC 规格同策略不进镜像);
`site/dev-server.js` 白名单增两个顶层目录。
**验收(原样)**:
```
node tools/verify-mobile-isolation.mjs → [OK] 隔离门禁全部通过(28 条断言)
node tools/verify-uniapp.mjs → [OK] uni-app 门禁全部通过(9 条断言 · 8 个 SFC)
node tools/run-mobile-regression.mjs → passRate 100% | pages 5 (all-pass 5) | assertions 77/77
node tools/run-regression.mjs ×8 → run 1..8 全为 100% | pass 1017 | fail 0 | na 34 | pages 79/79 | timedOut 0
node tools/pack-deploy.mjs → OK:components 79 薄壳/395 实现 · mobile 30 实现/5 文档页/5 测试页 · uniapp-pc 3 试点
```
**未执行**(写明并给复现命令,见 ROADMAP S7-P25):uni-app 真实编译(H5 / 微信小程序 / App)——
需 `@dcloudio/vite-plugin-uni`,与零运行时依赖不冲突但会污染主回归 job 的依赖,故本次只做静态门禁;
复现命令写在 `tools/verify-uniapp.mjs` 头部。
### Docs site · 主题模式默认改为「自动」(日间 / 夜间 / 自动 三态)
> 起因:用户要求「添加合适的日间/夜间/自动选择模式,默认为自动」。三态 UI(顶栏模式菜单、
> H5 `ThemeSwitcher`、在线测试页)此前已在,**缺的是默认值**:没有显式选择时回退 `light`。
- **默认值**:`kole-mode` 缺省 / 无法识别 / 被清空 一律为 `auto`(跟随系统 `prefers-color-scheme`);
首次加载把默认值落盘,之后各标签页由 `storage` 事件保持一致。改动点:`site/app.js`
(`MODE_DEFAULT` + `normalizeMode`/`readMode`,`applyMode(mode, persist)` 增加落盘开关)、
`site/playground.js`、`frameworks/ThemeSwitcher.html`。
- **无闪烁**:`site/index.html` 与 `site/playground.html` 的 `<head>` 加解析期 bootstrap,
在首个 `<link>`/脚本之前就定好 `kole-dark` 类,避免「先白后黑」。
- **原生控件跟随**:令牌文件 `:root` 加 `color-scheme: light`、`html.kole-dark` 加 `color-scheme: dark`
(暗色下滚动条 / 表单 / 系统色不再留在浅色);两页再补 `<meta name="color-scheme" content="light dark">`。
令牌数与三格式导出不变(仍 106 tokens)。
- **i18n**:新增 `{m}(当前:{e})` 词条,模式名与「当前生效」改为整句翻译,英文下不再出现中文残段。
- **验证**:`tools/verify-theme.mjs` 从 24 条扩到 38 条,覆盖「首访默认 auto(含系统夜间 + 切回日间即时反色)」
「默认值静态卡口」「bootstrap 先于 data.js」「ThemeSwitcher / playground 默认自动」。
- **顶栏常驻入口(同日追加)**:此前模式切换只在右下角悬浮按钮里,顶栏看不见(用户实测反馈「导航栏没有选择主题」)。
现于顶栏「框架选择器 → 语言选择器」之间加常驻触发器(图标 + 当前模式名),与悬浮按钮共用同一份状态:
两入口互斥展开、Esc 关闭、`aria-checked` / `aria-expanded` 同步。响应式降级:>1100px 显示模式名,
≤1100px 只留图标(与语言选择器同一档),≤560px 框架选择器整块收起,≤340px 语言选择器让位——
320px 顶栏溢出 54px 由此消除(只量顶栏自身;首页内容另有 15px 既有溢出,不在本次范围,另记 ROADMAP)。
触发器不加下拉箭头:顶栏在 1367px(英文、导航尚未折叠)只剩 2px 余量,箭头会直接溢出 6px。
- **新增断言**:`verify-theme.mjs` 覆盖顶栏入口可见性 / 默认选中自动 / 选夜间生效并落盘 / 模式名同步 /
两入口互斥 / Esc 关闭 / 375px 与 320px 降级;`verify-nav-responsive.mjs` 18 宽度 × 中英双语全过。
- **视觉打磨(同日追加)**:顶栏触发器加 15×15 图标底衬(`--kole-color-table-header-bg`,hover / 展开转品牌浅底);
菜单头改为**两行**(「当前选中」+ 自动模式下补「实际生效」,生效主题用品牌色强调)——
单行拼接在英文下会折行;菜单宽度 168 → 184px、选项高度 32 → 34px、加 160ms 展开动画;
选中行加 `inset 2px` 左侧品牌色条(浅底在暗色下对比有限);三态色标改为**语义化配色**:
日间 = 白底、夜间 = `--kole-color-page-bg`、自动 = 左右半分渐变(直接表达「跟随系统」)。
踩坑记录:色标最初用 `--kole-color-tooltip-bg`,而它是**反色**工具提示底(亮色下深、暗色下 `#E8EAED` 浅),
当「夜间」色标正好反掉,已改用页面底色令牌。
- **降级阶梯补齐(同日追加)**:`≤560px` 版本选择器(`#ver-select-wrap`,实测 122px)让位 ——
它是 ≤900px 接管出现的,加上主题入口后 375px 溢出 23px(中英皆是)。版本切换在更新日志页仍有入口。
### Docs site · 内容与信息架构对齐 Element 文档站(侧栏分组 / 快速开始叙述 / 组件页 API 说明)
> 起因:用户以 Element UI 文档站(element.eleme.cn)为参照,指出「快速入门等地方的描述更好,
> 还有组件页面等内容,还有左侧导航栏内容」。三块逐项对齐,但**不照搬其 API 表的「可选值」列**——
> 本仓库没有可引用的枚举数据源(见下)。
- **侧栏(`renderSidebar`)**:页面链接原先平铺在侧栏顶部、无归属标签,现按 Element 口径分为
「开发指南」(组件总览 / 快速开始 / 设计规范 / 常见问题 / **更新日志**)与「组件 N」两个段标题;
「更新日志」此前在侧栏没有任何入口(ROADMAP S5-P24 的实测表长期记着「维持原状」),本次补齐。
段标题用 `.side-section` 与可点的分类组(`.side-group`)区分开。
- **快速开始(`renderGuide`)**:改为 Element 式的分步叙述 —— 开头一句「本节将介绍…」+ 5 条步骤清单
(`.guide-outline`),每节先说明再给可拷贝的代码;补上 Element「全局配置」段在本系统的对应物
「全局配置(主题与暗色模式)」(令牌覆盖 + `html.kole-dark` 用法);收尾加「开始使用」四张下一步卡片;
并按 Element 的「完整引入 / 按需引入」对位说明本系统的口径(组件以源码发布,取哪几个文件即按需;
全量样式用聚合文件 `components.css`)。共 7 节,右侧目录同步。
- **组件页 API 表(`buildApiSection` + `tools/precompute.mjs`)**:说明列原先 **235 条里 230 条是 `—`**
(只有源码里写了注释的才有),事件说明则是模板句(「组件交互触发事件」占多数)。现改为构建期按
出处生成、每条都标注来源:**规格**(29 条,命中规格原文分条,`title` 逐字引用该条,如 Button
`type` → 「类型:主按钮、次按钮…」)/ **命名**(204 条,API 名释义,明确标注「非规格原文」)/
**源码**(1 条注释)/ **契约**(1 条 dims);事件说明由 emit 调用点判定,**同一事件名在不同组件里
语义不同**(`ok`:modal / fullscreenmodal 绑确认按钮 → 「点击确认按钮时触发」,alertmodal
`@click.self` 绑遮罩 → 「点击遮罩时触发」),98/98 全部具体化,不再有模板句;表头上方加出处图例。
- **不做「可选值」列(有意)**:Element 那一列靠其运行时枚举,本仓库实测没有等价数据源 ——
从 5 端源码反推比较字面量只覆盖 **12/235(5%)** 且多为 `1` / `number` 这类噪声,规格原文里
真正枚举取值的只有 **2 条**。硬填等于发明,违反契约 `doNotInvent` 口径;改为把规格里确实枚举了的
取值以「取值:主按钮 / 次按钮 / …」副行带出 —— **深度检测后又收紧了这道闸门**:初版命中 4 条,
其中 `card.title` / `modal.title` 命中的是「结构:标题区、内容区、操作区」这类**结构描述**
(枚举的是卡片分区,不是 title 的取值),`tag.color` 还切出了 `绿#F6FFED` / `#52C41A等)` 碎片。
现要求「分条标签 === 该 prop 的说明」且片段不含 `#` / 括号 / `px`,最终**只保留 `button.type` 1 条**
(类型:主按钮、次按钮、文字按钮、链接按钮、危险按钮),其余留空。
- **新增元素的对比度**:`.side-section`(段标题)与 `.api-src`(出处标记)初版用
`--kole-color-text-placeholder`(#767676),在页面底色 #F5F7FA 上只有 **4.23:1**,达不到 AA 正文 4.5:1;
改用同族 `.side-group` 已在用的 `--kole-color-text-secondary` 后为 **4.66:1(悬停行)/ 4.75:1(页面底)**,
暗色 5.65:1 / 7.12:1。深检另发现一处**既有**缺陷(暗色选中态文字 3.59:1)与两处**测试侧**问题
(`verify-nav-responsive` 红灯、对比度断言采到过渡中间帧),按仓库规矩记为 ROADMAP S6-P39 / P40 / P41,
未在本任务内顺手修。
- **i18n**:新增英文词条 166 条(含上述 106 个属性释义、29 个事件说明与新增 UI 文案),
`tools/verify-i18n.mjs` 的「literal T() coverage」从 289 键缺 1 到 **289 键全覆盖**。
**验收**:`verify-i18n` 16 项全过;`verify-site-routes` / `run-site-smoke` / `verify-theme` 全过;
数据层审计 9 项(含「cite 逐字出自规格原文」)、79 页浏览器巡检(零报错/零 4xx/说明列无空值)、
侧栏高亮不变量(8 路由 × 双语)、precompute 幂等与两次运行逐字节一致 —— 全过;
回归 **100%(1017/1017)连跑 8 次一致**,深检复跑 10 次仍 100%。
(`verify-nav-responsive` 当前红灯,原因是工作区里新增的 ≤420px 隐藏语言选择器与脚本固定 390px 的点击冲突,
见 ROADMAP S6-P40,非本改动引入。)
### Docs site · 「在线测试」预览帧:修掉资源解析错误 + 沙箱收紧 + 三处丢改动缺陷
> 起因:站点已通过 frp + EdgeOne 暴露到公网(`https://kole-ui.mymoyu.top/site/`),
> 「在线测试」是全站唯一把**用户编辑的 HTML** 灌进 iframe 执行的地方,因此按不可信内容重新过一遍。
- **预览帧的组件样式一直没加载(79 个组件里 36 个命中)**:`srcdoc` 的基准 URL 继承父页
(`/site/playground.html`),演示源码里的 `href="./Xxx.css"` 于是解析成 `/site/Xxx.css` ——
本地 404、线上 404(实测 `https://kole-ui.mymoyu.top/site/AnchorNav.css` → 404,
而 `.../frameworks/AnchorNav.css` → 200)。受影响的 36 个组件在预览里**丢掉整个组件 CSS**,
只剩内联 `<style>`。修法:渲染前在文档最前注入 `<base href="../frameworks/">`(放在
`<!DOCTYPE>` 之后,避免掉进怪异模式),让预览的资源解析与「演示原页」逐字一致;
注入的 base 是文档里第一个 base,用户代码里再写 base 也覆盖不了。
- **帧内策略收紧(只写比父页更严的项)**:注入 `meta CSP`,把 `connect-src` / `form-action` /
`frame-src` / `object-src` 一律置 `'none'` —— 帧内**彻底发不出网络请求**。两条 CSP 取交集,
所以这里每一项都只会收紧;父页 CSP 继续负责"允许什么"(`style-src 'self'` 等),
本页不重复声明,避免把 `'self'` 在不透明源下的解析差异带进来。
- **沙箱边界用探针服务器实测,而不是读代码**:新增 `tools/verify-playground.mjs`,脚本内起一个
本地 HTTP 探针,帧内发起的每个请求都会落在它身上。实测(Chromium,`sandbox="allow-scripts"`,
无 `allow-same-origin`):`location.origin === 'null'`;`parent.document` / `top.document` /
`document.cookie` / `localStorage` 全部 `SecurityError`;`window.opener` 为 `null`;
`window.open` 返回 `null`(缺 `allow-popups`);表单提交、子帧、外域样式/`@import`/图片
全部被拦;`fetch` / `sendBeacon` / `WebSocket` 被 CSP 拦下。**探针 0 命中**。
父页侧另有 nginx/dev-server 的 CSP + `X-Frame-Options: SAMEORIGIN` + `Referrer-Policy: no-referrer`
(线上实测这些响应头经 EdgeOne 完整透传)。
- **帧内报错不再静默**:注入一段上报器(跑在用户代码之前)捕获 `error` /
`unhandledrejection` / `securitypolicyviolation`,用 `postMessage` 回传,预览上方以
**纯文本**提示(`.pg-note`)。父页只认「`event.source` 严格等于本页预览帧」的消息,
且只取字符串渲染、每次渲染最多显示 3 条 —— 预览里的代码无法借此碰本页 DOM,也无法刷屏。
- **三处会丢改动的缺陷**:①切组件会**静默丢弃**未复位的编辑 → 现在先 `confirm()`,
取消则编辑器与选择器都留在原地;②源码拉取失败时旧实现会 `pristine = ''; editor.value = ''`
**清空编辑器** → 现在失败一律不改状态(编辑器、预览、选择器全保持原样,只报错);
③刷新/关页同样丢改动 → 补 `beforeunload`(仅在"改过且未复位"时拦)。
另把防抖清理提前到 `load()` 开头(异步回落路径下,挂着的定时器会把上一个组件的内容渲染到新组件头上)。
- **预览帧内自己跳走会让预览失效且不可恢复**:`<meta refresh>` 或 `location.href` 会让帧离开
`srcdoc`(实测落到 `chrome-error://chromewebdata/`,状态行却仍显示"已同步")。父页读不到
帧去了哪(不透明源),唯一能感知的是 `load`:新增看门狗 —— 凡是不是本页触发的加载,
就地重建预览并标注原因(实测帧回到 `about:srcdoc` 后脚本从头跑一遍,预览恢复可用)。
- **行为反转(原为「有意保留的差异」)**:预览帧现在**跟随站点暗色**。原实现之所以反不了色,
是"帧是不透明源 → 父页注入不进 `kole-dark`"这个**机制限制**,不是设计取向(S5-P23 已把
演示原页的取向定为「站点暗色时演示页也反色」)。在线测试页唯一可用的注入点是源码,
因此改在注入内容里带一个类;实测站点 `kole-mode=dark` 时帧内
`documentElement.className` 含 `kole-dark`、`--kole-color-page-bg` 解析为 `#14161C`。
另补 `storage` 监听:在文档站切主题,本页预览跟着重渲染。
- **验收**:`npm run verify:playground` → **38 条断言全过**(含全 79 个组件逐个切一遍、36 个带
`./` 引用的组件 CSS 确实从 `/frameworks/` 取到 200、探针服务器 0 命中、脏状态两条离开路径、
失败不改状态、帧内跳转重建、报错回传、暗色跟随、全程 0 个 4xx);已接入 `regression.yml`。
回归 **100%(79/79 页、1017/1017 断言、N/A 34、0 超时)连跑 8 次一致**;
`verify-site-routes` 24/24、`smoke:site` 全过、`verify:i18n` 16/16。
- **未执行**:未部署。工作树领先线上 1229 个文件(`site/data.json`、`site/playground.*` 均与线上
哈希不同),按 AGENTS §九 走 `pack-deploy` 会把在飞的 S6 组件族改动一并发布 —— 这属于发布决策,
留给用户定;命令见 ROADMAP 新增条目。
### Docs site · 文档站路由去掉 `#`(hash → History API)
- **URL 形态**:`/site/#/component/button/h5` → `/site/component/button/h5`,`/site/#/overview` → `/site/overview`。站内跳转改走 `pushState` 局部渲染(顶栏/侧栏/总览卡片/搜索/相关组件/上下篇统一收敛到 `go()` + 一处捕获的链接拦截),浏览器前进后退走 `popstate`。旧链接仍可用:加载时就地 `replaceState` 成无 `#` 地址且不留历史记录(实测 `/site/#/component/modal` → `/site/component/modal` 并正常渲染)。
- **深层路径要服务器接住**:磁盘上没有 `/site/component/button/h5` 这个文件。三处回落同时补上 —— `nginx.conf` 新增 `location /site/` 回落到 `try_files $uri $uri/ /site/index.html`(另加一条正则 location,带扩展名的缺失资源仍回真 404,不会把 HTML 当 JS/CSS 发出去)、`site/dev-server.js` 新增 `isSpaRoute`、GitHub Pages 发布包多放一份 `site/index.html` 副本作 `404.html`(Pages 无重写规则,深链与刷新靠它接住)。
- **SPA 壳的路径基准**:`site/index.html` 顶部内联脚本从 `pathname` 里第一段 `/site/` 反推站点根,写 `<base>` 并暴露 `window.KOLE_SITE_BASE`;`site/app.js` 的 `SITE_BASE` 用同一条规则(`tools/verify-site-routing.mjs` 会静态卡这两处一致)。部署前缀因此可变(本地/nginx 是 `/site/`,Pages 是 `/<repo>/site/`),代价是**站点目录必须继续叫 `site`**。
- **资源改绝对地址写出**:`<link>` / `<script>` 不能再写静态相对路径 —— 预扫描器在脚本执行前就按深层 URL 预取,实测多出 2 个 404 + 2 条控制台报错,`data.js`(precompute 后 122 KB)还会被整份白取一次。改成先 `document.write('<base …>')` 再输出 `base + 'xxx'` 的绝对地址后,深链冷加载 **10 个请求 / 0 个 4xx / 0 条控制台报错**,且 `<script>` 仍是解析期同步按序执行,语义与静态标签一致。
- **静态入口页跟着改**:`build-site.ps1` 生成的 79 薄壳 + 316 平台壳,重定向目标改成 `../component/<slug>` 与 `../../component/<slug>/<platform>`(重跑 `npm run build:site`);`tests/_template.html` 的「← 文档站」链接改成 `../site/component/<slug>`(重跑 `run-tests.ps1`,79 页);`site/llms.txt` 的页面链接同步去掉 `#`。
- **验收**:新增 `tools/verify-site-routes.mjs`(浏览器级,24 条断言全过,已接入 `regression.yml`)—— 深链渲染、URL 无 `#`、`--kole-color-brand` 令牌生效、冷加载 0 控制台报错 / 0 个 4xx、旧 `#` 就地改写、顶栏/侧栏/搜索跳转不整页刷新、框架切换写进 URL、前进后退、深链刷新、薄壳重定向、缺失的带扩展名资源回真 404、「在线测试」这类相对链接仍解析到站点根、`file://` 降级(`pushState` 不可用时退回 hash 形态,实测仍能渲染);`verify-site-routing` 79 组件 / 316 平台壳 / 396 URL 全过(新增:两处 SITE_BASE 同规则、`<base>` 引导、nginx/dev-server/Pages 三处回落、薄壳无 `#` 残留);`smoke:site`、`verify:nav`、`verify:i18n` 16/16、`verify-dev-server` 20/20、`pack-deploy` 全过;回归 **100%(79/79 页、1017/1017 断言、N/A 34、0 超时)连跑 8 次一致**(同一状态另跑 54 次,见下条)。
- **发现(与本改动无关,已记为 ROADMAP S6-P32)**:跨批次整轮连跑 62 次中有 2 次非 100%,捕获到的那次是 `densityswitcher` 的 `matrix:contrast>=4.5` 读到了**颜色过渡中间态**(`button.kole-densw-opt "默认" 4.46:1 < 4.5:1`)。实测两端点都达标(生效后 5.85、未生效 7.00),过渡途中会穿过 4.5 以下(7 → 1.79 → 2.4 → 4.58 → 5.85);`tests/_runtime.js` 的就绪轮询只看 innerHTML 是否稳定,而过渡期间 innerHTML 不变,拦不住。单页压测 300 次命中 2 次。旁证它不来自本次改动:测试页只改了一行「← 文档站」链接的 `href`,断言引擎对该链接零引用(`grep -c 't-btn\|文档站\|t-nav'` = 0),断言全部跑在未改动的 `frameworks/<Prefix>.html` 帧内。
- **顺带修正**:`site/llms.txt`「文档站页面」列表里还挂着 `#/agents` —— 该页早已随 AI 消费模块删除(路由与 i18n 词条都不存在),已删掉这行;`AGENTS.md` 铁律 2 里 `data.js` 的 1058 KB / 942 KB 是旧值,按 2026-09-20 实测改为 1166 KB / 122 KB。
### Docs site · 代码与演示都整段展示(去掉两处内滚动)+ 演示帧暗色真正生效
- **代码不再截断**:`.demo-code pre` 原有 `max-height: 420px`,长源码要在约 21 行的窗口里滚(cascader 7742 字符)。去掉 max-height(Element 文档同样显式 `max-height:none`),整段随页面滚动;长行仍可横向滚动。实测 5 个组件代码块纵向滚动量全为 **0**。
- **演示整段展示**:`.demo-stage iframe` 固定 `height: 420px`,比它高的演示在帧内出滚动条(滚轮滚的是 iframe 而不是页面)。新增 `autoSizeFrame()`:帧 `load` 后按 `body.scrollHeight + margin`(`documentElement.scrollHeight` 更大时取后者,否则短内容收缩不下去)写实际高度,配 `ResizeObserver` 跟随帧内异步渲染(加载态 / 展开行),换页时 `releaseFrameFits()` 摘掉观察器与 resize 监听;420px 降级为 CSS 兜底。实测帧高贴内容:button **571** / cascader **394** / table **413** / watermark **330** / expandabletable **311**,帧内滚动量全为 **0**。
- **代价:演示帧沙箱放宽到 `allow-same-origin`**(`sandbox` 属性保留,继续挡表单提交 / 顶层跳转 / 弹窗)。理由:该帧加载的是一方文件 `frameworks/<组件>.html`,与文档站同信任级;**用户可编辑的代码只在「在线测试」页里跑,那边仍是严格沙箱**。而量不到 `contentDocument` 就做不了自适应高度。
- **顺带修掉 S5-P23**:正因为放宽了沙箱,`injectIframeTheme()` 不再于 `if (!d) return` 静默返回 —— 实测站点 `kole-mode=dark` 时帧内 `documentElement.classList.contains('kole-dark') === true`、帧 body 背景 `rgb(20,22,28)`。v1.1.1 声称的「演示 iframe 注入 kole-dark 同步反色」到现在才真正成立(在线测试页的预览帧保持浅色,是有意保留的差异)。
- **验收**:`verify:i18n` 16/16、`smoke:site` 全过、`verify-site-routing` 79 组件 / 316 薄壳 / 396 URL 全过、`pack-deploy` 断言全过;回归 **100%(79/79 页,1017/1017 断言,N/A 34,0 超时)连跑 3 次一致**;浏览器侧复测在线测试实时预览闭环(注入标记 → 帧内出现 → 复位消失)、首次展开填充(cascader 7742 / tree 5194 字符)、入口新标签与英文文案(Show code / Hide code / Playground →),0 报错。
### Docs site · 侧栏「组件总览」降级为独立页面索引(不再兼作分节父项)
- **现象**:进入任意组件详情页,侧栏同时高亮两项 —— 「组件总览」与当前组件。两者样式逐属性相同(`site/style.css` 的 `.side-link.active` 228 行 / `.side-item.active` 264 行),并列两块蓝底,读起来就是「选了两个」。
- **根因**:`site/app.js` 的侧栏高亮规则写作 `r === 'overview' || r === 'component'` —— 让「组件总览」兼作整个组件区的分节父项。该规则出自 v1.2.0 初版提交(`git log -S` 溯源),非后期回归。
- **改法**:「组件总览」只代表自己那一页(`active: r === 'overview'`),与「快速开始 / 设计规范 / 常见问题」三条同级。顶栏「组件」**保持**分节语义不变 —— 顶栏没有逐组件入口,去掉后组件页将无任何顶栏高亮。
- **语义层无变化**:`aria-current="page"` 本就只加在真正的组件项上(`renderSidebar` 里给当前 slug 那一条),屏幕阅读器读到的始终是正确的一项,本次只修视觉层的重复高亮。
- **实测**(Chromium,本地 dev-server,逐路由探针):组件页侧栏高亮数 **2 → 1**;`#/component/button|modal|table` 侧栏均为 `[组件名]` 且「组件总览」不再命中;`#/overview` 仍为 `[组件总览]`;`#/design`、`#/guide`、`#/faq` 各 1;`#/changelog` 为 0(无侧栏入口,维持原状)。
- **登记缺口**:`tests/` 与 `tools/` 中无任何断言涉及 `side-link` / `side-item` 的选中态,故此类回归在八次连跑中不可见 → 已追加为 ROADMAP `S5-P25`。
- **验收**:回归 100%(79/79 页,1017/1017 断言,N/A 34)连跑 8 次一致、0 超时。
### Docs site · FAQ 问题搜索对齐全局组件搜索(Ctrl+K)
- **起因**:页面描述写着「支持按组件浏览与关键词过滤」,实际只有「输入 → 隐藏不匹配项」一条路径:没有命中高亮、没有键盘路径、没有范围限定,也搜不到「按钮」这种组件名之下的全部问题。
- **对齐全局组件搜索**(直接复用弹窗里的 `hiMark()` / `fuzzySubseq()` 与 `.sc-chip` 样式,交互同源):
- **命中高亮**:问题正文命中用 `<mark>` 标出片段;命中来自组件名时标记落在分组标题上。
- **搜索范围扩到组件名 / slug**:输入「按钮」会保留整个按钮组(实测 3/3 条全在)并高亮标题;`qrcode` 这类 slug 同样可搜。
- **类别 chips 限定范围**:全部 + 六大类,样式与搜索弹窗一致;实测切到「导航」范围收窄到 35 条 / 10 个组件,切回「全部」完整还原 244 条。
- **键盘全路径**:`↑`/`↓` 在命中项间循环移动并平滑滚到视区中央、`Enter` 把焦点落到该问题、`Esc` 清空查询(空查询再按一次才退出焦点)、`/` 一键聚焦搜索框(框内常驻 `kbd` 提示,输入后让位给自绘清空按钮)。
- **模糊回退与空态**:无精确命中且查询 ≥2 字符时回退子序列匹配,结果行标注「(模糊匹配)」;彻底无命中时显示空态并隐藏列表。
- **状态记忆**:查询与类别范围存 `state.faqQ` / `state.faqCat`,离开页面再回来仍在(实测往返后仍 11 条命中)。
- **实现细节**:`/` 快捷键一次性绑定(`renderFaq` 每次进入该页都会重跑,直接在内部注册 `document` 监听会不断累积);隐藏原生 `::-webkit-search-cancel-button` 改用自绘清空按钮,避免与 `kbd` 提示重叠;`↑↓` 移动用共享的 `visible[]` + `.hover` 类,与弹窗同款焦点反馈。
- **i18n**:新增 6 条(`清空搜索` / `按 / 聚焦搜索` / `无匹配问题` / `无匹配问题(已尝试模糊匹配)` / `(模糊匹配)` / `↑↓ 定位 · Enter 跳转 · Esc 清空`),英文侧沿用「空格烘进词条值」的既有约定(如 ` 条,`),`verify:i18n` 261 键全覆盖。
- **已知数据限制**:FAQ 条目正文取自契约原文(中文,无英文译文),故英文界面下英文关键词只能命中组件名 / slug(实测 `button` 命中按钮组并高亮标题;中文关键词「宽度」在英文界面照样可用)。属数据层现状,本轮未改。
- **验收**:33 项浏览器断言(高亮 / 组件名与 slug / chips 范围 / 方向键移动与滚动 / Enter 焦点 / Esc 清空 / 模糊回退 / 空态 / `/` 快捷键 / 清空按钮 / 查询持久化 / 中英双语 6 条文案);`smoke:site` 30/30、`verify:i18n` 16/16、上一轮的 30 项站点断言(语言下拉 / 首页核心区 / FAQ 搜索框)全部仍过、`verify:theme` / `verify-dark` / `verify-site-routing` 全过、零运行时依赖不变;回归 **100%(79/79 页,1017/1017 断言,N/A 34)连跑 8 次一致、0 超时**。
### Docs site · 试玩改为独立「在线测试」页 + 代码区控制条对齐 Element
- **起因**:组件详情页的「▶ 在线试玩」是内联 textarea —— 一枚胶囊按钮悬在演示区与代码区之间的白缝里,与页头「测试页 →」并排看像两个近义入口,归属和用途都含糊。
- **新增「在线测试」独立页**(`site/playground.html` + `site/playground.js`):控制条右侧常显「在线测试 →」,新标签打开 `playground.html?slug=<slug>` —— 左侧编辑 H5 源码、右侧 iframe `srcdoc` 实时预览(300ms 防抖),79 组件可切换、复位 / 复制源码 / ↗ 演示原页 / ← 文档站,Tab 缩进两格、Ctrl+S 立即同步,状态行回显「已同步 HH:MM:SS(已修改)· 已复位」。数据**单请求**取自同目录 `data.json`(`sources.html` 多数内联,缺省回落 `files.html`),零运行时依赖;暗色与文档站共用同一个 `localStorage['kole-mode']`。
- **控制条改为 Element 同款**:44px、居中「▼ 显示代码 / ▲ 隐藏代码」、**文字悬停或键盘聚焦才浮现**(`.lbl` opacity 0→1,`:focus-visible` 一并覆盖)、悬停底色 `table-header-bg` + 品牌色。整条不再可点:按钮管折叠、链接管跳转(此前整条 `div` 挂点击且无键盘路径)。
- **⚠️ 反转既有决定:代码区默认收起**(对齐 Element)。此前记过「默认展开:省掉一次点击」,本次按「以下拉条参考 Element」的要求改为收起 —— 页面因此可扫读,要读源码点一下,要动手改则走在线测试页。**已专门验证没踩回历史坑**:代码区改为首次展开才构建,实测全新加载 → 首次展开即有内容(cascader **7713** / tree **5159** 字符,与当年验收数字一致),CSS tab 正常(button 2122 / cascader 3552 / tree 1067)。改回默认展开只需 `var open = false` → `true`。
- **顺带修正「测试页 →」掉出标签行**:该链接此前 append 到 `.detail-head` 而非 `.chips`,脱离 flex 行后只剩 `.chip` 浅色底,在页头与「何时使用」之间单独占一行;同类的「规格文件」「契约 JSON ✓」都在行内,已改挂到 `.chips` 末尾。
- **删除内联试玩**:`play-btn` 系按钮、`.demo-playbar`、`.pg-textarea`、`currentHtml()` 轮询一并移除;`i18n.js` 清掉 6 条只服务于它的键(`✎ 在线试玩` / `● 试玩中` / `退出试玩` / `复位` / `展开代码` / `在线编辑 H5 演示源码…`)与此前已无人引用的 `试玩` / `仅 H5 形态支持在线试玩`,新增「在线测试 / 显示代码 / 隐藏代码」。`.play-btn` 样式保留(`app.js` 错误态的「重试」按钮仍在用)。
- **实测**(Chromium 1440×900,本地 dev-server):预览帧内 `document.querySelectorAll('.btn').length = 16`、首按钮背景 `rgb(47,84,235)`;源码尾部注入 `<div id="probe-marker">` → 帧内出现、复位后消失;切到 `table` 后 URL / 标题 / 编辑器(6587 字符)/ 预览同步;控制条 44px、`aria-expanded` false→true、`aria-controls=demo-code-button`;`kole-mode=dark` 下页面与代码面同为 `rgb(20,22,28)`;两页 **0 控制台报错**。
- **验收**:`verify:i18n` 16/16(`T()` 覆盖 258 键,独立复算 0 条未收录)、`smoke:site` 全过、`verify-site-routing` 79 组件 / 316 薄壳 / 396 URL 全过;回归 **100%(79/79 页,1017/1017 断言,N/A 34,0 超时)连跑 3 次一致**。
- **登记缺陷(同日已修)**:`sandbox="allow-scripts"` 的演示帧是不透明源,父页 `iframe.contentDocument` 实测为 `null` → `app.js` 的 `injectIframeTheme()` 恒在 `if (!d) return` 静默返回,暗色模式下演示帧当时不会反色(ROADMAP Q6 原记为「设计取向待定」,实为机制从未生效)。已登记 **S5-P23**,并在同日的下一条改动(放宽演示帧沙箱以做自适应高度)中一并修复。
### Docs site · 删除 AI 消费模块 + 首页核心组件改用总览同款卡片 + 修 FAQ 搜索框
- **删除「AI 消费」(For Agents)模块**:顶栏导航项、`#/agents` 路由、`renderAgents` 渲染(96 行)与顶栏 `NAV_KEY` 映射一并删除,并清掉随页面作废的 42 条 i18n 词条(先按「`T('…')` 字面量是否仍被引用」逐条判定,避免误删共享键如 `' 条,'`)。`#/agents` 现在回落到首页(实测),`site/llms.txt` 与 `site/data.json` 保留 —— 机器可读入口在文件层,不在文档站页面层。README 两处引用同步改写;AGENTS §5 的「For Agents 页承诺」改为「对外承诺(该页已移除)」,**承诺本身不变**。
- **首页「核心组件」改为与组件总览同一套展示**:抽出 `buildCompCard()` 与 `observeThumbs()` 供两处共用,首页核心区改用 `.ov-grid` / `.ov-card`(150px 在线预览 + 中文名 / 英文名),预览 iframe 同样按「进入视口 400px 内才挂载」懒加载。实测卡片宽 283px 与组件总览逐像素一致、缩略图高 150px、点击进详情正常。
- **顺带修掉一个内容缺陷**:该区块原先按 `c.hasContract` 过滤,而契约做到全量 79/79 之后这个条件恒为真 —— 标题写着「核心组件」,实际列了全部 79 个。改用 `tier === 'core'`(与组件详情页的「核心规范组件 / 扩展组件」同一口径),实测列出 10 个 = `data.meta.core`。
- **修复常见问题页搜索框样式异常**:输入框挂的 class 是 `.search-input`,而样式表里只有全局搜索弹窗的 `#search-input`(id 选择器),**该 class 没有任何规则** —— 页面渲染出的是浏览器默认输入框。新增 `.faq-search` / `.faq-search-input` 令牌化样式(36px、左侧放大镜、聚焦环、`::placeholder`、计数行间距),暗色模式实测对比度 10.34;关键词过滤与计数(244 条 / 79 组件)行为不变。
- **回归**:100%(79/79 页,1017/1017 断言,N/A 34)。同日 16 次连跑出现 2 次既有签名 `1016/1017`(78/79 页全通过,无失败页面落盘)——与 ROADMAP S5-P17 记录同因;另写逐页归因诊断(读 `#result-list .t-row` 的 fail 项)连跑 14 轮 **0 复现、无页面 `fail>0`**,证据已补进 S5-P17。该偶发与本改动无关:测试页只加载 `site/style.css`,而本次改动涉及的选择器(`topnav` / `lang-*` / `faq-search` / `ov-card` / `home-core-grid` / `core-card`)在 `tests/_template.html` 中出现次数均为 0。
- **验收**:`smoke:site` 30/30、`verify:i18n` 16/16、`verify:theme` / `verify-dark` / `verify-site-routing` 全过、零运行时依赖不变;另 30 项浏览器断言覆盖三项改动(导航 6 项且无 AI 消费入口、`#/agents` 回落首页、首页核心 10 张卡与总览同宽同高、预览 iframe 真实加载、FAQ 过滤 / 计数 / 暗色对比度)。
### Docs site · 快速开始新增「安装(npm 私有源)」+ 修复更新日志页首次进入卡加载
- **快速开始页(`#/guide`)新增第一节「安装(npm 私有源)」**:项目 `.npmrc` 一行配置(`@root:registry=…`)→ `npm install @root/ui` → 三端引入示例(Vue 3 / React / Vue 2),并显著提示「令牌必须先引」这一最常见接入坑(78/79 组件样式引用 `var(--kole-*)`)。同时把原「引入设计令牌」与「按端使用组件」两节改写为「不用 npm 时」的对照路径;页内目录与中英双语同步(新增 9 条英文词条,`verify-i18n` 256 键全覆盖)。
- **修复既有缺陷**:更新日志页**首次进入永远停在「加载中…」**。`renderChangelog` 在懒加载分支里只赋值 `window.__koleChangelogReady`,但全站没有任何地方调用它——只有离开再回来(`changelogCache` 已就绪)才会渲染。改为走 `listenChangelog()` 注册回调(该函数已正确处理「已缓存则立即回调 / 未缓存则入队」)。线上实测:修复前首次进入 208 字符且一直「加载中」,修复后 14756 字符 / 10 个版本。
- 验证:独立浏览器实例 11 项检查全过(首次进入渲染完成、npm 节排第一、7 个代码块、英文模式生效、7 条主路由 0 报错);回归 100%(79/79 页,1017/1017 断言,N/A 34);文档站冒烟通过。
### Docs site · 顶栏窄屏折叠为汉堡 + 导航抽屉(S5-P20)
- **问题(实测)**:顶栏没有任何窄屏规则。中文 1280px 起 7 条链接全部折行(`height:100%` + 无 `white-space: nowrap`,中文按字换行成竖排)、1100px 起文字顶出 60px 顶栏、960px 起顶栏溢出、768px 起最右链接不可达、480px 起 7 条**全部在视口外且无滚动条**(`docOverflow: 0`,即不可达而非可滚动);英文更早——1366px 起溢出,1200px 起最右链接已不可达。附带缺陷:**英文在 1600px 就折成 2 行**,因为 `.topbar-inner` 上限 1440px,与视口多宽无关。
- **修复**:`≤1366px` 折叠为汉堡 + 右侧抽屉(`.topnav` 就地变成抽屉,不复制 DOM,避免 i18n 文案与 active 态两处维护);基础层加 `white-space: nowrap`;收紧规则(logo 副标、顶栏间距、链接近距、搜索框上限)**改为无条件而非挂在断点上**——英文 7 条链接 + 可读搜索框在 1440px 上限内始终排不下,挂断点解决不了。60px 顶栏高度**未改**,故 5 处硬编码引用(`.body-wrap` / `.sidebar` / `.toc` / `html` 的 `scroll-padding-top`)连带影响为零。
- **验收中修掉两个只有交互测试能抓到的缺陷**:① **抽屉可见但点不到**——`.topbar` 的 `z-index:100` 形成层叠上下文,抽屉的 `120` 只在顶栏内部生效,整条顶栏被外部遮罩 `110` 压住;矩形与可见性断言**全部通过**,只有真实点击暴露 `intercepts pointer events`。修法:遮罩移入 `.topbar`,与抽屉同处一个上下文(仍低于 `.fab-stack` 150 / `.modal-mask` 200)。② **焦点进不了抽屉**——`visibility` 参与 `transition`,类切换后首帧仍是 `hidden`,`focus()` 静默失败(键盘用户完全进不去);改为「打开时 `visibility 0s`、关闭时延迟 0.22s」,并把焦点回收从 `contains()` 判断改成显式标志(元素被隐藏时 `activeElement` 会变成 `body`,`contains()` 判不出来)。
- **新增守门人**:`tools/verify-nav-responsive.mjs` + `npm run verify:nav`(18 宽度 × 中英双语 + 抽屉交互,346 断言,约 35 秒),已接入 `regression.yml`(步骤 15),并补装 `fonts-noto-cjk`——宽度断言依赖字体度量,缺 CJK 字体时中文退化成豆腐块/拉丁回退会导致假失败。脚本每次运行打印**字体度量基准**与**字体容忍度**(在最窄内联宽度 1367px 二分测出文字宽度还能再增多少仍不折行:**实测 17.5%**,该处余量 94px;同次实测现实字体跨度 Arial/Segoe UI `0.918×`、DejaVu/Liberation `1.000×`、Verdana `1.058×`,最宽者也远在容忍度内 —— 故 CI(ubuntu)与开发机(Windows)的字体差异不会让断言假失败;容忍度低于 5% 时额外打印告警)。服务未起时报 exit 2(同 `run-regression.mjs` 口径),不退化成一串断言失败。断言按 DOM 里的链接条数取值(不写死 7),故后续增删导航项不会误报。
- **部署改为被测试 gate**:`deploy-pages.yml` 的触发器从 `push` 换成 `workflow_run`(等 main 上的 `Regression` 完成)并加 `if: conclusion == 'success'` 守卫 —— 测试不过就不部署。用 `workflow_run` 而非把 Regression 拆成可复用工作流,是为了不让同一套测试在每次推 main 时跑两遍;`workflow_dispatch` 保留为有意的手动逃生口。checkout 显式取 `workflow_run.head_sha`(`workflow_run` 的 `github.sha` 指向默认分支头,不取会部署错提交)。
- **顺带修掉一处证据链缺口**:首页通过率卡片读 `tests/report.json`,而部署原先 `cp` 的是**入库的那份**——等于页面显示「提交里的数字」而不是「刚验过的数字」。现改为下载本次 Regression 的 `regression-report` 产物并用其中的 `report.json`(新增 `actions: read` 权限),取不到才回退到提交版并发 `::warning::`,回退路径保证部署不会因产物过期而变脆。
- **清理一条死规则**:≤900px 里「链接近距收到 8px」的补丁在抽屉方案下已无任何生效宽度(实测 900px 下 `.topnav a` 计算值为 `12px 20px`),已移除并留注释说明。
- **验收**:`npm run verify:nav` → `OK — all checks passed`(346 断言 / 0 FAIL);改动前后逐宽度对比见 ROADMAP「S5-P20 实测数据」;回归 100%(79/79 页,1017/1017 断言,N/A 34)**连跑 8 次一致、0 超时**;`smoke:site` 全通过;零运行时依赖不变。
### Design system · 组件族参数化:79 个并列组件 → 48 个概念组件(S6-P21)
- **问题**:79 个并列组件里有 44 个是同源变体,却各自手写了一遍。导航最典型——`topmenu` / `sidemenu` / `mixednavigation` 三份实现,差异只是"一级菜单横排还是竖排";规范契约自己就写着从属关系:`alertmodal` 与 `confirmmodal` 的 `doNotInvent` 原文是「弹窗尺寸档位(见 Modal 契约)」,`steps` 与 `steplist` 的 `semanticTypeCandidates` 完全相同(均为 `steps|wizard`)。并列表达让消费方看到 6 个表格组件,而它们实际是「一个 Table + 一个 feature 参数」。
- **新增族层(纯追加,不破坏任何既有承诺)**:新增 `.design_library/kole-ui/families.json`;`data.json` 增顶层 `families` 与每组件 `family` / `familyRole` / `familyParams`;`site/details/<slug>.json` 同步三元组。**13 族 / 44 成员 / 35 个独立组件;`data.json` 仍完整输出 79 个组件,slug 集合逐一不变**(铁律 5)。
- **归族判据可核对,不按名字猜**:每族在 `tools/lib/family-model.mjs` 里必写 `mergeBasis`,依据是契约中四类可核对字段——`semanticTypeCandidates` 重叠、`anatomy` 为同一骨架的子集、变体维度同构、`doNotInvent` 的显式从属声明。
- **实现层合并(导航族端到端切片)**:`tools/gen-family-impl.mjs` 从 `tools/lib/family-impl/nav-menu/` 的 5 端模板生成 `TopMenu` / `SideMenu` / `MixedNavigation` 共 15 个文件,参数为 `direction=top|side|mixed`。三份 CSS 的 **md5 完全相同**——一份样式表服务三个组件;改模板 + 重跑即三端同步,不再存在"改了 topmenu 忘了 sidemenu"。
- **为什么是「生成」而不是「抽共享模块」**:`tools/pack-deploy.mjs`(薄壳 79 / 实现 395)、`tools/precompute.mjs`(79 / 395)、`tools/verify-cross-platform.mjs`、`tools/verify-package-import.mjs` 四处硬断言文件数与导出数。抽跨文件 import 会同时打破它们,并失去"单文件可拷贝"。生成把重复消除在源头,而不改动文件图。
- **生成器护栏**:目标文件有未提交改动、且不含生成物标记时拒绝覆盖(除非 `--force`)——防止把工作区里没提交的手工调整冲掉。
- **跨端口径(两个数必须分开说,免得把自己的改动说大成整体改善)**:已提交 HEAD 为 `identical 2 / differing 77 / high 44`;本任务改动前的工作区已是 `79/79`(既有未提交改动先清了跨端漂移);本任务改造后仍为 **`identical 79 / differing 0`,与工作区持平**。中途曾掉到 76/79:族模板最初把方向写成对象字面量 `{ direction: 'side' }`,Vue 端 class 提取器会把其中的字符串值收作变体记号、H5/JSX 端不会。探针实验确认成因后改用独立常量 `FAMILY_DIRECTION` 承载方向,四端回到 79/79——是改代码对齐既有约定,未放宽校验脚本。提取器本身的不对称仍未修,已登记 ROADMAP S6-P22。
- **顺带修掉一处既有 RTL 不一致**:`MixedNavigation.css` 原文件同时写 `border-inline-start` 与 `border-left-color`(逻辑属性与物理属性混用,RTL 下两侧指示条表现不一致);族模板统一为逻辑属性。
- **验收**:族层 `node tools/verify-families.mjs` → `OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变)`,退出码 0;`node tools/gen-family-impl.mjs --only=nav-menu` 幂等(重跑 0 写入);回归 100%(79/79 页,1017/1017 断言,N/A 34)**连跑 8 次一致、0 超时**;零运行时依赖不变(`dependencies` 为空)。
### Package · 发布到私有 npm 源(可直接 npm install)
- **发布位置**:自建 Gitea 的 npm registry `https://gitea.mymoyu.top/api/packages/root/npm/`(Gitea 27.3.1),**匿名可读**——实测无凭据 `npm view` / `npm install` 均成功。
- **已发布 3 个可互换的包名**(内容一致:三端入口各 79 组件 + 令牌 + 组件样式):
| 包名 | 用途 |
|---|---|
| `@root/ui` | **推荐**:scoped 名,项目 `.npmrc` 写一行 `@root:registry=…` 即可,其余依赖仍走公共源 |
| `chunyu-ui` | 非 scoped 别名,需 `--registry=` 显式指定源 |
| `aurora-admin-design` | 仓库原名,同上 |
> ⚠️ 本表是 **v1.4.x 当时的发布事实**,其中的 `aurora-admin-design` 与导出名 `AaButton` / `AaTag` 是当时线上真实存在的东西,**不改写成新名**(改了就不是事实了)。改名后的对照与现状见 [2.0.0] 段的「发布现状」。
- **踩到的坑(已写进 README)**:非 scoped 包**不能**用 `@包名:registry=` 写法——该语法只对 scoped 名生效,实测报 404。故推荐 scoped 名 + 一行 `.npmrc`。
- **端到端验证**:建 Vite + Vue 3 工程,`.npmrc` 一行配置 + `npm install @root/ui`(公共依赖同时走公共源)→ `vite build` 通过(169 模块)→ 浏览器实测:`AaButton` 渲染、品牌色 `rgb(47, 84, 235)` 生效、`AaTag` 正常、点击计数交互正常、0 JS 报错。
- **未发布到公共 npm**:本机无 npm 凭据(`npm whoami` → `ENEEDAUTH`,无 `~/.npmrc`、无 token 环境变量),`npm publish` 到 registry.npmjs.org 被拒。公共 npm 上 `chunyu-ui` / `aurora-admin-design` 均未被占用(可用),是否发布待定。
- README 增补「从私有 npm 源安装」段(含正确的 `.npmrc` 写法与三端 import 示例)。
### Docs site · 导航语言选择改为下拉(S5-P19)
- **问题**:顶栏语言控件是「中 / EN」双段按钮——11px 圆角、28px 高、靠 2px 字号差和品牌色表示当前语言。两个语言标签同时高亮显示,当前语言靠粗细区分,一是看不出「点了会怎样」(无展开暗示),二是与旁边 34px 的框架选择器、搜索框不同高,三是纯鼠标控件:没有 `aria-haspopup`、没有键盘路径。
- **改为下拉选择**:触发器 = 地球图标 + 当前语言名(简体中文 / English)+ 箭头,34px 高与相邻控件齐平,展开时箭头旋转 180°、边框转品牌色并带聚焦环;菜单 186px 卡片,含「界面语言」标题分隔线、语言名 + `ZH`/`EN` 角标 + 选中对勾,8px 顶栏令牌阴影。
- **交互与无障碍**:`role="listbox"` + `aria-selected` + `aria-expanded`;点击展开/收起、点击外部关闭、`Esc` 关闭并回焦触发器、`↑`/`↓` 循环移动、`Home`/`End` 跳首尾、`Enter`/`Space` 选中、`Tab` 关闭且不抢焦点;选中后触发器重新获得焦点。语言名始终以该语言自身书写(简体中文 / English),不随界面语言翻译。
- **令牌与暗色**:颜色/圆角/阴影全部走 `--kole-*` 令牌,未新增任何硬编码色值;暗色模式实测菜单底色 `rgb(28,31,38)`,选中项对比度 4.57、菜单标题 6.50、触发器 4.57(均 ≥ 4.5)。`prefers-reduced-motion: reduce` 下关闭菜单动画与箭头过渡。
- **窄屏**:≤1100px 收起语言名(触发器 56px,与旧控件同宽)。实测改动前顶栏在 1024px 溢出 13px、900px 溢出 9px(旧控件同样外露),现 ≥860px 全部为 0;≤820px 的溢出与语言控件无关,已登记为 ROADMAP S5-P20。
- **i18n**:新增 `界面语言` / `选择语言` 两条,移除随之作废的 `切换到简体中文` / `切换到英语`(原按钮的 aria-label),一进一出净增 0 条。
- **验收适配**(验收命令本身随控件形态更新,判据强度不变):`tools/verify-i18n.mjs` 的两条静态检查改指新 id/绑定,并新增「旧控件不得残留」检查;`tools/run-site-smoke.mjs` 的点击路径改为「开菜单 → 选项」,并补 8 条下拉行为断言(`aria-expanded`、Esc、方向键、焦点回位、选中态、触发器文案)。
- 回归 100%(79/79 页,1017/1017 断言,N/A 34),连跑 8 次一致;`smoke:site` 30/30;`verify:i18n` 16/16;`verify:theme`、`verify-dark`、`verify-site-routing` 全过;零运行时依赖不变。
### Package · 三端可 import(组件库真正可用)
- **问题**:`dist/` 只发 CSS 与演示 HTML,`package.json` 的 `main` 甚至指向一个 CSS 文件,**没有任何可 import 的组件**——`import { KoleButton } from 'kole-ui/vue3'` 这类标准用法不成立。S1-P2 的任务范围写的是「能拿到令牌 + 组件样式」,因此这是**范围缺口**而非实现错误(已按 AGENTS 第五节登记为 ROADMAP S5-P18)。
- **新增三端聚合入口**:`dist/react/index.js`、`dist/vue3/index.js`、`dist/vue2/index.js`(各导出 79 个 `Kole*` 组件)+ 单组件源码。组件以 `.vue` / `.jsx` 源码发布,由宿主构建链编译(无额外编译产物与源码不同步的风险,且保持零构建依赖)。
- **`package.json`**:补 `exports` 映射(`.` / `./tokens.css` / `./components/*` / `./react` / `./vue3` / `./vue2` / `./manifest.json`)与 `peerDependencies`(`react >=17`、`vue >=2.6`,均为 optional)。
- **修掉两个「组件无法编译」的真实缺陷**(此前从未被发现,因为从未真编译过):
- `RangeQuickPicker` 的 `presetRange` 给 `const start` / `const end` 重新赋值 → **React 与 Vue 3 两端都无法编译**;已改为 `let` 并与 Vue 2 参照实现对齐(`start` 改为 `new Date(...)` 而非 `setMonth/setDate` 就地修改,行为一致)。
- `CodeInput.vue3.vue` 内 `function emit()` 遮蔽了 `const emit = defineEmits(...)` → 重复声明,编译失败;已重命名为 `emitChange()`。
- **样式随包发布**:56 个组件的 Vue 两端用 `<style src="./<Prefix>.css">` 引用外部样式、79 个 JSX 用 `import './<Prefix>.css'`。打包时把同名 CSS 一并放入 `dist/react|vue3|vue2/`,否则会出现「import 成功但样式全丢」。断链检查已纳入验证脚本。
- **新增 `tools/verify-package-import.mjs`**:结构级(入口/断链/exports/零依赖,15 项,零依赖可跑)+ 编译级(esbuild + `@vue/compiler-sfc` 真编译 79 个 SFC、vue/react SSR 真渲染 `KoleButton` 断言 DOM,5 项)。编译级依赖可用 `KOLE_VERIFY_DEPS_DIR` 指向隔离目录,避免污染仓库 `node_modules`。
- **端到端验证(真实工程,非模拟)**:分别建 Vite + Vue 3 与 Vite + React 工程,以 `file:` 依赖真实 `npm install` 本包,只写 README 里的那几行 `import` → `vite build` 通过(Vue 170 模块 / React 189 模块),浏览器实测:组件真渲染、品牌色 `rgb(47, 84, 235)` 生效(证明令牌与样式链正确)、`loading` 态 `disabled` 与 spinner 正确、点击交互正常、0 JS 报错。
- 回归 100%(79/79 页,1017/1017 断言,N/A 34);S1-P2 原始验收命令(tokens/aggregate/79+index/zero-dep/css usable/pack contents)全部仍通过。
### Docs site · 组件详情页代码块默认展开(含修复异步填充漏填)
- 组件详情页源码区改为**默认展开**:`demo-strip` 初始态由折叠改为展开,进页面即可看到当前框架源码,省掉一次点击;展开条箭头与提示文案随状态同步(▲ 收起代码 / ▼ 展开代码)。
- **修复既有缺陷(默认展开后被放大)**:源码异步加载后的填充回调**签名错位**——`listenSrc(slug + '/' + kind, fillCode)` 直接传函数,而 `notifySrc` 的回调签名是 `(text, error)`,于是源码文本被当成 `kind` 与 `'html'` 比对,永远不相等,**首次展开的代码区一片空白**(实测全新加载展开后字符数 0,离开组件再回来才有内容)。已改为闭包绑定 `kind`;并新增 `codeWrap.__fillCurrent()`,在代码区插入 DOM 后补填一次(命中内存缓存时源码是同步取得的,插入前填充会被 `isConnected` 守卫跳过)。
- 浏览器实测(本地 dev-server):首次进详情页即展开且有内容(cascader 7713 字符)、切到未访问组件自动填充(tree 5159 字符)、切 CSS tab 正常(1067 字符)、折叠再展开内容保留、0 JS 报错。
- 回归 100%(79/79 页,1017/1017 断言,N/A 34),文档站冒烟 ×2 全过。注:当日连跑 27 次中 2 次出现 1016/1017 的偶发失败,`tests/*.html` 不加载 `site/app.js`(grep 计数 0),与本改动无关;因 `tests/report.json` 不记录是哪条断言失败,已立任务包 S5-P17 追踪可追溯性。
- **顺带修掉「部署了但浏览器还在跑旧版」**:`nginx.conf` 之前没有任何 `Cache-Control`,浏览器对 `app.js` 走启发式缓存——实测重新部署后页面仍执行旧 `app.js`(同一页面内 `fetch` 能取到新版、`<script src="app.js">` 却用缓存,`duration=0`)。已在 server 级统一加 `add_header Cache-Control "no-cache" always;`:每次带 `If-None-Match` 重新校验,未变更返回 304(实测 app.js 带 ETag 请求 → 304),开销极小但部署立即生效。
### Security · 生产部署链审计修复(2026-09-19)
- **规范原文泄漏(根因)**:`.dockerignore` 里 `*.md` / `*.ps1` / `*.yml` / `*.png` 等 glob 在 Docker 下**只匹配构建上下文根目录**,嵌套路径全部失效(真实构建实证:`sub/nested.md` 仍进镜像)。改为 `**/` 前缀并补齐嵌套 glob;`.design_library/kole-ui/{specs,agent-reports,preview,ui_kits}` 与嵌套 `*.md` 实测不再进入镜像(修复前线上 `GET /.design_library/kole-ui/specs/组件1.txt` → 200 / 8623B)。
- **`nginx.conf` 加固**:补 `absolute_redirect off;`(根路径 302 曾丢失宿主端口 `:3311` 落到无服务的 :80)、`server_tokens off;`、与 `site/dev-server.js` 逐字一致的 CSP 及 `X-Content-Type-Options` / `Referrer-Policy` / `Permissions-Policy` / `X-Frame-Options`;`/healthz` 改用 `default_type` 修掉重复 `Content-Type`。
- **`Dockerfile`**:补 `COPY sitemap.xml`(修复前生产 `/sitemap.xml` → 404)。
- **`site/dev-server.js`**:修复单个 `GET /site/%00` 触发 `ERR_INVALID_ARG_VALUE` 未捕获异常打挂进程的问题——新增编码形态与解码后 NUL 双重拦截、`fs.readFile` try/catch;白名单、安全头、302/404 行为保持(92 条合法路径 status / content-type / 字节级一致)。新增 `KOLE_PORT` 开关(默认仍 3311)供验证脚本隔离端口。
- **消除动态求值 sink**:`site/app.js` 与 `tools/precompute.mjs` 原先把 `defineEmits([...])` 字面量交给函数构造器求值(实测 `defineEmits([1,globalThis.__pwned='x'])` 会真的执行;构建期同源 → 可打穿 CI)。改为只解析字符串字面量的 `parseStringLiteralArray`(构建期实现独立成 `tools/lib/parse-string-literal-array.mjs`),非字面量元素整体拒绝返回 null。等价性:158 个 `frameworks/*.vue` 双实现抽取结果不一致 0(54 处字面量 / 96 条事件名)。
- 新增验证脚本 `tools/verify-dev-server.mjs`(20 checks,含 3 条修复前必失败的反例断言)与 `tools/verify-emits-parse.mjs`(17 checks,含构建期副本漂移门)。
- ⚠️ **规划修正**:`PLAN.md` §8「不改 `site/dev-server.js`(已工作良好)」的立论已被实测推翻,按 AGENTS.md 第五节「以实测为准」处理并在此标注。
- 回归:100%(79/79 页,1017/1017 断言,N/A 34),连跑 9 次一致,0 超时;主 agent 另独立复跑 1 次亦 100%。
### Deploy · 线上部署(2026-09-19)
- 新增 `tools/pack-deploy.mjs`:以 `.dockerignore` 为**唯一真源**打包部署目录(实现 Docker 的匹配语义,含「`*.md` 只匹配根目录」这一实测结论),带硬断言(规范原文/agent-reports/preview/ui_kits 绝不出现;站点运行必需文件必须出现;薄壳 79、实现文件 395)。部署配置文件(`docker-compose.yml` 等)始终保留——`.dockerignore` 只管镜像构建上下文。
- 按 AGENTS §九 流程首次实际部署到 `192.168.5.7`:备份 → 解到暂存目录核对 → 替换 → `docker compose build && up -d`(回滚目录 `/opt/kole-ui.pre-audit-20260919-0609`)。
- 部署后验收:`specs/组件1.txt`、`agent-reports/*`、嵌套 `SKILL.md`/`README.md` → **404**;`sitemap.xml` → 200;`tests/report.json` → 200(首页通过率角标恢复数据源);根路径 302 → `Location: /site/`(相对,不再丢端口);`Server: nginx` 无版本号;CSP 等 5 条安全头齐备且 `/healthz` 只 1 条 `Content-Type`;**396/396 条 sitemap URL 全部 200**;容器 `healthy`。
- 顺带消除历史漂移:线上组件文件由 09-06 快照更新为当前工作树(此前后者与线上有 288 个文件不一致)。
- 决策:**暂不对外公用** —— 保持 `127.0.0.1:3311` 回环访问(需 SSH 隧道),不加反代/域名/认证;容器运行期加固与对外承诺口径统一另立任务包(ROADMAP S5-P14/P15/P16)。
### Theme modes · 日间 / 夜间 / 自动
- 文档站主题选择扩展为 `light` / `dark` / `auto` 三态;自动模式跟随 `prefers-color-scheme` 并监听系统主题变化。
- 新增可访问的主题模式菜单、跨标签页 `kole-mode` 同步和 iframe 主题同步。
- ThemeSwitcher H5 / React / Vue 2 / Vue 3 统一 `auto` 模式枚举与 ARIA 语义。
### Fixed — 文档站中英切换完整性
- 修复语言切换后相关组件、主题面板、搜索分类与详情目录仍显示初始语言的问题。
- 补齐加载态、重试、契约详情等动态文案的英文词典覆盖,并为顶栏切换器补充无障碍名称与状态。
- 新增 `tools/verify-i18n.mjs` 静态校验与 `tools/run-site-smoke.mjs` 浏览器冒烟测试。
### S3-P9 · 契约缺口解释层
- 为 Card 契约的 `doNotInvent` / `unknowns` 增加结构化解释:分别说明设计边界、开放问题、出现原因与待确认决策。
- 组件详情页明确区分“不要自行发明(设计边界)”与“规范未明示(待确认)”,不把现有实现值冒充正式规范。
- 保持 hover 行为与卡片网格间距待定,未擅自写入 16px/24px 或固定状态规则。
### S2-P7 · 行为断言(6 试点全绿)
- 新增 `tests/_behaviors.js`:8 动词(click-toggles-class/click-adds-node/click-removes-node/click-sets-attr/input-clears/input-filters/keyboard-activates/tab-switches)+ `@input` 相对定位 + `@doc` 全文档查询 + 同步 pump + page-load 幂等缓存;`_runtime.js` 聚合 + 重载清缓存;`_template.html` 引入;6 演示页 `data-behavior` 标注;79 测试页重生成。
- 全量回归:100%(79/79 页,1009/1009 断言,N/A 35)。关键修复:runner 兜底重跑导致行为断言第二轮误报,加缓存解决。
### S3-P8 · RTL 支持(28 文件迁移)
- 新增 `tools/migrate-rtl.mjs`(白名单 dry-run/`--write` 双模式):margin/padding/border-inline + text-align start/end;剩余物理属性 0(排除例外);逻辑属性文件 19;三代表页 RTL 零溢出零错误。
- 人工例外 14 处:`margin-left:auto`(3)、拼接边框(5)、`left:0+right:0` 并存(4)——不机械替换。
### S2-P9 · FAQ 页(details 懒加载版)
- 新增 `#/faq` 页:自动聚合 79 组件契约的 unknowns(139)+ doNotInvent(101),共 240 条;按组件分组 + 关键词搜索过滤 + 命中统计;顶栏/侧边栏双入口;9 条 i18n 英文。
- 浏览器实测:groups 80/items 240,搜索“宽度”命中 11 条/11 组,清空恢复 240,0 JS 错误。
- 规划偏差:契约已在 P4 阶段 D 移出 `data.js`,FAQ 改走 `details/*.json` 批量懒加载(每批 10 个)而非直接消费 `data.js`。
### S4-P12 · 版本发布流程
- 新增 `tools/release.mjs`:检查模式(package.json vs CHANGELOG 版本一致性 + Unreleased 提醒)与 `--bump major|minor|patch --date` 提升模式;只做本地准备,tag/push 由人执行。
- `CONTRIBUTING.md` 新增发布流程 5 步 + 版本号规则(major/minor/patch 判定)。
### S2-P6 · 跨端一致性自动验证(方案 B:静态结构比对)
- 新增 `tools/verify-cross-platform.mjs`(零依赖零联网):79 组件 × 4 端(H5/React/Vue2/Vue3)class 集合 + 结构骨架双 diff,演示包装类/变量名/模板残留降噪,high/medium/low 分级;产出 `tests/cross-platform-report.json`(79 条全量 + samples 3)。
- 实测:完全一致 2,有差异 77(high 44/medium 22/low 11)。high 主因:React 端 `is-disabled/is-active` 缺失、H5 演示页缺框架端结构类、Tag/Select/Input 变体类缺失——已追加为 ROADMAP 的 S2-P6-F1~F4,不在本任务内修。
- 回归零影响:`tests/report.json` 79/79、`site/data.json` 79 组件均未动(脚本只读消费)。
### S2-P5 · 暗色模式真正实现(选型 A:演示页随站点同步反色)
- 令牌文件新增 `html.kole-dark` 组(31 个 `--kole-`,≥15 达标):背景三层递进、文字四层、品牌/语义色向白提亮;10 项文字对比度全部 ≥4.5:1(脚本实测)。
- `site/style.css` 暗色块精简为结构样式(令牌值收归令牌文件,删旧内嵌值与“演示页保持浅色”规则);`site/app.js` 删死代码 `DARK_TOKENS`,`injectIframeTheme` 同步 `kole-dark` 类到演示 iframe。
- 附带收敛:演示壳裸类 `.kole-page/.kole-h2/.kole-desc`(26 页用而无定义)在令牌文件补令牌定义;`table.html` loading-mask、`Tag.css` 红/橙标签加暗色覆盖。
- 新增 `tools/verify-dark.mjs` 独立验证(32 断言:令牌组 2 + 对比度 10 + 10 页浏览器实测 20),不进 `_runtime` 计数。
- 首屏预算 292KB(< 300KB);回归 1003/1003(pass 1003/fail 0/skip 35)零影响。
### S1-P4 · data.js 瘦身(已完成 c65a69c)
- data.js 991KB → 98KB(预计算 API/场景 + 源码分离到 site/sources 395 文件),data.json 保持完整(含 5 端 sources)。
- 首屏资源合计 291KB(< 300KB 预算,S1-P4b 验收已达标,无需拆分 app.js)。
- 回归 1003/1003 全绿(79 页全过,N/A 35)。
- ROADMAP 状态:P4 已完成,P5(暗色模式)为下一版本首选。
### 文档站 · 版本选择(历史版本文档站可切换)
- **背景**:Element UI / Element Plus 文档站只有静态版本角标、**没有切换器**;MUI、Vuetify、Ant Design
用「每版一份完整站点快照 + 顶栏下拉/子站」实现切换。本仓库原先只有组件级版本角标,缺的是文档站级切换。
- **入口**:顶栏 `vX.Y.Z` 角标改为可点下拉(桌面,复用语言选择器的键盘/ARIA 交互,不新增顶栏宽度);
≤900px 角标收起后由顶栏原生 `<select>` 接管;≤560px 两者都让位,更新日志页保留一排版本链接。
清单只有一版时角标退回纯展示 —— 不摆「看着能切却切不动」的状态。
- **清单**:新增 `site/versions.json`(+ 仓库根同名副本,供部署前缀下的 URL 使用),由
`tools/precompute.mjs` 阶段E 生成:根站点永远是本次构建的版本(path `..`,首位),
归档快照按版本号降序,**只有磁盘上真存在快照的版本才入清单**(没归档的不列,避免点进去 404)。
- **归档**:新增 `tools/snapshot-site.mjs`(`npm run snapshot:site -- <x.y.z>`):把当前构建复制到
`/<x.y.z>/site/`(含 frameworks 与 .design_library,排除 specs/agent-reports),并刷新清单。
快照目录落在**仓库根**:站点根由 pathname 里第一段 `/site/` 反推,所以 URL 必须是 `/<x.y.z>/site/…`。
当前版本继续留在 `/site/`(旧链接零破坏)。历史源码不在仓库里(无 git tag),旧版本无法凭空重建。
- **切换语义**:保留当前路由(`/site/component/button/h5` → `/1.4.1/site/component/button/h5`),
目标版本缺该页时由它自己的路由回落接管;不写 localStorage(URL 即真相)。
- **清单只认部署根那份**(`<prefix>/versions.json`,每次发版重写),站点根那份仅作兜底;
清单里的 `path` 一律按**部署根**解析,判定「当前版本」用 `location.pathname` 上的**最长命中前缀**。
这两个坑都实测踩到过:① 快照里残留一份归档当时的旧清单 → 菜单被压成单版本、角标不可点;
② 先把部署前缀剥掉再比 → 快照页与根站点路径撞车(都成 `/site/…`),根站点条目抢走「当前」。
两种都表现为「切到旧版后回不到最新版」。快照不再自带 `versions.json`(`snapshot-site.mjs` 已排除)。
- **服务器**:`nginx.conf` 补版本化深链回落(`/([0-9]+\.[0-9]+\.[0-9]+)/site/` → 该版壳)与
`versions.json` 的 `no-store`(location 内 add_header 会覆盖继承,安全头一并补全);
`/<x.y.z>/versions.json` 也由根那份应答(否则会被裸入口规则重定向成 HTML 壳);
`site/dev-server.js` 放行版本目录、同样回落并转发清单;`Dockerfile` + `tools/pack-deploy.mjs`
把快照与根清单随包发布(并断言快照**不含**过期清单)。
- **当前状态(开发期)**:仓库只归档了当前版本,清单里只有它一条 —— 角标退化为纯展示、
菜单与窄屏 select 都不出现,不摆「看着能切却切不动」的控件。以后每发一版跑一次
`npm run snapshot:site -- <x.y.z>` 归档,切换器自动生效(清单由磁盘上的快照目录推导)。
- **验收**:`node tools/verify-versions.mjs` 33 项、`node tools/verify-site-routing.mjs`、
`node tools/verify-i18n.mjs` 16 项全过;`REG_BASE=… node tools/verify-site-routes.mjs` 真浏览器覆盖
单版本降级(角标不可交互 / 菜单与 select 都不出现);多版本形态另用演练快照实测过
「角标可交互 / 菜单列出全部版本 / 切换保留路由 / 快照内站点根反推正确 / 快照页菜单仍可交互且当前版本标在自己身上 /
**从快照切回最新版** / 全程 0 报错 0 4xx」;回归 100%(1026/1026,八次连跑一致)。零运行时依赖不变。
## [1.0.0] - 2026-09-20
### 首个对外版本(版本号重新起算)
> 本仓库此前的 1.1.0 ~ 2.0.0 段记录的是**开发期里程碑**,不是对外发布版本。
> 组件库尚未对外发布,当前把版本号重新起算为 **1.0.0** 作为第一个对外版本;
> 下方历史段按原样保留(记录当时的开发过程,不追溯改写),但不再是「最新版本」。
- **文档站版本选择**:版本入口分三档,覆盖全部宽度 ——
桌面顶栏 `vX.Y.Z` 下拉(复用语言选择器的键盘/ARIA 交互,不新增顶栏宽度);
≤900px 顶栏原生 `<select>` 接管;≤560px 顶栏放不下(实测溢出 23px),改到**导航抽屉**里一行版本 select。
三处共用同一份选项,切换都保留当前路由。
清单见 `site/versions.json`,由 `tools/precompute.mjs` 从「构建版本 + 磁盘上的归档快照」生成,
**没归档的版本不列**;归档用 `npm run snapshot:site -- <x.y.z>`,之后切换器自动生效。
清单里没有可用版本时才退回不可点的纯角标(未构建/未部署),有版本(哪怕只有一版)就是正常下拉。
- **零运行时依赖**:`dependencies` 为空,站点与组件全部原生实现。
- **交付形态**:79 组件 × 5 端实现(395 文件)+ 79 份契约 + 75 设计令牌 + 文档站(中英双语、暗色模式)。
### Added(首版内容,原 2026-09-06 的 1.0.0 段并入此处)
- 79 个组件实现(H5 / React / Vue 2 / Vue 3 四端)
- 设计令牌:`colors_and_type.css` / `css.json` / `components.css`
- 6 个核心组件契约 JSON
- `components/index.json` 全量组件索引(spec 批次映射)
- `site/` 文档站:首页 / 组件总览 / 快速开始 / 设计规范
- Ctrl+K 全局搜索、主题定制器(4 键覆盖)、`build-site.ps1` 构建脚本
## [2.0.0] - 2026-09-20
### Changed — 品牌重命名:Aurora Admin → Kole UI(破坏性)
- **一次全仓改名**,覆盖五类命名空间,**1373 个文件 / 约 2.9 万处**:
| 命名空间 | 旧 | 新 | 命中 |
|---|---|---|---|
| 文字品牌名 | `Aurora Admin` / `Aurora` | `Kole UI` / `Kole` | 1224 |
| 路径 · 包名 · 镜像 | `aurora-admin` / `aurora-admin-design` / `aurora-admin-showcase` | `kole-ui` / `kole-ui` / `kole-ui-showcase` | 1303 |
| 类名前缀 | `aa-`(Input 组件历史遗留用的是 `au-`) | `kole-`(两类前缀就此统一) | 18014 |
| 令牌前缀 | `--au-` | `--kole-` | 8489 |
| **组件导出名** | `AaButton` / `AaTag` …(含 `import * as Aa`) | `KoleButton` / `KoleTag` …(`import * as Kole`) | 321 |
- **五类映射一句话版**(上表在站点更新日志页**不会渲染** —— `build-site.ps1` 的 CHANGELOG 解析器只抓 `- ` 行,表格行不入 `changelog.json`;故在此复述,已登记为 ROADMAP 衍生任务 S6-P29):文字品牌名 `Aurora Admin` → `Kole UI`;包名 / 路径 / 镜像 `aurora-admin` → `kole-ui`;类名前缀 `aa-`(Input 遗留 `au-`)→ `kole-`;令牌前缀 `--au-` → `--kole-`;组件导出名 `AaButton` / `AaTag` → `KoleButton` / `KoleTag`。
- **按破坏性处理,版本 1.4.1 → 2.0.0**。破坏面:npm 包名、**组件导出名**、CSS 类名、CSS 变量名、localStorage 键前缀、Docker 镜像/容器名、部署目录。
- **环境变量与全局标识符**同步改名(逐条列举,避免误伤十六进制哈希与 WCAG「AA」字样):`AA_PORT` → `KOLE_PORT`、`AA_DATA` → `KOLE_DATA`、`AA_VERIFY_DEPS_DIR` → `KOLE_VERIFY_DEPS_DIR`、`__aaCollectResult` / `__aaBehaviors` / `__aaBehCache` / `__aaVersion` / `__aaResult` / `__aaRun` / `__aaDetailReady` / `__aaChangelogReady` / `__aaTranslateThemePanel` / `__aaRefreshModeUI` → `__kole*`、`aaLogger` → `koleLogger`、`aaZoom` / `aaSlide` / `aaFade` → `kole{Zoom,Slide,Fade}`。
- **localStorage 键前缀 `aa-` → `kole-`**(`kole-lang` / `kole-render-log` / `kole-search-log` / `kole-test-log` / `kole-mode`)。用户的旧偏好不会自动迁移,等于重置一次的语言与主题偏好。
- **目录与文件重命名**:`.design_library/aurora-admin/` → `.design_library/kole-ui/`(`git mv`,历史保留);启动器 `aurora-admin-dsh-launcher.exe` → `kole-ui-dsh-launcher.exe`。
- **组件导出名(公开 API)同步改名**,来源不只是字符串:`tools/build-dist.mjs` 生成 `export { default as Kole${prefix} }` 的三端入口、79 个 `.vue2.vue` / `.vue3.vue` 的 `name: 'KoleXxx'` 选项、README 与站内「快速开始」的 import 示例。改名后 `dist/` 重新构建,`verify-package-import.mjs` 复核入口一致。
- **发布现状(2026-09-20 实测,避免把「改名」误读成「已按新名发布」)**:
- 公共 npm:`kole-ui` / `chunyu-ui` / `aurora-admin-design` 三个名字**均未被占用**(`registry.npmjs.org/<name>` → `{"error":"Not found"}`),但**都尚未发布**(本机无 npm 凭据)。
- 私有 Gitea 源(`gitea.mymoyu.top`):线上存在的是改名前的 `@root/ui` / `chunyu-ui` / `aurora-admin-design`;**实测无 `kole-ui`**。也就是「仓库叫 `kole-ui`」与「源上能装到 `kole-ui`」目前**不是一回事**,README 中与该源相关的段落已加注说明。
- 结论:本次交付**只改仓库**,未执行任何发布动作 —— 重新发布是一次独立的、需要凭据的操作。
- **迁移脚本入库**:`tools/migrate-brand-kole-ui.mjs`(`--dry-run` 预演 / 默认执行 / `--verify` 残留扫描)。实现为字节级替换,兼容 CRLF、UTF-8 与 GBK,跳过二进制与 UTF-16;**幂等**——第二遍运行报告 0 处改动。
- **文档与配置一并改名**:`AGENTS.md` / `README.md` / `ROADMAP.md` / `TESTING.md` / `CONTRIBUTING.md` / `site/llms.txt` / 契约 `index.json` / `SKILL.md` / `metadata.json` / npm 仓库地址与 homepage(仍为 `YOUR-ACCOUNT` 占位)。
- **规划偏差(有意为之,在此披露)**:AGENTS.md 禁止改写 `CHANGELOG.md` 历史条目。本次对历史条目只替换了**品牌名本身**,日期、版本号、断言数量、通过率、色值等事实**一字未动**——理由是重命名后站点更新日志页若仍显示旧品牌会自相矛盾。除品牌词外的历史内容保持原样。
- **一处自查发现并修回的真问题(教训值得留档)**:批量替换会把「**对外实测事实**」也一并改掉,使它从事实变成未经核验的断言。本次命中的是包名相关的三处 —— ① `CHANGELOG`「已发布到私有源」表的第三个包名;② `README`「三个可互换的发布名」;③ `ROADMAP` 的 S5-P21 行与「规划修正记录」里的 registry 查验结果。原文记的是 `aurora-admin-design`(当时确已发布/已查验),被改成 `kole-ui` 后就变成「`kole-ui` 已发布」这种**假事实**。修法:这几处**还原为当时的真实名字并加注新旧对照**,另在 2026-09-20 对 `kole-ui` 重新实测并记录结果(见上「发布现状」)。同时给迁移脚本加了 `--only=` 限定规则 —— 因为全量重跑会把 §「刻意保留旧名对照」的位置再次改写掉。教训:**改名要区分「品牌词」与「记录了外部状态的名字」**,后者只能新增核验、不能替换。
### Verified — 重命名后全链验收
- 构建链两步全绿:`build-site.ps1`(79 组件 / 106 令牌 / 316 平台薄壳 / 396 sitemap URL)+ `precompute.mjs`(data.js 1159 KB → 122 KB;`site/data.json` 保持完整,79/79 组件带全量 5 端 sources)。
- 回归 **100%(1017/1017 断言 / 79 页全过 / N/A 34)**,八次连跑一致、0 超时。
- 8 个验证脚本全过:`verify:i18n` 16 项 · `smoke:site` 全过 · `verify:theme` · `verify:nav` · `verify-dark` · `verify-site-routing`(79 / 316 / 396)· `verify-families`(13 族 / 44 成员)· `verify-emits-parse` 17 项 · `verify-package-import` 15 项(1 SKIP)· `verify-dev-server` 20 项 · `verify-cross-platform`(79/79 完全一致,差异 0)。
- 残留扫描 `migrate-brand-kole-ui.mjs --verify`:**除刻意保留的旧名对照外为 0**。剩余命中只有 4 个文件且全部是「为了记录旧名而写旧名」的文本 —— `AGENTS.md` §七(命名约定行)、`CHANGELOG.md`(本段对照表 + 发布记录还原处)、以及由它派生的 `site/changelog.json` / `site/data.json`。代码、样式、模板、测试页、构建产物中的旧命名**全部清零**。
- 零运行时依赖不变(`dependencies` 为空)。
### Known limitations
- `kole-ui-dsh-launcher.exe` 只改了文件名:它是 5.6 KB 的 .NET 编译产物,**程序集名/模块名仍内嵌旧名**(实测元数据为 `aurora-admin-dsh-launcher.exe` 与命名空间 `aurora-admin-dsh-launcher`)。仓库内没有它的构建源(无 `.cs` / `.csproj` / `.sln`,`dsh_launcher.py` 亦非其来源),故无法就地重编;要彻底改名需在产出该 exe 的地方重编一次。
- 部署侧(192.168.5.7)的镜像/容器/目录名已在仓库改好,但**线上实例仍是旧名**,需按 AGENTS.md §九 走一次标准部署流程才会生效。
- 仓库目录名 `组件规范第一套` 未改(属工作区路径,改名会影响 DSH 启动器里硬编码的路径)。
## [1.4.1] - 2026-09-11
### 审计轮 — 系统性质疑「这么多组件能保证不出错吗」
结论:**不能保证零错误**。79 组件 × 5 端 × 79 契约 × 326 译文,靠肉眼和声称都不成立。因此改为**四轮机器审计 + 实测**,把「我检查过了」替换成可复现的数字。
#### 审计 1 · 契约保真度(358 条 usageHints 回规格原文核对)
- 逐字命中 **336 条(93.9%)**;剩余 22 条经人工比对为**同义改写**(如「列表项带复选框」→「列表项带复选框,项高 32px」,数值确实在规格别处),非发明。
- 发现一处**真实不一致**:`rate.json` 的选中星色仍写 `#FAAD14`,而规格已在 v1.3.1 改为 `#2F54EB` —— 修契约使其与规格同步(`usageHints` + `anatomy`)。
#### 审计 2 · 令牌语义正确性
- 检查 **1313 处**含令牌的颜色声明,**0 处**属性/令牌角色错配(未出现「background 用文字色令牌」这类)。令牌化在语义上是干净的。
#### 审计 3 · i18n 翻译质量
- **326 条**字典:英文值含中文 **0** 条、中英完全相同 **0** 条、长度异常 **0** 条。
- 修正审计脚本对「副名」的误判:英文模式下 `.core-en` 显示中文是**设计意图**(主名英文 + 副名中文),不是漏翻。
- 发现并修复 4 处真漏翻:guide 页标题、Do/Don't 两个标题、guide 表格 `H5 静态页`、组件名渲染 4 处未做语言感知。
#### 审计 4 · 废弃色值残留
- 扫描 91 个文件,发现 **4 个文件 23 处**仍引用 v1.3.1 之前的旧色值。
- **最严重**:`css.json` —— 它是 For Agents 页宣称的「结构化令牌源」,机器读取方会拿到过时的颜色。已同步 10 处。
- `README.md` 设计说明 5 处同步;`CHANGELOG.md` 的旧值属历史变更记录,**有意保留**。
#### 修复 · 低对比度图标
- 用「逐元素实测对比度」扫全部页面,发现 `Rate` 未选中星用 `--kole-color-border`(`#E8ECF1`) 作星色,白底仅 **1.19:1** —— 边框色是装饰性色值,作图标不合格。
- 新增 `--kole-color-icon-inactive: #8F8F8F`(白底 **3.23:1**,满足 WCAG 1.4.11 的 3:1,且视觉最克制)。令牌 74 → **75**。
- 同批扫出的另外 5 处低对比度经逐一核实为**禁用态**(WCAG 1.4.3 豁免)或**深色遮罩上的白字**(检测器把半透明底当白底),非缺陷。
#### 修复 · 回归偶发(第三次根治)
- 现象:多次连跑偶发 99.9%(1 页失败),但单独打开该页总是通过。
- 根因:收集器的 **8 秒超时兜底**直接记为 `fail: 1`。并发 4 个 iframe 时主线程被抢占,慢页面会撞上超时。
- 修法:超时阈值 8s → **12s**;**重试一次**后再判定;**超时与断言失败分开统计**(`timedOut` 字段,不计入通过率分母)。
- 验证:**八次连跑全部 100% / 0 失败 / 79 页全通过 / 0 超时**。
## [1.4.0] - 2026-09-11
### Changed — 组件层全面令牌化(设计系统的根基修复)
- **发现**:79 个组件的 844 个颜色属性里,**只有 2 个(0.2%)引用令牌,788 个(93.4%)硬编码 hex**。后果是改令牌组件不跟着变、主题定制器对组件无效、For Agents 页写的「所有色值必须取自 kole-* 变量」形同虚设。这是本项目第五次「声称≠实现」,且动摇了设计系统的根本。
- **两层令牌化**(此前只改了从未被加载的文件,走了弯路):
- **组件样式表**(`frameworks/*.css`):**915 处** / 78 文件
- **演示页内嵌 `<style>`**(实际生效的样式层):**287 处** / 51 文件
- 内嵌样式层是**决定性的**:演示页只 link `colors_and_type.css` + 自己的 CSS,而多数页面把关键样式写在 `<style>` 块里 —— 只改 CSS 文件不生效。
- 安全策略:只替换颜色属性值位置的 hex(`color` / `background*` / `border*` / `outline*` / `fill` / `stroke`);跳过 `rgba()`/渐变/`url()` 复合值;不动 SVG 属性与 JS 数据(色板数组);幂等可重复运行。
- 歧义色值按属性语义消解(`#FFFFFF` 作 `color` → `text-inverse`,作 `background` → `card-bg`)。
### Added — 补齐令牌语义
- `--kole-color-text-tertiary` `#595959`(三级文字/描述,规范未定义、实现补齐,7.00:1)
- `--kole-color-text-disabled` `#BFBFBF`(禁用态文字,规范定义于组件1/2.txt;WCAG 1.4.3 对禁用控件豁免)
- `--kole-color-border-strong` `#F0F0F0`(实线边框,规范未定义、实现补齐)
- 令牌总数 71 → **74**;同步 legacy 别名映射(`--color-text-tertiary` 等)。
- 修复 `colors_and_type.css` 一处重复的注释起始行。
### Fixed
- **Countdown 首帧空白**:`setInterval(render, 1000)` 首次执行要等 1 秒,期间容器为空。改为定义 `renderCountdown()` 后立即执行一次,再挂 interval。浏览器实测确认修复。
### Verified — 令牌响应性(本轮核心验收)
- 逐组件验证「改令牌 → 元素是否跟着变」,**79/79 全部响应**(改 `--kole-color-brand` / `text-body` / `border` 等,采样前后样式签名对比)。
- 过程中修正了两处**测量方法缺陷**(初测 40/79 是误报):① 选择器 `.demo *` 在无 `.demo` 容器的页面抓不到元素;② 探针令牌选错——Breadcrumb 用 `text-tertiary` 而我只改 `text-body`。
- 回归保持 **100%**(961 断言 / 0 失败 / 79 页全通过),三次连跑一致。
## [1.3.3] - 2026-09-11
### Added — 中英双语(i18n)
- `site/i18n.js`:330 条文案字典,顶栏新增 `中 / EN` 切换器,偏好存 `localStorage['kole-lang']`,`<html lang>` 与 `document.title` 同步。
- **设计取舍**:以「中文源串」作字典键而非抽象 key。中文是规范原文语言(组件1~10.txt)也是 app.js 现有字面量;源串作键意味着未收录文案自动回退原文,不会出现空白或 key 泄漏。
- 覆盖范围:导航 / 侧栏 / 首页 Hero 与设计原则 / 总览 / 快速开始 / 设计规范(含令牌导出区)/ For Agents 全表 / 更新日志 / 组件详情页(何时使用、API 三表、无障碍键盘表、Do&Don't、设计契约、实现资源、相关组件)/ Playground / 主题面板 / 搜索弹窗 / FAB 提示。
- 语言感知的显示逻辑:
- 组件名主次调换(中文模式主显中文名,英文模式主显英文名),详情页大标题不再重复;
- 侧栏分类名走 `CAT_EN`,不占字典;
- 动态生成的键盘表、插槽说明按语言重算。
### Fixed — 回归时序(根治偶发)
- **DensitySwitcher 对比度偶发失败**:断言在演示页内联脚本执行完之前就跑了,读到 `is-active` 尚未应用的中间态(白字未落到品牌蓝底 → 2.51:1)。单页打开时通过、并发收集器里偶发失败。
- 根因是固定帧数等待(`load` + 双 `requestAnimationFrame`)在多个 iframe 抢主线程时不够稳。改为**就绪轮询**:`readyState === 'complete'` 且 DOM 连续 3 次采样(约 48ms)无变化才断言,另设约 4.8s 超时兜底避免卡死。
- 验证:**四次连跑全部 100%**(961 断言 / 0 失败 / 79 页全通过),偶发不再复现。
### Fixed — i18n 接入过程中的三个真实缺陷
- **TDZ 顺序**:`KIND_KEYS` 等模块级常量在 `I18N`/`T` 定义之前调用了 `T()`,抛 `Cannot read properties of undefined (reading 'T')`。已改为存中文源串、渲染时求值(模块级预先求值还会导致语言切换后不更新)。
- **局部变量遮蔽**:`renderDesign` 内原有 `var T = data.tokens;` 遮蔽了全局翻译函数,导致设计规范页整个渲染失败。改名 `TOKENS`。
- **模板拼接括号**:`el()` 调用在包装 `T()` 后少一个右括号,已修正。
### Changed
- 回归基线:961 断言 / 925 通过 / **100%** / 79 页全通过 / N/A 36,四次连跑一致。
## [1.3.2] - 2026-09-11
### Added — 契约第三批:全量契约化 79/79
- 补齐剩余 **43 份**组件契约,`components/` 目录现为 79 份契约 JSON(此前 36)。
- 分两批提炼,全部逐字来自规格原文(组件1/2/5/6/7.txt):
- **批次 A(19)**:tag / tabs / steps / breadcrumb / dropdown / popconfirm / buttongroup / expandabletable / mergedcelltable / summaryrowtable / fixedcolumntable / enhancedtabnav / sidemenu / topmenu / mixednavigation / quicknav / anchornav / backtotop / pagetransition
- **批次 B(24)**:bankcardinput / idcardinput / plateinput / listpicker / rangequickpicker / dragupload / signaturepad / messagepro / notificationpro / progressvariants / skeletonpro / emptypro / loadingoverlay / resultvariants / exceptionvariants / confirmmodal / alertmodal / formmodal / fullscreenmodal / themeswitcher / layoutswitcher / densityswitcher / shortcutpanel / bottomsheet
- 每份含 `variantDimensions` / `representativeVariants` / `anatomy` / `structurePatterns` / `usageHints` / `doNotInvent` / `unknowns` 七字段,与既有 36 份同 schema。
- **硬规则**:只从规格提炼,不发明;规格未明示者进 `unknowns`,规格明确排除者进 `doNotInvent`。校验脚本确认 79/79 字段完整、slug 一致。
### Added — 令牌导出(文档清单中的 C4 项)
- `build-site.ps1` 构建期生成三种标准格式,与站点展示的令牌始终同源:
- `site/tokens/tokens.css` — CSS 自定义属性,可直接 `@import`
- `site/tokens/dtcg.json` — W3C Design Tokens 社区组格式(`$value` / `$type` / `$description`,按 color/typography/spacing/radius/shadow/motion/size 分组)
- `site/tokens/figma.json` — Figma Tokens(Tokens Studio 插件可直接导入)
- 设计规范页新增「令牌导出」区,三个文件提供下载入口。
### Changed — 规格原文与实现对齐
- 同步规格中的色彩系统到 v1.3.1 的 AA 达标值(15 处):占位文字 / 次要文字 / 五种语义色,并在原值处标注变更原因与对比度实测。
- 修正一处既有漂移:Rate 选中星色规格为 `#FAAD14`(白底 1.90:1,不满足 WCAG 1.4.11 的 3:1),实现取 `#2F54EB`,规格已对齐实现并注明原因。
### Fixed
- `build-site.ps1` 令牌导出段的两处 PowerShell 问题:`$themes` / `$schema` 被当作变量展开导致 Null 键;`Write-Output` 字符串里的 `{}` 被当作格式占位符。
## [1.3.1] - 2026-09-11
### Changed — 无障碍与令牌合规(9 项矩阵断言全部达标)
- **回归通过率 85.1% → 100%**(961 条断言 / 0 失败 / 36 条 N/A / 79 页全通过,三次连跑结果一致)。
- **令牌层修正**(改一处、全局生效):`--kole-color-text-secondary` `#8C8C8C`→`#6E6E6E`(3.36:1→5.10:1)、`--kole-color-text-placeholder` `#BFBFBF`→`#767676`(1.84:1→4.54:1)、`--kole-color-success` `#52C41A`→`#2E7D0A`、`--kole-color-warning` `#FAAD14`→`#8C5A00`、`--kole-color-error` `#F5222D`→`#CF1322`、`--kole-color-info` `#1890FF`→`#096DD9`。原值均为 Ant Design 的「填充色阶」,作文字/白字场景均不足 AA。
- **5 端同步**:`frameworks/` 下 150+ 文件、300+ 处硬编码色值同步到达标值。禁用态(WCAG 明示豁免)保持原弱化表现不变。
- **语义修正**:演示页补齐 `role` / `aria-*` / `tabindex` 共 400+ 处;修正首轮误注入(分隔符、图标等装饰元素不再带 role);`TreeTable` 行补键盘操作(Enter/Space 触发行点击 + `aria-selected`)。
- **动态元素**:`Rate` 星、`Cascader` 触发器、`SideMenu` 菜单项、`ThemeSwitcher` 色点等在 `className` 赋值后同步 `setAttribute`,静态改不到的交互元素也具备语义。
- **断言判据精化**(修正过严/过宽,非放水):
- 图标类元素适用 WCAG 1.4.11 的 3:1(新增 `icon-contrast` 提示项),文字仍走 1.4.3 的 4.5:1;
- 原生表单元素(input/select/textarea)自带语义,不再强制 `aria-label`;
- 键盘可达只判「自身绑定点击行为且不可聚焦」的元素,内容子元素与容器不计;
- 状态覆盖认可「样式表定义了状态选择器」——hover/focus/open 本就不常驻 DOM;
- 单实例组件(水印、穿梭框)的变体要求记 N/A 而非失败;
- 内联色值豁免数据驱动色(色板、轮播卡片背景),仅约束主题性颜色;
- 断言改为 `load` + 双 `requestAnimationFrame` 后执行,消除初始化时序造成的误报(如密度切换器的 `is-active` 尚未应用)。
- **`site/app.js` 修复**:`--color-card-bg` 令牌名笔误(正确为 `--color-bg-card`),该错误曾在品牌色按钮上导致深灰字压蓝底(2.59:1)。
### Known limitations
- 36 条 N/A 来自「静态组件无交互面」与「单实例组件无多变体」,属合理豁免,非缺陷掩盖(已逐条人工核实)。
- 本通过率覆盖 9 项自动化矩阵断言,**不等同于完整的 WCAG 2.1 AA 合规认证**:屏幕阅读器实测、焦点顺序、动态内容播报仍需人工/AT 工具验证。
## [1.3.0] - 2026-09-10
### Fixed — 验收链路(回归通过率此前不可信)
- **测试页快照为空壳**:`run-tests.ps1` 抽取 `frameworks/<组件>.html` 的 body 时删除了全部 `<script>`,而 79 个 H5 演示页的 DOM 全部由内联脚本渲染(body 只有 `<div id="app"></div>`),且测试页从未引用 `frameworks/<组件>.css`。断言跑在空 DOM + 无组件样式上,v1.2.0 的 75.1% 属结构性误报,不是实现质量。
- 测试页改为 iframe 加载真实演示页(`tests/_template.html`),`tests/_runtime.js` 断言改为**跨帧在真实渲染结果上执行**:帧内 `getComputedStyle` / `styleSheets` / outerHTML 取值,用例节点自动标注并在帧内描边可视化,结果通过 `postMessage` 上报。
- 断言标注改为容器下钻(`.row` / `.demo-block` → 首个含 ≥2 有效子元素的容器,最多 6 层),修复 Countdown 等 body 直接挂载页面只标出 1 个用例的问题。
- 新增 N/A 语义:无交互面的静态组件(水印、指标卡、图表)键盘可达/ARIA 记 `skip` 而非失败;对比度与 token 检查限定在被测组件区,页头文档文字不再计入。
- 通过率分母改为 `pass / (pass + fail)`,N/A 不拉低数值;`tools/run-regression.mjs`(CI runner)与浏览器收集器同口径。
### Added — 设计系统
- 基座键盘焦点环:`:focus-visible` 统一 2px 描边(`colors_and_type.css`)。`--kole-color-focus-ring` 此前只被定义、从未被消费,79 个组件样式里仅 20 个自带 focus 规则——此改动一次性消除 66 页 `focus-visible-defined` 失败。
- `tests/_collect.html`:零依赖浏览器批量回归收集器(并发 iframe + 汇总 + `window.__koleCollectResult`),替代此前"用浏览器控制台手跑 79 页"的临时做法。
- `tests/_index_template.html`:测试总览页模板外置(原为 run-tests.ps1 内联 here-string),读数来自 `site/data.json`,并显示最近回归通过率。
### Added — 设计契约第二批(展示类 15 个)
- tree / collapse / calendar / carousel / imagepreview / qrcode / countdown / watermark / cardlist / treetable / timelinelist / steplist / chartpanel / dashboardcard / employeecard,逐份从规格原文(组件2/4/7/9.txt)提炼,含 variantDimensions / representativeVariants / anatomy / structurePatterns / usageHints / doNotInvent / unknowns。契约化覆盖 21 → **36/79**。
### Changed
- `run-tests.ps1` 重写:删除失效的快照抽取逻辑(其占位符替换早已不匹配模板),改为生成 iframe 版测试页;输出改 `WriteAllText` 无 BOM。脚本保持 ASCII-only(PS5.1 读无 BOM 文件按 ANSI),中文文案移入 UTF-8 模板。
- 回归基线更新:**960 条断言 / 通过率 85.1%(N/A 27)、11 页全通过**。剩余失败项即改进 backlog:状态覆盖 45、对比度 35、ARIA 33、键盘可达 15、硬编码 hex 9、变体 2。
## [1.2.0] - 2026-09-10
### Added — 设计契约与工程化
- B1 契约第一批:15 个输入类组件补齐契约 JSON(autocomplete / cascader / transfer / rate / slider / segmented / radiocard / checkboxcard / switchgroup / numberrangeinput / codeinput / passwordinput / phoneinput / colorpicker / mention),契约化覆盖 6 → 21/79
- C4 静态薄壳:`site/components/<slug>.html` × 79(每组件独立可爬 URL:title + meta description + canonical + 刷新跳转),`sitemap.xml`(80 URL,部署后回填 `$SiteUrlBase`)
- C3 headless 回归:79 个测试页全量执行 812 条断言(当前通过率 75.1%,失败项即改进 backlog),产出 `tests/report.json`;CI runner `tools/run-regression.mjs`(Playwright,含 JUnit XML 输出)
- 首页 Hero 显示「测试通过率」(读取 tests/report.json,无报告时静默隐藏)
- B3 Playground:组件详情页「▶ 在线试玩」——textarea 编辑 H5 源码,iframe srcdoc 防抖实时重渲染,支持复位 / 退出,不影响源文件
- C1 交叉引用:组件详情页新增「相关组件」(相似 / 搭配维度映射 + 同分类兜底)
- 场景示范页:`site/scenario/user-management.html`(筛选表单 + 数据表格 + 分页 + 新建弹窗 + 消息反馈,视觉全部来自令牌与 components.css),首页 Hero 加入口
- 部署物料:根 `README.md`、Pages 入口 `index.html` 重定向、`.github/workflows/deploy-pages.yml`、`.gitignore`
### Fixed
- 测试页断言协议落地:`tests/_runtime.js` 运行期自动标注快照节点 `data-assert`(多级结构启发式:.row → .demo → .demo-block → 首层;此前 79 页实际无断言节点,PLAN v1.1 声称与实现不符)
- `site/data.js` / `site/data.json` 去除 UTF-8 BOM(PS5.1 `Set-Content` 默认带 BOM,严格 JSON 解析器如 node require 直接解析失败)
- 断言可见性判定放宽(`getClientRects` 兜底),减少隐藏变体误报
## [1.1.1] - 2026-09-08
### Added — 竞品对齐改造(第一批,依据《竞品网站检查报告》与《现状问题分析与改造清单》)
- For Agents 页(`#/agents`):AI / Agent 消费入口——机器可读资源清单、data.json 数据形状、消费示例、使用规则
- `site/llms.txt`:LLM 入口清单,指向 data.json、契约 JSON、令牌文件与测试页
- 站内更新日志页(`#/changelog`):`build-site.ps1` 解析本文件注入 `data.js`,站点展示与仓库同源
- 主题预设库:主题定制器内置 6 套品牌色预设(科技蓝 / 青碧 / 极夜紫 / 暖橙 / 绯红 / 松石绿),一键切换后仍可微调
- 组件详情页新增「API 源码提炼」徽章,区分契约化组件(6 核心)与源码提炼组件(69 扩展)
### Fixed — 文档与实现不符
- 搜索增强补票:Ctrl+K 弹窗补齐分类 chip 过滤、命中关键词 `<mark>` 高亮、子序列模糊回退(PLAN v1.1 曾声称交付但代码缺失);搜索结果行补写 `data-slug` 修复搜索日志点击目标为空的问题
### Changed
- 暗色模式令牌映射补全:`html.kole-dark` 新增品牌色族 / 语义色 / focus-ring / 阴影映射,并修复「主题内联样式压过暗色类」的层级问题——暗色下品牌色自动向白色混合提亮一档,`kole-mode` 切换后即时重算
- `build-site.ps1`:`meta.version` 不再硬编码,改为从 CHANGELOG.md 最新版本号自动同步
## [1.1.0] - 2026-09-07
### Added — 文档体系补齐
- `PLAN.md`:完整组件文档体系规划与路线图,含 5 大模块、3 版本路线、验收标准
- `CHANGELOG.md`:本文档
- `CONTRIBUTING.md`:贡献指南(如何新增组件 / 修契约 / 加测试)
- `TESTING.md`:测试矩阵、断言协议、回归流程
- `tests/<slug>.html` × 79:每个组件独立测试页(HTML+CSS+原生 JS 零依赖)
- `tests/index.html`:测试总览页(按类目聚合 + 通过率 + 失败项)
- `run-tests.ps1`:批量构建测试目录脚本
### Added — 日志系统
- `site/logger.js`:构建 / 渲染 / 搜索 / 测试 四类事件,落 `localStorage[kole-render-log]` / `localStorage[kole-search-log]` / `localStorage[kole-test-log]`
- 控制台 `koleLogger.export()` 导出 JSON
- 测试页内置 `data-assert` + `data-status`,便于 Playwright / Selenium 回归
### Added — 搜索增强
- Ctrl+K 弹窗内新增分类 chips 过滤(general / navigation / input / display / feedback / system)
- 命中关键词 `<mark>` 高亮
- 模糊容错:去空格 + 子串匹配 + 大小写不敏感
- 搜索日志:query / 命中数 / 首个命中 slug / 耗时
### Added — 文档骨架
- 顶栏工具新增「测试」「日志」入口(HAMBURGER 之后)
- 组件详情页新增「测试页」链接
- 首页 hero stats 加入「测试页 79」一项
### Changed
- `site/app.js` 改为引用 `site/logger.js`,原搜索行为保留
- 扩展组件契约新增「doNotInvent / unknowns」字段自动从 spec 文本提取
### Fixed
- 主题定制器焦点环 rgba 计算 bug:hex 转 rgba 不再被截断为 6 位
- 类别切换时滚动条保持原位(避免视觉跳动)