orbit

Composable desktop application foundations for MoonBit.

desktop
webview
ipc
moonbit
moon add Nanaloveyuki/orbit@0.1.0-alpha.6
Download zip
Version
0.1.0-alpha.6
License
Apache-2.0
Last updated
2 days ago
Downloads
15
README

#Orbit

validation npm

Orbit 是一个用 MoonBit 构建的原生桌面应用框架。它以 Orby 管理窗口和事件循环, 以 MoonView 嵌入系统 WebView,并在网页前端与 MoonBit 后端之间提供受能力策略约束的 IPC。应用可以使用原生 HTML/CSS/JavaScript,也可以接入 React、Vue 等 Vite 前端。

当前发布版本为 0.1.0-alpha.6。Windows x64 是当前唯一的一等支持目标;Linux 保持持续构建、打包验证的实验性支持。API 与配置仍可能在正式版前调整。

#已实现

  • 多窗口桌面生命周期,以及每个窗口独立的嵌入资源根。
  • 构建时嵌入 Web 资源,运行时不依赖源码目录中的前端文件。
  • 同步与异步 MoonBit 命令、超时、取消、结构化错误和 256 KiB 消息限制。
  • 以窗口、远端页面、HTTP 客户端、插件和后台任务为主体的 allow/deny 能力策略。
  • 可选的、默认关闭的认证 HTTP IPC 适配器。
  • HTTPS 远端页面、精确 origin 白名单和嵌入式失败回退页。
  • 原生插件 ABI v1/v2、sidecar schema、权限声明和可观察的安全关闭。
  • Vite 开发/生产流程,以及 JavaScript IPC bindings 生成。
  • 可选、内存限定的桌面生命周期诊断记录与 JSON 环境检查。
  • 图标生成、可校验目录包、Windows NSIS 安装包、Linux tar.gz、deb、rpm 和 Arch 包。

#五分钟运行

先安装 MoonBit 工具链 和本机原生编译工具链, 然后执行:

git clone https://github.com/Nanaloveyuki/orbit.git cd orbit moon update moon run orbit-example

运行后会打开一个由 Orby 创建、MoonView 渲染的原生窗口。页面按钮调用 example.ping,MoonBit 后端返回 JSON,完整路径可在 orbit-example 中查看。

修改 orbit-example/assets 后,先重新生成嵌入资源:

moon run --target native orbit-build orbit-example/orbit.conf.json orbit-example/generated_page.mbt moon run orbit-example

#平台前置

平台开发依赖当前状态
Windows x64MSVC/Windows SDK;首次 native build 会自动下载并校验 WebView2 SDK主要开发平台,CI 与安装包流程已验证
Linux x64C 编译器、GTK3、WebKitGTK 4.1 开发包Ubuntu/Fedora/Arch 构建与打包流程已验证
macOS-Orbit 顶层窗口宿主尚未实现
Android/OpenHarmony-相关底层库有独立探索,当前不是 Orbit 应用目标平台

Windows 的 WebView2 SDK 默认缓存到 %LOCALAPPDATA%\moonview\webview2\1.0.4078.44;通常不需要手工配置 SDK。 目标机器使用系统 Evergreen WebView2 Runtime,安装包可按配置下载或携带安装程序。

Ubuntu/Debian 开发机可安装:

sudo apt-get install libgtk-3-dev libwebkit2gtk-4.1-dev

#在应用中使用

新应用可以直接由 npm CLI 创建:

npx @nanaloveyuki/orbit-cli@alpha init my-orbit-app \ --name "My Orbit App" \ --identifier com.example.my-orbit-app \ --module example/my-orbit-app cd my-orbit-app moon update npm install npm run orbit:run

init 原子创建一个不会覆盖现有目录的 vanilla 应用,包含 MoonBit native 入口、schema v2 配置、受 capability 保护的 IPC 示例、前端资源和 npm scripts。第一次运行会生成 generated_page.mbt;建议将该文件提交,以便审查嵌入资源与权限变化。

向现有模块添加 Orbit:

moon add Nanaloveyuki/orbit@0.1.0-alpha.6 npm install --save-dev @nanaloveyuki/orbit-cli@alpha npx orbit generate npx orbit run

CLI 会优先使用工作区内的 orbit-build,其次使用 Mooncakes 已物化的生成器;首次构建 需要时会按 moon.mod 的固定版本 fetch 到项目级 .repos--orbit-build 仅用于显式 覆盖。

MoonBit 后端注册命令,配置文件决定哪个页面可以调用它:

let registry = @ipc.CommandRegistry::new()
registry.register_json(@ipc.CommandName::new("example.ping"), _payload => {
Ok({ "message": "IPC round trip completed." })
})

const response = await window.__ORBIT__.invoke("example.ping", { value: 1 }, { timeout: 5000, });

生成的 orbit-bindings.mjs 也可以为当前页面实际获授权的命令提供固定入口。完整安装、 moon.pkg、应用入口和生成步骤见入门指南

#原生文件选择

Windows 本地窗口可以在 capability 明确授予后调用内建的 orbit.dialog.openorbit.dialog.open_multipleorbit.dialog.save orbit.dialog.pick_directory。结果只包含不透明 handle、显示名称和项目类型,绝不包含 本机路径;HTTP、插件和远端页面不能使用这些命令。

文件 picker 与 orbit.fs.* 位于独立的 orbit-desktop-file 扩展包。应用必须显式创建并 注入该扩展;未注入时,即使 IPC policy 授予了对应命令,也不会注册任何文件命令:

let file_extension = @desktop_file.DesktopFileExtension::new()
let options = @core.DesktopOptions::new(
windows~,
ipc_registry=Some(registry),
ipc_policy=Some(policy),
extensions=[file_extension.as_extension()],
)

