ink-prompt 0.2.3 → 0.2.5
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/README.md +3 -3
- package/dist/components/MultilineInput/AtomicBlocks.d.ts +10 -20
- package/dist/components/MultilineInput/AtomicBlocks.js +33 -42
- package/dist/components/MultilineInput/BlockMarker.d.ts +19 -0
- package/dist/components/MultilineInput/BlockMarker.js +83 -0
- package/dist/components/MultilineInput/BlockRegistry.d.ts +39 -0
- package/dist/components/MultilineInput/BlockRegistry.js +236 -0
- package/dist/components/MultilineInput/BlockTypes.d.ts +22 -0
- package/dist/components/MultilineInput/ImageTypes.d.ts +0 -2
- package/dist/components/MultilineInput/ImageTypes.js +1 -2
- package/dist/components/MultilineInput/ImageValidator.js +1 -1
- package/dist/components/MultilineInput/KeyHandler.d.ts +1 -1
- package/dist/components/MultilineInput/KeyHandler.js +18 -0
- package/dist/components/MultilineInput/TextBuffer.d.ts +5 -31
- package/dist/components/MultilineInput/TextBuffer.js +91 -161
- package/dist/components/MultilineInput/TextRenderer.d.ts +6 -7
- package/dist/components/MultilineInput/TextRenderer.js +7 -7
- package/dist/components/MultilineInput/__tests__/BlockMarker.test.js +130 -0
- package/dist/components/MultilineInput/__tests__/BlockRegistry.test.js +225 -0
- package/dist/components/MultilineInput/__tests__/KeyHandler.test.js +18 -0
- package/dist/components/MultilineInput/__tests__/Placeholder.integration.test.js +44 -65
- package/dist/components/MultilineInput/__tests__/TextBuffer_images.test.js +10 -31
- package/dist/components/MultilineInput/__tests__/TextRenderer_images.test.js +27 -13
- package/dist/components/MultilineInput/__tests__/integration_images.test.js +2 -4
- package/dist/components/MultilineInput/__tests__/useTextInput_images.test.js +30 -29
- package/dist/components/MultilineInput/index.d.ts +6 -6
- package/dist/components/MultilineInput/index.js +2 -11
- package/dist/components/MultilineInput/types.d.ts +0 -20
- package/dist/components/MultilineInput/useTextInput.d.ts +4 -11
- package/dist/components/MultilineInput/useTextInput.js +79 -76
- package/package.json +1 -1
- package/dist/components/MultilineInput/ImageSentinel.d.ts +0 -15
- package/dist/components/MultilineInput/ImageSentinel.js +0 -62
- package/dist/components/MultilineInput/Placeholder.d.ts +0 -30
- package/dist/components/MultilineInput/Placeholder.js +0 -200
- package/dist/components/MultilineInput/__tests__/ImageSentinel.test.js +0 -154
- package/dist/components/MultilineInput/__tests__/Placeholder.test.js +0 -235
- package/dist/examples/examples/basic.js +0 -9
- package/dist/examples/src/components/MultilineInput/KeyHandler.d.ts +0 -15
- package/dist/examples/src/components/MultilineInput/KeyHandler.js +0 -97
- package/dist/examples/src/components/MultilineInput/TextBuffer.d.ts +0 -34
- package/dist/examples/src/components/MultilineInput/TextBuffer.js +0 -127
- package/dist/examples/src/components/MultilineInput/TextRenderer.d.ts +0 -24
- package/dist/examples/src/components/MultilineInput/TextRenderer.js +0 -72
- package/dist/examples/src/components/MultilineInput/__tests__/KeyHandler.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/KeyHandler.test.js +0 -115
- package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.js +0 -254
- package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.js +0 -176
- package/dist/examples/src/components/MultilineInput/__tests__/integration.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/integration.test.js +0 -71
- package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.js +0 -65
- package/dist/examples/src/components/MultilineInput/index.d.ts +0 -39
- package/dist/examples/src/components/MultilineInput/index.js +0 -82
- package/dist/examples/src/components/MultilineInput/types.d.ts +0 -55
- package/dist/examples/src/components/MultilineInput/types.js +0 -1
- package/dist/examples/src/components/MultilineInput/useTextInput.d.ts +0 -16
- package/dist/examples/src/components/MultilineInput/useTextInput.js +0 -82
- package/dist/examples/src/hello.test.d.ts +0 -1
- package/dist/examples/src/hello.test.js +0 -13
- package/dist/examples/src/index.d.ts +0 -2
- package/dist/examples/src/index.js +0 -2
- /package/dist/components/MultilineInput/{__tests__/ImageSentinel.test.d.ts → BlockTypes.js} +0 -0
- /package/dist/components/MultilineInput/__tests__/{Placeholder.test.d.ts → BlockMarker.test.d.ts} +0 -0
- /package/dist/{examples/examples/basic.d.ts → components/MultilineInput/__tests__/BlockRegistry.test.d.ts} +0 -0
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handles keyboard input and maps it to text input actions.
|
|
3
|
-
*
|
|
4
|
-
* @param key - The Ink key object
|
|
5
|
-
* @param input - The input string (if any)
|
|
6
|
-
* @param buffer - The current text buffer
|
|
7
|
-
* @param actions - The actions available to modify the state
|
|
8
|
-
* @param cursor - The current cursor position (optional, but required for some logic like backslash check)
|
|
9
|
-
*/
|
|
10
|
-
export function handleKey(key, input, buffer, actions, cursor) {
|
|
11
|
-
// Navigation
|
|
12
|
-
if (key.upArrow) {
|
|
13
|
-
actions.moveCursor('up');
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
if (key.downArrow) {
|
|
17
|
-
actions.moveCursor('down');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (key.leftArrow) {
|
|
21
|
-
actions.moveCursor('left');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (key.rightArrow) {
|
|
25
|
-
actions.moveCursor('right');
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
// Home/End (Ink might not provide these directly in all environments, but if it does)
|
|
29
|
-
// We check for 'home' and 'end' properties if they exist on the key object,
|
|
30
|
-
// or specific sequences if we were parsing raw input, but here we assume Ink's Key object.
|
|
31
|
-
// Note: Ink's Key interface might not have home/end in all versions, but we'll assume it does or we extend it.
|
|
32
|
-
// If not, we might need to check specific input sequences, but for now let's trust the test/types.
|
|
33
|
-
if (key.home) {
|
|
34
|
-
actions.moveCursor('lineStart');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (key.end) {
|
|
38
|
-
actions.moveCursor('lineEnd');
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
// History
|
|
42
|
-
if (key.ctrl) {
|
|
43
|
-
if (input === 'z') {
|
|
44
|
-
actions.undo();
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (input === 'y') {
|
|
48
|
-
actions.redo();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (input === 'j') {
|
|
52
|
-
actions.newLine();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// Editing
|
|
57
|
-
if (key.backspace) {
|
|
58
|
-
actions.delete();
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (key.delete) {
|
|
62
|
-
// Currently mapped to delete (backspace behavior) as per requirements/tests,
|
|
63
|
-
// but usually delete is forward.
|
|
64
|
-
// The plan said "Delete (delete at cursor)", which usually means forward.
|
|
65
|
-
// But our useTextInput only has `delete` (which is backspace).
|
|
66
|
-
// For now, we map it to `delete` as per the test "handles Delete".
|
|
67
|
-
actions.delete();
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Submission / New Line
|
|
71
|
-
if (key.return) {
|
|
72
|
-
if (cursor) {
|
|
73
|
-
const currentLine = buffer.lines[cursor.line];
|
|
74
|
-
// Check if line ends with backslash AND cursor is at the end (or we just check the line content?)
|
|
75
|
-
// Requirement: "Line ending with \ + Enter continues to next line"
|
|
76
|
-
// Usually this implies the user typed '\' then Enter.
|
|
77
|
-
// We should probably check if the character *before* the cursor is '\' if we want to be precise,
|
|
78
|
-
// or just if the line ends with '\'.
|
|
79
|
-
// Let's assume "line ends with \" means the last char of the line is '\'.
|
|
80
|
-
if (currentLine.endsWith('\\')) {
|
|
81
|
-
actions.delete(); // Remove the backslash
|
|
82
|
-
actions.newLine(); // Insert newline
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
actions.submit();
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
// Text Insertion
|
|
90
|
-
// Ignore control keys if they don't have a specific handler above
|
|
91
|
-
if (key.ctrl || key.meta) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (input) {
|
|
95
|
-
actions.insert(input);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Buffer, Cursor, Direction } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Create a new buffer from optional initial text
|
|
4
|
-
*/
|
|
5
|
-
export declare function createBuffer(text?: string): Buffer;
|
|
6
|
-
/**
|
|
7
|
-
* Insert a character at the cursor position
|
|
8
|
-
*/
|
|
9
|
-
export declare function insertChar(buffer: Buffer, cursor: Cursor, char: string): {
|
|
10
|
-
buffer: Buffer;
|
|
11
|
-
cursor: Cursor;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* Delete character before cursor (backspace)
|
|
15
|
-
*/
|
|
16
|
-
export declare function deleteChar(buffer: Buffer, cursor: Cursor): {
|
|
17
|
-
buffer: Buffer;
|
|
18
|
-
cursor: Cursor;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Insert a new line at cursor position (splits current line)
|
|
22
|
-
*/
|
|
23
|
-
export declare function insertNewLine(buffer: Buffer, cursor: Cursor): {
|
|
24
|
-
buffer: Buffer;
|
|
25
|
-
cursor: Cursor;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Move cursor in specified direction with bounds checking
|
|
29
|
-
*/
|
|
30
|
-
export declare function moveCursor(buffer: Buffer, cursor: Cursor, direction: Direction): Cursor;
|
|
31
|
-
/**
|
|
32
|
-
* Get the full text content from buffer (lines joined with newlines)
|
|
33
|
-
*/
|
|
34
|
-
export declare function getTextContent(buffer: Buffer): string;
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create a new buffer from optional initial text
|
|
3
|
-
*/
|
|
4
|
-
export function createBuffer(text) {
|
|
5
|
-
if (!text) {
|
|
6
|
-
return { lines: [''] };
|
|
7
|
-
}
|
|
8
|
-
return { lines: text.split('\n') };
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Insert a character at the cursor position
|
|
12
|
-
*/
|
|
13
|
-
export function insertChar(buffer, cursor, char) {
|
|
14
|
-
const { line, column } = cursor;
|
|
15
|
-
const currentLine = buffer.lines[line];
|
|
16
|
-
const newLine = currentLine.slice(0, column) + char + currentLine.slice(column);
|
|
17
|
-
const newLines = [...buffer.lines];
|
|
18
|
-
newLines[line] = newLine;
|
|
19
|
-
return {
|
|
20
|
-
buffer: { lines: newLines },
|
|
21
|
-
cursor: { line, column: column + 1 },
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Delete character before cursor (backspace)
|
|
26
|
-
*/
|
|
27
|
-
export function deleteChar(buffer, cursor) {
|
|
28
|
-
const { line, column } = cursor;
|
|
29
|
-
// At the very start of the buffer - nothing to delete
|
|
30
|
-
if (line === 0 && column === 0) {
|
|
31
|
-
return { buffer, cursor };
|
|
32
|
-
}
|
|
33
|
-
// At the start of a line - merge with previous line
|
|
34
|
-
if (column === 0) {
|
|
35
|
-
const previousLine = buffer.lines[line - 1];
|
|
36
|
-
const currentLine = buffer.lines[line];
|
|
37
|
-
const mergedLine = previousLine + currentLine;
|
|
38
|
-
const newLines = [...buffer.lines];
|
|
39
|
-
newLines[line - 1] = mergedLine;
|
|
40
|
-
newLines.splice(line, 1);
|
|
41
|
-
return {
|
|
42
|
-
buffer: { lines: newLines },
|
|
43
|
-
cursor: { line: line - 1, column: previousLine.length },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
// Delete character within the line
|
|
47
|
-
const currentLine = buffer.lines[line];
|
|
48
|
-
const newLine = currentLine.slice(0, column - 1) + currentLine.slice(column);
|
|
49
|
-
const newLines = [...buffer.lines];
|
|
50
|
-
newLines[line] = newLine;
|
|
51
|
-
return {
|
|
52
|
-
buffer: { lines: newLines },
|
|
53
|
-
cursor: { line, column: column - 1 },
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Insert a new line at cursor position (splits current line)
|
|
58
|
-
*/
|
|
59
|
-
export function insertNewLine(buffer, cursor) {
|
|
60
|
-
const { line, column } = cursor;
|
|
61
|
-
const currentLine = buffer.lines[line];
|
|
62
|
-
const beforeCursor = currentLine.slice(0, column);
|
|
63
|
-
const afterCursor = currentLine.slice(column);
|
|
64
|
-
const newLines = [...buffer.lines];
|
|
65
|
-
newLines[line] = beforeCursor;
|
|
66
|
-
newLines.splice(line + 1, 0, afterCursor);
|
|
67
|
-
return {
|
|
68
|
-
buffer: { lines: newLines },
|
|
69
|
-
cursor: { line: line + 1, column: 0 },
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Move cursor in specified direction with bounds checking
|
|
74
|
-
*/
|
|
75
|
-
export function moveCursor(buffer, cursor, direction) {
|
|
76
|
-
const { line, column } = cursor;
|
|
77
|
-
const currentLine = buffer.lines[line];
|
|
78
|
-
const lineCount = buffer.lines.length;
|
|
79
|
-
switch (direction) {
|
|
80
|
-
case 'left':
|
|
81
|
-
if (column > 0) {
|
|
82
|
-
return { line, column: column - 1 };
|
|
83
|
-
}
|
|
84
|
-
// Wrap to end of previous line
|
|
85
|
-
if (line > 0) {
|
|
86
|
-
return { line: line - 1, column: buffer.lines[line - 1].length };
|
|
87
|
-
}
|
|
88
|
-
return cursor;
|
|
89
|
-
case 'right':
|
|
90
|
-
if (column < currentLine.length) {
|
|
91
|
-
return { line, column: column + 1 };
|
|
92
|
-
}
|
|
93
|
-
// Wrap to start of next line
|
|
94
|
-
if (line < lineCount - 1) {
|
|
95
|
-
return { line: line + 1, column: 0 };
|
|
96
|
-
}
|
|
97
|
-
return cursor;
|
|
98
|
-
case 'up':
|
|
99
|
-
if (line > 0) {
|
|
100
|
-
const targetLine = buffer.lines[line - 1];
|
|
101
|
-
return { line: line - 1, column: Math.min(column, targetLine.length) };
|
|
102
|
-
}
|
|
103
|
-
return cursor;
|
|
104
|
-
case 'down':
|
|
105
|
-
if (line < lineCount - 1) {
|
|
106
|
-
const targetLine = buffer.lines[line + 1];
|
|
107
|
-
return { line: line + 1, column: Math.min(column, targetLine.length) };
|
|
108
|
-
}
|
|
109
|
-
return cursor;
|
|
110
|
-
case 'lineStart':
|
|
111
|
-
return { line, column: 0 };
|
|
112
|
-
case 'lineEnd':
|
|
113
|
-
return { line, column: currentLine.length };
|
|
114
|
-
default:
|
|
115
|
-
return cursor;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get the full text content from buffer (lines joined with newlines)
|
|
120
|
-
*/
|
|
121
|
-
export function getTextContent(buffer) {
|
|
122
|
-
// Single empty line is considered empty buffer
|
|
123
|
-
if (buffer.lines.length === 1 && buffer.lines[0] === '') {
|
|
124
|
-
return '';
|
|
125
|
-
}
|
|
126
|
-
return buffer.lines.join('\n');
|
|
127
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { Buffer, Cursor, WrapResult } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Props for the TextRenderer component
|
|
5
|
-
*/
|
|
6
|
-
export interface TextRendererProps {
|
|
7
|
-
/** Text buffer to render */
|
|
8
|
-
buffer: Buffer;
|
|
9
|
-
/** Current cursor position */
|
|
10
|
-
cursor: Cursor;
|
|
11
|
-
/** Terminal width for word wrapping (defaults to 80) */
|
|
12
|
-
width?: number;
|
|
13
|
-
/** Whether to show the cursor (defaults to true) */
|
|
14
|
-
showCursor?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Wrap buffer lines to fit within a given width.
|
|
18
|
-
* Returns visual lines and maps cursor position to visual coordinates.
|
|
19
|
-
*/
|
|
20
|
-
export declare function wrapLines(buffer: Buffer, cursor: Cursor, width: number): WrapResult;
|
|
21
|
-
/**
|
|
22
|
-
* TextRenderer component for displaying buffer content with cursor
|
|
23
|
-
*/
|
|
24
|
-
export declare function TextRenderer({ buffer, cursor, width, showCursor, }: TextRendererProps): React.ReactElement;
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Wrap buffer lines to fit within a given width.
|
|
4
|
-
* Returns visual lines and maps cursor position to visual coordinates.
|
|
5
|
-
*/
|
|
6
|
-
export function wrapLines(buffer, cursor, width) {
|
|
7
|
-
const visualLines = [];
|
|
8
|
-
let cursorVisualRow = 0;
|
|
9
|
-
let cursorVisualCol = 0;
|
|
10
|
-
let visualRowIndex = 0;
|
|
11
|
-
for (let lineIndex = 0; lineIndex < buffer.lines.length; lineIndex++) {
|
|
12
|
-
const line = buffer.lines[lineIndex];
|
|
13
|
-
const isCursorLine = lineIndex === cursor.line;
|
|
14
|
-
if (line.length <= width) {
|
|
15
|
-
// Line fits, no wrapping needed
|
|
16
|
-
visualLines.push(line);
|
|
17
|
-
if (isCursorLine) {
|
|
18
|
-
cursorVisualRow = visualRowIndex;
|
|
19
|
-
cursorVisualCol = cursor.column;
|
|
20
|
-
}
|
|
21
|
-
visualRowIndex++;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
// Line needs to be wrapped
|
|
25
|
-
let offset = 0;
|
|
26
|
-
while (offset < line.length) {
|
|
27
|
-
const chunk = line.slice(offset, offset + width);
|
|
28
|
-
visualLines.push(chunk);
|
|
29
|
-
if (isCursorLine) {
|
|
30
|
-
// Check if cursor falls within this chunk
|
|
31
|
-
if (cursor.column >= offset && cursor.column < offset + width) {
|
|
32
|
-
cursorVisualRow = visualRowIndex;
|
|
33
|
-
cursorVisualCol = cursor.column - offset;
|
|
34
|
-
}
|
|
35
|
-
else if (cursor.column === line.length && offset + chunk.length === line.length) {
|
|
36
|
-
// Cursor at end of line
|
|
37
|
-
cursorVisualRow = visualRowIndex;
|
|
38
|
-
cursorVisualCol = chunk.length;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
offset += width;
|
|
42
|
-
visualRowIndex++;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return { visualLines, cursorVisualRow, cursorVisualCol };
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Render a line with cursor inserted at the specified position
|
|
50
|
-
*/
|
|
51
|
-
function renderLineWithCursor(line, cursorCol, showCursor) {
|
|
52
|
-
if (!showCursor) {
|
|
53
|
-
return line;
|
|
54
|
-
}
|
|
55
|
-
const cursorChar = '█';
|
|
56
|
-
const before = line.slice(0, cursorCol);
|
|
57
|
-
const after = line.slice(cursorCol);
|
|
58
|
-
return before + cursorChar + after;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* TextRenderer component for displaying buffer content with cursor
|
|
62
|
-
*/
|
|
63
|
-
export function TextRenderer({ buffer, cursor, width = 80, showCursor = true, }) {
|
|
64
|
-
const { visualLines, cursorVisualRow, cursorVisualCol } = wrapLines(buffer, cursor, width);
|
|
65
|
-
return (React.createElement("div", null, visualLines.map((line, index) => {
|
|
66
|
-
const isCursorRow = index === cursorVisualRow;
|
|
67
|
-
const displayLine = isCursorRow
|
|
68
|
-
? renderLineWithCursor(line, cursorVisualCol, showCursor)
|
|
69
|
-
: line;
|
|
70
|
-
return (React.createElement("div", { key: index }, displayLine));
|
|
71
|
-
})));
|
|
72
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { handleKey } from '../KeyHandler';
|
|
3
|
-
describe('KeyHandler', () => {
|
|
4
|
-
let actions;
|
|
5
|
-
let buffer;
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
actions = {
|
|
8
|
-
insert: vi.fn(),
|
|
9
|
-
delete: vi.fn(),
|
|
10
|
-
newLine: vi.fn(),
|
|
11
|
-
moveCursor: vi.fn(),
|
|
12
|
-
undo: vi.fn(),
|
|
13
|
-
redo: vi.fn(),
|
|
14
|
-
setText: vi.fn(),
|
|
15
|
-
submit: vi.fn(),
|
|
16
|
-
};
|
|
17
|
-
buffer = { lines: [''] };
|
|
18
|
-
});
|
|
19
|
-
describe('Navigation', () => {
|
|
20
|
-
it('handles ArrowUp', () => {
|
|
21
|
-
handleKey({ upArrow: true }, 'up', buffer, actions);
|
|
22
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('up');
|
|
23
|
-
});
|
|
24
|
-
it('handles ArrowDown', () => {
|
|
25
|
-
handleKey({ downArrow: true }, 'down', buffer, actions);
|
|
26
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('down');
|
|
27
|
-
});
|
|
28
|
-
it('handles ArrowLeft', () => {
|
|
29
|
-
handleKey({ leftArrow: true }, 'left', buffer, actions);
|
|
30
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('left');
|
|
31
|
-
});
|
|
32
|
-
it('handles ArrowRight', () => {
|
|
33
|
-
handleKey({ rightArrow: true }, 'right', buffer, actions);
|
|
34
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('right');
|
|
35
|
-
});
|
|
36
|
-
it('handles Home', () => {
|
|
37
|
-
// Ink doesn't have a specific 'home' property usually, but we check for special keys
|
|
38
|
-
// Often represented as key.home if using a specific library or just checking input
|
|
39
|
-
// For Ink's useInput, we might receive specific sequences.
|
|
40
|
-
// Assuming we handle it via checking `key` object properties if available, or input string if it maps.
|
|
41
|
-
// However, standard Ink `Key` interface has `home` and `end`?
|
|
42
|
-
// Let's assume standard Ink Key interface.
|
|
43
|
-
handleKey({ home: true }, '', buffer, actions);
|
|
44
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('lineStart');
|
|
45
|
-
});
|
|
46
|
-
it('handles End', () => {
|
|
47
|
-
handleKey({ end: true }, '', buffer, actions);
|
|
48
|
-
expect(actions.moveCursor).toHaveBeenCalledWith('lineEnd');
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe('Editing', () => {
|
|
52
|
-
it('handles Backspace', () => {
|
|
53
|
-
handleKey({ backspace: true }, 'backspace', buffer, actions);
|
|
54
|
-
expect(actions.delete).toHaveBeenCalled();
|
|
55
|
-
});
|
|
56
|
-
it('handles Delete', () => {
|
|
57
|
-
handleKey({ delete: true }, 'delete', buffer, actions);
|
|
58
|
-
expect(actions.delete).toHaveBeenCalled(); // Our current simple delete handles backspace, we might need forward delete later
|
|
59
|
-
});
|
|
60
|
-
it('handles Ctrl+J (NewLine)', () => {
|
|
61
|
-
handleKey({ ctrl: true }, 'j', buffer, actions);
|
|
62
|
-
expect(actions.newLine).toHaveBeenCalled();
|
|
63
|
-
});
|
|
64
|
-
it('handles regular text insertion', () => {
|
|
65
|
-
handleKey({}, 'a', buffer, actions);
|
|
66
|
-
expect(actions.insert).toHaveBeenCalledWith('a');
|
|
67
|
-
});
|
|
68
|
-
it('ignores control keys without text', () => {
|
|
69
|
-
handleKey({ ctrl: true }, '', buffer, actions);
|
|
70
|
-
expect(actions.insert).not.toHaveBeenCalled();
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
describe('History', () => {
|
|
74
|
-
it('handles Ctrl+Z (Undo)', () => {
|
|
75
|
-
handleKey({ ctrl: true }, 'z', buffer, actions);
|
|
76
|
-
expect(actions.undo).toHaveBeenCalled();
|
|
77
|
-
});
|
|
78
|
-
it('handles Ctrl+Y (Redo)', () => {
|
|
79
|
-
handleKey({ ctrl: true }, 'y', buffer, actions);
|
|
80
|
-
expect(actions.redo).toHaveBeenCalled();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe('Submission', () => {
|
|
84
|
-
it('handles Enter as submit by default', () => {
|
|
85
|
-
buffer = { lines: ['hello'] };
|
|
86
|
-
handleKey({ return: true }, 'return', buffer, actions);
|
|
87
|
-
expect(actions.submit).toHaveBeenCalled();
|
|
88
|
-
expect(actions.newLine).not.toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
it('handles Enter as newline if line ends with backslash', () => {
|
|
91
|
-
buffer = { lines: ['hello\\'] };
|
|
92
|
-
const cursor = { line: 0, column: 6 };
|
|
93
|
-
handleKey({ return: true }, 'return', buffer, actions, cursor);
|
|
94
|
-
// It should probably remove the backslash first.
|
|
95
|
-
// Since we are mocking actions, we can't easily verify the state change between calls unless we implement a fake.
|
|
96
|
-
// But we can check call order.
|
|
97
|
-
expect(actions.delete).toHaveBeenCalled();
|
|
98
|
-
expect(actions.newLine).toHaveBeenCalled();
|
|
99
|
-
expect(actions.submit).not.toHaveBeenCalled();
|
|
100
|
-
});
|
|
101
|
-
it('handles Enter as newline if line ends with backslash (multiple lines)', () => {
|
|
102
|
-
// Cursor is implicitly at the end for this logic usually, or we need to pass cursor to handleKey?
|
|
103
|
-
// The plan says `handleKey` takes `buffer`. It probably needs `cursor` too to know which line we are on?
|
|
104
|
-
// Ah, `TextBuffer` logic usually needs cursor.
|
|
105
|
-
// If `handleKey` decides based on "current line", it needs to know the current line.
|
|
106
|
-
// So `handleKey` signature should probably include `cursor`.
|
|
107
|
-
const cursor = { line: 1, column: 6 };
|
|
108
|
-
buffer = { lines: ['first', 'second\\'] };
|
|
109
|
-
handleKey({ return: true }, 'return', buffer, actions, cursor);
|
|
110
|
-
expect(actions.delete).toHaveBeenCalled();
|
|
111
|
-
expect(actions.newLine).toHaveBeenCalled();
|
|
112
|
-
expect(actions.submit).not.toHaveBeenCalled();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|