fix(app-iced): M95 r3 摘要页脚占比 4:2 + 演示数据自洽 + 样例解析不再 panic
视觉复审 r2 的 P2-A 与两个顺手的 p3。
**P2-A 摘要页脚占比过大(3:2 → 4:2)**:1180×760 下诊断卡 338px,摘要页脚固定吃掉 ~150px
(标签+按钮 40 + 摘要折 5 行 80 + 间距/分割 30),滚动视口只剩 141px,而一条完整结论
(分组标题 → 建议动作 → 置信度)要 165px ⇒ 首屏看不到「建议动作」,那是任务清单 T1/T3 要求的
可见项。改成 `DIAGNOSIS_SHARE = 4`(`LOGS_SHARE` 仍 2)。**数值实测**(`r3-crash-light.png`
逐像素量,非估算):诊断卡 376px、滚动视口 **178px**(y 201–378,两侧 1px 分割线的位置)、
一条完整结论块 **165px**、底部余量 **13px** —— 建议动作与 `Confidence 85%` 整块在首屏可见。
**p3 演示数据自相矛盾**:`live` 状态页头写「Finished normally」却配崩溃日志尾行
(`启动失败:exit code 1`)。新增 `sample_live.log`(一场正常跑完的会话:加载 → 进世界 →
停服),`State::Live` 用它,不再复用崩溃样例。
**p3 `demo::parse` 对非 ASCII 首字符 panic**:`raw.split_at(1)` 会切在多字节序列中间。
改成按字符取前缀(`chars().next()` + `chars.as_str()`);顺带把它提成 `pub(super)` 以便测。
新增 `the_fixture_parser_survives_a_non_ascii_first_char`:非 ASCII 首字符不 panic(静默跳过),
并钉住 `I/W/E` 前缀与注释/空行的解析。
测试:**278 passed / 0 failed**(lib)+ 6(`no_hardcoded_tokens`)+ 23(i18n)全绿;
`fmt --check` 通过;`clippy -p unlucky-app-iced -p unlucky-i18n --all-targets -D warnings` 干净。
出图:`C:\Temp\ull-shots-m95\r3-{crash-light,crash-dark,live-light,empty-light}.png`(1180×760)。
This commit is contained in:
1 parent
62d4af150e
commit
5ed2bf3a7d
5 files changed
+65
-12
No files matched your search
@@ -34,6 +34,9 @@ pub const STATE_ENV: &str = "ULL_LOGS_FIXTURE";
|
||||
/// 内嵌的崩溃日志片段(见模块文档的出处)。
|
||||
const SAMPLE: &str = include_str!("sample_crash.log");
|
||||
|
||||
/// 内嵌的**正常跑完**一场的日志片段(`live` 状态用;见 `sample_live.log` 的文件头)。
|
||||
const LIVE_SAMPLE: &str = include_str!("sample_live.log");
|
||||
|
||||
/// 演示状态。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum State {
|
||||
@@ -61,10 +64,14 @@ impl State {
|
||||
pub fn build(self, lang: Lang) -> Logs {
|
||||
match self {
|
||||
Self::Empty => Logs::default().with_status(lang),
|
||||
// 正常跑完的一场用**自己的**日志样例:页头写「正常结束」却配崩溃日志尾行是自己打自己
|
||||
// (视觉复审 r2 的 p3)。
|
||||
Self::Live => Logs {
|
||||
instance: Some("开发测试".to_owned()),
|
||||
when: Some("11-02 19:22".to_owned()),
|
||||
outcome: Some(Outcome::Ok),
|
||||
findings: Vec::new(),
|
||||
..crash()
|
||||
lines: parse(LIVE_SAMPLE),
|
||||
..Logs::default()
|
||||
}
|
||||
.with_status(lang),
|
||||
Self::Crash => crash().with_status(lang),
|
||||
@@ -94,7 +101,7 @@ pub fn crash() -> Logs {
|
||||
}
|
||||
|
||||
/// 解析内嵌样例:`#` 注释与空行跳过,首字符是等级前缀。
|
||||
fn parse(text: &str) -> Vec<Line> {
|
||||
pub(super) fn parse(text: &str) -> Vec<Line> {
|
||||
text.lines()
|
||||
.filter_map(|raw| {
|
||||
let raw = raw.trim_end();
|
||||
@@ -102,17 +109,19 @@ fn parse(text: &str) -> Vec<Line> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (prefix, rest) = raw.split_at(1);
|
||||
let level = match prefix {
|
||||
"D" => Level::Debug,
|
||||
"I" => Level::Info,
|
||||
"W" => Level::Warn,
|
||||
"E" => Level::Error,
|
||||
// 按**字符**取等级前缀,不 `split_at(1)`:后者在非 ASCII 首字符上会切到
|
||||
// 多字节序列中间直接 panic(视觉复审 r2 的 p3)。
|
||||
let mut chars = raw.chars();
|
||||
let level = match chars.next() {
|
||||
Some('D') => Level::Debug,
|
||||
Some('I') => Level::Info,
|
||||
Some('W') => Level::Warn,
|
||||
Some('E') => Level::Error,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Line {
|
||||
text: rest.trim_start().to_owned(),
|
||||
text: chars.as_str().trim_start().to_owned(),
|
||||
level,
|
||||
})
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user