本文へスキップ

拡張の作成

拡張は、通常の Solid コンポーネント、状態を保持するネイティブコンポーネント、非同期ネイティブサービスを提供できます。Editor、Markdown、Terminal は任意のコンポーネントパッケージ、Updater は任意のサービスです。第三者のパッケージも同じ SDK を使い、quickgui-host の変更は不要です。

#拡張の種類を選ぶ

再利用可能な Solid UI には通常の TypeScript パッケージを使います。Rust が状態や描画ロジックを所有する場合はネイティブコンポーネント、非同期処理にはネイティブサービスを使います。ネイティブライブラリはアプリの既存プロセスとレンダラーを使い、別のホストを同梱しません。

#拡張プロジェクトを作成する

quickgui init-extension my-components --type go
quickgui init-extension my-service --type rust

TypeScript アプリは、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-terminal
import { 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 を使います。安定したキーのプリミティブを返し、仮想リストは表示行だけを要求します。バックグラウンド処理が完了したら所有インスタンスだけを起こし、破棄時に状態と wake ハンドルを解放します。

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();

新しいプロジェクトで、パック済みの拡張に対してテストします。パッケージがないアプリにはライブラリがバンドルされないことを確認します。