From e54af329f038c07fd992c489896db1d863da4e38 Mon Sep 17 00:00:00 2001 From: awardhuang Date: Wed, 14 Jan 2026 12:13:23 +0800 Subject: [PATCH] fix(opencode): disable mouse tracking on exit --- packages/opencode/src/cli/cmd/tui/app.tsx | 32 ++++++++++------- .../opencode/src/cli/cmd/tui/context/exit.tsx | 35 ++++++++++++++++--- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 1fea3f4b305..5f2f4c5c526 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -27,7 +27,7 @@ import { FrecencyProvider } from "./component/prompt/frecency" import { PromptStashProvider } from "./component/prompt/stash" import { DialogAlert } from "./ui/dialog-alert" import { ToastProvider, useToast } from "./ui/toast" -import { ExitProvider, useExit } from "./context/exit" +import { ExitProvider, useExit, destroyRenderer } from "./context/exit" import { Session as SessionApi } from "@/session" import { TuiEvent } from "./event" import { KVProvider, useKV } from "./context/kv" @@ -110,7 +110,15 @@ export function tui(input: { // promise to prevent immediate exit return new Promise(async (resolve) => { const mode = await getTerminalBackgroundColor() + + const sigintHandler = () => { + destroyRenderer() + process.exit(0) + } + process.on("SIGINT", sigintHandler) + const onExit = async () => { + process.off("SIGINT", sigintHandler) await input.onExit?.() resolve() } @@ -606,6 +614,15 @@ function App() { }) }) + sdk.event.on(Installation.Event.Updated.type, (evt) => { + toast.show({ + variant: "success", + title: "Update Complete", + message: `OpenCode updated to v${evt.properties.version}`, + duration: 5000, + }) + }) + sdk.event.on(Installation.Event.UpdateAvailable.type, (evt) => { toast.show({ variant: "info", @@ -658,18 +675,9 @@ function ErrorComponent(props: { mode?: "dark" | "light" }) { const term = useTerminalDimensions() - const renderer = useRenderer() - - const handleExit = async () => { - renderer.setTerminalTitle("") - renderer.destroy() - props.onExit() - } useKeyboard((evt) => { - if (evt.ctrl && evt.name === "c") { - handleExit() - } + if (evt.ctrl && evt.name === "c") props.onExit() }) const [copied, setCopied] = createSignal(false) @@ -721,7 +729,7 @@ function ErrorComponent(props: { Reset TUI - + props.onExit()} backgroundColor={colors.primary} padding={1}> Exit diff --git a/packages/opencode/src/cli/cmd/tui/context/exit.tsx b/packages/opencode/src/cli/cmd/tui/context/exit.tsx index 414cb1a41d0..691a5e6da5a 100644 --- a/packages/opencode/src/cli/cmd/tui/context/exit.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/exit.tsx @@ -1,22 +1,47 @@ import { useRenderer } from "@opentui/solid" import { createSimpleContext } from "./helper" import { FormatError, FormatUnknownError } from "@/cli/error" +import * as fs from "fs" + +type Renderer = ReturnType + +const state: { renderer: Renderer | null } = { renderer: null } + +const DISABLE_MOUSE = "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?1015l\x1b[?25h" + +function cleanupTerminal() { + fs.writeSync(1, DISABLE_MOUSE) +} + +export function destroyRenderer() { + if (!state.renderer) { + cleanupTerminal() + return + } + state.renderer.setTerminalTitle("") + state.renderer.destroy() + state.renderer = null + cleanupTerminal() +} export const { use: useExit, provider: ExitProvider } = createSimpleContext({ name: "Exit", init: (input: { onExit?: () => Promise }) => { const renderer = useRenderer() - return async (reason?: any) => { - // Reset window title before destroying renderer + state.renderer = renderer + + return async (reason?: unknown) => { + cleanupTerminal() renderer.setTerminalTitle("") renderer.destroy() + await input.onExit?.() + if (reason) { const formatted = FormatError(reason) ?? FormatUnknownError(reason) - if (formatted) { - process.stderr.write(formatted + "\n") - } + if (formatted) process.stderr.write(formatted + "\n") } + process.exit(0) } },