# 更新日志

[English](./CHANGELOG.md) · **简体中文**

## 尚未发布

- 文档首页新增响应式的收尾行动区：在功能概览之后持续提供安装与试验场入口，支持中英文文案，并通过部署基址安全生成链接。
- 视口观察器在注入宿主的单个清理接口抛错时仍尝试其余清理；初始化回滚失败不再掩盖原始错误，动画帧调度失败后会销毁观察器。源码回归与 ESM/CommonJS 产物冒烟测试覆盖相应异常路径。
- 文档搜索通过 Escape、移动端返回按钮或遮罩关闭后，将焦点恢复到搜索入口；选择搜索结果时不会抢走焦点。
- 真实 Vite 构建新增省略、空对象及单侧 fluid 边界验证，覆盖正数间距与负外边距，同时确认其他画布和组件库路由不受影响。
- 中英文 API 文档新增编辑 AST 后重新生成结果并重新审计的示例；测试直接执行文档原文，验证旧 CSS 和旧审计仍保留原始结果。
- CI 与文档工作流的 checkout/setup-node 统一升级至 v6，与已有发布工作流保持一致；保留 Node 18/20/22/24 兼容检查，Pages 专用动作未改动。

- 配置字段校验不再为每个较短的允许字段列表临时分配 Set；未知字段拒绝与拼写建议保持不变。
- 配置指南现在分开说明真正必填的嵌套字段与可选设置，并提供零配置、画布简写和单侧边界示例；测试直接编译中英文示例并验证二次编译稳定性。
- 参考表格正文与窄屏表头支持长内容换行；英文组件库快捷入口移至 More 菜单，避免平板宽度下顶栏溢出，侧栏与页面链接保持可用。
- Markdown 页面复制跨导航串行处理剪贴板写入，取消过期下载，并在组件销毁后停止反馈；集成测试覆盖请求超时恢复、迟到响应及 Markdown 原文传递。

- Node 18 CI 除锁定依赖版本外，新增声明的最低 PostCSS 8.4.0 版本下的产物冒烟检查。这是兼容性覆盖，不代表建议使用旧版 PostCSS。

- 试验场新增受保护的“复制 CSS”操作：只复制当前成功结果，支持空 CSS，处理剪贴板权限失败，并在输入变化或组件销毁后抑制过期完成提示。

- 组件库验证器会记录样式发现阶段的错误（包括损坏的缓存目录），并继续检查其余目标，不再中断整批验证。

- CLI 遇到空字符串或纯空白异常消息时，现在会在终端与 JSON 输出中提供明确的兜底诊断，不再输出空错误。

- 试验场配置抛出空字符串或纯空白错误消息时，现在会显示兜底错误，避免静默失败后将上次输出误认为当前结果。

- 显式提供非空画布映射时，初始化不再创建并校验不会使用的默认 App/PC 预设。省略或空映射仍使用原有默认值，用户配置校验保持不变。

- 编译 API 质量门禁支持单类别字符串（`failOn: 'warnings'` 或 `'compatibility'`）及数组。此参数仍可省略，结果统一返回类别数组；兼容性门禁仍需提供浏览器目标。

- Playground 现在能安全报告无法转成字符串的异常值，覆盖用户配置表达式及 Worker 启动、消息发送路径；回归测试验证资源清理和同一 Worker 的后续编译恢复。
- 文档目录的长标题改为完整换行，不再截断；试验场新增无边界流体示例，纯根字号文字示例移除不必要的边界。指南明确根字号响应不保证文字按比例放大或自动满足无障碍要求。
- Pages 部署现在必须先通过完整 `npm run check`，包括主题/Worker 测试、类型、Lint、格式、覆盖率与文档构建检查，再上传产物；性能与依赖审计仍是独立 CI 任务。
- 重复传入 CLI `--targets` 现在会合并浏览器条目，并跨别名及参数顺序保留最低版本，不再覆盖先前目标。重复的空列表仍会报错；已构建 CLI 的冒烟测试在 Node 18 与 24 上验证了对应的兼容性门禁。
- 组件库验证器现在会拒绝预期不转换的库在首次编译中发生 CSS 改写，即使输出具有幂等性。Quasar 原始样式中的接缝诊断仍会展示，并继续使严格接缝门禁失败。
- 新增纯 Node 的 ESM 与 CommonJS 使用方夹具，以 ES2022 且不包含 DOM 库检查主入口；浏览器运行时类型仍需 DOM 声明。
- Node 18/24 运行时冒烟现在会通过已构建的 ESM 与 CommonJS 入口执行全部语言无关一致性用例，并核对告警数量与二次编译幂等性；规范套件同时覆盖简写 profile 下纯根字号文字与无边界流体间距的组合。
- 公共兼容性辅助函数现在会以与编译 API 一致的明确错误拒绝非字符串 CSS，并在读取浏览器目标或强制转换调用方对象前失败。
- 兼容性审计的内部特性信息与支持版本表现在与公开对象隔离。修改返回的特性、检测正则或导出的支持元数据，不再改变后续审计结论。
- 兼容性目标现在拒绝数组、BigInt 和对象，不再将其强制转换为版本字符串；程序化编译在调用动态配置函数前校验目标。
- CLI 配置校验错误现在同时报告传入的配置文件路径，并保留字段级诊断和拼写建议，适用于文本与 JSON 报告。
- 组件库配置数组中的空缺项现在会直接触发条目校验，不再进入名称去重检查后抛出内部属性访问异常。
- 路由媒体边界错误现在报告完整配置路径，包括路由与匹配条件数组下标，便于直接定位嵌套数值和空区间配置问题。
- CLI 新增 `-v`／`--version`，直接读取当前安装包元数据，并在加载配置、CSS 或 stdin 之前退出。
- 可复用转换器在创建时一次性规范化文字属性模式，不再为每个新属性名重复处理配置；转义标准属性名与区分大小写的自定义属性名保持原匹配规则。
- Token 诊断限制分支替换工作量与展开结果长度，预算耗尽时返回未知，避免重复引用无限放大；正常编译不受此诊断限制影响。
- Token 诊断按重要性及源码顺序预排定义，使重复宽度查询命中首个有效定义后即可返回，同时保留断点发现顺序。
- 属性过滤和文字分类缓存不再保留超过 256 个 UTF-16 码元的属性名；更长名称仍正常匹配和转换，该上限只控制缓存保留，不限制合法 CSS 输入。
- 属性通配符过滤器现在按顺序匹配字面片段，不再生成回溯正则，避免通配符组合重试；差分测试覆盖匹配语义及首尾片段重叠。
- CLI 颜色行为现在覆盖非 TTY 输出、`NO_COLOR`、强制颜色及选项顺序回归测试；显式 `--color`／`--no-color` 仍以最后一次为准。
- CLI 带值选项现在拒绝显式传入的空字符串参数，与 `--option=` 行为一致，不再静默忽略空的 config/from/profile 值。
- 基准预检现在会按属性验证组件、工具类和应用语料的预期转换状态；计时前即可捕获部分转换或误改无关声明。
- 性能门禁现在会预检每个默认语料，并要求确实发生转换，同时允许有意保留的无关声明。这样不会把空操作或被跳过的工作误判为提速；应用语料预期的路由警告仍会保留并可观察。
- 新增文档打印样式：隐藏导航和复制控件、展开正文宽度、换行显示长代码与表格内容，并请求代码块、表格和引用保持整块。实际打印分页尚未经浏览器验证。
- JSON Schema 现在拒绝空白的文件、选择器、声明值匹配及属性过滤字符串，与运行时校验一致；可选过滤字段仍无需填写，`textProperties` 仍允许空列表。

- 原生且不带全局/粘连标志的正则过滤器现在跳过不必要的游标维护，冻结的普通正则也不再逐次复制；有状态正则与自定义执行钩子仍保留游标恢复路径。

- 可复用编译器现在捕获 `include` 与 `exclude` 文件过滤数组；修改调用方数组不再影响既有编译器对后续文件的转换判断。过滤条件仍可省略，空数组保留原有校验错误。

- 文档生产站点现在按需加载独立的中英文搜索索引，保留章节锚点与标题；开发模式仍使用共享实时索引以维持原生热更新。离线构建检查验证语言隔离和延迟加载，支持域名根路径及子路径部署。

- 连续性诊断现在识别转义形式的 `var()`、数学函数名及视口单位；分量切分保留转义终止符和转义标点。单次分析内复用补偿项移除结果，不跨调用保留值。

- CI 现在新增 Windows + Node 24 的完整验证与运行时冒烟检查，覆盖平台相关的 CLI 和文件路径行为，并保留 Linux 矩阵。

- 收紧公开 JSON Schema，拒绝空白查询条件并与运行时校验一致，同时保持可选的 `query` 字段无需填写。

- 开发工具升级至 Vitest/coverage 4.1.11，锁定的 js-yaml 依赖更新至 4.3.2，消除本次审计报告的 mocker 文件读取与 YAML 合并 CPU 消耗告警；不改变运行时 API。

- 转换值缓存除条目数限制外，新增输入/结果各 16,384 字符及键/结果合计 4 Mi UTF-16 码元预算；更大的值仍正常转换但不保留。这些是缓存预算，不是进程堆内存上限。

- 单位转换会在 PostCSS 原始文本仍有效时保留值内注释，不会恢复前置插件已修改的旧值；带注释回退输出保持幂等，包括固定偏移与 gutter 补偿的组合。

- 转换器现在以迭代方式遍历并序列化深层 value-parser 函数，避免调用栈失败，同时保留 URL/字符串的不透明处理。

- 长度求值拒绝 CSS 数学函数之外的算式和不匹配的括号，包括在需要右括号的位置误用左括号。
- 大型 `min()`/`max()` 表达式改为逐项归约，不再展开为函数实参，避免参数过多触发调用栈错误。
- 诊断表达式求值将递归嵌套限制为 128 层，超出后返回未知而不是溢出 JavaScript 调用栈。

- 视口长度求值在最终像素结果仍有限时避免中间乘法溢出；真正超出数值范围的结果仍返回未知。

- 合并重复连续性诊断、更新最终报告断点时，同步更新两侧采样像素值，保证报告数值可按该断点复现。

- 令牌解析现在遵守媒体范围的严格端点语义，包括在排除边界上准确使用回退值。

- 媒体条件归一化保留 `<`/`>` 的严格端点语义，具体宽度匹配不再错误包含端点；路由区间仍使用保守数值包络。

