From 6e8031a9621ce2bda22b72538905ab545f135a99 Mon Sep 17 00:00:00 2001 From: Sebastian Herrlinger Date: Wed, 25 Mar 2026 20:30:15 +0100 Subject: [PATCH] refactor --- packages/opencode/src/cli/cmd/tui/app.tsx | 37 ++------------ .../opencode/src/cli/cmd/tui/plugin/api.tsx | 48 +++++++++++++++---- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 59d0120c19..2557d965ad 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -54,12 +54,10 @@ import { Provider } from "@/provider/provider" import { ArgsProvider, useArgs, type Args } from "./context/args" import open from "open" import { writeHeapSnapshot } from "v8" -import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2" import { PromptRefProvider, usePromptRef } from "./context/prompt" import { TuiConfigProvider, useTuiConfig } from "./context/tui-config" import { TuiConfig } from "@/config/tui" import { createTuiApi, TuiPluginRuntime, type RouteMap } from "./plugin" -import type { TuiPluginApi } from "@opencode-ai/plugin/tui" import { FormatError, FormatUnknownError } from "@/cli/error" async function getTerminalBackgroundColor(): Promise<"dark" | "light"> { @@ -262,30 +260,6 @@ function App(props: { onSnapshot?: () => Promise }) { const themeState = useTheme() const { theme, mode, setMode, locked, lock, unlock } = themeState const sync = useSync() - const map = new Map() - const root = "__local__" - const scoped = (workspaceID?: string) => { - const key = workspaceID ?? root - const hit = map.get(key) - if (hit) return hit - - const next = createOpencodeClient({ - baseUrl: sdk.url, - fetch: sdk.fetch, - directory: sync.data.path.directory || sdk.directory, - experimental_workspaceID: workspaceID, - }) - map.set(key, next) - return next - } - const workspace: TuiPluginApi["workspace"] = { - current() { - return sdk.workspaceID - }, - set(workspaceID) { - sdk.setWorkspace(workspaceID) - }, - } const exit = useExit() const promptRef = usePromptRef() const routes: RouteMap = new Map() @@ -294,9 +268,6 @@ function App(props: { onSnapshot?: () => Promise }) { routeRev() return routes.get(name)?.at(-1)?.render } - onCleanup(() => { - map.clear() - }) const api = createTuiApi({ command, @@ -307,15 +278,15 @@ function App(props: { onSnapshot?: () => Promise }) { route, routes, bump: () => setRouteRev((x) => x + 1), + sdk, sync, theme: themeState, toast, - client: () => sdk.client, - scopedClient: scoped, - workspace, - event: sdk.event, renderer, }) + onCleanup(() => { + api.dispose() + }) const [ready, setReady] = createSignal(false) TuiPluginRuntime.init(api) .catch((error) => { diff --git a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx index d3fed12c50..d23a9c97f7 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/api.tsx +++ b/packages/opencode/src/cli/cmd/tui/plugin/api.tsx @@ -3,6 +3,7 @@ import type { TuiDialogSelectOption, TuiPluginApi, TuiRouteDefinition } from "@o import type { useCommandDialog } from "@tui/component/dialog-command" import type { useKeybind } from "@tui/context/keybind" import type { useRoute } from "@tui/context/route" +import type { useSDK } from "@tui/context/sdk" import type { useSync } from "@tui/context/sync" import type { useTheme } from "@tui/context/theme" import { Dialog as DialogUI, type useDialog } from "@tui/ui/dialog" @@ -15,6 +16,7 @@ import { DialogPrompt } from "../ui/dialog-prompt" import { DialogSelect, type DialogSelectOption as SelectOption } from "../ui/dialog-select" import type { useToast } from "../ui/toast" import { Installation } from "@/installation" +import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2" type RouteEntry = { key: symbol @@ -32,16 +34,18 @@ type Input = { route: ReturnType routes: RouteMap bump: () => void + sdk: ReturnType sync: ReturnType theme: ReturnType toast: ReturnType - client: () => TuiPluginApi["client"] - scopedClient: TuiPluginApi["scopedClient"] - workspace: TuiPluginApi["workspace"] - event: TuiPluginApi["event"] renderer: TuiPluginApi["renderer"] } +type TuiHostPluginApi = TuiPluginApi & { + map: Map + dispose: () => void +} + function routeRegister(routes: RouteMap, list: TuiRouteDefinition[], bump: () => void) { const key = Symbol() for (const item of list) { @@ -200,7 +204,29 @@ function appApi(): TuiPluginApi["app"] { } } -export function createTuiApi(input: Input): TuiPluginApi { +export function createTuiApi(input: Input): TuiHostPluginApi { + const map = new Map() + const scoped: TuiPluginApi["scopedClient"] = (workspaceID) => { + const hit = map.get(workspaceID) + if (hit) return hit + + const next = createOpencodeClient({ + baseUrl: input.sdk.url, + fetch: input.sdk.fetch, + directory: input.sync.data.path.directory || input.sdk.directory, + experimental_workspaceID: workspaceID, + }) + map.set(workspaceID, next) + return next + } + const workspace: TuiPluginApi["workspace"] = { + current() { + return input.sdk.workspaceID + }, + set(workspaceID) { + input.sdk.setWorkspace(workspaceID) + }, + } const lifecycle: TuiPluginApi["lifecycle"] = { signal: new AbortController().signal, onDispose() { @@ -317,11 +343,11 @@ export function createTuiApi(input: Input): TuiPluginApi { }, state: stateApi(input.sync), get client() { - return input.client() + return input.sdk.client }, - scopedClient: input.scopedClient, - workspace: input.workspace, - event: input.event, + scopedClient: scoped, + workspace, + event: input.sdk.event, renderer: input.renderer, slots: { register() { @@ -352,5 +378,9 @@ export function createTuiApi(input: Input): TuiPluginApi { return input.theme.ready }, }, + map, + dispose() { + map.clear() + }, } }