@multi-indiegame/akashic-serve-extension-dock
v1.0.0
Published
akashic serve の画面で、Akashic 拡張ライブラリの操作盤を 1 か所に並べるためのドック
Maintainers
Readme
@multi-indiegame/akashic-serve-extension-dock
akashic serve の画面に、Akashic 拡張ライブラリの操作盤を 1 か所にまとめて 出すためのドック。
拡張ライブラリを作る人向け。コンテンツにバンドルされるものではなく、sandbox.config.js の client.external から読ませる akashic serve 用バックエンドの中で使う。
ねらい
akashic serve は sandbox.config.js の client.external という拡張の口を用意しており、拡張ライブラリは自身の裁量で画面に手を入れることができる。一方で、どこに何を出すかの取り決めは特に無い。
必要なときだけ出して消える UI(akashic serve 自体が出すユーザー名提供の許可ダイアログのようなもの)なら、取り決めが無くても置き場所に迷うことはない。一方、ゲーム開発者がいつでも操作できる UI を出したい拡張ライブラリ(たとえば akashic-player-ban の「ゲーム外から操作」)の場合、画面のどこに置くか、他の拡張ライブラリと場所がぶつからないか、不要なときにどう隠すかを、ライブラリごとに一から決めることになる。
そこで、常時出す UI の置き場をドックとして用意する。各ライブラリは「タブとその中身」を用意する。画面のどこに出すか、他のライブラリとぶつからないか、開閉や on/off をどうするかはドックが引き受ける。ライブラリ間で表示場所を調整し合わなくて済み、ゲーム開発者から見ても操作盤が 1 か所にまとまる。
インストール
npm install -D @multi-indiegame/akashic-serve-extension-dockdevDependency でよい。 実行時に解決される依存ではなく、拡張ライブラリ側が esbuild などで自分の plugin.js に畳み込む前提のパッケージだから(互換性の約束)。
使い方
import {
getDock,
create,
button,
} from "@multi-indiegame/akashic-serve-extension-dock";
const handle = getDock().register({
id: "player-ban", // 並び順を決める。パッケージ名の末尾を使う
label: "ゲーム外から操作", // 縦タブに出る
heading: "ゲーム外から操作(実行基盤の代役)", // パネルの見出し
build: () => buildPanelBody(), // 中身。開くたびに呼ばれる
onClose: () => releaseNodes(), // DOM 参照を捨てる
showTab: tabEnabled(), // false でもプログラムからは開ける
});
handle.open();
handle.toggle();id は 1 つのページに載る他のライブラリと重複しない名前にする(例: パッケージ名)。重複したときの挙動は互換性の約束を参照。
見た目を揃えるため、パネルの中身も create() と button() で組むこと。
互換性の約束
このドックを保守・拡張するときの注意点。
akashic serve はプラグインを「module と exports しか無いスコープ」で評価する。require() が無いので、プラグインは依存を持たない 1 ファイルでなければならない。つまりこのドックは拡張ライブラリにバンドルされて配られるものであり、1 つのページにドックの実装が複数コピー載る。どの版がいくつ載るかは、そのページに載ったライブラリ群がいつビルドされたか次第で、こちらからは決められない。
そのときの動きは次の通り。
getDock()はwindow.akashicServeExtensionDockを見て、先に登録されたドック実装をそのまま返す。実際に動くのは先着 1 つだけで、後から読み込まれたコピーのドック実装は使われずに捨てられる- 捨てられるのはドック実装だけ。後から来たコピーが呼ぶ
register()は、生き残っている先着のドックに届いてパネルとして出る DOCK_VERSIONは見ない。registerを持ってさえいれば使う
なので保守するときは次を守る。
- 新旧どの版のドック実装が採用されても動くようにする。 v1 のドック実装が生き残ったページで最新版のライブラリが
register()を呼ぶこともあれば、その逆もある Dock/DockPanelSpec/DockHandleに破壊的変更を入れない。 省略可能なフィールドを足すのは安全(古いドック実装は知らないフィールドを無視するだけ)。既存フィールドの意味を変える、省略可能だったものを必須にする、名前を変える、削る、はすべて古いドック実装が生き残ったページで壊れるGLOBAL_KEYを変えない。 変えると、旧キーを見るコピーと新キーを見るコピーがそれぞれドックを立て、ページ上にドックが 2 つ並ぶ- 同じ
idの二重登録では、先に登録されたパネルのDockHandleを返す(後勝ちにしない)。 同じライブラリの別版が 2 つ載ると起きる。後勝ちにすると、そのパネルが開いている最中に中身を作った spec と閉じるときにonCloseが呼ばれる spec が別物になり、画面に出ている DOM と後片付けが食い違う
URL クエリ
?akashicServeDock=0(=false も可)が指定されるとドックはすべてのタブを生成しない。
ライブラリごとにタブを出し分けたい場合は、設定値を showTab に渡す。クエリの命名、値の取得処理ははライブラリ側で実装すること。
const tabEnabled = () =>
new URLSearchParams(location.search).get("playerBan") !== "0";
getDock().register({ /* ... */, showTab: tabEnabled() });パネル本体はライブラリ側で handle.open() を呼べば表示される。
拡張ライブラリへの組み込み方
akashic serve 用のプラグインは依存を持たない 1 ファイルでなければならないので、このドックは拡張ライブラリ側のビルドで plugin.js へ畳み込む。
- その拡張ライブラリの devDependencies に
@multi-indiegame/akashic-serve-extension-dockとesbuildを足す akashic serve用のソースをsrc/plugin.tsに置き、その中でgetDock().register(...)を呼ぶ- esbuild で
src/plugin.tsをplugin.jsへバンドルする(format: "iife"、charset: "utf8"、module.exportsへの橋渡しは footer で) package.jsonのexportsに".": "./plugin.js"を置き、ゲーム開発者がsandbox.config.jsからrequire.resolve()で参照できるようにする
@multi-indiegame/akashic-player-ban-serve が上記に沿った例。参考までに。
開発
npm install
npm run build # lib/ に .js と .d.ts を出す
npm run format配るのはコンパイル済みの lib/ だけ。利用側の tsconfig でソースを型検査させないため、src/ は publish に含めない。
ライセンス
MIT