同样可显式授予 orbit.window.print。它只接受 {},只为调用它的本地窗口打开当前 WebView 文档的原生打印对话框,并返回 { "opened": true };远端、HTTP、插件和后台 principal 都不能调用它。

#轻量运行模式

关闭窗口时,应用可保留 Orby 顶层窗口和托盘,但释放该窗口的嵌入 WebView。关闭最后一个 WebView 后,MoonView 会释放 Windows 上对应的 WebView2 controller 与 environment;Linux 会销毁 GTK WebView child,但不承诺回收 WebKitGTK 的全部进程级缓存。

将 close policy 返回为 Suspend,或在托盘回调中调用 DesktopController::suspend_window(label)。之后 show_window(label) 会在原窗口中创建一个 新的 WebView,并按原始 RuntimeOptions 重新加载页面:

let options = @core.DesktopOptions::new(
windows~,
close_request_handler=Some((_label, _controller) => {
@core.DesktopCloseAction::suspend()
}),
runtime_suspend_handler=Some(label => {
// Persist application-owned state previously received through IPC.
save_window_state(label)
}),
)

runtime_suspend_handler 在 UI thread 上、运行时释放之前调用,返回 Ok(()) 才会继续释放 runtime;返回 Err(reason) 会保留当前窗口与 WebView,并由 suspend_window 返回 RuntimeOperationFailed。Orbit 不会同步导出任意 DOM 状态;应用应使用正常 IPC 将主要状态 保存到 MoonBit,或使用持久化的浏览器存储。挂起期间 UI-bound extension command 不可用, orbit-desktop-file 会撤销该窗口的文件 capability。

{ "identifier": "main-file-dialogs", "effect": "allow", "principals": [{ "kind": "window", "identifier": "main" }], "scopes": [], "commands": ["orbit.dialog.open", "orbit.dialog.save"] }

const selected = await window.__ORBIT__.invoke("orbit.dialog.open", { title: "Open document", filters: [{ name: "Text", extensions: ["txt", "md"] }] }); if (!selected.cancelled) console.log(selected.files);

@core.run_async 启动的应用中,显式授予 orbit.fs.read_binaryorbit.fs.read_text 后,页面可继续将同一 handle 传给读取命令。读取命令只接受 { "id": "..." },单次至多 64 KiB;binary 结果使用 base64,text 结果会验证 UTF-8。路径、目录句柄和保存句柄都不能用于读取。

orbit.fs.write_text 只接受保存 picker 返回的 Write handle 和 UTF-8 text;它在 同目录中完成写入和替换,成功只返回字节数。读取句柄、目录句柄和任意原生路径不能写入。

目录 picker 返回的 Directory handle 可传给 orbit.fs.read_directory。Orbit 会在 picker 返回时取得目录的原生 no-follow handle;枚举最多 128 个非隐藏直接子项,不接受 路径参数。每个条目为 { "name", "kind", "id" }:能够安全地相对父句柄打开的普通 文件会有 Read id,可传给 orbit.fs.read_textorbit.fs.read_binary;子目录有新的 Directory id,可再次传给同一命令。symlink、Windows junction 和其他 reparse point 不会被枚举或跟随。 同一父目录的同名子 capability 会复用;每个窗口最多保留 256 个目录派生的原生 capability(文件和目录合计),达到上限时条目仍可显示但不会携带新的 id

目录 capability 支持受限枚举、安全子目录导航和目录派生普通文件的只读访问;写入、创建 和删除仍不提供。

可选 bridge 的所有文件操作都只接受这些 handle;当前版本不会向页面提供任意路径文件系统 API。

#React、Vue 与 Vite

Orbit 不绑定前端框架。只要前端能够输出静态目录,就可以嵌入可执行文件;开发模式由 CLI 启动明确配置的 Vite 命令并等待 dev_url

{ "build": { "vite": { "dev_command": "npm run dev", "dev_url": "http://127.0.0.1:5173", "build_command": "npm run build", "dist_dir": "dist" } } }

npx orbit dev npx orbit build

CLI 不猜测 React、Vue、包管理器或输出目录;所有命令都来自 orbit.conf.json diagnose --json 输出单个版本化 JSON 文档,用于采集本机 WebView 与编译环境状态;完整 字段与应用内生命周期历史见诊断

#CLI 与发布产物

@nanaloveyuki/orbit-cli 是零运行时依赖的 Node.js 20+ 工具,覆盖以下常用流程:

npx orbit init --help npx orbit diagnose --json npx orbit bindings npx orbit icon --source assets/icon-1024.png --out-dir icons npx orbit package --release --out-dir dist npx orbit verify-package --package-dir dist

Windows 可以从已校验的目录包生成 NSIS 安装程序;Linux 可以生成可移植 archive 或 调用发行版原生工具生成 deb、rpm、Arch 包。生产产物要求外部签名命令,本地测试必须 显式使用 --allow-unsigned。详见打包指南

#生态组成

项目职责
Orbit on Mooncakes框架、构建器、运行时适配和 IPC 包
Orbit CLI on npm生成、开发、构建、图标和分发命令
Orby原生窗口与宿主事件循环
MoonView系统 WebView 嵌入层
orbit-plugin-abi稳定的 C 插件 ABI 与异步 executor
Ajni通用 JNI 与 Android JNI 基础设施
sync原生线程同步原语
dynlib动态库加载
image图标解码、缩放和多格式输出
Parsec严格 JSON 解析等解析基础设施
moonbitlang/async官方结构化异步运行时

Orbit 参考了 Tauri 的分层经验,但不是 Tauri API 的 MoonBit 移植。窗口、WebView、IPC、 插件与打包边界均按 MoonBit 当前语言能力和原生生态重新设计。

#文档

#许可证

Orbit 使用 Apache License 2.0