cleanup sdk provider

actual-tui-plugins
Sebastian Herrlinger 2026-03-04 23:08:06 +01:00
parent d3db93a0ff
commit 8743dddde6
4 changed files with 19 additions and 20 deletions

View File

@ -150,7 +150,7 @@ export function tui(input: {
}
const renderer = await createCliRenderer(rendererConfig(input.config))
const slots = TuiPlugin.slots(renderer)
TuiPlugin.initializeSlots(renderer)
await render(() => {
return (
@ -166,7 +166,6 @@ export function tui(input: {
<SDKProvider
url={input.url}
renderer={renderer}
slots={slots}
directory={input.directory}
fetch={input.fetch}
headers={input.headers}

View File

@ -1,6 +1,5 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2"
import type { CliRenderer } from "@opentui/core"
import type { TuiSlots } from "@opencode-ai/plugin/tui"
import { createSimpleContext } from "./helper"
import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { batch, onCleanup, onMount } from "solid-js"
@ -16,7 +15,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
init: (props: {
url: string
renderer: CliRenderer
slots: TuiSlots
directory?: string
fetch?: typeof fetch
headers?: RequestInit["headers"]
@ -47,7 +45,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
client: sdk,
event: emitter,
renderer: props.renderer,
slots: props.slots,
}).catch((error) => {
console.error("Failed to load TUI plugins", error)
})

View File

@ -18,6 +18,7 @@ import { resolvePluginTarget, uniqueModuleEntries } from "@/plugin/shared"
import { registerThemes } from "./context/theme"
type Slot = <K extends keyof TuiSlotMap>(props: { name: K } & TuiSlotMap[K]) => JSX.Element | null
type InitInput<Renderer> = Omit<TuiPluginInput<Renderer>, "slots">
function empty<K extends keyof TuiSlotMap>(_props: { name: K } & TuiSlotMap[K]) {
return null
@ -57,10 +58,15 @@ export namespace TuiPlugin {
const log = Log.create({ service: "tui.plugin" })
let loaded: Promise<void> | undefined
let view: Slot = empty
let api: TuiSlots = {
register() {
return () => {}
},
}
export const Slot: Slot = (props) => view(props)
export function slots(renderer: CliRenderer): TuiSlots {
export function initializeSlots(renderer: CliRenderer) {
const reg = createSolidSlotRegistry<TuiSlotMap, TuiSlotContext>(
renderer,
{},
@ -79,8 +85,7 @@ export namespace TuiPlugin {
const slot = createSlot<TuiSlotMap, TuiSlotContext>(reg)
view = (props) => slot(props)
return {
api = {
register(pluginSlot) {
if (!isTuiSlotPlugin(pluginSlot)) return () => {}
return reg.register(pluginSlot)
@ -88,14 +93,18 @@ export namespace TuiPlugin {
}
}
export async function init<Renderer>(input: TuiPluginInput<Renderer>) {
export async function init<Renderer>(input: InitInput<Renderer>) {
if (loaded) return loaded
loaded = load(input)
return loaded
}
async function load<Renderer>(input: TuiPluginInput<Renderer>) {
async function load<Renderer>(input: InitInput<Renderer>) {
const dir = process.cwd()
const ctx: TuiPluginInput<Renderer> = {
...input,
slots: api,
}
await Instance.provide({
directory: dir,
@ -133,11 +142,11 @@ export namespace TuiPlugin {
if (theme) registerThemes(theme)
const slotPlugin = getTuiSlotPlugin(entry)
if (slotPlugin) input.slots.register(slotPlugin)
if (slotPlugin) ctx.slots.register(slotPlugin)
const tuiPlugin = getTuiPlugin<Renderer>(entry)
if (!tuiPlugin) continue
await tuiPlugin(input, Config.pluginOptions(item))
await tuiPlugin(ctx, Config.pluginOptions(item))
}
}
},

View File

@ -2,7 +2,6 @@ import { expect, mock, spyOn, test } from "bun:test"
import fs from "fs/promises"
import path from "path"
import { pathToFileURL } from "url"
import type { TuiPluginInput } from "@opencode-ai/plugin/tui"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { tmpdir } from "../../fixture/fixture"
import { Log } from "../../../src/util/log"
@ -82,7 +81,7 @@ test("ignores function-only tui exports and loads object exports", async () => {
const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path)
try {
const input = {
await TuiPlugin.init({
client: createOpencodeClient({
baseUrl: "http://localhost:4096",
}),
@ -90,12 +89,7 @@ test("ignores function-only tui exports and loads object exports", async () => {
on: () => () => {},
},
renderer: {},
slots: {
register: () => () => {},
},
} satisfies TuiPluginInput<object>
await TuiPlugin.init(input)
})
expect(await fs.readFile(tmp.extra.objMarker, "utf8")).toBe("called")
await expect(fs.readFile(tmp.extra.fnMarker, "utf8")).rejects.toThrow()