Merge branch 'feat/m108-versionmgmt-rename-download-page-to'

This commit is contained in:
AskaEth committed 2026-11-05 03:13:18 +08:00
commit 5adac230fe
7 files changed
+235 -21

No files matched your search

+3
View File
@@ -2,6 +2,9 @@
//!
//! 用户令(信息架构调整)把这一页从「下载」改称「版本管理」:下载页只负责整合包与 Mod/光影,
//! 而这一页管的是**游戏版本与加载器安装**。本文件是那次改版后的页面装配点。
//! **标识符不改**(用户令 2026-11-04 的「只改显示层」边界):`Route::Download` /
//! `Screen::Download` / 文件名 / 类型名一律保留,全仓重命名另开 finding 后置——所以本文件里
//! 「下载页」三个字到处都是,那是标识符的叫法,不是界面上出现的词。
//! **M102 版面订正**:旧版把整页块压到 424 并水平居中(1180 宽下左缘 422、页头标题在 112,
//! 左右各空 ~300px,视觉复审判为「内容缩在中间一条窄栏」)。现在页面块走 [`frame::body`]
//! ——左缘与页头标题同一条线(= 页边距 24),宽 = 块宽 `frame::body_width`(1180 下 1044,
+142 -9
View File
@@ -15,6 +15,7 @@
use iced::Element;
use unlucky_design::icons::IconKind;
use unlucky_i18n::{Key, Lang};
use unlucky_presenter::router::{NavSection, Route};
use crate::app::components::navigation::drawer::{self, Navigation};
@@ -23,44 +24,80 @@ use crate::theme::Ui;
use super::Message;
/// 导航轨的固定项:图标 + 标签 + 目标路由(顺序 = 自上而下)。
/// 导航轨的固定项:图标 + 文案 key + 目标路由(顺序 = 自上而下)。
///
/// 顺序与 `presenter::router::NavSection` 的声明顺序一致,改这里等于改导航栏。
///
/// 第 3 列是 [`Key`] 而不是字面量(M108):导航文案归目录管,`pages_demo` 出的图与将来的中文
/// 界面读的是同一份表,不会出现「页面翻译了、导航还是硬编码英文」。
///
/// **显示名 ≠ 标识符**:`Key::NavDownload` 这条说的是「版本管理 / Versions」,而它指向的仍是
/// `Route::Download`(用户令只改显示层,标识符改名后置)。
///
/// 表里存的是**收起态(88)的 key**;展开态(240)要对 [`label_key`] 过一道才换全名。
/// 可见性是 `pub(crate)`:`pages/tests.rs` 的三条断言读它(M99 从 `pages/mod.rs` 搬出来时,
/// 它从「模块私有」变成「crate 内可见」,断言一条没动、强度也没降)。
pub(crate) const NAV: [(Option<NavSection>, IconKind, &str, Route); 5] = [
pub(crate) const NAV: [(Option<NavSection>, IconKind, Key, Route); 5] = [
(
Some(NavSection::Instances),
IconKind::Instances,
"Instances",
Key::NavInstances,
Route::Instances,
),
(
Some(NavSection::Download),
IconKind::Download,
"Download",
Key::NavDownload,
Route::Download,
),
(Some(NavSection::Logs), IconKind::Logs, "Logs", Route::Logs),
(
Some(NavSection::Logs),
IconKind::Logs,
Key::NavLogs,
Route::Logs,
),
(
Some(NavSection::Settings),
IconKind::Settings,
"Settings",
Key::NavSettings,
Route::Settings,
),
// AI 独立页没有导航高亮项(`Route::nav()` 对它返回 `None`)
(None, IconKind::Ai, "Assistant", Route::Agent),
// AI 独立页没有导航高亮项(`Route::nav()` 对它返回 `None`);文案用 M106 给导航项新开的
// `Key::NavAi`(与产品路径的 `shell/nav.rs` 同一条 key,两条 rail 不许各借一个词)。
(None, IconKind::Ai, Key::NavAi, Route::Agent),
];
/// 这一项在当前宽度态下的文案 key。
///
/// `expanded` = D33 的 240 展开抽屉(用全名),`false` = 88 收起轨(用短名)。产品路径**当前只有
/// 88 收起态**,所以调用点只出现 `false`。
///
/// **为什么是 bool 参数而不是 `enum NavWidth { Rail, Drawer }`**:D33 的抽屉还没有渲染路径,
/// 枚举里那个 `Drawer` 变体在产品代码里永远构造不到,`-D warnings` 会按 `dead_code` 把它拦下来
/// (M108 第一版就是这么红的)。参数化之后没有「构造不到的状态」,切换点仍只有这一处。
pub(crate) const fn label_key(label: Key, expanded: bool) -> Key {
if expanded && matches!(label, Key::NavDownload) {
Key::NavDownloadExpanded
} else {
label
}
}
/// 导航轨:**真组件**(`components::navigation` 的 rail 形态,宽 = 令牌 `nav_rail_width` = 88)。
///
/// M84 这里是一条留白带;M90 换成 M78 的 `NavigationRail`,于是「导航项 → 路由」是真的可点。
///
/// 语言暂时固定 [`Lang::En`]:组件版 rail 的调用点只有 `pages/mod.rs` 的 `nav::rail(ui, model.active)`,
/// 那里还没把 `Model.lang` 传进来(`pages/mod.rs` 不在 M108 的 scope 内)。文案本身已经走 [`Key`],
/// 接线时只改这一行——产品路径那边同一笔欠账归 M106(它给 `shell/nav.rs` 接 i18n)。
pub(super) fn rail<'a>(ui: &'a Ui, active: Option<NavSection>) -> Element<'a, Message> {
let catalog = Lang::En.catalog();
let mut group = drawer::NavGroup::new();
for (section, icon, label, route) in NAV {
let mut item = drawer::NavItem::new(label, icon, Message::Navigate(route));
// 88 收起轨 = 短名(240 展开抽屉才用全名,见 [`label_key`])
let label = label_key(label, false);
let mut item = drawer::NavItem::new(catalog.text(label), icon, Message::Navigate(route));
if section.is_some() && active == section {
item = item.selected();
}
@@ -69,3 +106,99 @@ pub(super) fn rail<'a>(ui: &'a Ui, active: Option<NavSection>) -> Element<'a, Me
navigation(ui, Navigation::rail().group(group))
}
#[cfg(test)]
mod tests {
//! 导航表与文案的守门(M108)。
//!
//! 这条断言原先挂在 `pages/tests.rs`,但那份文件在 M102 合并后是 464 行、撞了「单文件 ≤400 行」
//! 的硬门禁(`components/gate.rs` 按全仓判红),于是按「谁的数据谁守」挪到本文件——`NAV` 表在
//! 这里,守门也在这里,强度一条没降。
use super::*;
/// **M108**:这一页对外叫「版本管理」,代码里仍是 `Route::Download`。
///
/// 用户令(2026-11-04)只要**显示层**改名:全仓重命名标识符会与在途的多条线全面撞车,
/// 所以 `Route::Download` / `NavSection::Download` / `Screen::Download` / 文件名一个都不动。
///
/// 英文有两个不同长度的名字,**是刻意的**(见 `catalog/nav.rs` 的「两条 key」一节):
/// 88px 收起轨放不下 `Version management`(≈101px),用短名 `Versions`;页标题与 240 展开抽屉
/// 用全名。中文两处都是「版本管理」(≈48px,放得下就不缩)。
///
/// **抓得住**:① 导航项或页标题又写回「下载」「Download」;② 88 轨上误用全名(会截断/换行);
/// ③ 副标题退回「只说下载」;④ 中文被「为了对称」砍短。**抓不住**:产品路径的导航轨
/// (`shell/nav.rs` 是另一套手搓实现,它自己有一条同样的断言)、真画出来会不会截断(那要出图)、
/// 中文目录在真机上的字体(截图通道固定 En,见 M108 报告与 finding)。
#[test]
fn the_download_route_is_presented_as_version_management() {
let en = Lang::En.catalog();
let zh = Lang::ZhCn.catalog();
let entry = NAV
.iter()
.find(|entry| entry.3 == Route::Download)
.expect("导航表里有版本管理页那一项");
assert_eq!(entry.2, Key::NavDownload, "导航文案走目录,不写字面量");
// 88 收起轨 = 短名;240 展开与页标题 = 全名(中文两处同值)
assert_eq!(en.text(Key::NavDownload), "Versions");
assert_eq!(en.text(Key::NavDownloadExpanded), "Version management");
assert_eq!(en.text(Key::DownloadTitle), "Version management");
assert_eq!(zh.text(Key::NavDownload), "版本管理");
assert_eq!(zh.text(Key::NavDownloadExpanded), "版本管理");
assert_eq!(zh.text(Key::DownloadTitle), "版本管理");
assert_eq!(
label_key(Key::NavDownload, false),
Key::NavDownload,
"88 收起态必须用短名"
);
assert_eq!(
label_key(Key::NavDownload, true),
Key::NavDownloadExpanded,
"240 展开态换全名"
);
assert_eq!(
label_key(Key::NavLogs, true),
Key::NavLogs,
"其余四项两态同一条 key"
);
// 目录里凡是指向这一页的文案都不许再说「下载」。唯一豁免是 `Downloading files`:
// 那是安装流程里真在传文件的那一步,说的是当时的动作、不是页面身份。
let page_keys = Key::ALL.iter().filter(|key| {
(key.name().starts_with("Download") && **key != Key::DownloadStageDownloading)
|| **key == Key::NavDownload
|| **key == Key::NavDownloadExpanded
});
let mut checked = 0;
for key in page_keys {
assert!(
!en.text(*key).to_ascii_lowercase().contains("download"),
"{} 的英文文案还在说「下载」:{}",
key.name(),
en.text(*key)
);
assert!(
!zh.text(*key).contains("下载"),
"{} 的中文文案还在说「下载」:{}",
key.name(),
zh.text(*key)
);
checked += 1;
}
assert!(checked > 20, "只扫到 {checked} 条,守门形同虚设");
// 副标题要讲这个页面「管什么」(版本 + 加载器),不是「能下载什么」
let subtitle_en = en.text(Key::DownloadSubtitle);
let subtitle_zh = zh.text(Key::DownloadSubtitle);
assert!(
subtitle_en.contains("versions") && subtitle_en.contains("loaders"),
"副标题要提到版本与加载器:{subtitle_en}"
);
assert!(
subtitle_zh.contains("版本") && subtitle_zh.contains("加载器"),
"副标题要提到版本与加载器:{subtitle_zh}"
);
}
}
+5 -2
View File
@@ -7,6 +7,7 @@ use super::nav::NAV;
use super::*;
use unlucky_design::color::SchemeFlavor;
use unlucky_design::typography::Platform;
use unlucky_i18n::Lang;
use unlucky_presenter::router::{NavSection, Route};
use crate::theme::Families;
@@ -334,13 +335,15 @@ fn routes_map_onto_pages() {
#[test]
fn every_rail_item_points_at_a_route_that_highlights_it() {
assert_eq!(NAV.len(), 5, "导航轨 5 项(4 个一级页 + AI)");
let catalog = Lang::En.catalog();
for (section, _icon, label, route) in NAV {
assert!(!label.is_empty());
assert!(!catalog.text(label).is_empty(), "{} 没有文案", label.name());
assert_eq!(
route.nav(),
section,
"{label} 指向的 {route:?} 高亮的是 {:?}",
"{} 指向的 {route:?} 高亮的是 {:?}",
label.name(),
route.nav()
);
}
+27 -1
View File
@@ -34,7 +34,7 @@ use crate::theme::Ui;
pub enum NavItem {
/// 实例列表。
Instances,
/// 下载/版本。
/// 版本管理(显示名;变体名按用户令「只改显示层」不改,标识符改名后置)。
Download,
/// 日志。
Logs,
@@ -274,4 +274,30 @@ mod tests {
assert!(!item.icon().icon().path.is_empty(), "{item:?} 没有图标");
}
}
/// **M108**:导航栏上这一项叫 `Versions`(88px 轨放不下全名 `Version management`)。
///
/// 文案本身走目录(M106 的 `label_key()` + `label(lang)`),这条只守**取值**与**宽度余量**:
///
/// - **抓得住**:① 有人把 `Key::NavDownload` 的英文改回「下载」或塞回 18 字符的全名
/// (88px 轨上会截断/换行,实测全名 ≈101px);② 中文被「为了对称」砍成两个字。
/// - **抓不住**:真画出来的截断(那要出图,见 M108 报告里的产品通道与 pages_demo 图);
/// 也抓不住 240 展开抽屉(那一档在产品里还不可达)。
#[test]
fn the_download_item_is_labelled_version_management() {
// 88px 轨的余量:中文 4 字 ≈48px、英文短名 ≈45px,都该有明显的余量
assert_eq!(NavItem::Download.label(Lang::En), "Versions");
assert_eq!(NavItem::Download.label(Lang::ZhCn), "版本管理");
for item in NavItem::ALL {
let label = item.label(Lang::En);
assert!(
!label.to_ascii_lowercase().contains("download"),
"{item:?} 的英文标签还在说下载:{label}"
);
assert!(
label.chars().count() <= 12,
"{item:?} 的标签太长,88px 的轨放不下:{label}"
);
}
}
}
+17 -4
View File
@@ -1,4 +1,4 @@
//! 下载页文案(M94)。
//! 版本管理页文案(M94 建页时叫「下载页」,M108 起显示名改成「版本管理」)。
//!
//! 一句话一个 key:带变量的句子用 `{name}`,页面只做 [`crate::Catalog::render`],不拼片段
//! ([`crate::bridge`] 的规矩 1)。
@@ -6,15 +6,28 @@
//! 文案口径(M94 r1 / P2-2):**只说这个页面真能做到的动作**(切分类、刷新、看日志),
//! 不写「稍后会自动重试」这类 UI 没有的能力,也不拿文案去补功能。
//!
//! **「下载」字样不许再指向这一页**(M108 用户令):这一页管的是「版本与加载器」,用户点它
//! 不是为了下载点什么。唯一的例外是 [`Key::DownloadStageDownloading`]——那是安装流程里**真在
//! 传文件**的那一步,说的不是页面身份而是当时的动作,留着才对(守门见 `pages/tests.rs` 的
//! `the_download_route_is_presented_as_version_management`,它把本域每条 key 的两种语言都扫一遍)。
//!
//! 域文件的形状见 [`crate::catalog`] 的「域装配」一节。
macro_rules! download_entries {
($next:ident, $($rest:ident,)* { $($acc:tt)* }) => {
$next! { $($rest,)* {
$($acc)*
// --- download page (M94) ---
DownloadTitle => ("Downloads", "下载"),
DownloadSubtitle => ("Versions you can install", "可以安装的版本"),
// --- version management page (M94; renamed in M108) ---
DownloadTitle => ("Version management", "版本管理"),
// 例外说明(M108 复审 p3):副标题承诺「安装、升级、修复」,但页面目前只有
// 「选版本 → 安装」一条路,「修复」与「加载器选择」都还没有入口
// (`InstallView::of` 连 `InstallProgressView.loader` 都没读)。
// 这是用户令(2026-11-04)定的口径,能力由 M105 / M113 补齐;
// 补齐之前不许拿这句话当已实现功能宣传。
DownloadSubtitle => (
"Manage versions and loaders: install, upgrade, repair",
"管理版本与加载器:安装、升级、修复"
),
DownloadRefresh => ("Refresh", "刷新"),
DownloadInstall => ("Install", "安装"),
DownloadCancel => ("Cancel install", "取消安装"),
+16 -3
View File
@@ -5,6 +5,8 @@
//! M99 把 `catalog.rs` 的单张条目表按域拆到 `catalog/<域>.rs` 之后,这 72 行就是「拆分前那一份」
//! 的原样导出:由拆分前的 `Catalog::of(En)` / `Catalog::of(ZhCn)` 逐条 `println!` 出来,
//! 再原封不动抄进来(生成方式见 `catalog/tests.rs` 里 `split_keeps_every_translation` 的注释)。
//! M108 在它上面改了 3 条(`NavDownload` / `DownloadTitle` / `DownloadSubtitle`)、追加了 1 条
//! (`NavDownloadExpanded`);M105/M106/M107 各自追加的条目也都在,整表现在是 138 条。
//!
//! ## 为什么不是「数条数」
//!
@@ -19,11 +21,22 @@
//! | 域的顺序被调换(`Key` 的 `Ord` 与导出表顺序都靠它) | 该行 key 名对不上 |
//!
//! **抓不住**:译文本身翻得对不对(金表是「拆分前」的快照,它只能证明「没变」)。
//!
//! ## M108 更新了三条、新增了一条(版本管理改名)
//!
//! 用户令(2026-11-04)把这页的显示名从「下载 / Downloads」改成「版本管理 / Version management」:
//! [`super::Key::NavDownload`] / [`super::Key::DownloadTitle`] / [`super::Key::DownloadSubtitle`]
//! 三条译文按令改掉,并**新增** [`super::Key::NavDownloadExpanded`](240 展开态的全标签,
//! 88 轨放不下 18 字符的英文,见 `catalog/nav.rs` 的「两条 key」一节)。
//!
//! 其余各行仍逐字节等于拆分前那份快照——也就是说这张表现在证明的是「除这几条按令改过的,
//! 其余一字未动」,不再是「整表一字未动」。
/// 拆分前 `Key::ALL` 的顺序 × (en, zh-CN)。第一列是变体名(`Key::name()` 的返回值)。
pub(super) const GOLDEN: &[(&str, &str, &str)] = &[
("NavInstances", "Instances", "实例"),
("NavDownload", "Downloads", "下载"),
("NavDownload", "Versions", "版本管理"),
("NavDownloadExpanded", "Version management", "版本管理"),
("NavLogs", "Logs", "日志"),
("NavSettings", "Settings", "设置"),
("InstancesNew", "New instance", "新建实例"),
@@ -45,8 +58,8 @@ pub(super) const GOLDEN: &[(&str, &str, &str)] = &[
("SettingsLanguage", "Language", "界面语言"),
("SettingsLanguageHint", "Switches the interface language. Chinese text needs a Chinese font.", "切换界面语言。显示中文需要中文字体。"),
("CoreTextUntranslated", "[system text not translated]", "[未翻译的系统文案]"),
("DownloadTitle", "Downloads", "下载"),
("DownloadSubtitle", "Versions you can install", "可以安装的版本"),
("DownloadTitle", "Version management", "版本管理"),
("DownloadSubtitle", "Manage versions and loaders: install, upgrade, repair", "管理版本与加载器:安装、升级、修复"),
("DownloadRefresh", "Refresh", "刷新"),
("DownloadInstall", "Install", "安装"),
("DownloadCancel", "Cancel install", "取消安装"),
+25 -2
View File
@@ -1,7 +1,29 @@
//! 导航轨文案:四个一级页。AI 独立页没有导航高亮项(`Route::Agent.nav()` 是 `None`),所以没有 key。
//! 导航文案:四个一级页。AI 独立页没有导航高亮项(`Route::Agent.nav()` 是 `None`),所以没有 key。
//!
//! 域文件的形状见 [`crate::catalog`] 的「域装配」一节:本宏把条目**追加**到 `$acc` 之后,
//! 再把「还没装配的域」原样交给 `$next`。条目顺序 = `Key` 的声明顺序(金表 `catalog::golden` 守门)。
//!
//! **显示名与标识符不同名**(M108):`NavDownload` 这条说的是「版本管理」,而它指向的仍是
//! `Route::Download` / `NavSection::Download`。用户令(2026-11-04)只改**显示层**:全仓重命名
//! 标识符会与在途的多条线全面撞车,收益只在观感,所以标识符改名后置。
//!
//! ## 为什么「版本管理」有两条 key(M108)
//!
//! D33 定死了导航两态:**88 收起轨 ⇄ 240 展开抽屉**。88px 里塞得下中文「版本管理」(4 字 ≈48px),
//! 但塞不下英文 `Version management`(18 字符 ≈101px > 88)——那会变成截断或两行,两个都是视觉缺陷
//! (两行还要把项高从 52 抬到 68,等于为一个词改全局导航节奏)。
//!
//! 所以按**容器宽度**分两条:
//!
//! | key | 用在哪 | en | zh-CN |
//! |---|---|---|---|
//! | [`NavDownload`] | 88 收起轨(`shell/nav.rs` / `pages/nav.rs`) | `Versions` | 版本管理 |
//! | [`NavDownloadExpanded`] | 240 展开抽屉(D33 的展开态) | `Version management` | 版本管理 |
//!
//! 中文两条**故意同值**:48px 放得下就没有理由为「对称」砍短(那是视觉缺陷,不是风格)。
//! 英文短名选 `Versions` 而不是 `Versions list` / `Downloads`:单一词最省宽度,且不与「下载」
//! (真在传文件的动作、设置页的下载分组)混淆。切换点只有 [`crate::catalog`] 的调用方一处,
//! 见 `pages/nav.rs` 的 `label_key`。
macro_rules! nav_entries {
($next:ident, $($rest:ident,)* { $($acc:tt)* }) => {
@@ -9,7 +31,8 @@ macro_rules! nav_entries {
$($acc)*
// ── 导航轨(`pages::Route` 的四项)
NavInstances => ("Instances", "实例"),
NavDownload => ("Downloads", "下载"),
NavDownload => ("Versions", "版本管理"),
NavDownloadExpanded => ("Version management", "版本管理"),
NavLogs => ("Logs", "日志"),
NavSettings => ("Settings", "设置"),
} }