| author | |
| committer | |
| log | 7968f278ee54202cc3ae63f8e52d224b13e0265f |
| tree | e4ce6526725a0ebfaf55a6f42cbbe8d3d78e7e08 |
| parent | 44afe784a3b17f0468cc73aeb87b0870d0d78d55 |
| signature | Signed by PGP key B5690EEEBB952194 |
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 { |
| 28 | 28 | resolveWrappedTerminalLinkRange, |
| 29 | 29 | wrappedTerminalLinkRangeIntersectsBufferLine, |
| 30 | 30 | } from "../terminal-links"; |
| 31 | import { isTerminalClearShortcut, terminalNavigationShortcutData } from "../keybindings"; | |
| 31 | import { | |
| 32 | isTerminalClearShortcut, | |
| 33 | terminalDeleteShortcutData, | |
| 34 | terminalNavigationShortcutData, | |
| 35 | } from "../keybindings"; | |
| 32 | 36 | import { |
| 33 | 37 | DEFAULT_THREAD_TERMINAL_HEIGHT, |
| 34 | 38 | DEFAULT_THREAD_TERMINAL_ID, |
| ... | ... | @@ -407,6 +411,14 @@ export function TerminalViewport({ |
| 407 | 411 | return false; |
| 408 | 412 | } |
| 409 | 413 | |
| 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 | ||
| 410 | 422 | if (!isTerminalClearShortcut(event)) return true; |
| 411 | 423 | event.preventDefault(); |
| 412 | 424 | event.stopPropagation(); |
apps/web/src/keybindings.test.ts+29| ... | ... | @@ -20,6 +20,7 @@ import { |
| 20 | 20 | resolveShortcutCommand, |
| 21 | 21 | shouldShowThreadJumpHints, |
| 22 | 22 | shortcutLabelForCommand, |
| 23 | terminalDeleteShortcutData, | |
| 23 | 24 | terminalNavigationShortcutData, |
| 24 | 25 | threadJumpCommandForIndex, |
| 25 | 26 | threadJumpIndexFromCommand, |
| ... | ... | @@ -560,6 +561,34 @@ describe("isTerminalClearShortcut", () => { |
| 560 | 561 | }); |
| 561 | 562 | }); |
| 562 | 563 | |
| 564 | describe("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 | ||
| 563 | 592 | describe("terminalNavigationShortcutData", () => { |
| 564 | 593 | it("maps Option+Arrow on macOS to word movement", () => { |
| 565 | 594 | assert.strictEqual( |
apps/web/src/keybindings.ts+23| ... | ... | @@ -37,6 +37,7 @@ const TERMINAL_WORD_BACKWARD = "\u001bb"; |
| 37 | 37 | const TERMINAL_WORD_FORWARD = "\u001bf"; |
| 38 | 38 | const TERMINAL_LINE_START = "\u0001"; |
| 39 | 39 | const TERMINAL_LINE_END = "\u0005"; |
| 40 | const TERMINAL_DELETE_TO_LINE_START = "\u0015"; | |
| 40 | 41 | const EVENT_CODE_KEY_ALIASES: Readonly<Record<string, readonly string[]>> = { |
| 41 | 42 | BracketLeft: ["["], |
| 42 | 43 | BracketRight: ["]"], |
| ... | ... | @@ -370,6 +371,28 @@ export function isTerminalClearShortcut( |
| 370 | 371 | ); |
| 371 | 372 | } |
| 372 | 373 | |
| 374 | export 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 | ||
| 373 | 396 | export function terminalNavigationShortcutData( |
| 374 | 397 | event: ShortcutEventLike, |
| 375 | 398 | platform = navigator.platform, |