Block Kit

本页内容

EmDash 的 Block Kit 允许沙箱插件将其管理界面描述为 JSON。宿主负责渲染这些块——插件提供的 JavaScript 永远不会在浏览器中执行。

工作原理

  1. 用户导航到插件的管理页面。
  2. 管理器向插件的管理路由发送 page_load 交互。
  3. 插件返回包含块数组的 BlockResponse
  4. 管理器使用 BlockRenderer 组件渲染这些块。
  5. 当用户交互时(点击按钮、提交表单),管理器将交互发送回插件。
  6. 插件返回新的块,循环重复。
import type { SandboxedPlugin } from "emdash/plugin";

interface BlockInteraction {
	type: "page_load" | "block_action" | "form_submit";
	page?: string;
	action_id?: string;
	values?: Record<string, unknown>;
}

export default {
	routes: {
		admin: {
			handler: async (routeCtx, ctx) => {
				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: [] };
			},
		},
	},
} satisfies SandboxedPlugin;

路由处理器接受两个参数:routeCtx(包含 inputrequestrequestMeta)和 ctxPluginContext)。satisfies SandboxedPlugin 会推断这两者。

块类型

类型描述
header大号粗体标题
section带有可选附属元素的文本
divider水平分隔线
fields双列标签/值网格
table带格式化、排序、分页的数据表格
actions水平排列的按钮和控件行
stats带趋势指示器的仪表板指标卡片
form带条件可见性和提交的输入字段
image带标题的块级图片
context小号柔和的帮助文本
columns2-3 列布局,带嵌套块
empty空状态占位符,包含图标、标题、描述、可选命令行和操作按钮
accordion包裹嵌套块的可折叠区域

元素类型

类型描述
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 交互式地构建和测试块布局。