authorgravatar for 126517689+SAKETH11111@users.noreply.github.comSaketh <126517689+SAKETH11111@users.noreply.github.com> 2026-04-15 01:29:45-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-15 06:29:45+00:00
log7968f278ee54202cc3ae63f8e52d224b13e0265f
treee4ce6526725a0ebfaf55a6f42cbbe8d3d78e7e08
parent44afe784a3b17f0468cc73aeb87b0870d0d78d55
signature Signed by PGP key B5690EEEBB952194

Fix terminal Cmd+Backspace on macOS (#2027)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

3 files changed, 65 insertions(+), 1 deletions(-)

apps/web/src/components/ThreadTerminalDrawer.tsx+13-1
......@@ -28,7 +28,11 @@ import {
2828 resolveWrappedTerminalLinkRange,
2929 wrappedTerminalLinkRangeIntersectsBufferLine,
3030} from "../terminal-links";
31import { isTerminalClearShortcut, terminalNavigationShortcutData } from "../keybindings";
31import {
32 isTerminalClearShortcut,
33 terminalDeleteShortcutData,
34 terminalNavigationShortcutData,
35} from "../keybindings";
3236import {
3337 DEFAULT_THREAD_TERMINAL_HEIGHT,
3438 DEFAULT_THREAD_TERMINAL_ID,
......@@ -407,6 +411,14 @@ export function TerminalViewport({
407411 return false;
408412 }
409413
414 const deleteData = terminalDeleteShortcutData(event);
415 if (deleteData !== null) {
416 event.preventDefault();
417 event.stopPropagation();
418 void sendTerminalInput(deleteData, "Failed to delete terminal input");
419 return false;
420 }
421
410422 if (!isTerminalClearShortcut(event)) return true;
411423 event.preventDefault();
412424 event.stopPropagation();
apps/web/src/keybindings.test.ts+29
......@@ -20,6 +20,7 @@ import {
2020 resolveShortcutCommand,
2121 shouldShowThreadJumpHints,
2222 shortcutLabelForCommand,
23 terminalDeleteShortcutData,
2324 terminalNavigationShortcutData,
2425 threadJumpCommandForIndex,
2526 threadJumpIndexFromCommand,
......@@ -560,6 +561,34 @@ describe("isTerminalClearShortcut", () => {
560561 });
561562});
562563
564describe("terminalDeleteShortcutData", () => {
565 it("maps Cmd+Backspace on macOS to delete-to-line-start", () => {
566 assert.strictEqual(
567 terminalDeleteShortcutData(event({ key: "Backspace", metaKey: true }), "MacIntel"),
568 "\u0015",
569 );
570 });
571
572 it("ignores non-macOS platforms and modified variants", () => {
573 assert.isNull(terminalDeleteShortcutData(event({ key: "Backspace", metaKey: true }), "Linux"));
574 assert.isNull(
575 terminalDeleteShortcutData(
576 event({ key: "Backspace", metaKey: true, altKey: true }),
577 "MacIntel",
578 ),
579 );
580 });
581
582 it("ignores non-keydown events", () => {
583 assert.isNull(
584 terminalDeleteShortcutData(
585 event({ type: "keyup", key: "Backspace", metaKey: true }),
586 "MacIntel",
587 ),
588 );
589 });
590});
591
563592describe("terminalNavigationShortcutData", () => {
564593 it("maps Option+Arrow on macOS to word movement", () => {
565594 assert.strictEqual(
apps/web/src/keybindings.ts+23
......@@ -37,6 +37,7 @@ const TERMINAL_WORD_BACKWARD = "\u001bb";
3737const TERMINAL_WORD_FORWARD = "\u001bf";
3838const TERMINAL_LINE_START = "\u0001";
3939const TERMINAL_LINE_END = "\u0005";
40const TERMINAL_DELETE_TO_LINE_START = "\u0015";
4041const EVENT_CODE_KEY_ALIASES: Readonly<Record<string, readonly string[]>> = {
4142 BracketLeft: ["["],
4243 BracketRight: ["]"],
......@@ -370,6 +371,28 @@ export function isTerminalClearShortcut(
370371 );
371372}
372373
374export function terminalDeleteShortcutData(
375 event: ShortcutEventLike,
376 platform = navigator.platform,
377): string | null {
378 if (event.type !== undefined && event.type !== "keydown") {
379 return null;
380 }
381
382 if (!isMacPlatform(platform)) {
383 return null;
384 }
385
386 const key = normalizeEventKey(event.key);
387 if (key !== "backspace") {
388 return null;
389 }
390
391 return event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey
392 ? TERMINAL_DELETE_TO_LINE_START
393 : null;
394}
395
373396export function terminalNavigationShortcutData(
374397 event: ShortcutEventLike,
375398 platform = navigator.platform,