Merge branch 'feat/m118-slintexit-drop-legacy-slint-crate-f'

This commit is contained in:
AskaEth committed 2026-11-04 00:09:41 +08:00
commit 40a4c06d42
11 files changed
+289 -2459

No files matched your search

+26 -8
View File
@@ -3,6 +3,12 @@ name: CI
# 主干门禁:fmt / clippy / test / build 双平台矩阵。 # 主干门禁:fmt / clippy / test / build 双平台矩阵。
# 架构守门(core 零 UI、文件 ≤400 行、依赖许可)见 guards.yml。 # 架构守门(core 零 UI、文件 ≤400 行、依赖许可)见 guards.yml。
# 平台基线 Win10+(标准工具链产物),平台矩阵见 AGENTS.md §7。 # 平台基线 Win10+(标准工具链产物),平台矩阵见 AGENTS.md §7。
#
# M118(D26):根 Cargo.toml 已 `exclude = ["crates/app"]`(Slint 退役树,源码原样保留)。
# 所以本文件里所有 `--workspace` 都**不再**覆盖 `unlucky-app`:CI 不再编 slint 1.18 + usvg 0.48
# 那棵依赖树(本机单路冷编 ≈14 分钟;并行超配时表现为 `rustc` 退 1 且零诊断,
# 见 DevDocs/00-踩坑记录-p2.md T7)。历史实现只作源码参考,**没有构建方式**(见 README:
# 它的 manifest 整份是 workspace 继承写法,离开 workspace 后连解析都过不了)。
on: on:
push: push:
@@ -38,7 +44,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Linux 图形栈构建依赖(winit / femtovg 编译期需要) - name: Linux 图形栈构建依赖(winit / wgpu 编译期需要)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt-get update sudo apt-get update
@@ -49,6 +55,8 @@ jobs:
with: with:
components: clippy components: clippy
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
# `--workspace` 现在是 8 个 crate,不含 `unlucky-app`(M118 的 workspace exclude),
# 所以这条命令不会拉起 slint / usvg / i-slint-*。
- run: cargo clippy --workspace --all-targets -- -D warnings - run: cargo clippy --workspace --all-targets -- -D warnings
test: test:
@@ -69,6 +77,9 @@ jobs:
libwayland-dev libxcb1-dev libwayland-dev libxcb1-dev
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
# 同 clippy:`--workspace` 不含 `unlucky-app`(M118),Slint 退役树不再被 `--all-targets` 拉起。
# 也因此**不要**再写 `--exclude unlucky-app`:它已不是成员,那面旗子只会打一条
# `excluded package(s) … not found in workspace` 的警告然后空转(实测 exit 0,不报错)。
- run: cargo test --workspace --all-targets - run: cargo test --workspace --all-targets
build: build:
@@ -89,14 +100,19 @@ jobs:
libwayland-dev libxcb1-dev libwayland-dev libxcb1-dev
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
# 同上:`--workspace` 不含 `unlucky-app`(M118)。被摘掉的正是 release 档
# (lto=fat + codegen-units=1)最贵的那棵 slint + usvg 树。
- run: cargo build --workspace --release - run: cargo build --workspace --release
# 运行时冒烟(M62):Linux 上**真渲染一帧**,补上「能编译 ≠ 能跑」的空白。 # 运行时冒烟(M62):Linux 上**真渲染一帧**,补上「能编译 ≠ 能跑」的空白。
# 上面四个 job 都只证明代码编得过、测得过,从不创建真实图形窗口;本 job 在 Xvfb 虚拟显示里 # 上面四个 job 都只证明代码编得过、测得过,从不创建真实图形窗口;本 job 在 Xvfb 虚拟显示里
# 启动 GUI 进程,让 Slint 软件渲染器把当前场景渲进内存像素缓冲并编码成 PNG,再对**像素本身** # 启动 GUI 进程,让软件渲染器把当前场景渲进内存像素缓冲并编码成 PNG,再对**像素本身**
# 断言(不是只看退出码:窗口起来了但一帧空白同样是「退出码 0」)。 # 断言(不是只看退出码:窗口起来了但一帧空白同样是「退出码 0」)。
# 诚实边界:证明「Linux 上真能跑出非空白的一帧」,**不**证明视觉正确——视觉靠人眼看 artifact。 # 诚实边界:证明「Linux 上真能跑出非空白的一帧」,**不**证明视觉正确——视觉靠人眼看 artifact。
# 本 job 绑死 Slint Window::take_snapshot;迁 iced 后必须提供等价的离屏截图通道,否则本 job 静默退化成「能起进程」。 # M118(D26):这里原本绑死 Slint 的 `Window::take_snapshot`,打的是 Slint 版 bin
# `unlucky-launcher`;`crates/app` 退出 workspace 后那个 bin 不再存在,改用 iced 版
# `unlucky-launcher-iced` 自带的 `--screenshot` 通道(`crates/app-iced/src/shot/`)。
# 命令行参数刻意不动:iced 的 `cli.rs` 就是照 Slint 版的口径写的,`--data-dir` 也仍被接受(忽略)。
runtime-smoke: runtime-smoke:
name: runtime smoke (ubuntu x64, xvfb) name: runtime smoke (ubuntu x64, xvfb)
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -116,13 +132,14 @@ jobs:
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
# 用 debug 而不是 release:本 job 验的是「运行时渲染通路能通」,不是优化后二进制的行为; # 用 debug 而不是 release:本 job 验的是「运行时渲染通路能通」,不是优化后二进制的行为;
# release 的 lto="fat" + codegen-units=1 会把这一步拖到十几分钟却不带来额外证据。 # release 的 lto="fat" + codegen-units=1 会把这一步拖到十几分钟却不带来额外证据。
- run: cargo build --bin unlucky-launcher --locked # M118:bin 从 Slint 版 `unlucky-launcher` 换成 iced 版(见上方 job 注释)。
- run: cargo build --bin unlucky-launcher-iced --locked
- name: 在 Xvfb 里跑一帧并截图(退出码非 0 即失败) - name: 在 Xvfb 里跑一帧并截图(退出码非 0 即失败)
run: | run: |
set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播 set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播
mkdir -p "$RUNNER_TEMP/smoke/data" mkdir -p "$RUNNER_TEMP/smoke/data"
xvfb-run -a --server-args="-screen 0 1280x1024x24" \ xvfb-run -a --server-args="-screen 0 1280x1024x24" \
target/debug/unlucky-launcher --software-renderer \ target/debug/unlucky-launcher-iced --software-renderer \
--screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \ --screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \
--data-dir "$RUNNER_TEMP/smoke/data" --data-dir "$RUNNER_TEMP/smoke/data"
- name: 断言 PNG 存在且非空白(尺寸 / 唯一色数 / 亮度σ / 强边缘占比) - name: 断言 PNG 存在且非空白(尺寸 / 唯一色数 / 亮度σ / 强边缘占比)
@@ -137,10 +154,11 @@ jobs:
# macOS 槽位探针(M62):**默认不在 PR 上跑**,只有手动 dispatch 才触发。 # macOS 槽位探针(M62):**默认不在 PR 上跑**,只有手动 dispatch 才触发。
# 之所以是探针而不是常驻槽位:官方 macOS runner 处于 LaunchDaemon 上下文,没有登录的 # 之所以是探针而不是常驻槽位:官方 macOS runner 处于 LaunchDaemon 上下文,没有登录的
# WindowServer 会话,Slint 的 winit backend 创建窗口大概率直接失败;而 worker 禁 push, # WindowServer 会话,GUI 的 winit backend 创建窗口大概率直接失败;而 worker 禁 push,
# 本机也没有 macOS,这条路径**尚无任何实测证据**——不拿未验证的 job 卡住主干门禁。 # 本机也没有 macOS,这条路径**尚无任何实测证据**——不拿未验证的 job 卡住主干门禁。
# 首跑结论决定去留:绿 → 去掉 if/continue-on-error 升为常驻槽位;红 → 按 D26 归入 iced # 首跑结论决定去留:绿 → 去掉 if/continue-on-error 升为常驻槽位;红 → 按 D26 归入 iced
# 重写后再规划,并把错误原文贴进 progress 文档(不静默删 job、不吞失败原因)。 # 重写后再规划,并把错误原文贴进 progress 文档(不静默删 job、不吞失败原因)。
# M118:bin 与 Linux 那条一样,从 Slint 版换成 iced 版(`unlucky-launcher-iced`)。
runtime-smoke-macos: runtime-smoke-macos:
name: runtime smoke (macos arm64, probe) name: runtime smoke (macos arm64, probe)
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
@@ -153,12 +171,12 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- run: cargo build --bin unlucky-launcher --locked - run: cargo build --bin unlucky-launcher-iced --locked
- name: 尝试截图(macOS 没有 X 服务器,直接跑;失败即为结论) - name: 尝试截图(macOS 没有 X 服务器,直接跑;失败即为结论)
run: | run: |
set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播 set -o pipefail # 当前没有管道(死行),保留是为将来加管道时先有失败传播
mkdir -p "$RUNNER_TEMP/smoke/data" mkdir -p "$RUNNER_TEMP/smoke/data"
target/debug/unlucky-launcher --software-renderer \ target/debug/unlucky-launcher-iced --software-renderer \
--screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \ --screenshot "$RUNNER_TEMP/smoke/runtime-smoke.png" \
--data-dir "$RUNNER_TEMP/smoke/data" --data-dir "$RUNNER_TEMP/smoke/data"
- name: 断言 PNG 存在且非空白 - name: 断言 PNG 存在且非空白
+18
View File
@@ -7,6 +7,16 @@ name: Guards
# 4. file-size —— 全仓代码/文档单文件 ≤400 行(用户硬约束) # 4. file-size —— 全仓代码/文档单文件 ≤400 行(用户硬约束)
# 5. licenses —— cargo-deny:许可证白名单 + GPL 依赖拦截 + 供应链来源校验 # 5. licenses —— cargo-deny:许可证白名单 + GPL 依赖拦截 + 供应链来源校验
# 6. unsafe-scope —— 全仓 `allow(unsafe_code)` 的文件集合必须恰好只有一个(D36 / M91) # 6. unsafe-scope —— 全仓 `allow(unsafe_code)` 的文件集合必须恰好只有一个(D36 / M91)
#
# M118(D26):根 Cargo.toml 已 `exclude = ["crates/app"]`(Slint 退役树)。逐条核对过本文件,
# **没有**任何一条守卫以 `--workspace` 为前提,所以排除不会让它们空转:
# ① core-no-ui / presenter-no-ui / design-zero-dep —— 「manifest 文本 + `cargo tree -p <crate>`」
# 口径,被守的 crate 仍是 workspace 成员,依赖树与排除前逐行一致;
# ② file-size / unsafe-scope —— 走 `git ls-files` / `git grep`,与构建图无关,**仍然覆盖
# `crates/app/**`**(源码还在仓库里,就还得守它的行数与 unsafe 落点);
# ③ licenses —— 唯一走 workspace 依赖图的守卫。排除后 Slint 那棵树不再进图(实测 `cargo deny
# list` 里 `slint*` / `i-slint-*` 为 0 个),这与「不再随任何产物发布」一致;代价是它的许可证
# 不再被本门禁审 —— 真要重新纳入发布,得先把它加回 workspace,门禁随之恢复。
on: on:
push: push:
@@ -48,6 +58,9 @@ jobs:
# presenter 是 Slint 版(crates/app)与 iced 版(crates/app-iced)共用的唯一业务层(D26)。 # presenter 是 Slint 版(crates/app)与 iced 版(crates/app-iced)共用的唯一业务层(D26)。
# 一旦 slint/iced/winit 被拉进来,分层就废了,而编译与单测仍会全绿 —— 所以必须由 CI 守。 # 一旦 slint/iced/winit 被拉进来,分层就废了,而编译与单测仍会全绿 —— 所以必须由 CI 守。
# M118 之后 Slint 版已被 workspace `exclude`,但本条**照样守得住**:它查的是 presenter 自己的
# manifest 与 `cargo tree -p unlucky-presenter`,与「谁在消费 presenter」无关;依赖树里一旦出现
# slint/iced/winit 仍会当场红。名单里的 slint 继续留着,就是为了防「把 UI 依赖偷偷塞进宿主层」。
# 与 core-no-ui 同样是「依赖表 + cargo tree」双查,名单保持一致;脚本刻意复制而非抽公共脚本, # 与 core-no-ui 同样是「依赖表 + cargo tree」双查,名单保持一致;脚本刻意复制而非抽公共脚本,
# 理由是 CI 守门脚本自身挂掉的排查成本远高于这几行重复。 # 理由是 CI 守门脚本自身挂掉的排查成本远高于这几行重复。
presenter-no-ui: presenter-no-ui:
@@ -356,4 +369,9 @@ jobs:
- uses: EmbarkStudios/cargo-deny-action@v2 - uses: EmbarkStudios/cargo-deny-action@v2
with: with:
# 只跑许可/禁用/来源三类,advisories 由后续安全轮次单独接管 # 只跑许可/禁用/来源三类,advisories 由后续安全轮次单独接管
# M118:cargo-deny 走 **workspace 依赖图**,`crates/app` 被 exclude 后 Slint 那棵树不再进图
# (实测 `cargo deny list` 里 `slint*` / `i-slint-*` 命中 0 个),所以本门禁不再审 Slint 的
# RF-2.0 许可 —— 与「它不再随任何产物发布」一致。这不是回退:`deny.toml` 里为 Slint 留的
# allow 条目**故意没删**(删它得先确认没有别的路径重新引入),当前等于一条死条目。
# 若将来把 `crates/app` 加回 workspace,本门禁的覆盖会自动恢复。
command: check licenses bans sources command: check licenses bans sources
+11 -2
View File
@@ -132,6 +132,13 @@ jobs:
exit 1 exit 1
fi fi
# M118(D26):**Slint 版停发**。旧写法 `--bin unlucky-launcher` 打的是 `crates/app`
# (Slint 退役树)的 bin —— 也就是说这条流水线一直在发 Slint 版二进制,而 D26 早已把
# iced 定为产品 UI。现在 GUI 来源只有 `unlucky-launcher-iced`;`crates/app` 也已在根
# Cargo.toml 被 `exclude`,不参与任何 workspace 构建(它连 `--manifest-path` 都编不了)。
# 对外产物文件名**不变**(`unlucky-launcher-<ver>-<slot>.*`):改名发生在
# `packaging/make_artifacts.py` 内部(`LAUNCHER_BIN` 找 iced 产物、落盘用对外名),
# 下载链接 / SHA256SUMS / README 都不用跟着漂。
- name: cargo build --release --locked - name: cargo build --release --locked
shell: bash shell: bash
env: env:
@@ -143,14 +150,16 @@ jobs:
if [ -n "$TARGET" ]; then if [ -n "$TARGET" ]; then
target_args+=(--target "$TARGET") target_args+=(--target "$TARGET")
fi fi
cargo build --release --locked "${target_args[@]}" --bin unlucky-launcher --bin ull cargo build --release --locked "${target_args[@]}" --bin unlucky-launcher-iced --bin ull
- name: Windows 单文件自检(导入表不得出现 VC++ 可再发行 DLL) - name: Windows 单文件自检(导入表不得出现 VC++ 可再发行 DLL)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: bash shell: bash
env: env:
# 跨槽位的关键点:产物在 target/<triple>/release 而不是 target/release。 # 跨槽位的关键点:产物在 target/<triple>/release 而不是 target/release。
EXE: ${{ matrix.build_dir }}/unlucky-launcher.exe # M118:校验的是**构建产物**(iced bin 的原始文件名)。对外名 `unlucky-launcher`
# 由 packaging/make_artifacts.py 在落盘时才换上,那一步之后才存在。
EXE: ${{ matrix.build_dir }}/unlucky-launcher-iced.exe
run: | run: |
set -euo pipefail set -euo pipefail
# +crt-static 是否真的生效,只看导入表:出现 VCRUNTIME/MSVCP/msvcr/ucrtbase 说明还得装 # +crt-static 是否真的生效,只看导入表:出现 VCRUNTIME/MSVCP/msvcr/ucrtbase 说明还得装
Generated
+64 -2360
View File
File diff suppressed because it is too large. Load diff
+11 -2
View File
@@ -4,13 +4,13 @@ members = [
"crates/core", "crates/core",
"crates/agent", "crates/agent",
# M85(D26):UI 框架无关的宿主层。Slint 版(crates/app)与 iced 版(crates/app-iced) # M85(D26):UI 框架无关的宿主层。Slint 版(crates/app)与 iced 版(crates/app-iced)
# 共用它;Slint 退役后只剩 iced 依赖,本 crate 不受影响。 # 共用它;Slint 退役(M118 起 `crates/app` 已不是 workspace 成员)后只剩 iced 依赖,本 crate 不受影响。
"crates/presenter", "crates/presenter",
"crates/app",
"crates/cli", "crates/cli",
"crates/design", "crates/design",
# P4(D26 iced 重写)第一步:与 Slint 版 `crates/app` 并存的新 UI crate。 # P4(D26 iced 重写)第一步:与 Slint 版 `crates/app` 并存的新 UI crate。
# bin 名 `unlucky-launcher-iced`,翻牌时才改名(见 DevDocs/SubAgentsTalk/app-iced-骨架与翻牌.md)。 # bin 名 `unlucky-launcher-iced`,翻牌时才改名(见 DevDocs/SubAgentsTalk/app-iced-骨架与翻牌.md)。
# M118 起它是发布矩阵里**唯一**的 GUI 来源:发布了 Slint 版的那个 bin 名属于被排除的 `crates/app`。
"crates/app-iced", "crates/app-iced",
# M87(D40 第 2 层):配色取色的解码适配层。**唯一**允许引入 `image` 类依赖的地方, # M87(D40 第 2 层):配色取色的解码适配层。**唯一**允许引入 `image` 类依赖的地方,
# 使 `crates/design` 能继续宣称「零第三方依赖」。 # 使 `crates/design` 能继续宣称「零第三方依赖」。
@@ -19,6 +19,15 @@ members = [
# 壳层(iced 版现在,别的壳将来)与 presenter 都依赖它,所以它不能认任何一个渲染框架。 # 壳层(iced 版现在,别的壳将来)与 presenter 都依赖它,所以它不能认任何一个渲染框架。
"crates/i18n", "crates/i18n",
] ]
# M118(D26):`crates/app` 是 Slint 退役树 —— UI 已迁到 iced,目录与源码**原样保留**作历史
# 参考(另一份参考资产见 DevDocs/ops/slint-reference/),但不再进默认构建与发布矩阵。摘出去的
# 是整棵 slint 1.18 + usvg 0.48 依赖树:本机单路冷编 ≈14 分钟,而 CI 的 `--workspace` 每次都会
# 编它,既拖长反馈,又会在并行超配时随机 OOM(见 DevDocs/00-踩坑记录-p2.md T7)。
# 排除 ≠ 删除,但**它确实不再可编**:该 manifest 整份都是 `*.workspace = true` 继承写法,
# 一旦不是 workspace 成员,继承无源、manifest 解析即失败(实测:
# `cargo metadata --manifest-path crates/app/Cargo.toml` → "error inheriting `edition` …")。
# 要读这一版实现请直接读源码,别再尝试构建它。
exclude = ["crates/app"]
[workspace.package] [workspace.package]
version = "0.1.0" version = "0.1.0"
+18 -7
View File
@@ -7,16 +7,25 @@ Copyright 2026 AskaEth (YeiJ)
第三方组件声明 第三方组件声明
-------------------------------------------------------------------------- --------------------------------------------------------------------------
1. Slint(https://slint.dev) 1. Slint(https://slint.dev)——**历史源码,不随任何产物分发**
本产品的 GUI 层使用 Slint。Slint 以 Royalty-Free 2.0 许可(LicenseRef-Slint-Royalty-free-2.0) 本产品**当前发布的 GUI 框架是 iced**(见第 3 节),二进制里不含任何 Slint 代码。
使用,未修改 Slint 本体源码。 仓库里仍保留 Slint 版 GUI 的源码(`crates/app`),仅作历史参考:它已被 cargo workspace
`exclude`,不参与构建、测试与发布,也不会进入本项目的任何 Release 资产。
Slint Royalty-Free 2.0 的唯一义务:应用「关于」页显示官方 AboutSlint 部件, 因此 Slint Royalty-Free 2.0(LicenseRef-Slint-Royalty-free-2.0)**对当前产物不适用**;它适用的是
或在公开下载页放置「Made with Slint」徽章。本项目的落地位置见 crates/app 的关于页(P2 排期) 此前已发布、且确实链接了 Slint 的 `v0.1.0` 构建——义务挂在每一次「随应用分发」上,而不是挂在某个
与 GitHub Release 说明页;在此之前由本文件的声明承担告知义务。 版本号上。上游许可 §2 给出两种形式:(a) 在可从顶层菜单进入的「关于」页/对话框里显示官方
`AboutSlint` 部件(没有该页时改为启动画面);(b) 在可下载该二进制的公开网页上显示官方归属徽章。
**`v0.1.0` 的设置页「关于」分组里内置了 `AboutSlint` 部件**(`crates/app/ui/pages/settings.slint`,
从侧栏进入);该部件是否满足 §2(a) 的全部条件、以及 `v0.1.0` 发布页上那是文字链接而不是 §2(b) 点名的
官方徽章图这两点,**本文件不作法律判断**,具体以 Slint 官方许可原文为准。`v0.1.0` 的 NOTICE、发布页
说明与本文件这一节**都只是记录,不构成履行声明**。后续版本不再链接 Slint,该义务不随新产物新增;
本文件保留这一节,是为了不让这段历史丢失,而不是声明本产品依赖 Slint。
Slint 的其余许可分支(GPL-3.0-only / Slint Software License 3.0)本项目不适用。 若将来重新把 `crates/app` 纳入构建并发布链接 Slint 的二进制,该义务随之恢复——届时需同时恢复
AboutSlint 部件/徽章与本节的现行声明。Slint 的其余许可分支(GPL-3.0-only / Slint Software
License 3.0)本项目始终不适用。
2. Google Material Color Utilities(https://github.com/material-foundation/material-color-utilities) 2. Google Material Color Utilities(https://github.com/material-foundation/material-color-utilities)
@@ -45,6 +54,8 @@ Copyright 2026 AskaEth (YeiJ)
3. Rust crates 生态 3. Rust crates 生态
当前 GUI 框架:iced 0.14(MIT),连同 winit(Apache-2.0)、wgpu(MIT OR Apache-2.0)、
tiny-skia(BSD-3-Clause)——它们都在下面的白名单内。
cargo-deny 白名单见 deny.toml:Apache-2.0 / MIT / BSD-2-Clause / BSD-3-Clause / Zlib / BSL-1.0 / ISC / cargo-deny 白名单见 deny.toml:Apache-2.0 / MIT / BSD-2-Clause / BSD-3-Clause / Zlib / BSL-1.0 / ISC /
CC0-1.0 / MIT-0 / Unlicense / Unicode-3.0 / CDLA-Permissive-2.0 / Apache-2.0 WITH LLVM-exception。 CC0-1.0 / MIT-0 / Unlicense / Unicode-3.0 / CDLA-Permissive-2.0 / Apache-2.0 WITH LLVM-exception。
GPL 系(含 AGPL、LGPL 静态链接、SSPL)依赖不得进入主干。 GPL 系(含 AGPL、LGPL 静态链接、SSPL)依赖不得进入主干。
+54 -28
View File
@@ -15,8 +15,10 @@ attached to other agent tooling as an MCP server.
- **Single-file native binary**: the launcher itself needs no JVM, no WebView and no sidecar - **Single-file native binary**: the launcher itself needs no JVM, no WebView and no sidecar
runtime. The JDK a game needs is downloaded and managed per instance, not as a launcher runtime. The JDK a game needs is downloaded and managed per instance, not as a launcher
dependency. dependency.
- **Two UI stacks during the rewrite**: the released UI is Slint (Royalty-Free 2.0); the - **One UI stack in the build**: the UI is iced 0.14 + Material 3 Expressive. The earlier Slint
next-generation UI is iced 0.14 + Material 3 Expressive and is still being written (see Status). implementation (`crates/app`) is kept as source-only history: it is excluded from the cargo
workspace, so the workspace never builds, tests or ships it. v0.1.0 — the only tagged release so
far — still carries a Slint build (see Status).
- **CPU software-rendering fallback**: `--software-renderer` keeps the interface rendering - **CPU software-rendering fallback**: `--software-renderer` keeps the interface rendering
normally on machines with no usable GPU or with broken drivers. normally on machines with no usable GPU or with broken drivers.
- **AI agent + MCP**: chat, setting edits and crash diagnosis inside the launcher; `ull agent - **AI agent + MCP**: chat, setting edits and crash diagnosis inside the launcher; `ull agent
@@ -34,7 +36,14 @@ attached to other agent tooling as an MCP server.
Two tracks. They are not the same product yet, so read the headings before downloading anything. Two tracks. They are not the same product yet, so read the headings before downloading anything.
### Track A — released: v0.1.0, Slint UI (this is what you can download today) ### Track A — historical: v0.1.0, Slint UI (the only tagged release so far)
> **Historical implementation — no longer in the default build or the release matrix.** The Slint
> tree (`crates/app`) is kept in the repository as source-only reference: it is excluded from the
> cargo workspace, so `cargo build` / `cargo test` / `cargo clippy` never touch it and no artifact is
> built from it. Read the source if you want to see how that UI was put together; do not expect to
> build it. The list below is kept because it records what the v0.1.0 assets actually contain —
> since v0.1.0 the pipeline builds the iced binary and ships it under the same public asset names.
- **GitHub Releases `v0.1.0`** (2026-11-01, flagged pre-release): single-file assets for Windows - **GitHub Releases `v0.1.0`** (2026-11-01, flagged pre-release): single-file assets for Windows
x64 (a bare `.exe`), Linux x64 and macOS (arm64 and x64) `.tar.gz`. Every release also carries x64 (a bare `.exe`), Linux x64 and macOS (arm64 and x64) `.tar.gz`. Every release also carries
@@ -67,16 +76,18 @@ Two tracks. They are not the same product yet, so read the headings before downl
with automatic fallback, and the screenshot/diff channel the visual reviews use). with automatic fallback, and the screenshot/diff channel the visual reviews use).
- **Not done**: the component library (buttons, dialogs, lists, menus, search, and the rest of the - **Not done**: the component library (buttons, dialogs, lists, menus, search, and the rest of the
M3 set), page migration, and the i18n pass. Every page in the iced binary is a placeholder. M3 set), page migration, and the i18n pass. Every page in the iced binary is a placeholder.
- **Slint stays until the flip**: `crates/app` remains the reference implementation until the iced - **Slint is retired from the build, kept as reference**: `crates/app` is no longer a workspace member
build passes equivalence acceptance; only then is `unlucky-launcher-iced` renamed to (M118) — the source stays for historical reference only: not compiled, not tested, not shipped, and
`unlucky-launcher` and the Slint crate retired. no longer buildable as a workspace member. What is left of the flip is the *rename*: until the iced
build passes equivalence acceptance its binary is still called `unlucky-launcher-iced`, while the
release pipeline already ships it under the public `unlucky-launcher` asset name.
Binaries in the workspace, so you can tell them apart: Binaries you may run into, so you can tell them apart:
| Binary | Crate | State | | Binary | Crate | State |
|--------|-------|-------| |--------|-------|-------|
| `unlucky-launcher` | `crates/app` (Slint) | released in v0.1.0; still the usable GUI; also runs every CLI subcommand | | `unlucky-launcher-iced` | `crates/app-iced` (iced 0.14) | the only GUI binary the workspace builds; the release pipeline stages it under the public name `unlucky-launcher`; same subcommand dispatch |
| `unlucky-launcher-iced` | `crates/app-iced` (iced 0.14) | under construction; skeleton only, not shipped; same subcommand dispatch | | `unlucky-launcher` | `crates/app` (Slint) | **historical**: that crate is excluded from the workspace, so this binary is no longer built or shipped; the v0.1.0 assets are the last Slint builds |
| `ull` | `crates/cli` | the zero-UI build of the same command set, shipped as its own asset | | `ull` | `crates/cli` | the zero-UI build of the same command set, shipped as its own asset |
`unlucky-launcher` and `ull` are not duplicates. The GUI binary needs the graphics stack when it `unlucky-launcher` and `ull` are not duplicates. The GUI binary needs the graphics stack when it
@@ -122,8 +133,9 @@ Unlucky-Launcher (cargo workspace)
├── crates/agent # AI agent: ToolSpec registry + built-in client + MCP server (one core, two shapes) ├── crates/agent # AI agent: ToolSpec registry + built-in client + MCP server (one core, two shapes)
├── crates/design # design tokens: real HCT color / type / shape / spacing / elevation / motion / icons ├── crates/design # design tokens: real HCT color / type / shape / spacing / elevation / motion / icons
│ # / component metrics — zero UI dependencies, no third-party crates │ # / component metrics — zero UI dependencies, no third-party crates
├── crates/app # Slint UI (bin `unlucky-launcher`): the released GUI, kept until equivalence acceptance ├── crates/app # HISTORICAL Slint UI (bin `unlucky-launcher`): excluded from the workspace,
├── crates/app-iced # next-generation iced UI (bin `unlucky-launcher-iced`): under construction │ # source-only reference — never built, tested or shipped any more
├── crates/app-iced # the iced UI (bin `unlucky-launcher-iced`): the only GUI the workspace builds
├── crates/cli # CLI (bin `ull`): headless, zero UI ├── crates/cli # CLI (bin `ull`): headless, zero UI
└── packaging # single-file artifact matrix and release scripts └── packaging # single-file artifact matrix and release scripts
``` ```
@@ -149,7 +161,7 @@ Prerequisites:
`rustfmt` and `clippy` components) `rustfmt` and `clippy` components)
- Windows: MSVC toolchain (what CI uses) or MinGW-w64 (`x86_64-pc-windows-gnu`) - Windows: MSVC toolchain (what CI uses) or MinGW-w64 (`x86_64-pc-windows-gnu`)
- Linux: `libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev libwayland-dev - Linux: `libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev libwayland-dev
libxcb1-dev` — needed by both UI stacks (Slint's winit + femtovg, iced's winit + wgpu/tiny-skia) libxcb1-dev` — winit + wgpu/tiny-skia ask for these at build time (the Slint build also needed femtovg)
```bash ```bash
cargo fmt --all --check # formatting cargo fmt --all --check # formatting
@@ -158,24 +170,29 @@ cargo test --workspace # tests
cargo build --release # binaries land in target/release/ cargo build --release # binaries land in target/release/
``` ```
`--workspace` covers the eight members in the module map. `crates/app` is excluded at the workspace
level (M118), so none of these commands compiles the Slint tree — and there is no build command for
it: that crate is kept as source-only reference and can no longer be built as a workspace member.
What the build produces: What the build produces:
- `unlucky-launcher` — the Slint UI (the released one). It is also the CLI: the same binary answers - `unlucky-launcher-iced` — the GUI (iced). It is also the CLI: the same binary answers every
every subcommand listed above. subcommand listed above. The name is temporary — it becomes `unlucky-launcher` at the flip. The
- `unlucky-launcher-iced` — the iced UI skeleton. The name is temporary: it can only become release artifacts already carry the public name: the pipeline builds this bin and
`unlucky-launcher` at the flip, because two crates in one workspace cannot emit the same binary `packaging/make_artifacts.py` stages it as `unlucky-launcher[.exe]`, so download links never change.
name into the same target directory. Same subcommand dispatch. - `unlucky-launcher` — **historical**: no workspace build produces this binary any more; it belonged
to `crates/app`, which is excluded. The v0.1.0 assets are the last builds under this name.
- `ull` — the CLI as a standalone, UI-free binary. Same command set, no graphics stack, smaller, and - `ull` — the CLI as a standalone, UI-free binary. Same command set, no graphics stack, smaller, and
the right artifact for a machine that has no display at all. the right artifact for a machine that has no display at all.
Rendering switches: Rendering switches:
- `unlucky-launcher --software-renderer` (equivalent to `SLINT_BACKEND=winit-software`) forces the
CPU path on the Slint build
- `unlucky-launcher-iced --software-renderer` forces tiny-skia (`ICED_BACKEND=tiny-skia`); - `unlucky-launcher-iced --software-renderer` forces tiny-skia (`ICED_BACKEND=tiny-skia`);
`--screenshot <png>` writes one frame and exits, which is the acceptance channel `--screenshot <png>` writes one frame and exits, which is the acceptance channel CI's headless
- Both stacks fall back to the CPU path when no usable adapter is there: the Slint build through its smoke job uses
own renderer fallback chain, the iced build through its explicit probe - With no usable adapter the iced build's explicit probe falls back to the CPU path on its own
- **Historical** (v0.1.0's Slint build still takes it): `unlucky-launcher --software-renderer`, i.e.
`SLINT_BACKEND=winit-software`, forced the CPU renderer of that build through its own fallback chain
Dependency license check (optional locally, required in CI): Dependency license check (optional locally, required in CI):
`cargo deny check licenses bans sources`. `cargo deny check licenses bans sources`.
@@ -212,12 +229,21 @@ Minimum baseline: **Windows 10**; Windows 8.1 / 8 / 7 are not supported.
- **This project**: Apache License 2.0, see [LICENSE](LICENSE); third-party notices in - **This project**: Apache License 2.0, see [LICENSE](LICENSE); third-party notices in
[NOTICE](NOTICE). [NOTICE](NOTICE).
- **Slint** (<https://slint.dev>), Royalty-Free 2.0: obligation for the Slint build that v0.1.0 - **Slint** (<https://slint.dev>), Royalty-Free 2.0: obligation for the Slint build that `v0.1.0`
ships — an `AboutSlint` widget on the about page (still outstanding) and, until it lands, the ships — the condition attaches to each distribution of an Application, not to a version number.
NOTICE file, this section and a "Made with Slint" note on the release page. The obligation is Upstream §2 offers two forms: (a) the official `AboutSlint` widget on an "About" screen or dialog
kept for as long as a Slint-linked binary ships. reachable from the top level menu (or the splash screen when there is none); (b) the official Slint
- **iced** (<https://github.com/iced-rs/iced>), MIT: the framework of the next-generation UI, attribution badge on a public page where the binaries can be downloaded. **`v0.1.0`'s settings page
together with `winit` and, through it, `wgpu` / `tiny-skia` (Apache-2.0 / MIT). does embed the `AboutSlint` widget** (`crates/app/ui/pages/settings.slint`, reachable from the
sidebar); whether that satisfies §2(a), and the fact that the `v0.1.0` release page carries a text
link rather than the §2(b) badge, are **not judged here** — Slint's own licence text is the
authority. The `v0.1.0` NOTICE, that page's text and this section are **records, not a statement of
discharge**. Later builds link no Slint, so they add nothing to this obligation. The obligation
follows a **Slint-linked binary**: it applies to the `v0.1.0` assets (and to any later build that
links Slint again), not to new iced builds — `crates/app` is excluded from the workspace, so the
pipeline no longer produces one.
- **iced** (<https://github.com/iced-rs/iced>), MIT: the framework of the UI, together with `winit`
(Apache-2.0) and, through it, `wgpu` (MIT / Apache-2.0) / `tiny-skia` (BSD-3-Clause).
- **Google Material Color Utilities** (Apache-2.0): the color tokens in `crates/design` are a Rust - **Google Material Color Utilities** (Apache-2.0): the color tokens in `crates/design` are a Rust
port of its HCT / ColorSpec2021 algorithms; details and the exact ported functions are listed in port of its HCT / ColorSpec2021 algorithms; details and the exact ported functions are listed in
[NOTICE](NOTICE). [NOTICE](NOTICE).
+45 -24
View File
@@ -13,8 +13,9 @@ Unlucky Launcher(ULL)是一个用 Rust 写的免费开源 Minecraft: Java
- **单文件原生二进制**:启动器本体不需要 JVM、不需要 WebView、不带旁挂运行时。游戏要的 JDK 由 - **单文件原生二进制**:启动器本体不需要 JVM、不需要 WebView、不带旁挂运行时。游戏要的 JDK 由
启动器按实例下载管理,不是启动器的依赖。 启动器按实例下载管理,不是启动器的依赖。
- **重写期两套 UI 并存**:已发布版本是 Slint(Royalty-Free 2.0);下一代 UI 是 iced 0.14 + - **默认构建里只有一套 UI**:UI 是 iced 0.14 + Material 3 Expressive。早先的 Slint 实现
Material 3 Expressive,目前仍在写(见「当前状态」)。 (`crates/app`)作为**历史参考**保留源码:已被 workspace `exclude`,默认构建不编、测试不跑、
发布不发。唯一已发布的 tag `v0.1.0` 发的仍是 Slint 版(见「当前状态」)。
- **CPU 软渲染兜底**:`--software-renderer` 让没有可用 GPU 或驱动有问题的老机也能正常显示界面。 - **CPU 软渲染兜底**:`--software-renderer` 让没有可用 GPU 或驱动有问题的老机也能正常显示界面。
- **AI Agent + MCP**:启动器内可对话、改设置、读崩溃诊断;`ull agent serve` 把同一个内核以 MCP - **AI Agent + MCP**:启动器内可对话、改设置、读崩溃诊断;`ull agent serve` 把同一个内核以 MCP
server 形态(stdio 或 Streamable HTTP)开放给外部 Agent 工具。 server 形态(stdio 或 Streamable HTTP)开放给外部 Agent 工具。
@@ -29,7 +30,13 @@ Unlucky Launcher(ULL)是一个用 Rust 写的免费开源 Minecraft: Java
两条轨道,还不是同一个东西——看小标题再决定下载什么。 两条轨道,还不是同一个东西——看小标题再决定下载什么。
### 轨道 A —— 已发布:v0.1.0,Slint 版(现在能下载的就是它) ### 轨道 A —— 历史:v0.1.0,Slint 版(目前唯一的 tag)
> **历史实现——已不在默认构建与发布矩阵内。** Slint 那棵树(`crates/app`)作为源码参考留在仓库里:
> 已被 cargo workspace `exclude`,所以 `cargo build` / `cargo test` / `cargo clippy` 都不会碰它,
> 也没有任何产物从它构建。想了解那一版 UI 怎么写的就直接读源码——**不要再尝试构建它**(它的 manifest
> 整份是 workspace 继承写法,离开 workspace 后连解析都过不了)。下面这段原文保留,因为它记录的是
> `v0.1.0` 资产里到底装了什么;从 `v0.1.0` 之后,流水线构建的是 iced 版,对外文件名不变。
- **GitHub Releases `v0.1.0`**(2026-11-01,标记为 pre-release):单文件产物覆盖 Windows x64(裸 - **GitHub Releases `v0.1.0`**(2026-11-01,标记为 pre-release):单文件产物覆盖 Windows x64(裸
`.exe`)、Linux x64、macOS(arm64 与 x64)`.tar.gz`。每个 Release 另挂 `SHA256SUMS.txt` 与 `.exe`)、Linux x64、macOS(arm64 与 x64)`.tar.gz`。每个 Release 另挂 `SHA256SUMS.txt` 与
@@ -59,15 +66,16 @@ Unlucky Launcher(ULL)是一个用 Rust 写的免费开源 Minecraft: Java
对比通道)。 对比通道)。
- **没完成**:组件库(按钮、对话框、列表、菜单、搜索栏,以及 M3 其余组件)、页面搬迁、i18n 文案。 - **没完成**:组件库(按钮、对话框、列表、菜单、搜索栏,以及 M3 其余组件)、页面搬迁、i18n 文案。
iced 版里现在每一个页面都是占位。 iced 版里现在每一个页面都是占位。
- **Slint 版在翻牌前不退场**:`crates/app` 作为参考实现保留,直到 iced 版通过等价验收;那时才把 - **Slint 版已退出构建,只留源码作参考**:`crates/app` 不再是 workspace 成员(M118)——源码留作历史
`unlucky-launcher-iced` 改名成 `unlucky-launcher`,并让 Slint 版退役。 参考,不编译、不测试、不发布,也不再有可用的构建方式。翻牌只剩**改名**这一步:在 iced 版通过等价
验收前,它的 bin 还叫 `unlucky-launcher-iced`;发布流水线已经把它按对外名 `unlucky-launcher` 打出去。
工作区里的三个 bin,先认清楚: 可能会遇到的 bin,先认清楚:
| 二进制 | crate | 状态 | | 二进制 | crate | 状态 |
|--------|-------|------| |--------|-------|------|
| `unlucky-launcher` | `crates/app`(Slint) | v0.1.0 已发布,仍是当前能用的图形界面;同时支持全部 CLI 子命令 | | `unlucky-launcher-iced` | `crates/app-iced`(iced 0.14) | workspace 里**唯一**的 GUI bin;发布流水线把它按对外名 `unlucky-launcher` 落盘;子命令分发同款 |
| `unlucky-launcher-iced` | `crates/app-iced`(iced 0.14) | 施工中,仅骨架,不随发布包分发;子命令分发同款 | | `unlucky-launcher` | `crates/app`(Slint) | **历史**:该 crate 已被 workspace `exclude`,这个 bin 不再被构建或分发;`v0.1.0` 的资产是最后一批 Slint 版 |
| `ull` | `crates/cli` | 同一套命令的零 UI 构建,作为独立资产随发布分发 | | `ull` | `crates/cli` | 同一套命令的零 UI 构建,作为独立资产随发布分发 |
`unlucky-launcher` 与 `ull` 不是重复品:开窗时 GUI 二进制要用图形栈(X11 / Wayland / GL 走 `unlucky-launcher` 与 `ull` 不是重复品:开窗时 GUI 二进制要用图形栈(X11 / Wayland / GL 走
@@ -109,8 +117,9 @@ Unlucky-Launcher (cargo workspace)
├── crates/agent # AI Agent:ToolSpec 注册表 + 内置客户端 + MCP server(双形态共用一个内核) ├── crates/agent # AI Agent:ToolSpec 注册表 + 内置客户端 + MCP server(双形态共用一个内核)
├── crates/design # 设计令牌:真 HCT 色彩 / 排版 / 形状 / 间距 / 高程 / 动效 / 图标 / 组件度量 ├── crates/design # 设计令牌:真 HCT 色彩 / 排版 / 形状 / 间距 / 高程 / 动效 / 图标 / 组件度量
│ # —— 零 UI 依赖、零第三方 crate │ # —— 零 UI 依赖、零第三方 crate
├── crates/app # Slint UI(bin `unlucky-launcher`):已发布的图形界面,保留到等价验收通过 ├── crates/app # 历史 Slint UI(bin `unlucky-launcher`):已被 workspace `exclude`,
├── crates/app-iced # 下一代 iced UI(bin `unlucky-launcher-iced`):施工中 │ # 仅作源码参考——不再被构建 / 测试 / 发布
├── crates/app-iced # iced UI(bin `unlucky-launcher-iced`):workspace 里唯一的 GUI bin
├── crates/cli # CLI(bin `ull`):headless,零 UI ├── crates/cli # CLI(bin `ull`):headless,零 UI
└── packaging # 单文件产物矩阵与发布脚本 └── packaging # 单文件产物矩阵与发布脚本
``` ```
@@ -135,28 +144,34 @@ Unlucky-Launcher (cargo workspace)
- Rust stable(`rust-toolchain.toml` 会自动选工具链;门禁还需要 `rustfmt` / `clippy` 组件) - Rust stable(`rust-toolchain.toml` 会自动选工具链;门禁还需要 `rustfmt` / `clippy` 组件)
- Windows:MSVC 工具链(CI 用这套)或 MinGW-w64(`x86_64-pc-windows-gnu`) - Windows:MSVC 工具链(CI 用这套)或 MinGW-w64(`x86_64-pc-windows-gnu`)
- Linux:`libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev libwayland-dev - Linux:`libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev libwayland-dev
libxcb1-dev`——两套 UI 都要用(Slint 的 winit + femtovg,iced 的 winit + wgpu/tiny-skia) libxcb1-dev`——winit + wgpu/tiny-skia 编译期要用(Slint 版当年还要 femtovg)
```bash ```bash
cargo fmt --all --check # 格式 cargo fmt --all --check # 格式
cargo clippy --workspace --all-targets -- -D warnings # 静态检查(零 warning 门禁) cargo clippy --workspace --all-targets -- -D warnings # 静态检查(零 warning 门禁)
cargo test --workspace # 测试 cargo test --workspace # 测试
cargo build --release # 三个 bin 都落在 target/release/ cargo build --release # bin 落在 target/release/
``` ```
编出来的三个 bin: `--workspace` 就是模块地图里那八个成员:`crates/app` 已在 workspace 层被排除(M118),上面这些命令
都不会编 Slint 那棵树——而且**它没有构建方式**:该 crate 只作源码参考,作为 workspace 成员已不可编。
- `unlucky-launcher`——Slint 版(已发布的那个)。它同时也是 CLI:同一个二进制认上面列的全部子命令。 编出来的 bin:
- `unlucky-launcher-iced`——iced 版骨架。名字是临时的:只有翻牌时才能改成 `unlucky-launcher`,
因为同一个工作区里两个 crate 不能往同一个 target 目录里产出同名 bin。子命令分发同款。 - `unlucky-launcher-iced`——GUI(iced)。它同时也是 CLI:同一个二进制认上面列的全部子命令。名字是
临时的,翻牌时才改成 `unlucky-launcher`;发布产物**已经**用对外名:流水线编这个 bin,
`packaging/make_artifacts.py` 落盘时改成 `unlucky-launcher[.exe]`,所以下载链接不用变。
- `unlucky-launcher`——**历史**:workspace 构建已不产出它(它属于被排除的 `crates/app`)。
`v0.1.0` 的资产是最后一批用这个名字发出去的构建。
- `ull`——CLI 的独立无 UI 构建:同一套命令、不链接图形栈、体积更小,给完全没有显示设备的机器用。 - `ull`——CLI 的独立无 UI 构建:同一套命令、不链接图形栈、体积更小,给完全没有显示设备的机器用。
渲染开关: 渲染开关:
- `unlucky-launcher --software-renderer`(等价于 `SLINT_BACKEND=winit-software`)强制 Slint 版走 CPU
- `unlucky-launcher-iced --software-renderer` 强制 tiny-skia(`ICED_BACKEND=tiny-skia`); - `unlucky-launcher-iced --software-renderer` 强制 tiny-skia(`ICED_BACKEND=tiny-skia`);
`--screenshot <png>` 出一帧图后退出,这是验收通道 `--screenshot <png>` 出一帧图后退出,这是 CI 无头冒烟 job 用的验收通道
- 两套栈在没有可用适配器时都会退到 CPU:Slint 版走它自己的渲染器回退链,iced 版走显式探测 - 没有可用适配器时,iced 版靠显式后端探测自己退到 CPU
- **历史**(`v0.1.0` 的 Slint 版仍认这条):`unlucky-launcher --software-renderer`,即
`SLINT_BACKEND=winit-software`,让那一版走它自己的渲染器回退链
依赖许可检查(本地可选,CI 必跑):`cargo deny check licenses bans sources`。 依赖许可检查(本地可选,CI 必跑):`cargo deny check licenses bans sources`。
@@ -186,11 +201,17 @@ cargo build --release # 三个 bin 都落在 ta
## 许可证与致谢 ## 许可证与致谢
- **本项目**:Apache License 2.0,见 [LICENSE](LICENSE);第三方声明见 [NOTICE](NOTICE)。 - **本项目**:Apache License 2.0,见 [LICENSE](LICENSE);第三方声明见 [NOTICE](NOTICE)。
- **Slint**(<https://slint.dev>),Royalty-Free 2.0:v0.1.0 发布的正是 Slint 版,这项义务继续有效 - **Slint**(<https://slint.dev>),Royalty-Free 2.0:义务针对 `v0.1.0` 发布的那批构建——条件挂在每一次
——关于页放 `AboutSlint` 部件(尚未落地),在此之前由 NOTICE、本节与发布页的「Made with Slint」 「随应用分发」上,而不是挂在某个版本号上。上游 §2 给出两种形式:(a) 在可从顶层菜单进入的「关于」页/
说明承担告知义务。只要还有链接 Slint 的产物在发,这条义务就保留。 对话框里显示官方 `AboutSlint` 部件(没有该页时改为启动画面);(b) 在可下载该二进制的公开网页上显示
- **iced**(<https://github.com/iced-rs/iced>),MIT:下一代 UI 的框架;与它同栈的 `winit`、以及经它 官方归属徽章。**`v0.1.0` 的设置页「关于」分组里内置了 `AboutSlint` 部件**(`crates/app/ui/pages/
引入的 `wgpu` / `tiny-skia`(Apache-2.0 / MIT)。 settings.slint`,从侧栏进入);该部件是否满足 §2(a) 的全部条件、以及 `v0.1.0` 发布页上那是文字链接
而不是 §2(b) 点名的官方徽章图这两点,**本节不作法律判断**,具体以 Slint 官方许可原文为准。`v0.1.0`
的 NOTICE、发布页说明与本节**都只是记录,不构成履行声明**。后续版本不再链接 Slint,该义务不随新产物
新增。义务跟着**链接了 Slint 的产物**走:已发布的 `v0.1.0` 资产(以及将来任何重新链接 Slint 的构建)
适用;`crates/app` 已被 workspace `exclude`,流水线不再产出链接 Slint 的二进制,所以新的 iced 版不适用。
- **iced**(<https://github.com/iced-rs/iced>),MIT:现役 UI 的框架;与它同栈的 `winit`(Apache-2.0)、
以及经它引入的 `wgpu`(MIT / Apache-2.0)/ `tiny-skia`(BSD-3-Clause)。
- **Google Material Color Utilities**(Apache-2.0):`crates/design` 的色彩令牌是它 HCT / - **Google Material Color Utilities**(Apache-2.0):`crates/design` 的色彩令牌是它 HCT /
ColorSpec2021 算法的 Rust 重写移植,移植清单与逐函数对照写在 [NOTICE](NOTICE)。 ColorSpec2021 算法的 Rust 重写移植,移植清单与逐函数对照写在 [NOTICE](NOTICE)。
- **其余主要依赖**:MCP server 的 `rmcp` 与 `axum`(Apache-2.0 / MIT)、`tokio`(MIT)、`reqwest` - **其余主要依赖**:MCP server 的 `rmcp` 与 `axum`(Apache-2.0 / MIT)、`tokio`(MIT)、`reqwest`
+17 -18
View File
@@ -25,8 +25,8 @@
- 归档解压后是**一个顶层目录** `unlucky-launcher-<ver>-<槽位>/`,用户不会解压出一堆散文件。 - 归档解压后是**一个顶层目录** `unlucky-launcher-<ver>-<槽位>/`,用户不会解压出一堆散文件。
- **`ull` 是并列的裸资产,不塞进 tar.gz**:headless 用户要的就是「下一个小文件直接跑」;归档里放 - **`ull` 是并列的裸资产,不塞进 tar.gz**:headless 用户要的就是「下一个小文件直接跑」;归档里放
LICENSE/NOTICE/README.md 是许可义务,而这三份本来就作为并列 Release 资产分发,裸 `ull` 不缺条款。 LICENSE/NOTICE/README.md 是许可义务,而这三份本来就作为并列 Release 资产分发,裸 `ull` 不缺条款。
- `unlucky-launcher` 本体也认全部 CLI 子命令(M82),`ull` 不是第二个界面,而是**不链接图形栈**的 - `unlucky-launcher` 本体也认全部 CLI 子命令(M82),`ull` 不是第二个界面,而是**不链接图形栈**的同一套命令(差异见 §4 与 §7.4),两者不是重复品。
同一套命令(差异见 §4 与 §7.4)——两者都不是重复品。 M118:GUI 的**构建产物**名是 `unlucky-launcher-iced`(`crates/app` 已被 workspace `exclude`,不再产出 `unlucky-launcher`);对外文件名仍是 `unlucky-launcher`,改名在 `make_artifacts.py` 落盘时(`LAUNCHER_BIN` 找产物 / `LAUNCHER_ASSET` 定对外名),所以下载链接 / `SHA256SUMS` / 本文矩阵不变。
- `<ver>` 不带前导 `v`(tag 是 `v0.1.0`,文件名是 `0.1.0`)——由脚本统一裁剪。 - `<ver>` 不带前导 `v`(tag 是 `v0.1.0`,文件名是 `0.1.0`)——由脚本统一裁剪。
- UNIX 归档内 `mtime`/`uid`/`gid` 归零、权限固定(二进制 0755、文本 0644), - UNIX 归档内 `mtime`/`uid`/`gid` 归零、权限固定(二进制 0755、文本 0644),
同源构建的 sha256 不受打包时刻影响。 同源构建的 sha256 不受打包时刻影响。
@@ -51,7 +51,7 @@
### 本地(当前平台) ### 本地(当前平台)
```bash ```bash
cargo build --release --locked # 产出 target/release/{unlucky-launcher,ull,unlucky-launcher-iced} cargo build --release --locked # 产出 target/release/{unlucky-launcher-iced,ull}(M118:Slint 版不再进 workspace)
cargo build --profile fast --bins # 只是想快点看到画面/跑测试:快档(§6),产物在 target/fast/ cargo build --profile fast --bins # 只是想快点看到画面/跑测试:快档(§6),产物在 target/fast/
python packaging/make_artifacts.py --out dist # 产物落在 dist/,含 SHA256SUMS.txt python packaging/make_artifacts.py --out dist # 产物落在 dist/,含 SHA256SUMS.txt
python packaging/make_artifacts.py --version v0.1.0 # 顺带校验版本一致性 python packaging/make_artifacts.py --version v0.1.0 # 顺带校验版本一致性
@@ -67,7 +67,7 @@ macOS x64),其它组合直接报错退出——不在矩阵里的平台不
```bash ```bash
rustup target add aarch64-pc-windows-msvc rustup target add aarch64-pc-windows-msvc
cargo build --release --locked --target aarch64-pc-windows-msvc \ cargo build --release --locked --target aarch64-pc-windows-msvc \
--bin unlucky-launcher --bin ull # 不用 --bins:发布矩阵只发 GUI + ull,iced 不进产物 --bin unlucky-launcher-iced --bin ull # 不用 --bins:矩阵只发 GUI + ull;GUI 的构建产物名是 iced 的 bin(M118)
python packaging/make_artifacts.py --slot windows-arm64 \ python packaging/make_artifacts.py --slot windows-arm64 \
--build-dir target/aarch64-pc-windows-msvc/release --out dist --build-dir target/aarch64-pc-windows-msvc/release --out dist
``` ```
@@ -147,17 +147,16 @@ Windows ARM64 原生库(Mojang/LWJGL 只发 x64/x86),实际游玩仍走系
| glibc ≥ 2.35 | 由 `ubuntu-22.04` 构建决定;更老的发行版请自行重建或等 musl 版 | | glibc ≥ 2.35 | 由 `ubuntu-22.04` 构建决定;更老的发行版请自行重建或等 musl 版 |
| `libxkbcommon.so.0` | winit 键盘处理 | | `libxkbcommon.so.0` | winit 键盘处理 |
| `libfontconfig.so.1` + `libfreetype.so.6` | 文本渲染 | | `libfontconfig.so.1` + `libfreetype.so.6` | 文本渲染 |
| `libGL.so.1` / `libEGL.so.1` | femtovg GL 渲染;缺失走软渲染 | | `libGL.so.1` / `libEGL.so.1` | Slint 版:femtovg 的 GL 渲染;iced 版:wgpu 的 GL/EGL 后端(**推断,未实测**)。两者缺失都退到 CPU 软渲染(tiny-skia) |
| `libxcb.so.1` / `libX11.so.6` | X11 后端(Wayland 会话下也需 XWayland 相关库) | | `libxcb.so.1` / `libX11.so.6` | X11 后端(Wayland 会话下也需 XWayland 相关库) |
| `libwayland-client.so.0` | Wayland 后端 | | `libwayland-client.so.0` | Wayland 后端 |
Debian/Ubuntu 一行:`sudo apt install libxkbcommon0 fontconfig libgl1 libegl1 libxcb1 libx11-6 libwayland-client0`。 Debian/Ubuntu 一行:`sudo apt install libxkbcommon0 fontconfig libgl1 libegl1 libxcb1 libx11-6 libwayland-client0`。
运行期**不需要** `-dev` 包、不需要 Java(Java 是启动游戏时才由启动器自己管理的东西)。 运行期**不需要** `-dev` 包、不需要 Java(Java 是启动游戏时才由启动器自己管理的东西)。
上表是**开窗**要用的库,不是进程启动时就要的:实测(§7.4)除 `libfontconfig.so.1` 外它们**都不在** ELF **M118(D26)口径**:除上表里标注「推断,未实测」的那一行外,这张表与下面这条结论都是 **Slint 版 GUI** 的实测记录(M82:`readelf -d` + `ldd` + `nm -D`);iced 版(`crates/app-iced`)的 `DT_NEEDED` / `dlopen` 集合**尚未重测**,请按「同一族库、细节待验」读,别当成 iced 版的实测承诺。
的 `DT_NEEDED` 里,由 `dlopen` 在创建窗口那一刻才加载。所以同一台没有 X11 / Wayland / GL 的机器上 仍然成立的部分:上表是**开窗**要用的库,不是进程启动门槛——实测(§7.4,Slint 版)除 `libfontconfig.so.1` 外它们**都不在** ELF 的 `DT_NEEDED` 里,由 `dlopen` 在开窗那一刻才加载,所以没有 X11 / Wayland / GL 的机器上 `unlucky-launcher instances list` 照样跑得起来,`ull` 连 fontconfig 都不需要。
`unlucky-launcher instances list` 照样跑得起来(GUI 二进制先分发后开窗),而 `ull` 连 fontconfig 都不需要。 arm64 侧同理(同一份依赖树;历史口径里的「winit + femtovg」中 femtovg 只属于 Slint 版)。
arm64 侧是同一套(同一份依赖树、同一个 winit/femtovg 组合)。
### Linux arm64(aarch64) ### Linux arm64(aarch64)
@@ -181,7 +180,7 @@ macOS 产物是**裸二进制**而非 `.app` bundle:符合单文件承诺,
## 5. 软渲染兜底(无 GPU / 老驱动 / 远程桌面) ## 5. 软渲染兜底(无 GPU / 老驱动 / 远程桌面)
Slint 默认 `renderer-femtovg`(GL)。两条兜底路径,效果等价,任选其一: M118(D26):以下是**历史实现(Slint 版,默认 `renderer-femtovg`/GL)**的兜底路径,已不在默认构建与发布矩阵内(`crates/app` 被 workspace `exclude`,源码仅作参考、不编译)。现役 GUI 是 iced 版:`unlucky-launcher-iced --software-renderer`(= `ICED_BACKEND=tiny-skia`)走 tiny-skia 光栅化。v0.1.0 的产物仍认下面两条:
```bash ```bash
unlucky-launcher --software-renderer # 启动参数(本仓库解析后设 SLINT_BACKEND) unlucky-launcher --software-renderer # 启动参数(本仓库解析后设 SLINT_BACKEND)
@@ -190,9 +189,8 @@ SLINT_BACKEND=winit-software unlucky-launcher # 环境变量(Slint
- 参数解析在 `crates/app/src/main.rs` 的 `apply_renderer_flag()`,只认完全相等的 `--software-renderer`。 - 参数解析在 `crates/app/src/main.rs` 的 `apply_renderer_flag()`,只认完全相等的 `--software-renderer`。
- 软渲染是 CPU 光栅化,无 GPU 也能出图;代价是动画与大窗口重绘更慢(Metro 纯色界面影响有限)。 - 软渲染是 CPU 光栅化,无 GPU 也能出图;代价是动画与大窗口重绘更慢(Metro 纯色界面影响有限)。
- 验收/无交互桌面场景另有进程内截图通道:`--screenshot <png>`(见 `crates/app/src/screenshot.rs`), - 截图通道 `--screenshot <png>`:历史实现 `crates/app/src/screenshot.rs`,现役 `crates/app-iced/src/shot/`;
它同样能在软渲染下工作,是 CI 与远程机唯一可靠的"真出图"证据。 软渲染下照样出图,是 CI 与远程机唯一可靠的"真出图"证据(发布冒烟见 §7,本机实跑 `--software-renderer --screenshot`)。
- 发布冒烟(本机实跑,见 §7)就是用 `--software-renderer --screenshot` 组合证明"无 GPU 也能起来"。
## 6. Cargo 构建档位(release 体积档 / fast 速度档) ## 6. Cargo 构建档位(release 体积档 / fast 速度档)
@@ -237,14 +235,14 @@ SLINT_BACKEND=winit-software unlucky-launcher # 环境变量(Slint
① **依赖 opt-level 从 3 退回 `"z"` 又快 29.6% 又小 19~24%**(两臂同为 cu16,唯一自变量是 opt-level)——抬 opt-level 是拿编译时间和体积换运行期; ① **依赖 opt-level 从 3 退回 `"z"` 又快 29.6% 又小 19~24%**(两臂同为 cu16,唯一自变量是 opt-level)——抬 opt-level 是拿编译时间和体积换运行期;
`cu1 → cu16` 的收益本轮**没有同条件对照**,未归因。② `fast` **冷编译比 `release` 还慢**(24m27s vs 16m01s)、体积 2.3 倍,瓶颈在两个大 UI `cu1 → cu16` 的收益本轮**没有同条件对照**,未归因。② `fast` **冷编译比 `release` 还慢**(24m27s vs 16m01s)、体积 2.3 倍,瓶颈在两个大 UI
crate + 链接 + 机械盘 IO——定位是 **SSD 机器与增量重编**,不是冷编译加速。体积归因:GUI 相对旧基线 +12.9%(13.92 → 15.72 MiB)里 crate + 链接 + 机械盘 IO——定位是 **SSD 机器与增量重编**,不是冷编译加速。体积归因:GUI 相对旧基线 +12.9%(13.92 → 15.72 MiB)里
「代码增长」与「依赖 cu16」**未分离**;`ull` 只 +1.7%,在 +1~3% 口径内。体积构成:Slint 运行时(双渲染器 + winit/accesskit/托盘)+ rustls/reqwest + rig/rmcp/axum 为主,`ull` 更小是 D18(CLI 零 UI 依赖)的结果。 「代码增长」与「依赖 cu16」**未分离**;`ull` 只 +1.7%,在 +1~3% 口径内。体积构成(**M118 前的 Slint 版,历史数**):Slint 运行时(双渲染器 + winit/accesskit/托盘)+ rustls/reqwest + rig/rmcp/axum 为主,`ull` 更小是 D18(CLI 零 UI 依赖)的结果。
### 7.2 冒烟(真跑,2026-10-31 夜间 + 11-01 早,本机 Win11 26100) ### 7.2 冒烟(真跑,2026-10-31 夜间 + 11-01 早,本机 Win11 26100)
| 命令 | 实测结果 | | 命令 | 实测结果 |
|------|---------| |------|---------|
| `ull.exe --version` | 输出 `ull 0.1.0(unlucky-cli)` + `os: Windows 11 · 10.0.26100(x86_64)· 满足最低要求`,exit 0 —— 版本与 workspace `0.1.0` 一致 | | `ull.exe --version` | 输出 `ull 0.1.0(unlucky-cli)` + `os: Windows 11 · 10.0.26100(x86_64)· 满足最低要求`,exit 0 —— 版本与 workspace `0.1.0` 一致 |
| `unlucky-launcher.exe --software-renderer --screenshot … --data-dir …` | exit 0,产出 1180×760 PNG(3,588,298 B);看图确认 Metro 实例页(侧栏/搜索/空态/AI 助手面板)正常渲染 → **软渲染路径真的能出图**,不依赖 GL | | `unlucky-launcher.exe --software-renderer --screenshot … --data-dir …`(Slint 版,历史实测) | exit 0,产出 1180×760 PNG(3,588,298 B);看图确认 Metro 实例页(侧栏/搜索/空态/AI 助手面板)正常渲染 → **软渲染路径真的能出图**,不依赖 GL |
| `unlucky-launcher.exe --version` | **无输出**,8 秒后被 timeout 杀掉(exit 124)——GUI 当时不解析 `--version`,会直接开窗口。M82 后已修(走 CLI 分发,见 §10-1)| | `unlucky-launcher.exe --version` | **无输出**,8 秒后被 timeout 杀掉(exit 124)——GUI 当时不解析 `--version`,会直接开窗口。M82 后已修(走 CLI 分发,见 §10-1)|
### 7.3 打包验证(本机 Windows 槽位真跑) ### 7.3 打包验证(本机 Windows 槽位真跑)
@@ -280,7 +278,7 @@ CI 的 MSVC `+crt-static` 产物同理(静态 CRT);`release.yml` 里用 `d
总之不允许"悄悄要用户装可再发行包"。 总之不允许"悄悄要用户装可再发行包"。
注意:本机只装了 GNU 工具链,MSVC 那个槽位的产物本身**没有在本机验证过**,由 CI 首跑验证。 注意:本机只装了 GNU 工具链,MSVC 那个槽位的产物本身**没有在本机验证过**,由 CI 首跑验证。
Linux 侧同一件事的对照(M82 实测:`readelf -d` + `ldd` + `nm -D`,容器内 Debian 12 glibc 2.36):Slint Linux 侧同一件事的对照(M82 实测:`readelf -d` + `ldd` + `nm -D`,容器内 Debian 12 glibc 2.36;被测的是 **Slint 版 GUI,历史实测**):Slint
GUI 的 ELF `DT_NEEDED` 只有 `libfontconfig.so.1` / `libgcc_s.so.1` / `libm.so.6` / `libc.so.6` / GUI 的 ELF `DT_NEEDED` 只有 `libfontconfig.so.1` / `libgcc_s.so.1` / `libm.so.6` / `libc.so.6` /
`ld-linux-x86-64.so.2`;`libX11` / `libxcb` / `libxkbcommon` / `libwayland-client` / `libGL` / `libEGL` `ld-linux-x86-64.so.2`;`libX11` / `libxcb` / `libxkbcommon` / `libwayland-client` / `libGL` / `libEGL`
**都不在**导入表里,而是以字符串形式由 `dlopen` 在开窗那一刻加载(`nm -D` 可见 `U dlopen@GLIBC_2.34` / **都不在**导入表里,而是以字符串形式由 `dlopen` 在开窗那一刻加载(`nm -D` 可见 `U dlopen@GLIBC_2.34` /
@@ -318,6 +316,7 @@ merge 后由 tower 重指 tag 重跑流水线,两槽位进 `SHA256SUMS.txt`
三份文本在 UNIX 归档内、在 Windows 侧作为并列 Release 资产。 三份文本在 UNIX 归档内、在 Windows 侧作为并列 Release 资产。
- Slint 的 RF 2.0 义务:关于页保留 `AboutSlint` 部件(已在 UI 内),不得移除; - Slint 的 RF 2.0 义务:关于页保留 `AboutSlint` 部件(已在 UI 内),不得移除;
选 RF 分支即不触发 GPL 分支义务(见 `deny.toml` 的许可证白名单注释)。 选 RF 分支即不触发 GPL 分支义务(见 `deny.toml` 的许可证白名单注释)。
- **M118 后这条义务只跟着已发布的 `v0.1.0` 资产走**:`crates/app` 已被 workspace `exclude`,发布矩阵里再没有链接 Slint 的二进制,新的 iced 版产物不链接 Slint;`deny.toml` 那条 RF-2.0 allow 因此暂时是死条目(不删,留给将来真有别的路径引入 Slint 时兜底)。
- `README.md`(英文主版)随包分发;中文版 `README.zh-CN.md` 留在仓库不进包(避免包体噪音)。 - `README.md`(英文主版)随包分发;中文版 `README.zh-CN.md` 留在仓库不进包(避免包体噪音)。
- 依赖许可门禁不变:`deny.toml` 白名单(Apache-2.0 / MIT / BSD / Zlib / BSL-1.0 / ISC / CC0 / Unlicense / - 依赖许可门禁不变:`deny.toml` 白名单(Apache-2.0 / MIT / BSD / Zlib / BSL-1.0 / ISC / CC0 / Unlicense /
Unicode-3.0 / CDLA-Permissive-2.0 / Slint-RF-2.0)。发布任务不新增任何依赖。 Unicode-3.0 / CDLA-Permissive-2.0 / Slint-RF-2.0)。发布任务不新增任何依赖。
@@ -384,8 +383,8 @@ experimental / not part of this release matrix)——改一边必须同步另
更不证明视觉正确**——视觉靠人眼看 artifact。 更不证明视觉正确**——视觉靠人眼看 artifact。
- 只覆盖 Linux x64;macOS 是 dispatch-only 探针(无实测证据),Windows 已有本机人眼验收。 - 只覆盖 Linux x64;macOS 是 dispatch-only 探针(无实测证据),Windows 已有本机人眼验收。
**本地复跑**:`bash packaging/linux-smoke-docker.sh . D:/tmp/ull-smoke`(输出目录别写 `/tmp`,Git Bash 下会 **本地复跑**:`bash packaging/linux-smoke-docker.sh . D:/tmp/ull-smoke`(输出目录别写 `/tmp`,Git Bash 下会静默落进 Docker VM;脚本内部用 `cygpath -m` 归一并回显真实路径)。**M118 提醒**:该脚本仍按 Slint 版 bin `unlucky-launcher` 编译与启动(`linux-smoke-docker.sh:81/83/88`),而 `crates/app` 已被 workspace `exclude`,**这条命令现在会失败**。
静默落进 Docker VM;脚本内部用 `cygpath -m` 归一并回显真实路径)。 在它把 bin 换成 `unlucky-launcher-iced` 之前,本地要复刻请直接照 `ci.yml` 的 runtime-smoke 三步手跑:`cargo build --bin unlucky-launcher-iced --locked` → `xvfb-run … --software-renderer --screenshot <png> --data-dir <dir>` → `python3 packaging/check_screenshot.py <png> --json`。
## 12. 文件清单 ## 12. 文件清单
+9 -3
View File
@@ -54,7 +54,13 @@ import tarfile
from pathlib import Path from pathlib import Path
from typing import NoReturn from typing import NoReturn
LAUNCHER_BIN = "unlucky-launcher" # M118(D26):GUI 的**构建产物**名是 iced 版的 `unlucky-launcher-iced`。旧值 `unlucky-launcher`
# 是 `crates/app`(Slint 退役树)的 bin —— 那棵树已被根 Cargo.toml `exclude`,再也不产出它了。
# **对外**文件名仍叫 `unlucky-launcher`:下载链接 / SHA256SUMS / README 都绑着这个名字,
# 改名是「翻牌」那一步的事,不是打包这一步的事。所以刻意拆成两个常量:
# 找文件用 LAUNCHER_BIN,落盘(tar 内文件名 / exe 资产名)用 LAUNCHER_ASSET。
LAUNCHER_BIN = "unlucky-launcher-iced"
LAUNCHER_ASSET = "unlucky-launcher"
CLI_BIN = "ull" CLI_BIN = "ull"
# 随包分发的合规文本(仓库根目录);英文主版 README 是对外分发口径。 # 随包分发的合规文本(仓库根目录);英文主版 README 是对外分发口径。
COMPLIANCE_DOCS = ("LICENSE", "NOTICE", "README.md") COMPLIANCE_DOCS = ("LICENSE", "NOTICE", "README.md")
@@ -216,7 +222,7 @@ def stage_archive(out_dir: Path, tag: str, version: str, launcher: Path) -> Path
if stage.exists(): if stage.exists():
shutil.rmtree(stage) shutil.rmtree(stage)
stage.mkdir(parents=True) stage.mkdir(parents=True)
shutil.copy2(launcher, stage / launcher.name) shutil.copy2(launcher, stage / LAUNCHER_ASSET)
for doc in COMPLIANCE_DOCS: for doc in COMPLIANCE_DOCS:
source = REPO_ROOT / doc source = REPO_ROOT / doc
if not source.is_file(): if not source.is_file():
@@ -232,7 +238,7 @@ def stage_archive(out_dir: Path, tag: str, version: str, launcher: Path) -> Path
info.mtime = 0 info.mtime = 0
info.uid = info.gid = 0 info.uid = info.gid = 0
info.uname = info.gname = "" info.uname = info.gname = ""
info.mode = 0o755 if entry.name == launcher.name else 0o644 info.mode = 0o755 if entry.name == LAUNCHER_ASSET else 0o644
with entry.open("rb") as handle: with entry.open("rb") as handle:
tar.addfile(info, handle) tar.addfile(info, handle)
shutil.rmtree(stage) shutil.rmtree(stage)
+16 -7
View File
@@ -1,6 +1,6 @@
# Unlucky Launcher {{VERSION}} # Unlucky Launcher {{VERSION}}
Rust-native **single-file** Minecraft launcher: Slint + Metro UI, built-in AI agent Rust-native **single-file** Minecraft launcher: iced 0.14 + Material 3 Expressive UI, built-in AI agent
(chat / config edits / crash diagnosis) and an MCP server other agent tools can attach to. (chat / config edits / crash diagnosis) and an MCP server other agent tools can attach to.
No WebView, no bundled runtime, no JRE download needed to start the launcher itself. No WebView, no bundled runtime, no JRE download needed to start the launcher itself.
@@ -75,11 +75,12 @@ chmod +x unlucky-launcher && ./unlucky-launcher
## No GPU? Software renderer ## No GPU? Software renderer
The UI prefers OpenGL (femtovg) and falls back to Slint's CPU renderer when no usable GL The UI renders through `wgpu` (GPU) and falls back to the `tiny-skia` CPU rasteriser when no usable
driver is present. Force it on any machine with: adapter is present. Force the CPU path on any machine with:
```bash ```bash
unlucky-launcher --software-renderer # or: SLINT_BACKEND=winit-software unlucky-launcher unlucky-launcher --software-renderer # the documented switch
unlucky-launcher --renderer=cpu # equivalent: pick the CPU renderer explicitly
``` ```
## Verify the download ## Verify the download
@@ -109,7 +110,15 @@ Get-FileHash .\unlucky-launcher-{{VERSION}}-windows-x64.exe -Algorithm SHA256
## License ## License
Apache-2.0. Third-party notices (including Slint's Royalty-Free 2.0 track) are in `NOTICE`. Apache-2.0. Third-party notices are in `NOTICE`. Unlucky Launcher is not affiliated with Mojang or
Unlucky Launcher is not affiliated with Mojang or Microsoft; it does not ship game files. Microsoft; it does not ship game files.
Made with [Slint](https://slint.dev) — Slint Royalty-Free 2.0 track, unmodified. ## UI framework in this build
Every asset in this release is built from the iced UI (`crates/app-iced`, iced 0.14, MIT) — no Slint
code is linked into it. The earlier Slint UI (`crates/app`) stays in the repository as historical
source only: excluded from the cargo workspace, not built, not part of any asset here and not part of
the release matrix. It did ship in the previous `v0.1.0` release; that release's Slint Royalty-Free
2.0 attribution remains where it was published, and no asset in this build carries that obligation.
There is deliberately no "Made with Slint" badge on this page: the obligation follows a Slint-linked
binary, and this release has none.