Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
Shoubhit Dash
51e4cabfda fix(tui): inline renderer error handling 2026-06-26 14:44:01 +05:30
LukeParkerDev
7409c954dc test(tui): inject renderer factory 2026-06-26 14:37:44 +10:00
LukeParkerDev
565285d951 fix(tui): preserve renderer initialization errors 2026-06-26 12:32:08 +10:00
2 changed files with 53 additions and 16 deletions

View file

@ -8,7 +8,7 @@ import { ClipboardProvider, useClipboard } from "./context/clipboard"
import { ExitProvider, useExit } from "./context/exit" import { ExitProvider, useExit } from "./context/exit"
import { EpilogueProvider } from "./context/epilogue" import { EpilogueProvider } from "./context/epilogue"
import * as Selection from "./util/selection" import * as Selection from "./util/selection"
import { createCliRenderer, MouseButton, type CliRenderer } from "@opentui/core" import { createCliRenderer, MouseButton } from "@opentui/core"
import { RouteProvider, useRoute } from "./context/route" import { RouteProvider, useRoute } from "./context/route"
import { import {
Switch, Switch,
@ -183,21 +183,23 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
const result = yield* Effect.scoped( const result = yield* Effect.scoped(
Effect.gen(function* () { Effect.gen(function* () {
const renderer = yield* Effect.acquireRelease( const renderer = yield* Effect.acquireRelease(
Effect.tryPromise(() => Effect.tryPromise({
createCliRenderer({ try: () =>
externalOutputMode: "passthrough", createCliRenderer({
targetFps: 60, externalOutputMode: "passthrough",
gatherStats: false, targetFps: 60,
exitOnCtrlC: false, gatherStats: false,
useKittyKeyboard: {}, exitOnCtrlC: false,
autoFocus: false, useKittyKeyboard: {},
openConsoleOnError: false, autoFocus: false,
useMouse: !Flag.OPENCODE_DISABLE_MOUSE && input.config.mouse, openConsoleOnError: false,
consoleOptions: { useMouse: !Flag.OPENCODE_DISABLE_MOUSE && input.config.mouse,
keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }], consoleOptions: {
}, keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }],
}), },
), }),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}),
(renderer) => (renderer) =>
Effect.sync(() => { Effect.sync(() => {
destroyRenderer(renderer) destroyRenderer(renderer)

View file

@ -0,0 +1,35 @@
import { expect, mock, test } from "bun:test"
import { Effect } from "effect"
import { Global } from "@opencode-ai/core/global"
import { createTuiResolvedConfig } from "./fixture/tui-runtime"
test("run preserves the original renderer initialization error message", async () => {
const message = 'Failed to open library "opentui.dll": error code 126'
const core = await import("@opentui/core")
mock.module("@opentui/core", () => ({
...core,
createCliRenderer: async () => {
throw new Error(message)
},
}))
try {
const { run } = await import("../src/app")
await expect(
Effect.runPromise(
run({
url: "http://test",
config: createTuiResolvedConfig({ plugin_enabled: {} }),
args: {},
pluginHost: {
async start() {},
async dispose() {},
},
}).pipe(Effect.provide(Global.defaultLayer)),
),
).rejects.toThrow(message)
} finally {
mock.restore()
}
})