diff --git a/.opencode/plugins/tui-smoke.tsx b/.opencode/plugins/tui-smoke.tsx index e3e5a159e1..9bf17e5235 100644 --- a/.opencode/plugins/tui-smoke.tsx +++ b/.opencode/plugins/tui-smoke.tsx @@ -51,25 +51,6 @@ const cfg = (options: Record | undefined) => { } } -const boot = (meta?: TuiPluginInit) => { - if (!meta) { - return { - state: "unknown", - first: false, - updated: false, - count: 0, - source: "n/a", - } - } - return { - state: meta.state, - first: meta.first, - updated: meta.updated, - count: meta.entry.load_count, - source: meta.entry.source, - } -} - const names = (input: ReturnType) => { return { modal: `${input.route}.modal`, @@ -358,7 +339,7 @@ const Screen = (props: { input: ReturnType route: ReturnType keys: Keys - meta: ReturnType + meta: TuiPluginInit params?: Record }) => { const dim = useTerminalDimensions() @@ -549,9 +530,9 @@ const Screen = (props: { plugin state: {props.meta.state} first: {props.meta.first ? "yes" : "no"} · updated: {props.meta.updated ? "yes" : "no"} · loads:{" "} - {props.meta.count} + {props.meta.entry.load_count} - plugin source: {props.meta.source} + plugin source: {props.meta.entry.source} source: {value.source} note: {value.note || "(none)"} selected: {value.selected || "(none)"} @@ -875,24 +856,23 @@ const reg = (api: TuiApi, input: ReturnType, keys: Keys) => { ]) } -const tui = async (input: TuiPluginInput, options?: Record, meta?: TuiPluginInit) => { +const tui = async (input: TuiPluginInput, options: Record | null, meta: TuiPluginInit) => { if (options?.enabled === false) return await input.api.theme.install("./smoke-theme.json") input.api.theme.set("smoke-theme") - const value = cfg(options) + const value = cfg(options ?? undefined) const route = names(value) const keys = input.api.keybind.create(bind, value.keybinds) const fx = new VignetteEffect(value.vignette) - const info = boot(meta) input.renderer.addPostProcessFn(fx.apply.bind(fx)) input.api.route.register([ { name: route.screen, render: ({ params }) => ( - + ), }, { diff --git a/packages/opencode/src/cli/cmd/tui/plugin.ts b/packages/opencode/src/cli/cmd/tui/plugin.ts index 622f883ef3..b44272431f 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin.ts @@ -129,15 +129,6 @@ function makeInstallFn(meta: TuiConfig.PluginMeta, root: string): TuiTheme["inst } } -function initData(meta: { state: PluginMeta.State; entry: PluginMeta.Entry }): TuiPluginInit { - return { - state: meta.state, - first: meta.state === "new", - updated: meta.state === "changed", - entry: meta.entry, - } -} - export namespace TuiPlugin { const log = Log.create({ service: "tui.plugin" }) let loaded: Promise | undefined @@ -225,6 +216,31 @@ export namespace TuiPlugin { }) } + const now = Date.now() + const init: TuiPluginInit = meta + ? { + state: meta.state, + first: meta.state === "new", + updated: meta.state === "changed", + entry: meta.entry, + } + : { + state: "new", + first: true, + updated: false, + entry: { + name: spec, + source: spec.startsWith("file://") ? "file" : "npm", + spec, + target, + first_time: now, + last_time: now, + time_changed: now, + load_count: 1, + fingerprint: target, + }, + } + const root = pluginRoot(spec, target) const install = makeInstallFn(getPluginMeta(config, item), root) const mod = await import(target).catch((error) => { @@ -238,7 +254,7 @@ export namespace TuiPlugin { spec, mod, install, - init: meta ? initData(meta) : undefined, + init, } } @@ -293,7 +309,7 @@ export namespace TuiPlugin { }), }, }, - Config.pluginOptions(load.item), + Config.pluginOptions(load.item) ?? null, load.init, ) } diff --git a/packages/opencode/test/cli/tui/plugin-loader.test.ts b/packages/opencode/test/cli/tui/plugin-loader.test.ts index 81f842a854..2b6b26912f 100644 --- a/packages/opencode/test/cli/tui/plugin-loader.test.ts +++ b/packages/opencode/test/cli/tui/plugin-loader.test.ts @@ -28,6 +28,7 @@ mock.module("@opentui/solid/jsx-runtime", () => ({ })) const { allThemes, addTheme } = await import("../../../src/cli/cmd/tui/context/theme") const { TuiPlugin } = await import("../../../src/cli/cmd/tui/plugin") +const { PluginMeta } = await import("../../../src/plugin/meta") async function waitForLog(text: string, timeout = 1000) { const start = Date.now() @@ -281,6 +282,12 @@ export const object_plugin = { }) process.env.OPENCODE_PLUGIN_META_FILE = path.join(tmp.path, "plugin-meta.json") if (!process.env.OPENCODE_PLUGIN_META_FILE) throw new Error("missing meta file") + await PluginMeta.touch(tmp.extra.localSpec, tmp.extra.localSpec) + await PluginMeta.touch(tmp.extra.globalSpec, tmp.extra.globalSpec) + await PluginMeta.persist() + await Bun.sleep(20) + const text = await Bun.file(tmp.extra.globalPluginPath).text() + await Bun.write(tmp.extra.globalPluginPath, `${text}\n`) const cwd = spyOn(process, "cwd").mockImplementation(() => tmp.path) let selected = "opencode" @@ -410,21 +417,21 @@ export const object_plugin = { expect(local.depth_after).toBe(1) expect(local.open_after).toBe(true) expect(local.open_clear).toBe(false) - expect(local.init_state).toBe("new") - expect(local.init_first).toBe(true) + expect(local.init_state).toBe("same") + expect(local.init_first).toBe(false) expect(local.init_updated).toBe(false) expect(local.init_source).toBe("file") - expect(local.init_load_count).toBe(1) + expect(local.init_load_count).toBe(2) const global = JSON.parse(await fs.readFile(tmp.extra.globalMarker, "utf8")) expect(global.has).toBe(true) expect(global.set_installed).toBe(true) expect(global.selected).toBe(tmp.extra.globalThemeName) - expect(global.init_state).toBe("new") - expect(global.init_first).toBe(true) - expect(global.init_updated).toBe(false) + expect(global.init_state).toBe("changed") + expect(global.init_first).toBe(false) + expect(global.init_updated).toBe(true) expect(global.init_source).toBe("file") - expect(global.init_load_count).toBe(1) + expect(global.init_load_count).toBe(2) const preloaded = JSON.parse(await fs.readFile(tmp.extra.preloadedMarker, "utf8")) expect(preloaded.before).toBe(true) @@ -470,9 +477,12 @@ export const object_plugin = { const meta = JSON.parse(await fs.readFile(path.join(tmp.path, "plugin-meta.json"), "utf8")) as Record< string, - { spec: string; load_count: number } + { name: string; load_count: number } > - expect(Object.keys(meta).length > 0).toBe(true) + const rows = Object.values(meta) + expect(rows.find((item) => item.name === "local-plugin")?.load_count).toBe(2) + expect(rows.find((item) => item.name === "global-plugin")?.load_count).toBe(2) + expect(rows.find((item) => item.name === "preloaded-plugin")?.load_count).toBe(1) } finally { cwd.mockRestore() if (backup === undefined) { diff --git a/packages/opencode/test/cli/tui/theme-store.test.ts b/packages/opencode/test/cli/tui/theme-store.test.ts index 64d9b196c7..baa64e16c6 100644 --- a/packages/opencode/test/cli/tui/theme-store.test.ts +++ b/packages/opencode/test/cli/tui/theme-store.test.ts @@ -6,12 +6,6 @@ mock.module("@opentui/solid/jsx-runtime", () => ({ jsxs: () => null, jsxDEV: () => null, })) -mock.module("@opentui/solid", () => ({ - useRenderer: () => ({ - getPalette: async () => ({ palette: [] as string[] }), - clearPaletteCache: () => {}, - }), -})) const { DEFAULT_THEMES, allThemes, addTheme, hasTheme } = await import("../../../src/cli/cmd/tui/context/theme") diff --git a/packages/plugin/src/tui.ts b/packages/plugin/src/tui.ts index 080bc0e645..0bf74fdcd0 100644 --- a/packages/plugin/src/tui.ts +++ b/packages/plugin/src/tui.ts @@ -223,8 +223,8 @@ export type TuiPluginInput = { export type TuiPlugin = ( input: TuiPluginInput, - options?: PluginOptions, - init?: TuiPluginInit, + options: PluginOptions | null, + init: TuiPluginInit, ) => Promise export type TuiPluginModule = {