Core 插槽

本页展示插件插槽的 Core 宿主 API。

当您希望外部模块在宿主定义的布局区域中渲染 BaseRenderable UI 而无需 fork 应用时,可以使用插槽。宿主保持对布局和插槽类型的控制;插件仅通过您暴露的 API 进行渲染。

如果您尚未阅读共享模型,请从插件插槽开始。

Core 提供的功能

createSlotRegistry 之上,Core 提供了:

  • createCoreSlotRegistry — 创建为 BaseRenderable 节点类型化的注册表
  • registerCorePlugin — 使用 Core 特定的 CorePlugin 接口注册插件
  • SlotRenderable — 将插槽挂载到可渲染树中的 Renderable
  • resolveCoreSlot — 无需挂载即可解析插槽条目
  • @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 接口

字段类型必需描述
idstring唯一标识符。重复的 id 会抛出错误。
ordernumber排序优先级(升序)。默认为 0
setup(ctx, renderer) => void注册时调用一次。如果抛出错误,插件将不会被注册。
dispose() => void插件被注销或注册表被清除时调用。
slotsPartial<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,所以它接受所有标准布局选项(widthheightflexDirectionpadding 等)以及插槽特定的选项。

SlotRenderable 选项

选项类型必需描述
idstring唯一的可渲染标识符(继承自 RenderableOptions
registryCoreSlotRegistry从中读取插件的注册表
name插槽名称要挂载的插槽
data对象作为第二个参数传递给插件渲染器的插槽数据
modeSlotMode"append"(默认)、"replace""single_winner"。参见插槽模式
fallbackBaseRenderable | BaseRenderable[] | () => ...回退节点或创建它们的工厂函数
pluginFailurePlaceholder(failure, ctx) => BaseRenderable | BaseRenderable[] | undefined插件抛出错误时创建占位符 UI
…布局选项RenderableOptions标准布局属性:widthheightflexDirectionpadding 等。

实例 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