AskaEth
|
13248ddc9b
|
test(design): 新增走 resolve_source 的接线守护测试(复审 r1 p1-1)+ 两条小修
**p1-1**:新增 `resolved_image_scheme_keeps_tinting_from_the_seed`——走产品公开入口
`resolve_source(Some(ImageInput::pixels(..)), None, Some(CardKey::Sky/Teal), Light)`,断言
`provenance.level == Level::BackgroundImage`、`surface == #FFF8F7`(暖白,跟着图走)、色相
≈22°(远离固定的 239.51°)、两张卡下主色与底色都相同、且与天蓝卡的 surface 不同。
上一版只有 `image_scheme_ignores_the_preset_neutral_hue`,它测的是 `Seed::scheme` 生成器层:
把 `resolve::try_level` 的取色分支改成固定中性时 8 条测试全绿(复审已实证)。那条保留并补了
⚠️ 注明「不覆盖接线」,避免下次又把生成器层当成接线守护。
**小修 1**:`every_card_scheme_meets_wcag_aa_under_the_fixed_neutral` 的 on-surface/surface
量级断言按 `flavor` 分开(原来 `(16.30..=16.40) || (14.28..=14.36)`,浅/深两值互换也能过)。
**小修 2**:测试文件拆分以守住 ≤400 行——`tests/card_neutral.rs`(crate 根,400 行)+
`tests/card_neutral/common.rs`(改前路径复原 / 模式列表 / 色相环距离,27 行)+
`tests/card_neutral/dump.rs`(出图数据导出 + 改前/改后实测表,68 行,无断言)。crate 根的子模块
默认在 `tests/` 下找,所以用 `#[path]` 指明目录。
`cargo test -p unlucky-design --test card_neutral` → 9 passed(8 + 新 1 条)。
|
2026-11-02 13:12:44 +08:00 |
|
AskaEth
|
4b059201fd
|
fix(design): 中性色相常量改容差断言 + 取色接线注释指向真正的守护测试(复审 r1 p2)
- `palettes.rs::neutral_hue_is_the_default_seed_hue`:`to_bits()` 位相等 → 1e-9° 容差。
色相来自 `atan2(a).to_degrees()`,`NEUTRAL_HUE_DEG` 的字面值是在 Windows(UCRT) 上取的,
CI 的 ubuntu(glibc) 理论上可差 1 ulp 就让位相等在另一个 OS 上红;而这条要抓的是「换了
`DEFAULT_SEED` 却没复核常量」(偏几十度),1e-9° 足够且不受平台末位差影响。
`tests/card_neutral.rs::sky_card_is_bit_identical_to_the_product_baseline` 是**同平台内**
两个算出来的值互比,继续用位相等(p1 硬约束的钉子,不动)。
常量文档同步说明「为什么不用 `to_bits`」。
- `resolve/mod.rs` 取色分支的注释:原来只写「`tests/card_neutral.rs` 有反证」,而那条用例
测的是 `Seed::scheme` 生成器层、覆盖不到这里(复审 r1 p1-1 的病根)。改成点名
`resolved_image_scheme_keeps_tinting_from_the_seed`——走的就是本函数的公开入口。
`palettes.rs` 仍是 400 行(本轮改 doc 同时压缩了相邻说明,没突破上限)。
|
2026-11-02 13:12:43 +08:00 |
|
AskaEth
|
b748986aae
|
test(design): 钉住 D43 的固定中性色相(八卡同底色 / 天蓝不动 / 动态路径不退化)
`tests/card_neutral.rs`(新文件,8 条用例):
1. `sky_card_is_bit_identical_to_the_product_baseline`:默认档天蓝的 49 个角色 ==
`Scheme::baseline`,两套模式逐值相等(M88 的 p1 级硬约束)。
2. `preset_cards_share_one_neutral_hue_and_chroma`:八张卡 surface 的 CAM16 色相
一致(±1°)、实测彩度落在 ±1.5 带宽内,且更强的一条——八张卡的 surface 是同一个
hex(实测差 0.00° / 0.00)。
3. `the_old_per_card_tinting_really_was_a_ninefold_gap`:反证,证明上面的带宽量得出
问题——改前浅色档彩度差 9.3 倍(rose 1.29 / teal 12.03),改后 1.0 倍;深色档改前
彩度本来就一致(tone 6 装得下 12),问题在色相横跨 285°,改后色相跨度 0。
4. `cards_only_change_the_accent_families`:强调族不靠手抄清单,而是实测「换强调色会
变的角色」(深色 26 个 / 浅色 23 个——浅色 on-primary/secondary/tertiary 的 tone 是
100,任何色相都是纯白),其余角色必须逐值留在基线。
5. `card_scheme_matches_from_accent_when_chroma_is_the_default`:色卡方案 ==
`Scheme::from_accent(卡种子)`,把 D43 接到 `tests/official_accent.rs` 的官方对账上。
6. `image_scheme_ignores_the_preset_neutral_hue`:动态路径的守护测试——同一张图在 sky /
teal 卡下强调色相同,中性调色板色相来自图像种子(精确口径查 `Palettes::neutral`,
另加一条观感口径),且远离 `NEUTRAL_HUE_DEG`;谁把取色也改成固定中性就会红。
7. `every_card_scheme_meets_wcag_aa_under_the_fixed_neutral`:8 卡 × 浅/深 × 26 对全部
≥ 4.5:1(`palette::wcag::check` 全表),并把 on-surface/surface 钉在实测量级。
8. `dump_card_palette_for_the_color_card`:出图数据(`CARD|…` 行,改前/改后并排),
渲染脚本只做排版。
实测:8 卡 surface 改前 `#EBFEF8`~`#FFF8F4`(浅)/`#061613`~`#1F0F10`(深)→ 改后统一
`#F6FAFF` / `#09151C`;全表最差比值浅 5.44 / 深 4.98,均远高于 AA 的 4.5。
门禁:`cargo test -p unlucky-design` 231 条全绿(216+5 doc → 226+5 doc,+10 条增量:
本文件 8 条 + `color::palettes` 2 条)。
|
2026-11-02 13:12:42 +08:00 |
|
AskaEth
|
de9d5337ac
|
feat(design): 内置色卡改用固定中性色相,切卡只换强调色(D43)
色卡路径原先走 TonalSpot「中性色也跟种子染色」,八张卡的中性色相 = 种子色相,
浅色档 surface 的实测 CAM16 彩度从 1.29(rose)跨到 12.03(teal)——tone 98 处
暖相色域装不下彩度 12,同一个请求值在暖相上只兑现 1.29。9 倍的底色跳变让用户在
设置页逐张试色时觉得「每张卡都像换了个应用」。
- `color::palettes::NEUTRAL_HUE_DEG`(新常量)= 出厂默认种子 `#3C8DBC` 的 CAM16
色相 239.51420577630955°,出处与「换默认色时要复核什么」写在常量文档里;
`neutral_hue_is_the_default_seed_hue` 用位相等钉住它与 `DEFAULT_SEED` 的绑定。
- `Palettes::accent_on_fixed_neutral(accent, neutral_hue, neutral_chroma)`:强调三族
跟 accent 色相,中性两族固定在给定色相;其余常数(36/16/24/25°/84、tone 表、
gamut 映射)一行未动。私有 `tonal_spot_with_neutral` 承载实现,`tonal_spot` 只是
它的「中性色相 = 种子色相」特例。
- `Card::scheme` 改走上述入口(`NEUTRAL_HUE_DEG` + 卡自己的中性彩度)。默认档天蓝的
49 个角色**逐值不变**(固定在等于它自己中性色相的那个值上,属「沿用」而非「另选」)。
- 动态取色路径**刻意不动**:`resolve::try_level` 仍按图像种子逐色染色、只借色卡的中性
彩度定浓淡;两处注释都写明「两条路径不同,别统一处理」。
- 订正 `palette/card.rs` 的模块文档:原文把「请求值相同」当成「观感相同」,现在改成
可直接当契约读的三条(表面与描边八卡逐值相同 / 只有强调族跟卡走 / 天蓝卡 == baseline)。
门禁:fmt 干净、clippy -D warnings 通过、`cargo test -p unlucky-design` 全绿、
`cargo tree -p unlucky-design --edges normal` 仍是单行(零第三方依赖)。
|
2026-11-02 13:12:41 +08:00 |
|
AskaEth
|
7c274afd7c
|
fix(design,app-iced): 守门改成调用形状+逐调用点计数;filled-tonal 事实纠正(M86 r2 P1)
P1-1(守门漏网):sub_24_boxes_go_through_view_at 原判据只认带 turbofish 的 icons::view::,
且 plain && !sized 让「同文件有 view_at」整文件放行,checked 还数文件不数调用点。现在:
- 先剥注释(注释里写 icons::view 不再误报);
- 按调用形状匹配:icons::view( / view::< / crate::icons::· / super::icons::· / use …view as v
与 {view} 之后的 v( / view(;
- 逐调用点计数,每文件必须恰好等于白名单期望(未列出 = 0)——所以「同文件改对 3 处漏 1 处」也红。
P1-2(事实错误):FilledTonalButtonTokens.DisabledContainerOpacity = 0.12f 是存在的,
javap -p -constants 可见;它是编译期常量、被内联进 getDefaultFilledTonalButtonColors,所以在
<clinit> 里看不到——我上一轮把「没在 clinit 出现」误写成「字段不存在」。同时把「TonalButtonTokens」
从 0.1f 家族的依据里剔除(它在整个 AAR 里零消费者,正是本 mission 警告的死令牌坑),改为只引用
ButtonDefaults 真的调了 getDisabledContainerOpacity 的 Filled/Elevated/Outlined,并注明
TextButton(容器透明)与 Baseline(基准表、运行时无消费者)。
|
2026-11-02 13:12:18 +08:00 |
|
AskaEth
|
7627462593
|
test(design): 两族禁用 α 的令牌层钉值(硬编码期望,去自指)
M86 r1 P2:app-iced 那条 surface_family 断言原本从被测常量算期望,改坏常量也绿。除了把它改成
硬编码,令牌层自己也钉一条(cargo test -p unlucky-design 就能红,不必等 app-iced 链接):
DISABLED_CONTAINER_ALPHA == 0.10、DISABLED_SURFACE_ALPHA == 0.12、内容 0.38,且 surface > button。
|
2026-11-02 13:12:16 +08:00 |
|
AskaEth
|
da1fe3aa4f
|
fix(app-iced): 盒 ≠ RENDER_PX 的图标改走 view_at,线宽回到 1.5px(M86 r1 P2)
r1 视觉/代码复审撞上同一件事:本轮把按钮/chip/switch 的盒子改成 18/18/16,但 SVG 仍按 24
烘焙、靠布局盒缩下去 ⇒ 实测线宽 1.04~1.25px(令牌要 1.5px)。label 的对照证据是同屏 caption
(20px,走了 view_at)实测正好 1.50px——差别只在有没有走按盒烘焙。
- buttons/assemble.rs(前置/后置)、selection/chip.rs(前置/后置)、selection/switch.rs 滑块勾
共 5 处改为 icons::view_at(kind, color, geometry.icon);
- 新增单测 view_at_bakes_the_requested_size:source(18) 必须是 width=18 + stroke-width=2.0
(视觉 1.5px),并反证「24 烘焙塞进 18 盒只有 1.125px」;
- 新增源码级守门 sub_24_boxes_go_through_view_at:icons::view 只允许出现在图标盒=24 的文件,
否则红——谁把上面 5 处改回 view 立刻被抓;
- 三处会带偏人的文字订正:icons.rs 模块文档改成「24 盒走 view、其余一律 view_at」;
selection/mod.rs 的「图标一律 icons::view」同改;token_contract.rs 明说那条只保证烘焙路径。
|
2026-11-02 13:12:14 +08:00 |
|
AskaEth
|
fa40d798e2
|
docs+test(design,app-iced): filled-tonal 的禁用容器偏离写进代码;禁用色断言去自指(M86 r1 P2)
r1 复审两条:
- 我在报告里说「filled-tonal 的 0.12 偏离已写进代码」,实际一个字都没写,而 state.rs:47 还把
TonalButtonTokens 列进 0.1f 家族,等于让人误以为已对齐。现在 state.rs 的常量文档与
disabled.rs 的模块文档都写清:官方 getDefaultFilledTonalButtonColors 硬编码 0.12f
(FilledTonalButtonTokens 无该字段),我们的入口不区分变体、tonal 实际画 0.10,属已登记偏离。
- surface_family_disabled_colors_are_the_12_percent_class 的期望值原本从被测常量自己算,
把 DISABLED_SURFACE_ALPHA 改成 0.10 也照样绿(自指断言)。现在期望值硬编码 0.12/0.38 并
先钉常量本身;按钮族那条同改。
|
2026-11-02 13:12:13 +08:00 |
|
AskaEth
|
b566097107
|
fix(design,app-iced): 图标尺寸与禁用 α 收敛到官方字节码,两族拆开(M86)
图标:IconSize::Button 16→18(AAR ButtonDefaults.<clinit> 的 bipush 18);新增
IconSize::SwitchThumb=16(SwitchTokens.Selected/UnselectedIconSize)承接开关滑块,按钮与
滑块不再借同一档;IconSize::Nav 的值不动但文档订正(无消费,官方导航/列表图标是 24)。
ButtonSmallTokens.IconSize=20 在整包 AAR 里零消费(javap 搜 getIconSize 无外部调用),
是死令牌,不再作为取值依据。
禁用 α:官方本来就分两族——按钮容器 0.10(*ButtonTokens.DisabledContainerOpacity)与
面状控件 0.12(chip 描边 / slider 轨道 / switch 轨道)。原来的缺陷是一个全局 12% 同时
服务两族(按钮那半边一直错、另半边一直对),现拆成 disabled_button_colors/…_content
与 disabled_colors/disabled_content_over_role 两个入口;interaction 首次超 400 行,
禁用口径与测试搬到 interaction/disabled.rs。
标题栏 caption 三键:官方 SmallIconButtonTokens.IconSize=24,按用户点名缩到 20
(容器 46×48 不动),登记为项目偏离;icons.rs 加按令牌取尺寸的入口 view_at,按
(种类,尺寸) 分别烘焙 SVG,20px 的 stroke 取 1.8 保视觉线宽 1.5px。
|
2026-11-02 13:12:11 +08:00 |
|
AskaEth
|
8be91a9cc9
|
docs(design)+test(palette-extract): 修正「种子一定是原图真实颜色」的过度承诺
自测发现:400×400 红蓝棋盘在 MAX_EDGE=256(适配层 thumbnail 缩放)与 512(design 块平均)
下都取到 #704070,都不在原图上——「≤MAX_SAMPLES ⇒ 真实颜色」只对「传进 analyze_rgba 的
那个缓冲」成立,不是整条链的性质。
- Seed::color 文档写明边界 + 上游缩放同样会破坏该性质(附实测例)
- 结构性断言的注释/断言文案改为「design 不再叠第二层块平均」,不再声称能保住真实颜色
|
2026-11-02 11:05:06 +08:00 |
|
AskaEth
|
09b716ae79
|
docs(design)+test(palette-extract): r2 三条口径修正与常数绑定
- LowChroma 文档:删掉「彩度必然 < 候选门槛 5」(人气门槛也会淘汰高彩度小面积,
实测 0.5% 纯红报 113.36),改为与契约文档 §6 一致的口径;代码臂注释与 score::most_chromatic 同步
- Seed::color / share 改成有边界的说法:≤MAX_SAMPLES 才是图上真实色、share 的分母是有效采样点
- palette-extract 新增 max_edge_stays_within_the_design_sample_budget:
MAX_EDGE² ≤ MAX_SAMPLES(256² 当前余量为零,调大就红)
- card.rs:把 6.43~6.50 / 7.70~7.76 写成断言(原来只有 ≥4.5 下限,文档却宣称区间)
- LowChroma.color 字段注释改成「最接近可用的候选色」(113 那条并不「能用」)
|
2026-11-02 11:05:03 +08:00 |
|
AskaEth
|
f4c48752c2
|
fix(design): Outcome::NotReached / 色相环回绕断言 / LowChroma 口径统一 / 文档指向(复审 r1 p2-4 + 色彩 r1 p2-2/3/4)
- Outcome 新增 NotReached:区分「用户没提供」与「更高级成功所以没轮到」,
UI 不会再对着用户已选的卡说「色卡:未选择」;新增 not_reached_is_distinguishable_from_not_provided
- cards_are_visually_distinct 补色相环回绕间隔(原来只比排序相邻差,350° 的卡会静默通过)
- Failure::LowChroma 的 color 与 chroma 现在永远来自同一个簇(原来一条路径拿 A 簇的色值配 B 簇的彩度);
两条路径的口径写进文档注释,并提醒 UI 别把数字写成「彩度 X < 门槛 8」
- card.rs:测试名指向修正(card_keys_are_stable_and_unique,测试就在本文件)、
「对比度 8.4:1」的假示例换成实测区间(light 6.43~6.49 / dark 7.70~7.75)、
中性彩度那段补上「请求值相同 ≠ 实际表面同冷度」的事实与 D43 去向
|
2026-11-02 11:05:02 +08:00 |
|
AskaEth
|
948a3685cd
|
test(design): 钉住 D40 的彩度门槛 8(复审 r1 p2-3)
MIN_SEED_CHROMA 8.0 → 6.0 的变异原本全绿(那条断言比的是常量自己)。
新增两条用实测 chroma≈7.23 的灰蓝(#7E8489):
- seed::tests::chroma_just_below_the_gate_is_rejected(门槛之上/之下夹住)
- resolve::tests::faint_colour_falls_back_to_the_selected_card(回落 + 落点是选中卡 + 方案逐值等于该卡)
顺带把 resolve 的单测拆到 resolve/tests.rs(400 行上限)
|
2026-11-02 11:05:01 +08:00 |
|
AskaEth
|
2ce394fbfc
|
fix(design): 混合透明度不再被误判为「全透明」(复审 r1 p2-1)
先按 alpha 过滤像素、再做块平均(整块全透明则跳过)。原实现先平均再比门槛,
块内 255 与 0 的整数除法得 127 < 128 → 512×512 里 131072 个不透明像素被报
NoOpaquePixels,UI 会告诉用户「背景图几乎全透明」,与事实相反。
- 新增 half_transparent_image_keeps_its_opaque_colour(红)
- 新增 mostly_transparent_image_still_samples_the_visible_part
- 模块文档加一行「混合透明度」的处置与踩坑说明
|
2026-11-02 11:04:59 +08:00 |
|
AskaEth
|
36c1cce00d
|
test(design): 补「先量化再打分」的端到端反证(复审 r1 p1)
reviewer 实测变异:把 analyze_rgba 改成不透明像素算术平均,150 个测试全绿——
因为反证只测了 quantize/score 中间层,接线没人覆盖。新增:
- seed::tests::end_to_end_picks_the_five_percent_accent(95% 米白 + 5% 珊瑚 → #FF6B5A)
- seed::tests::end_to_end_picks_the_three_percent_accent(97/3 → #FF8800)
- resolve::tests::a_five_percent_accent_drives_level_one(一级成功、种子是珊瑚、≠ 回落色卡)
顺带把 seed 的单测拆到 seed/tests.rs(410 行 → 220 + 190,恢复 ≤400 纪律)
|
2026-11-02 11:04:58 +08:00 |
|
AskaEth
|
80314f4830
|
docs(design): hint_key 的文案映射表搬进 provenance.rs 的文档注释
DevDocs/ 是 gitignore 的本地文档,契约里的「键 → 文案」表review 时看不到,
所以在 hint_key 的 rustdoc 里放同样一张表(差异只有措辞建议,结构一致)。
|
2026-11-02 11:04:57 +08:00 |
|
AskaEth
|
31ee69100a
|
refactor(design): resolve 拆成目录模块(单文件 ≤400 行),同步文档口径
- palette/resolve/{mod.rs,provenance.rs}:编排函数与 Provenance 类型分开,路径不变
- color/mod.rs + palettes.rs:原来的「取色/色卡库不在本 crate、还没实现」改成指向 crate::palette
- 取色 gap 登记改为「单种子已实现,多 source 色仍未实现」
|
2026-11-02 11:04:55 +08:00 |
|
AskaEth
|
18327fb038
|
feat(design): 配色来源纯数学层(Monet 式量化取色 + 色卡 + 三级回落 + 对比度收敛)
D40 分层:design 保持零第三方依赖,只吃已解码的 RGBA 像素。
- palette/quantize:5bit 直方图 + CIELAB k-means(QuantizerCelebi/Score 的形状)
- palette/score:官方 Score 口径(CUTOFF_CHROMA 5 / TARGET 48 / 权重 0.7+0.3)
- palette/seed:seed_from_rgba + D40 彩度门槛 8,灰白图回落而非凭空上色
- palette/card:8 张内置色卡,key 为持久化键
- palette/wcag:26 对文字对比度门禁 + 沿 tone 轴收敛并记录修正
- palette/resolve:纯函数三级来源编排 + Provenance(原因码/提示键)
|
2026-11-02 11:04:10 +08:00 |
|
AskaEth
|
40864d93d0
|
fix(design): M70 r4 —— 偏离登记补齐「neutral-variant 8→14」+ 文档口径与 pin 可读性
复审 P2-1:neutral-variant 的那一半偏离漏登记,且文档把 nv 说成官方相对式。
- `color/mod.rs`:标题改「两处有意偏离(同一处偏离的两半)」;偏离登记把
neutral 6→12 与 neutral-variant 8→14 分列,写明官方 `getNeutralVariantPalette`
是**绝对值 8**(不是 neutral+2)、影响角色含 outline/outline-variant/surface-variant,
以及「nv 钉回 8 会让表面(12)比中性变体(8)更有色」的倒挂理由
- `palettes.rs`:`NEUTRAL_VARIANT_OFFSET` 文档改述为「官方绝对值 8,本实现相对式=有意偏离」;
`DEFAULT_NEUTRAL_CHROMA` 的矛盾区间改正为「数值安全区间 6..=13(14 才撞 secondary),产品取 12」;
`from_seed_with_neutral_chroma` 加调用方约束(越界静默退化:<0/NaN → 彩度 0;≥14 → nv 撞 secondary);
新增 `product_default_neutral_chroma_is_a_registered_deviation`(钉 neutral 12 / nv 14,官方锚点仍 6/8)
- nit1:`sky_seed_candidates_meet_wcag_aa` 的「三档」→「四档(208 项)」
- nit2:`product_default_is_the_adopted_candidate` 改成比 hex 文案,失败时可直读「期望 #3C8DBC、实测 #87CEEB(③)」
- nit4:`lib.rs` 的 `DesignTokens::baseline` 文档改准(产品默认 ④ 天蓝,官方口径走 `Scheme::official_baseline`)
无物理行为变更:算法、tone 表、gamut、所有色值一个没动。
|
2026-11-01 18:09:14 +08:00 |
|
AskaEth
|
e37b452345
|
feat(design): M70 r2 —— 出厂默认定为 ④ 天蓝 #3C8DBC(用户 2026-11-01 拍板)
- `palettes::DEFAULT_SEED`:`#87CEEB`(③ 浅冷蓝)→ `#3C8DBC`(④ 天蓝,主色 `#236488`);
`DEFAULT_NEUTRAL_CHROMA` 保持 12
- `tests/skyseed_candidates.rs`:`product_default_is_the_recommended_candidate` →
`product_default_is_the_adopted_candidate`,绑定 `CANDIDATES[3]`(④);四档 pin 不变
- 文档:`color/mod.rs` / `palettes.rs` 的「临时默认 ③ / 待用户确认」全部换成
「出厂默认 ④(用户 2026-11-01 拍板)」,③ 标注为备选(值仍 pin 住,可一行换回)
- 文档新增定位(D38):这两个常量只定义「**莫奈关闭时的出厂预选色卡**」(配色来源第三级),
不再代表唯一配色来源;背景图/壁纸取色与内置色卡库都不在本 crate、尚未实现
|
2026-11-01 18:09:14 +08:00 |
|
AskaEth
|
9519a89472
|
feat(design): M70 r1 —— 补第 4 档「天蓝」候选 #3C8DBC(四档对比)
③ 的 primary `#0C6780` 偏青(teal),不是「天蓝」。新增 ④ 天蓝:种子 `#3C8DBC`
(CAM16 hue 239.5°,夹 ③ 与 ①② 之间)+ 中性彩度 12 → primary `#236488`。
- `tests/skyseed_candidates.rs`:候选表 3 → 4 档,④ 的 9 角色 × light/dark 全部进 pin;
`the_three_tiers_are_actually_distinguishable` 改为 `the_candidates_are_actually_distinguishable`,
增加「③ 与 ④ 中性彩度相同、主色/容器必须靠色相区分」的断言
- `tests/contrast_aa.rs`:候选 WCAG 门禁 3 → 4 档(26 × 4 × 2 = 208 项)
- 默认值**不动**:仍为 ③ `#87CEEB`(临时默认,待用户看完四档再拍);
`product_default_is_the_recommended_candidate` 保持原样,专门在默认与推荐不一致时报警
- 文档:`color/mod.rs` 与 `palettes.rs` 的候选清单补上 ④ 与「改一行即可切 ④」的说明
|
2026-11-01 18:09:13 +08:00 |
|
AskaEth
|
79404fa9d1
|
feat(design): M70 默认色候选 —— 浅冷天蓝落成两行常量 + 三档候选色卡
默认色从官方紫 `#6750A4` 换成「浅冷天蓝」,并把「换默认只改哪一行」做成常量:
- `palettes::DEFAULT_SEED` = `#87CEEB`(CSS skyblue)+ `palettes::DEFAULT_NEUTRAL_CHROMA` = 12
- `Palettes::baseline()` / `Scheme::baseline()` = 产品默认(消费方拿到的就是它)
- 官方口径锚点独立保留:`Palettes::official_baseline()` / `Scheme::official_baseline()`
(种子 `#6750A4` + 中性 6),官方对齐测试改指向它,98 项仍每通道误差 0
- 新增 `Palettes::from_seed_with_neutral_chroma(seed, neutral_chroma)`:中性彩度从写死
常数变成入参(neutral-variant 按官方 +2 跟随),算法、tone 表、gamut 映射一行未动
有意偏离登记(已写进 color 模块文档):中性彩度 6 → 12 是对官方 Tonal Spot 的偏离,
理由=产品要冷调表面;primary 的 tone 被官方钉在 40,种子再浅也变不成浅蓝,
「浅」只能落在中性色上。
三档候选(同一条 HCT 管道,只差种子与中性彩度)逐个 pin 在 tests/skyseed_candidates.rs:
① 保守蓝 `#2196F3`/6、② 冷调蓝 `#2196F3`/12、③ 浅冷蓝 `#87CEEB`/12(临时默认,待用户确认)。
WCAG AA 扩到候选档:26 组 × 3 档 × 2 模式 = 156 项全绿,最紧 4.99:1。
|
2026-11-01 18:09:12 +08:00 |
|
AskaEth
|
16bdad1b98
|
fix(design): M69 r1 —— SEARCH_BAR 内边距语义收口 + ComponentRadius 取值口上闸
P2-1(语义缝隙):`SEARCH_BAR` 的 pad_start / pad_end 原为 16,与
icon_offset = 4 按字面相加得 20,比官方图标可视边 16 多 4px(越过 rubric
±2px 容差);而被加数的 12 不在 CM 里,消费方只能自己写 12.0 —— 正是 G10
要消灭的字面量。改为 12(文本框内容内边距,Space::X3)+ 4,两侧同式:
图标可视边 = pad + icon_offset = 16。字段文档写明求和公式;测试钉死
pad + offset == 16 且 pad != 16(防有人把 16 写回 pad 再叠加)。
顺带给 SEARCH_BAR.gap 补 [估] 出处(官方无图标↔文字 token)。
P2-2(绕过路径):ComponentRadius::dp() 收成 pub(crate)。它不看尺寸、
无条件返回 2.0,公开等于把 D32 的尺寸闸交给自觉。对外唯一取值口是
radius_px(size) -> Option<f32>;ALL 只暴露变体名、不暴露数值。
tokext_pins 原用 dp(),改为紧邻的 radius_px(20.0) == Some(2.0),覆盖不丢。
顺手:修掉本轮新引入的 rustdoc warning(component_map 文档链到私有模块
metrics,改指 ComponentMetrics);Divider 行标注 motion/duration 为占位,
并在 ComponentSpec 的 text / motion 字段文档写明「无文字 / 无动效的组件
这里是占位值,消费方不得据此排版 / 排动效」。
门禁:test 165 全绿、fmt --check、clippy -D warnings、check --workspace
--all-targets、doc(warning 2 → 1,只剩既有的 typography→emphasized 一条)。
|
2026-11-01 17:05:19 +08:00 |
|
AskaEth
|
141e9b12c7
|
feat(design): M69 令牌补齐 —— 圆角分层 / 组件微度量 / 骨架值与一像素线
G13(D32 分层):新增 shape::ComponentRadius(白名单只有 Micro=2)、
MICRO_CONTROL_MAX_SIZE=24 与纯函数 radius_layer_for();微半径走
radius_px(size) -> Option<f32>,尺寸 >24dp 一律 None,靠签名而不是自觉拦。
ShapeScale 与 rank() 一个字未动,2 不进刻度。
G11:LayoutMetrics 加 search_bar_height=56 / nav_rail_width=88 /
nav_drawer_width=240 / divider_thickness=1;删除语义相反的
nav_expanded_width=88 / nav_collapsed_width=48(D33:展开 drawer 240 /
折叠 rail 88;值 88 的后继是新名字 nav_rail_width,48 无后继)。
G10:新增 component_map::ComponentMetrics(12 字段 + 4 个命名常量 +
indicator_width),入口 Component::metrics();框内几何与 LayoutMetrics 的
骨架值分工写进模块文档,骨架级尺寸在 CM 里留 0 不许重复。
G9:Component 末尾追加 Checkbox / Radio / Search / NavigationDrawer /
Divider(20 -> 25),同步 table.rs 槽位、ALL 与 name();ComponentSpec 新增
micro_radius(Some 时 shape 必须是占位 None,由构造器 + 测试钉死)。
G12:新增 spacing::stroke::HAIRLINE = 1.0(crate 根再导出为
unlucky_design::stroke),divider_thickness 直接引用它。
其余:lib.rs 的「OKLCh 近似」文案改回真实口径(真 HCT / ColorSpec2021);
component_map 单测拆到 tests.rs 以免 mod.rs 超 400 行;测试 145 -> 165。
|
2026-11-01 16:53:30 +08:00 |
|
AskaEth
|
a4d86e9b19
|
docs(design): M65 r1 收口 —— 修正覆盖数量口径(290 项 / 8 个种子)
复审 P2-1:两处「验证覆盖数量」还停留在扩种子之前的旧值,只改文档文本。
- NOTICE:「242 项」→「合计 290 项」,并写出分项口径
(baseline 49 角色 × 2 模式 = 98;强调色 8 种子 × 12 角色 × 2 模式 = 192)。
这是 Apache-2.0 归属声明,覆盖范围写错比普通注释更该修。
- crates/design/src/color/mod.rs:51:「6 个种子」→「8 个种子 × 12 个强调族角色 × 2 模式 = 192 项」。
顺带核对了 242 的来历:242 = 98 + 6×12×2 = 98 + 144,是扩到 8 个种子之前的旧值,过期而非算错。
配色相关的 HCT 彩度措辞(#E8DEF8 是 16.096 而非 11.9)此前只出现在我的完成报告里,代码/文档/测试
均未写入 11.9(已 grep 确认),故无第三处改动。
不动代码、不动测试、不动阈值。
|
2026-11-01 16:40:36 +08:00 |
|
AskaEth
|
29f65b8e6f
|
feat(design): M65 真 HCT 色彩 —— CAM16 + ColorSpec2021 + 官方逐角色对齐
把 M61 的「OKLCh 近似 CAM16 + gamut 硬裁」换成官方的 HCT 算法,与
material-color-utilities(Apache-2.0)逐角色对上:baseline 49 角色 ×
light/dark、8 个种子 × 12 个强调族角色 × 2 模式,**实测每通道误差 0**
(判据写入测试:≤1)。
实现(逐函数对照官方 Java 重写为 Rust,f64 内部精度):
- color/cam16.rs + viewing_conditions.rs:CAM16 色相/彩度(D65、200 lux、
背景 L* 50),默认观察条件的中间量与 Java 实测逐位对齐
- color/hct_solver.rs + critical_planes.rs:官方 gamut 映射(Newton 迭代 +
色调平面上的色相二分,255 项关键平面表原样搬运),只压彩度不硬裁
- color/hct.rs:HCT 坐标(hue/chroma/tone),公开给工具与测试
- color/space.rs:官方 ColorUtils 的 sRGB↔线性↔XYZ↔L* 换算(矩阵、阈值、
取整口径一致)
- color/palettes.rs:Tonal Spot 官方彩度常数 36/16/24/6/8,error 25°/84
- color/scheme.rs:tone 表换 ColorSpec2021(on-*container 10→30、
background 99/10→98/6)
两个旧缺陷关门:
- primary-container 与 secondary-container 不再撞色(彩度 36 vs 16,
tone 90 装得下,无需「共享基准压彩度」补丁);reference_chroma /
tone_scaled 随之删除
- 强调色色相不再被 gamut 硬裁(只压彩度,实测偏移 <1°)
对齐证据:tests/official_baseline.rs(98 项 + 12 个 HCT 探针)、
tests/official_accent.rs(192 项)。期望值是把官方 Java 实现本机编译运行
的输出抄本,并用 Flutter 的 ColorScheme.fromSeed 用例交叉验证
(#00FF00 → #406836、#000000 → #8C4A60、#FFFFFF → #006874 一致)。
诚实口径:只做 Tonal Spot 变体 + contrastLevel 0;对比度三档、其他变体、
壁纸多色取色、非默认观察条件都在模块文档的 gap 表里登记。
注:token_pins 的 tone 值变化来自官方算法(ColorSpec2021),不是为过测试而改。
NOTICE 增「Google Material Color Utilities」归属(Apache-2.0 要求,
含逐文件映射与对齐方法)。
|
2026-11-01 16:40:36 +08:00 |
|
AskaEth
|
83e19e58f0
|
fix(design): M61 r1 收口 —— 官方 emphasized/弹簧/高程 + 冻结测试
reviewer r1 判 p2-2items(fix-then-merge),本轮按条目收口:
P2-1 冻结测试补全(变异实测证明原来可静默漂移):
- tests/type_scale_pins.rs:baseline 15 档 (size,line_height,weight,tracking,family)
与 Expressive 强调 15 档逐值对拍,期望值独立于实现重写
- tests/token_pins.rs:49 条 tone(light+dark 全表)、7 条 easing 曲线参数、
16 档时长、9 组弹簧、28 个 IconKind 下标、形状阶梯
- 自证:TitleMedium 16→17 与 EmphasizedDecelerate 换曲线两次变异均 FAILED
P2-2 emphasized 口径事实错误(用户点名):
- 取回 A 级官方值(androidx TypeScaleTokens v0_103 的 *Emphasized* 四元组):
label-*/title-medium/title-small 强调是 700(此前给 600)、body-large 0.5→0.15、
title-medium 0.2→0.15、body-medium 0.2→0.25、display-large -0.2→0
- TypeRole::emphasized() 改为官方表(typography/emphasized.rs);
原「只加一档字重」规则改名 emphasized_simplified() 并标注非官方
- 顺带按官方订正 baseline tracking(display-large -0.2、body-medium 0.2、
title-medium 0.2)与 TitleLarge 的 Brand 族归属
- crate 文档新增「baseline MD3 vs M3 Expressive 差异清单」+ 未支持能力 gap
弹簧改为两套 token 结构(用户追加令):
- motion::spring(scheme × category × speed) 二维表 12 格:
standard spatial ζ=0.9 (k 1400/700/300)、expressive spatial ζ=0.6~0.8 (k 800/380/200)、
effects 两套同值 ζ=1.0 (k 3800/1600/800),均取 A 级 MotionTokens 原值
- Spring 改存阻尼比(与 Compose 令牌同口径),9 个具名常量
中文字重覆盖(用户追加令):
- FontFace 带 WeightCoverage(可变区间/静态表),Platform::resolve_weight 给
Native/Nearest/Synthetic 三档结论;雅黑无 500、苹方无 700 两处 gap 显式登记
component_map 高程按官方回调(P2 并入 finding):
- FilledCard L2→L0、OutlinedCard L1→L0、Menu L4→L2、Dialog L5→L3(Menu 圆角同时
按 MenuTokens 改 4dp);新增官方值 pin 测试 + R4「全屏都在飘」守门断言
P3 四条:
- 同族容器撞色:彩度按共享基准等比压缩(Palettes.reference_chroma),
primary/secondary-container 不再同值,且强调色覆盖时表面逐像素不变
- femtovg 引用改函数名(行号已漂);删掉 contrast_aa 的死代码后门
- 骨架数值跟 M63:nav 56→44、titlebar 40→48,control 40/touch 48 换算依据入文档
- 图标折算口径订正为「居中描边,视觉外扩 <1px」
|
2026-11-01 15:58:19 +08:00 |
|
AskaEth
|
65ca0ed29a
|
feat(design): 加 unlucky-design 令牌 crate(M3 色彩/排版/形状/动效/图标)
D26 把视觉基线锁到 Material 3 Expressive,本 crate 是「UI 整体性」的地基:
令牌唯一真源,后续 iced 页面只消费不新增。零第三方依赖、零 UI 框架依赖
(cargo tree -p unlucky-design --edges normal 只有它自己一行)。
- 色彩:49 个 M3 语义角色 × light/dark,种子色派生 6 条 tone 调色板;
tone 轴严格用 CIELAB L*(偏差 ≤1),色相/彩度用 OKLCh 近似 CAM16 并在
模块文档里写明口径与已知偏差(不宣称是标准 HCT 动态取色)
- 对比度:26 组文字对 × 6 组种子 × 2 模式全部 ≥4.5:1;3 条例外显式登记
(Outline 非文字 3:1、OutlineVariant 装饰、SurfaceVariant 是容器色)
- 排版:M3 type scale 15 档 + 三平台字体族回退栈(只写族名,不内嵌受限字体)
- 形状/高程/动效:Expressive 形状刻度 + shape morph 数据、elevation 级别与
surface container 映射、时长/缓动/弹簧(纯函数可采样)
- 图标:24 个从 Slint 版迁移的 SVG path + 4 个新增,IconKind 冻结只可末尾追加;
写明 Slint 的 stroke-width 是绝对像素、SVG 是 viewBox 用户单位的语义差异,
换算封装成 Icon::stroke_width
- 组件档位表:20 个组件的形状/高程/字体/动效一次取全
TODO(M63):elevation shadow 的 blur/offset/alpha 与四组弹簧参数是占位值,
接口先定、数值待玻璃视觉规格表(tower 已通告边界)。
|
2026-11-01 15:34:04 +08:00 |
|