This commit is contained in:
Sebastian Herrlinger
2026-03-25 20:30:15 +01:00
parent 1e711cda3f
commit 6e8031a962
2 changed files with 43 additions and 42 deletions
+4 -33
View File
@@ -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<string[]> }) {
const themeState = useTheme()
const { theme, mode, setMode, locked, lock, unlock } = themeState
const sync = useSync()
const map = new Map<string, OpencodeClient>()
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<string[]> }) {
routeRev()
return routes.get(name)?.at(-1)?.render
}
onCleanup(() => {
map.clear()
})
const api = createTuiApi({
command,
@@ -307,15 +278,15 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
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) => {
@@ -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<typeof useRoute>
routes: RouteMap
bump: () => void
sdk: ReturnType<typeof useSDK>
sync: ReturnType<typeof useSync>
theme: ReturnType<typeof useTheme>
toast: ReturnType<typeof useToast>
client: () => TuiPluginApi["client"]
scopedClient: TuiPluginApi["scopedClient"]
workspace: TuiPluginApi["workspace"]
event: TuiPluginApi["event"]
renderer: TuiPluginApi["renderer"]
}
type TuiHostPluginApi = TuiPluginApi & {
map: Map<string | undefined, OpencodeClient>
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<string | undefined, OpencodeClient>()
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()
},
}
}