pi-mono/packages/coding-agent/test/keybindings.test.ts
robopipi[bot] 37b38e9f65
fix(coding-agent): keep ctrl+v as a paste key on Windows and WSL (#11780)
On Windows and WSL, `app.clipboard.pasteImage` was bound only to
`alt+v`. Most Windows terminals handle Ctrl+V themselves and send a
bracketed paste. Some terminals have no Ctrl+V paste action and pass
Ctrl+V to the program as the raw `0x16` key instead; Tern's embedded
terminal is one. In those terminals nothing in pi was bound to `ctrl+v`,
so pressing it did nothing and showed no message.

This change makes the Windows/WSL default `["alt+v", "ctrl+v"]`. `alt+v`
keeps working as before. In terminals that handle Ctrl+V themselves, pi
never receives the key, so the extra binding has no effect there. No
other default binding uses `ctrl+v`. `docs/keybindings.md` is updated to
match.

Testing:
- Added a test to `test/keybindings.test.ts`. It sets `WSL_DISTRO_NAME`,
re-imports the keybindings module, and checks that the paste defaults
are `["alt+v", "ctrl+v"]`. It fails without the change and passes with
it.
- Updated the existing defaults assertion in the same file to match the
new value.
- `node ../../node_modules/vitest/dist/cli.js --run
test/keybindings.test.ts`: 6 passed.
- `npm run check`: passes.

Fixes #11765

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 09:33:31 +01:00

57 lines
2.5 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { KEYBINDINGS, useWindowsKeybindings } from "../src/core/keybindings.ts";
describe("Windows keybinding defaults", () => {
it("uses Windows keybindings on native Windows", () => {
expect(useWindowsKeybindings("win32", {})).toBe(true);
});
it("uses Windows keybindings in WSL without relying on Windows Terminal detection", () => {
expect(useWindowsKeybindings("linux", { WSL_DISTRO_NAME: "Ubuntu" })).toBe(true);
expect(useWindowsKeybindings("linux", { WSL_INTEROP: "/run/WSL/123_interop" })).toBe(true);
});
it("does not use Windows keybindings from WT_SESSION alone", () => {
expect(useWindowsKeybindings("linux", { WT_SESSION: "session" })).toBe(false);
});
it("keeps non-Windows defaults on other platforms", () => {
expect(useWindowsKeybindings("linux", {})).toBe(false);
expect(useWindowsKeybindings("darwin", {})).toBe(false);
});
it("keeps ctrl+v as a paste key on WSL", async () => {
if (process.platform !== "linux") return;
vi.stubEnv("WSL_DISTRO_NAME", "Ubuntu");
vi.resetModules();
try {
const { KEYBINDINGS: wslKeybindings } = await import("../src/core/keybindings.ts");
expect(wslKeybindings["app.clipboard.pasteImage"].defaultKeys).toEqual(["alt+v", "ctrl+v"]);
} finally {
vi.unstubAllEnvs();
vi.resetModules();
}
});
it("applies the detected defaults consistently", () => {
const windowsKeybindings = useWindowsKeybindings();
const nativeWindows = process.platform === "win32";
expect(KEYBINDINGS["app.clipboard.pasteImage"].defaultKeys).toEqual(
windowsKeybindings ? ["alt+v", "ctrl+v"] : "ctrl+v",
);
expect(KEYBINDINGS["tui.altScreen.search"].defaultKeys).toBe(windowsKeybindings ? "ctrl+f" : "ctrl+shift+f");
expect(KEYBINDINGS["app.message.followUp"].defaultKeys).toBe(windowsKeybindings ? "ctrl+q" : "alt+enter");
expect(KEYBINDINGS["app.model.cycleBackward"].defaultKeys).toBe(windowsKeybindings ? "alt+p" : "shift+ctrl+p");
expect(KEYBINDINGS["tui.editor.undo"].defaultKeys).toBe(
nativeWindows ? "ctrl+z" : windowsKeybindings ? "alt+z" : "ctrl+-",
);
expect(KEYBINDINGS["tui.altScreen.previousPrompt"].defaultKeys).toEqual(
windowsKeybindings ? "ctrl+up" : ["ctrl+shift+up", "ctrl+up"],
);
expect(KEYBINDINGS["tui.altScreen.nextPrompt"].defaultKeys).toEqual(
windowsKeybindings ? "ctrl+down" : ["ctrl+shift+down", "ctrl+down"],
);
expect(KEYBINDINGS["app.message.dequeue"].defaultKeys).toBe(windowsKeybindings ? "alt+q" : "alt+up");
});
});