cliedit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 CodeTease
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # cliedit
2
+
3
+ A lightweight, zero-dependency (other than `keypress`), raw-mode terminal editor component for Node.js.
4
+
5
+ `cliedit` is designed to be imported into your own CLI application to provide a full-featured, TTY-based text editing experience. It's perfect for applications that need to ask the user for multi-line input, edit configuration files, or write commit messages.
6
+
7
+ It includes line wrapping, visual navigation, undo/redo, text selection, and clipboard support.
8
+
9
+ ## Features
10
+
11
+ - **Raw Mode TTY:** Takes over the terminal for a full "app-like" feel.
12
+ - **Visual Line Wrapping:** Text wraps to fit the terminal width.
13
+ - **Visual Navigation:** `Up`/`Down` arrows move by visual rows, not logical lines.
14
+ - **Undo/Redo:** `Ctrl+Z` / `Ctrl+Y` for persistent history.
15
+ - **Text Selection:** `Ctrl+Arrow` keys to select text.
16
+ - **Clipboard Support:** `Ctrl+C` (Copy), `Ctrl+X` (Cut), `Ctrl+V` (Paste) for system clipboard (macOS/Windows).
17
+ - **File I/O:** Loads from and saves to the filesystem.
18
+ - **Search:** `Ctrl+W` to find text.
19
+
20
+ ## Installation
21
+ ```bash
22
+ npm install cliedit
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ The package exports an `async` function `openEditor` that returns a `Promise`. The promise resolves when the user quits the editor.
28
+ ```javascript
29
+ import { openEditor } from 'cliedit';
30
+ import path from 'path';
31
+
32
+ async function getCommitMessage() {
33
+ const tempFile = path.resolve(process.cwd(), 'COMMIT_MSG.txt');
34
+ console.log('Opening editor for commit message...');
35
+
36
+ try {
37
+ const result = await openEditor(tempFile);
38
+
39
+ // Give the terminal a moment to restore
40
+ await new Promise(res => setTimeout(res, 50));
41
+
42
+ if (result.saved) {
43
+ console.log('Message saved!');
44
+ console.log('---------------------');
45
+ console.log(result.content);
46
+ console.log('---------------------');
47
+ } else {
48
+ console.log('Editor quit without saving.');
49
+ }
50
+ } catch (err) {
51
+ console.error('Editor failed to start:', err);
52
+ }
53
+ }
54
+
55
+ getCommitMessage();
56
+ ```
57
+
58
+ ## Public API
59
+
60
+ `openEditor(filepath: string)`
61
+
62
+ Opens the editor for the specified file. If the file doesn't exist, it will be created upon saving.
63
+ - **Returns:** `Promise<{ saved: boolean; content: string }>`
64
+ * `saved`: `true` if the user saved (Ctrl+S), `false` otherwise (Ctrl+Q).
65
+ * `content`: The final content of the file as a string.
66
+
67
+ `CliEditor`
68
+
69
+ The main editor class. You can import this directly if you need to extend or instantiate the editor with custom logic.
70
+ ```javascript
71
+ import { CliEditor } from 'cliedit';
72
+ ```
73
+
74
+ ### Types
75
+
76
+ Key types are also exported for convenience:
77
+ ```javascript
78
+ import type {
79
+ DocumentState,
80
+ VisualRow,
81
+ EditorMode,
82
+ NormalizedRange,
83
+ } from 'cliedit';
84
+ ```
85
+
86
+ ## License
87
+
88
+ [MIT](LICENSE)
@@ -0,0 +1,47 @@
1
+ /**
2
+ * ANSI escape codes for terminal manipulation.
3
+ */
4
+ export declare const ANSI: {
5
+ CLEAR_SCREEN: string;
6
+ CLEAR_LINE: string;
7
+ MOVE_CURSOR_TOP_LEFT: string;
8
+ HIDE_CURSOR: string;
9
+ SHOW_CURSOR: string;
10
+ INVERT_COLORS: string;
11
+ RESET_COLORS: string;
12
+ ENTER_ALTERNATE_SCREEN: string;
13
+ EXIT_ALTERNATE_SCREEN: string;
14
+ };
15
+ /**
16
+ * Key definitions for special keypresses (using Ctrl+ keys for reliable detection).
17
+ */
18
+ export declare const KEYS: {
19
+ CTRL_C: string;
20
+ CTRL_Q: string;
21
+ CTRL_S: string;
22
+ CTRL_W: string;
23
+ CTRL_G: string;
24
+ CTRL_Z: string;
25
+ CTRL_Y: string;
26
+ CTRL_K: string;
27
+ CTRL_U: string;
28
+ CTRL_X: string;
29
+ CTRL_V: string;
30
+ CTRL_ARROW_UP: string;
31
+ CTRL_ARROW_DOWN: string;
32
+ CTRL_ARROW_RIGHT: string;
33
+ CTRL_ARROW_LEFT: string;
34
+ ENTER: string;
35
+ BACKSPACE: string;
36
+ ESCAPE: string;
37
+ ARROW_UP: string;
38
+ ARROW_DOWN: string;
39
+ ARROW_RIGHT: string;
40
+ ARROW_LEFT: string;
41
+ DELETE: string;
42
+ HOME: string;
43
+ END: string;
44
+ PAGE_UP: string;
45
+ PAGE_DOWN: string;
46
+ TAB: string;
47
+ };
@@ -0,0 +1,52 @@
1
+ // src/constants.ts
2
+ /**
3
+ * ANSI escape codes for terminal manipulation.
4
+ */
5
+ export const ANSI = {
6
+ CLEAR_SCREEN: '\x1b[2J', // Clear entire screen
7
+ CLEAR_LINE: '\x1b[K', // Clear from cursor to end of line
8
+ MOVE_CURSOR_TOP_LEFT: '\x1b[H', // Move cursor to top-left (1;1)
9
+ HIDE_CURSOR: '\x1b[?25l', // Hide cursor
10
+ SHOW_CURSOR: '\x1b[?25h', // Show cursor
11
+ INVERT_COLORS: '\x1b[7m', // Invert background/foreground colors
12
+ RESET_COLORS: '\x1b[0m', // Reset colors
13
+ ENTER_ALTERNATE_SCREEN: '\x1b[?1049h', // Enter alternate screen
14
+ EXIT_ALTERNATE_SCREEN: '\x1b[?1049l', // Exit alternate screen
15
+ };
16
+ /**
17
+ * Key definitions for special keypresses (using Ctrl+ keys for reliable detection).
18
+ */
19
+ export const KEYS = {
20
+ // Control Sequences
21
+ CTRL_C: '\x03', // Copy/Quit (contextual)
22
+ CTRL_Q: '\x11', // Quit
23
+ CTRL_S: '\x13', // Save
24
+ CTRL_W: '\x17', // Find (Where is)
25
+ CTRL_G: '\x07', // Go to next
26
+ CTRL_Z: '\x1a', // Undo
27
+ CTRL_Y: '\x19', // Redo
28
+ CTRL_K: '\x0b', // Cut/Kill line
29
+ CTRL_U: '\x15', // Paste/Un-kill
30
+ CTRL_X: '\x18', // Cut Selection
31
+ CTRL_V: '\x16', // Paste Selection
32
+ // Selection Keys (Mapped to Ctrl+Arrow for reliable detection)
33
+ CTRL_ARROW_UP: 'C-up',
34
+ CTRL_ARROW_DOWN: 'C-down',
35
+ CTRL_ARROW_RIGHT: 'C-right',
36
+ CTRL_ARROW_LEFT: 'C-left',
37
+ // Standard Keys
38
+ ENTER: '\r', // Carriage Return
39
+ BACKSPACE: '\x7f', // DEL (usually Backspace)
40
+ ESCAPE: '\x1b',
41
+ // Escape sequences for navigation keys (used by keypress.name)
42
+ ARROW_UP: 'up',
43
+ ARROW_DOWN: 'down',
44
+ ARROW_RIGHT: 'right',
45
+ ARROW_LEFT: 'left',
46
+ DELETE: 'delete',
47
+ HOME: 'home',
48
+ END: 'end',
49
+ PAGE_UP: 'pageup',
50
+ PAGE_DOWN: 'pagedown',
51
+ TAB: '\t',
52
+ };
@@ -0,0 +1,35 @@
1
+ import { CliEditor } from './editor.js';
2
+ /**
3
+ * Methods related to system clipboard interaction (Copy/Paste/Cut).
4
+ */
5
+ declare function setClipboard(this: CliEditor, text: string): Promise<void>;
6
+ declare function getClipboard(this: CliEditor): Promise<string>;
7
+ /**
8
+ * Cuts the current line and copies it to the clipboard.
9
+ * Falls back to line-based behavior if no selection.
10
+ */
11
+ declare function cutLine(this: CliEditor): Promise<void>;
12
+ /**
13
+ * Pastes the clipboard content at the cursor position.
14
+ * Handles single-line and multi-line pastes.
15
+ */
16
+ declare function pasteLine(this: CliEditor): Promise<void>;
17
+ /**
18
+ * Cuts the current selection to the clipboard. Returns a promise.
19
+ * Renamed to avoid collision with command handler in editor.keys.ts
20
+ */
21
+ declare function cutSelectionAsync(this: CliEditor): Promise<void>;
22
+ /**
23
+ * Pastes clipboard content at the cursor position.
24
+ * Always deletes selection if one exists.
25
+ */
26
+ declare function pasteSelection(this: CliEditor): Promise<void>;
27
+ export declare const clipboardMethods: {
28
+ setClipboard: typeof setClipboard;
29
+ getClipboard: typeof getClipboard;
30
+ cutLine: typeof cutLine;
31
+ pasteLine: typeof pasteLine;
32
+ cutSelectionAsync: typeof cutSelectionAsync;
33
+ pasteSelection: typeof pasteSelection;
34
+ };
35
+ export {};
@@ -0,0 +1,129 @@
1
+ // src/editor.clipboard.ts
2
+ import { platform } from 'os';
3
+ import { exec } from 'child_process';
4
+ /**
5
+ * Methods related to system clipboard interaction (Copy/Paste/Cut).
6
+ */
7
+ // --- Helper Functions (Clipboard Access) ---
8
+ function setClipboard(text) {
9
+ return new Promise((resolve, reject) => {
10
+ let command;
11
+ switch (platform()) {
12
+ case 'darwin':
13
+ command = 'pbcopy';
14
+ break;
15
+ case 'win32':
16
+ command = 'clip';
17
+ break;
18
+ default:
19
+ this.setStatusMessage('Clipboard only supported on macOS/Windows for now');
20
+ return resolve();
21
+ }
22
+ const process = exec(command, (error) => {
23
+ if (error) {
24
+ this.setStatusMessage(`Clipboard error: ${error.message}`);
25
+ return reject(error);
26
+ }
27
+ this.setStatusMessage('Text copied to clipboard', 1000);
28
+ resolve();
29
+ });
30
+ if (process.stdin) {
31
+ process.stdin.write(text);
32
+ process.stdin.end();
33
+ }
34
+ });
35
+ }
36
+ function getClipboard() {
37
+ return new Promise((resolve, reject) => {
38
+ let command;
39
+ switch (platform()) {
40
+ case 'darwin':
41
+ command = 'pbpaste';
42
+ break;
43
+ case 'win32':
44
+ command = 'powershell -command "Get-Clipboard"';
45
+ break;
46
+ default:
47
+ this.setStatusMessage('Clipboard only supported on macOS/Windows for now');
48
+ return resolve('');
49
+ }
50
+ exec(command, (error, stdout) => {
51
+ if (error) {
52
+ this.setStatusMessage(`Paste error: ${error.message}`);
53
+ return reject(error);
54
+ }
55
+ resolve(stdout);
56
+ });
57
+ });
58
+ }
59
+ // --- Clipboard Actions ---
60
+ /**
61
+ * Cuts the current line and copies it to the clipboard.
62
+ * Falls back to line-based behavior if no selection.
63
+ */
64
+ async function cutLine() {
65
+ if (this.lines.length > 1 || (this.lines.length === 1 && this.lines[0] !== '')) {
66
+ const lineToCut = this.lines[this.cursorY];
67
+ await this.setClipboard(lineToCut);
68
+ this.lines.splice(this.cursorY, 1);
69
+ if (this.cursorY >= this.lines.length) {
70
+ this.cursorY = Math.max(0, this.lines.length - 1);
71
+ }
72
+ if (this.lines.length === 0) {
73
+ this.lines = [''];
74
+ }
75
+ this.cursorX = 0;
76
+ this.setDirty();
77
+ this.setStatusMessage('Line cut to clipboard', 1000);
78
+ }
79
+ }
80
+ /**
81
+ * Pastes the clipboard content at the cursor position.
82
+ * Handles single-line and multi-line pastes.
83
+ */
84
+ async function pasteLine() {
85
+ try {
86
+ const textToPaste = await this.getClipboard();
87
+ if (!textToPaste)
88
+ return;
89
+ const pasteLines = textToPaste.split(/\r?\n/);
90
+ if (this.selectionAnchor) {
91
+ this.deleteSelectedText();
92
+ }
93
+ this.insertContentAtCursor(pasteLines);
94
+ }
95
+ catch (error) {
96
+ this.setStatusMessage(`Paste failed: ${error.message}`);
97
+ }
98
+ }
99
+ /**
100
+ * Cuts the current selection to the clipboard. Returns a promise.
101
+ * Renamed to avoid collision with command handler in editor.keys.ts
102
+ */
103
+ async function cutSelectionAsync() {
104
+ if (!this.selectionAnchor) {
105
+ await this.cutLine();
106
+ }
107
+ else {
108
+ const textToCut = this.getSelectedText();
109
+ await this.setClipboard(textToCut);
110
+ this.deleteSelectedText();
111
+ this.setDirty();
112
+ this.setStatusMessage('Selection cut!', 1000);
113
+ }
114
+ }
115
+ /**
116
+ * Pastes clipboard content at the cursor position.
117
+ * Always deletes selection if one exists.
118
+ */
119
+ async function pasteSelection() {
120
+ await this.pasteLine();
121
+ }
122
+ export const clipboardMethods = {
123
+ setClipboard,
124
+ getClipboard,
125
+ cutLine,
126
+ pasteLine,
127
+ cutSelectionAsync, // Renamed
128
+ pasteSelection,
129
+ };
@@ -0,0 +1,78 @@
1
+ import { HistoryManager } from './history.js';
2
+ import { VisualRow, EditorMode } from './types.js';
3
+ declare module 'keypress' {
4
+ interface KeypressEvent {
5
+ name?: string;
6
+ ctrl: boolean;
7
+ meta: boolean;
8
+ shift: boolean;
9
+ sequence: string;
10
+ }
11
+ }
12
+ import { editingMethods } from './editor.editing.js';
13
+ import { clipboardMethods } from './editor.clipboard.js';
14
+ import { navigationMethods } from './editor.navigation.js';
15
+ import { renderingMethods } from './editor.rendering.js';
16
+ import { searchMethods } from './editor.search.js';
17
+ import { historyMethods } from './editor.history.js';
18
+ import { ioMethods } from './editor.io.js';
19
+ import { TKeyHandlingMethods } from './editor.keys.js';
20
+ import { TSelectionMethods } from './editor.selection.js';
21
+ type TEditingMethods = typeof editingMethods;
22
+ type TClipboardMethods = typeof clipboardMethods;
23
+ type TNavigationMethods = typeof navigationMethods;
24
+ type TRenderingMethods = typeof renderingMethods;
25
+ type TSearchMethods = typeof searchMethods;
26
+ type THistoryMethods = typeof historyMethods;
27
+ type TIOMethods = typeof ioMethods;
28
+ export interface CliEditor extends TEditingMethods, TClipboardMethods, TNavigationMethods, TRenderingMethods, TSearchMethods, THistoryMethods, TIOMethods, TKeyHandlingMethods, TSelectionMethods {
29
+ }
30
+ /**
31
+ * Main editor class managing application state, TTY interaction, and rendering.
32
+ */
33
+ export declare class CliEditor {
34
+ lines: string[];
35
+ filepath: string;
36
+ isDirty: boolean;
37
+ cursorX: number;
38
+ cursorY: number;
39
+ selectionAnchor: {
40
+ x: number;
41
+ y: number;
42
+ } | null;
43
+ rowOffset: number;
44
+ screenRows: number;
45
+ screenCols: number;
46
+ gutterWidth: number;
47
+ screenStartRow: number;
48
+ visualRows: VisualRow[];
49
+ mode: EditorMode;
50
+ statusMessage: string;
51
+ statusTimeout: NodeJS.Timeout | null;
52
+ isMessageCustom: boolean;
53
+ quitConfirm: boolean;
54
+ readonly DEFAULT_STATUS = "HELP: Ctrl+S = Save & Quit | Ctrl+Q = Quit | Ctrl+C = Copy All | Ctrl+Arrow = Select";
55
+ searchQuery: string;
56
+ searchResults: {
57
+ y: number;
58
+ x: number;
59
+ }[];
60
+ searchResultIndex: number;
61
+ history: HistoryManager;
62
+ isCleanedUp: boolean;
63
+ resolvePromise: ((value: {
64
+ saved: boolean;
65
+ content: string;
66
+ }) => void) | null;
67
+ rejectPromise: ((reason?: any) => void) | null;
68
+ isExiting: boolean;
69
+ constructor(initialContent: string, filepath: string);
70
+ run(): Promise<{
71
+ saved: boolean;
72
+ content: string;
73
+ }>;
74
+ private setupTerminal;
75
+ private handleResize;
76
+ private updateScreenSize;
77
+ }
78
+ export {};
@@ -0,0 +1,39 @@
1
+ import { CliEditor } from './editor.js';
2
+ /**
3
+ * Core methods for editing the document content (insert, delete, split/join lines).
4
+ */
5
+ /**
6
+ * Inserts content (potentially multi-line) at the current cursor position.
7
+ * Note: This function ASSUMES the selection has already been deleted.
8
+ * @param contentLines An array of strings to insert.
9
+ */
10
+ declare function insertContentAtCursor(this: CliEditor, contentLines: string[]): void;
11
+ /**
12
+ * Inserts a single character at the cursor position.
13
+ */
14
+ declare function insertCharacter(this: CliEditor, char: string): void;
15
+ /**
16
+ * Inserts a soft tab (4 spaces).
17
+ */
18
+ declare function insertSoftTab(this: CliEditor): void;
19
+ /**
20
+ * Inserts a new line, splitting the current line at the cursor position.
21
+ */
22
+ declare function insertNewLine(this: CliEditor): void;
23
+ /**
24
+ * Deletes the character before the cursor, or joins the current line with the previous one.
25
+ */
26
+ declare function deleteBackward(this: CliEditor): void;
27
+ /**
28
+ * Deletes the character after the cursor, or joins the current line with the next one.
29
+ */
30
+ declare function deleteForward(this: CliEditor): void;
31
+ export declare const editingMethods: {
32
+ insertContentAtCursor: typeof insertContentAtCursor;
33
+ insertCharacter: typeof insertCharacter;
34
+ insertSoftTab: typeof insertSoftTab;
35
+ insertNewLine: typeof insertNewLine;
36
+ deleteBackward: typeof deleteBackward;
37
+ deleteForward: typeof deleteForward;
38
+ };
39
+ export {};
@@ -0,0 +1,115 @@
1
+ // src/editor.editing.ts
2
+ /**
3
+ * Core methods for editing the document content (insert, delete, split/join lines).
4
+ */
5
+ /**
6
+ * Inserts content (potentially multi-line) at the current cursor position.
7
+ * Note: This function ASSUMES the selection has already been deleted.
8
+ * @param contentLines An array of strings to insert.
9
+ */
10
+ function insertContentAtCursor(contentLines) {
11
+ const pasteLength = contentLines.length;
12
+ if (pasteLength === 1) {
13
+ const line = this.lines[this.cursorY] || '';
14
+ this.lines[this.cursorY] = line.slice(0, this.cursorX) + contentLines[0] + line.slice(this.cursorX);
15
+ this.cursorX += contentLines[0].length;
16
+ }
17
+ else {
18
+ // Multi-line paste logic
19
+ const line = this.lines[this.cursorY] || '';
20
+ const remainder = line.slice(this.cursorX);
21
+ // 1. Update current line with the first part of the paste
22
+ this.lines[this.cursorY] = line.slice(0, this.cursorX) + contentLines[0];
23
+ const linesToInsert = contentLines.slice(1, -1);
24
+ const lastPasteLine = contentLines[contentLines.length - 1];
25
+ // 2. Insert middle lines
26
+ if (linesToInsert.length > 0) {
27
+ this.lines.splice(this.cursorY + 1, 0, ...linesToInsert);
28
+ }
29
+ // 3. Insert last line and append remainder
30
+ this.lines.splice(this.cursorY + 1 + linesToInsert.length, 0, lastPasteLine + remainder);
31
+ // 4. Update cursor position
32
+ this.cursorY += pasteLength - 1;
33
+ this.cursorX = lastPasteLine.length;
34
+ }
35
+ this.setDirty();
36
+ this.recalculateVisualRows();
37
+ }
38
+ /**
39
+ * Inserts a single character at the cursor position.
40
+ */
41
+ function insertCharacter(char) {
42
+ const line = this.lines[this.cursorY] || '';
43
+ this.lines[this.cursorY] = line.slice(0, this.cursorX) + char + line.slice(this.cursorX);
44
+ this.cursorX += char.length;
45
+ }
46
+ /**
47
+ * Inserts a soft tab (4 spaces).
48
+ */
49
+ function insertSoftTab() {
50
+ this.insertCharacter(' ');
51
+ }
52
+ /**
53
+ * Inserts a new line, splitting the current line at the cursor position.
54
+ */
55
+ function insertNewLine() {
56
+ const line = this.lines[this.cursorY] || '';
57
+ const remainder = line.slice(this.cursorX);
58
+ this.lines[this.cursorY] = line.slice(0, this.cursorX);
59
+ this.lines.splice(this.cursorY + 1, 0, remainder);
60
+ this.cursorY++;
61
+ this.cursorX = 0;
62
+ this.setDirty();
63
+ }
64
+ /**
65
+ * Deletes the character before the cursor, or joins the current line with the previous one.
66
+ */
67
+ function deleteBackward() {
68
+ if (this.cursorX > 0) {
69
+ const line = this.lines[this.cursorY] || '';
70
+ this.lines[this.cursorY] = line.slice(0, this.cursorX - 1) + line.slice(this.cursorX);
71
+ this.cursorX--;
72
+ }
73
+ else if (this.cursorY > 0) {
74
+ const currentLine = this.lines[this.cursorY];
75
+ const previousLine = this.lines[this.cursorY - 1];
76
+ this.cursorX = previousLine.length;
77
+ this.lines[this.cursorY - 1] = previousLine + currentLine;
78
+ this.lines.splice(this.cursorY, 1);
79
+ this.cursorY--;
80
+ }
81
+ if (this.lines.length === 0) {
82
+ this.lines = [''];
83
+ this.cursorY = 0;
84
+ this.cursorX = 0;
85
+ }
86
+ this.setDirty();
87
+ }
88
+ /**
89
+ * Deletes the character after the cursor, or joins the current line with the next one.
90
+ */
91
+ function deleteForward() {
92
+ const line = this.lines[this.cursorY] || '';
93
+ if (this.cursorX < line.length) {
94
+ this.lines[this.cursorY] = line.slice(0, this.cursorX) + line.slice(this.cursorX + 1);
95
+ }
96
+ else if (this.cursorY < this.lines.length - 1) {
97
+ const nextLine = this.lines[this.cursorY + 1];
98
+ this.lines[this.cursorY] = line + nextLine;
99
+ this.lines.splice(this.cursorY + 1, 1);
100
+ }
101
+ if (this.lines.length === 0) {
102
+ this.lines = [''];
103
+ this.cursorY = 0;
104
+ this.cursorX = 0;
105
+ }
106
+ this.setDirty();
107
+ }
108
+ export const editingMethods = {
109
+ insertContentAtCursor,
110
+ insertCharacter,
111
+ insertSoftTab,
112
+ insertNewLine,
113
+ deleteBackward,
114
+ deleteForward,
115
+ };
@@ -0,0 +1,33 @@
1
+ import { CliEditor } from './editor.js';
2
+ import { DocumentState } from './types.js';
3
+ /**
4
+ * Methods related to Undo/Redo operations.
5
+ */
6
+ /**
7
+ * Gets the current document state (content and cursor position).
8
+ */
9
+ declare function getCurrentState(this: CliEditor): DocumentState;
10
+ /**
11
+ * Saves the current state to the history manager.
12
+ */
13
+ declare function saveState(this: CliEditor, initial?: boolean): void;
14
+ /**
15
+ * Loads a document state from the history manager.
16
+ */
17
+ declare function loadState(this: CliEditor, state: DocumentState): void;
18
+ /**
19
+ * Performs an undo operation.
20
+ */
21
+ declare function undo(this: CliEditor): void;
22
+ /**
23
+ * Performs a redo operation.
24
+ */
25
+ declare function redo(this: CliEditor): void;
26
+ export declare const historyMethods: {
27
+ getCurrentState: typeof getCurrentState;
28
+ saveState: typeof saveState;
29
+ loadState: typeof loadState;
30
+ undo: typeof undo;
31
+ redo: typeof redo;
32
+ };
33
+ export {};