fix(m187): r1 复审——深色遮罩提亮、点遮罩关闭、包内条目名、截断保尾、启动日志接线
P1(阻断):遮罩从 Role::OnSurface@32% 换成组件层的不透明预合成 (feedback::dialog::scrim,黑@0.32 叠画布)。深色档此前是「近白@32%」把整窗提亮, 实测 (9,21,28)→(129,138,145);现在 (6,14,19)(= 组件文档预测值),且两后端逐字节相同。 顺带按纪律改用 Component::Dialog + DialogGeometry(半径 28 / 宽 560 / 内距 24), 两后端弹窗图差异从 66% 降到 2.65%(浅)/ 2.76%(深),余量全是文字抗锯齿。 P2-1:卡片那层的命中区改回卡片本身(opaque 只包卡片)。此前 mouse_area 包的是整窗居中 容器 ⇒ 点卡片外先被 Swallow 捕获,遮罩永远收不到,「点遮罩关闭」只在注释里成立。 守卫:断言卡片层的 size_hint 任一维都不是 Fill。 P2-2:条目名与清单**同源**(BundleSpec::entry_names),会话 JSONL 钉成 session/session.jsonl。 此前条目名用原始文件名(会话 id 原样进包)而 manifest 过了 redactor ⇒ 清单指向的条目不存在。 (上一轮报告把这条写反了,本轮以 unzip -l 实输出为准。) P2-3:截断改「头 + 尾」(MAX_ENTRY_BYTES 一半一半,中间插 <truncated N bytes>), 并在弹窗补一句「有 N 个文件太大,已截断」。此前只留头部 ⇒ 长会话 JSONL 的 exit 事件丢失。 P2-4:app 启动那一拍(主题/语言/路由/窗口/实例数/msa 行)真的写进 launcher_log; 目录仍由 presenter 在首次启动时登记,所以这些行先进 core 缓冲、登记时一次刷出。 P3-1/P3-2:关闭与 Esc 立即重画(否则空闲 1Hz 下弹窗再留 1 秒);导出成败按 write_bundle 真实结果判(不再看 launcher_logs 有没有值)。 用例:新增 5 条(深色遮罩方向、卡片层命中区、条目名与清单一致、截断保尾、启动日志落点), 每条都给了变异证明(只改实现侧,均已还原)。
This commit is contained in:
1 parent
414f6ff886
commit
3cd371b537
11 files changed
+513
-112
No files matched your search
@@ -247,7 +247,10 @@ fn export_writes_a_bundle_even_without_a_crash_report() {
|
||||
spec_report.run_dir = None;
|
||||
spec_report.version_json = None;
|
||||
|
||||
let text = super::export::export(&spec_report, Lang::ZhCn);
|
||||
let done = super::export::export(&spec_report, Lang::ZhCn);
|
||||
let text = done.text.clone();
|
||||
assert!(done.ok, "{text}");
|
||||
assert_eq!(done.truncated, 0);
|
||||
assert!(text.contains("已导出"), "{text}");
|
||||
assert!(text.contains("字节"), "{text}");
|
||||
|
||||
@@ -264,3 +267,106 @@ fn export_writes_a_bundle_even_without_a_crash_report() {
|
||||
"报出来的字节数要与磁盘一致:{text} / {size}"
|
||||
);
|
||||
}
|
||||
|
||||
/// P2-1:**卡片那一层只能是卡片大**。
|
||||
///
|
||||
/// 抓得住什么:r1 复审实测的那个 bug —— `mouse_area(整窗居中容器)` 的命中区是整窗,
|
||||
/// 卡片外任何一次点击都先被它捕获,遮罩永远收不到(「点遮罩关闭」形同虚设)。
|
||||
/// `Widget::size_hint()` 不需要渲染器就能问,所以这条能在单测里跑:卡片层若声明
|
||||
/// `Fill`/`Fill`,就等于「命中区 = 整窗」。
|
||||
///
|
||||
/// **抓不住什么**:端到端指针派发(本仓没有事件 harness,`Element` 的 `layout`/`update`
|
||||
/// 需要真渲染器)。所以这条守的是**结构属性**,不是「点一下真的关了」。
|
||||
#[test]
|
||||
fn the_card_layer_is_card_sized_so_the_backdrop_can_be_clicked() {
|
||||
use iced::Length;
|
||||
|
||||
let mut app = headless();
|
||||
app.state.dialogs.push(
|
||||
DialogSource::UserAction,
|
||||
"t",
|
||||
"b",
|
||||
DialogKind::Confirm {
|
||||
primary: "p".to_string(),
|
||||
secondary: "s".to_string(),
|
||||
},
|
||||
);
|
||||
let modal = {
|
||||
let request = app.state.dialogs.current().expect("当前条");
|
||||
let view = DialogRequestView::of(request);
|
||||
super::current(app.lang(), &view, None)
|
||||
};
|
||||
let plane = super::view::card_plane(app.ui(), &modal);
|
||||
let hint = iced::advanced::Widget::size_hint(plane.as_widget());
|
||||
// 任一维是 `Fill` 就意味着「跟随父级 = 整窗」(外层是 Fill 的居中容器),
|
||||
// 命中区于是变成整窗 —— 那正是 r1 抓到的原实现。
|
||||
assert_ne!(
|
||||
hint.width,
|
||||
Length::Fill,
|
||||
"卡片层宽度不能 Fill(会撑成整窗)"
|
||||
);
|
||||
assert_ne!(
|
||||
hint.height,
|
||||
Length::Fill,
|
||||
"卡片层高度不能 Fill(会撑成整窗)"
|
||||
);
|
||||
}
|
||||
|
||||
/// P1-1:遮罩必须是**不透明预合成**,而且两档都**压暗**(深色档尤其)。
|
||||
///
|
||||
/// 抓得住 r1 实测的那个 p1:`Role::OnSurface @ 32%` 在深色档是「近白 @32%」⇒ 遮罩把整窗
|
||||
/// 提亮(实测 (9,21,28) → (129,138,145))。这里用亮度直接判方向:遮罩必须比画布暗。
|
||||
///
|
||||
/// 抓不住:具体色值对不对(那是令牌层的事)。
|
||||
#[test]
|
||||
fn the_backdrop_is_an_opaque_precomposite_and_darkens_both_flavors() {
|
||||
use unlucky_design::color::{Role, SchemeFlavor};
|
||||
use unlucky_design::elevation::MaterialLayer;
|
||||
use unlucky_design::typography::Platform;
|
||||
|
||||
let luma = |color: iced::Color| 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
|
||||
for flavor in [SchemeFlavor::Light, SchemeFlavor::Dark] {
|
||||
let ui = crate::theme::Ui::with_platform(flavor, Platform::Windows, FAMILIES);
|
||||
// 断言**视图真正用的那一份**(不是组件层的默认值):改回「OnSurface @32%」这里就红。
|
||||
let backdrop = super::view::backdrop_color(&ui);
|
||||
assert_eq!(
|
||||
backdrop.a, 1.0,
|
||||
"{flavor:?}:遮罩必须是不透明预合成(D27:CPU 档零半透明)"
|
||||
);
|
||||
|
||||
let canvas = crate::theme::to_iced(
|
||||
unlucky_design::color::Scheme::baseline(flavor)
|
||||
.get(MaterialLayer::Canvas.surface_role()),
|
||||
);
|
||||
assert!(
|
||||
luma(backdrop) < luma(canvas),
|
||||
"{flavor:?}:遮罩必须比画布暗(当前 lane {:.3} vs 画布 {:.3})—— 用 Role::OnSurface 当遮罩会在深色档把整窗提亮",
|
||||
luma(backdrop),
|
||||
luma(canvas)
|
||||
);
|
||||
assert_eq!(Role::Surface, MaterialLayer::Canvas.surface_role());
|
||||
}
|
||||
}
|
||||
|
||||
/// P2-4:启动那一拍的**事实**确实进了启动器日志(登记目录后一次刷出)。
|
||||
///
|
||||
/// 抓得住:记录函数被拆掉/改空(缓冲里就什么都没有)。
|
||||
/// 抓不住:`on_boot` 有没有调它 —— 那靠这条用例旁边的调用点与 `host/launch.rs` 的 `set_dir`,
|
||||
/// 真机第一次崩溃才是最终验收闸门(复审与我的一致口径)。
|
||||
#[test]
|
||||
fn boot_facts_are_recorded_into_the_launcher_log() {
|
||||
let dir = std::env::temp_dir().join(format!("ull-m187-bootlog-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).expect("建目录");
|
||||
unlucky_core::launch::launcher_log::set_dir(&dir);
|
||||
|
||||
let app = headless();
|
||||
crate::app::update::boot_env::record_boot_log(&app);
|
||||
|
||||
let path = dir.join(unlucky_core::launch::launcher_log::FILE_NAME);
|
||||
let text = std::fs::read_to_string(&path).expect("launcher.log");
|
||||
assert!(text.contains("[boot] app boot"), "{text}");
|
||||
assert!(text.contains("theme="), "{text}");
|
||||
assert!(text.contains("window="), "{text}");
|
||||
}
|
||||
Reference in new issue
Block a user