feat(m187): 启动失败弹窗 + 诊断包导出,并接通 DialogQueue

用户实测「MC 启动失败找不到崩溃日志」的直接修法:失败时弹一个框,把退出码、
一句人话原因、证据形态、四条真实路径与三个按钮摆在同一个地方。

- app-iced:最小一层模态(遮罩 + 卡片 + 主/次按钮 + Esc/点遮罩关闭),
  消费 presenter 的 DialogQueue(此前全仓零引用,所有弹窗都画不出来);
  队列管弹窗,CrashCard 管这次失败的事实,按 DialogId 绑定。
- 弹窗区分两种失败:游戏自己写了 crash-reports(贴 Description + 栈片段)/
  游戏没来得及写(明说「这次没有生成崩溃报告」并贴出捕获的最后 30 行)/
  什么都没抓到(给最可能原因分类)。绝不对第二种说「找不到日志」。
- 导出诊断包:core 侧新 crash_bundle 写 zip(清单 + 启动器日志 + 会话 JSONL +
  crash-reports/* + hs_err + latest.log + 版本 JSON),落盘即脱敏(家目录 / token /
  会话 id),单文件 4 MiB、总量 48 MiB 上限并在清单记截断。
- 启动器自身日志落盘(core::launch::launcher_log → <.ULL>/logs/launcher.log,
  登记目录前先缓冲),补上「双击启动时诊断行直接蒸发」的缺口。
- i18n:25 条中英文案进 catalog/logs.rs + 金表。
- 取证:ULL_DIALOG_FIXTURE=crash-report|no-logs|export 在临时目录造真文件出图,
  wgpu 与 tiny-skia 各一套,见 DevDocs/artifacts/m187/。

未复现 M190 的「owned 文字在 wgpu 下丢字」:同一串中文两条文字路径、两种后端逐字相同
(ab-wgpu.png / ab-tinyskia.png),因此本轮未改任何渲染路径。
This commit is contained in:
AskaEth committed 2026-11-08 22:42:00 +08:00
1 parent 29da7e2e47
commit 414f6ff886
23 files changed
+2818 -10

No files matched your search

+24
View File
@@ -244,6 +244,30 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
("LogsEmptyTitle", "No output yet", "还没有输出"),
("LogsEmptyBody", "Logs stream here while the launcher or the game is running. Pick an instance and start it to see the first lines.", "启动器或游戏运行时,日志会实时出现在这里。选择一个实例并启动,就能看到第一批日志。"),
("LogsEmptyAction", "Choose an instance", "选择实例"),
("CrashDialogTitleFailed", "Launch failed", "启动失败"),
("CrashDialogTitleCrashed", "The game crashed", "游戏崩溃"),
("CrashDialogExitCode", "Exit code {code}", "退出码 {code}"),
("CrashDialogStartedThenDied", "The game process started, then exited abnormally.", "游戏进程起来了,随后异常退出。"),
("CrashDialogSilentFailure", "The game exited with code 0 but never really started.", "游戏以退出码 0 结束,但并没有真正起来。"),
("CrashDialogStageReason", "It failed before the game could start: {stage}", "游戏还没起来就失败了:{stage}"),
("CrashDialogCrashReportFound", "The game wrote a crash report — the details are below.", "游戏写出了崩溃报告,详见下方说明。"),
("CrashDialogNoGameLog", "No crash report this time: the game stopped before it could write one. The last lines the launcher captured are below.", "这次没有生成崩溃报告:游戏还没来得及写就停了。下面是启动器捕获的最后若干行。"),
("CrashDialogNoLogAtAll", "No crash report and no captured output. Most likely: missing Java, not enough memory, wrong launch arguments, or missing libraries.", "既没有崩溃报告,也没有捕获到输出。最可能的原因:缺少 Java、内存不足、启动参数错误、或库文件缺失。"),
("CrashDialogShowDetail", "Show crash details", "展开崩溃说明"),
("CrashDialogHideDetail", "Hide crash details", "收起崩溃说明"),
("CrashDialogLauncherLogs", "Launcher logs", "启动器日志"),
("CrashDialogSessionLog", "This session", "本次会话"),
("CrashDialogGameLog", "Game latest.log", "游戏 latest.log"),
("CrashDialogCrashReports", "Crash reports", "崩溃报告目录"),
("CrashDialogCapturedTail", "CAPTURED OUTPUT (last {count} lines)", "捕获输出(最后 {count} 行)"),
("CrashDialogExport", "Export report bundle", "导出诊断包"),
("CrashDialogOpenFolder", "Open log folder", "打开日志文件夹"),
("CrashDialogCopyPaths", "Copy file paths", "复制文件路径"),
("CrashDialogClose", "Close", "关闭"),
("CrashDialogCopied", "File paths copied to the clipboard", "文件路径已复制到剪贴板"),
("CrashDialogExportDone", "Exported {bytes} bytes to {path}", "已导出 {bytes} 字节到 {path}"),
("CrashDialogExportFailed", "Export failed: {error}", "导出失败:{error}"),
("CrashDialogOpenFailed", "Could not open the folder: {error}", "打不开文件夹:{error}"),
("AgentTitle", "Assistant", "助手"),
("AgentSubtitle", "Simulated assistant · no real model is connected", "模拟助手 · 还没接真实模型"),
("AgentCollapse", "Collapse", "收起"),
+25
View File
@@ -33,6 +33,31 @@ macro_rules! logs_entries {
"启动器或游戏运行时,日志会实时出现在这里。选择一个实例并启动,就能看到第一批日志。"
),
LogsEmptyAction => ("Choose an instance", "选择实例"),
// --- 启动失败弹窗(M187)---
CrashDialogTitleFailed => ("Launch failed", "启动失败"),
CrashDialogTitleCrashed => ("The game crashed", "游戏崩溃"),
CrashDialogExitCode => ("Exit code {code}", "退出码 {code}"),
CrashDialogStartedThenDied => ("The game process started, then exited abnormally.", "游戏进程起来了,随后异常退出。"),
CrashDialogSilentFailure => ("The game exited with code 0 but never really started.", "游戏以退出码 0 结束,但并没有真正起来。"),
CrashDialogStageReason => ("It failed before the game could start: {stage}", "游戏还没起来就失败了:{stage}"),
CrashDialogCrashReportFound => ("The game wrote a crash report — the details are below.", "游戏写出了崩溃报告,详见下方说明。"),
CrashDialogNoGameLog => ("No crash report this time: the game stopped before it could write one. The last lines the launcher captured are below.", "这次没有生成崩溃报告:游戏还没来得及写就停了。下面是启动器捕获的最后若干行。"),
CrashDialogNoLogAtAll => ("No crash report and no captured output. Most likely: missing Java, not enough memory, wrong launch arguments, or missing libraries.", "既没有崩溃报告,也没有捕获到输出。最可能的原因:缺少 Java、内存不足、启动参数错误、或库文件缺失。"),
CrashDialogShowDetail => ("Show crash details", "展开崩溃说明"),
CrashDialogHideDetail => ("Hide crash details", "收起崩溃说明"),
CrashDialogLauncherLogs => ("Launcher logs", "启动器日志"),
CrashDialogSessionLog => ("This session", "本次会话"),
CrashDialogGameLog => ("Game latest.log", "游戏 latest.log"),
CrashDialogCrashReports => ("Crash reports", "崩溃报告目录"),
CrashDialogCapturedTail => ("CAPTURED OUTPUT (last {count} lines)", "捕获输出(最后 {count} 行)"),
CrashDialogExport => ("Export report bundle", "导出诊断包"),
CrashDialogOpenFolder => ("Open log folder", "打开日志文件夹"),
CrashDialogCopyPaths => ("Copy file paths", "复制文件路径"),
CrashDialogClose => ("Close", "关闭"),
CrashDialogCopied => ("File paths copied to the clipboard", "文件路径已复制到剪贴板"),
CrashDialogExportDone => ("Exported {bytes} bytes to {path}", "已导出 {bytes} 字节到 {path}"),
CrashDialogExportFailed => ("Export failed: {error}", "导出失败:{error}"),
CrashDialogOpenFailed => ("Could not open the folder: {error}", "打不开文件夹:{error}"),
} }
};
}