wiggum-cli 0.11.7 → 0.11.8
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/.claude/settings.local.json +3 -1
- package/dist/tui/components/ChatInput.d.ts +23 -3
- package/dist/tui/components/ChatInput.d.ts.map +1 -1
- package/dist/tui/components/ChatInput.js +49 -52
- package/dist/tui/components/ChatInput.js.map +1 -1
- package/dist/tui/utils/input-utils.d.ts +126 -0
- package/dist/tui/utils/input-utils.d.ts.map +1 -0
- package/dist/tui/utils/input-utils.js +178 -0
- package/dist/tui/utils/input-utils.js.map +1 -0
- package/package.json +1 -1
- package/src/tui/components/ChatInput.tsx +58 -53
- package/src/tui/utils/input-utils.test.ts +428 -0
- package/src/tui/utils/input-utils.ts +219 -0
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ChatInput -
|
|
2
|
+
* ChatInput - Robust single-line input with slash command support and history
|
|
3
3
|
*
|
|
4
4
|
* Displays a `›` prompt character followed by a text input.
|
|
5
5
|
* Shows command dropdown when typing `/`.
|
|
6
6
|
* Supports ↑/↓ arrow keys for command history navigation.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Robust paste handling: Multi-line text is flattened to single line
|
|
10
|
+
* - Large paste support: Handles 2-4KB pastes without lag
|
|
11
|
+
* - Full editing: Backspace, delete, left/right arrow navigation
|
|
12
|
+
* - Word navigation: Option+left/right (macOS) for word-by-word cursor movement
|
|
13
|
+
* - History preservation: Draft text preserved when navigating history
|
|
7
14
|
*/
|
|
8
15
|
import React from 'react';
|
|
9
16
|
import { type Command } from './CommandDropdown.js';
|
|
@@ -27,9 +34,22 @@ export interface ChatInputProps {
|
|
|
27
34
|
/**
|
|
28
35
|
* ChatInput component
|
|
29
36
|
*
|
|
30
|
-
* Provides a text input with `›` prompt for chat-style interactions.
|
|
37
|
+
* Provides a robust single-line text input with `›` prompt for chat-style interactions.
|
|
31
38
|
* Shows command dropdown when input starts with `/`.
|
|
32
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* **Keyboard shortcuts:**
|
|
41
|
+
* - Enter: Submit input
|
|
42
|
+
* - Backspace: Delete character before cursor
|
|
43
|
+
* - Delete: Delete character after cursor
|
|
44
|
+
* - ←/→: Move cursor left/right
|
|
45
|
+
* - ↑/↓: Navigate command history
|
|
46
|
+
* - Option+←/→ (macOS): Move cursor by word
|
|
47
|
+
* - Cmd+←/→ (macOS): Move cursor to start/end
|
|
48
|
+
*
|
|
49
|
+
* **Paste behavior:**
|
|
50
|
+
* - Multi-line text is automatically flattened to a single line
|
|
51
|
+
* - Large pastes (up to 4KB) are handled efficiently
|
|
52
|
+
* - Consecutive whitespace is collapsed to single spaces
|
|
33
53
|
*
|
|
34
54
|
* @example
|
|
35
55
|
* ```tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../src/tui/components/ChatInput.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../src/tui/components/ChatInput.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAwC,MAAM,OAAO,CAAC;AAG7D,OAAO,EAAqC,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAWvF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,WAAoC,EACpC,QAAgB,EAChB,UAAkB,EAClB,QAA2B,EAC3B,SAAS,GACV,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAkRrC"}
|
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* ChatInput -
|
|
3
|
+
* ChatInput - Robust single-line input with slash command support and history
|
|
4
4
|
*
|
|
5
5
|
* Displays a `›` prompt character followed by a text input.
|
|
6
6
|
* Shows command dropdown when typing `/`.
|
|
7
7
|
* Supports ↑/↓ arrow keys for command history navigation.
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Robust paste handling: Multi-line text is flattened to single line
|
|
11
|
+
* - Large paste support: Handles 2-4KB pastes without lag
|
|
12
|
+
* - Full editing: Backspace, delete, left/right arrow navigation
|
|
13
|
+
* - Word navigation: Option+left/right (macOS) for word-by-word cursor movement
|
|
14
|
+
* - History preservation: Draft text preserved when navigating history
|
|
8
15
|
*/
|
|
9
16
|
import { useState, useCallback, useRef } from 'react';
|
|
10
17
|
import { Box, Text, useInput } from 'ink';
|
|
11
18
|
import { theme } from '../theme.js';
|
|
12
19
|
import { CommandDropdown, DEFAULT_COMMANDS } from './CommandDropdown.js';
|
|
13
20
|
import { useCommandHistory } from '../hooks/useCommandHistory.js';
|
|
21
|
+
import { normalizePastedText, insertTextAtCursor, deleteCharBefore, deleteCharAfter, moveCursorByWordLeft, moveCursorByWordRight, } from '../utils/input-utils.js';
|
|
14
22
|
/**
|
|
15
23
|
* ChatInput component
|
|
16
24
|
*
|
|
17
|
-
* Provides a text input with `›` prompt for chat-style interactions.
|
|
25
|
+
* Provides a robust single-line text input with `›` prompt for chat-style interactions.
|
|
18
26
|
* Shows command dropdown when input starts with `/`.
|
|
19
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* **Keyboard shortcuts:**
|
|
29
|
+
* - Enter: Submit input
|
|
30
|
+
* - Backspace: Delete character before cursor
|
|
31
|
+
* - Delete: Delete character after cursor
|
|
32
|
+
* - ←/→: Move cursor left/right
|
|
33
|
+
* - ↑/↓: Navigate command history
|
|
34
|
+
* - Option+←/→ (macOS): Move cursor by word
|
|
35
|
+
* - Cmd+←/→ (macOS): Move cursor to start/end
|
|
36
|
+
*
|
|
37
|
+
* **Paste behavior:**
|
|
38
|
+
* - Multi-line text is automatically flattened to a single line
|
|
39
|
+
* - Large pastes (up to 4KB) are handled efficiently
|
|
40
|
+
* - Consecutive whitespace is collapsed to single spaces
|
|
20
41
|
*
|
|
21
42
|
* @example
|
|
22
43
|
* ```tsx
|
|
@@ -63,26 +84,6 @@ export function ChatInput({ onSubmit, placeholder = 'Type your message...', disa
|
|
|
63
84
|
setShowDropdown(false);
|
|
64
85
|
}
|
|
65
86
|
}, [clampCursor, resetNavigation]);
|
|
66
|
-
const moveCursorByWordLeft = useCallback((currentValue, currentCursor) => {
|
|
67
|
-
let idx = currentCursor;
|
|
68
|
-
while (idx > 0 && /\s/.test(currentValue[idx - 1])) {
|
|
69
|
-
idx -= 1;
|
|
70
|
-
}
|
|
71
|
-
while (idx > 0 && /[A-Za-z0-9_]/.test(currentValue[idx - 1])) {
|
|
72
|
-
idx -= 1;
|
|
73
|
-
}
|
|
74
|
-
return idx;
|
|
75
|
-
}, []);
|
|
76
|
-
const moveCursorByWordRight = useCallback((currentValue, currentCursor) => {
|
|
77
|
-
let idx = currentCursor;
|
|
78
|
-
while (idx < currentValue.length && /\s/.test(currentValue[idx])) {
|
|
79
|
-
idx += 1;
|
|
80
|
-
}
|
|
81
|
-
while (idx < currentValue.length && /[A-Za-z0-9_]/.test(currentValue[idx])) {
|
|
82
|
-
idx += 1;
|
|
83
|
-
}
|
|
84
|
-
return idx;
|
|
85
|
-
}, []);
|
|
86
87
|
const handleEscapeSequence = useCallback((input) => {
|
|
87
88
|
const seq = input;
|
|
88
89
|
if (seq === '\u001bb' || seq === '\u001b[1;3D') {
|
|
@@ -102,14 +103,7 @@ export function ChatInput({ onSubmit, placeholder = 'Type your message...', disa
|
|
|
102
103
|
return true;
|
|
103
104
|
}
|
|
104
105
|
return false;
|
|
105
|
-
}, [cursorOffset,
|
|
106
|
-
const normalizePaste = useCallback((input) => {
|
|
107
|
-
let cleaned = input.replace(/\u001b\[200~|\u001b\[201~/g, '');
|
|
108
|
-
cleaned = cleaned.replace(/[\r\n]+/g, ' ');
|
|
109
|
-
cleaned = cleaned.replace(/\t/g, ' ');
|
|
110
|
-
cleaned = cleaned.replace(/\u001b/g, '');
|
|
111
|
-
return cleaned;
|
|
112
|
-
}, []);
|
|
106
|
+
}, [cursorOffset, updateValue, value]);
|
|
113
107
|
// Handle keyboard input for history navigation + editing
|
|
114
108
|
useInput((input, key) => {
|
|
115
109
|
if (disabled)
|
|
@@ -122,20 +116,16 @@ export function ChatInput({ onSubmit, placeholder = 'Type your message...', disa
|
|
|
122
116
|
handleSubmit(value);
|
|
123
117
|
return;
|
|
124
118
|
}
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
updateValue(nextValue, cursorOffset - 1);
|
|
130
|
-
}
|
|
119
|
+
const isBackspace = key.backspace || input === '\x7f' || input === '\b';
|
|
120
|
+
if (isBackspace) {
|
|
121
|
+
const { newValue, newCursorIndex } = deleteCharBefore(value, cursorOffset);
|
|
122
|
+
updateValue(newValue, newCursorIndex);
|
|
131
123
|
return;
|
|
132
124
|
}
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
updateValue(nextValue, cursorOffset);
|
|
138
|
-
}
|
|
125
|
+
const isDelete = key.delete || input === '\u001b[3~';
|
|
126
|
+
if (isDelete) {
|
|
127
|
+
const { newValue, newCursorIndex } = deleteCharAfter(value, cursorOffset);
|
|
128
|
+
updateValue(newValue, newCursorIndex);
|
|
139
129
|
return;
|
|
140
130
|
}
|
|
141
131
|
// Left/right arrows
|
|
@@ -189,19 +179,26 @@ export function ChatInput({ onSubmit, placeholder = 'Type your message...', disa
|
|
|
189
179
|
return;
|
|
190
180
|
}
|
|
191
181
|
if (input.includes('\u001b')) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
182
|
+
// Check for bracketed paste markers - these must reach normalizePastedText
|
|
183
|
+
// even though they start with ESC. Single-chunk pastes from some terminals
|
|
184
|
+
// arrive as: \u001b[200~content\u001b[201~
|
|
185
|
+
const hasBracketedPaste = input.includes('\u001b[200~') || input.includes('\u001b[201~');
|
|
186
|
+
if (!hasBracketedPaste) {
|
|
187
|
+
if (handleEscapeSequence(input)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Ignore unknown escape sequences to avoid garbage insertion
|
|
191
|
+
if (input.startsWith('\u001b')) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
198
194
|
}
|
|
195
|
+
// Bracketed paste falls through to normalizePastedText below
|
|
199
196
|
}
|
|
200
|
-
const textToInsert = input.length > 1 ?
|
|
197
|
+
const textToInsert = input.length > 1 ? normalizePastedText(input) : input;
|
|
201
198
|
if (!textToInsert)
|
|
202
199
|
return;
|
|
203
|
-
const
|
|
204
|
-
updateValue(
|
|
200
|
+
const { newValue, newCursorIndex } = insertTextAtCursor(value, cursorOffset, textToInsert);
|
|
201
|
+
updateValue(newValue, newCursorIndex);
|
|
205
202
|
});
|
|
206
203
|
/**
|
|
207
204
|
* Handle input submission
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.js","sourceRoot":"","sources":["../../../src/tui/components/ChatInput.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"ChatInput.js","sourceRoot":"","sources":["../../../src/tui/components/ChatInput.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input manipulation utilities for ChatInput component
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for text normalization, cursor manipulation, and word navigation.
|
|
5
|
+
* These are extracted for testability and reusability.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Result of a cursor manipulation operation
|
|
9
|
+
*/
|
|
10
|
+
export interface CursorManipulationResult {
|
|
11
|
+
/** The new value after manipulation */
|
|
12
|
+
newValue: string;
|
|
13
|
+
/** The new cursor index after manipulation */
|
|
14
|
+
newCursorIndex: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes pasted text for single-line input
|
|
18
|
+
*
|
|
19
|
+
* Handles:
|
|
20
|
+
* - Strips bracket paste mode markers (`\u001b[200~`, `\u001b[201~`)
|
|
21
|
+
* - Converts all newline variants (`\n`, `\r`, `\r\n`) to spaces
|
|
22
|
+
* - Converts tabs to spaces
|
|
23
|
+
* - Collapses consecutive whitespace to single spaces
|
|
24
|
+
* - Strips escape sequences
|
|
25
|
+
*
|
|
26
|
+
* @param input - Raw pasted text (potentially multi-line)
|
|
27
|
+
* @returns Normalized single-line text suitable for insertion
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* normalizePastedText("line1\nline2") // => "line1 line2"
|
|
32
|
+
* normalizePastedText("a\r\nb\nc") // => "a b c"
|
|
33
|
+
* normalizePastedText("hello\tworld") // => "hello world"
|
|
34
|
+
* normalizePastedText("foo bar") // => "foo bar"
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizePastedText(input: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Inserts text at a specific cursor position
|
|
40
|
+
*
|
|
41
|
+
* @param value - Current input value
|
|
42
|
+
* @param cursorIndex - Current cursor position (0 <= cursorIndex <= value.length)
|
|
43
|
+
* @param text - Text to insert
|
|
44
|
+
* @returns New value and cursor index after insertion
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* insertTextAtCursor("hello", 5, " world")
|
|
49
|
+
* // => { newValue: "hello world", newCursorIndex: 11 }
|
|
50
|
+
*
|
|
51
|
+
* insertTextAtCursor("foobar", 3, "baz")
|
|
52
|
+
* // => { newValue: "foobazbar", newCursorIndex: 6 }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function insertTextAtCursor(value: string, cursorIndex: number, text: string): CursorManipulationResult;
|
|
56
|
+
/**
|
|
57
|
+
* Deletes the character before the cursor (backspace behavior)
|
|
58
|
+
*
|
|
59
|
+
* @param value - Current input value
|
|
60
|
+
* @param cursorIndex - Current cursor position
|
|
61
|
+
* @returns New value and cursor index after deletion
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* deleteCharBefore("hello", 5)
|
|
66
|
+
* // => { newValue: "hell", newCursorIndex: 4 }
|
|
67
|
+
*
|
|
68
|
+
* deleteCharBefore("hello", 0)
|
|
69
|
+
* // => { newValue: "hello", newCursorIndex: 0 } (no-op at start)
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function deleteCharBefore(value: string, cursorIndex: number): CursorManipulationResult;
|
|
73
|
+
/**
|
|
74
|
+
* Deletes the character after the cursor (delete-forward behavior)
|
|
75
|
+
*
|
|
76
|
+
* @param value - Current input value
|
|
77
|
+
* @param cursorIndex - Current cursor position
|
|
78
|
+
* @returns New value and cursor index after deletion
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* deleteCharAfter("hello", 0)
|
|
83
|
+
* // => { newValue: "ello", newCursorIndex: 0 }
|
|
84
|
+
*
|
|
85
|
+
* deleteCharAfter("hello", 5)
|
|
86
|
+
* // => { newValue: "hello", newCursorIndex: 5 } (no-op at end)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function deleteCharAfter(value: string, cursorIndex: number): CursorManipulationResult;
|
|
90
|
+
/**
|
|
91
|
+
* Moves cursor to the start of the previous word (word-left navigation)
|
|
92
|
+
*
|
|
93
|
+
* A "word" is defined as a contiguous sequence of alphanumeric characters and underscores.
|
|
94
|
+
* The cursor skips over whitespace, then moves to the start of the word.
|
|
95
|
+
*
|
|
96
|
+
* @param value - Current input value
|
|
97
|
+
* @param cursorIndex - Current cursor position
|
|
98
|
+
* @returns New cursor index at the start of the previous word
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* moveCursorByWordLeft("hello world", 11) // => 6 (start of "world")
|
|
103
|
+
* moveCursorByWordLeft("foo bar", 8) // => 5 (start of "bar")
|
|
104
|
+
* moveCursorByWordLeft("test", 0) // => 0 (no-op at start)
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function moveCursorByWordLeft(value: string, cursorIndex: number): number;
|
|
108
|
+
/**
|
|
109
|
+
* Moves cursor to the end of the next word (word-right navigation)
|
|
110
|
+
*
|
|
111
|
+
* A "word" is defined as a contiguous sequence of alphanumeric characters and underscores.
|
|
112
|
+
* The cursor skips over whitespace, then moves to the end of the word.
|
|
113
|
+
*
|
|
114
|
+
* @param value - Current input value
|
|
115
|
+
* @param cursorIndex - Current cursor position
|
|
116
|
+
* @returns New cursor index at the end of the next word
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* moveCursorByWordRight("hello world", 0) // => 5 (end of "hello")
|
|
121
|
+
* moveCursorByWordRight("foo bar", 3) // => 8 (end of "bar")
|
|
122
|
+
* moveCursorByWordRight("test", 4) // => 4 (no-op at end)
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export declare function moveCursorByWordRight(value: string, cursorIndex: number): number;
|
|
126
|
+
//# sourceMappingURL=input-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-utils.d.ts","sourceRoot":"","sources":["../../../src/tui/utils/input-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,wBAAwB,CAO1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,wBAAwB,CAY1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,wBAAwB,CAa1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAc/E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAchF"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input manipulation utilities for ChatInput component
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for text normalization, cursor manipulation, and word navigation.
|
|
5
|
+
* These are extracted for testability and reusability.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes pasted text for single-line input
|
|
9
|
+
*
|
|
10
|
+
* Handles:
|
|
11
|
+
* - Strips bracket paste mode markers (`\u001b[200~`, `\u001b[201~`)
|
|
12
|
+
* - Converts all newline variants (`\n`, `\r`, `\r\n`) to spaces
|
|
13
|
+
* - Converts tabs to spaces
|
|
14
|
+
* - Collapses consecutive whitespace to single spaces
|
|
15
|
+
* - Strips escape sequences
|
|
16
|
+
*
|
|
17
|
+
* @param input - Raw pasted text (potentially multi-line)
|
|
18
|
+
* @returns Normalized single-line text suitable for insertion
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* normalizePastedText("line1\nline2") // => "line1 line2"
|
|
23
|
+
* normalizePastedText("a\r\nb\nc") // => "a b c"
|
|
24
|
+
* normalizePastedText("hello\tworld") // => "hello world"
|
|
25
|
+
* normalizePastedText("foo bar") // => "foo bar"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function normalizePastedText(input) {
|
|
29
|
+
// Strip bracket paste mode markers
|
|
30
|
+
let cleaned = input.replace(/\u001b\[200~|\u001b\[201~/g, '');
|
|
31
|
+
// Replace all newline variants with spaces
|
|
32
|
+
cleaned = cleaned.replace(/[\r\n]+/g, ' ');
|
|
33
|
+
// Replace tabs with spaces
|
|
34
|
+
cleaned = cleaned.replace(/\t/g, ' ');
|
|
35
|
+
// Strip remaining escape sequences
|
|
36
|
+
cleaned = cleaned.replace(/\u001b/g, '');
|
|
37
|
+
// Collapse consecutive whitespace to single spaces
|
|
38
|
+
cleaned = cleaned.replace(/\s+/g, ' ');
|
|
39
|
+
return cleaned;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Inserts text at a specific cursor position
|
|
43
|
+
*
|
|
44
|
+
* @param value - Current input value
|
|
45
|
+
* @param cursorIndex - Current cursor position (0 <= cursorIndex <= value.length)
|
|
46
|
+
* @param text - Text to insert
|
|
47
|
+
* @returns New value and cursor index after insertion
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* insertTextAtCursor("hello", 5, " world")
|
|
52
|
+
* // => { newValue: "hello world", newCursorIndex: 11 }
|
|
53
|
+
*
|
|
54
|
+
* insertTextAtCursor("foobar", 3, "baz")
|
|
55
|
+
* // => { newValue: "foobazbar", newCursorIndex: 6 }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function insertTextAtCursor(value, cursorIndex, text) {
|
|
59
|
+
const before = value.slice(0, cursorIndex);
|
|
60
|
+
const after = value.slice(cursorIndex);
|
|
61
|
+
const newValue = before + text + after;
|
|
62
|
+
const newCursorIndex = cursorIndex + text.length;
|
|
63
|
+
return { newValue, newCursorIndex };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Deletes the character before the cursor (backspace behavior)
|
|
67
|
+
*
|
|
68
|
+
* @param value - Current input value
|
|
69
|
+
* @param cursorIndex - Current cursor position
|
|
70
|
+
* @returns New value and cursor index after deletion
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* deleteCharBefore("hello", 5)
|
|
75
|
+
* // => { newValue: "hell", newCursorIndex: 4 }
|
|
76
|
+
*
|
|
77
|
+
* deleteCharBefore("hello", 0)
|
|
78
|
+
* // => { newValue: "hello", newCursorIndex: 0 } (no-op at start)
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function deleteCharBefore(value, cursorIndex) {
|
|
82
|
+
if (cursorIndex <= 0) {
|
|
83
|
+
// At start of line, nothing to delete
|
|
84
|
+
return { newValue: value, newCursorIndex: 0 };
|
|
85
|
+
}
|
|
86
|
+
const before = value.slice(0, cursorIndex - 1);
|
|
87
|
+
const after = value.slice(cursorIndex);
|
|
88
|
+
const newValue = before + after;
|
|
89
|
+
const newCursorIndex = cursorIndex - 1;
|
|
90
|
+
return { newValue, newCursorIndex };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Deletes the character after the cursor (delete-forward behavior)
|
|
94
|
+
*
|
|
95
|
+
* @param value - Current input value
|
|
96
|
+
* @param cursorIndex - Current cursor position
|
|
97
|
+
* @returns New value and cursor index after deletion
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* deleteCharAfter("hello", 0)
|
|
102
|
+
* // => { newValue: "ello", newCursorIndex: 0 }
|
|
103
|
+
*
|
|
104
|
+
* deleteCharAfter("hello", 5)
|
|
105
|
+
* // => { newValue: "hello", newCursorIndex: 5 } (no-op at end)
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function deleteCharAfter(value, cursorIndex) {
|
|
109
|
+
if (cursorIndex >= value.length) {
|
|
110
|
+
// At end of line, nothing to delete
|
|
111
|
+
return { newValue: value, newCursorIndex: cursorIndex };
|
|
112
|
+
}
|
|
113
|
+
const before = value.slice(0, cursorIndex);
|
|
114
|
+
const after = value.slice(cursorIndex + 1);
|
|
115
|
+
const newValue = before + after;
|
|
116
|
+
// Cursor stays in same position
|
|
117
|
+
const newCursorIndex = cursorIndex;
|
|
118
|
+
return { newValue, newCursorIndex };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Moves cursor to the start of the previous word (word-left navigation)
|
|
122
|
+
*
|
|
123
|
+
* A "word" is defined as a contiguous sequence of alphanumeric characters and underscores.
|
|
124
|
+
* The cursor skips over whitespace, then moves to the start of the word.
|
|
125
|
+
*
|
|
126
|
+
* @param value - Current input value
|
|
127
|
+
* @param cursorIndex - Current cursor position
|
|
128
|
+
* @returns New cursor index at the start of the previous word
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* moveCursorByWordLeft("hello world", 11) // => 6 (start of "world")
|
|
133
|
+
* moveCursorByWordLeft("foo bar", 8) // => 5 (start of "bar")
|
|
134
|
+
* moveCursorByWordLeft("test", 0) // => 0 (no-op at start)
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export function moveCursorByWordLeft(value, cursorIndex) {
|
|
138
|
+
let idx = cursorIndex;
|
|
139
|
+
// Skip trailing whitespace
|
|
140
|
+
while (idx > 0 && /\s/.test(value[idx - 1])) {
|
|
141
|
+
idx -= 1;
|
|
142
|
+
}
|
|
143
|
+
// Skip word characters
|
|
144
|
+
while (idx > 0 && /[A-Za-z0-9_]/.test(value[idx - 1])) {
|
|
145
|
+
idx -= 1;
|
|
146
|
+
}
|
|
147
|
+
return idx;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Moves cursor to the end of the next word (word-right navigation)
|
|
151
|
+
*
|
|
152
|
+
* A "word" is defined as a contiguous sequence of alphanumeric characters and underscores.
|
|
153
|
+
* The cursor skips over whitespace, then moves to the end of the word.
|
|
154
|
+
*
|
|
155
|
+
* @param value - Current input value
|
|
156
|
+
* @param cursorIndex - Current cursor position
|
|
157
|
+
* @returns New cursor index at the end of the next word
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* moveCursorByWordRight("hello world", 0) // => 5 (end of "hello")
|
|
162
|
+
* moveCursorByWordRight("foo bar", 3) // => 8 (end of "bar")
|
|
163
|
+
* moveCursorByWordRight("test", 4) // => 4 (no-op at end)
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export function moveCursorByWordRight(value, cursorIndex) {
|
|
167
|
+
let idx = cursorIndex;
|
|
168
|
+
// Skip leading whitespace
|
|
169
|
+
while (idx < value.length && /\s/.test(value[idx])) {
|
|
170
|
+
idx += 1;
|
|
171
|
+
}
|
|
172
|
+
// Skip word characters
|
|
173
|
+
while (idx < value.length && /[A-Za-z0-9_]/.test(value[idx])) {
|
|
174
|
+
idx += 1;
|
|
175
|
+
}
|
|
176
|
+
return idx;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=input-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-utils.js","sourceRoot":"","sources":["../../../src/tui/utils/input-utils.ts"],"names":[],"mappings":"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"}
|