拡張の作成
拡張は、通常の Go コンポーネント、状態を保持するネイティブコンポーネント、非同期ネイティブサービスを提供できます。Editor、Markdown、Terminal は任意のネイティブコンポーネント、Updater は任意のサービスです。すべて同じ公開登録インターフェースを使い、第三者の拡張も quickgui-host の変更を必要としません。
#拡張の種類を選ぶ
| 追加するもの | 配布するもの |
|---|---|
| コンポーネントまたはアプリケーションロジック | Go モジュール |
| ネイティブサービスまたはオプションのバックエンド | Go パッケージ、マニフェスト、ネイティブライブラリ |
| 状態を保持するネイティブ UI | コンポーネント SDK ライブラリと Go バインディング |
利用者はあなたの Go パッケージをインポートします。CLI はマニフェストを見つけ、対応するネイティブ成果物を同梱します。
#拡張プロジェクトを作成する
quickgui init-extension my-components --type go
quickgui init-extension my-service --type zig
quickgui init-extension my-service --type rustgo が既定で、再利用可能なコンポーネントの雛形を作ります。zig と rust は、型付き Go ラッパー、マニフェスト、npm 成果物パッケージ付きのネイティブサービスの雛形を作ります。どのテンプレートにも cmd/demo と quickgui.toml が含まれます:
cd my-service
bun run devGo モジュールには --module github.com/acme/my-service、成果物パッケージには --npm-package @acme/extension-my-service を使います。Zig テンプレートには Zig 0.16.x が必要です。このチェックアウトでは、CLI が公開されるまで bun packages/cli/src/cli.ts init-extension を実行します。
#コンポーネントの共有
独自モジュールからコンポーネント関数をエクスポートします。このパッケージにネイティブマニフェストは不要です。
package notice
import "github.com/egoist/quickgui/go/ui"
func Notice(message string, styles ...ui.StyleBuilder) *ui.Element {
return ui.Text(
message,
).
Padding(12).
BackgroundColor("#eff6ff").
TextColor("#1e40af").
BorderRadius(8).
Style(ui.Style().
Merge(styles...))
}Go のバージョンタグで公開します。利用者は go get で追加します。terminal や updater をインポートすると、それらのネイティブパッケージがアプリに入ります。オプションの連携は別パッケージに分けてください。
#ネイティブ拡張の構成
extensions/ の各組み込み拡張は Go モジュールと npm パッケージを兼ねます。Rust は src/、TypeScript バインディングは js/ に置きます。独自の複合パッケージにも同じ構成を使えます:
Cargo.toml
go.mod
badge.go
package.json
quickgui.extension.json
src/
lib.rs
js/
index.ts
lib/
darwin-arm64/libquickgui_acme_badge.dylibC、C++、Rust など C ABI を公開できる言語で実装できます。Rust のコンポーネントライブラリは、レンダラーや quickgui-host ではなく quickgui-extension-sdk に依存します。Component と ComponentFactory を実装すると、ComponentRuntime がライフサイクルの関数表を提供します。名前、コンポーネント名、プロパティ、リリースバージョンは拡張が定義します。
#依存関係の宣言
このマニフェストはインポートする Go パッケージのソースの隣に置きます:
{
"schema": 1,
"name": "acme-echo",
"abi": 1,
"package": "@acme/extension-echo",
"version": "1.0.0",
"library": "quickgui_acme_echo"
}名前は小文字で始まり、小文字・数字・ハイフンを最大 64 文字まで含めます。host は予約されています。library フィールドは quickgui_ に、ハイフンをアンダースコアに置き換えた名前を足したものです。
Go パッケージはその要求を記録し、型付きの呼び出しを公開します:
package echo
import (
"encoding/json"
"github.com/egoist/quickgui/go/host"
"github.com/egoist/quickgui/go/native"
)
func init() {
host.RequireExtension("acme-echo", "1.0.0")
}
func Send(message string, done func(string, error)) {
native.InvokeExtension("acme-echo", "echo", message, func(raw string, err error) {
var reply string
if err == nil {
err = json.Unmarshal([]byte(raw), &reply)
}
done(reply, err)
})
}操作は native.Run の準備ができたあとに呼び出してください。永続的なサービスでは native.OpenExtension(name, options, changed, ready) がセッションを返します。所有者が破棄されたら、UI ゴルーチン上で閉じてください。
CLI は go list -deps でマニフェストを見つけます。マニフェストと RequireExtension の両方が必要です。一方は同梱を、もう一方は読み込みを制御します。
#ネイティブインターフェースを実装する
名前、独立したバージョン、ABI バージョン 1、汎用関数表を持つディスクリプタを、C リンケージの quickgui_extension_v1 から返します。コンポーネント表は create、update、render、event、destroy、サービス表は invoke、shutdown です。
コンポーネントは不透明なインスタンスを保持し、SDK のプリミティブツリーを返します。安定したノードキーでフォーカス、選択、スクロールを保持し、仮想リストは表示行とオーバースキャンだけを要求します。入力は拡張に戻り、出力イベントは Go バインディングへ渡ります。インスタンスへの呼び出しはネイティブ UI スレッド上で直列に実行されます。重い処理はワーカーに移し、データが変わったときだけそのインスタンスを起こします。同じプロパティには変更なしを返し、破棄時に状態と wake ハンドルを解放してください。
Go ラッパーから任意の登録済みコンポーネントをマウントできます:
package badge
import (
"github.com/egoist/quickgui/go/host"
"github.com/egoist/quickgui/go/ui"
)
func init() { host.RequireExtension("acme-badge", "1.0.0") }
func View(label string) *ui.Element {
return ui.ExtensionComponent("acme-badge", "badge", map[string]any{
"label": label,
}, ui.Props{})
}独立してコンパイルされる C カウンター に完全なディスクリプタとライフサイクル実装があります。コンポーネントフレームは 16 MiB、フロントエンドのプロパティ JSON は 12 MiB が上限です。
サービスでは、借用したリクエストを保持する前にコピーし、すぐに戻って後から応答します。
| リクエスト | 拡張が行うこと |
|---|---|
echo のようなワンショットのメソッド | 1 回応答する |
start | セッションを開き、そのイベントシンクを保持する |
send のようなコマンド | そのセッションを見つけ、このコマンドに応答する |
stop | そのセッションをキャンセルする |
shutdown | すべてのセッションをキャンセルし、ブロックしない |
リクエストと応答のペイロードはそれぞれ 64 KiB までです。C の echo 実装 は、完結したワンショットの例です。
#自動登録
アプリは起動時に選ばれた各ライブラリを読み込み、名前、バージョン、ABI を確認します。同じ名前のバージョンが衝突すると失敗します。ライブラリイメージはプロセスの生存期間中ロードされたままです。shutdown 関数は残っているセッションをキャンセルしなければなりません。
#成果物のビルドとパッケージ化
サポートする各ホスト向けにネイティブライブラリをビルドし、lib/<platform>-<arch>/ に配置します。そのフォルダーを、マニフェストと同じ version の npm パッケージとして公開します。
{
"name": "@acme/extension-echo",
"version": "1.0.0",
"files": ["js", "lib", "quickgui.extension.json", "README.md"],
"exports": { ".": "./js/index.ts", "./package.json": "./package.json" }
}TypeScript バインディングを含む npm パッケージは js/index.ts を公開します。@quickgui/* ランタイムと Solid は peer dependencies にし、開発用コピーは devDependencies に置き、実行時の dependencies には含めません。README はビルド手順だけにし、利用ガイドはウェブサイトに置きます。
ヘルパーファイルはライブラリの隣に置きます。ベース名はマニフェストの resources オブジェクトに、darwin、linux、windows をキーにして宣言します。
#テストと配布
利用者はモジュールを go get し、quickgui dev を実行します。CLI は対応する npm パッケージを取得します。開発中は QUICKGUI_EXTENSION_DIR をローカルの成果物ディレクトリに設定することもできます。
インポートのないアプリがその拡張を同梱しないこと、インポートを追加すると含まれること、セッションを閉じるとイベントが止まることを確認してください。エンドユーザーが受け取るのはパッケージ済みアプリです。実行に Bun やネイティブのツールチェーンは不要です。