- 媒体宽度分析遇到换算为像素后溢出的 `em`/`rem` 边界时返回无法解析，不再将非有限边界传入诊断；仍支持可表示的极大宽度。

- 试验场新增显式重新编译操作，首次失败不再误显示正在编译；手动重试会清理待触发的防抖任务，后续失败仍保留此前成功结果。
- 文档门禁新增 Vue 脚本与严格模板类型检查，并验证搜索和试验场代码不进入页面立即加载依赖链；配置说明明确属性白名单不要求填写 `*`。

- CLI 的 CSS、对照、JSON、帮助及批次汇总输出会等待 stdout 背压；等待期间关闭或报错会正常失败，JSON 写入失败后回退至 stderr，不重试已损坏的流。测试覆盖真实暂停读取、断管及 JSON 错误报告写入失败。
- CLI 的 JSON 配置语法错误不再回显解析器附带的原文片段；保留配置路径与可识别的数字错误位置。可执行 JavaScript 配置抛出的错误不在这项脱敏范围内。
- 组件库验证遇到编译或接缝分析异常时，将该项记录为失败并继续检查剩余样式表，不再中断整个报告。
- 标识符处理对无转义名称跳过解码，对已为小写的属性跳过大小写替换；保留仅 ASCII 大小写折叠、转义拼写与自定义属性大小写敏感语义。
- 组件库验证现在分别检查 Quasar 发布的 LTR 与 RTL 样式，不再只把最大的 RTL 文件当作全部方向的代表。
- 属性筛选缓存最多 1024 个原始属性名（包括拒绝结果），淘汰后仍保留自定义属性大小写与转义匹配语义；新增可选的 `--cache-churn` 基准，覆盖 4000 个不同自定义属性。
- 程序化编译在异步处理前保存 syntax 与对象形式 stringifier 的钩子，避免后续替换方法改变待完成输出，同时保留显式 parser/stringifier 的优先级。
- 完整且未转义的单长度值复用原转换与告警保护，不再构建完整值解析树；非数值开头会跳过该探测，函数、字符串、转义单位及复合值仍走原解析路径。
- 复用转换器时只解析一次单位正则，不再对每个未缓存值重复序列化单位列表；共享正则缓存限制为 256 组配置，清理后已有转换器仍可继续使用。
- 创建编译器时保存嵌套 profile 配置、媒体路由边界及根样式注入过滤数组，避免调用方后续修改改变已有实例的这些设置；动态画布与根字号回调仍按文件执行。
- 修复巨大有限长度仅因按配置精度舍入而发生溢出的问题。
- Schema 提示及中英文配置指南明确：保留 rem 文字部分不等于保证页面缩放满足无障碍要求。
- 溢出长度现在按声明产生一次带位置告警，命中缓存时也会报告，warnings 门禁可拒绝保留但未转换的产物。
- CSS 转换在中间运算溢出时保留原始长度，不再输出无效的 `Infinity` 或 `NaN`；同一声明内其他正常长度仍会转换。
- 迁移指南新增与 `postcss-px-to-viewport` 的实测边界差异，包括混合单位边界表达式和大写单位。
- CLI 在读取 CSS 前会明确提示误传 PostCSS `plugins` 外壳的配置格式问题，并指引改用编译器选项文件。
- 视口高度估计在有限宿主读数运算溢出时回退为零，避免输出无穷大的 CSS 偏移；后续有效读数仍可正常恢复。
- Playground Worker 响应在交给界面前会校验结构；格式错误的结构化克隆数据现在会失败并清理，不再污染结果面板。
- 修复试验场移动端/桌面端示例使用过期预设参数名而无法编译的问题；测试直接通过真实 Worker 编译路径运行页面全部示例。
- 视口观察器初始化期间若宿主注册事件时触发取消，会在写入 CSS 变量前清理，避免遗漏取消状态。
- 试验场 Worker 消息解码失败时立即终止任务并清理计时器，不再等待超时。
- 支持冻结的正则匹配器，包括全局和粘连模式，不再尝试修改其只读 `lastIndex`。
- 文档 Markdown 请求增加十秒超时，并在切换页面时清理取消流程，覆盖响应正文长期无返回的情况。
- 接缝分析支持 PostCSS Document，各份样式表独立计算，避免跨 Root 混用层叠规则和自定义属性。
- 接缝分析现在会拒绝非法根字号，不再生成误导性的像素比较结果。
- 为复用编译器的属性分类缓存增加容量上限，避免生成大量不同自定义属性名时持续积累条目；清理后仍保持文本转换语义。
- 修复 URL 路径含类似注释的文本时隐藏后续兼容性结果的问题，同时覆盖转义或引号内的右括号。
- 兼容性审计现在会忽略 `url()` 资源中看似视口或容器单位的文本（包括转义的 `url` 写法），同时继续检测后续真实声明。
- 程序化编译在异步执行前保存源码映射选项快照，避免调用方后续修改影响待完成产物的映射格式或源码内容包含设置。
- 视口观察器销毁时使用初始化时的 AbortSignal，避免调用方替换 `options.signal` 后原信号监听器泄漏。
- 修复标识符含转义引号或斜杠时的兼容性漏报：转义标点不再被误当作字符串或注释起点，后续真实特性仍会被检测。
- 兼容性检测在没有标识符转义时跳过逐字符位置映射，同时保留原始诊断摘录与注释、字符串屏蔽行为。
- 程序化接口新增可选警告与兼容性门禁，失败时仍保留产物和诊断；未知浏览器目标不会通过显式启用的兼容性门禁。
- 新增 `compileAdaptiveCss()` 与可复用的 `createAdaptiveCompiler()`，支持可选处理参数、源码映射、警告和浏览器兼容性报告。
- 试验场改用独立 Worker 编译，超过 5 秒自动终止，修改输入与离开页面时清理任务；新增零配置、单侧边界、容器单位示例，以及耗时与状态反馈。
- 新增双语首页交互公式演示，更新文档主题配色、卡片、阅读排版与键盘焦点样式。
- 减少转换热路径上的缓存键重复序列化，同时保留动态画布与根字号的逐文件刷新。
- 修复宿主替换 VisualViewport 对象后旧监听器无法清理的问题。
- 打包检查现会验证导出入口、CLI、类型和必要文档，避免 npm 预检成功却缺少产物。
- npm 发布前置门禁现会在完整构建与测试后执行产物入口检查、运行时冒烟和性能预算检查，再执行安全审计。运行 `npm run prepublishOnly` 只验证，不会发布。

## 0.8.0 — 2026-08-27

### 正确性与诊断

