规格卡 DevDocs/artifacts/components-ink.svg 的令牌化,只动 crates/design,零依赖不变。 - color::boards:BrandBoard(Ink 深 / Silver 浅)→ 49 角色 × 2 套固化色板。 spec 直写值进 ANCHORS(测试逐条比 hex,规格卡改了代码不改会红);规格卡没给的 角色按「同色相 + M3 tone 关系」推导后固化,规则写在 boards/ink.rs 的行注释里。 Scheme::from_board / DesignTokens::brand 是两个新入口;Scheme::baseline 一字未改 (它是 D38 取色链的中性锚点,改它等于同时改 app-iced 观感 + 取色语义)。 - palette::visibility:容器层可视性裁决,即债务台账「SecondaryContainer 对 Surface 仅 1.06」的令牌层结论。状态容器(*-container)对 surface ≥1.20:1 是唯一数字门禁; 容器层之间不设亮度门禁,改为「选中态 = 容器底 + 文字对比 + 形状/图标」三件套的构成 要求(浅色板实测 1.05~1.24,任何有意义的门禁都过不了,详情见模块文档实测表)。 - inkcat:规格卡的尺寸与形状数据。ControlSize(按钮 XS~XL = 32/40/56/96/136,圆角 一律半高)、FabSize(40/56/80/160×56,圆角 12/16/20/16)、控件方框(卡片 16、 对话框 28、snackbar 52@16、列表行 60 药丸、chip 32@8、字段 4/28 + 内距 16/32、 复选 18@4、单选 18、开关 52×32 + 拇指 24、进度 4/6)、SpecText(规格卡 11 个字号 → 命中 / 落档 / 低于 11px 下限三类结论)。 component_map 的表值一处未动:app-iced 的 pages/frame/tests.rs:42 与 shell/tests.rs:30 按 Medium(12) pin 住卡片,改表等于连带改观感且破坏既有断言;差异在 inkcat 模块文档 逐条登记,换肤时由 M229 一次切换 + 截图验收。 - 测试:186 lib(+13 条新单测)+ tests/ink_cat_boards.rs(10 条:完整性/锚点/双主题 切换/26 对 WCAG AA/25 组规格卡用法/状态容器 1.2 门禁/阶梯单调)+ tests/ink_cat_geometry.rs (3 条:按钮 FAB 几何、控件圆角、字号校核)。
68 lines
3.7 KiB
Rust
68 lines
3.7 KiB
Rust
//! # 配色来源(D38 / D40):图片取色 · 内置色卡 · 三级回落 · WCAG 收敛
|
||
//!
|
||
//! 用户在 2026-11-01 定的配色来源是三级(D38):
|
||
//!
|
||
//! ```text
|
||
//! ① 启动器背景图取色(用户设了背景图时)
|
||
//! ↓ 没有背景图 / 取色失败
|
||
//! ② 桌面壁纸取色(用户开了「莫奈取色」但没设背景图时)
|
||
//! ↓ 没有壁纸 / 取色失败 / 对比度收敛失败
|
||
//! ③ 内置色卡(用户手动选的那张;没选过就是出厂默认「天蓝」)
|
||
//! ```
|
||
//!
|
||
//! 取色失败**不静默**:本模块的 [`Provenance`] 会把「最终用了哪一级」和「前几级为什么没用」
|
||
//! 都带回来,设置页据此显示提示;解码失败也在里面(它不在这里发生,但原因是这里记录的)。
|
||
//! 契约(UI 怎么调、文案怎么映射)见 `DevDocs/SubAgentsTalk/palette-source-contract.md`。
|
||
//!
|
||
//! ## 分层(D40):本模块只做纯数学
|
||
//!
|
||
//! | 层 | 在哪 | 职责 | 依赖 |
|
||
//! |---|---|---|---|
|
||
//! | 纯数学 | **本模块**(`crates/design`) | 像素 → 种子 → 49 角色;色卡;回落决策;对比度收敛 | 无(零第三方依赖) |
|
||
//! | 解码 | `crates/palette-extract` | 路径/字节 → RGBA 像素 | `image`(唯一引入处) |
|
||
//! | 调用侧 | 后续 appearance/settings mission | 拿路径、把 [`Provenance`] 变文案 | UI 框架 |
|
||
//!
|
||
//! 所以本模块的「图片输入」永远是**已经解码好的像素**,从来没有路径、没有 IO。
|
||
//!
|
||
//! ## 四个部分
|
||
//!
|
||
//! - [`seed`]:`seed_from_rgba(pixels, w, h)`(D40 的入口形状)。量化在 [`quantize`],
|
||
//! 挑主色在 [`score`]——**不是取平均色**,理由见 [`quantize`] 的模块文档。
|
||
//! - [`card`]:内置色卡(≥6 张,含出厂默认「天蓝」)。卡的 `key` 是**持久化键**,写进用户设置。
|
||
//! - [`wcag`]:对任意一套方案跑对比度断言;不达标就沿 tone 轴修正,并记录「改了什么」。
|
||
//! - [`visibility`]:容器层可视性裁决(状态容器对 `surface` 的 1.20:1 门禁 + 容器阶梯单调),
|
||
//! 即债务台账「SecondaryContainer 对 Surface 仅 1.06」的令牌层结论。
|
||
//! - [`resolve`]:纯函数 `resolve_source(...) -> (Scheme, Provenance)`,实现三级优先与回落。
|
||
//!
|
||
//! ## D40 的两条实测事实(不是风格偏好,是物理事实)
|
||
//!
|
||
//! 1. **零彩度种子的 CAM16 色相是常数 209.49°**,不是 NaN。所以「灰白壁纸」硬取色会得到
|
||
//! 一个凭空上色的青色(`#006874`),且极低彩度段色相是噪声放大器(`#808080` → 209.5°、
|
||
//! `#7A7C7E` → 231.2°)。→ [`seed::MIN_SEED_CHROMA`] = 8 的种子彩度门槛,低于它就回落色卡。
|
||
//! 2. **动态色也必须过 WCAG**。候选色卡的 208 项门禁(`tests/contrast_aa.rs`)不覆盖取色结果,
|
||
//! 所以 [`wcag`] 对**任何**来源的方案都跑同一套检查,不达标就收敛或回落。
|
||
|
||
pub mod card;
|
||
pub mod resolve;
|
||
pub mod seed;
|
||
pub mod visibility;
|
||
pub mod wcag;
|
||
|
||
mod quantize;
|
||
mod score;
|
||
|
||
pub use card::{Card, CardKey, CARDS, DEFAULT_CARD};
|
||
pub use resolve::{resolve_source, ImageInput, ImageSource, Level, Outcome, Provenance, Skip};
|
||
pub use seed::{
|
||
analyze_rgba, seed_from_rgba, Failure, Seed, MAX_SAMPLES, MIN_PIXELS, MIN_SEED_CHROMA,
|
||
MUTED_SEED_CHROMA,
|
||
};
|
||
pub use visibility::{
|
||
check_surface_hierarchy, LayerStep, SurfaceHierarchy, LAYER_ROLES, LAYER_STEP_MIN_LSTAR,
|
||
STATE_CONTAINERS, STATE_CONTAINER_MIN,
|
||
};
|
||
pub use wcag::{converge, Adjustment, WcagReport, AA_NON_TEXT, AA_TEXT, TEXT_PAIRS};
|
||
|
||
/// 一套方案的来源层级([`Level`] 的别名,读起来更清楚)。
|
||
pub use resolve::Level as SourceLevel;
|