feat(client): RMIC::2026 界面重做、可计算的版本机制与固件版本联动
构建客户端 / 检查(类型 + 单元测试) (push) Canceled after 0s
构建客户端 / Linux x64(deb + AppImage) (push) Canceled after 0s
构建客户端 / Windows x64(NSIS 安装包) (push) Canceled after 0s
构建客户端 / macOS 通用(.app + .dmg) (push) Canceled after 0s

- feat(client): 界面换成墨黑蓝底 + 单支红的矩形语言,切角全部去掉,仅装甲条保留平行四边形
- feat(client): 自托管 HarmonyOS Sans SC 与 JetBrains Mono,中文与拉丁数字逐字回退
- feat(client): 新增启动序列、首启门、用户端 HUD、裁判端控制台、设置抽屉与固件维护页
- feat(client): 裁判端中缝与下方控制台可拖,鼠标落在哪一路下方就看哪一路
- feat(client): 设置抽屉常驻实时预览,与画面共用同一份准星绘制代码
- feat(client): 动效改用 Material 3 的缓动曲线与时长,逐条替换掉自造的贝塞尔
- feat(version): 版本号与 CHANGELOG 由 scripts/version.mjs 从 git 历史算出,不再手写
- feat(firmware): Hello 报文携带语义化版本、构建号与自报协议号,负载向后兼容
- feat(client): 固件版本显示在绑定列表与裁判端每一格;协议不匹配时给出具体原因
- feat(firmware): 固件包版本与客户端同源,都从 version.json 读
- fix(client): getCurrentWindow() 在没有 Tauri 注入时同步抛异常,浏览器预览整棵树被卸载
- fix(client): 启动序列的淡出 effect 清掉了自己刚设的 setTimeout,界面永远停在启动页
- fix(client): 设置抽屉左侧导航的锚点没挂到 section 上,且用错了坐标系
- fix(protocol): Hello 改用宽松帧头解析,让协议不匹配的板子能被解释而不是静默消失
- fix(build): Windows 上 bindgen 加载不了 libclang 的同目录依赖,构建脚本补上搜索路径
- fix(build): esp-idf-sys 在长路径下拒绝构建,固件构建改用短的目标目录
- build(client): 队标图标,含光学尺寸分级与手工拼的多尺寸 ICO
- build(ci): 守门 job 增加版本一致性检查,产物名改为 rmic-2026-*
- docs: 补充界面语言、版本机制与协议通告布局的说明
This commit is contained in:
2026-10-19 02:40:08 +08:00
parent cc6b82d7ec
commit 896b21fe44
332 changed files with 16729 additions and 2765 deletions
+77 -1
View File
@@ -1,3 +1,79 @@
// ESP-IDF 的环境导出 + 固件版本信息注入。
//
// # 版本号为什么在这里注入
//
// 固件的版本必须和客户端、和 `version.json` 是**同一个数**。手工在 `src/` 里写一个
// 常量,第一次忘了改就分叉了,而且没有任何东西会报错——现场只会表现为"界面说固件 0.2.0,
// 实际烧的是别的"。
//
// 所以这里在编译期把 `version.json` 读进来,导出成 `cargo:rustc-env`,源码里用
// `env!()` 取。`version.json` 由 `scripts/version.mjs` 从 git 历史算出来,于是
// 一条链是通的:git → version.json → 固件二进制 → Hello 报文 → 客户端界面。
use std::fs;
use std::path::Path;
fn main() {
embuild::espidf::sysenv::output();
}
emit_version_env();
}
/// 把 `version.json` 里的版本拆成几个编译期常量。
fn emit_version_env() {
let manifest = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
let path = Path::new(&manifest).join("version.json");
// 这个文件改了要重新编译,否则版本号会停在旧值上而二进制看起来是新的。
println!("cargo:rerun-if-changed={}", path.display());
// 没有 version.json 也要能编:直接 cargo build 的人可能还没跑过生成脚本。
// 这时退回 0.0.0+0 并留一条编译器警告——比编不过友好,比编出一个假的版本诚实。
let (major, minor, patch, build) = match fs::read_to_string(&path) {
Ok(text) => match parse(&text) {
Some(parsed) => parsed,
None => {
println!("cargo:warning=version.json 解析失败,固件版本回退为 0.0.0+0");
(0, 0, 0, 0)
}
},
Err(_) => {
println!(
"cargo:warning=找不到 version.json,固件版本回退为 0.0.0+0(先跑 node scripts/version.mjs --write)"
);
(0, 0, 0, 0)
}
};
let packed = ((major as u32) << 16) | (((minor as u32) & 0xff) << 8) | ((patch as u32) & 0xff);
println!("cargo:rustc-env=RMIC_VERSION={major}.{minor}.{patch}");
println!("cargo:rustc-env=RMIC_VERSION_PACKED={packed}");
println!("cargo:rustc-env=RMIC_BUILD={build}");
}
/// 从 `version.json` 里抠出四段数字。
///
/// 刻意不引入 `serde_json`:解析四个字段不值得多一个编译期依赖,而固件的构建时间本来
/// 就是这个项目里最贵的东西。
fn parse(text: &str) -> Option<(u64, u64, u64, u64)> {
let field = |name: &str| -> Option<u64> {
let key = format!("\"{name}\"");
let at = text.find(&key)?;
let rest = &text[at + key.len()..];
let colon = rest.find(':')?;
let rest = &rest[colon + 1..];
let start = rest.find(|c: char| c.is_ascii_digit())?;
let digits: String = rest[start..]
.chars()
.take_while(|c| c.is_ascii_digit())
.collect();
digits.parse().ok()
};
Some((
field("major")?,
field("minor")?,
field("patch")?,
field("build")?,
))
}