Fix Shift+Enter in terminal input modes (#999)

* Fix Shift+Enter terminal input modes

* Advertise terminal input capabilities
This commit is contained in:
Mohamed Boudra
2026-05-14 17:48:22 +08:00
committed by GitHub
parent eb7fa02ac8
commit e1ab779fe0
16 changed files with 702 additions and 21 deletions
+14
View File
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
import { createExternalProcessEnv } from "../server/paseo-env.js";
import { writePrivateFileAtomicSync } from "../server/private-files.js";
import type { TerminalCell, TerminalState } from "../shared/messages.js";
import { TerminalInputModeTracker } from "../shared/terminal-input-mode.js";
const { Terminal } = xterm;
const require = createRequire(import.meta.url);
@@ -54,6 +55,7 @@ export interface TerminalSession {
getSize(): { rows: number; cols: number };
getState(): TerminalState;
getStateSnapshot(): TerminalStateSnapshot;
getReplayPreamble(): string;
getTitle(): string | undefined;
getExitInfo(): TerminalExitInfo | null;
kill(): void;
@@ -219,6 +221,7 @@ export function buildTerminalEnvironment(
): Record<string, string> {
const baseEnv: Record<string, string> = createExternalProcessEnv(process.env, input.env, {
TERM: "xterm-256color",
TERM_PROGRAM: "kitty",
});
if (basename(input.shell) !== "zsh") {
@@ -551,6 +554,7 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
let pendingInput = "";
let inputFlushImmediate: ReturnType<typeof setImmediate> | null = null;
let stateRevision = 0;
const inputModeTracker = new TerminalInputModeTracker();
// Create xterm.js headless terminal
const terminal = new Terminal({
@@ -703,6 +707,7 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
}
disposed = true;
pendingInput = "";
inputModeTracker.reset();
if (inputFlushImmediate) {
clearImmediate(inputFlushImmediate);
inputFlushImmediate = null;
@@ -735,6 +740,10 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
// Pipe PTY output to terminal emulator
ptyProcess.onData((data) => {
if (killed) return;
const inputModeUpdate = inputModeTracker.feed(data);
for (const response of inputModeUpdate.responses) {
ptyProcess.write(response);
}
recentOutputText = `${recentOutputText}${data}`;
if (recentOutputText.length > TERMINAL_EXIT_OUTPUT_CHAR_LIMIT) {
recentOutputText = recentOutputText.slice(-TERMINAL_EXIT_OUTPUT_CHAR_LIMIT);
@@ -803,6 +812,10 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
};
}
function getReplayPreamble(): string {
return inputModeTracker.getPreamble();
}
function writeInputToPty(data: string): void {
ptyProcess.write(data);
}
@@ -1034,6 +1047,7 @@ export async function createTerminal(options: CreateTerminalOptions): Promise<Te
getSize,
getState,
getStateSnapshot,
getReplayPreamble,
getTitle,
getExitInfo,
kill,