fix(m204): Java 组写盘成功后请 host 重发设置快照

`pages/settings/java/store.rs` 的 `save()` / `set_selected()` / `prefs_store::save()`
是页面自己的一次同步原子落盘(`SettingStore::set` 多个键 + 一次 `save()`),
不经过 `AppCommand::SetSetting` 的写盘分支;而 `CoreEvent::SettingsChanged`
全仓只有 `dispatch.rs` 的 `SetSetting` 分支与 `host/mod.rs::refresh_all` 两处发出。
于是三键写成功之后,`AppState::settings_groups` 里 java 组那几行会停在旧值,
直到下一次任何别的写入顺手带一份新快照回来。

修法用一条**动作键**(`dto::JAVA_REFRESH_ACTION` = `java.action.refresh`,
与 `download.action.detect_region` 同一族:不写盘、见到就返回 Ok):
写盘成功后派发它,`SetSetting` 分支照常重发快照。

不用 `RefreshAll`:那个除重发快照之外还会 `versions::spawn_list` 与
`start_update_check`——每点一次「保存内存」打两个网络请求(实测:一次真 host 出图
就在数据根里落了 `cache/meta/version_manifest_v2.json`),代价与动作不相称。

另外给 D30 §2.1 补一行自证身份的日志:`--screenshot` 且身份变化时打
`[shot] settings route=… group=… theme=…`(`view` 每帧都跑,所以只在变化时落一行),
免得再出现「拿外观页当下载组证据」那种图与结论对不上的事故。

守卫(`tests/java_refresh.rs`,4 条):
- 真 `CoreHost` + 临时数据根跑完整链:写盘 ⇒ 快照里 `java.extra_args` 真的变了;
- 被拒的保存不许请求刷新;
- 成功之后确实派发了那条动作键(键名写错就红);
- 「立即扫描」在 App 层真的接线(此前没有任何用例驱动 `Message::Scan`)。
This commit is contained in:
AskaEth committed 2026-11-09 12:09:01 +08:00
1 parent a48d055118
commit 92ab3d4ddf
8 files changed
+436 -1

No files matched your search

+3 -1
View File
@@ -67,7 +67,9 @@ pub use pack::{
ImportState, ImportStatusView, ImportedFileView, MissingFileView, MissingReasonView,
PackCommand, PackEvent, PackFormatView, PackState, ProbeView,
};
pub use settings::{find_row, SettingControl, SettingGroupView, SettingRowView};
pub use settings::{
find_row, SettingControl, SettingGroupView, SettingRowView, JAVA_REFRESH_ACTION,
};
// M159: 自更新
pub use update::{
ReadyUpdate, UpdateBanner, UpdateCandidateView, UpdateFailureView, UpdateOutcome, UpdateView,
+27
View File
@@ -6,6 +6,33 @@
use serde::{Deserialize, Serialize};
/// 设置页 Java 组「写回完成,请重发快照」的动作键(M204)。
///
/// ## 它解决的问题
///
/// Java 组把三键(`java.default_memory_mb` / `java.extra_args` / `java.selected_path`)**一次原子**
/// 写进 `launcher.json`(`app-iced` 的 `pages/settings/java/store.rs`:`set` 几个键 + 一次
/// `save()`),走的是页面自己的 `SettingStore`,**不经过** [`crate::dto::AppCommand::SetSetting`]
/// 的写盘分支。而 `CoreEvent::SettingsChanged` 全仓只有两处发出(`host/dispatch.rs` 的
/// `SetSetting` 分支、`host/mod.rs` 的 `refresh_all`)。于是三键写成功之后,presenter 的 java 组
/// 快照会陈旧到下一次任何别的写入——`AppState::settings_groups` 里的 `java.selected_path` /
/// `java.extra_args` 还是旧值。
///
/// ## 为什么不是「把写盘搬进 SetSetting」
///
/// 那会把一次原子写拆成最多三条命令(`SetSetting` 一次只收一个键),于是内存与 JVM 参数的保存
/// 会出现「第一键落盘、第二键被 schema 拒」的中间态;每条命令还要各走一次异步落盘,用户点一次
/// 保存会看到三次写盘与三次快照回流。取舍见 M204 报告。
///
/// ## 形状
///
/// 与 `host::engine::DETECT_REGION_ACTION`(`download.action.detect_region`,M165)同一族:
/// **动作键不写盘**,`host::settings::set` 见到它就返回 `Ok(())`,于是 `dispatch.rs` 的
/// `SetSetting` 分支照常执行它那四个副作用臂(重建引擎 / 重发 `SettingsChanged` / 清 Java 链)。
/// 键名带 `action` 段,是为了让人一眼看出它**不是** schema 键(`core::setting::keys` 里没有它,
/// 手写进 `launcher.json` 也只会被当成未知键忽略)。
pub const JAVA_REFRESH_ACTION: &str = "java.action.refresh";
/// 控件类型(决定设置页画什么)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SettingControl {
+8
View File
@@ -250,6 +250,14 @@ pub fn set(env: &HostEnv, key: &str, value: &str) -> Result<(), String> {
if key == super::engine::DETECT_REGION_ACTION {
return detect_region(env);
}
// M204:设置页 Java 组的「写回完成,重发快照」动作键。**不写盘**——页面那一侧已经把三键
// 一次原子写进 `launcher.json` 了(`pages/settings/java/store.rs::save`),这里只要返回
// `Ok(())`,`dispatch.rs` 的 `SetSetting` 分支就会重发 `CoreEvent::SettingsChanged`,
// `AppState::settings_groups` 里 java 组那几行随之变成刚写进去的真值。
// 键的来历与取舍见 `crate::dto::JAVA_REFRESH_ACTION` 的文档。
if key == crate::dto::JAVA_REFRESH_ACTION {
return Ok(());
}
let parsed = parse_value(key, value)?;
let mut store = env.store();
store