【免费下载链接】craft-launcher
The Craft App Launcher
ArtCraft Launcher 是一款用 Rust 编写的 Craft App Suite 启动器,负责安装、更新和打开 PhotoCraft、LightCraft、FilmCraft 等 12 款创意应用。它的 Web 版把同一套界面代码编译成 WebAssembly(WASM)直接跑在浏览器里:展示全套应用目录、自动识别访客的操作系统,并给出对应的下载链接。这篇文章带你完整了解 Web 版能做什么、代码是怎么分层的,以及如何构建部署。
Web 版能做什么、不能做什么 🌐
浏览器不能操作本地文件系统,所以 Web 版无法安装应用。它把自己定位成一个"目录 + 下载中心":
| 功能 | 桌面版 | Web 版 |
|---|---|---|
| 展示 12 款应用目录与最新版本 | ✅ | ✅ |
| 识别访客系统,给出对应下载包 | ✅ | ✅ |
| 一键安装 / 更新 / 启动 / 卸载 | ✅ | ❌(改为"下载"按钮 + GitHub 链接) |
| 系统托盘、登录项、通知 | ✅ | ❌ |
| 多语言界面(含简体中文) | ✅ | ✅(跟随浏览器语言) |
| 记住你的设置 | ✅(本地文件) | ✅(浏览器 localStorage) |
具体来看,Web 版打开后会自动完成这几件事:
- 拉取最新 release:逐个访问 GitHub REST API(
api.github.com允许跨域请求),得到每款应用的最新版本号、发布时间和全部下载文件——不需要自己的后端服务器; - 识别访客平台:解析浏览器 User-Agent,判断是 Windows / macOS / Linux / FreeBSD,以及 x64 / ARM64 / RISC-V 架构,据此为每个应用挑出正确的下载包(
.msi、.dmg、.AppImage……); - 匹配界面语言:优先读取地址栏里的
?lang=ru参数,其次读浏览器的偏好语言,加载提示文字也会用对应语言显示; - 记住设置:语言、视图方式等设置通过 eframe 的持久化机制存入浏览器 localStorage,下次打开自动恢复。
💡 一个小细节:桌面版会用系统自带的 CJK 字体渲染中文、日文等文字,而 Web 版没有这个条件——当系统无法提供字体时,界面会退回英文,并在设置里说明原因。
界面可以在"网格(Grid)"和"表格(Table)"两种布局间切换,支持排序与搜索,这些交互逻辑在两个平台完全一致——因为它们本就是同一份 UI 代码。
同一套代码的关键:三层 Crate 架构
整个项目是一个 Cargo 工作区(见 Cargo.toml),按职责拆成三层,这是"一套代码两端跑"能成立的根本原因:
| 层 | 位置 | 内容 | 能否编译到 WASM |
|---|---|---|---|
| core | crates/core/ | 纯数据与逻辑:应用目录、版本比较、GitHub release 解析、按平台挑选下载包 | ✅ |
| ui-egui | crates/ui-egui/ | 共享界面:网格/表格视图、设置、主题、多语言 | ✅ |
| native | crates/native/ | 桌面专属:下载、安装、托盘、自更新 | ❌ |
| artcraft-launcher-web | apps/artcraft-launcher-web/ | Web 壳:WASM 入口 + 浏览器后端,仅约 260 行 | ✅ |
crates/core/src/lib.rs 开头就说明了设计意图:core 里没有任何 I/O,所以桌面版、Web 版和测试代码可以原样共享它。比如"从一堆下载文件里挑出最适合当前平台的包"这个逻辑,两端都用同一个函数pick_asset(crates/core/src/platform.rs)。
UI 层与后端之间只通过一条清晰的协议对话,定义在 crates/core/src/protocol.rs:
- UI → 后端:发送
Command(检查更新、安装、启动、配置……); - 后端 → UI:每帧拉取一次
Event(最新 release、进度、完成/失败)。
桌面后端在独立线程里干活,Web 后端在 async 任务里干活,两边都不阻塞渲染帧。
还有一个巧妙的"能力开关"——Capabilities结构体。每个后端通过它告诉 UI 自己能干什么:
- 桌面版声明
manage_apps: true,按钮就是"安装 / 更新 / 打开"; - Web 版声明
manage_apps: false(见 apps/artcraft-launcher-web/src/web.rs),同一份 UI 代码就会自动把主按钮换成"下载",并链接到对应的 GitHub 下载文件。
UI 因此可以保持"薄薄一层":只负责画卡片、读状态、发命令,完全不关心背后是操作系统还是浏览器。
Web 壳的实现:fetch、UA 与 localStorage
Web 壳的入口是 apps/artcraft-launcher-web/src/main.rs,通过eframe::WebRunner把 egui 界面渲染到页面里的<canvas>上(index.html 就只有一个画布和一个加载提示)。真正有浏览器味道的代码都集中在 apps/artcraft-launcher-web/src/web.rs:
查最新版本:用浏览器的fetch直接向 GitHub API 发 CORS 请求。对每个应用请求releases/latest,把 JSON 交给 core 里现成的解析函数。遇到 403/429(限流)时会读取x-ratelimit-reset响应头,把恢复时间告诉界面;没登录的访客共享的是"每 IP 每小时 60 次请求"的额度,所以界面会把限流情况说清楚,而不是干转圈。
猜访客平台:visitor_platform函数(apps/artcraft-launcher-web/src/web.rs)检查 User-Agent 中的 "windows"、"mac os"、"linux" 等关键字来确定系统,再用 "aarch64" / "arm64" / "riscv" 确定架构。
语言检测:detect_language函数(apps/artcraft-launcher-web/src/web.rs)在首帧之前执行,把?lang=xx参数和navigator.languages一起交给 i18n 模块(实现见 crates/ui-egui/src/i18n/detect.rs),并同步更新页面的<html lang>属性和加载文字。
设置持久化:eframe 在 Web 上自动使用 localStorage,所以"保存设置"只需一行eframe::set_value,和桌面版共用同一个Settings结构体(crates/ui-egui/src/settings.rs)。
如何构建并部署 Web 版 🛠️
本地构建只需两条命令:
cargo install trunk --locked # 安装 WASM 构建工具 trunk rustup target add wasm32-unknown-unknown # 添加 WASM 目标 cd apps/artcraft-launcher-web && trunk serve # 本地运行于 127.0.0.1:8766Trunk.toml 指定了两个关键设置:输出目录为../../dist/web,public_url = "./"让所有资源都走相对路径——这保证了站点无论是挂在域名根目录、子路径还是 CDN 桶里都能正常工作。
apps/artcraft-launcher-web/Cargo.toml 里还有个值得注意的注释:渲染走 wgpu,浏览器里是WebGPU 优先、WebGL2 兜底,所以无需担心老显卡。工作区 Cargo.toml 里的wasm-releaseprofile 开启了opt-level = "s"(按体积优化)+fatLTO,尽量压缩最终的.wasm文件。
产物是什么?一个自包含的纯静态站点(packaging/web/README.md 有完整说明):
| 文件 | 说明 |
|---|---|
index.html | 页面本身,全部使用相对 URL 加载资源 |
artcraft-launcher-web-<hash>.js | wasm-bindgen 生成的胶水代码(ES module) |
artcraft-launcher-web-<hash>_bg.wasm | 应用主体(egui 界面 + 业务逻辑) |
_headers/.htaccess | Netlify / Cloudflare Pages / Apache 的示例配置 |
没有服务端代码,也没有一行手写的 JavaScript——把文件夹上传到任何能托管静态文件的地方即可。官方构建脚本 packaging/web/package.sh 还会做两道具保险:发现根绝对路径 URL 直接报错(防止子路径部署翻车),WASM 超过 24 MiB 也报错(Cloudflare 单文件上限 25 MiB)。
部署时真正需要关心的是这几项(详见 packaging/web/README.md):
.wasm的 MIME 类型必须是application/wasm,否则浏览器拒绝流式编译;- 对
.wasm/.js/.html开启 gzip 或 Brotli 压缩; - 带内容哈希的
.wasm和.js可以设置"一年 + immutable"缓存,只有index.html需要no-cache; - 应用不使用
SharedArrayBuffer,因此不需要 COOP/COEP 那套隔离头。
想把它嵌进别的页面?一个iframe即可(沙箱化 iframe 至少需要allow-scripts allow-same-origin allow-popups,其中allow-popups是为了让下载链接能打开)。本地想先验证一下,在站点目录里跑python3 -m http.server 8765然后打开http://localhost:8765/就够了。
如何验证 WASM 构建没问题
仓库的 CI 任务把 WASM 兼容性也纳入了日常检查(xtask/src/main.rs):cargo xtask wasm会对"必须能跑在浏览器里"的 crate(core 与 web 应用)执行cargo check --target wasm32-unknown-unknown,cargo xtask ci则一次跑完格式检查、clippy、全部测试和 WASM 检查。这样任何误引入"只有原生平台才有"的依赖都会立刻被发现,而不是等到上传部署时才爆炸。
小结
ArtCraft Launcher Web 版的做法可以概括为三句话:
- 纯逻辑下沉:目录、版本、release 解析全部放进无 I/O 的 core 层,天然可编译为 WASM;
- 协议隔离:UI 只认
Command/Event协议和Capabilities开关,桌面后端与 Web 后端各写各的,UI 一行不改; - 静态站点交付:trunk 构建出的 HTML + JS + WASM 三件套,扔到任何静态托管上即可运行。
对想给 Rust/egui 项目加"浏览器版"的开发者来说,这套分层值得直接抄作业:先想清楚哪部分代码不碰 I/O,再把平台差异全部塞进 trait 的另一侧。
【免费下载链接】craft-launcher
The Craft App Launcher
相关推荐
rust-libp2p 浏览器 WebRTC 示例:用 wasm-pack 将 libp2p-webrtc-websys 跑进浏览器并 Ping 原生服务器
rust libp2p 浏览器 WebRTC 示例:用 wasm pack 将 libp2p webrtc websys 跑进浏览器并 Ping 原生服务器 e
网络通信后端Panda CSS compiler-wasm:让 Rust 编译引擎跑进浏览器与 Playground 的 WebAssembly 绑定解析
Panda CSS compiler wasm:让 Rust 编译引擎跑进浏览器与 Playground 的 WebAssembly 绑定解析 @pandacs
前端构建工具开发工具在浏览器中为 Rust 代码计时:wasm-bindgen 的 web-sys `performance.now` 实战示例
在浏览器中为 Rust 代码计时:wasm bindgen 的 web sys performance.now 实战示例 导读 本文以 wasm bindgen
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考