chore(tui): replace koffi with Windows VT input helper

closes #4480
This commit is contained in:
Mario Zechner
2026-05-21 00:21:21 +02:00
parent 385a11bf53
commit 4868222e34
11 changed files with 176 additions and 70 deletions

View File

@@ -1,6 +1,7 @@
import * as fs from "node:fs";
import { createRequire } from "node:module";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { setKittyProtocolActive } from "./keys.ts";
import { StdinBuffer } from "./stdin-buffer.ts";
@@ -210,23 +211,30 @@ export class ProcessTerminal implements Terminal {
private enableWindowsVTInput(): void {
if (process.platform !== "win32") return;
try {
// Dynamic require to avoid bundling koffi's 74MB of cross-platform
// native binaries into every compiled binary. Koffi is only needed
// on Windows for VT input support.
const koffi = cjsRequire("koffi");
const k32 = koffi.load("kernel32.dll");
const GetStdHandle = k32.func("void* __stdcall GetStdHandle(int)");
const GetConsoleMode = k32.func("bool __stdcall GetConsoleMode(void*, _Out_ uint32_t*)");
const SetConsoleMode = k32.func("bool __stdcall SetConsoleMode(void*, uint32_t)");
const arch = process.arch;
if (arch !== "x64" && arch !== "arm64") return;
const STD_INPUT_HANDLE = -10;
const ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200;
const handle = GetStdHandle(STD_INPUT_HANDLE);
const mode = new Uint32Array(1);
GetConsoleMode(handle, mode);
SetConsoleMode(handle, mode[0]! | ENABLE_VIRTUAL_TERMINAL_INPUT);
// Dynamic require so non-Windows and bundled/browser paths never load the
// native helper. In the npm package native/ is next to dist/; in compiled
// binary archives native/ is copied next to the executable.
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
const nativePath = path.join("native", "win32", "prebuilds", `win32-${arch}`, "win32-console-mode.node");
const candidates = [
path.join(moduleDir, "..", nativePath),
path.join(moduleDir, nativePath),
path.join(path.dirname(process.execPath), nativePath),
];
for (const modulePath of candidates) {
try {
const helper = cjsRequire(modulePath) as { enableVirtualTerminalInput?: () => boolean };
helper.enableVirtualTerminalInput?.();
return;
} catch {
// Try the next possible packaging location.
}
}
} catch {
// koffi not available — Shift+Tab won't be distinguishable from Tab
// Native helper not available — Shift+Tab won't be distinguishable from Tab.
}
}