- Profile 映射、组件库名称与浏览器别名现统一按对象自有键查找；`constructor`、`toString`、`__proto__` 等名字不会再借 JavaScript 对象原型伪装成不存在的 profile 或内置项。明确配置的 profile 名会在公开 resolver API 等全部路径中保持其精确身份。
- CSS 标识符转义现已在属性、函数、选择器、媒体特性、自定义属性引用与作者书写的单位之间统一规范化。`16p\78` 这类合法 dimension 会正常转换；转义标点、形似数字的 identifier、受保护函数与 hairline 则保留原始身份及拼写。
- 生成的居中列 gutter 引用现通过真实 `var()` 解析查找，不再做子串匹配；只有大小写精确一致的 `--adaptive-root-gutter` 会被修正或在连续性比较中剔除，相近变量名、字符串、fallback 与转义写法均不会误命中。
- 视口 observer 初始化现具备事务性：监听注册或首次 CSS 变量写入失败时，会在重新抛错前移除所有已注册资源；后续动画帧发布失败时也只销毁一次，不会留下持续报错的 resize 循环。
- CLI 的取值选项现也支持 `--config=adaptive.config.json`、`--from=src/app.css`、`--profile=pc`、`--targets=safari 14`、`--fail-on=any` 等通行的行内写法；空行内值会得到与缺少后续参数相同的精准错误。
- 空的 `profiles: {}` 现在遵循该可选字段的省略语义，恢复内置 app/desktop 画布。条件拼装配置时不必再为了避免“用空集合覆盖可用默认值”而特判零条目；任何非空映射仍是一套完整的作者 profile 定义。
- CI 现在会在项目声明的 Node 18 下实际执行已构建的 ESM、CommonJS、runtime 与 CLI 产物。开发用 Vitest 仍留在其支持的较新 Node 矩阵中，而对外运行时承诺有独立、可执行的冒烟校验。
- 导出的核心 `convertValue()` API 现在有省略/单侧 fluid 边界、多长度值、按文件画布函数及非法动态结果的直接合约测试，不再只依赖 PostCSS 插件入口的间接覆盖。
- `appPcPreset({ container: false })` 与 `{ fixedContainingBlock: false }` 不再仅因字段出现就启用整套 root 基础样式。root 缺席时这些能力本来就关闭，因此 false 现在保持“不注入全局 CSS”的默认；true 仍会启用 root，真正有意义的 root 专属配置也保留原有简写行为。
- 可选视口观察器现在用 `null` 表示“没有动画帧”，因此标准允许的句柄 `0` 仍能被合并与取消。销毁操作永久且幂等：竞态中已取得的监听回调无法再排入销毁后写入，手动 `update()` 在清理后返回 `null`。
- Profile 名及其在 `defaultProfile`、路由、组件库 `basedOn` 和 `withAtomicCss` 中的引用现在会拒绝首尾空白，避免创建会被 `@adaptive` 先裁剪、因而永远选不中的画布。内部空格仍受支持并有测试覆盖，`a 10` 这类现有名称会保持精确身份。
- 单独显式传入的路径限定组件库现在会得到与数组形式相同的缺失 `from` 告警。尤其是标量写法 `libraries: 'antd-mobile-2x'` 不再静默丢失用于区分 750 与 375 画布的路径；普通可由选择器识别的单库仍不依赖路径且不会告警。
- 主配置的 `routes.property` 现在会拒绝普通属性名和错误的自定义属性前缀，因为仅处理自定义属性的执行路径永远不可能访问它们。合法的未转义 `--...` 前缀仍区分大小写，也仍允许单独写 `--` 来明确认领全部自定义属性。
- `withAtomicCss` 现在对调用方传入的 `tokenPrefixes` 使用与主编译器相同的 CSS 自定义属性标识符语法；不可能匹配自定义属性的标点前缀会在包装器返回前被拒绝，不再留下死路由。
- 已发布的 JSON Schema 现在与运行时校验一致，会识别空 profile 集合与匹配数组、没有任何命中通道的独立组件库、缺少文件路径的独立 scoped 组件库，以及 `basedOn` 会被忽略的不换算组件库。这些约束仅在对应选项/对象已经传入时生效，可选字段仍保持可选。
- 可选视口运行时现在会在准确路径上拒绝非法 options 容器、未知字段及显式非法的 `target` / `window` / `document`；所有字段仍可省略，省略时仍选择浏览器默认值，而 `null` 不再伪装成省略或泄漏原生属性访问错误。
- `root.fixedContainingBlock: true` 现在要求至少一张 profile 设置 `rootMaxWidth`；否则根本没有居中列偏移需要修正，该选项只会把声明改写成留白永远为零的等价值。相同原则下，`designWidth: false` 的组件库现在会拒绝 `basedOn`，因为不换算的组件库从不读取它。
- 字符串形式的 `include` / `exclude`、文件路由、组件库路径及 `root.injectTo` 现在可跨 Windows `\\` 与 POSIX `/` 分隔符匹配；正则与谓词函数仍收到原始宿主路径，保留显式的平台相关逻辑。
- 函数型 `designWidth` 现在每个 profile/文件只解析一次，省略的 `textAnchorWidth` 会复用同一结果；带状态回调不再让流体项与静态项误用两张画布。非法返回值会指出源文件，动态 profile 宽度缺少 `from` 时也会像其他路径相关配置一样给出精准告警。
- 现有 `px-to-viewport-ignore(-next)` 与 postcss-mobile-forever 的 `mobile-ignore(-next)` 指令无需兼容开关即可继续生效，替换旧编译器时不会静默转换作者明确要求固定的长度；注释仍保留在产物中，并与原生指令一样保证二次编译。
- 宽度路由与死区间诊断现在会从媒体查询中安全投影掉合法的 `orientation: landscape` / `portrait`，不再连同可证明的宽度区间一起丢弃。仅含方向的规则保留继承画布，`orientation + min-width` 可选择正确设计稿；连续性/token 级联检查仍会拒绝猜测具体设备方向。
- `withAtomicCss()` 与 `defineConfig()` 不再要求传一个不承载任何信息的 `{}`；零参数形式直接使用既有默认值，原子 CSS 专属设置可直接写成 `withAtomicCss({ tokenPrefixes: [...] })`，包装真实配置时仍保留调用方的精确泛型类型。
- `defineLibraries()` 在零参数调用时也会遵循主 `libraries` 配置的自动默认；只有显式列表、`'auto'` 或 `false` 真正表达选择时才需要传入。
- `libraries` 与 `defineLibraries` 现在可直接接收一个内置名称或自定义条目，多个条目时仍用数组；默认 `'auto'` 与显式 `false` 含义不变，单库配置不再需要包一层数组。
- `propList`、`textProperties`、`selectorExclude` 与 `valueExclude` 现在同样支持直接传一个条目或只读数组；解析后仍统一为可变数组，编译器热路径无需增加分支。
- `routes` 现在可直接传入一条路由对象，多个有序路由时仍用数组；解析阶段会先统一归一化，匹配优先级与热路径行为不变。
- `withAtomicCss` 的 `tokenPrefixes` 现在可直接传一个字符串，多个自定义 token 族时仍使用数组。
- 可选的画布数值现在会严格区分“省略”与显式传入 `null`：`textAnchorWidth`、`fontFluidity`、`rootMaxWidth` 会在配置解析阶段直接报错，不再被静默视为未配置（或拖到 CSS 处理时才失败）。
- `defaultProfile: null` 与 `unitToConvert: null` 同样会被拒绝，不再触发省略字段时的默认值；错误的 JavaScript/JSON 配置不会再被空值回退掩盖。
- `observeAdaptiveViewport` 现在会把显式 `prefix: null` 判为无效，不再悄悄选用省略时的 `adaptive` 前缀；只有真正省略才使用默认值。
- `appPcPreset().rootInjectTo` 现在支持只读匹配器数组，与主配置的 `root.injectTo` 契约一致；共享的 `as const` 配置不再需要额外复制。
- `tsup` 内部仅用于构建的 `esbuild` 已固定到修复后的 0.28.2，关闭 Windows 开发服务器任意文件读取公告，同时不改变运行时依赖面。
- 只有宽度的 profile 现在可直接写 `designWidth`（`profiles: { mobile: 375 }`），也支持按文件解析的函数；只有确实存在覆盖项时才需要对象形式。
- `root.containerName` 现在会自行启用容器模式，不再要求重复填写 `container: true`；若显式冲突为 `container: false`，会在生成 CSS 前直接报错。
- profile 的 `query.name` 现在同样会推断 `type: 'container'`；命名容器查询只需名称和条件，显式冲突的媒体类型会被拒绝。
- 单条路由的诊断现在使用真实书写路径（`routes.media`、`routes.property`、`routes.file`），不再虚构 `[0]`；真正的路由数组仍保留下标定位。
- `from` 不再被写成无条件必填项：普通转换以及 selector/property/media 路由无需它；只有显式配置路径匹配却缺少源路径时，才会给出一次精准告警。Taro 指引也改为仅在构建确实使用 `@adaptive` 时要求 `query: false`，`minPixelValue` 的公开说明则统一为实现中的严格“小于”边界。
- 死区间发现现在归属到实际完成转换的声明，并携带真实的 selector/property 路由画布与媒体区间。嵌套子规则不再让未转换的父规则按错误画布告警；经 property 路由的 token 会按自己的 profile 判断并给出 property 路由建议；写在规则内部嵌套媒体查询中的声明也不再漏诊。显式 `@adaptive` 选中的画布不会再收到根本无法覆盖它的 route 建议。
- 性质测试现在覆盖省略边界、仅最小边界、仅最大边界在多种画布、正负布局长度、可缩放文字、边界两侧及完整二次编译下的行为。
- 当前 profile 的输出单位不再被同一 profile 重新当作设计稿输入单位读取。即使 `unitToConvert` 同时列出 `vw`/`vi`/`cqw`/`cqi`，裸值 `strategy: 'viewport'` 产物也保持幂等；配置成不同目标单位时，显式的单位间转换仍然有效。
- `LibraryAdaptation` 现在直接表达两种合法形状：继承条目只要求 `extends`，独立自定义库仍要求 `name` 与 `designWidth`。显式把 `{ extends: 'vant' }` 标成该类型时，TypeScript 不再强迫填写运行时、Schema 与文档本就会继承的字段。
- 死区间诊断现在要求转换确实新增了 `clamp()`/`min()`/`max()` 边界。用 `calc(<rem>)` 标记的静态文字和用 `calc(<视口长度>)` 标记的无边界输出，并不是被 profile 流体区间钉死的表达式，因此不再给出误导性的画布路由告警。
- 经不同倍率组件库画布路由的静态文字，在 `rem` 同时作为输入单位时现在仍保持幂等。生成结果携带结构化的 `calc(<rem>)` 标记，消费端第二次构建不会再把 `2rem` 静默重复锚定成 `4rem`。
- 默认策略的无边界输出现在使用数值等价的 `calc(<视口长度>)` 写法，静态文字使用 `calc(<rem>)`。即使没有 fluid 边界，二次构建与连续性门禁仍能识别生成结果；显式 `strategy: 'viewport'` 继续为兼容性输出裸值。
- 十六进制转义续段判断现在严格使用 CSS 定义的五个空白字符。垂直制表符等仅被 JavaScript 视为空白的字符，不会再在值解析器切分后藏住后方本应转换的真实 dimension。
- Profile 的 `fluid`、`fluid.minWidth`、`fluid.maxWidth` 现均可独立省略：无边界输出首选视口表达式，只写一端输出 `min()`/`max()`，两端都写仍输出 `clamp()`；运行时只校验实际传入的值。
- `root.selector` 现可省略并解析为 `:root`；只写 `root: {}` 即可启用默认基础样式，不必重复填写编译器已经确定的选择器。
- 主插件现在也支持用 `root: true` 无信息简写启用默认 `:root` 基础样式，与 `appPcPreset` 一致；只有确实需要定制时才使用对象形式，省略或 `false` 仍不会注入任何内容。
- `rootValue` 继续保持可选，并额外支持 `(context: { file: string }) => number`，按源文件解析一次正数有限标尺。同一 monorepo 可以用一份配置同时编译 `10px`/`rem` 的旧子应用与 `16px`/`rem` 的新子应用；缺少 `from` 时只给出一次精准告警，不会让普通转换整体失败。
- `appPcPreset({ root: true })` 及其 root 专属配置现在无需 `rootSelector` 即可启用同一套 `:root` 基础样式；只有一张自定义 profile 时也会自动推断为 `defaultProfile`，多张非 `app` profile 的歧义仍会明确报错。
- CLI 现支持通行的 `--` 选项终止符；其后所有参数都严格按文件路径处理，因此以 `-` 开头的合法样式表文件名不再被误报为未知选项，也不会在前置参数出错时被误当成输出格式标志。
- 组件库定义现在会在两套路由静默共享一张被覆盖的派生画布之前拒绝重名；库名不能再藏有首尾空白，class/token 前缀也会按语法校验，避免非法前缀静默永不命中或认领全部自定义属性。
- 兼容性检测现在会在保持源码偏移的同时屏蔽注释与带引号字符串；文档注释、`content` 文本或字符串数据里的特性示例不会再伪造浏览器兼容缺口，导致已启用的 CLI 兼容门禁误失败。
- 兼容特性边界现使用 CSS 精确定义的五字符空白集合；NBSP 等可用于 identifier 的 Unicode 空格不再伪造自定义属性、容器、逻辑属性或媒体范围语法要求。
- 单声明连续性检查的快速路径现在也会识别大小写不敏感的 `VAR()`，与 token 解析器保持一致，不再跳过用大写函数书写的断点 token 变化。
- `--css` 现在会把完整连续性问题与浏览器兼容证据连同编译器警告一起写到 stderr；stdout 仍是可管道处理的纯 CSS，而门禁失败不再把可操作详情压缩成一个无法定位的计数。
- 连续性分组现在遵循 CSS 普通属性名的 ASCII 大小写不敏感语义；断点两侧写成 `FONT-SIZE` 与 `font-size` 的声明会作为同一个级联属性比较，不再静默绕过接缝检查。
- 兼容性检测现在能识别非 ASCII 与转义形式的自定义属性声明；`--间距` 等合法名称及其转义写法不会再向旧浏览器审计隐藏真实的 CSS Variables 依赖。
- 视口/容器单位兼容检测现在与编译器共享完整 CSS 数字文法及标识符边界；带符号和科学计数法长度可以检出，非法小数及转义/国际化标识符中的疑似单位文本不会被误报成浏览器要求。
- 数值幅度超出 JavaScript 双精度范围的 CSS number 现在会保持原文，不再被改写为无效的 `Infinity<单位>` 声明；数值型 `convertLength()` API 会用明确错误拒绝非有限输入。
- 既有 `preserveOriginal` fallback 对现在要求转换 twin 的重要性至少不弱于原声明；普通 twin 不会再藏在 `!important` 像素 fallback 后面失效。普通属性 twin 按大小写不敏感匹配，自定义属性仍保持大小写敏感。
- 连续性算术现在强制遵守 CSS `calc()` 二元 `+`/`-` 两侧空白规则；`8px+16px`、`8px +16px` 等浏览器会拒绝的表达式保持未知，不再生成貌似可信的假长度与错误接缝发现。
- 连续性 tokenization 现会区分 CSS 空白与 JavaScript 更宽泛的 Unicode 空白集合；用 NBSP 分隔的非法算术保持未知，不再被求成貌似可信的长度或拆成虚假的 shorthand 分量。
- 连续性求值不再把元素相对的 `em` 当作根相对的 `rem`；缺少继承字号级联时不存在可信像素值，因此混合编译公式/`em` 的断点值会被拒绝，不再伪造接缝诊断。
- shorthand 组件扫描现在要求圆括号、方括号、花括号、字符串与注释正确成对；非法声明会保持为一个不可求值整体，不再暴露貌似合法的片段并伪造连续性问题。
- 生成 foundation 现在带明确的开始/结束标记；第二趟只跳过生成区间，因此预编译依赖后拼接的应用 CSS 仍会转换，旧版仅开始标记的产物则继续保守保护到文件末尾。
- math、`var()` 与 `env()` 兼容检测现在遵守非 ASCII 及普通 CSS 标识符边界；作者标识符内部形似函数的后缀不会再伪造不支持特性发现。
- token 替换器现在会在查找 `var()` 时跳过带引号字符串、注释与转义；作为 CSS 数据使用的形似函数文本会逐字保留，不再被误当成真实自定义属性引用。
- `var()` 替换现在会把首参数校验为一个完整 CSS 自定义属性名，并支持合法转义；非法名称不会再被当成“仅仅未声明”而错误启用浏览器本应拒绝的数值 fallback。
- token 引用现在会把 `var()` 自定义属性名周围已闭合的注释按 CSS 空白处理，同时继续拒绝把名称截成多个 token 或未闭合的注释。
- `var()` 参数分割不再把自定义属性名中的转义逗号、或名称后注释里的逗号误当成 fallback 分隔符。
- selector 与 token 分析现在共享同一套 CSS 标识符转义解码器；等价的字面量/转义自定义属性名会解析到同一 token，同时保持自定义属性大小写敏感，并正确解码以 CRLF 终止的十六进制转义。
- CSS 转义解码现只吞掉 CSS Syntax 定义的五种空白字符；不换行空格等 Unicode 空格会继续作为 identifier 码点保留，不再把不同 selector 或自定义属性折叠成同一个解码名称。
- fixed 根列修正现在会用共享 CSS 数字文法识别零 inset 与满宽；`.0px`、`1e2%` 等小数、带符号和科学计数法等价写法会与 `0px`、`100%` 得到相同修正。
- pattern 匹配现在会在每次测试后恢复调用方正则的 `lastIndex`；全局/粘连正则仍会稳定地从头匹配，但同一配置或正则在别处复用时不再观察到插件污染的状态。
- 新增 `adaptive-matrix --json`：单个或多个文件统一输出一份带版本号的报告，包含结构化声明变化、告警、断点连续性问题、浏览器兼容缺口与汇总计数。失败仍输出 JSON 并以 1 退出，`--all` 控制是否包含未改动声明，报告类型和版本常量也作为公开 TypeScript API 导出。
- 新增可选 CLI 质量门禁 `--fail-on warnings,continuity,compatibility`（或 `any`）。有效产物只要命中所选策略，便会在 `--css`、`--json` 等流程中非零退出；JSON 会区分编译成功与门禁通过。
- `+16px` 这类显式正号长度现在会编译成有效的 `clamp()`，不再输出无效的 `+clamp(...)`。
- 媒体路由与 token 诊断现与转换器共享完整 CSS number 语法：可识别科学计数、显式正负号、无单位零，以及大小写不敏感的媒体特性/单位；错误小数和非有限指数仍按未知处理，不会把 `NaN` 污染进宽度区间。
- 媒体路由现只识别 CSS 定义的空白来分隔特性、运算符与 `and`；查询形 identifier 内的 Unicode 空格不再让非法查询认领设计画布并缩放整组规则。
- 媒体路由与 token 诊断现支持 Media Queries Level 4 range context：可识别 feature-first、value-first、链式及等值 width 比较，也允许 token 之间的注释；方向矛盾的链及不支持的特性仍会被明确视为不可读，兼容报告则会为旧目标浏览器标出这项新语法。
- 互相矛盾的媒体边界现会形成明确的不可达区间：没有媒体路由认领，已转换规则只给出一次可操作告警，不再错误宣称 clamp 被钉在一个首尾倒置的宽度范围内。
- Profile 查询条件在写入生成的 at-rule 前会执行与具体语法版本无关的结构校验。未闭合的字符串、注释或 component-value 块，以及顶层边界字符，不再能够逃逸包裹层或吞掉后续样式；新的嵌套查询语法仍可正常使用。
- Root selector 在包进 `:where()` 前也会通过同一套与语法版本无关的结构守卫。未平衡的字符串、注释、圆括号、属性方括号或未转义规则花括号，不再能够破坏生成的 foundation。
- Token 替换与连续性诊断现会先按 `!important`、再按源码顺序决定胜者。带层 token 或分散在不同全局选择器写法中的 token 会被保守拒绝而非猜测，同时正确解析大小写不敏感的 `var()` 与字符串内括号。
- 连续性算术现会在加减、乘除及数学函数中携带 CSS number/length 维度。维度混用的无效表达式及非零裸数字会返回「未知」，不再生成看似可信的假像素；数字 tokenization 也与转换器共用严格文法。
- 连续性 tokenization 现支持 CSS 注释及全部 CSS 空白字符；shorthand component 拆分会保留字符串、注释和嵌套括号块，不再从内部空格处切断。
- 选择器路由与 specificity 分析现会统一跳过 CSS 注释。注释中的组件库类名、逗号、伪类或 ID 不再能够误选画布，或虚构 selector list / specificity 诊断。
- 选择器分析现会解码转义伪类名，并正确处理属性中的转义右方括号。`:n\\6ft()` 这类等价写法不再能够绕过 `:not()` / `:has()` 路由语义或产生错误 specificity。
- 标准属性名与 `propList` 模式按 CSS 规则不区分 ASCII 大小写，`FONT-SIZE` 仍会保留可缩放的 `rem + vw` 文字公式；自定义属性过滤与路由则按 CSS 规则继续区分大小写。
- 长度匹配现会遵守非 ASCII 及转义 CSS identifier 的边界。identifier 内形似 dimension 的文本（包括 value-parser 拆出的十六进制转义续段）会保持原样，不再被局部替换成函数。
- `atRuleName` 在校验与匹配前统一规范化，首尾空格不再出现「校验通过、实际匹配不到，最后被浏览器整块丢弃」的情况。
- `unitToConvert`、`root`、`root.selector` 的运行时形状错误现在会给出明确配置提示，不再泄漏底层 `trim` 异常。
- 运行时配置校验现已覆盖全部集合及嵌套 query/library 结构。非有限阈值不会再输出 `NaNrem` 或 `Infinitypx`，缺少查询条件不会再输出 `@media undefined`；非法策略、路由边界、CSS 标识符和基础样式字符串都会在读取任何样式表前按精确配置路径报错。
- JavaScript 配置的未知字段现会在每一层被拒绝，与公开 Schema 的封闭对象规则一致。相近拼写会得到明确字段建议，不再被混入解析结果后静默忽略。
- `appPcPreset` 与 `withAtomicCss` 现会在构造配置前校验自身公开输入。helper 字段拼错、预设边界非有限、互相矛盾或形状错误的 root 配置、包装器集合形状错误及非法 token 前缀都会在调用处失败；重复自定义属性前缀按大小写敏感语义去重。
- 配置生成的 CSS 名称现按语法校验，不再只检查非空：输入单位必须是标识符，root 层必须是单个点分层名，容器与查询名必须是非保留 custom-ident。已接受的配置不会再产出非法 `@layer`、`container-name` 或 `@container` 语法。
- 转换器缓存键改为元组编码，不再用空格拼接。包含空格的 profile 名和声明值不会再与另一组画布/文字/值参数撞键并复用错误公式。
- 可选 VisualViewport 运行时不再把双指缩放误判成软键盘：键盘遮挡会按当前 `scale` 下的布局高度计算。非法变量前缀会在注册监听前报错，视口指标未变化时也不会因噪声 resize 重复写 DOM。
- 居中列 fixed 修正现按声明块内实际生效的 `position` 判断，同时遵守顺序与 `!important`；前面的 `position: fixed` 兜底不会再误改后续已覆盖为 `static` 或 `absolute` 的元素。
- CLI 会在读取任何内容前拒绝歧义输入：显式 stdin（`-`）不再因与其它输入混用而隐藏或被误当文件，一个 `--from` 也不会再抹掉多个文件各自用于路由的路径身份。
- `--profile` 现通过覆盖副本生效，不再修改配置模块的 default export，因此冻结配置可正常使用，ESM 缓存也不会把一次调用的选择泄漏到下一次。多文件运行改为逐个读取并编译，不再把整批源码同时留在内存中。
- 浏览器目标现要求格式正确的点分版本及符合「最低支持版本」语义的分隔符。重复别名不再受参数顺序影响，而会保留最老版本；程序化兼容性审计也会拒绝畸形版本，不再把错误版本段当成 0 比较。
- 构建清理现在只在 tsup 启动前执行一次，ESM、CommonJS 与 CLI 并发 worker 不再互相删除刚生成的声明文件。
- 新增正负号、标准属性大小写、自定义属性大小写及 JSON 风格非法选项的回归测试与一致性夹具。

