fix(app-iced): 三处 Pixel 偏离按角色语义收敛(M248)

P2:Elevated 的容器改由高程令牌推导(Elevation::Level1.surface_role),解析值仍是
SurfaceContainerLow,图上逐像素不变;「比 Surface 高一档」从此只有 elevation.rs 一处定义,
组件里不再手抄角色名。

P3:Outlined 的 1dp 描边 OutlineVariant → Outline。design/color/mod.rs 的对比度纪律里
Outline 才是「非文字元素(目标 3:1)」,OutlineVariant 是纯装饰分隔线;图标按钮那一式本来
就是 Outline,文字按钮此前与它不一致(全站两套描边)。实测 Outline/Surface:Ink 5.08、
Silver 4.24(OutlineVariant 1.97 / 1.62)。

P4:进度轨道(线性 + 环形)SecondaryContainer → SurfaceContainerHighest——轨道是「还剩多少」
的背景尺,强调色只属于指示条。与 A1(LinearProgressIndicatorTokens.TrackColor)的差异在
progress.rs 模块文档与 geometry.rs 的 12.2 行登记,gallery 图注同步改成角色名。

判据:新增 buttons/tests/roles.rs 与进度轨道用例,断言的是「取到的角色」与 Ink / Silver /
Scheme::from_seed 三套方案下解析出的颜色,不钉任何 hex;三处各做一次变异自证(改回旧值即红)。

另修 feedback probe 的定位判据:轨道换成中性容器面后,「画布→轨道」的混色轴穿过中性文字,
按「计数」会把图注行数成轨道,改按「连续段」判(bands_by_run);顺带修掉 indeterminate_row
把标题行当主色带的既有问题(用改前的图 + 改前的探针复现过,与本次换色无关)。
This commit is contained in:
AskaEth committed 2026-11-11 14:32:26 +08:00
1 parent 81710ba38c
commit 8f5dac2ed6
10 files changed
+402 -34

No files matched your search

@@ -209,6 +209,50 @@ pub(super) fn band_width(shot: &Shot, band: (usize, usize), fill: Color, under:
.unwrap_or(0)
}
/// 按「**每行最长连续段** ≥ `min_run`」聚成的竖带。
///
/// 与 [`bands`] 的区别只在「一行算不算墨」的判法:`bands` 数**覆盖率 ≥50% 的像素个数**,
/// 本函数要求这些像素连成一长段。M248 换了进度轨道的角色之后,这个区别变成了分水岭:
///
/// - 轨道从强调色族(`SecondaryContainer`)改成中性容器面(`SurfaceContainerHighest`)后,
/// 「画布 → 轨道」这条混色轴**正好穿过中性文字与中性容器面**:文字像素的投影 `t` 会 clamp 到
/// 1.0、残差只剩 <8,于是整行文字都能被数成「轨道墨」(浅色板实测:命中行数从 2 条涨到 5 条,
/// 多出来的三条是图注文字,高度 6 / 2 / 6 行);
/// - 按**连续段**判就不受影响:一行 12px 的字形边缘凑不出 200px 的连续同色段,而 360px 的轨道
/// 一眼就能认出来。
///
/// 阈值的取值口径:轨道带最窄的整段是 200px 以上(线性进度宿主 320/360 宽,指示条压在轨道上
/// 会把轨道切成几段,最长的那段仍 ≥200——不加指示条的确定态那一格是整条 360)。
pub(super) fn bands_by_run(
shot: &Shot,
fill: Color,
under: Color,
min_run: usize,
) -> Vec<(usize, usize)> {
let mut found: Vec<(usize, usize)> = Vec::new();
let mut current: Option<usize> = None;
for y in 0..shot.height {
let longest = runs(shot, y, fill, under)
.into_iter()
.map(|(_, len)| len)
.max()
.unwrap_or(0);
let hit = longest >= min_run;
match (hit, current) {
(true, None) => current = Some(y),
(false, Some(top)) => {
found.push((top, y - 1));
current = None;
}
_ => {}
}
}
if let Some(top) = current {
found.push((top, shot.height - 1));
}
found
}
/// 某种颜色的**精确色**连通块(只用于「找着一大块实心」这种粗定位)。
pub(super) fn blobs(shot: &Shot, color: Color, min_side: usize) -> Vec<BBox> {
let target = rgb(color);