编写扩展
扩展可以提供普通 Solid 组件、保留状态的原生组件,或异步原生服务。Editor、Markdown 和 Terminal 是可选组件包,Updater 是可选服务。第三方包使用同一套 SDK,无需修改 quickgui-host。
#选择扩展类型
可复用的 Solid 界面使用普通 TypeScript 包。状态或渲染逻辑属于 Rust 时使用原生组件,异步操作使用原生服务。原生库在应用现有进程内运行并复用现有渲染器,不会再打包一份宿主。
#创建扩展项目
quickgui init-extension my-components --type go
quickgui init-extension my-service --type rustTypeScript 应用使用与 Go 相同的原生包。--npm-package 设置产物包名称。
#共享组件
import { Button, type JSX } from "@quickgui/solid";
export function PrimaryButton(props: JSX.NativeProps) {
return (
<Button
{...props}
style={[props.style, { bg: "#2563eb", color: "#ffffff", roundedLg: true, p3: true }]}
/>
);
}发布该包,并把它添加为应用依赖。
#组织原生扩展
一个组合扩展可以同时是 Go 模块和 npm 包。Rust 放在 src/,TypeScript 放在 js/,构建产物放在 lib/<platform>-<arch>/。根目录的清单声明包名、独立版本、ABI 和库名称。参见 Go 扩展指南及内置的 extensions/ 包。
Cargo.toml
go.mod
badge.go
package.json
quickgui.extension.json
src/
lib.rs
js/
index.ts
lib/
darwin-arm64/libquickgui_acme_badge.dylib#声明依赖
bun add @quickgui/extension-terminalimport { defineConfig } from "@quickgui/cli";
export default defineConfig({
language: "typescript",
name: "Terminal App",
identifier: "com.example.terminal",
extensions: ["@quickgui/extension-terminal"],
});自定义扩展可以使用诸如 @acme/extension-echo 这样的作用域名称。
#实现原生接口
原生组件实现 create、update、render、event 和 destroy。Rust 作者使用 quickgui-extension-sdk 的 Component、ComponentFactory 和 ComponentRuntime,SDK 不依赖渲染器。返回稳定键的基础节点,只请求可见虚拟行,后台任务完成时只唤醒所属实例,销毁时释放状态和唤醒句柄。
TypeScript 绑定挂载已注册的组件并传入响应式属性:
import { ExtensionComponent } from "@quickgui/solid";
export function Badge(props: { label: string }) {
return ExtensionComponent({
package: "acme-badge",
component: "badge",
get properties() { return { label: props.label }; },
});
}onEvent(kind, value, event) 接收组件输出,类型化公共 API 由绑定负责。宿主不会解释包专有的属性。
原生服务则实现 invoke 和 shutdown。返回前复制要保留的借用输入,把事件限定在各自会话内,会话停止时释放工作线程。
#自动注册
CLI 会打包已声明的扩展。应用在启动时加载它们,因此组件可以在 app.whenReady() 之后调用它们。
#构建与打包产物
原生实现变化后重建原生库,再重建应用。清单中的资源会复制到库旁边。npm 包导出 js/index.ts。@quickgui/* 和 Solid 放在 peerDependencies,开发副本放在 devDependencies,不要再打包一份 QuickGUI 运行时。扩展 README 只保留构建说明,用户指南放在网站上。
#测试与分发
import { ExtensionSession, invokeExtension } from "@quickgui/native";
const result = await invokeExtension("acme-service", "lookup", { key: "example" });
const session = new ExtensionSession("acme-service", {}, (event) => console.log(event));
await session.ready;
await session.request("refresh", null);
await session.close();在全新项目中针对已打包的扩展进行测试。确认没有该包的应用不会捆绑该库。
