AskaEth
|
29f65b8e6f
|
feat(design): M65 真 HCT 色彩 —— CAM16 + ColorSpec2021 + 官方逐角色对齐
把 M61 的「OKLCh 近似 CAM16 + gamut 硬裁」换成官方的 HCT 算法,与
material-color-utilities(Apache-2.0)逐角色对上:baseline 49 角色 ×
light/dark、8 个种子 × 12 个强调族角色 × 2 模式,**实测每通道误差 0**
(判据写入测试:≤1)。
实现(逐函数对照官方 Java 重写为 Rust,f64 内部精度):
- color/cam16.rs + viewing_conditions.rs:CAM16 色相/彩度(D65、200 lux、
背景 L* 50),默认观察条件的中间量与 Java 实测逐位对齐
- color/hct_solver.rs + critical_planes.rs:官方 gamut 映射(Newton 迭代 +
色调平面上的色相二分,255 项关键平面表原样搬运),只压彩度不硬裁
- color/hct.rs:HCT 坐标(hue/chroma/tone),公开给工具与测试
- color/space.rs:官方 ColorUtils 的 sRGB↔线性↔XYZ↔L* 换算(矩阵、阈值、
取整口径一致)
- color/palettes.rs:Tonal Spot 官方彩度常数 36/16/24/6/8,error 25°/84
- color/scheme.rs:tone 表换 ColorSpec2021(on-*container 10→30、
background 99/10→98/6)
两个旧缺陷关门:
- primary-container 与 secondary-container 不再撞色(彩度 36 vs 16,
tone 90 装得下,无需「共享基准压彩度」补丁);reference_chroma /
tone_scaled 随之删除
- 强调色色相不再被 gamut 硬裁(只压彩度,实测偏移 <1°)
对齐证据:tests/official_baseline.rs(98 项 + 12 个 HCT 探针)、
tests/official_accent.rs(192 项)。期望值是把官方 Java 实现本机编译运行
的输出抄本,并用 Flutter 的 ColorScheme.fromSeed 用例交叉验证
(#00FF00 → #406836、#000000 → #8C4A60、#FFFFFF → #006874 一致)。
诚实口径:只做 Tonal Spot 变体 + contrastLevel 0;对比度三档、其他变体、
壁纸多色取色、非默认观察条件都在模块文档的 gap 表里登记。
注:token_pins 的 tone 值变化来自官方算法(ColorSpec2021),不是为过测试而改。
NOTICE 增「Google Material Color Utilities」归属(Apache-2.0 要求,
含逐文件映射与对齐方法)。
|
2026-11-01 16:40:36 +08:00 |
|
AskaEth
|
83e19e58f0
|
fix(design): M61 r1 收口 —— 官方 emphasized/弹簧/高程 + 冻结测试
reviewer r1 判 p2-2items(fix-then-merge),本轮按条目收口:
P2-1 冻结测试补全(变异实测证明原来可静默漂移):
- tests/type_scale_pins.rs:baseline 15 档 (size,line_height,weight,tracking,family)
与 Expressive 强调 15 档逐值对拍,期望值独立于实现重写
- tests/token_pins.rs:49 条 tone(light+dark 全表)、7 条 easing 曲线参数、
16 档时长、9 组弹簧、28 个 IconKind 下标、形状阶梯
- 自证:TitleMedium 16→17 与 EmphasizedDecelerate 换曲线两次变异均 FAILED
P2-2 emphasized 口径事实错误(用户点名):
- 取回 A 级官方值(androidx TypeScaleTokens v0_103 的 *Emphasized* 四元组):
label-*/title-medium/title-small 强调是 700(此前给 600)、body-large 0.5→0.15、
title-medium 0.2→0.15、body-medium 0.2→0.25、display-large -0.2→0
- TypeRole::emphasized() 改为官方表(typography/emphasized.rs);
原「只加一档字重」规则改名 emphasized_simplified() 并标注非官方
- 顺带按官方订正 baseline tracking(display-large -0.2、body-medium 0.2、
title-medium 0.2)与 TitleLarge 的 Brand 族归属
- crate 文档新增「baseline MD3 vs M3 Expressive 差异清单」+ 未支持能力 gap
弹簧改为两套 token 结构(用户追加令):
- motion::spring(scheme × category × speed) 二维表 12 格:
standard spatial ζ=0.9 (k 1400/700/300)、expressive spatial ζ=0.6~0.8 (k 800/380/200)、
effects 两套同值 ζ=1.0 (k 3800/1600/800),均取 A 级 MotionTokens 原值
- Spring 改存阻尼比(与 Compose 令牌同口径),9 个具名常量
中文字重覆盖(用户追加令):
- FontFace 带 WeightCoverage(可变区间/静态表),Platform::resolve_weight 给
Native/Nearest/Synthetic 三档结论;雅黑无 500、苹方无 700 两处 gap 显式登记
component_map 高程按官方回调(P2 并入 finding):
- FilledCard L2→L0、OutlinedCard L1→L0、Menu L4→L2、Dialog L5→L3(Menu 圆角同时
按 MenuTokens 改 4dp);新增官方值 pin 测试 + R4「全屏都在飘」守门断言
P3 四条:
- 同族容器撞色:彩度按共享基准等比压缩(Palettes.reference_chroma),
primary/secondary-container 不再同值,且强调色覆盖时表面逐像素不变
- femtovg 引用改函数名(行号已漂);删掉 contrast_aa 的死代码后门
- 骨架数值跟 M63:nav 56→44、titlebar 40→48,control 40/touch 48 换算依据入文档
- 图标折算口径订正为「居中描边,视觉外扩 <1px」
|
2026-11-01 15:58:19 +08:00 |
|
AskaEth
|
65ca0ed29a
|
feat(design): 加 unlucky-design 令牌 crate(M3 色彩/排版/形状/动效/图标)
D26 把视觉基线锁到 Material 3 Expressive,本 crate 是「UI 整体性」的地基:
令牌唯一真源,后续 iced 页面只消费不新增。零第三方依赖、零 UI 框架依赖
(cargo tree -p unlucky-design --edges normal 只有它自己一行)。
- 色彩:49 个 M3 语义角色 × light/dark,种子色派生 6 条 tone 调色板;
tone 轴严格用 CIELAB L*(偏差 ≤1),色相/彩度用 OKLCh 近似 CAM16 并在
模块文档里写明口径与已知偏差(不宣称是标准 HCT 动态取色)
- 对比度:26 组文字对 × 6 组种子 × 2 模式全部 ≥4.5:1;3 条例外显式登记
(Outline 非文字 3:1、OutlineVariant 装饰、SurfaceVariant 是容器色)
- 排版:M3 type scale 15 档 + 三平台字体族回退栈(只写族名,不内嵌受限字体)
- 形状/高程/动效:Expressive 形状刻度 + shape morph 数据、elevation 级别与
surface container 映射、时长/缓动/弹簧(纯函数可采样)
- 图标:24 个从 Slint 版迁移的 SVG path + 4 个新增,IconKind 冻结只可末尾追加;
写明 Slint 的 stroke-width 是绝对像素、SVG 是 viewBox 用户单位的语义差异,
换算封装成 Icon::stroke_width
- 组件档位表:20 个组件的形状/高程/字体/动效一次取全
TODO(M63):elevation shadow 的 blur/offset/alpha 与四组弹簧参数是占位值,
接口先定、数值待玻璃视觉规格表(tower 已通告边界)。
|
2026-11-01 15:34:04 +08:00 |
|
AskaEth
|
dc4fac8391
|
fix(core/game): M49 —— Java 去重键平台无关归一化(Linux CI 三红)
`game::java::discover::path_key` 只在 `cfg!(windows)` 分支折分隔符,POSIX 上
把 `C:\a\b\java.exe` 原样留成带反斜杠的键,与 `C:/a/b/java.exe` 不同键 →
`dedupe` 不去重。三条用例(Windows 语义无 cfg 门)因此在
`test (ubuntu-latest)` 挂:`path_key_normalises_separators`、
`dedupe_keeps_the_higher_priority_source`、
`java_discover::dedupe_collapses_one_install_found_twice`。
- `path_key`:`\?\` 前缀剥离与分隔符折成 `/` 改为**与平台无关**(同一个
`bin/java[.exe]` 用哪种拼法都是同一个文件,键必须相同);大小写折叠仍只有
Windows 做——POSIX 上 `Java` 与 `java` 是两个目录,折了会把两份真实安装误并。
已知取舍写进文档:POSIX 上文件名里的字面反斜杠会一并被当分隔符。
- 三条用例:不靠 `#[cfg(windows)]` 跳过。`path_key` 单测改成平台无关断言 +
大小写折叠的双侧断言(Windows 折 / POSIX 不折,两侧都钉住);集成用例的
「同一路径第二种写法」按平台给(Windows 用大小写+分隔符,POSIX 用分隔符)。
- 回归钉子:新增 `one_install_normalises_to_one_key_whatever_the_spelling`
(普通写法 / 反斜杠写法 / `\?\` 前缀写法 / 混合写法 四种拼法同键)与
`path_key_folds_case_only_where_the_filesystem_does`。
验证:Linux(rust:1-bookworm 容器,ull-linux-target 热缓存)
`cargo test -p unlucky-core --lib --test game` → 224 + 120 passed / 0 failed
(基线是 lib 220/2 failed、game 119/1 failed);
`cargo test --workspace --all-targets --no-fail-fast` → 34 个测试块
1137 passed / 0 failed(该次跑在 M48 launch 修复叠加的临时副本上,因为 M48
尚未合入 main,见报告)。Windows 本机 `cargo fmt --check`、
`cargo clippy -p unlucky-core --all-targets -D warnings`、
`cargo test -p unlucky-core` 全绿(17 个测试块 0 failed)。
|
2026-11-01 11:00:25 +08:00 |
|
AskaEth
|
6ce825db8d
|
fix(launch): M48 —— POSIX 组杀假成功导致 stop() 空等超时
CI test (ubuntu-latest) 自 M43 起红的根因不是「组杀打到自身进程组」,
而是 procps-ng 的 kill 把裸 `-<pgid>` 当成又一个信号选项吞掉:退出码 0、
什么都没发。旧实现据此提前 return,单 pid 兜底永远不执行,于是
process_smoke::stop_kills_process_tree 与
pipeline_cases::pipeline_stop_records_killed_exit 只能干等
graceful+5s 预算后报「强制结束后进程仍在运行」。
- process.rs:Unix 分支用 CommandExt::process_group(0)(std 1.64+ 安全
API,无需 pre_exec/unsafe)让子进程自成一个进程组,与 Windows 的
CREATE_NEW_PROCESS_GROUP 对称;这也让「树」回收真正覆盖后代。
- tree_kill.rs:组杀改用 `kill -TERM -- -<pgid>`(`--` 不可省),并且
先过 group_kill_target 三重验证——目标必须是自己的进程组组长、必须
读得到我们自己的组、目标组不能就是我们所在的组;单 pid 兜底改为
「永远执行」,kill 缺失(NotFound)返回明确错误而不是静默失败。
pid==0 守卫从 kill_tree 提到 terminate/force_kill 共用入口。
- 测试钉子(Linux 门住):spawn 后子进程 pgid == 自己的 pid 且 !=
测试进程 pgid;组杀只接受验证过的独立组;组杀覆盖孙进程且不碰我们
自己的组;同组子进程靠单 pid 兜底仍能被干掉;pid 0 一律拒绝。
验证:rust:1-bookworm 容器(ull-linux-target 热缓存)cargo fmt --check、
cargo clippy --workspace --all-targets -D warnings、cargo test -p
unlucky-core --test launch(82 passed,两条原红灯转绿)、cargo test
--workspace --all-targets --no-fail-fast 不再被 SIGTERM 打断;Windows
本机 fmt/clippy/cargo test -p unlucky-core 全绿。
|
2026-11-01 10:32:18 +08:00 |
|
AskaEth
|
d95c9664c6
|
fix(app): M45 r1 两条 P2(向导拒装文案 + 处理器段进度满格)
P2-1:wizard.slint 的 Forge/NeoForge 提示还写着「需要 installer processors:本轮请用 ull install」,
改成「需要 installer processors(首装会外调 java,约 1–3 分钟)」;全仓再扫一遍
「ull install / 显式拒装 / 终端跑」等措辞,user-facing 文案已无残留(余下命中全是
OptiFine/LiteLoader 的正确说明与测试注释)。
P2-2:进度条在处理器依赖库下完那一刻就满格(36/36),随后加载器库的 29 个文件才被计入
→ 100% 回落再涨回,且 bytes_total 用 .max() 与文件数对不上。
- Meter 新增 expect(files, bytes):文件数与字节都 saturating_add;
- install_loader 在处理器段之前就把加载器库的文件/字节一次预记账(两段之和),
加载器库阶段只剩切阶段文案,不再记账;
- Reporter::expect 改为委托 Meter::expect,处理器依赖库沿用同一条曲线;
- 回归测试钉住:预记账后 files_total=95、bytes_total=17MB、ratio 不再满格,
处理器库下完仍是中间态,加载器库下完才到 100%。
真机重截 + 识图:D:/tmp/ull-m45-shots/forge-mid-fixed-25s.png —— 同一时刻面板
36/65 文件、3.4 MB/15.3 MB、进度条只填了约一半(旧图 36/36 + 满格)。
|
2026-11-01 09:14:02 +08:00 |
|
AskaEth
|
3d9372029e
|
refactor(app): 处理器测试夹具用 StepView 别名,过 clippy::type_complexity
clippy --all-targets -D warnings 报 very complex type(RefCell<Vec<(usize, String, Vec<String>, usize)>>),
抽 pub(super) type StepView 别名,语义不变。
|
2026-11-01 09:14:01 +08:00 |
|
AskaEth
|
b0f747074f
|
feat(app): GUI 接上 Forge/NeoForge installer processors(M45)
- host/install_processors:GUI 与 CLI 同序跑 processors(计划 → 依赖库 → 选 Java → 串行 java -cp),
进度经 HostTask::Install 回流(阶段文案 + 每步处理器名 + 文件/字节/速度/ETA),取消在阶段边界生效;
- host/install_journal:安装过程追加到 .ULL/logs/install.log,失败文案带日志路径,不再静默失败;
- host/install.rs:supported() 扩到原版 + Fabric/Quilt/Forge/NeoForge,删除 Forge/NeoForge 拒装路径
(仅剩 OptiFine/LiteLoader 两家 core 侧无管线者),spawn_install 统一持 Job 上下文;
- 失败诊断:缺 Java / processor 非零退出(保留 processor_* 大类码与脱敏 stderr 尾部)/ 依赖库下漏
/ 元数据缺失,均为结构化文案 + Toast + 装日志;
- dto::InstallStage::Installing 文案改「生成版本」(同时覆盖写版本 JSON 与跑 installer 两段);
- screenshot:新增 --screenshot-loader,Forge/NeoForge 中间态可离屏取图;
- 测试 +14(138 全绿):supported() 语义、处理器阶段注入 fake 后端覆盖跳过/缺 Java/非零退出/
依赖库失败/命令行构造/进度文案,另修旧用例(Forge 不再被门禁拒装)。
|
2026-11-01 09:14:01 +08:00 |
|
AskaEth
|
07fd6113a7
|
fix(core): 回调先校验 state 再处理 error,堵掉伪造取消失效登录
reviewer P2(crates/core/src/auth/microsoft/callback.rs):handle() 先处理 error 参数、
之后才校验 state,于是任意一次 `GET http://localhost:{port}/auth?error=access_denied`
(state 随便给甚至不给)都会让 wait_for_code 直接返回 Canceled,登录被静默中止、账户不入库
—— 不需要知道随机 state、不需要 code、不需要任何凭据。违反 RFC 6749 §4.1.2.1 / RFC 9207
(错误响应同样要校验 state),也与本模块「与本次登录无关的请求一律继续等待」的声明矛盾。
- state 校验提到 error 之前:state 不匹配的请求(无论带不带 error)一律 400 后继续等;
只有带**本机 state** 的 access_denied 才判 Canceled。模块文档第 4 条同步改写。
- 顺带采纳 reviewer 的两条非计数观察:
* 读头预算改为 accept 之后按 deadline 重算(原来复用 accept 前的 remaining,
总时长可超发 ≤5s);预算归零直接超时。
* state 由 64 位升到 128 位(两个独立随机种子的 SipHash 输出拼接),对齐
RFC 6749 §10.10 的熵建议;新增单测断言 32 位 hex 且每次不同。
- 新增回归用例(crates/core/tests/auth/microsoft_authcode.rs):
`foreign_state_cannot_cancel_the_login` —— 先发 `?error=access_denied`(无 state)
再发 `?error=access_denied&state=not-our-state`,两次都必须 400 且服务器继续等待,
随后真回调拿到 code;`access_denied_with_our_state_is_still_canceled` 钉住另一半语义。
既有的「陈旧 state 回 400 后继续等」用例保持通过。
门禁:cargo fmt --all --check OK;cargo clippy --workspace --all-targets -- -D warnings
exit 0 无告警;cargo test -p unlucky-core -p unlucky-app 18 个 suite 全绿(auth 59 例)。
AskaEth <i@yeij.top>
|
2026-11-01 08:33:11 +08:00 |
|
AskaEth
|
8194e38bcf
|
feat(core): 加 MSA 授权码流(本地回调服务器)并接到账户页
research/08 方案 A:GUI 的微软登录改走授权码流(HMCL 主流做法),设备码流保留给
CLI/无浏览器场景。两条流只差「怎么拿到 MS access_token」,拿到之后共用
chain::complete_chain 与 MicrosoftSession,落盘/刷新语义没有第二套。
core(crates/core/src/auth/microsoft/)
- endpoints:新增 authorize(…/consumers/oauth2/v2.0/authorize),与 token 同租户。
- authcode:RedirectUri 是 http://localhost:{port}/auth 的唯一来源(授权 URL、
token 请求体、实际监听三处共用,坑 5);AuthCodePolicy(回调超时 300s / 绑定重试 3 次 /
固定端口 / 固定 state);authorization_url(response_type=code + prompt=select_account +
scope=XboxLive.signin%20offline_access);exchange_code 复用 flow 的 oauth_error/tokens_from。
- callback:本地 HTTP/1.1 回调服务器。内核分配端口(占用按次数退避重试);非 /auth 路径
(favicon、预连)404 后继续等;state 不匹配 400 后继续等;error=access_denied 归 Canceled;
单条连接读头超时 5s,半开连接拖不垮时间预算。
- MicrosoftAuth::authcode_login(present):绑端口 → present(宿主开浏览器)→ 等回调 →
换 token → 整链;失败与设备码流一样落 M38 失败日志。
app(crates/app)
- AppCommand::StartBrowserLogin + host::accounts::start_browser_login;设备码/授权码共用
LoginJob 取消槽位、收尾入库(store_session)与面板 DTO。
- host::opener 用系统默认浏览器打开授权 URL(Windows rundll32 url.dll,FileProtocolHandler)。
- 账户页:主按钮「微软登录」走授权码流,「设备码登录」作备用;DeviceLoginView::browser_waiting
在面板里给出可复制的授权链接(user_code 留空)。
测试
- crates/core/tests/auth/microsoft_authcode.rs:授权 URL 向量(租户/编码/prompt)、
URI 一致性(授权 URL 与 token 表单同字面量)、mock 全链、favicon+陈旧 state 后仍拿到 code、
端口占用重试文案、超时、access_denied→Canceled、login_with_xbox 403 的 M38 诊断
(detail_code=login_with_xbox_403,真机白名单未批时验收止步于此)。
- 单测:authcode 编码/向量、callback query 解码与错误分流、DTO browser_waiting、conv 面板。
未做/未验证:真机完整登录需 Azure 公共客户端 id(ULL_MSA_CLIENT_ID)与 Minecraft API 许可,
本机只能到「授权页 302 到 login.live.com 且 redirect_uri/scope/prompt 原样保留」这一步。
AskaEth <i@yeij.top>
|
2026-11-01 08:33:11 +08:00 |
|
AskaEth
|
50b321ec64
|
fix(app): LaunchEnd 也清 pending 并记失败原因,补早失败回归测试(M43 r3 两条 P2)
P2-1(本轮新引入的回归):`pending` 只在 LaunchStarted 清除,但 `launch::start`
的两条早失败出口(build_request 失败 / spawn 失败——没装 Java、版本 JSON 坏就是
这两条)只发 LaunchEnd → pending 永久残留 → `in_flight` 恒真 → 该实例本会话再也
启动不了,磁贴锁在「启动中」。修:LaunchEnd 分支先 `pending.remove`,早失败与
正常收尾都释放。
P2-2(failed 零 insert):上一轮只在 note 里声称「失败原因进 failed 表」,代码没
落地(唯一 insert 缺失,refresh 的 Failed 分支是死代码,test 6 还靠手动塞表绕过)。
修:LaunchEnd 非 Ok 时 `failed.insert(id, summary)`、Ok 时 remove;refresh_instances
的 Failed 分支从此由真实路径喂数据。
测试(123 → 124,全部走真实入口):
- 新增 `launch_end_before_launch_started_still_releases_the_instance`:mark_pending
→ handle(LaunchEnd Crashed) → pending 空、in_flight 假、refresh 后磁贴是
Failed{reason}(同时钉住 P2-1 与 P2-2)。
- test 6 重写:Busy 断言改走 `mark_pending`(start_launch 用的同一入口),Failed
断言改走 `handle(LaunchEnd Crashed)`,不再手动塞 failed 表。
- 所有测试的数据根加 `TempRoot` Drop 守卫,跑完不留临时目录。
|
2026-10-31 22:14:24 +08:00 |
|
AskaEth
|
7b2ff62ddc
|
fix(app): 运行时状态回归真实来源 + 启动状态机补回归测试(M43 r2 两条 P2)
P2-1(修复零测试):新增 host/launch_state_tests.rs,6 条单测钉住启动状态机:
- running_pid 按实例 id 取 pid(并发 A/B 后停 A 不会拿到 B 的 pid,A 收尾不动 B);
- 重复启动守卫:launches 已落盘时 start_launch不发 Busy、只给 warn(不起第二个进程);
- pending 窗口守卫:后台线程还没回报 LaunchStarted 时同样挡住重复启动;
- LaunchStarted 清 pending 并登记 pid;LaunchEnd 清启动表与失败记录;
- refresh 后在跑实例仍是 Running{pid};pending 是 Busy;失败记录是 Failed(保住
磁贴的停止键/运行条/重试外观)。
P2-2(运行时状态未派生):refresh_instances 重建快照后按 host 的启动表覆写运行时
(Running{pid} / Busy / Failed),不再被 game::instances 的硬编码 Idle 打回原形——
装版本成功、加删账户、别的实例收尾等任意刷新都不再让「在跑的实例」丢掉停止入口。
顺带加固(reviewer 建议的 pending 窗口):单槽守卫改为 pending ∪ launches 并集,
launch::start 到 LaunchStarted 之间的重复 dispatch 不会起第二个进程;失败原因也
记进 failed 表,避免收尾后的刷新把「失败/重试」覆盖成 Idle。
|
2026-10-31 22:09:47 +08:00 |
|
AskaEth
|
ecded9bef0
|
fix(app): 停止实例按实例 id 找 pid,并发启动不再杀错进程树
reviewer-guiwire r1 的 P2-1:`running_pid` 只记「最近一次启动」的单槽 pid,
并发启动 A、B 后点 A 的停止键会 tree_kill 掉 B;且实时尾随在 B 启动时被抢占,
A 的日志再也没人跟。兜底分支也永远不命中(`game::instances` 每次重建都把
runtime 置 Idle)。
改法:
- 单槽 `launch_pid` → `launches: Vec<LiveLaunch{instance, session, pid}>`,
停止/收尾/尾随判定都按实例 id 或会话 id 精确匹配;
- 同一实例重复点启动直接拒绝(「已经在启动/运行中」),不再起第二个进程;
- 收尾只拆自己那场的尾随,并发时另一实例的实时日志不被掐断;
- 实时尾随仍是单窗口(一次只跟一场,另一场收尾后可从 JSONL 回放)——这是
有意的 UI 取舍,不是漏判。
|
2026-10-31 21:55:59 +08:00 |
|
AskaEth
|
ddc0577f83
|
feat(app): GUI 全量接线,五页改吃 core 真实数据(M43)
app 从「mock 骨架」切到「真实 core host」:新增 src/host 装配层(env/job/
game/launch/identity/versions/install/meter/java/accounts/logs/settings/engine/
dispatch),是 app 里除 settings_bridge 外唯一 use unlucky_core 的地方,出口只有
crate::dto 的 DTO/事件(三通道纪律,AGENTS §6)。
页面接线:
- 实例页:profiles+版本 JSON 继承链+会话索引 → 磁贴(真实版本/加载器徽标/最后启动/
隔离档位/载荷缺失);启动按钮走 LaunchRequest 装配 → Launcher::spawn → JSONL
尾随 → 运行/失败回写;M30 ModeChangeWarning 进横幅通知条。
- 新建实例向导:版本 → 加载器 → 名字 → 确认 → 真实安装进度;Fabric/Quilt 可装,
Forge/NeoForge 因需要 installer processors(仅 CLI 有)显式拒装并给出 CLI 命令。
- 下载页:远端 manifest(12h 缓存)+ 内嵌 unlisted → 分类(正式/预览/远古四子类);
安装进度(文件数/字节/速度/ETA,速度由相邻采样差分)+ 阶段边界可取消;
Java 分组:四家发行版 + 回退链可视化(计划链/真实 ChainLog)+ 本地运行时。
- 账户页:三轨账户列表(只读公开字段)+ 离线新增 + 删除/切换 + 微软设备码登录
(用户码/链接/轮询状态;取消后丢弃结果)。凭据只在 host/identity 与 host/accounts
出现,DTO 无 token 字段(单测钉住)。
- 设置页:新增 download/java/data/ai/mcp 五组,行由 core::setting schema 键生成,
写回走 SettingStore::set(与 ull config set / AI set_config 同源 launcher.json)。
- 日志页:会话索引 → 列表;事件 JSONL → 日志行(等级高亮)+ D8 规则引擎诊断卡片;
启动中实时尾随(600ms/拍,只读新增字节);崩溃日志导入分析(hs_err/crash/latest)。
架构与守线:
- 新页面状态走 ui/page_state.slint 的 PageState global(main.slint 再导出),
Rust 侧 page_sync.rs 灌入、callbacks_pages.rs 接线;避免属性逐层转发顶破 400 行。
- DTO 全部 Serialize+Deserialize(IPC 契约),新增 accounts/java/settings/install/
notices 五个子模块;事件扩展 LogAppended/LaunchFinished/AccountsChanged/
DeviceLoginChanged/JavaChanged/InstallProgress/SettingsChanged/NoticesChanged。
- 截图通道扩展:--data-dir(钉数据根且 settings 同源)/--mock/--screenshot-page/
--screenshot-wizard/--screenshot-session/--screenshot-install/--screenshot-launch
(真实中间态,取图后自动 stop 防孤儿进程)。
已知边界:GUI 账户「当前启动账户」仅内存态(core schema 无 auth.* 键,app 不自建键);
Forge/NeoForge GUI 安装待 processors 执行器上移。
|
2026-10-31 21:55:58 +08:00 |
|
AskaEth
|
bc41dfcf7d
|
Unlucky Launcher v0.0.1-alpha
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent),
MCP server 接入外部 Agent 工具,GUI 开发中。
|
2026-10-31 21:54:10 +08:00 |
|