EmDash の Block Kit を使用すると、サンドボックス化されたプラグインが管理 UI を JSON として記述できます。ホストがブロックをレンダリングし、プラグインが提供する JavaScript はブラウザで実行されることはありません。
仕組み
- ユーザーがプラグインの管理ページに移動します。
- 管理画面がプラグインの管理ルートに
page_loadインタラクションを送信します。 - プラグインがブロックの配列を含む
BlockResponseを返します。 - 管理画面が
BlockRendererコンポーネントを使用してブロックをレンダリングします。 - ユーザーが操作(ボタンのクリック、フォームの送信)すると、管理画面がインタラクションをプラグインに送り返します。
- プラグインが新しいブロックを返し、サイクルが繰り返されます。
import { definePlugin } from "emdash";
import type { PluginContext } from "emdash";
interface BlockInteraction {
type: "page_load" | "block_action" | "form_submit";
page?: string;
action_id?: string;
values?: Record<string, unknown>;
}
export default definePlugin({
routes: {
admin: {
handler: async (routeCtx, ctx: PluginContext) => {
const interaction = routeCtx.input as BlockInteraction;
if (interaction.type === "page_load") {
return {
blocks: [
{ type: "header", text: "My Plugin Settings" },
{
type: "form",
block_id: "settings",
fields: [
{ type: "text_input", action_id: "api_url", label: "API URL" },
{ type: "toggle", action_id: "enabled", label: "Enabled", initial_value: true },
],
submit: { label: "Save", action_id: "save" },
},
],
};
}
if (interaction.type === "form_submit" && interaction.action_id === "save") {
await ctx.kv.set("settings", interaction.values);
return {
blocks: [/* ... updated blocks ... */],
toast: { message: "Settings saved", type: "success" },
};
}
return { blocks: [] };
},
},
},
});
標準形式のルートハンドラーは 2 つの引数を取ります:routeCtx(input、request、requestMeta を含む)と ctx(PluginContext)。
ブロックタイプ
| Type | Description |
|---|---|
header | 大きな太字の見出し |
section | オプションのアクセサリ要素を持つテキスト |
divider | 水平線 |
fields | 2 列のラベル/値グリッド |
table | フォーマット、ソート、ページネーション付きのデータテーブル |
actions | ボタンとコントロールの水平行 |
stats | トレンドインジケーター付きのダッシュボードメトリックカード |
form | 条件付き表示と送信を持つ入力フィールド |
image | キャプション付きのブロックレベル画像 |
context | 小さなミュートされたヘルプテキスト |
columns | ネストされたブロックを持つ 2〜3 列レイアウト |
empty | アイコン、タイトル、説明、オプションのコマンドライン、アクションボタンを持つ空状態プレースホルダー |
accordion | ネストされたブロックをラップする折りたたみ可能なセクション |
要素タイプ
| Type | Description |
|---|---|
button | オプションの確認ダイアログ付きアクションボタン |
text_input | 単一行または複数行のテキスト入力 |
number_input | 最小値/最大値付きの数値入力 |
select | ドロップダウン選択 |
toggle | オン/オフスイッチ |
secret_input | API キーとトークン用のマスクされた入力 |
ビルダーヘルパー
@emdash-cms/blocks パッケージは、よりクリーンなコードのためのビルダーヘルパーをエクスポートします:
import { blocks, elements } from "@emdash-cms/blocks";
const { header, form, section, stats } = blocks;
const { textInput, toggle, select, button } = elements;
return {
blocks: [
header("SEO Settings"),
form({
blockId: "settings",
fields: [
textInput("site_title", "Site Title", { initialValue: "My Site" }),
toggle("generate_sitemap", "Generate Sitemap", { initialValue: true }),
select("robots", "Default Robots", [
{ label: "Index, Follow", value: "index,follow" },
{ label: "No Index", value: "noindex,follow" },
]),
],
submit: { label: "Save", actionId: "save" },
}),
],
};
条件付きフィールド
フォームフィールドは、他のフィールドの値に基づいて条件付きで表示できます:
{
"type": "toggle",
"action_id": "auth_enabled",
"label": "Enable Authentication"
}
{
"type": "secret_input",
"action_id": "api_key",
"label": "API Key",
"condition": { "field": "auth_enabled", "eq": true }
}
api_key フィールドは auth_enabled がオンになっている場合にのみ表示されます。条件はラウンドトリップなしでクライアント側で評価されます。
試してみる
Block Playground を使用して、ブロックレイアウトをインタラクティブに構築およびテストします。