### 安全与维护

- media route 与自定义属性前缀匹配不再在逐规则/逐 token 路径分配 `some()` 回调，补齐零闭包路由热路径，同时保持按声明顺序首个命中的语义。
- 热路径 matcher 现直接遍历只读 route 数组，不再为每条规则复制数组或分配 `some()` 回调；解析后的 route 也会复用不可变激活对象。吞吐基准仍会以 PostCSS 解析/输出成本为参照，同时测量纯转换及启用全部内置组件库 route 的开销。
- VitePress 1.6.4 现通过其兼容范围使用已修复的 Vite 6.4.3，在不采用 VitePress 2 alpha 的前提下移除文档工具链中的 Windows 路径穿越高危漏洞及旧版 sourcemap 路径穿越。
- CI 与发布流水线新增基于官方 npm registry 的中危及以上审计门禁。剩余一条低危 esbuild 公告只影响其开发服务器；本项目仅通过构建/测试 API 使用该依赖，不会暴露该服务器。

## 0.7.0 — 2026-08-11

### JSON 配置

- 命令行现在接受 `adaptive.config.json`，编译前会移除 `$schema` 元数据；JSON 损坏或顶层不是对象时，会给出清晰错误而不暴露内部调用栈。
- 发布的 JSON Schema 已描述 `$schema`，编辑器可以用与文档站相同的选项模型补全并校验 JSON 配置。
- JSON 文件通过 `readFile` 与 `JSON.parse` 读取，继续支持 Node 18；需要正则或判断函数时仍可使用 JavaScript 配置。

