feat(app-iced): M90 —— app.rs 拆成 src/app/ + presenter 真接线 + 实例页端到端

拆分(行为不变,截图四件套逐字节相同):
- 400 行的 app.rs 拆成 src/app/{mod,state,message,update,subscription,view,boot,tests}.rs,
  每个文件 ≤ 300 行;components 的 #[path] 跟着搬到 src/app/mod.rs(逻辑路径仍是 crate::app::components),
  可见性放到 pub(crate) 让页面层能用真组件。
- 拆分前后各出 shell/gallery 浅深四张图,cmp 全部 IDENTICAL。

presenter 接线(crate 文档 §4):
- App 持有 Box<dyn Backend> + SettingsBridge + AppState;启动 build_backend + SettingsBridge::open + AppState::new。
- 截图通道强制 mock(D30 可复现);交互模式走真 host,--data-dir 从 M90 起真的被消费。
- Boot 抽干首屏快照(第 3 帧落盘,等节拍会把空页面拍进图);节拍 = 帧事件 + 33ms 时间闸门
  (iced::time::every 要 tokio/smol,见 finding),tick → drain_events → AppState::apply。
- 用户动作不乐观回显:Launch/Stop 走 Backend::dispatch,状态等事件回来才变。
- 路由:pages::Message::Navigate(Route) 直接进 AppState::navigate,选中项 = 路由栈顶(不再有本地副本);
  首屏自动选中第一行(list-detail 桌面惯例),只做一次。

实例页打通(其余三页保留占位视觉 + Message/update + 路由):
- 列表行换成 components::navigation::ListItem(真五态/选中),页头与详情动作换成 components::buttons
  (Play 是唯一的实心 accent),分隔线换成 components::divider。
- 数据全部来自 presenter 快照:VERSION/LOADER/ISOLATION/VERSION ID/FILES/LAST PLAYED + 运行状态行。
- 导航轨换成真 NavigationRail(88),5 项各自压/替换路由。

版面约束未破(实测):列表列 336、正文列 424(元信息卡 206+12+206,右缘 896)、rail 88(药丸 16..71)、
断点 600/840 走 frame_metrics;新增 pages/tests.rs 的 9 条数值断言钉住它们。
This commit is contained in:
AskaEth committed 2026-11-02 15:56:57 +08:00
1 parent 270026338f
commit b484998fc9
17 files changed
+1990 -676

No files matched your search

+5 -2
View File
@@ -33,7 +33,7 @@ use crate::probe::{self, Probe};
use crate::shot;
use crate::theme::Ui;
use super::{demo, Screen};
use super::{app, demo_model, Model, Screen};
/// 截图落盘的帧号(见模块文档)。
const CAPTURE_AT_FRAME: u32 = 3;
@@ -218,6 +218,7 @@ pub fn run(request: Request) -> Result<(), String> {
(
Harness {
ui: Ui::new(boot.flavor, families),
model: demo_model(boot.screen, boot.width),
request: boot.clone(),
window: None,
frames: 0,
@@ -266,6 +267,8 @@ pub fn run(request: Request) -> Result<(), String> {
struct Harness {
request: Request,
ui: Ui,
/// 视图模型(演示数据;harness 不接 presenter,只出**单页**图)
model: Model,
window: Option<window::Id>,
frames: u32,
}
@@ -331,7 +334,7 @@ impl Harness {
}
fn view(&self) -> Element<'_, Message> {
demo(&self.ui, self.request.screen, self.request.width).map(Message::Page)
app(&self.ui, &self.model).map(Message::Page)
}
}