fix(tui): preserve cursor position on Windows exit

This commit is contained in:
Kail Tian
2026-08-30 12:17:40 +08:00
committed by chengyongru
parent 2c87143f77
commit 1c1b13a3a9
3 changed files with 40 additions and 2 deletions
+2 -1
View File
@@ -94,7 +94,7 @@ import {
type FooterMode, type FooterMode,
type FooterHintTheme, type FooterHintTheme,
} from "./footer-hints" } from "./footer-hints"
import { createTuiHost, type TuiHost } from "./host" import { configureOpenTuiEnvironment, createTuiHost, type TuiHost } from "./host"
interface AppOptions { interface AppOptions {
wsUrl?: string wsUrl?: string
@@ -820,6 +820,7 @@ export class NanobotTui {
} }
static async create(options: AppOptions): Promise<NanobotTui> { static async create(options: AppOptions): Promise<NanobotTui> {
configureOpenTuiEnvironment()
const host = createTuiHost() const host = createTuiHost()
const renderer = await createCliRenderer({ const renderer = await createCliRenderer({
targetFps: 30, targetFps: 30,
+25 -1
View File
@@ -1,6 +1,9 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import { createTuiHost } from "./host" import {
configureOpenTuiEnvironment,
createTuiHost,
} from "./host"
async function settle(): Promise<void> { async function settle(): Promise<void> {
await Bun.sleep(0) await Bun.sleep(0)
@@ -8,6 +11,27 @@ async function settle(): Promise<void> {
} }
describe("TUI host integration", () => { describe("TUI host integration", () => {
test("disables the explicit-width probe on Windows", () => {
const environment: Record<string, string | undefined> = {}
configureOpenTuiEnvironment(environment, "win32")
expect(environment.OPENTUI_FORCE_EXPLICIT_WIDTH).toBe("false")
})
test("preserves explicit probe choices and leaves other platforms unchanged", () => {
const overridden = {
OPENTUI_FORCE_EXPLICIT_WIDTH: "true",
}
const nonWindows: Record<string, string | undefined> = {}
configureOpenTuiEnvironment(overridden, "win32")
configureOpenTuiEnvironment(nonWindows, "linux")
expect(overridden.OPENTUI_FORCE_EXPLICIT_WIDTH).toBe("true")
expect(nonWindows.OPENTUI_FORCE_EXPLICIT_WIDTH).toBeUndefined()
})
test("standalone terminals remain a no-op", async () => { test("standalone terminals remain a no-op", async () => {
const commands: string[][] = [] const commands: string[][] = []
const host = createTuiHost({}, async (command) => { commands.push([...command]) }) const host = createTuiHost({}, async (command) => { commands.push([...command]) })
+13
View File
@@ -8,6 +8,19 @@ type CommandRunner = (command: readonly string[]) => Promise<void>
const METADATA_SOURCE = "nanobot:tui:metadata" const METADATA_SOURCE = "nanobot:tui:metadata"
export function configureOpenTuiEnvironment(
environment: Environment = process.env,
platform = process.platform,
): void {
if (platform !== "win32") return
// OpenTUI probes OSC 66 support on the main screen before its renderer is
// active. Some Windows terminal hosts do not restore the cursor around that
// probe, so shutdown resumes in terminal history instead of below the TUI.
// Keep an explicit user choice, but use the safe default on Windows.
environment.OPENTUI_FORCE_EXPLICIT_WIDTH ??= "false"
}
class StandaloneHost implements TuiHost { class StandaloneHost implements TuiHost {
reportTitle(): void {} reportTitle(): void {}
release(): void {} release(): void {}