### 质量门禁

- `npm run check` 与 CI 新增 ESLint、Prettier、VitePress 源码类型检查、文档站构建，以及更严格的覆盖率阈值。
- 新增基于比值的性能预算，用编译器耗时对比 PostCSS 自身的解析与打印耗时，避免共享 runner 上不稳定的绝对毫秒限制。
- 补齐求值器、选择器扫描器、CLI、Schema、stdin、兼容性与错误路径测试；覆盖率达到语句 98.10%、分支 93.59%、行 99.35%。
- 新检查发现并修复两处真实问题：通过不受支持的路径序列化 value-parser 节点，以及未知值可能在生成产物中被写成 `[object Object]`。

## 0.6.0 — 2026-08-11

### 断点终于有了自己的画布

- **新增 `media` 路由通道。** 一份响应式样式表，是一个文件里装着两份设计稿：手机端那些数字量自 750 的稿子，`@media (min-width: 1024px)` 里那些量自 1440 的稿子。CSS 里没有任何地方写着这件事，而在此之前也没有任何地方能写。`routes: [{ media: { minWidth: 1024 }, profile: 'pc' }]` 把这段断点交还给它原本那份设计稿。
- 这不是「差一点」，所以它值一个特性而不是一句备注。750 画布、流体区间上界 600px 时，`@media (min-width: 1024px) { .hero { padding: 40px } }` 编译出的是 `clamp(17.07px, 5.33vw, 32px)`——而这条规则只在 1024px 以上生效，那已经越过画布停止缩放的地方，所以在它生效的每一个宽度上，`clamp()` 早就顶死在上界了。这个 padding 永远是 32px。编译器跑过了，产物看着也像编译过，可没有一个值动过。
- **匹配靠的是蕴含关系，不是文本。** `{ minWidth: 1024 }` 认领的是「不可能在 1024px 以下生效」的规则，因此 `screen and (min-width: 1200px)` 和 `(min-width: 1024px) and (max-width: 1600px)` 都算数，嵌套也天然成立——嵌套本来就是「且」。拿 params 当字符串比对会漏掉那个 `screen and`；而画布是「这条规则够得到哪些宽度」的事实，不是「这个查询是怎么拼写的」的事实。
- **媒体查询里的 `rem` 与 `em` 一律按 16px 折算**，既不看 `rootValue`，也不看根元素的字号。查询在任何声明能改动 `font-size` 之前就要求值，因此它不能依赖它自己所筛选的那一层层叠：哪怕样式表里 `html` 写着 `62.5%`，`64rem` 也还是 1024px。这是关于查询本身的事实，不是关于页面的假设；而只认 `px` 会让所有 Tailwind 与 UnoCSS 项目——它们的断点全写成 `rem`——变成读不懂的东西。断点连续性检查用的是同一个解析器，覆盖面一并扩到了这里。
- 编译器读不懂的查询——带逗号、带 `not`、带 `only`，或者任何非宽度特性——**谁都不认领**。这是「拒绝作答」，不是「全都匹配」：按一个没人核对过的条件去改派规则，正是画布错误产生的方式，而不是被抓住的方式。`@container` 同样从不参与计数；它约束的是元素，而 `vw` 从来就与元素无关。
- `selector` 路由仍然高于宽度区间：组件库在任何视口宽度下都画在它自己那张画布上，跨过一个断点并不改变这个组件出自哪份设计稿。一条路由可以同时写上两者——`{ selector: ['.van-'], media: { minWidth: 1024 }, profile: 'pc' }`——这才是「这个组件在桌面断点处被重画了」的说法。
- 空区间（`{}`）和反向区间（`minWidth: 1024, maxWidth: 600`）都是配置错误，直接抛出。前者匹配样式表里的每一条规则，等于用一种更慢的方式改 `defaultProfile`；后者一条都匹配不到。两者看上去都像能用的配置，所以都不能留给用户从产物里去发现。

### 死区告警

- **上面这些你一条都不用先知道，也能发现问题。** 只要一条规则确实换算了长度、而它的生效区间又整个落在所属画布的流体区间之外，编译器就会说出来，把两段区间的数字都报出来，并给出能修好它的那条路由。这是算术而不是启发式——两个区间压根不相交——而它正是多画布模型本来要防的那种失败，只不过是从模型唯一没盯住的那道门进来的。
- 每个文件里，同一张画布配同一段区间只报一次，而且**只对真的换算了东西的规则报**。一段只改 `display` 和 `color` 的断点是再普通不过的 CSS，里面没有长度，也就无所谓常量不常量；对这些报警会把真正该看的那条淹掉。第一版就是这么干的，那条测试正是从这里写出来的。
- 建议的修法会随情况变：当画布是被 `selector` 路由定下的，消息会改成让你写 `{ selector: […], media: … }`——因为光一条 media 路由会输给 selector 路由，照做等于什么都没做。不管用的建议，比没有建议更糟。
- **`appPcPreset` 现在不只写自己的断点，也读它。** 这个预设本来就宣称「桌面设计稿从 768px 起接管」——那个数字设定了两个 profile 的 `query`，所以 `@adaptive pc` 块能被正确地包上媒体查询。它只是从来不*读*媒体查询，于是手写的 `@media (min-width: 768px)` 块仍旧按手机画布编译：预设自己跟自己打架。现在两个方向都路由了，而且 `max-width` 那一半是显式写出来的、没有留给 `defaultProfile` 兜底——这样即便有人把预设摊进一份桌面优先的配置里，这一对路由说的仍然是它字面上说的那件事。
- 新增两个一致性用例 `breakpoints/media-route` 与 `breakpoints/dead-band`，覆盖「认领什么、拒绝什么，以及同一段区间只报一次」。

### 选择器路由

- 修正 **`:not()` 与 `:has()` 的参数在决定规则落到哪块画布**。`.page-hero:not(.van-cell)` 修饰的是页面元素——恰恰是那些*不是* Vant cell 的元素——却因为文本里某处出现了 `.van-cell` 这串字符而被送去 Vant 的 375 画布。这类规则里每一个长度都会变成应有尺寸的整两倍，而且是静默的。路由现在读的是选择器的**主体**：`:not()` 与 `:has()` 的参数指的是被修饰元素之外的另一个元素，在匹配之前就被摘掉；`:is()` / `:where()` / `:matches()` / `-*-any()` 的参数保留，因为那些确实是主体的候选项。在 11 份已发布组件库样式表、22,761 条规则上实测，其中 1,035 条含 `:not()` 或 `:has()`：画布归属**零**变化——库 CSS 的自身前缀总是落在被摘掉的部分之外。这个修复不付任何代价，堵上的是应用代码会一头撞进去的洞。
- **`:is()` / `:where()` 内部跨画布的列表现在会告警。** 此前文档把它写成「查不出来」。`:is(.van-cell, .page-hero) { padding: 16px }` 是一条声明想要两个画布，和逗号列表是同一个问题、只深了一层括号，而它一直在无声通过。
- 告警会说出**拆分要付什么代价**，是算出来的而不是丢给读者：`:is()` 以其最高的那一支的特异度匹配每一支，所以把分支拆开只有在它们本来就一致时才是免费的。不一致时，告警指明降幅——`:is() matches every branch at its highest, 1-1-0, so ".page-hero" would drop to 0-1-0`。这才让建议可执行；一个照做之后会悄悄改变层叠的告警，是会被学会无视的告警。每条规则只报一次：剩下的列表成因相同、修法相同。
- 新增 `src/core/selectors.ts`，零依赖：`splitSelectorList`、`routingSelector`、`nestedSelectorLists`、`specificity`、`compareSpecificity`、`formatSpecificity`、`splitIsSpecificityNeutral`。拆分识别字符串与属性选择器，`[data-x="a)b"], .c` 会正确拆成两支——只数括号会拆错。特异度按 Selectors Level 4 计算，含 `:where()` 计零、`:is()` / `:not()` / `:has()` 取最高分支、`:nth-child(n of S)` 计一个伪类加 `S` 的最高分支，以及四个单冒号历史写法的伪元素（`:before`、`:after`、`:first-line`、`:first-letter`）按元素计。新增 40 条单元测试。

### 浏览器特性支持审计

- **`:has()` 纳入审计。** 它是日常在用的选择器里最新的一个，四个引擎之间隔了好几年——Chrome 105（2022 年 8 月）、Safari 15.4，而 Firefox 要到 121（2023 年 12 月）。这是表里首尾差距最大的一行，而且失败发生在选择器层面：整条规则作废。一份在 Chrome 和 Safari 上验收过的样式表，在旧一点的 Firefox 里可能正无声地少着若干条规则。编译器不产出 `:has()`，它从你自己的 CSS 或组件库进来、原样穿过这一趟——而这正是「读**产物**」的审计能看见它的原因。与原生嵌套不同，它在文本里毫不含糊，所以是识别出来的，不是猜的。
- `scripts/capture-compat.mjs` 加入 `css-has` 并重新烘焙进 `src/core/compat-data.ts`；仍然不引入任何运行时依赖。

