Jelajahi Sumber

fix(tui): default Ctrl+Z to undo on Windows (#21138)

Luke Parker 1 Minggu lalu
induk
melakukan
77a462c930

+ 1 - 0
packages/opencode/src/cli/cmd/tui/app.tsx

@@ -761,6 +761,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
       keybind: "terminal_suspend",
       category: "System",
       hidden: true,
+      enabled: tuiConfig.keybinds?.terminal_suspend !== "none",
       onSelect: () => {
         process.once("SIGCONT", () => {
           renderer.resume()

+ 3 - 1
packages/opencode/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx

@@ -148,5 +148,7 @@ const TIPS = [
   "Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs",
   "Run {highlight}/help{/highlight} or {highlight}Ctrl+X H{/highlight} to show the help dialog",
   "Use {highlight}/rename{/highlight} to rename the current session",
-  "Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell",
+  ...(process.platform === "win32"
+    ? ["Press {highlight}Ctrl+Z{/highlight} to undo changes in your prompt"]
+    : ["Press {highlight}Ctrl+Z{/highlight} to suspend the terminal and return to your shell"]),
 ]

+ 9 - 1
packages/opencode/src/config/tui.ts

@@ -111,7 +111,15 @@ export namespace TuiConfig {
       }
     }
 
-    acc.result.keybinds = Config.Keybinds.parse(acc.result.keybinds ?? {})
+    const keybinds = { ...(acc.result.keybinds ?? {}) }
+    if (process.platform === "win32") {
+      // Native Windows terminals do not support POSIX suspend, so prefer prompt undo.
+      keybinds.terminal_suspend = "none"
+      keybinds.input_undo ??= unique(["ctrl+z", ...Config.Keybinds.shape.input_undo.parse(undefined).split(",")]).join(
+        ",",
+      )
+    }
+    acc.result.keybinds = Config.Keybinds.parse(keybinds)
 
     const deps: Promise<void>[] = []
     if (acc.result.plugin?.length) {

+ 48 - 0
packages/opencode/test/config/tui.test.ts

@@ -9,6 +9,7 @@ import { Global } from "../../src/global"
 import { Filesystem } from "../../src/util/filesystem"
 
 const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR!
+const wintest = process.platform === "win32" ? test : test.skip
 
 beforeEach(async () => {
   await Config.invalidate(true)
@@ -441,6 +442,53 @@ test("merges keybind overrides across precedence layers", async () => {
   })
 })
 
+wintest("defaults Ctrl+Z to input undo on Windows", async () => {
+  await using tmp = await tmpdir()
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await TuiConfig.get()
+      expect(config.keybinds?.terminal_suspend).toBe("none")
+      expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z")
+    },
+  })
+})
+
+wintest("keeps explicit input undo overrides on Windows", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { input_undo: "ctrl+y" } }))
+    },
+  })
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await TuiConfig.get()
+      expect(config.keybinds?.terminal_suspend).toBe("none")
+      expect(config.keybinds?.input_undo).toBe("ctrl+y")
+    },
+  })
+})
+
+wintest("ignores terminal suspend bindings on Windows", async () => {
+  await using tmp = await tmpdir({
+    init: async (dir) => {
+      await Bun.write(path.join(dir, "tui.json"), JSON.stringify({ keybinds: { terminal_suspend: "alt+z" } }))
+    },
+  })
+
+  await Instance.provide({
+    directory: tmp.path,
+    fn: async () => {
+      const config = await TuiConfig.get()
+      expect(config.keybinds?.terminal_suspend).toBe("none")
+      expect(config.keybinds?.input_undo).toBe("ctrl+z,ctrl+-,super+z")
+    },
+  })
+})
+
 test("OPENCODE_TUI_CONFIG provides settings when no project config exists", async () => {
   await using tmp = await tmpdir({
     init: async (dir) => {