Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/opencode/src/cli/cmd/tui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -714,6 +718,7 @@ function ErrorComponent(props: {
const handleExit = async () => {
renderer.setTerminalTitle("")
renderer.destroy()
resetTerminalState()
props.onExit()
}

Expand Down
2 changes: 2 additions & 0 deletions packages/opencode/src/cli/cmd/tui/context/exit.tsx
Original file line number Diff line number Diff line change
@@ -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<void>) & {
message: {
set: (value?: string) => () => void
Expand Down Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions packages/opencode/src/cli/cmd/tui/util/terminal.ts
Original file line number Diff line number Diff line change
@@ -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[<u", // pop/disable Kitty keyboard protocol
"\x1b[0m", // reset text attributes
]
try {
process.stdout.write(sequences.join(""))
} catch {
// stdout may already be closed during exit
}
}

export namespace Terminal {
export type Colors = Awaited<ReturnType<typeof colors>>
/**
Expand Down