import { Editor, type EditorTheme, matchesKey } from "@mariozechner/pi-tui"; import type { AppAction, KeybindingsManager } from "../../../core/keybindings.js"; /** * Custom editor that handles app-level keybindings for coding-agent. */ export class CustomEditor extends Editor { private keybindings: KeybindingsManager; public actionHandlers: Map void> = new Map(); // Special handlers that can be dynamically replaced public onEscape?: () => void; public onCtrlD?: () => void; public onPasteImage?: () => void; /** Handler for extension-registered shortcuts. Returns true if handled. */ public onExtensionShortcut?: (data: string) => boolean; constructor(theme: EditorTheme, keybindings: KeybindingsManager) { super(theme); this.keybindings = keybindings; } /** * Register a handler for an app action. */ onAction(action: AppAction, handler: () => void): void { this.actionHandlers.set(action, handler); } handleInput(data: string): void { // Check extension-registered shortcuts first if (this.onExtensionShortcut?.(data)) { return; } // Check for Ctrl+V to handle clipboard image paste if (matchesKey(data, "ctrl+v")) { this.onPasteImage?.(); return; } // Check app keybindings first // Escape/interrupt - only if autocomplete is NOT active if (this.keybindings.matches(data, "interrupt")) { if (!this.isShowingAutocomplete()) { // Use dynamic onEscape if set, otherwise registered handler const handler = this.onEscape ?? this.actionHandlers.get("interrupt"); if (handler) { handler(); return; } } // Let parent handle escape for autocomplete cancellation super.handleInput(data); return; } // Exit (Ctrl+D) - only when editor is empty if (this.keybindings.matches(data, "exit")) { if (this.getText().length === 0) { const handler = this.onCtrlD ?? this.actionHandlers.get("exit"); if (handler) handler(); } return; // Always consume } // Check all other app actions for (const [action, handler] of this.actionHandlers) { if (action !== "interrupt" && action !== "exit" && this.keybindings.matches(data, action)) { handler(); return; } } // Pass to parent for editor handling super.handleInput(data); } }