diff --git a/packages/cli/src/commands/handlers/default.ts b/packages/cli/src/commands/handlers/default.ts index bb03b6b472..8ecc1b891f 100644 --- a/packages/cli/src/commands/handlers/default.ts +++ b/packages/cli/src/commands/handlers/default.ts @@ -2,9 +2,9 @@ import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { Global } from "@opencode-ai/core/global" import { run } from "@opencode-ai/tui" import { loadBuiltinPlugins } from "@opencode-ai/tui/builtins" -import { TuiConfig } from "@opencode-ai/tui/config" import { Commands } from "../commands" import { Runtime } from "../../framework/runtime" +import { TuiConfig } from "../../tui-config" import { Effect, Option } from "effect" import { Server } from "../../services/server" import { Updater } from "../../services/updater" @@ -34,7 +34,7 @@ export default Runtime.handler(Commands, (input) => ), ) yield* Effect.promise(() => preflight.finish()) - const config = TuiConfig.resolve({}, { terminalSuspend: false }) + const config = yield* TuiConfig.load() let disposeSlots: (() => void) | undefined const runFork = Effect.runForkWith(yield* Effect.context()) yield* run({ diff --git a/packages/cli/src/tui-config.ts b/packages/cli/src/tui-config.ts new file mode 100644 index 0000000000..48c43bf1d3 --- /dev/null +++ b/packages/cli/src/tui-config.ts @@ -0,0 +1,24 @@ +export * as TuiConfig from "./tui-config" + +import { Global } from "@opencode-ai/core/global" +import { TuiConfig } from "@opencode-ai/tui/config" +import { Effect, FileSystem, Option, Schema } from "effect" +import { parse, type ParseError } from "jsonc-parser" +import path from "path" + +export const load = Effect.fn("TuiConfig.load")(function* () { + const fs = yield* FileSystem.FileSystem + const global = yield* Global.Service + const filepath = path.join(global.config, "tui.json") + const text = yield* fs.readFileString(filepath).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (!text) return TuiConfig.resolve({}, { terminalSuspend: process.platform !== "win32" }) + + const errors: ParseError[] = [] + const input: unknown = parse(text, errors, { allowTrailingComma: true }) + if (errors.length) return TuiConfig.resolve({}, { terminalSuspend: process.platform !== "win32" }) + + return TuiConfig.resolve( + Option.getOrElse(Schema.decodeUnknownOption(TuiConfig.Info)(input), () => ({})), + { terminalSuspend: process.platform !== "win32" }, + ) +}) diff --git a/packages/cli/test/tui-config.test.ts b/packages/cli/test/tui-config.test.ts new file mode 100644 index 0000000000..a9656c4fae --- /dev/null +++ b/packages/cli/test/tui-config.test.ts @@ -0,0 +1,25 @@ +import { NodeFileSystem } from "@effect/platform-node" +import { Global } from "@opencode-ai/core/global" +import { Effect } from "effect" +import { expect, test } from "bun:test" +import path from "path" +import { TuiConfig } from "../src/tui-config" + +test("loads the global tui config", async () => { + const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) + await Bun.write(path.join(directory, "tui.json"), JSON.stringify({ keybinds: { leader: "ctrl+o" } })) + + try { + const config = await Effect.runPromise( + TuiConfig.load().pipe( + Effect.provide(Global.layerWith({ config: directory })), + Effect.provide(NodeFileSystem.layer), + ), + ) + + expect(config.keybinds.get("leader")?.[0]?.key).toBe("ctrl+o") + expect(config.keybinds.get("session.new")?.[0]?.key).toBe("n") + } finally { + await Bun.$`rm -rf ${directory}` + } +})