Core 插槽
本页展示插件插槽的 Core 宿主 API。
当您希望外部模块在宿主定义的布局区域中渲染 BaseRenderable UI 而无需 fork 应用时,可以使用插槽。宿主保持对布局和插槽类型的控制;插件仅通过您暴露的 API 进行渲染。
如果您尚未阅读共享模型,请从插件插槽开始。
Core 提供的功能
在 createSlotRegistry 之上,Core 提供了:
createCoreSlotRegistry— 创建为BaseRenderable节点类型化的注册表registerCorePlugin— 使用 Core 特定的CorePlugin接口注册插件SlotRenderable— 将插槽挂载到可渲染树中的RenderableresolveCoreSlot— 无需挂载即可解析插槽条目@opentui/core/runtime-plugin-support/createRuntimePlugin— Bun 中外部插件/模块加载的运行时支持
Core 插槽渲染器同时接收宿主上下文和插槽数据:(ctx, data) => BaseRenderable。
createCoreSlotRegistry 接受与 createSlotRegistry 相同的 SlotRegistryOptions。
运行时加载的外部插件
如果您的应用在运行时从磁盘加载插件模块,请在应用入口中导入一次:
import "@opentui/core/runtime-plugin-support"
这会安装 @opentui/core 的 Bun 运行时支持以及默认的 Core 运行时入口点(@opentui/core/testing)。当插件导入第一方包的运行时模块时,请显式传递。
如果您需要额外的宿主解析模块,请在副作用导入加载之前配置运行时支持:
const { ensureRuntimePluginSupport } = await import("@opentui/core/runtime-plugin-support/configure")
ensureRuntimePluginSupport({
additional: {
"my-runtime-module": { marker: "ok" },
},
})
对于导入 @opentui/three 的插件,请在宿主应用中安装 @opentui/three 并传递其运行时模块映射:
import { runtimeModules as threeRuntimeModules } from "@opentui/three/runtime-modules"
import { ensureRuntimePluginSupport } from "@opentui/core/runtime-plugin-support/configure"
ensureRuntimePluginSupport({
additional: threeRuntimeModules,
})
副作用导入和可配置入口点安装相同的运行时插件。延迟添加会抛出清晰的错误,而不是被忽略。
CorePlugin 接口
| 字段 | 类型 | 必需 | 描述 |
|---|---|---|---|
id | string | 是 | 唯一标识符。重复的 id 会抛出错误。 |
order | number | 否 | 排序优先级(升序)。默认为 0。 |
setup | (ctx, renderer) => void | 否 | 注册时调用一次。如果抛出错误,插件将不会被注册。 |
dispose | () => void | 否 | 插件被注销或注册表被清除时调用。 |
slots | Partial<Record<SlotName, CoreSlotContribution>> | 是 | 每个值是一个渲染函数或托管插槽对象。 |
CoreSlotContribution 可以是普通渲染函数 (ctx, data) => BaseRenderable 或带有生命周期钩子的 CoreManagedSlot。
基本用法
SlotRenderable 扩展了 Renderable,因此可以像 BoxRenderable 一样添加到任何父节点中。它从注册表中解析插件,管理其生命周期,并将输出协调为其子节点。
import {
BoxRenderable,
createCliRenderer,
createCoreSlotRegistry,
registerCorePlugin,
SlotRenderable,
TextRenderable,
} from "@opentui/core"
type Slots = "statusbar"
type SlotData = { label: string }
const context = { appName: "core-app", version: "1.0.0" }
const renderer = await createCliRenderer()
const registry = createCoreSlotRegistry<Slots, typeof context, SlotData>(renderer, context)
const unregister = registerCorePlugin(registry, {
id: "clock-plugin",
order: 0,
slots: {
statusbar(_ctx, data) {
return new TextRenderable(renderer, {
id: "clock-status",
content: `clock: ${data.label}`,
})
},
},
})
const slot = new SlotRenderable(renderer, {
id: "statusbar-slot",
registry,
name: "statusbar",
data: { label: "ok" },
mode: "append",
width: "100%",
height: 3,
flexDirection: "row",
fallback: () =>
new TextRenderable(renderer, {
id: "statusbar-fallback",
content: "fallback",
}),
})
renderer.root.add(slot)
// 稍后
slot.mode = "replace"
slot.data = { label: "updated" }
slot.refresh()
slot.destroy()
registerCorePlugin 返回一个注销函数(() => void)。调用它会移除插件并调用其 dispose 钩子。
因为 SlotRenderable 扩展了 Renderable,所以它接受所有标准布局选项(width、height、flexDirection、padding 等)以及插槽特定的选项。
SlotRenderable 选项
| 选项 | 类型 | 必需 | 描述 |
|---|---|---|---|
id | string | 是 | 唯一的可渲染标识符(继承自 RenderableOptions) |
registry | CoreSlotRegistry | 是 | 从中读取插件的注册表 |
name | 插槽名称 | 是 | 要挂载的插槽 |
data | 对象 | 否 | 作为第二个参数传递给插件渲染器的插槽数据 |
mode | SlotMode | 否 | "append"(默认)、"replace" 或 "single_winner"。参见插槽模式。 |
fallback | BaseRenderable | BaseRenderable[] | () => ... | 否 | 回退节点或创建它们的工厂函数 |
pluginFailurePlaceholder | (failure, ctx) => BaseRenderable | BaseRenderable[] | undefined | 否 | 插件抛出错误时创建占位符 UI |
| …布局选项 | RenderableOptions | 否 | 标准布局属性:width、height、flexDirection、padding 等。 |
实例 API
| 成员 | 描述 |
|---|---|
mode | 获取器/设置器。更改模式会自动刷新插槽。 |
refresh() | 重新解析插件并协调已挂载的子节点。 |
destroy() | 继承自 Renderable。除了标准清理外,SlotRenderable 还会取消注册表订阅,调用活跃托管插槽的 onDeactivate,然后调用所有托管插槽的 onDispose。宿主拥有的节点(普通函数贡献)会被销毁;插件拥有的节点(托管插槽贡献)会被分离但留给插件在 onDispose 中清理。 |
resolveCoreSlot
无需挂载即可解析插槽条目。当您需要手动检查或渲染插件输出时非常有用。
import { createCliRenderer, createCoreSlotRegistry, resolveCoreSlot } from "@opentui/core"
const renderer = await createCliRenderer()
const registry = createCoreSlotRegistry<"statusbar">(renderer, {
appName: "core-app",
version: "1.0.0",
})
const entries = resolveCoreSlot(registry, "statusbar")
// Array<{ id: string, renderer: (ctx, data) => BaseRenderable }>
插件失败占位符
如果插件在插槽渲染期间抛出错误,您可以提供宿主控制的回退 UI:
const slot = new SlotRenderable(renderer, {
registry,
name: "statusbar",
fallback: () => new TextRenderable(renderer, { id: "fallback", content: "fallback" }),
pluginFailurePlaceholder(failure, ctx) {
return new TextRenderable(renderer, {
id: `error-${failure.pluginId}`,
content: `plugin error: ${failure.pluginId}`,
})
},
})
托管插槽贡献
Core 插槽贡献可以是普通函数或带有生命周期钩子的托管插槽对象:
registerCorePlugin(registry, {
id: "managed-plugin",
slots: {
statusbar: {
render(_ctx, data) {
return new TextRenderable(renderer, { id: "managed", content: "managed" })
},
onActivate(ctx) {
// 插件在此插槽中变为活跃状态(可见)
},
onDeactivate(ctx) {
// 插件不再活跃(例如模式切换为 single_winner 且此插件未获胜)
},
onDispose(ctx) {
// 插件在此插槽中被移除/释放
},
},
},
})
CoreManagedSlot 接口
| 字段 | 类型 | 必需 | 描述 |
|---|---|---|---|
render | (ctx, data) => BaseRenderable | 是 | 为此插槽创建可渲染节点 |
onActivate | (ctx) => void | 否 | 插件在插槽中变为活跃状态(可见)时调用 |
onDeactivate | (ctx) => void | 否 | 插件不再活跃时调用(例如失去 single_winner) |
onDispose | (ctx) => void | 否 | 插件被移除或插槽被销毁时调用 |
节点所有权
当插槽贡献是普通函数时,宿主拥有返回的节点。在停用或释放时,宿主会分离并销毁它们。
当插槽贡献是托管插槽对象时,插件拥有节点。在停用时,宿主会分离它们但_不会_销毁它们——如果插件再次变为活跃状态,可以重用它们。在释放时,会调用 onDispose,以便插件可以进行清理。
可观测性
registry.onPluginError((event) => {
console.error(event.pluginId, event.phase, event.source, event.error.message)
})
示例:packages/examples/src/core-plugin-slots-demo.ts