Fix Shift+Enter for multiline input in agent terminals

xterm.js sends plain \r for Enter regardless of modifiers. Intercept
modified Enter in the custom key handler so it routes through CSI u
encoding (Kitty keyboard protocol), which Claude Code uses for
Shift+Enter newlines.
This commit is contained in:
Mohamed Boudra
2026-04-12 22:10:16 +07:00
parent 609d11ee8d
commit 0095f61121
3 changed files with 81 additions and 1 deletions
@@ -57,7 +57,9 @@ describe("terminal key helpers", () => {
shouldInterceptDomTerminalKey({
key: "Escape",
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(false);
@@ -65,7 +67,9 @@ describe("terminal key helpers", () => {
shouldInterceptDomTerminalKey({
key: "c",
ctrlKey: true,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(false);
@@ -73,7 +77,9 @@ describe("terminal key helpers", () => {
shouldInterceptDomTerminalKey({
key: "c",
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: true, shift: false, alt: false },
}),
).toBe(true);
@@ -81,12 +87,73 @@ describe("terminal key helpers", () => {
shouldInterceptDomTerminalKey({
key: "Escape",
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: true },
}),
).toBe(true);
});
it("intercepts Enter with DOM shift modifier for CSI u encoding", () => {
expect(
shouldInterceptDomTerminalKey({
key: "Enter",
ctrlKey: false,
shiftKey: true,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(true);
});
it("intercepts Enter with any DOM modifier for CSI u encoding", () => {
expect(
shouldInterceptDomTerminalKey({
key: "Enter",
ctrlKey: true,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(true);
expect(
shouldInterceptDomTerminalKey({
key: "Enter",
ctrlKey: false,
shiftKey: false,
altKey: true,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(true);
expect(
shouldInterceptDomTerminalKey({
key: "Enter",
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: true,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(true);
});
it("does not intercept plain Enter without modifiers", () => {
expect(
shouldInterceptDomTerminalKey({
key: "Enter",
ctrlKey: false,
shiftKey: false,
altKey: false,
metaKey: false,
pendingModifiers: { ctrl: false, shift: false, alt: false },
}),
).toBe(false);
});
it("detects pending modifier state", () => {
expect(hasPendingTerminalModifiers({ ctrl: false, shift: false, alt: false })).toBe(false);
expect(hasPendingTerminalModifiers({ ctrl: true, shift: false, alt: false })).toBe(true);