fix(keybindings): migrate to namespaced ids closes #2391

This commit is contained in:
Mario Zechner
2026-03-20 01:50:47 +01:00
parent 74a46fc7ea
commit e3fee7a511
43 changed files with 1077 additions and 883 deletions

View File

@@ -1,4 +1,4 @@
import { getEditorKeybindings } from "../keybindings.js";
import { getKeybindings } from "../keybindings.js";
import { Loader } from "./loader.js";
/**
@@ -27,8 +27,8 @@ export class CancellableLoader extends Loader {
}
handleInput(data: string): void {
const kb = getEditorKeybindings();
if (kb.matches(data, "selectCancel")) {
const kb = getKeybindings();
if (kb.matches(data, "tui.select.cancel")) {
this.abortController.abort();
this.onAbort?.();
}

View File

@@ -1,5 +1,5 @@
import type { AutocompleteProvider, CombinedAutocompleteProvider } from "../autocomplete.js";
import { getEditorKeybindings } from "../keybindings.js";
import { getKeybindings } from "../keybindings.js";
import { decodeKittyPrintable, matchesKey } from "../keys.js";
import { KillRing } from "../kill-ring.js";
import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.js";
@@ -517,12 +517,12 @@ export class Editor implements Component, Focusable {
}
handleInput(data: string): void {
const kb = getEditorKeybindings();
const kb = getKeybindings();
// Handle character jump mode (awaiting next character to jump to)
if (this.jumpMode !== null) {
// Cancel if the hotkey is pressed again
if (kb.matches(data, "jumpForward") || kb.matches(data, "jumpBackward")) {
if (kb.matches(data, "tui.editor.jumpForward") || kb.matches(data, "tui.editor.jumpBackward")) {
this.jumpMode = null;
return;
}
@@ -566,29 +566,29 @@ export class Editor implements Component, Focusable {
}
// Ctrl+C - let parent handle (exit/clear)
if (kb.matches(data, "copy")) {
if (kb.matches(data, "tui.input.copy")) {
return;
}
// Undo
if (kb.matches(data, "undo")) {
if (kb.matches(data, "tui.editor.undo")) {
this.undo();
return;
}
// Handle autocomplete mode
if (this.autocompleteState && this.autocompleteList) {
if (kb.matches(data, "selectCancel")) {
if (kb.matches(data, "tui.select.cancel")) {
this.cancelAutocomplete();
return;
}
if (kb.matches(data, "selectUp") || kb.matches(data, "selectDown")) {
if (kb.matches(data, "tui.select.up") || kb.matches(data, "tui.select.down")) {
this.autocompleteList.handleInput(data);
return;
}
if (kb.matches(data, "tab")) {
if (kb.matches(data, "tui.input.tab")) {
const selected = this.autocompleteList.getSelectedItem();
if (selected && this.autocompleteProvider) {
const shouldChainSlashArgumentAutocomplete = this.shouldChainSlashArgumentAutocompleteOnTabSelection();
@@ -615,7 +615,7 @@ export class Editor implements Component, Focusable {
return;
}
if (kb.matches(data, "selectConfirm")) {
if (kb.matches(data, "tui.select.confirm")) {
const selected = this.autocompleteList.getSelectedItem();
if (selected && this.autocompleteProvider) {
this.pushUndoSnapshot();
@@ -644,68 +644,68 @@ export class Editor implements Component, Focusable {
}
// Tab - trigger completion
if (kb.matches(data, "tab") && !this.autocompleteState) {
if (kb.matches(data, "tui.input.tab") && !this.autocompleteState) {
this.handleTabCompletion();
return;
}
// Deletion actions
if (kb.matches(data, "deleteToLineEnd")) {
if (kb.matches(data, "tui.editor.deleteToLineEnd")) {
this.deleteToEndOfLine();
return;
}
if (kb.matches(data, "deleteToLineStart")) {
if (kb.matches(data, "tui.editor.deleteToLineStart")) {
this.deleteToStartOfLine();
return;
}
if (kb.matches(data, "deleteWordBackward")) {
if (kb.matches(data, "tui.editor.deleteWordBackward")) {
this.deleteWordBackwards();
return;
}
if (kb.matches(data, "deleteWordForward")) {
if (kb.matches(data, "tui.editor.deleteWordForward")) {
this.deleteWordForward();
return;
}
if (kb.matches(data, "deleteCharBackward") || matchesKey(data, "shift+backspace")) {
if (kb.matches(data, "tui.editor.deleteCharBackward") || matchesKey(data, "shift+backspace")) {
this.handleBackspace();
return;
}
if (kb.matches(data, "deleteCharForward") || matchesKey(data, "shift+delete")) {
if (kb.matches(data, "tui.editor.deleteCharForward") || matchesKey(data, "shift+delete")) {
this.handleForwardDelete();
return;
}
// Kill ring actions
if (kb.matches(data, "yank")) {
if (kb.matches(data, "tui.editor.yank")) {
this.yank();
return;
}
if (kb.matches(data, "yankPop")) {
if (kb.matches(data, "tui.editor.yankPop")) {
this.yankPop();
return;
}
// Cursor movement actions
if (kb.matches(data, "cursorLineStart")) {
if (kb.matches(data, "tui.editor.cursorLineStart")) {
this.moveToLineStart();
return;
}
if (kb.matches(data, "cursorLineEnd")) {
if (kb.matches(data, "tui.editor.cursorLineEnd")) {
this.moveToLineEnd();
return;
}
if (kb.matches(data, "cursorWordLeft")) {
if (kb.matches(data, "tui.editor.cursorWordLeft")) {
this.moveWordBackwards();
return;
}
if (kb.matches(data, "cursorWordRight")) {
if (kb.matches(data, "tui.editor.cursorWordRight")) {
this.moveWordForwards();
return;
}
// New line
if (
kb.matches(data, "newLine") ||
kb.matches(data, "tui.input.newLine") ||
(data.charCodeAt(0) === 10 && data.length > 1) ||
data === "\x1b\r" ||
data === "\x1b[13;2~" ||
@@ -722,7 +722,7 @@ export class Editor implements Component, Focusable {
}
// Submit (Enter)
if (kb.matches(data, "submit")) {
if (kb.matches(data, "tui.input.submit")) {
if (this.disableSubmit) return;
// Workaround for terminals without Shift+Enter support:
@@ -739,7 +739,7 @@ export class Editor implements Component, Focusable {
}
// Arrow key navigation (with history support)
if (kb.matches(data, "cursorUp")) {
if (kb.matches(data, "tui.editor.cursorUp")) {
if (this.isEditorEmpty()) {
this.navigateHistory(-1);
} else if (this.historyIndex > -1 && this.isOnFirstVisualLine()) {
@@ -752,7 +752,7 @@ export class Editor implements Component, Focusable {
}
return;
}
if (kb.matches(data, "cursorDown")) {
if (kb.matches(data, "tui.editor.cursorDown")) {
if (this.historyIndex > -1 && this.isOnLastVisualLine()) {
this.navigateHistory(1);
} else if (this.isOnLastVisualLine()) {
@@ -763,31 +763,31 @@ export class Editor implements Component, Focusable {
}
return;
}
if (kb.matches(data, "cursorRight")) {
if (kb.matches(data, "tui.editor.cursorRight")) {
this.moveCursor(0, 1);
return;
}
if (kb.matches(data, "cursorLeft")) {
if (kb.matches(data, "tui.editor.cursorLeft")) {
this.moveCursor(0, -1);
return;
}
// Page up/down - scroll by page and move cursor
if (kb.matches(data, "pageUp")) {
if (kb.matches(data, "tui.editor.pageUp")) {
this.pageScroll(-1);
return;
}
if (kb.matches(data, "pageDown")) {
if (kb.matches(data, "tui.editor.pageDown")) {
this.pageScroll(1);
return;
}
// Character jump mode triggers
if (kb.matches(data, "jumpForward")) {
if (kb.matches(data, "tui.editor.jumpForward")) {
this.jumpMode = "forward";
return;
}
if (kb.matches(data, "jumpBackward")) {
if (kb.matches(data, "tui.editor.jumpBackward")) {
this.jumpMode = "backward";
return;
}
@@ -1149,10 +1149,10 @@ export class Editor implements Component, Focusable {
}
}
private shouldSubmitOnBackslashEnter(data: string, kb: ReturnType<typeof getEditorKeybindings>): boolean {
private shouldSubmitOnBackslashEnter(data: string, kb: ReturnType<typeof getKeybindings>): boolean {
if (this.disableSubmit) return false;
if (!matchesKey(data, "enter")) return false;
const submitKeys = kb.getKeys("submit");
const submitKeys = kb.getKeys("tui.input.submit");
const hasShiftEnter = submitKeys.includes("shift+enter") || submitKeys.includes("shift+return");
if (!hasShiftEnter) return false;

View File

@@ -1,4 +1,4 @@
import { getEditorKeybindings } from "../keybindings.js";
import { getKeybindings } from "../keybindings.js";
import { decodeKittyPrintable } from "../keys.js";
import { KillRing } from "../kill-ring.js";
import { type Component, CURSOR_MARKER, type Focusable } from "../tui.js";
@@ -82,69 +82,69 @@ export class Input implements Component, Focusable {
return;
}
const kb = getEditorKeybindings();
const kb = getKeybindings();
// Escape/Cancel
if (kb.matches(data, "selectCancel")) {
if (kb.matches(data, "tui.select.cancel")) {
if (this.onEscape) this.onEscape();
return;
}
// Undo
if (kb.matches(data, "undo")) {
if (kb.matches(data, "tui.editor.undo")) {
this.undo();
return;
}
// Submit
if (kb.matches(data, "submit") || data === "\n") {
if (kb.matches(data, "tui.input.submit") || data === "\n") {
if (this.onSubmit) this.onSubmit(this.value);
return;
}
// Deletion
if (kb.matches(data, "deleteCharBackward")) {
if (kb.matches(data, "tui.editor.deleteCharBackward")) {
this.handleBackspace();
return;
}
if (kb.matches(data, "deleteCharForward")) {
if (kb.matches(data, "tui.editor.deleteCharForward")) {
this.handleForwardDelete();
return;
}
if (kb.matches(data, "deleteWordBackward")) {
if (kb.matches(data, "tui.editor.deleteWordBackward")) {
this.deleteWordBackwards();
return;
}
if (kb.matches(data, "deleteWordForward")) {
if (kb.matches(data, "tui.editor.deleteWordForward")) {
this.deleteWordForward();
return;
}
if (kb.matches(data, "deleteToLineStart")) {
if (kb.matches(data, "tui.editor.deleteToLineStart")) {
this.deleteToLineStart();
return;
}
if (kb.matches(data, "deleteToLineEnd")) {
if (kb.matches(data, "tui.editor.deleteToLineEnd")) {
this.deleteToLineEnd();
return;
}
// Kill ring actions
if (kb.matches(data, "yank")) {
if (kb.matches(data, "tui.editor.yank")) {
this.yank();
return;
}
if (kb.matches(data, "yankPop")) {
if (kb.matches(data, "tui.editor.yankPop")) {
this.yankPop();
return;
}
// Cursor movement
if (kb.matches(data, "cursorLeft")) {
if (kb.matches(data, "tui.editor.cursorLeft")) {
this.lastAction = null;
if (this.cursor > 0) {
const beforeCursor = this.value.slice(0, this.cursor);
@@ -155,7 +155,7 @@ export class Input implements Component, Focusable {
return;
}
if (kb.matches(data, "cursorRight")) {
if (kb.matches(data, "tui.editor.cursorRight")) {
this.lastAction = null;
if (this.cursor < this.value.length) {
const afterCursor = this.value.slice(this.cursor);
@@ -166,24 +166,24 @@ export class Input implements Component, Focusable {
return;
}
if (kb.matches(data, "cursorLineStart")) {
if (kb.matches(data, "tui.editor.cursorLineStart")) {
this.lastAction = null;
this.cursor = 0;
return;
}
if (kb.matches(data, "cursorLineEnd")) {
if (kb.matches(data, "tui.editor.cursorLineEnd")) {
this.lastAction = null;
this.cursor = this.value.length;
return;
}
if (kb.matches(data, "cursorWordLeft")) {
if (kb.matches(data, "tui.editor.cursorWordLeft")) {
this.moveWordBackwards();
return;
}
if (kb.matches(data, "cursorWordRight")) {
if (kb.matches(data, "tui.editor.cursorWordRight")) {
this.moveWordForwards();
return;
}

View File

@@ -1,4 +1,4 @@
import { getEditorKeybindings } from "../keybindings.js";
import { getKeybindings } from "../keybindings.js";
import type { Component } from "../tui.js";
import { truncateToWidth, visibleWidth } from "../utils.js";
@@ -110,26 +110,26 @@ export class SelectList implements Component {
}
handleInput(keyData: string): void {
const kb = getEditorKeybindings();
const kb = getKeybindings();
// Up arrow - wrap to bottom when at top
if (kb.matches(keyData, "selectUp")) {
if (kb.matches(keyData, "tui.select.up")) {
this.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;
this.notifySelectionChange();
}
// Down arrow - wrap to top when at bottom
else if (kb.matches(keyData, "selectDown")) {
else if (kb.matches(keyData, "tui.select.down")) {
this.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;
this.notifySelectionChange();
}
// Enter
else if (kb.matches(keyData, "selectConfirm")) {
else if (kb.matches(keyData, "tui.select.confirm")) {
const selectedItem = this.filteredItems[this.selectedIndex];
if (selectedItem && this.onSelect) {
this.onSelect(selectedItem);
}
}
// Escape or Ctrl+C
else if (kb.matches(keyData, "selectCancel")) {
else if (kb.matches(keyData, "tui.select.cancel")) {
if (this.onCancel) {
this.onCancel();
}

View File

@@ -1,5 +1,5 @@
import { fuzzyFilter } from "../fuzzy.js";
import { getEditorKeybindings } from "../keybindings.js";
import { getKeybindings } from "../keybindings.js";
import type { Component } from "../tui.js";
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils.js";
import { Input } from "./input.js";
@@ -174,17 +174,17 @@ export class SettingsList implements Component {
}
// Main list input handling
const kb = getEditorKeybindings();
const kb = getKeybindings();
const displayItems = this.searchEnabled ? this.filteredItems : this.items;
if (kb.matches(data, "selectUp")) {
if (kb.matches(data, "tui.select.up")) {
if (displayItems.length === 0) return;
this.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;
} else if (kb.matches(data, "selectDown")) {
} else if (kb.matches(data, "tui.select.down")) {
if (displayItems.length === 0) return;
this.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;
} else if (kb.matches(data, "selectConfirm") || data === " ") {
} else if (kb.matches(data, "tui.select.confirm") || data === " ") {
this.activateItem();
} else if (kb.matches(data, "selectCancel")) {
} else if (kb.matches(data, "tui.select.cancel")) {
this.onCancel();
} else if (this.searchEnabled && this.searchInput) {
const sanitized = data.replace(/ /g, "");