mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 05:02:14 +00:00
The Editor component now accepts TUI as the first constructor parameter, enabling it to query terminal dimensions. When content exceeds available height, the editor scrolls vertically keeping the cursor visible. Features: - Max editor height is 30% of terminal rows (minimum 5 lines) - Page Up/Down keys scroll by page size - Scroll indicators show lines above/below: ─── ↑ 5 more ─── Breaking change: Editor constructor signature changed from new Editor(theme) to new Editor(tui, theme) fixes #732
155 lines
3.5 KiB
TypeScript
155 lines
3.5 KiB
TypeScript
import { type KeyId, matchesKey } from "./keys.js";
|
|
|
|
/**
|
|
* Editor actions that can be bound to keys.
|
|
*/
|
|
export type EditorAction =
|
|
// Cursor movement
|
|
| "cursorUp"
|
|
| "cursorDown"
|
|
| "cursorLeft"
|
|
| "cursorRight"
|
|
| "cursorWordLeft"
|
|
| "cursorWordRight"
|
|
| "cursorLineStart"
|
|
| "cursorLineEnd"
|
|
| "pageUp"
|
|
| "pageDown"
|
|
// Deletion
|
|
| "deleteCharBackward"
|
|
| "deleteCharForward"
|
|
| "deleteWordBackward"
|
|
| "deleteToLineStart"
|
|
| "deleteToLineEnd"
|
|
// Text input
|
|
| "newLine"
|
|
| "submit"
|
|
| "tab"
|
|
// Selection/autocomplete
|
|
| "selectUp"
|
|
| "selectDown"
|
|
| "selectPageUp"
|
|
| "selectPageDown"
|
|
| "selectConfirm"
|
|
| "selectCancel"
|
|
// Clipboard
|
|
| "copy"
|
|
// Tool output
|
|
| "expandTools";
|
|
|
|
// Re-export KeyId from keys.ts
|
|
export type { KeyId };
|
|
|
|
/**
|
|
* Editor keybindings configuration.
|
|
*/
|
|
export type EditorKeybindingsConfig = {
|
|
[K in EditorAction]?: KeyId | KeyId[];
|
|
};
|
|
|
|
/**
|
|
* Default editor keybindings.
|
|
*/
|
|
export const DEFAULT_EDITOR_KEYBINDINGS: Required<EditorKeybindingsConfig> = {
|
|
// Cursor movement
|
|
cursorUp: "up",
|
|
cursorDown: "down",
|
|
cursorLeft: "left",
|
|
cursorRight: "right",
|
|
cursorWordLeft: ["alt+left", "ctrl+left"],
|
|
cursorWordRight: ["alt+right", "ctrl+right"],
|
|
cursorLineStart: ["home", "ctrl+a"],
|
|
cursorLineEnd: ["end", "ctrl+e"],
|
|
pageUp: "pageUp",
|
|
pageDown: "pageDown",
|
|
// Deletion
|
|
deleteCharBackward: "backspace",
|
|
deleteCharForward: "delete",
|
|
deleteWordBackward: ["ctrl+w", "alt+backspace"],
|
|
deleteToLineStart: "ctrl+u",
|
|
deleteToLineEnd: "ctrl+k",
|
|
// Text input
|
|
newLine: "shift+enter",
|
|
submit: "enter",
|
|
tab: "tab",
|
|
// Selection/autocomplete
|
|
selectUp: "up",
|
|
selectDown: "down",
|
|
selectPageUp: "pageUp",
|
|
selectPageDown: "pageDown",
|
|
selectConfirm: "enter",
|
|
selectCancel: ["escape", "ctrl+c"],
|
|
// Clipboard
|
|
copy: "ctrl+c",
|
|
// Tool output
|
|
expandTools: "ctrl+o",
|
|
};
|
|
|
|
/**
|
|
* Manages keybindings for the editor.
|
|
*/
|
|
export class EditorKeybindingsManager {
|
|
private actionToKeys: Map<EditorAction, KeyId[]>;
|
|
|
|
constructor(config: EditorKeybindingsConfig = {}) {
|
|
this.actionToKeys = new Map();
|
|
this.buildMaps(config);
|
|
}
|
|
|
|
private buildMaps(config: EditorKeybindingsConfig): void {
|
|
this.actionToKeys.clear();
|
|
|
|
// Start with defaults
|
|
for (const [action, keys] of Object.entries(DEFAULT_EDITOR_KEYBINDINGS)) {
|
|
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
this.actionToKeys.set(action as EditorAction, [...keyArray]);
|
|
}
|
|
|
|
// Override with user config
|
|
for (const [action, keys] of Object.entries(config)) {
|
|
if (keys === undefined) continue;
|
|
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
this.actionToKeys.set(action as EditorAction, keyArray);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if input matches a specific action.
|
|
*/
|
|
matches(data: string, action: EditorAction): boolean {
|
|
const keys = this.actionToKeys.get(action);
|
|
if (!keys) return false;
|
|
for (const key of keys) {
|
|
if (matchesKey(data, key)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get keys bound to an action.
|
|
*/
|
|
getKeys(action: EditorAction): KeyId[] {
|
|
return this.actionToKeys.get(action) ?? [];
|
|
}
|
|
|
|
/**
|
|
* Update configuration.
|
|
*/
|
|
setConfig(config: EditorKeybindingsConfig): void {
|
|
this.buildMaps(config);
|
|
}
|
|
}
|
|
|
|
// Global instance
|
|
let globalEditorKeybindings: EditorKeybindingsManager | null = null;
|
|
|
|
export function getEditorKeybindings(): EditorKeybindingsManager {
|
|
if (!globalEditorKeybindings) {
|
|
globalEditorKeybindings = new EditorKeybindingsManager();
|
|
}
|
|
return globalEditorKeybindings;
|
|
}
|
|
|
|
export function setEditorKeybindings(manager: EditorKeybindingsManager): void {
|
|
globalEditorKeybindings = manager;
|
|
}
|