diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 017cb1c4..86fb301f 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -22,7 +22,10 @@ import { isEnter, isEscape, isHome, + isShiftBackspace, + isShiftDelete, isShiftEnter, + isShiftSpace, isTab, } from "../keys.js"; import type { Component } from "../tui.js"; @@ -457,8 +460,8 @@ export class Editor implements Component { this.onSubmit(result); } } - // Backspace - else if (isBackspace(data)) { + // Backspace (including Shift+Backspace) + else if (isBackspace(data) || isShiftBackspace(data)) { this.handleBackspace(); } // Line navigation shortcuts (Home/End keys) @@ -467,8 +470,8 @@ export class Editor implements Component { } else if (isEnd(data)) { this.moveToLineEnd(); } - // Forward delete (Fn+Backspace or Delete key) - else if (isDelete(data)) { + // Forward delete (Fn+Backspace or Delete key, including Shift+Delete) + else if (isDelete(data) || isShiftDelete(data)) { this.handleForwardDelete(); } // Word navigation (Option/Alt + Arrow or Ctrl + Arrow) @@ -503,6 +506,10 @@ export class Editor implements Component { // Left this.moveCursor(0, -1); } + // Shift+Space - insert regular space (Kitty protocol sends escape sequence) + else if (isShiftSpace(data)) { + this.insertCharacter(" "); + } // Regular characters (printable characters and unicode, but not control characters) else if (data.charCodeAt(0) >= 32) { this.insertCharacter(data); diff --git a/packages/tui/src/keys.ts b/packages/tui/src/keys.ts index da8cf699..58d57c4a 100644 --- a/packages/tui/src/keys.ts +++ b/packages/tui/src/keys.ts @@ -42,6 +42,7 @@ const CODEPOINTS = { escape: 27, tab: 9, enter: 13, + space: 32, backspace: 127, } as const; @@ -464,6 +465,15 @@ export function isBackspace(data: string): boolean { return data === "\x7f" || data === "\x08" || matchesKittySequence(data, CODEPOINTS.backspace, 0); } +/** + * Check if input matches Shift+Backspace (Kitty protocol). + * Returns true so caller can treat it as regular backspace. + * Ignores lock key bits. + */ +export function isShiftBackspace(data: string): boolean { + return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.shift); +} + /** * Check if input matches Shift+Enter. * Ignores lock key bits. @@ -480,6 +490,15 @@ export function isAltEnter(data: string): boolean { return data === Keys.ALT_ENTER || data === "\x1b\r" || matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt); } +/** + * Check if input matches Shift+Space (Kitty protocol). + * Returns true so caller can insert a regular space. + * Ignores lock key bits. + */ +export function isShiftSpace(data: string): boolean { + return matchesKittySequence(data, CODEPOINTS.space, MODIFIERS.shift); +} + /** * Check if input matches Option/Alt+Left (word navigation). * Handles multiple formats including Kitty protocol. @@ -545,3 +564,12 @@ export function isEnd(data: string): boolean { export function isDelete(data: string): boolean { return data === "\x1b[3~" || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0); } + +/** + * Check if input matches Shift+Delete (Kitty protocol). + * Returns true so caller can treat it as regular delete. + * Ignores lock key bits. + */ +export function isShiftDelete(data: string): boolean { + return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, MODIFIERS.shift); +}