fix(tui): detect Apple Terminal Shift+Enter
This commit is contained in:
60
packages/tui/src/native-modifiers.ts
Normal file
60
packages/tui/src/native-modifiers.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createRequire } from "node:module";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const cjsRequire = createRequire(import.meta.url);
|
||||
|
||||
export type ModifierKey = "shift" | "command" | "control" | "option";
|
||||
|
||||
type NativeModifiersHelper = {
|
||||
isModifierPressed: (name: ModifierKey) => boolean;
|
||||
};
|
||||
|
||||
let nativeModifiersHelper: NativeModifiersHelper | null | undefined;
|
||||
|
||||
function isNativeModifiersHelper(value: unknown): value is NativeModifiersHelper {
|
||||
if (typeof value !== "object" || value === null) return false;
|
||||
const candidate = (value as { isModifierPressed?: unknown }).isModifierPressed;
|
||||
return typeof candidate === "function";
|
||||
}
|
||||
|
||||
function loadNativeModifiersHelper(): NativeModifiersHelper | undefined {
|
||||
if (nativeModifiersHelper !== undefined) return nativeModifiersHelper ?? undefined;
|
||||
nativeModifiersHelper = null;
|
||||
if (process.platform !== "darwin") return undefined;
|
||||
const arch = process.arch;
|
||||
if (arch !== "x64" && arch !== "arm64") return undefined;
|
||||
|
||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const nativePath = path.join("native", "darwin", "prebuilds", `darwin-${arch}`, "darwin-modifiers.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 unknown;
|
||||
if (isNativeModifiersHelper(helper)) {
|
||||
nativeModifiersHelper = helper;
|
||||
return helper;
|
||||
}
|
||||
} catch {
|
||||
// Try the next possible packaging location.
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isNativeModifierPressed(key: ModifierKey): boolean {
|
||||
if (process.env.PI_TUI_DISABLE_NATIVE_MODIFIERS === "1") return false;
|
||||
const helper = loadNativeModifiersHelper();
|
||||
if (!helper) return false;
|
||||
try {
|
||||
return helper.isModifierPressed(key) === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { setKittyProtocolActive } from "./keys.ts";
|
||||
import { isNativeModifierPressed } from "./native-modifiers.ts";
|
||||
import { StdinBuffer } from "./stdin-buffer.ts";
|
||||
|
||||
const cjsRequire = createRequire(import.meta.url);
|
||||
@@ -10,6 +11,16 @@ const cjsRequire = createRequire(import.meta.url);
|
||||
const TERMINAL_PROGRESS_KEEPALIVE_MS = 1000;
|
||||
const TERMINAL_PROGRESS_ACTIVE_SEQUENCE = "\x1b]9;4;3\x07";
|
||||
const TERMINAL_PROGRESS_CLEAR_SEQUENCE = "\x1b]9;4;0;\x07";
|
||||
const APPLE_TERMINAL_SHIFT_ENTER_SEQUENCE = "\x1b[13;2u";
|
||||
|
||||
export function isAppleTerminalSession(): boolean {
|
||||
return process.platform === "darwin" && process.env.TERM_PROGRAM === "Apple_Terminal";
|
||||
}
|
||||
|
||||
export function normalizeAppleTerminalInput(data: string, isAppleTerminal: boolean, isShiftPressed: boolean): string {
|
||||
if (isAppleTerminal && data === "\r" && isShiftPressed) return APPLE_TERMINAL_SHIFT_ENTER_SEQUENCE;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal terminal interface for TUI
|
||||
@@ -159,7 +170,13 @@ export class ProcessTerminal implements Terminal {
|
||||
}
|
||||
|
||||
if (this.inputHandler) {
|
||||
this.inputHandler(sequence);
|
||||
const isAppleTerminal = sequence === "\r" && isAppleTerminalSession();
|
||||
const input = normalizeAppleTerminalInput(
|
||||
sequence,
|
||||
isAppleTerminal,
|
||||
isAppleTerminal && isNativeModifierPressed("shift"),
|
||||
);
|
||||
this.inputHandler(input);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user