Files
Unlucky-Launcher/crates/app-iced/src/app/mod.rs
T
worker-logplace 5fd5cd47dc feat(m188): launcher.log 早落盘 + 会话日志搬到 logs/game(旧家只读兼容)
用户投诉原话:「我们的启动日志放在哪?…他找半天没找到崩溃日志」「只有一个安装日志」。

启动器运行日志(.ULL/logs/launcher.log)
- presenter 构造 HostEnv 时就登记日志目录:启动早期缓冲的 200 行([probe]/[font]/[theme])
  当场刷盘,不再是「只有开过一局游戏才有日志」。
- app/** 里所有 eprintln! 诊断改走 app::diag::diag!(stderr + 文件双写,行首 [tag] 即 scope),
  含 boot/boot::fallback/state/update 各线。
- boot 那一拍补一行后端事实(request/effective/fallback)。
- 滚动上限(1 MiB → launcher.1.log,只留一份)写进 README 与 docs/logs-location.md。

会话日志搬家:data/agent/sessions → logs
- LauncherLayout: sessions_root()/game_logs() 新增,agent_sessions() 降级为只读兼容。
- host/logs 与 cli/commands/sessions 两个根合并(同 id 新根优先),event_path 回落旧家。
- 不搬不删;日志目录写一份 session-log-location.json 记录(moved=0, policy=read-only-compat)。

入口与文案
- 设置页「通用」新增「日志文件夹」行:路径 + 打开目录 + 复制路径(i18n 中英)。
- 日志页页头的「打开目录」不再是占位:走 M187 的 open_dir,失败文本带路径并送进剪贴板。
- README.md / README.zh-CN.md 新增「日志在哪」章节(含 crash-reports 只在游戏走到那一步才有),
  docs/logs-location.md 给出上限、双写判据、迁移策略与没验的部分。

守门(每条都有变异证明,见报告)
- 旧家会话仍列得出/打不开 ⇒ 只读新根变红;新会话写 logs/game ⇒ 只读旧根、写回旧家都变红。
- launcher.log 滚动 ⇒ 干掉 rename 变红。
- 打开日志文件夹指向真实目录 ⇒ 换成 cache/ 或去掉 create_dir_all 都变红。
- 日志根随三模式变化 ⇒ 把 HostEnv::logs_dir 写死成便携落点变红。
2026-11-09 00:43:45 +08:00

75 lines
3.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Elm 三件套:`State` / `Message` / `update` + `view` 接线,以及启动序列。
//!
//! 启动序列:①探测渲染后端并写 `ICED_BACKEND`(必须在 `run()` 之前,M60 Q1)→ ②求值字体栈
//! (D28 前半)→ ③`iced::application(..).run()`(1180×760、无边框自绘标题栏)→ ④首个 `view()`
//! 打耗时;截图模式第 3 帧落盘后退出(无边框窗首帧可能是旧尺寸,M60 坑 #5)。
//!
//! ## 为什么拆成目录(M90)
//!
//! 本文件原来是**单个 400 行的 `app.rs`**:正好卡在「单文件 ≤400 行、动它前必须先拆」的硬门槛上
//! (AGENTS 的文件上限 + 本 mission 的第一条任务)。拆法与职责:
//!
//! | 文件 | 职责(**只做这一件事**) |
//! |---|---|
//! | [`state`] | [`App`] 字段 + 构造(presenter 三件在此持有)+ 主题切换 |
//! | [`message`] | [`Message`] 枚举与「导航项 → 路由」的翻译 |
//! | [`update`] | 全部 `Message` 分支 + 「用户动作 → `AppCommand`」的派发与事件回流 |
//! | [`subscription`] | 帧事件(截图)与 presenter 的 TICK 节拍 |
//! | [`view`] | `view` / `theme` / `title` |
//! | [`boot`] | [`run`]:后端探测 → 字体 → `iced::application(..)` |
//!
//! 拆分的每一步都保持**可观察行为不变**(截图四件套 `--screenshot` / `--screenshot-theme` /
//! `--screen=gallery` / `--diff` 出图逐字节相同),presenter 接线是在拆分**之后**另一步做的。
//!
//! ## 组件库为什么还在本模块下声明
//!
//! `src/lib.rs` 是五条组件线共用的文件,改它必然冲突;`#[path]` 保证物理路径仍是
//! `src/components/`(组件目录自己的归属规则不变),逻辑路径仍是 `crate::app::components`
//! (组件源码里的文档与测试都按这个路径写)。
mod appearance;
mod backend;
mod boot;
mod diag;
mod dialog;
pub(crate) mod logs_folder;
mod message;
mod state;
mod subscription;
mod update;
mod view;
mod window_alpha;
#[cfg(test)]
mod tests;
pub(crate) use boot::run;
pub use message::Message;
// 组件库(D39)在本文件声明:`src/lib.rs` 是五条组件线共用的文件,改它必然冲突;
// `#[path]` 保证物理路径仍是 `src/components/`(组件目录自己的归属规则不变)。
//
// M90:可见性从 `mod` 放到 `pub(crate) mod` —— 页面层(`src/pages/**`)要**用**真组件
// (`components::buttons` / `navigation` / `divider`),而 `app` 不是它的父模块。
// 只放开 crate 内可见,组件目录的归属规则一字不改。
#[path = "../components/mod.rs"]
pub(crate) mod components;
/// 验收窗口尺寸(1180×760 @100% DPI —— D30 的硬要求)。
pub const WINDOW_SIZE: iced::Size = iced::Size {
width: 1180.0,
height: 760.0,
};
/// 产品名(自绘标题栏的标题;`'static` 好让 view 借用)。
pub const APP_TITLE: &str = "Unlucky Launcher";
/// 窗口最小尺寸(再小骨架会挤坏:88 导航 + 页面留白 + 卡片)。
const MIN_SIZE: iced::Size = iced::Size {
width: 880.0,
height: 560.0,
};
/// 截图落盘的帧号(见模块文档)。
const CAPTURE_AT_FRAME: u32 = 3;