tui: move server to worker for better isolation and lifecycle management

This commit is contained in:
Dax Raad 2025-10-01 23:55:12 -04:00
parent 1190fe3711
commit 7b1554d337
5 changed files with 51 additions and 19 deletions

View file

@ -1,11 +1,14 @@
import { createContext, Show, useContext, type ParentProps } from "solid-js"
export function createSimpleContext<T>(input: { name: string; init: () => T }) {
export function createSimpleContext<T, Props extends Record<string, any>>(input: {
name: string
init: ((input: Props) => T) | (() => T)
}) {
const ctx = createContext<T>()
return {
provider: (props: ParentProps) => {
const init = input.init()
provider: (props: ParentProps<Props>) => {
const init = input.init(props)
return (
// @ts-expect-error
<Show when={init.ready === undefined || init.ready === true}>

View file

@ -62,11 +62,6 @@ export const { use: useKeybind, provider: KeybindProvider } = createSimpleContex
leader(false)
})
}
if (result.match("app_exit", evt)) {
await Instance.disposeAll()
renderer.destroy()
}
})
const result = {

View file

@ -1,17 +1,11 @@
import { createOpencodeClient } from "@opencode-ai/sdk"
import { Server } from "@/server/server"
import { createSimpleContext } from "./helper"
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
name: "SDK",
init: () => {
init: (props: { url: string }) => {
const client = createOpencodeClient({
baseUrl: "http://localhost:4096",
// @ts-ignore
fetch: async (r) => {
// @ts-ignore
return Server.App().fetch(r)
},
baseUrl: props.url,
})
return client
},

View file

@ -72,18 +72,34 @@ export const TuiCommand = cmd({
directory: process.cwd(),
fn: () => Config.get(),
})
const worker = new Worker(import.meta.resolve("./worker.ts"))
worker.onerror = console.log
worker.onmessageerror = console.log
const url = await new Promise<string>((resolve) => {
worker.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === "ready") {
resolve(data.url)
}
}
})
await render(
() => {
return (
<RouteProvider>
<SDKProvider>
<SDKProvider url={url}>
<SyncProvider>
<LocalProvider>
<KeybindProvider>
<DialogProvider>
<CommandProvider>
<PromptHistoryProvider>
<App />
<App
onExit={() => {
worker.terminate()
}}
/>
</PromptHistoryProvider>
</CommandProvider>
</DialogProvider>
@ -104,7 +120,7 @@ export const TuiCommand = cmd({
},
})
function App() {
function App(props: { onExit: () => void }) {
const route = useRoute()
const dimensions = useTerminalDimensions()
const renderer = useRenderer()
@ -131,6 +147,11 @@ function App() {
renderer.console.toggle()
return
}
if (keybind.match("app_exit", evt)) {
await Instance.disposeAll()
renderer.destroy()
props.onExit()
}
})
createEffect(() => {

View file

@ -0,0 +1,19 @@
import { Installation } from "@/installation"
import { Server } from "@/server/server"
import { Log } from "@/util/log"
await Log.init({
print: process.argv.includes("--print-logs"),
dev: Installation.isDev(),
level: (() => {
if (Installation.isDev()) return "DEBUG"
return "INFO"
})(),
})
const server = Server.listen({
port: 4096,
hostname: "127.0.0.1",
})
postMessage(JSON.stringify({ type: "ready", url: server.url }))