### 文档站

- **文档以站点形式发布**，中英双语，地址 `https://moresyl.github.io/postcss-adaptive-matrix/`。它是从仓库本身构建的，而不是从仓库的一份拷贝：`srcDir` 就是仓库根目录，所以 `docs/README.md` 里那条指向 `../conformance/README.md` 的链接，在站点上和在 GitHub 上是同一个道理，没有一个文件因此挪位置。搜索、暗色模式、编辑本页、最后更新时间一并到位。
- 唯一需要设计而不是配置的，是语言切换。VitePress 按页面**改写后**的位置解析相对链接，于是中文页顶部那句 `[English](./README.md)`——在 GitHub 上完全正确——在站点上会解回中文页自己。所以链接改为在**仓库里**按源文件路径解析，输出为绝对站内地址。页面再怎么挪，链接也断不了。
- **全部配置项以数据形式发布**在 `/schema/options.json`：一份 JSON Schema 2020-12 文档，含每个配置项的类型、允许值、取值范围与默认值。像「`precision` 是不是整数、上限多少」这种问题，散文不是合适的载体。
- 有两件事让它不至于沦为摆设。它的属性表按源接口做了类型标注，所以存在却没被描述的配置项会让 `tsc` 失败，被描述却已经不存在的同样如此。默认值则是在生成这份文件时从 `resolveOptions()` 里读出来的，不是抄的——代码里的默认值一变，这里在同一个提交里跟着变。还有一条测试补上了类型系统看不见的那一环：配置参考里印着的默认值，和上面这个是不是同一个。它当场查出一处不一致——`unitToConvert` 解析后是列表，而两份参考表都写着 `'px'`。
- 中英文都装在同一份 Schema 里：`description` 是英文，`x-description-zh` 是中文。类型不是翻译。`x-also` 则标注那些只有 JavaScript 能写、JSON 表达不了但配置文件接受的形式——正则、判断函数。
- **编译器就跑在读者自己的浏览器里。** 插件只有一个运行时依赖，链路上没有任何 Node API，所以发布出去的那份源码被直接引进试验场页面，PostCSS 在客户端运行。背后没有服务，也没有任何需要跟着版本同步的东西。配置栏按 JavaScript 表达式求值而不是按 JSON 解析，因为值得一试的东西有一半是 JSON 写不出来的。六个示例，每个对应一个真的有人问的问题，其中一个专门演示会触发告警的配置。
- **每种语言各有一份 `llms.txt` 与 `llms-full.txt`**，遵循 llms.txt 约定；每一页的原始 Markdown 也在「页面路径 + `.md`」上直接提供——于是「把这一页交给模型」是一次抓取，而不是把渲染后的 HTML 再刮回文本。大纲上方的三个按钮用的就是它：复制为 Markdown、查看原始 Markdown、把这一页带进对话。
- 新增 `docs/agents.md` / `docs/agents.zh-CN.md`，把这些入口集中到一页，并写清提示词里该有什么：一个默认「全局只有一个设计稿宽度」的 Agent，写出来的配置能编译，但是错的。
- 由 `.github/workflows/docs.yml` 在推送到 `main` 时部署。`tsconfig.json` 现在也覆盖 `docs/.vitepress`，站点自己的源码和其余代码一起做类型检查。

### 文档

- 新增 `scripts/check-docs.mjs`，接进 `npm run check`，因而也进了 CI。它校验每个本地链接可达、每个 `#锚点` 对得上真实标题，以及**每一页都有另一种语言的对应页、并且在顶部链接过去**。真正划算的是最后一条：两种语言是分别写的而不是翻译的——这既是它们读起来顺的原因，也是其中一份可以悄悄不存在的原因。它当场查出两处：`CODE_OF_CONDUCT` 只有中文内容却挂着英文文件名，示例 README 把两种语言堆在同一个文件里。
- 锚点检查按 `/\r?\n/` 切行。Windows 上这里每个文件都是 CRLF，行尾的 `\r` 会让非多行模式下的 `$` 匹配不上，而 `.` 也不匹配它——于是直接按 `'\n'` 切会一条标题都找不到，把仓库里所有锚点全报成坏的。这不是假想，这是这个检查第一版的真实表现。
- `examples/app-pc` 新增 `adaptive.config.mjs`，单独导出配置本身；`postcss.config.mjs` 改为 import 它。两个 runner 要的形状不同——PostCSS 要 `{ plugins: [...] }`，CLI 要配置本身——而把画布写两遍正是两边最后描述出两张不同设计稿的方式。示例 README 现在给的是真能跑起来的命令，含 `--targets`。

### 性能

- `routingSelector` 对不含 `:` 的选择器立即返回，而绝大多数选择器都不含；同时这道还原挪进了 `forSelector` 内部——在「压根不做选择器路由」的那道判断之后。配了 `libraries: false` 的项目现在为这个特性一分钱都不用付。utility-framework 语料上的编译器耗时：4.38ms → 3.58ms。

### 一致性套件

- 原子化用例补入 `space-x-4` 与 `divide-y-2`。此前整个套件里**一个功能性伪类都没有**，而这两个是日常工具类。同一个工具类，三种毫不相干的真实形状：Tailwind 4 把整体裹进 `:where(...)`，UnoCSS wind3 写成扁平的 `> :not([hidden]) ~ :not([hidden])`，UnoCSS wind4 直接产出带 `&` 的*原生嵌套*。现在同样的 2px 在三者中都得到一模一样的 `clamp(1.70667px, 0.53333vw, 2.56px)`，`border` 的细线三者都保住，wind4 的原生嵌套原样写回。
- 新增 `test/idempotence.test.ts`：**把编译产物再编译一遍，什么都不该变**，九种配置 × 八份样式表。一致性套件本来就对每个用例断言了这一点，但只在那个用例自己声明的选项下断言——没被覆盖的是那些会改变产物**形状**的开关之间的组合，而第二趟要扛住的正是形状。第二趟不是假想：一个发布预编译 CSS 的包，会再经过使用方应用的构建流水线；monorepo 里先编译共享组件库、再编译引用它的应用，也是同一回事。
- 真正需要钉死的是原子化模式配上静态字号。原子化会把 `rem` 加进 `unitToConvert`，而文字通常写成 `rem + vw`——那个 `vw` 就是告诉第二趟「这个值已经编译过了」的记号。`fontFluidity: 0` 之下没有 `vw`：`32px` 变成光秃秃的 `2rem`，下一趟会把它当成设计稿长度再换算一次。它扛住了，但没有任何东西在守着它——`rootValue` 两端用的是同一个，写的时候 ÷16、读的时候 ×16，互为精确的逆运算，这个值是它自己的不动点。这是算术的性质，不是谁写下来的规则；这道除法的任意一端一挪，失败都是静默的：不报错、不告警，只是每存一次盘文字就小一点。

### 类型

- 所有取数组的选项现在都接受 `readonly` 数组：`routes`、`libraries`、`textProperties`、`propList`、`selectorExclude`、`valueExclude`、`include`、`exclude`、`root.injectTo`，以及路由与库定义里的 `file` / `selector` / `property` / `prefix` / `tokenPrefix`。`unitToConvert` 本来就接受，这让 API 自相矛盾：用 `as const` 写的配置——在 TypeScript 里这是最自然的写法——除了那一个字段之外每个都报类型错误。

## 0.5.0 — 2026-08-10

### 文档

- **全部描述文件改为中英双语**：`X.md` 为英文、`X.zh-CN.md` 为中文，每页顶部互链。覆盖 README、docs 全部 11 篇、一致性套件说明、贡献指南、安全策略与本文件。英文不是把中文机翻一遍——同一件事在两种语言里该由不同的句子承担，所以两版是分别写的。
- 英文版配套的图放在 `docs/assets/en/`，是按英文重画的：中文标签短，直接替换会撑出卡片，`tracks the viewport` 还会被虚线从下面穿过。
- 画布模型图移除第三方库名。模型与是哪家库无关，「移动端组件库 / 375 设计稿」「桌面端组件库 / 无设计稿 · 真实像素」说的是同一件事，而整张图从头到尾只在讲本编译器自己。

### 浏览器特性支持审计与降级

