diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 42c5b413dc..29a2f7214f 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -40,6 +40,7 @@ import { writeHeapSnapshot } from "v8" import { PromptRefProvider, usePromptRef } from "./context/prompt" import { registerKiloCommands } from "@/kilocode/kilo-commands" // kilocode_change import { initializeTUIDependencies } from "@kilocode/kilo-gateway/tui" // kilocode_change +import { resetTerminalState } from "@tui/util/terminal" async function getTerminalBackgroundColor(): Promise<"dark" | "light"> { // can't set raw mode if not a TTY @@ -120,6 +121,9 @@ export function tui(input: { resolve() } + // Safety net: ensure mouse tracking is disabled regardless of exit path + process.on("exit", resetTerminalState) + render( () => { return ( @@ -714,6 +718,7 @@ function ErrorComponent(props: { const handleExit = async () => { renderer.setTerminalTitle("") renderer.destroy() + resetTerminalState() props.onExit() } diff --git a/packages/opencode/src/cli/cmd/tui/context/exit.tsx b/packages/opencode/src/cli/cmd/tui/context/exit.tsx index 2aac152204..c463073e6f 100644 --- a/packages/opencode/src/cli/cmd/tui/context/exit.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/exit.tsx @@ -1,6 +1,7 @@ import { useRenderer } from "@opentui/solid" import { createSimpleContext } from "./helper" import { FormatError, FormatUnknownError } from "@/cli/error" +import { resetTerminalState } from "@tui/util/terminal" type Exit = ((reason?: unknown) => Promise) & { message: { set: (value?: string) => () => void @@ -32,6 +33,7 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({ // Reset window title before destroying renderer renderer.setTerminalTitle("") renderer.destroy() + resetTerminalState() await input.onExit?.() if (reason) { const formatted = FormatError(reason) ?? FormatUnknownError(reason) diff --git a/packages/opencode/src/cli/cmd/tui/util/terminal.ts b/packages/opencode/src/cli/cmd/tui/util/terminal.ts index 2b81068b3f..541f204182 100644 --- a/packages/opencode/src/cli/cmd/tui/util/terminal.ts +++ b/packages/opencode/src/cli/cmd/tui/util/terminal.ts @@ -1,5 +1,27 @@ import { RGBA } from "@opentui/core" +/** + * Write escape sequences to disable all mouse tracking modes and reset terminal state. + * This is a safety net to ensure the terminal is clean after exit, even if the renderer's + * cleanup didn't flush properly (e.g. on Windows). + */ +export function resetTerminalState() { + const sequences = [ + "\x1b[?1000l", // disable normal mouse tracking + "\x1b[?1002l", // disable button-event mouse tracking + "\x1b[?1003l", // disable any-event mouse tracking (all movement) + "\x1b[?1006l", // disable SGR extended mouse mode + "\x1b[?1015l", // disable RXVT mouse mode + "\x1b[> /**