AskaEth
|
e2021645d7
|
feat(launch): M157 实例级启动参数(五项回落 + graphicsBackend 三态)
core: 新增 game/launch_overrides(LaunchOverrides 五项 All-Option + 三级回落 + 文本补丁校验)与 launch/overrides(拼装落位:全局 java.extra_args 进 user.jvm、实例 jvmArgs 进 override_jvm、gameArgs 进 user.game、memory 进 -Xmx、backend 进 --graphicsBackend 并复用既有版本门槛告警);实例 schema 登记 launch.* 五键。
presenter: dto/LaunchArgsView+LaunchArgsPatch,主机侧读/写与门槛判定(backend_allowed),AppCommand::SetInstanceLaunchArgs。
cli: 新命令 instances launch-args(读/写/--clear),launch 装配链消费回落值。
app-iced: 实例详情页「启动参数」区(JVM/游戏参数、内存、Java、三态后端 chips + 灰化提示 + 保存/恢复),表单状态归 App、i18n 中英文案同步。
|
2026-11-07 10:36:35 +08:00 |
|
AskaEth
|
d0a8f68607
|
docs(m120): 页面模块的守门说明改成白名单口径
|
2026-11-05 13:36:45 +08:00 |
|
AskaEth
|
f86be2bd38
|
fix(app-iced): M120 r1 —— 徽标判据改回「类型是原版」;旧串黑名单翻成白名单
r1 复审 p2-1(真回归,我引入的):`loader_badge` 把「非原版 + 空版本号」也送进了
`Key::LoaderVanilla`。而 `host::game::loader_from_id("1.20.1-forge")` 这类 id 取不到版本段
(它按 `forge-` 找,id 却是 `-forge` 结尾),`LoaderInfo::badge()` 对它们给的正是品牌名
`Forge` / `NeoForge` / `OptiFine` —— 于是这三个显示成「原版 / Vanilla」。
判据改成「**加载器类型本身是原版** ⇒ 过词表,其余交给 `badge()`」:
- 修的是判据,不是加分支:函数注释本来就写着「只有原版这一支要过词表」,代码与注释相矛盾。
- 新回归测试 `the_loader_badge_keeps_the_brand_when_the_version_is_missing`:
`1.20.1-forge` / `1.20.1-neoforge` / `1.20.1-OptiFine` 三条 id 各断言列表副文案与 LOADER
卡的值都是品牌名(中英两套语言各跑一遍,品牌名两套语言相同)。
变异实测:把判据改回旧写法 ⇒ 立刻红(`left: "1.20.4 · Vanilla" / right: "1.20.4 · Forge"`,
与复审给的实证逐字相同)。
r1 复审 p2-2(不阻塞,按塔台要求顺手做):源码反扫从**黑名单 29 条旧串**翻成**白名单**。
- 先数过:五个页面源码里一共 13 个字面量,其中带 ASCII 单词的只有 6 条,全是专有名词 /
技术标识 / 占位符名 / 族名前缀(`{date} {time} UTC` / `isolated` / `shared` / `pid` /
`reason` / `Instances`),逐条写了理由。
- 判据反过来之后,往页面里**新写**一句英文也会红(复审抓到的哑点)——变异实测:
写进 `"Ready"` ⇒ 红;写回 `"ALL INSTANCES"` ⇒ 红。
- 反向锚保留:扫描器必须真扫到 ≥6 条、`Texts::of(` / `Header::new(texts.title())` /
`Key::Instances` / `Key::InstancesStage` 必须在用(防止断言空转)。
|
2026-11-05 13:36:45 +08:00 |
|
AskaEth
|
1a4e1fee12
|
feat(app-iced): M120 实例页与启动阶段标签跟着界面语言走
来源:M106 r1 复审点名——中文机器上首屏(实例页)仍是英中混排(页头 / New instance /
Import / ALL INSTANCES / VERSION / Present on disk / Running · pid / Launch failed)。
用户的原话是「我系统语言是中文 为什么打开软件是英文」。
改了什么
- i18n:catalog/instances.rs 从 6 条死 key 补到 34 条(页头 / 列表列头 / 详情动作 /
窄窗说明 / 元信息标签与值 / 隔离档位 / 运行状态 / 四个启动阶段)。族在装配链第 2 位,
新 key 追加在本族末尾,golden 对应位置插入(不是文件末尾追加)。
- 页面:新增 pages/instances/text.rs(Texts)——页面唯一的取词入口,实例页里不再有
任何可见英文字面量;detail / demo 也改从它取。
- 阶段标签:view/instances.rs 的 Running / Launch failed / 四阶段全部走目录;
**数字与分隔符不随语言变形**(运行中 · pid 1234 与 Running · pid 1234 只换前半句)。
- 顺手修掉两处「语言反向泄漏」:原版徽标此前直接画 presenter 的中文「原版」(英文界面
会漏中文),现在走 LoaderVanilla;隔离档位 isolated/shared 也从 core 的稳定 id 换成人话。
- 删掉 InstancesLastPlayed:M117 加的整句 key,从未接线,形状与「标签 + 值」的元信息卡
不符,由 InstancesMetaLastPlayed + InstancesNeverPlayed 取代。
守门(pages/instances/tests/i18n.rs + app/view/tests.rs)
- 中文态无 ASCII 英文单词(品牌 Java / 技术标识 pid 白名单);
- 每一条 Instances* 的两套译文必须不同(切语言时真的变);
- Running 的数字与 · pid 分隔符在两套语言里逐字保留;
- 页面源码里 29 条旧英文基线不许回来;每条 key 都必须被页面引用(死 key)。
出图:DevDocs/artifacts/m120/01~12(有实例 / 空态 / 运行中 × 浅深 × 中英)。
|
2026-11-05 13:36:44 +08:00 |
|
AskaEth
|
4b26a0a9dc
|
test(app-iced): M102 r5 行距护栏追绑定(M4 假绿做死)+ 等价重构不再假红(M3/M6)
r4 复审的结论是「产品代码没问题,两条 p1 都在护栏的『抓得住』与实测不符」:
- **M4(真正的假绿,做死)**:`rows = rows.spacing(ui.space(Space::X1));` 只要另起一条语句
就能绕过「只锁构建链」的旧护栏。现在 `list_column_spaces_rows_only_from_the_shared_constant`
用 `function_region` 取整段函数,再用 `source::call_arguments` 拿每一处 `rows.spacing(…)`
的实参,并用新增的 `source::derived_from` 顺 `let` / `Some(x) =` 绑定往回追(最多三跳):
实参必须追回 `ROW_SPACING`。于是
· 直接 `ui.space(spacing)`(来自 `if let Some(spacing) = ROW_SPACING`)→ 绿;
· `let spacing = ROW_SPACING;` 再中转(H3)→ 绿;
· 先把令牌挪进 `let extra = Space::X1;` 再 `rows.spacing(ui.space(extra))`(M4)→ 红。
- **M3(假红)**:日志接线护栏改用 `function_region`,于是 `wrap::breakable` 外面套一层同义
包装(`fn soft(…) { mono_owned(ui, wrap::breakable(…)) }`)不再假红;整条链路都没碰
`wrap::breakable` 照样红。
- **M6(假红)**:新增 `source::region_across`(多文件转调跟踪,`frame.rs` + `layout/block.rs`),
把盒子原样搬进 `block::body_box` 再转调不再假红;`align_x` 改判**实参**(`Alignment::Start`
与 `iced::Alignment::Start` 同等放行),于是搬进别的模块后路径写全也不假红。
- **M1/M2 按塔的裁定不加断言**:与 `frame::body` 逐像素等价的另写就是等价重构;docstring 写明
「抓得住:语义变化;抓不住:逐像素等价的另写」。
最终变异矩阵(本轮自跑、每条跑完 `git checkout --` 回滚、白名单已核):
基线 429 passed / 0 failed;M4 红(只红 list_column_spaces_rows_only_from_the_shared_constant);
M3 绿;M6 绿;G 红(只红 single_column_pages_call_the_body_box_not_the_centred_one);
I 红(只红 the_page_wires_the_soft_breaks_into_both_rendering_paths);
旧 p1 复跑 A 红、D 红,等价重构 J 绿。
|
2026-11-04 19:53:45 +08:00 |
|
AskaEth
|
f499961e5b
|
test(app-iced): M102 r3 护栏补到「页面调用点」——回退 body/软断点接线一律红,等价重构不再假红
r2 复审抓的两条 p1 都是「守卫只到函数内部」:把页面调用点改回 frame::single_column(变体 G)、
或把日志页两处 wrap::breakable 接线摘掉(变体 I),420 条全绿,而视觉复审 ① / ③ 原样回来。
r3 用 r2 自建的 layout/source.rs 把守卫推进到调用点,并把三条 p2 的假红/假强一并修掉:
- 新增 `single_column_pages_call_the_body_box_not_the_centred_one`:四个页面文件
(download / logs / instances / instances/detail,共 7 个调用点)必须出现 `frame::body(`、
不许出现 `frame::single_column(`;narrow_detail 单独再断言一次。(变体 G → 红)
- 新增 `the_page_wires_the_soft_breaks_into_both_rendering_paths`:扫 `lines::entry` 与
`diagnostic::summary_block`,必须含 `wrap::breakable(`(变体 I → 红);
同时把 `log_text_carries_soft_breaks_but_keeps_the_original` 的 docstring 改成它实际守的东西
(断点表 + 原文可还原),不再冒充「抓得住接线」。
- p2-1:`frame_body_is_built_from_the_block_quantities` 改用新的
`source::function_region`(函数体 + 本文件内被转调的函数,最多两层)⇒ 把 body 拆成
`body` → `body_impl` 的等价重构不再假红(H1 → 绿),换回 centered 仍红(A → 红)。
顺带修掉 region 的泛型签名 bug:早先拿 `fn {name}(` 当定义 needle,
遇到 `fn body_impl<'a>(` 会 panic(护栏自己假红);现在按 `fn {name}` 找定义,并补了一条
`the_region_follows_delegation_into_a_generic_function` 自测。
- p2-2:`list_column_spaces_rows_only_from_the_shared_constant` 的禁词收窄到
`let mut rows = column(…)` **这一条**构建链(新工具 `source::statement`,停在第一个 `;`),
于是 `column![head, scroller]` 上加合法间距不再假红(J → 绿),行容器叠间距仍红(D → 红)。
- p2-3:`frame::body` 的 align_x 改成「出现就必须是 `.align_x(Alignment::Start)`」,
不再被 `.align_y(Alignment::Start)` 顺手满足;「干脆不写 align_x」(默认 Start)视为等价重构放行。
- p2-4(措辞):`layout/block.rs` 文档写明 1440 上限是**有意行为变化**(≥1576 窗宽),
1180/900 上才是零像素变化。
变异矩阵(本轮自跑,逐条回滚,`git status` 已核):A/C/D/E/G/I 全红且各自只红对应那一条;
H1(body_impl 转调)/H2b(删显式 align_x)/H3(ROW_SPACING 经局部变量)/J(head/scroller 加合法间距)
全绿。基线 `cargo test -p unlucky-app-iced --lib` = 426 passed / 0 failed。
|
2026-11-04 19:53:43 +08:00 |
|
AskaEth
|
824bb36f8e
|
test(app-iced): M102 r2 护栏改成「抓得住页面换摆法」——块偏移/行距/散文接线全部有源码结构断言
r1 复审的两条 P1 都是假护栏:把修复原样回滚(A:frame::body 换回居中 424;
C:内缩 X2→X1;D:行距加回 4px)后测试仍全绿,因为断言只喂 frame_metrics() 与常量重算,
既不观察 frame::body,也不引用页面常量。本轮把「摆法」做成可断言的东西:
- 新增 `pages/layout/block.rs`:body_width(pane 宽封顶 1440)、body_offset(恒 0 = 贴左)、
prose_width(= metrics.column ≤424)三个纯量 + 单测。frame::body / frame::prose 从它们建盒,
frame::body_width 改成 pub use(不另算一份)。
- 新增 `pages/layout/source.rs`(cfg(test)):把某个函数的实现文本切出来。iced 的 Element
没有几何内省,量不到「块摆在哪」,只能断言「照哪个数建的」;仓里已有 no_hardcoded_tokens
扫源码、download/tests 用 include_str! 扫 key 的先例。
- 新护栏(每条都做过变异实测,见报告):
· frame_body_is_built_from_the_block_quantities(A 变异红)
· frame_prose_is_built_from_the_prose_width(E 变异红)
· list_column_spaces_rows_only_from_the_shared_constant(D 变异红)
· instances 几何断言改用页面真源 LIST_BOTTOM_INSET.dp() / ROW_SPACING(C 变异红)
· the_detail_note_goes_through_the_prose_box(散文接线)
- P2:wrap::strip 的「会删原文自带 U+200B」写成限界 + 一条断言;docs 口径从 layout.rs
挪到 layout/block.rs(layout.rs 399 → 389)。
零像素变化:改后 9 张图与 r1 的 m102-after 逐张 cmp 完全相同;2560×760 新出三张证明块宽封顶
1440(112..1551)且仍贴左。
|
2026-11-04 19:53:41 +08:00 |
|
AskaEth
|
89d98184c1
|
fix(app-iced): M102 页面几何 —— 正文块贴左吃满页宽、列表末行不再被裁半行、日志按词断行
① 正文列与页头不对齐(下载 / 日志):根因是 `frame::single_column` 把**行宽上限 424**
当成**整页块宽度**并水平居中(1180 下 1044 里只摆 424,左缘 422 对页头标题 112)。
现在两个量分开:`layout` 的 `column` 只管成段散文(≤424),页面块宽走新的
`frame::body_width`(= available / main_pane)。新增 `frame::body`(贴左吃满)与
`frame::prose`(散文锁 424)。1180×760 下下载 / 日志的块左缘 = 112 = 页头标题左缘。
② 实例列表末行被裁半行:页级行距 `Space::X1`(4) 让行节奏变成 64、与可视高度除不尽,
且滚动区直贴卡片 1px 圆角描边。行距归零(下载页同改)+ 滚动区底部内缩 `Space::X2`(8)
⇒ 可视区 580−32−8 = 540 = 9×60,Redstone Lab 及其副标题完整可见,第 10 行整体在裁切线外。
③ 日志断行把单词劈开(`graphics.gl_o` / `peration_failure`):新增
`layout::wrap::breakable`,在 `.` `_` `:` `/` `\` 后插零宽空格(U+200B 软断点),
日志行正文与一行摘要都过它;悬挂缩进不变(前缀仍是独立文本控件),
`Line::text` 与 `Logs::summary()`(剪贴板)仍是原文。工具函数放 `pages/layout/`:
断点是「列宽之后」的另一半排版判据,纯函数、可单测、不碰控件树。
护栏:`body_block_starts_at_the_header_title_and_fills_the_page`、
`list_viewport_fits_whole_rows_and_still_overflow`(把 580/32/8/540/60 五个数钉住)、
`wrap::tests`(断点位置 / 不重复插 / 可还原 / 幂等)、`log_text_carries_soft_breaks_but_keeps_the_original`。
`download/tests.rs` 的「宽窗留白」口径按新分工改成「宽度归块、不归散文列」。
|
2026-11-04 19:53:38 +08:00 |
|
AskaEth
|
022778a5a7
|
fix(app-iced): M90 r2 —— 找回窗口装饰 + 按需节拍 + 数据根一致 + 假绿守卫
复审 P1-1(产品页丢窗口装饰):shell 加内容槽位 view_with_content(ui, nav, title, content, forced),
app/view.rs 把 pages::page(...) 塞进真外壳;shell::view 保留给 Route::Agent 回落(删了会让私有
view/nav::rail 变死代码,-D warnings 红)。content() 拆到 shell/fallback.rs 让 mod.rs 回到 365 行。
1180 浅/深新图右上角三个 caption 键实测:ink 中心 y=23.5(栏高 48 的一半)、x 中心 1064.5/1110.5/
1156.5 = 46px 分档的 1065/1111/1157(±0.5px),close 命中区右缘 = 1180 窗角。
偏离 #7 改写成「交互面缺失」并注明本轮已修。
P1-2(harness.rs 402 行):tests 外置到 pages/harness/tests.rs(343 + 64 行);
components/gate.rs 的行数守门从走查 components/ 扩到 src/**(新测试当场抓出 app/tests.rs 416 行,
已按同样办法拆成 app/tests.rs + app/tests/presenter.rs)。
P2-1(常驻重绘 + 每拍重建模型):subscription 只在「截图 / 有 Busy-Running / 有 toast / 3 s 派发
观察窗」时订阅 window::frames();refresh_view 加 view_dirty 门。实测交互模式 9 s:只有 2 行 tick
trace(n=30/60,观察窗内),之后 6.5 s 一条都没有;refreshes 全程停在 7。
P2-2(两个数据根):--data-dir 时 SettingsBridge::with_path(config_path(root)),与后端同根。
P3-2(假绿):新增 RecorderBackend(dispatch 只记账、drain_events 恒空)+ launching_only_dispatches
_and_never_echoes_locally;植入本地回显后该测试实测转红(status 变 Downloading · 0%、can_stop 翻
true),撤回后转绿。
P3-3:尺寸字面量守门目录加 app;P3-1:--force-state 经 pages::Model::forced 作用到产品页控件
(hover 图里页头/列表/动作行/rail/caption 全部可见状态层);视觉 P2-2:加载器名统一走 badge()。
gallery 回归改口径:r2 与「主线 + r1」的 gallery 逐字节相同(cmp IDENTICAL);与 r1 记的
c825e1a 基线不同,差异来自主线 M86/M88 的令牌收口,不是本 mission。
|
2026-11-02 15:56:58 +08:00 |
|
AskaEth
|
b484998fc9
|
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 条数值断言钉住它们。
|
2026-11-02 15:56:57 +08:00 |
|
AskaEth
|
a650f8184b
|
fix(app-iced): 必修2 元信息卡最小宽改由内容推(136 → 160),1180 下网格 2 列
视觉 r2 抓到的回归:上一轮为塞进三列把 `META_CARD_MIN_WIDTH` 收到 136,只算外框、忘了
`card_padding = 20`,卡内只剩 96px —— demo 里 `NeoForge 20.4.237`(实测 118px)与
`NeoForge 21.1.72`(110px)都会按词折成两行,同排另两张卡仍一行 → **卡底不齐**。
- 最小宽改由**卡内最长内容**反推:118(Segoe UI 14px 实测)+ 2×20(令牌 `card_padding`)= 158
→ 取 4px 栅格 **160**(值上等于最初的 160,但这次是内容推出来的,不是凑上限凑出来的)。
- 于是元信息网格在 1180 宽落到 **2 列**(2×160 + 12 = 332;3 列要 504 > 行宽上限),
卡内可排 **166px** ≥ 118,所有值单行放下;6 格 = 3 行 2 列。
- 新增单测 `meta_card_is_wide_enough_for_the_longest_value`(此前 39 条 pages 测试没有一条覆盖它):
① 夹具 `NeoForge 20.4.237` 必须仍在 DEMO 里(数据变了就红,提醒重新量);
② `META_CARD_MIN_WIDTH − 2×card_padding ≥ 118`;③ 实际两列布局下每卡内宽也 ≥ 118。
- `meta_grid_drops_columns_as_the_detail_narrows` 跟着改成「上限处 2 列」(1180 与 900 同为 2 列,
差别落在详情列宽 435 vs 404)。
|
2026-11-02 02:04:04 +08:00 |
|
AskaEth
|
2c442585b1
|
feat(app-iced): M84 页面骨架与布局系统(断点 / 行宽上限 / 三种版面)
视觉复审在 M68 的判词是不能把「全宽单卡」当页面模板(1180 宽下正文单行 712px ≈ 118 字符)。
本 mission 把它落成一套**布局系统**,四个页面只是它的用例:
- `pages/layout.rs`:官方 600 / 840 两个档位 → 三种版面(单列 / supporting-pane / list-detail),
阈值由列宽和算出(设置 240+24+320 → 720;实例 336+24+320 → 816 ≤ 840),行宽硬上限
14 × 0.5em × 72 = **504px**,全部是纯函数,`pages/layout/tests.rs` 用绝对值和边界表钉死。
- `pages/frame.rs`:页头(56 / 两行 84)+ 内容列(≤504)+ 两列骨架 + 空态位;
`pages/frame/pieces.rs` 是占位控件(卡片 / 分隔线 / 文本按钮 / 图标),令牌驱动、无完整状态机,
待 M75 的 components 家族合入后替换。
- 四页:实例(list-detail,列表列 336 独立滚动,窄窗退化成整页切换)、
设置(supporting-pane,240 分组导航 + ≤504 表单列)、下载 / 日志(单列,两行页头)。
- `pages/harness.rs` + `examples/pages_demo.rs`:自带 `--screen/--width/--screenshot-theme`,
不碰 `cli.rs` / `app.rs`(M75 在改)。
偏离与说明:
- 布局用「页边距 16/24」而非 16/24/32,官方 expanded 也是 24(少一条偏离登记);
- 实例空详情态一屏会有 5 档字号(22/16/24/12 + 14 的操作按钮),未截图的状态,已知并登记;
- `lib.rs` 只加一行 `pub mod pages;`。
|
2026-11-01 23:27:28 +08:00 |
|