- 新增 `auditCompatibility(css, targets)`：给一串「浏览器 + 你要支持的最低版本」，逐条列出产物里超出目标的语法、不支持时丢掉的东西、以及关掉它的开关。审计读的是**编译产物文本**而不是配置——这是让审计和输出永远不会走偏的唯一做法，经预设、经组件库路由、经手写 CSS 进来的特性一样能看见。同时导出 `detectFeatures`、`COMPAT_FEATURES`、`FEATURE_SUPPORT`、`compatFeature`，类型齐全。
- 命令行新增 `--targets "safari 14, ios_saf 13"`，在对照表之后输出 `needs` 段。四行的顺序是有意的：先说丢什么，再说换成什么——「iOS Safari 13 太老了」单独拿出来没法行动，而 CSS 支持缺口真正要紧的一直是**跟着一起消失的有多少**。CSS 不报错，它丢弃：值读不懂丢一条声明，选择器读不懂丢一整条规则，`@` 规则读不懂丢一整块，全程无声。特性表因此按「丢得多」排序，不按「谁更新」。
- 覆盖 11 项：`@layer`、`:where()`、`@container` / 容器单位、`clamp()` / `min()` / `max()`、`vi`、逻辑属性、`var()`、`env(safe-area-inset-*)`、`vw`、原生嵌套。原生嵌套本编译器不产出（读进来的嵌套原样写回），列入是因为它是会被问到的问题，而靠模式识别它会把普通 CSS 误读成嵌套——报错比不报更糟。
- 版本数据由 `scripts/capture-compat.mjs` 从 caniuse-lite 烘焙进 `src/core/compat-data.ts`，caniuse-lite 只是 devDependency，插件不增加任何运行时依赖，离线可审计。「某特性从哪个版本开始能用」是不会再变的历史；**使用率则故意不看**——0.4% 的用户算不算数是关于你的项目的决定，`browserslist` 已经是回答那个问题的地方。取的是「从此再没断过支持」的版本而非第一个出现 `y` 的版本（个别特性发布后被撤回过）。
- `--targets` 收显式的名字与版本，不收 browserslist 查询：查询要拉进 browserslist 包，回答的是关于用户的问题而不是关于这份样式表的问题，而且**同一条查询会随数据库更新而改变含义**——代码一个字没动，下个月构建就红了。不认识的目标名报错并以 `1` 退出，不静默跳过：被悄悄丢掉的目标比没有审计更糟，因为它读起来像通过了。
- caniuse 没有 `:where()` 和 `vi` 的独立条目，改用 `:is()` 与 `svh`/`lvh`/`dvh` 条目，并在数据里标出这是代理而非实测。两组都出自同一节规范、同批发布（`:where()` / `:is()`：Chrome 88、Firefox 78、Safari 14）。
- 新增 `root.logical`（预设字段 `rootLogical`）：设为 `false` 时基础样式改写 `width` / `margin-left` / `margin-right` / `max-width`，而不是 `inline-size` / `margin-inline` / `max-inline-size`。**这是审计过程中发现的真实缺口**——逻辑属性是编译器产出的语法里唯一一个失败之后页面看起来还正常的：丢掉 `margin-inline: auto`，列宽完全正确、贴在屏幕左边；丢掉 `max-inline-size`，列铺满整屏。两种都不像故障，因而比一眼可见的崩塌更容易活着上线，而此前没有任何开关能避开它。横排页面上两种拼法等价，关掉不损失任何东西。预设同时新增 `rootLayer`，透传到 `layer`——同样是支持开关，不该为了够到它而放弃预设。
- 文档新增[浏览器特性支持与降级](docs/compatibility.zh-CN.md)：特性 × 浏览器最低版本表、逐项的「谁产出它 / 丢什么 / 怎么关、代价是什么」，以及这份审计**不能**代替真机测试的边界（它只能证明目标浏览器解析得了这份 CSS 的语法；渲染差异、软键盘、地址栏是别的问题）。反过来，版本门槛这件事真机也测不了——手上那台 iOS 17 读得懂 `@layer`，对「15.4 以下读不懂」没有任何说明力。

### 断点检查

- 修正**默认预设上稳定误报两条 `shrinks`**：`root.fixedContainingBlock` 会把固定元素的 `left: 0` 改写成 `left: var(--adaptive-root-gutter)`，而这个留白**本来就该在断点处跳变**——列宽从 480 换到 1920，留白从 143.96px 正确地掉到 0。读成设计长度是倒退，读成它本身是修正在起作用。比较前把这个变量代成 `0px` 而不是整条跳过，`left: calc(clamp(…) + var(--adaptive-root-gutter))` 里的 `clamp()` 照样接受检查。在默认配置上就狂叫的检查，是会被学会跳过的检查。

### 原子化 CSS（Tailwind / UnoCSS）

- 修正**工具类一个都换算不到**：这是静默的——手写 CSS 被缩放、工具类原样保留，两套尺寸从此对不齐，不报错。两个大版本各卡在一处。旧版（Tailwind 3、UnoCSS `presetUno` / `presetWind3`）把长度写成 `rem`，而编译器只读 `px`；新版（Tailwind 4、UnoCSS `presetWind4`）把长度整个搬进主题 token，`.p-4` 编译成 `padding: calc(var(--spacing) * 4)`，工具类里根本没有长度可读，而自定义属性默认不换算。
- `unitToConvert` 现在接受数组，一趟读多种单位。这不是锦上添花：这两个框架同一张产物里两种单位都有——间距和字号是 `rem`，而边框宽度、`p-[13px]` 这类方括号任意值是 `px`，各自描述同一张设计稿。只读 `rem` 会漏掉所有边框，只读 `px` 会漏掉所有间距，两种单选都是错的。`rem` 按 `rootValue` 折算成像素，其余单位按面值读取。
- 修正 `unitToConvert: 'rem'` 一直把 `1.5rem` 当成 1.5 像素：换算前不折算，于是低于 `minPixelValue` 与 `hairline` 阈值而被整批跳过。这两道阈值现在一律按**像素**判定——`0.0625rem` 与 `1px` 是同一根细线，怎么写的不影响它有多细。
- 新增 `rootValue`（默认 16），读写两头共用一把尺：既决定 `rem` 输入折合多少像素，也决定文字静态项写成多少 `rem`。`html { font-size: 62.5% }` 的项目配 `rootValue: 10` 即可。
- 新增 `withAtomicCss(base, options?)`：**包装**而非替换现有配置，把 `rem` 补进 `unitToConvert`，并认领主题 token 前缀 `--spacing`、`--text-`、`--leading-`、`--radius-`、`--container-`。认领源头即可，工具类不用动——`calc(clamp(a, b, c) * 4)` 恒等于 `clamp(4a, 4b, 4c)`（正系数下乘法可穿过 clamp），产物与直接换算 `16px` 逐位相同。故意不认领三类：`--breakpoint-*` 是画布**切换**的宽度，缩放它等于移动断点本身；`--tracking-*` 用 `em` 发布，依附的字号已被做成流体，再缩一次是叠加；`--shadow-*` 的像素是按屏幕尺度画的层次感。自定义长度族用 `tokenPrefixes` 补。
- 默认 `textProperties` 加入 `--text-*` 与 `--leading-*`：字号被发布成 token 时，名字不长得像字体属性，漏掉就意味着这个字号**失去浏览器缩放**。这一条只决定一个已经要换算的长度怎么写，不决定它换不换算，因此对未被认领的 token 不起作用。
- 新增一致性用例 `atomic/{tailwind-v4,unocss-wind3,unocss-wind4}`，输入是三者的**真实产物原文**（Tailwind 4.3.3、UnoCSS 66.7.5 两个预设），用 `scripts/capture-atomic.mjs` 重抓。两个大版本的产物形状差别大到手写必然写成想象中的样子。这两个框架不设为 devDependency：抓下来的 CSS 就是全部输入，把 `npm test` 绑在别人的发版节奏上换不来额外信息。
- 路由的 `property` 通道收到正则时立即报 `TypeError` 并说明正确写法。另两个通道都收正则，这里只收字符串前缀，此前的表现是运行中途 `prefix.toLowerCase is not a function`。

**破坏性（类型）**：`ResolvedAdaptiveMatrixOptions.unitToConvert` 由 `string` 变为 `string[]`；`AdaptiveMatrixOptions.unitToConvert` 放宽为 `string | readonly string[]`，传字符串照旧。`findContinuityIssues` 新增可选第二参数 `rootFontSize`。

### 编译器

- 修正跨画布的文字尺寸：**项目画布与组件库画布不一致时，该库的文字全错**。普通长度两边一直是一致的（都归结为 `值 ÷ 画布`），文字不是——文字保留一段固定的 `rem` 以便浏览器缩放仍然有效，而这段固定长度此前锚在各自的画布上。Vant 画在 375、页面画在 750 时，两者描述的是同一份设计的两套单位，Vant 的 16px 与页面的 32px 本是同一个尺寸，却在 390px 视口下分别渲染成 16.22px 与 26.62px——**小了约 40%**，而 375 与 1440 上都看不出来。750 稿的项目配 Vant 是国内移动端最常见的组合之一；antd-mobile 的 1x/2x 双份产物同理。
- 新增 profile 字段 `textAnchorWidth`：文字静态部分锚定的宽度，默认等于 `designWidth`。组件库画布一律继承所属 profile 的锚点，因此无需配置。等价形式是「先把长度换算成锚点画布的单位，再照常套公式」：流体项恰好不变（`P × F / D` 与画布同比例约掉），只有静态项被归一化。非文字长度 `fontFluidity = 1`、静态项恒为 0，产物逐字节不变，`strategy: 'viewport'` 同样不受影响。
- 新增性质测试「同一份设计换一张画布尺寸不变」：随机画布与随机缩放比下，`V px on D` 与 `V×k px on D×k` 在每个视口上必须一致。一致性套件只能覆盖有人想到要写下来的画布组合，而这个 bug 恰好只在两张画布同时出现时暴露。

### 编译器与校验

- `@adaptive <画布>` 落在一个没有声明 `query` 的 profile 上时会告警。按文件夹分双端的项目（`src/mobile/**` 与 `src/pc/**` 各一套页面代码）通常两个 profile 都不带 `query`——切换本来就不由 CSS 负责。此时在共用组件里写 `@adaptive pc { ... }` 读起来是「这些规则给 PC 用」，编译出来却是无条件规则，而且更靠后，**在任何视口都会赢**，没有任何地方会说话。`query: false` 不告警：那是作者明确表示切换发生在 CSS 之外。指向同一张画布的 `@adaptive` 也不告警：没有切换，拆开不丢东西。
- 文档新增「两套页面代码，按文件夹分」一节，写清文件路由只决定按哪张稿换算、不会附带媒体查询，以及共用组件、组件库、路由具体度这几处的取舍。

### 构建工具集成

- 新增真实 Vite 构建测试：脚手架里有一个由 Vite 自行发现的 `postcss.config.mjs`、一份从 `node_modules` 引入的依赖 CSS、以及一个按 `@vitejs/plugin-vue` 同款方式提供的 `<style>` 块。此前集成文档里的每一条说法都没有任何东西验证过——而这些说法一旦不成立，构建仍然成功，只是样式表是错的。现在验证：配置被找到并生效、依赖按 `node_modules` 路径落到组件库画布且与页面等价尺寸逐字节相同、带 query 串的 SFC id 能被包含式 `file` 路由命中、二次构建产物完全一致。
- 并且把文档里「锚定结尾的正则匹配不到 SFC」这条坑也写成了测试：用 `/\.mobile\.css$/` 构建一遍，断言该 `<style>` 块确实静默留在默认画布上，而其余产物一字不差。这类说法只有被反向验证过才算数。

### 可选运行时

- 修正 `observeAdaptiveViewport` 的帧泄漏：`update()` 是公开方法，却会把调度器的帧句柄清零。「先手动 `update()`、再 `destroy()`」这一串下来，已排队的那一帧既没被记录也没被取消，会在下一拍落到已销毁的观察者上。现在只有调度器自己清句柄，`destroy()` 之后也把句柄归零，重复 `destroy()` 不会去取消宿主已经回收再分配的句柄。
- 补齐运行时测试：没有 `visualViewport` 的旧 WebView 回退、iOS 橡皮筋回弹导致的负键盘高度、逐字段的非数值读数、事件合帧、销毁时机、以及不传参数时读全局对象这条所有浏览器使用者实际走的路径。分支覆盖 72% → 100%。

