refactor(pages,i18n): M99 纯搬移 —— 导航表抽 pages/nav.rs、设置页拆目录、catalog 按域拆开

- pages/mod.rs 400 -> 356 行:NAV 表与组件版 rail 搬进 pages/nav.rs(NAV 可见性变 pub(crate),
  只为让 pages/tests.rs 的三条断言原样保留)
- pages/settings.rs -> pages/settings/{mod,frame,general,appearance,java,download,ai,tests}.rs:
  mod.rs 只留「分组注册表 + 选中分组 + 内容分发」,一组一个文件,为四条 section 线各留位置
- i18n catalog.rs 310 -> 273 行:条目表按域拆到 catalog/<域>.rs,catalog.rs 留 catalog! 宏、
  装配链、Catalog 与对外 API;新增 catalog/golden.rs(拆分前 key->译文 全量快照)守门
- 行为/文案/样式零变化:pages_demo 的 instances/settings × 浅/深 四张图逐字节相同
This commit is contained in:
AskaEth committed 2026-11-03 13:19:17 +08:00
1 parent 4b1471a60d
commit 330f7216fd
29 files changed
+1173 -541

No files matched your search

+8 -52
View File
@@ -6,11 +6,12 @@
//! |---|---|---|
//! | M84 | **布局系统**(档位/版面/列宽)+ 四页占位视觉 | 下面的版面阈值表 |
//! | **M90(本文件当前状态)** | ①视图模型 [`model`] ②导航项接上 `presenter::router::Route` ③**实例页整页接真组件 + 真数据** | 「接真组件后版面约束一条没破」(见 `pages/tests.rs` 的数值断言) |
//! | M99 `SETTINGSFRAME` | **纯搬移**:导航表搬到 [`nav`],设置页拆成目录(`pages/settings/**`),i18n 目录按域拆开 | 截图逐字节相同 + `catalog` 金表(`key -> 译文` 全量不变) |
//!
//! **导航轨归属(M90 r3 订正,复审 P2-1)**:产品路径(`app/view.rs`)走
//! `shell::view_with_content`,导航轨由 **`shell/nav.rs` 的手搓实现**画——它与 M78 组件几何同值
//! (宽 88、药丸 56×32),复审逐像素量过「r1 产品图(真组件 rail)vs r2 产品图(shell rail)
//! 在 y≥48 区域差异为 0」;组件版的 [`rail`] 现在只服务 `pages::app`(`pages_demo` 出口图、
//! 在 y≥48 区域差异为 0」;组件版的 `nav::rail` 现在只服务 `pages::app`(`pages_demo` 出口图、
//! harness)与单测。为什么不是「把 rail 挪进内容槽位」:那需要把 `shell` 的消息类型泛型化/映射,
//! 成本远大于收益(见偏离表 #10)。
//!
@@ -41,8 +42,9 @@
//! | [`layout`] | **布局系统**:档位 / 版面 / 列宽 / 阈值,纯函数 + 单测(无 iced 控件树) |
//! | [`model`] | **视图模型**:presenter 快照 → 页面要画的东西(页面层不认识 presenter 类型) |
//! | [`frame`] | 页头 / 内容列 / 两列骨架 / 空态 / 卡片 / 分隔线 |
//! | [`nav`] | 导航轨的 `NAV` 表 + 部件版 `rail()`(harness / 单测;产品走 `shell/nav.rs`) |
//! | `instances` | 实例页(list-detail)——**M90 唯一打通到真组件 + 真数据真命令的页** |
//! | `settings` | 设置页(supporting-pane,占位视觉) |
//! | `settings` | 设置页(supporting-pane,占位视觉):分组注册表 + 每个分组一个文件 |
//! | `download` / `logs` | 下载 / 日志页(单列,占位视觉) |
//! | [`harness`] | 截图 harness(`examples/pages_demo.rs` 的入口,**不是** `cli.rs` 的一部分) |
//!
@@ -76,9 +78,9 @@
//!
//! 修法:`shell::view_with_content(ui, nav, title, content, forced)` 把外壳(标题栏 + 三条热区 +
//! 导航轨)与内容区拆开,产品页把 [`page`] 的树塞进内容槽位;`shell::view`(M68 骨架卡)保留给
//! `Route::Agent` 回落 —— 删了会让私有模块里的 `view` / `nav::rail` 变死代码,`-D warnings` 会红。
//! `Route::Agent` 回落 —— 删了会让私有模块里的 `view` / `shell::nav::rail` 变死代码,`-D warnings` 会红。
//!
//! [`app`](本文件)现在**只服务 harness**(`pages_demo` example 与单测):它的标题栏仍是留白带,
//! `pages::app`(本文件)现在**只服务 harness**(`pages_demo` example 与单测):它的标题栏仍是留白带,
//! 因为 `shell` 的回调类型是 `crate::app::Message`,套不进 `pages::Message` 的树里。
//!
//! ## 截图通道
@@ -101,6 +103,7 @@ pub mod model;
mod download;
mod instances;
mod logs;
mod nav;
mod settings;
#[cfg(test)]
@@ -114,11 +117,8 @@ use iced::widget::{column, container, row, space};
use iced::{Element, Length};
use unlucky_design::elevation::MaterialLayer;
use unlucky_design::icons::IconKind;
use unlucky_presenter::router::{NavSection, Route};
use crate::app::components::navigation::drawer::{self, Navigation};
use crate::app::components::navigation::navigation;
use crate::theme::Ui;
use frame::filled;
@@ -266,33 +266,6 @@ impl Screen {
}
}
/// 导航轨的固定项:图标 + 标签 + 目标路由(顺序 = 自上而下)。
///
/// 顺序与 `presenter::router::NavSection` 的声明顺序一致,改这里等于改导航栏。
const NAV: [(Option<NavSection>, IconKind, &str, Route); 5] = [
(
Some(NavSection::Instances),
IconKind::Instances,
"Instances",
Route::Instances,
),
(
Some(NavSection::Download),
IconKind::Download,
"Download",
Route::Download,
),
(Some(NavSection::Logs), IconKind::Logs, "Logs", Route::Logs),
(
Some(NavSection::Settings),
IconKind::Settings,
"Settings",
Route::Settings,
),
// AI 独立页没有导航高亮项(`Route::nav()` 对它返回 `None`)
(None, IconKind::Ai, "Assistant", Route::Agent),
];
/// **单页内容**(不含外壳):交给 `shell::view_with_content` 塞进真标题栏与导航轨之间。
///
/// 产品路径(`app/view.rs`)走 [`page_with_appearance`]:外壳由 `shell` 画(拖动/缩放/三个
@@ -341,7 +314,7 @@ fn render<'a>(
pub fn app<'a>(ui: &'a Ui, model: &'a Model) -> Element<'a, Message> {
let body = page(ui, model);
let shell = row![rail(ui, model.active), body]
let shell = row![nav::rail(ui, model.active), body]
.width(Length::Fill)
.height(Length::Fill);
@@ -370,23 +343,6 @@ pub fn demo_model(screen: Screen, window_width: f32) -> Model {
model
}
/// 导航轨:**真组件**(`components::navigation` 的 rail 形态,宽 = 令牌 `nav_rail_width` = 88)。
///
/// M84 这里是一条留白带;M90 换成 M78 的 `NavigationRail`,于是「导航项 → 路由」是真的可点。
fn rail<'a>(ui: &'a Ui, active: Option<NavSection>) -> Element<'a, Message> {
let mut group = drawer::NavGroup::new();
for (section, icon, label, route) in NAV {
let mut item = drawer::NavItem::new(label, icon, Message::Navigate(route));
if section.is_some() && active == section {
item = item.selected();
}
group = group.item(item);
}
navigation(ui, Navigation::rail().group(group))
}
/// 标题栏留白带(高 = `LayoutMetrics::titlebar_height`)——**只给 [`app`] 的 harness 路径用**。
///
/// 产品路径的标题栏是 `shell::view_with_content` 画的那个真的(三个 caption 键 + 拖动热区),