diff --git a/packages/opencode/src/cli/cmd/opentui/component/prompt.tsx b/packages/opencode/src/cli/cmd/opentui/component/prompt.tsx index f25b6bbbd..2a558a337 100644 --- a/packages/opencode/src/cli/cmd/opentui/component/prompt.tsx +++ b/packages/opencode/src/cli/cmd/opentui/component/prompt.tsx @@ -11,7 +11,6 @@ import { useSync } from "../context/sync" import { Identifier } from "../../../../id/id" import { createStore, produce } from "solid-js/store" import type { FilePart } from "@opencode-ai/sdk" -import { Instance } from "../../../../project/instance" import fuzzysort from "fuzzysort" export type PromptProps = { @@ -295,7 +294,7 @@ function Autocomplete(props: { type: "file", mime: "text/plain", filename: file, - url: `file://${Instance.directory}/${file}`, + url: `file://${process.cwd()}/${file}`, source: { type: "file", text: { diff --git a/packages/opencode/src/cli/cmd/opentui/context/sync.tsx b/packages/opencode/src/cli/cmd/opentui/context/sync.tsx index 9705b6fc4..2c71a52c8 100644 --- a/packages/opencode/src/cli/cmd/opentui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/opentui/context/sync.tsx @@ -1,7 +1,7 @@ import type { Message, Agent, Provider, Session, Part, Config, Todo } from "@opencode-ai/sdk" import { createStore, produce, reconcile } from "solid-js/store" import { useSDK } from "./sdk" -import { createContext, Show, useContext, type ParentProps } from "solid-js" +import { createContext, onCleanup, Show, useContext, type ParentProps } from "solid-js" import { Binary } from "../../../../util/binary" function init() { diff --git a/packages/opencode/src/cli/cmd/opentui/opentui.tsx b/packages/opencode/src/cli/cmd/opentui/opentui.tsx index ef3db6881..9155fe664 100644 --- a/packages/opencode/src/cli/cmd/opentui/opentui.tsx +++ b/packages/opencode/src/cli/cmd/opentui/opentui.tsx @@ -1,5 +1,5 @@ import { cmd } from "../cmd" -import { render, useKeyHandler, useRenderer, useTerminalDimensions } from "@opentui/solid" +import { render, useKeyboard, useKeyHandler, useRenderer, useTerminalDimensions } from "@opentui/solid" import { TextAttributes } from "@opentui/core" import { RouteProvider, useRoute } from "./context/route" import { Home } from "./home" @@ -8,21 +8,31 @@ import { Theme } from "./context/theme" import { Installation } from "../../../installation" import { Global } from "../../../global" import { DialogProvider, useDialog } from "./ui/dialog" -import { bootstrap } from "../../bootstrap" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" import { LocalProvider, useLocal } from "./context/local" import { DialogModel } from "./component/dialog-model" import { DialogCommand } from "./component/dialog-command" import { Session } from "./session" +import { Instance } from "../../../project/instance" +import { EventLoop } from "../../../util/eventloop" export const OpentuiCommand = cmd({ command: "opentui", describe: "print hello", handler: async () => { - await bootstrap(process.cwd(), async () => { - await render( - () => ( + await render( + () => { + const renderer = useRenderer() + useKeyboard(async (evt) => { + if (evt.name === "c" && evt.ctrl) { + await Instance.disposeAll() + renderer.destroy() + await EventLoop.wait() + process.exit(0) + } + }) + return ( @@ -34,15 +44,14 @@ export const OpentuiCommand = cmd({ - ), - { - targetFps: 60, - gatherStats: false, - useAlternateScreen: false, - }, - ) - console.log("done") - }) + ) + }, + { + targetFps: 60, + gatherStats: false, + exitOnCtrlC: false, + }, + ) }, }) diff --git a/packages/opencode/src/cli/cmd/opentui/session.tsx b/packages/opencode/src/cli/cmd/opentui/session.tsx index a7f686864..c2f614ded 100644 --- a/packages/opencode/src/cli/cmd/opentui/session.tsx +++ b/packages/opencode/src/cli/cmd/opentui/session.tsx @@ -16,7 +16,6 @@ import type { ReadTool } from "../../../tool/read" import type { WriteTool } from "../../../tool/write" import { BashTool } from "../../../tool/bash" import type { GlobTool } from "../../../tool/glob" -import { Instance } from "../../../project/instance" import { TodoWriteTool } from "../../../tool/todo" import type { GrepTool } from "../../../tool/grep" import type { ListTool } from "../../../tool/ls" @@ -71,7 +70,7 @@ export function Session() { (scroll = r)} + ref={(r) => (scroll = r)} scrollbarOptions={{ visible: false }} stickyScroll={true} stickyStart="bottom" @@ -525,7 +524,7 @@ ToolRegistry.register({ function normalizePath(input: string) { if (path.isAbsolute(input)) { - return path.relative(Instance.directory, input) || "." + return path.relative(process.cwd(), input) || "." } return input } diff --git a/packages/opencode/src/project/instance.ts b/packages/opencode/src/project/instance.ts index 01ea87a3c..5a3adbd4d 100644 --- a/packages/opencode/src/project/instance.ts +++ b/packages/opencode/src/project/instance.ts @@ -44,4 +44,12 @@ export const Instance = { async dispose() { await State.dispose(Instance.directory) }, + async disposeAll() { + for (const [key, value] of cache) { + context.provide(value, async () => { + process.stdout.write(`disposing ${key}...`) + await Instance.dispose() + }) + } + }, } diff --git a/packages/opencode/src/util/eventloop.ts b/packages/opencode/src/util/eventloop.ts new file mode 100644 index 000000000..87f6eef41 --- /dev/null +++ b/packages/opencode/src/util/eventloop.ts @@ -0,0 +1,20 @@ +import { Log } from "./log" + +export namespace EventLoop { + export async function wait() { + return new Promise((resolve) => { + const check = () => { + const active = [...(process as any)._getActiveHandles(), ...(process as any)._getActiveRequests()] + Log.Default.info("eventloop", { + active, + }) + if ((process as any)._getActiveHandles().length === 0 && (process as any)._getActiveRequests().length === 0) { + resolve() + } else { + setImmediate(check) + } + } + check() + }) + } +} diff --git a/packages/opencode/src/util/signal.ts b/packages/opencode/src/util/signal.ts new file mode 100644 index 000000000..bc633ecc6 --- /dev/null +++ b/packages/opencode/src/util/signal.ts @@ -0,0 +1,12 @@ +export function signal() { + let resolve: any + const promise = new Promise((r) => (resolve = r)) + return { + trigger() { + return resolve() + }, + wait() { + return promise + }, + } +}