### 组件库

- 新增 `scripts/verify-libraries.ts`：下载每个内置库的**已发布产物**，用真实的 `node_modules` 路径编译，核对前缀是否真的存在、路由落到哪张画布、是否幂等、有无告警、接缝检查是否误报。此前注册表里除 Vant 外都是照文档写的，没有实证；下面三条都是这个脚本查出来的。
- 修正 antd-mobile：该库把同一份样式表发布了两次，`bundle/` 画在 375 上、`2x/bundle/` 画在 750 上，类名与 token 名完全相同（实测 5.42.3，后者每个长度恰好是前者的两倍）。此前 `.adm-` 前缀路由会把 2x 产物按 375 换算，页面上每一个尺寸都是应有的两倍，且没有任何报错或告警。新增 `antd-mobile-2x` 条目，自动模式下无需配置。
- 新增 `scoped`：限定 `prefix` 与 `tokenPrefix` 只在 `file` 同时命中时生效。一个前缀对应两张画布时，只有路径能区分。限定路径的路由先于不限定的路由测试，因为它更具体；路径不存在时（打包器内联依赖）退回不限定的那一条。`scoped` 却不给 `file` 直接报错。
- 移除 Varlet 的 `tokenPrefix: '--var-'`：该库的自定义属性根本不带前缀，叫 `--field-padding`、`--icon-size-md`，声明在光秃秃的 `:root` 上。这条规则此前匹配不到任何东西。认领这些名字等于认领 `--card-width` 本身，注册表只收无歧义的前缀，所以不补，改为在文档里给出显式路由的写法。
- 文档补上实测的前缀命中率，并写明**设计宽度这一列核对不了**（CSS 里看不出稿子画在多宽），以及 `naive-ui` 与 `mui` 的样式在运行时生成、磁盘上没有样式表。

### 断点接缝检查

- 只在**至少一侧是编译器产出的公式**时才报。此前会把库自己有意写下的断点差异当成接缝：Quasar 的 `.q-tooltip` 手机上 `padding: 8px 16px`、600px 往上 `6px 10px`，是点击区域的取舍，两个数字都是人手写的、也比对过。「倒退只可能来自跨画布」这个完备性论证只覆盖本编译器产出的公式，对原样保留的样式表不成立。一侧换算另一侧没有，仍然报。
- 一致性套件的用例改用编译器形状的值。此前十余条负向用例写的是裸 `40px`，加上这道门之后会因为「没被编译」而通过，而不是因为它们各自要验的那件事。

## 0.4.0 — 2026-08-09

### 断点接缝检查

- 新增断点接缝检查：命令行会指出「视口变宽、尺寸反而变小」的声明，并给出两侧的实际像素值。两张设计稿各自都对，接缝处未必对，而这类问题只在某一个宽度上出现——日常调试的 375 和 1440 都正常。检查也从包里导出为 `findContinuityIssues(root)`，可用于让构建直接失败。
- 检查按**绝对值**比较，且断点两侧变号一律不报。编译器输出的公式，其绝对值对视口宽度单调不减且全程不变号，所以绝对值倒退只可能来自跨断点换画布。按数值比较对每一条负长度都是反的：负外边距、外溢这类值本来就靠远离零来变大。
- 检查会代入同一份样式表里的主题 token。组件库的尺寸几乎不写字面量——Vant 4.10.0 的 3198 条普通声明里有 1173 条完全经 `var()` 读取，在第一个 `var(` 就放弃等于避开了本插件要适配的那一层。代入只在值由视口宽度单独决定时进行：token 只声明在 `:root` / `:host` / `html` 上且别处没有第二份，每一处声明要么无条件、要么位于纯像素宽度的 `@media` 里。token 自身在断点处被改写也算一次接缝，即使消费它的规则只写了一次。实测（Vant 4.10.0 完整样式表）可求值的值分量 622 → 1309 条（17.6% → 36.9%），收录 779 个 token。
- 实测误报：69 份一致性套件产物、上述 Vant 样式表、本仓库示例工程，`shrinks` 报告数均为 0。
- 新增 `evaluateLength`：把编译器产出的 `clamp()` / `min()` / `max()` / `calc()` 在指定视口宽度上求值。`env()`、`%`、容器单位一律返回 `null` 而不是猜一个数。

### 打包与类型

- 修正 CommonJS 入口：`require('postcss-adaptive-matrix')` 此前返回的是命名空间对象，直接调用会抛 `plugin is not a function`，必须写 `.default`。而 `plugins: [require('postcss-adaptive-matrix')({ ... })]` 正是所有 `postcss.config.js` 的通用写法——本仓库 Webpack 文档里的示例自己就是错的。现在 `module.exports` 就是插件本身，`.default` 与各具名导出仍作为属性保留。
- 修正 CommonJS 类型入口。两处：`exports` 里的 `types` 此前只有顶层一处，CJS 使用者拿到的是 ESM 版 `.d.ts`；而 `.d.cts` 本身只声明具名导出，于是 `import x = require('postcss-adaptive-matrix')` 报 “has no call signatures”——代码能跑、编辑器报红。现在 `require` 指向 `.d.cts`，且 `.d.cts` 以 `export =` 描述真实形状，类型经合并的命名空间保留，`import type { AdaptiveMatrixOptions }` 照常可用。ESM 入口与类型不受影响。
- 新增针对构建产物本身的测试：其余测试都从 `src` 导入，而「`require` 拿到什么」「类型解析到哪个文件」由构建与 `package.json` 决定，从源码导入永远测不到。`npm run check` 因此改为先构建再测试。

### 编译器与校验

- 跨画布的选择器列表（如 `.van-cell, .page-hero { ... }`）现在会告警并指出哪个选择器落空了。一条声明只能有一个结果，此前是静默按第一个命中的画布编译整条规则。
- `@adaptive pc;`（没有块）此前被改写成 `@media (min-width: 768px);`——不是合法 CSS，而作者想放到那张画布上的规则仍留在原画布。现在告警并保持原样。
- 命令行的 `-c` 不再在配置模块漏写 `default`、或预设忘了调用时静默改用内置默认值。这两种写法此前会跑完并打印一份看着正确的对照表，没有任何地方说过配置没被读到。
- 新增配置校验：`unit` 与 `strategy` 的取值、空的 `unitToConvert`、与 CSS 已定义的 at-rule 重名的 `atRuleName`、空的 `root.selector`。这些字段写错的后果都是静默的，其中 `unit` 写错会直接产出无效 CSS。
- 新增基于性质的随机测试：以 `evaluateLength` 为判据，在数百组生成的画布上验证设计宽度恒等、绝对值单调、区间外恒定、区间内线性与幂等。一致性套件只能覆盖有人想到要写下来的设计宽度。

## 0.3.0

- 新增 `adaptive-matrix` 命令行预览：逐条声明的前后对照，`--from` 可在上线前验证文件路由，`--css` 输出完整产物且警告走 stderr。
- 修正嵌套场景：`@media` / `@supports` / `@layer` / `@container` / `@scope` / `@starting-style` 内的声明现在会被换算，`@font-face` / `@page` / `@property` / `@counter-style` 内的长度保持原样。
- 修正幂等性：已经带视口单位的 `clamp()` / `min()` / `max()` 不再被二次换算。
- 修正根容器基础样式的幂等性：产物带 `/* postcss-adaptive-matrix foundation */` 标记，再编译一次既不会把 `max-inline-size: 480px` 这类固定上限当成设计稿尺寸缩放，也不会叠出第二份。
- 忽略注释（`adaptive-ignore` 系列）不再从产物中删除。被忽略的值没有任何自身痕迹，注释一旦消失，第二趟编译就会把作者明确排除的尺寸换算掉；注释会被压缩器去掉，不影响上线体积。
- 一致性套件对每一个样例增加幂等断言：产物再编译一遍必须原样返回。
- 修正重复声明：同一规则内重复书写的声明现在每一条都会换算。此前只换算第一条，而层叠中生效的是最后一条，等于整条换算失效。
- 支持带指数的数字：`1e2px` 就是 100px，此前被静默跳过；`min(1e2vw, 50px)` 也不再被误判为「没有视口单位」。
- 修正 at-rule 大小写：`@ADAPTIVE` / `@Adaptive` 与 `@adaptive` 等价，与 CSS 对 at-keyword 的大小写不敏感一致。此前不被识别，整块会被浏览器丢弃且没有任何提示。
- 新增 `root.injectTo`（预设为 `rootInjectTo`）：限定根容器基础样式注入到哪些文件。组件化项目里每个 `<style>` 块都是独立文件，默认会逐个注入一份。
- 只写排除项的 `propList`（如 `['!border*']`）现在直接报错——它匹配不到任何属性，等于整份样式表都不换算。
- 在 `profiles` 里使用保留前缀 `library:` 现在直接报错并指回 `libraries: [{ extends }]`——此前会被合成的组件库画布静默覆盖。
- 未知画布的告警不再列出内部合成的组件库画布，并说明浏览器会整块丢弃该 at-rule；`unknownProfile: 'error'` 下的报错不再建议开启已经开启的选项。
- 新增注册表不变量测试，覆盖前缀歧义、画布取值与自动模式的短前缀策略。
- 重写文档结构，新增快速上手、构建工具集成、组件库适配、可选运行时与命令行预览五篇，并补充 SVG 图示。

## 0.2.0

- 新增组件库画布、自动识别、路由覆盖和设计令牌适配。
- 新增 fixed 根包含块定位矫正与桌面端偏移处理。
- 将编译核心与 PostCSS 适配层解耦，公开解析后的多画布配置能力。
- 建立 50+ 组 conformance 固定样例、133 项测试和性能基准工具。
- 重写多画布模型、组件库接入、架构与迁移文档。

## 0.1.0

- 首次实现 App/PC 多设计画布编译模型。
- 支持有界流体尺寸、可缩放文字、媒体查询和容器查询 profile。
- 支持动态设计宽度、文件/属性/选择器/值过滤与忽略指令。
- 提供可选根布局、安全区变量和 VisualViewport 运行时。
- 发布 ESM、CommonJS 与 TypeScript 类型。
