feat(app-iced): 强调色接线落地(M250 ACCENTWIRE)

appearance.accent_color 此前是死线:配置键、presenter 往返、预设色板都在,但造 tokens 的
唯一入口恒走 DesignTokens::brand(board),用户填了色界面不变。

- theme.rs:Ui::with_accent + accent_scheme(唯一调用 DesignTokens::with_accent 的地方)
- app/state/theme.rs:rebuild_ui 三选一(显式强调色 > D38 取色方案 > 出厂板),diag 一行说清是谁生效
- pages/appearance:新增 accent 状态(原文/解析结果/方案/结构化错误)+ 设置页输入行与预设色块;
  非法值保持上一次合法值并给出 Short/Long/Digit 三档原因,不静默回退出厂色
- presenter::prefs:AccentChoice::parse 成为唯一解析点,set_accent_checked 交回结构化原因
- app/appearance.rs:persist_appearance 带上 accent_color(此前写不进去),只写上一次合法值
- pages/harness/app.rs:gallery 也能吃 ULL_APPEARANCE_ACCENT,供出图矩阵
- core keys.rs:订正「空 = 出厂 #0078D4;accent 令牌全域生效」这句半真半假的说明
- i18n:强调色 7 条文案(链尾新域,金表只追加)

判据:同一组件(进度条 Primary)在空配置与 #006A5C 下色值不同(App 级 + 令牌级两条);
非法值拿到结构化错误、生效色不回退、不写盘、tokens 仍可构造。
This commit is contained in:
AskaEth committed 2026-11-11 14:56:26 +08:00
1 parent 4cdb46d2e0
commit e6b2200360
18 files changed
+787 -38

No files matched your search

+17 -4
View File
@@ -21,6 +21,8 @@ use unlucky_i18n::catalog::Key;
use unlucky_i18n::Lang;
use unlucky_presenter::prefs::{keys, BgFill, PaletteSource};
mod accent;
/// 抓得住:每条改动都点名到自己那个 schema 键(写错键 = 静默写到别的配置项上)。
/// 抓不住:这些键是否真的在 core 的 schema 白名单里(那由 presenter/core 的测试守)。
#[test]
@@ -93,7 +95,7 @@ fn appearance_texts(lang: Lang, case: Case) -> Vec<(Key, String)> {
};
let (backend, alpha) = match case {
Case::Factory => (Backend::Auto, 1.0),
Case::Factory | Case::BadAccent => (Backend::Auto, 1.0),
Case::Cpu => (Backend::Cpu, 1.0),
Case::Dim => (Backend::Gpu, 0.5),
};
@@ -106,6 +108,10 @@ fn appearance_texts(lang: Lang, case: Case) -> Vec<(Key, String)> {
if alpha < 1.0 {
state.apply(Change::WindowOpacity(alpha));
}
if matches!(case, Case::BadAccent) {
// M250:错行只在失败态出现,所以「三档状态合起来覆盖本页全部 key」需要这一档。
state.apply(Change::Accent("zzzzzz".to_owned()));
}
let ui = Ui::new(SchemeFlavor::Light, FAMILIES);
// 前面几条用例可能留了记录:本线程的取证每次都从清空开始。
@@ -142,6 +148,8 @@ enum Case {
Cpu,
/// 整窗 α 调低(圆角暂时不生效)。
Dim,
/// 强调色填了非法值(M250:错行 + 输入框失败态)。
BadAccent,
}
/// 这一页**常驻**的 key(三档状态合起来必须一个不缺地取到)。
@@ -150,6 +158,11 @@ enum Case {
/// 小图平铺)取决于运行时状态,单测里要让它们出现得造磁盘上的坏图;那些由
/// `background/tests.rs` 与 `palette.rs` 的单测按 key 逐条钉(见各自的用例注释)。
const CARD_KEYS: &[Key] = &[
Key::SettingsAppearanceAccent,
Key::SettingsAppearanceAccentPresets,
Key::SettingsAppearanceAccentFactory,
Key::SettingsAppearanceAccentHint,
Key::SettingsAppearanceAccentDigit,
Key::SettingsAppearanceCorner,
Key::SettingsAppearanceCornerSystem,
Key::SettingsAppearanceCornerSquare,
@@ -191,7 +204,7 @@ fn the_appearance_card_speaks_chinese() {
// 白名单:技术专名允许是纯 ASCII(现在一条都没有——`%` / 数字不在文案里,它们是参数)。
const ASCII_OK: &[&str] = &[];
for case in [Case::Factory, Case::Cpu, Case::Dim] {
for case in [Case::Factory, Case::Cpu, Case::Dim, Case::BadAccent] {
let pairs = appearance_texts(Lang::ZhCn, case);
assert!(
pairs.len() >= 20,
@@ -222,7 +235,7 @@ fn the_appearance_card_speaks_chinese() {
#[test]
fn every_appearance_card_key_is_drawn() {
let mut seen: Vec<Key> = Vec::new();
for case in [Case::Factory, Case::Cpu, Case::Dim] {
for case in [Case::Factory, Case::Cpu, Case::Dim, Case::BadAccent] {
seen.extend(
appearance_texts(Lang::ZhCn, case)
.into_iter()
@@ -244,7 +257,7 @@ fn every_appearance_card_key_is_drawn() {
/// 英文界面下不许出现汉字(否则「含汉字」可能来自写死的中文,而不是词表)。
#[test]
fn the_appearance_card_keeps_its_english_baseline() {
for case in [Case::Factory, Case::Cpu, Case::Dim] {
for case in [Case::Factory, Case::Cpu, Case::Dim, Case::BadAccent] {
let texts = appearance_texts(Lang::En, case);
let with_han: Vec<&(Key, String)> = texts
.iter()