@mariozechner/pi-tui 0.5.48 → 0.7.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/README.md +166 -475
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +2 -0
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/{text-editor.d.ts → editor.d.ts} +9 -5
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/{text-editor.js → editor.js} +125 -70
- package/dist/components/editor.js.map +1 -0
- package/dist/components/input.d.ts +14 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +120 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/{loading-animation.d.ts → loader.d.ts} +5 -5
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/{loading-animation.js → loader.js} +13 -10
- package/dist/components/loader.js.map +1 -0
- package/dist/components/markdown.d.ts +46 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +499 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/select-list.d.ts +3 -3
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +24 -16
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/spacer.d.ts +11 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +20 -0
- package/dist/components/spacer.js.map +1 -0
- package/dist/components/text.d.ts +26 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +141 -0
- package/dist/components/text.js.map +1 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -12
- package/dist/index.js.map +1 -1
- package/dist/terminal.d.ts +12 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +33 -3
- package/dist/terminal.js.map +1 -1
- package/dist/tui.d.ts +30 -52
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +131 -337
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +15 -0
- package/dist/utils.js.map +1 -0
- package/package.json +6 -5
- package/dist/components/loading-animation.d.ts.map +0 -1
- package/dist/components/loading-animation.js.map +0 -1
- package/dist/components/markdown-component.d.ts +0 -15
- package/dist/components/markdown-component.d.ts.map +0 -1
- package/dist/components/markdown-component.js +0 -247
- package/dist/components/markdown-component.js.map +0 -1
- package/dist/components/text-component.d.ts +0 -14
- package/dist/components/text-component.d.ts.map +0 -1
- package/dist/components/text-component.js +0 -90
- package/dist/components/text-component.js.map +0 -1
- package/dist/components/text-editor.d.ts.map +0 -1
- package/dist/components/text-editor.js.map +0 -1
- package/dist/components/whitespace-component.d.ts +0 -13
- package/dist/components/whitespace-component.d.ts.map +0 -1
- package/dist/components/whitespace-component.js +0 -22
- package/dist/components/whitespace-component.js.map +0 -1
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { getNextComponentId } from "../tui.js";
|
|
3
2
|
import { SelectList } from "./select-list.js";
|
|
4
|
-
export class
|
|
3
|
+
export class Editor {
|
|
4
|
+
state = {
|
|
5
|
+
lines: [""],
|
|
6
|
+
cursorLine: 0,
|
|
7
|
+
cursorCol: 0,
|
|
8
|
+
};
|
|
9
|
+
config = {};
|
|
10
|
+
// Autocomplete support
|
|
11
|
+
autocompleteProvider;
|
|
12
|
+
autocompleteList;
|
|
13
|
+
isAutocompleting = false;
|
|
14
|
+
autocompletePrefix = "";
|
|
15
|
+
// Paste tracking for large pastes
|
|
16
|
+
pastes = new Map();
|
|
17
|
+
pasteCounter = 0;
|
|
18
|
+
// Bracketed paste mode buffering
|
|
19
|
+
pasteBuffer = "";
|
|
20
|
+
isInPaste = false;
|
|
21
|
+
onSubmit;
|
|
22
|
+
onChange;
|
|
23
|
+
disableSubmit = false;
|
|
5
24
|
constructor(config) {
|
|
6
|
-
this.id = getNextComponentId();
|
|
7
|
-
this.state = {
|
|
8
|
-
lines: [""],
|
|
9
|
-
cursorLine: 0,
|
|
10
|
-
cursorCol: 0,
|
|
11
|
-
};
|
|
12
|
-
this.config = {};
|
|
13
|
-
this.isAutocompleting = false;
|
|
14
|
-
this.autocompletePrefix = "";
|
|
15
|
-
this.disableSubmit = false;
|
|
16
25
|
if (config) {
|
|
17
26
|
this.config = { ...this.config, ...config };
|
|
18
27
|
}
|
|
@@ -24,21 +33,12 @@ export class TextEditor {
|
|
|
24
33
|
this.autocompleteProvider = provider;
|
|
25
34
|
}
|
|
26
35
|
render(width) {
|
|
27
|
-
// Box drawing characters
|
|
28
|
-
const topLeft = chalk.gray("╭");
|
|
29
|
-
const topRight = chalk.gray("╮");
|
|
30
|
-
const bottomLeft = chalk.gray("╰");
|
|
31
|
-
const bottomRight = chalk.gray("╯");
|
|
32
36
|
const horizontal = chalk.gray("─");
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const boxWidth = width - 1;
|
|
36
|
-
const contentWidth = boxWidth - 4; // Account for "│ " and " │"
|
|
37
|
-
// Layout the text
|
|
38
|
-
const layoutLines = this.layoutText(contentWidth);
|
|
37
|
+
// Layout the text - use full width
|
|
38
|
+
const layoutLines = this.layoutText(width);
|
|
39
39
|
const result = [];
|
|
40
40
|
// Render top border
|
|
41
|
-
result.push(
|
|
41
|
+
result.push(horizontal.repeat(width));
|
|
42
42
|
// Render each layout line
|
|
43
43
|
for (const layoutLine of layoutLines) {
|
|
44
44
|
let displayText = layoutLine.text;
|
|
@@ -55,44 +55,82 @@ export class TextEditor {
|
|
|
55
55
|
// visibleLength stays the same - we're replacing, not adding
|
|
56
56
|
}
|
|
57
57
|
else {
|
|
58
|
-
// Cursor is at the end -
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
// Cursor is at the end - check if we have room for the space
|
|
59
|
+
if (layoutLine.text.length < width) {
|
|
60
|
+
// We have room - add highlighted space
|
|
61
|
+
const cursor = "\x1b[7m \x1b[0m";
|
|
62
|
+
displayText = before + cursor;
|
|
63
|
+
// visibleLength increases by 1 - we're adding a space
|
|
64
|
+
visibleLength = layoutLine.text.length + 1;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Line is at full width - use reverse video on last character if possible
|
|
68
|
+
// or just show cursor at the end without adding space
|
|
69
|
+
if (before.length > 0) {
|
|
70
|
+
const lastChar = before[before.length - 1];
|
|
71
|
+
const cursor = `\x1b[7m${lastChar}\x1b[0m`;
|
|
72
|
+
displayText = before.slice(0, -1) + cursor;
|
|
73
|
+
}
|
|
74
|
+
// visibleLength stays the same
|
|
75
|
+
}
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
// Calculate padding based on actual visible length
|
|
66
|
-
const padding = " ".repeat(Math.max(0,
|
|
67
|
-
// Render the line
|
|
68
|
-
result.push(
|
|
79
|
+
const padding = " ".repeat(Math.max(0, width - visibleLength));
|
|
80
|
+
// Render the line (no side borders, just horizontal lines above and below)
|
|
81
|
+
result.push(displayText + padding);
|
|
69
82
|
}
|
|
70
83
|
// Render bottom border
|
|
71
|
-
result.push(
|
|
84
|
+
result.push(horizontal.repeat(width));
|
|
72
85
|
// Add autocomplete list if active
|
|
73
86
|
if (this.isAutocompleting && this.autocompleteList) {
|
|
74
87
|
const autocompleteResult = this.autocompleteList.render(width);
|
|
75
|
-
result.push(...autocompleteResult
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// This ensures cursor position updates are always reflected
|
|
79
|
-
return {
|
|
80
|
-
lines: result,
|
|
81
|
-
changed: true,
|
|
82
|
-
};
|
|
88
|
+
result.push(...autocompleteResult);
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
83
91
|
}
|
|
84
92
|
handleInput(data) {
|
|
93
|
+
// Handle bracketed paste mode
|
|
94
|
+
// Start of paste: \x1b[200~
|
|
95
|
+
// End of paste: \x1b[201~
|
|
96
|
+
// Check if we're starting a bracketed paste
|
|
97
|
+
if (data.includes("\x1b[200~")) {
|
|
98
|
+
this.isInPaste = true;
|
|
99
|
+
this.pasteBuffer = "";
|
|
100
|
+
// Remove the start marker and keep the rest
|
|
101
|
+
data = data.replace("\x1b[200~", "");
|
|
102
|
+
}
|
|
103
|
+
// If we're in a paste, buffer the data
|
|
104
|
+
if (this.isInPaste) {
|
|
105
|
+
// Append data to buffer first (end marker could be split across chunks)
|
|
106
|
+
this.pasteBuffer += data;
|
|
107
|
+
// Check if the accumulated buffer contains the end marker
|
|
108
|
+
const endIndex = this.pasteBuffer.indexOf("\x1b[201~");
|
|
109
|
+
if (endIndex !== -1) {
|
|
110
|
+
// Extract content before the end marker
|
|
111
|
+
const pasteContent = this.pasteBuffer.substring(0, endIndex);
|
|
112
|
+
// Process the complete paste
|
|
113
|
+
this.handlePaste(pasteContent);
|
|
114
|
+
// Reset paste state
|
|
115
|
+
this.isInPaste = false;
|
|
116
|
+
// Process any remaining data after the end marker
|
|
117
|
+
const remaining = this.pasteBuffer.substring(endIndex + 6); // 6 = length of \x1b[201~
|
|
118
|
+
this.pasteBuffer = "";
|
|
119
|
+
if (remaining.length > 0) {
|
|
120
|
+
this.handleInput(remaining);
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Still accumulating, wait for more data
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
85
129
|
// Handle special key combinations first
|
|
86
130
|
// Ctrl+C - Exit (let parent handle this)
|
|
87
131
|
if (data.charCodeAt(0) === 3) {
|
|
88
132
|
return;
|
|
89
133
|
}
|
|
90
|
-
// Handle paste - detect when we get a lot of text at once
|
|
91
|
-
const isPaste = data.length > 10 || (data.length > 2 && data.includes("\n"));
|
|
92
|
-
if (isPaste) {
|
|
93
|
-
this.handlePaste(data);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
134
|
// Handle autocomplete special keys first (but don't block other input)
|
|
97
135
|
if (this.isAutocompleting && this.autocompleteList) {
|
|
98
136
|
// Escape - cancel autocomplete
|
|
@@ -106,8 +144,8 @@ export class TextEditor {
|
|
|
106
144
|
if (data === "\x1b[A" || data === "\x1b[B") {
|
|
107
145
|
this.autocompleteList.handleInput(data);
|
|
108
146
|
}
|
|
109
|
-
// If Tab was pressed, apply the selection
|
|
110
|
-
if (data === "\t") {
|
|
147
|
+
// If Tab or Enter was pressed, apply the selection
|
|
148
|
+
if (data === "\t" || data === "\r") {
|
|
111
149
|
const selected = this.autocompleteList.getSelectedItem();
|
|
112
150
|
if (selected && this.autocompleteProvider) {
|
|
113
151
|
const result = this.autocompleteProvider.applyCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol, selected, this.autocompletePrefix);
|
|
@@ -121,11 +159,6 @@ export class TextEditor {
|
|
|
121
159
|
}
|
|
122
160
|
return;
|
|
123
161
|
}
|
|
124
|
-
// If Enter was pressed, cancel autocomplete and let it fall through to submission
|
|
125
|
-
else if (data === "\r") {
|
|
126
|
-
this.cancelAutocomplete();
|
|
127
|
-
// Don't return here - let Enter fall through to normal submission handling
|
|
128
|
-
}
|
|
129
162
|
else {
|
|
130
163
|
// For other keys, handle normally within autocomplete
|
|
131
164
|
return;
|
|
@@ -169,14 +202,22 @@ export class TextEditor {
|
|
|
169
202
|
if (this.disableSubmit) {
|
|
170
203
|
return;
|
|
171
204
|
}
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
//
|
|
205
|
+
// Get text and substitute paste markers with actual content
|
|
206
|
+
let result = this.state.lines.join("\n").trim();
|
|
207
|
+
// Replace all [paste #N +xxx lines] markers with actual paste content
|
|
208
|
+
for (const [pasteId, pasteContent] of this.pastes) {
|
|
209
|
+
// Match both old format [paste #N] and new format [paste #N +xxx lines]
|
|
210
|
+
const markerRegex = new RegExp(`\\[paste #${pasteId}( \\+\\d+ lines)?\\]`, "g");
|
|
211
|
+
result = result.replace(markerRegex, pasteContent);
|
|
212
|
+
}
|
|
213
|
+
// Reset editor and clear pastes
|
|
175
214
|
this.state = {
|
|
176
215
|
lines: [""],
|
|
177
216
|
cursorLine: 0,
|
|
178
217
|
cursorCol: 0,
|
|
179
218
|
};
|
|
219
|
+
this.pastes.clear();
|
|
220
|
+
this.pasteCounter = 0;
|
|
180
221
|
// Notify that editor is now empty
|
|
181
222
|
if (this.onChange) {
|
|
182
223
|
this.onChange("");
|
|
@@ -230,9 +271,9 @@ export class TextEditor {
|
|
|
230
271
|
if (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === "")) {
|
|
231
272
|
// Empty editor
|
|
232
273
|
layoutLines.push({
|
|
233
|
-
text: "
|
|
274
|
+
text: "",
|
|
234
275
|
hasCursor: true,
|
|
235
|
-
cursorPos:
|
|
276
|
+
cursorPos: 0,
|
|
236
277
|
});
|
|
237
278
|
return layoutLines;
|
|
238
279
|
}
|
|
@@ -240,21 +281,19 @@ export class TextEditor {
|
|
|
240
281
|
for (let i = 0; i < this.state.lines.length; i++) {
|
|
241
282
|
const line = this.state.lines[i] || "";
|
|
242
283
|
const isCurrentLine = i === this.state.cursorLine;
|
|
243
|
-
const prefix = i === 0 ? "> " : " ";
|
|
244
|
-
const prefixedLine = prefix + line;
|
|
245
284
|
const maxLineLength = contentWidth;
|
|
246
|
-
if (
|
|
285
|
+
if (line.length <= maxLineLength) {
|
|
247
286
|
// Line fits in one layout line
|
|
248
287
|
if (isCurrentLine) {
|
|
249
288
|
layoutLines.push({
|
|
250
|
-
text:
|
|
289
|
+
text: line,
|
|
251
290
|
hasCursor: true,
|
|
252
|
-
cursorPos:
|
|
291
|
+
cursorPos: this.state.cursorCol,
|
|
253
292
|
});
|
|
254
293
|
}
|
|
255
294
|
else {
|
|
256
295
|
layoutLines.push({
|
|
257
|
-
text:
|
|
296
|
+
text: line,
|
|
258
297
|
hasCursor: false,
|
|
259
298
|
});
|
|
260
299
|
}
|
|
@@ -262,8 +301,8 @@ export class TextEditor {
|
|
|
262
301
|
else {
|
|
263
302
|
// Line needs wrapping
|
|
264
303
|
const chunks = [];
|
|
265
|
-
for (let pos = 0; pos <
|
|
266
|
-
chunks.push(
|
|
304
|
+
for (let pos = 0; pos < line.length; pos += maxLineLength) {
|
|
305
|
+
chunks.push(line.slice(pos, pos + maxLineLength));
|
|
267
306
|
}
|
|
268
307
|
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
|
|
269
308
|
const chunk = chunks[chunkIndex];
|
|
@@ -271,8 +310,8 @@ export class TextEditor {
|
|
|
271
310
|
continue;
|
|
272
311
|
const chunkStart = chunkIndex * maxLineLength;
|
|
273
312
|
const chunkEnd = chunkStart + chunk.length;
|
|
274
|
-
const cursorPos =
|
|
275
|
-
const hasCursorInChunk = isCurrentLine && cursorPos >= chunkStart && cursorPos
|
|
313
|
+
const cursorPos = this.state.cursorCol;
|
|
314
|
+
const hasCursorInChunk = isCurrentLine && cursorPos >= chunkStart && cursorPos <= chunkEnd;
|
|
276
315
|
if (hasCursorInChunk) {
|
|
277
316
|
layoutLines.push({
|
|
278
317
|
text: chunk,
|
|
@@ -349,6 +388,19 @@ export class TextEditor {
|
|
|
349
388
|
.join("");
|
|
350
389
|
// Split into lines
|
|
351
390
|
const pastedLines = filteredText.split("\n");
|
|
391
|
+
// Check if this is a large paste (> 10 lines)
|
|
392
|
+
if (pastedLines.length > 10) {
|
|
393
|
+
// Store the paste and insert a marker
|
|
394
|
+
this.pasteCounter++;
|
|
395
|
+
const pasteId = this.pasteCounter;
|
|
396
|
+
this.pastes.set(pasteId, filteredText);
|
|
397
|
+
// Insert marker like "[paste #1 +123 lines]"
|
|
398
|
+
const marker = `[paste #${pasteId} +${pastedLines.length} lines]`;
|
|
399
|
+
for (const char of marker) {
|
|
400
|
+
this.insertCharacter(char);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
352
404
|
if (pastedLines.length === 1) {
|
|
353
405
|
// Single line - just insert each character
|
|
354
406
|
const text = pastedLines[0] || "";
|
|
@@ -566,6 +618,9 @@ export class TextEditor {
|
|
|
566
618
|
this.autocompleteList = undefined;
|
|
567
619
|
this.autocompletePrefix = "";
|
|
568
620
|
}
|
|
621
|
+
isShowingAutocomplete() {
|
|
622
|
+
return this.isAutocompleting;
|
|
623
|
+
}
|
|
569
624
|
updateAutocomplete() {
|
|
570
625
|
if (!this.isAutocompleting || !this.autocompleteProvider)
|
|
571
626
|
return;
|
|
@@ -583,4 +638,4 @@ export class TextEditor {
|
|
|
583
638
|
}
|
|
584
639
|
}
|
|
585
640
|
}
|
|
586
|
-
//# sourceMappingURL=
|
|
641
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport type { AutocompleteProvider, CombinedAutocompleteProvider } from \"../autocomplete.js\";\nimport type { Component } from \"../tui.js\";\nimport { SelectList } from \"./select-list.js\";\n\ninterface EditorState {\n\tlines: string[];\n\tcursorLine: number;\n\tcursorCol: number;\n}\n\ninterface LayoutLine {\n\ttext: string;\n\thasCursor: boolean;\n\tcursorPos?: number;\n}\n\nexport interface TextEditorConfig {\n\t// Configuration options for text editor (none currently)\n}\n\nexport class Editor implements Component {\n\tprivate state: EditorState = {\n\t\tlines: [\"\"],\n\t\tcursorLine: 0,\n\t\tcursorCol: 0,\n\t};\n\n\tprivate config: TextEditorConfig = {};\n\n\t// Autocomplete support\n\tprivate autocompleteProvider?: AutocompleteProvider;\n\tprivate autocompleteList?: SelectList;\n\tprivate isAutocompleting: boolean = false;\n\tprivate autocompletePrefix: string = \"\";\n\n\t// Paste tracking for large pastes\n\tprivate pastes: Map<number, string> = new Map();\n\tprivate pasteCounter: number = 0;\n\n\t// Bracketed paste mode buffering\n\tprivate pasteBuffer: string = \"\";\n\tprivate isInPaste: boolean = false;\n\n\tpublic onSubmit?: (text: string) => void;\n\tpublic onChange?: (text: string) => void;\n\tpublic disableSubmit: boolean = false;\n\n\tconstructor(config?: TextEditorConfig) {\n\t\tif (config) {\n\t\t\tthis.config = { ...this.config, ...config };\n\t\t}\n\t}\n\n\tconfigure(config: Partial<TextEditorConfig>): void {\n\t\tthis.config = { ...this.config, ...config };\n\t}\n\n\tsetAutocompleteProvider(provider: AutocompleteProvider): void {\n\t\tthis.autocompleteProvider = provider;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst horizontal = chalk.gray(\"─\");\n\n\t\t// Layout the text - use full width\n\t\tconst layoutLines = this.layoutText(width);\n\n\t\tconst result: string[] = [];\n\n\t\t// Render top border\n\t\tresult.push(horizontal.repeat(width));\n\n\t\t// Render each layout line\n\t\tfor (const layoutLine of layoutLines) {\n\t\t\tlet displayText = layoutLine.text;\n\t\t\tlet visibleLength = layoutLine.text.length;\n\n\t\t\t// Add cursor if this line has it\n\t\t\tif (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {\n\t\t\t\tconst before = displayText.slice(0, layoutLine.cursorPos);\n\t\t\t\tconst after = displayText.slice(layoutLine.cursorPos);\n\n\t\t\t\tif (after.length > 0) {\n\t\t\t\t\t// Cursor is on a character - replace it with highlighted version\n\t\t\t\t\tconst cursor = `\\x1b[7m${after[0]}\\x1b[0m`;\n\t\t\t\t\tconst restAfter = after.slice(1);\n\t\t\t\t\tdisplayText = before + cursor + restAfter;\n\t\t\t\t\t// visibleLength stays the same - we're replacing, not adding\n\t\t\t\t} else {\n\t\t\t\t\t// Cursor is at the end - check if we have room for the space\n\t\t\t\t\tif (layoutLine.text.length < width) {\n\t\t\t\t\t\t// We have room - add highlighted space\n\t\t\t\t\t\tconst cursor = \"\\x1b[7m \\x1b[0m\";\n\t\t\t\t\t\tdisplayText = before + cursor;\n\t\t\t\t\t\t// visibleLength increases by 1 - we're adding a space\n\t\t\t\t\t\tvisibleLength = layoutLine.text.length + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Line is at full width - use reverse video on last character if possible\n\t\t\t\t\t\t// or just show cursor at the end without adding space\n\t\t\t\t\t\tif (before.length > 0) {\n\t\t\t\t\t\t\tconst lastChar = before[before.length - 1];\n\t\t\t\t\t\t\tconst cursor = `\\x1b[7m${lastChar}\\x1b[0m`;\n\t\t\t\t\t\t\tdisplayText = before.slice(0, -1) + cursor;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// visibleLength stays the same\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Calculate padding based on actual visible length\n\t\t\tconst padding = \" \".repeat(Math.max(0, width - visibleLength));\n\n\t\t\t// Render the line (no side borders, just horizontal lines above and below)\n\t\t\tresult.push(displayText + padding);\n\t\t}\n\n\t\t// Render bottom border\n\t\tresult.push(horizontal.repeat(width));\n\n\t\t// Add autocomplete list if active\n\t\tif (this.isAutocompleting && this.autocompleteList) {\n\t\t\tconst autocompleteResult = this.autocompleteList.render(width);\n\t\t\tresult.push(...autocompleteResult);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// Handle bracketed paste mode\n\t\t// Start of paste: \\x1b[200~\n\t\t// End of paste: \\x1b[201~\n\n\t\t// Check if we're starting a bracketed paste\n\t\tif (data.includes(\"\\x1b[200~\")) {\n\t\t\tthis.isInPaste = true;\n\t\t\tthis.pasteBuffer = \"\";\n\t\t\t// Remove the start marker and keep the rest\n\t\t\tdata = data.replace(\"\\x1b[200~\", \"\");\n\t\t}\n\n\t\t// If we're in a paste, buffer the data\n\t\tif (this.isInPaste) {\n\t\t\t// Append data to buffer first (end marker could be split across chunks)\n\t\t\tthis.pasteBuffer += data;\n\n\t\t\t// Check if the accumulated buffer contains the end marker\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(\"\\x1b[201~\");\n\t\t\tif (endIndex !== -1) {\n\t\t\t\t// Extract content before the end marker\n\t\t\t\tconst pasteContent = this.pasteBuffer.substring(0, endIndex);\n\n\t\t\t\t// Process the complete paste\n\t\t\t\tthis.handlePaste(pasteContent);\n\n\t\t\t\t// Reset paste state\n\t\t\t\tthis.isInPaste = false;\n\n\t\t\t\t// Process any remaining data after the end marker\n\t\t\t\tconst remaining = this.pasteBuffer.substring(endIndex + 6); // 6 = length of \\x1b[201~\n\t\t\t\tthis.pasteBuffer = \"\";\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.handleInput(remaining);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t// Still accumulating, wait for more data\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Handle special key combinations first\n\n\t\t// Ctrl+C - Exit (let parent handle this)\n\t\tif (data.charCodeAt(0) === 3) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle autocomplete special keys first (but don't block other input)\n\t\tif (this.isAutocompleting && this.autocompleteList) {\n\t\t\t// Escape - cancel autocomplete\n\t\t\tif (data === \"\\x1b\") {\n\t\t\t\tthis.cancelAutocomplete();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Let the autocomplete list handle navigation and selection\n\t\t\telse if (data === \"\\x1b[A\" || data === \"\\x1b[B\" || data === \"\\r\" || data === \"\\t\") {\n\t\t\t\t// Only pass arrow keys to the list, not Enter/Tab (we handle those directly)\n\t\t\t\tif (data === \"\\x1b[A\" || data === \"\\x1b[B\") {\n\t\t\t\t\tthis.autocompleteList.handleInput(data);\n\t\t\t\t}\n\n\t\t\t\t// If Tab or Enter was pressed, apply the selection\n\t\t\t\tif (data === \"\\t\" || data === \"\\r\") {\n\t\t\t\t\tconst selected = this.autocompleteList.getSelectedItem();\n\t\t\t\t\tif (selected && this.autocompleteProvider) {\n\t\t\t\t\t\tconst result = this.autocompleteProvider.applyCompletion(\n\t\t\t\t\t\t\tthis.state.lines,\n\t\t\t\t\t\t\tthis.state.cursorLine,\n\t\t\t\t\t\t\tthis.state.cursorCol,\n\t\t\t\t\t\t\tselected,\n\t\t\t\t\t\t\tthis.autocompletePrefix,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tthis.state.lines = result.lines;\n\t\t\t\t\t\tthis.state.cursorLine = result.cursorLine;\n\t\t\t\t\t\tthis.state.cursorCol = result.cursorCol;\n\n\t\t\t\t\t\tthis.cancelAutocomplete();\n\n\t\t\t\t\t\tif (this.onChange) {\n\t\t\t\t\t\t\tthis.onChange(this.getText());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\t// For other keys, handle normally within autocomplete\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// For other keys (like regular typing), DON'T return here\n\t\t\t// Let them fall through to normal character handling\n\t\t}\n\n\t\t// Tab key - context-aware completion (but not when already autocompleting)\n\t\tif (data === \"\\t\" && !this.isAutocompleting) {\n\t\t\tthis.handleTabCompletion();\n\t\t\treturn;\n\t\t}\n\n\t\t// Continue with rest of input handling\n\t\t// Ctrl+K - Delete current line\n\t\tif (data.charCodeAt(0) === 11) {\n\t\t\tthis.deleteCurrentLine();\n\t\t}\n\t\t// Ctrl+A - Move to start of line\n\t\telse if (data.charCodeAt(0) === 1) {\n\t\t\tthis.moveToLineStart();\n\t\t}\n\t\t// Ctrl+E - Move to end of line\n\t\telse if (data.charCodeAt(0) === 5) {\n\t\t\tthis.moveToLineEnd();\n\t\t}\n\t\t// New line shortcuts (but not plain LF/CR which should be submit)\n\t\telse if (\n\t\t\t(data.charCodeAt(0) === 10 && data.length > 1) || // Ctrl+Enter with modifiers\n\t\t\tdata === \"\\x1b\\r\" || // Option+Enter in some terminals\n\t\t\tdata === \"\\x1b[13;2~\" || // Shift+Enter in some terminals\n\t\t\t(data.length > 1 && data.includes(\"\\x1b\") && data.includes(\"\\r\")) ||\n\t\t\t(data === \"\\n\" && data.length === 1) || // Shift+Enter from iTerm2 mapping\n\t\t\tdata === \"\\\\\\r\" // Shift+Enter in VS Code terminal\n\t\t) {\n\t\t\t// Modifier + Enter = new line\n\t\t\tthis.addNewLine();\n\t\t}\n\t\t// Plain Enter (char code 13 for CR) - only CR submits, LF adds new line\n\t\telse if (data.charCodeAt(0) === 13 && data.length === 1) {\n\t\t\t// If submit is disabled, do nothing\n\t\t\tif (this.disableSubmit) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get text and substitute paste markers with actual content\n\t\t\tlet result = this.state.lines.join(\"\\n\").trim();\n\n\t\t\t// Replace all [paste #N +xxx lines] markers with actual paste content\n\t\t\tfor (const [pasteId, pasteContent] of this.pastes) {\n\t\t\t\t// Match both old format [paste #N] and new format [paste #N +xxx lines]\n\t\t\t\tconst markerRegex = new RegExp(`\\\\[paste #${pasteId}( \\\\+\\\\d+ lines)?\\\\]`, \"g\");\n\t\t\t\tresult = result.replace(markerRegex, pasteContent);\n\t\t\t}\n\n\t\t\t// Reset editor and clear pastes\n\t\t\tthis.state = {\n\t\t\t\tlines: [\"\"],\n\t\t\t\tcursorLine: 0,\n\t\t\t\tcursorCol: 0,\n\t\t\t};\n\t\t\tthis.pastes.clear();\n\t\t\tthis.pasteCounter = 0;\n\n\t\t\t// Notify that editor is now empty\n\t\t\tif (this.onChange) {\n\t\t\t\tthis.onChange(\"\");\n\t\t\t}\n\n\t\t\tif (this.onSubmit) {\n\t\t\t\tthis.onSubmit(result);\n\t\t\t}\n\t\t}\n\t\t// Backspace\n\t\telse if (data.charCodeAt(0) === 127 || data.charCodeAt(0) === 8) {\n\t\t\tthis.handleBackspace();\n\t\t}\n\t\t// Line navigation shortcuts (Home/End keys)\n\t\telse if (data === \"\\x1b[H\" || data === \"\\x1b[1~\" || data === \"\\x1b[7~\") {\n\t\t\t// Home key\n\t\t\tthis.moveToLineStart();\n\t\t} else if (data === \"\\x1b[F\" || data === \"\\x1b[4~\" || data === \"\\x1b[8~\") {\n\t\t\t// End key\n\t\t\tthis.moveToLineEnd();\n\t\t}\n\t\t// Forward delete (Fn+Backspace or Delete key)\n\t\telse if (data === \"\\x1b[3~\") {\n\t\t\t// Delete key\n\t\t\tthis.handleForwardDelete();\n\t\t}\n\t\t// Arrow keys\n\t\telse if (data === \"\\x1b[A\") {\n\t\t\t// Up\n\t\t\tthis.moveCursor(-1, 0);\n\t\t} else if (data === \"\\x1b[B\") {\n\t\t\t// Down\n\t\t\tthis.moveCursor(1, 0);\n\t\t} else if (data === \"\\x1b[C\") {\n\t\t\t// Right\n\t\t\tthis.moveCursor(0, 1);\n\t\t} else if (data === \"\\x1b[D\") {\n\t\t\t// Left\n\t\t\tthis.moveCursor(0, -1);\n\t\t}\n\t\t// Regular characters (printable ASCII)\n\t\telse if (data.charCodeAt(0) >= 32 && data.charCodeAt(0) <= 126) {\n\t\t\tthis.insertCharacter(data);\n\t\t}\n\t}\n\n\tprivate layoutText(contentWidth: number): LayoutLine[] {\n\t\tconst layoutLines: LayoutLine[] = [];\n\n\t\tif (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === \"\")) {\n\t\t\t// Empty editor\n\t\t\tlayoutLines.push({\n\t\t\t\ttext: \"\",\n\t\t\t\thasCursor: true,\n\t\t\t\tcursorPos: 0,\n\t\t\t});\n\t\t\treturn layoutLines;\n\t\t}\n\n\t\t// Process each logical line\n\t\tfor (let i = 0; i < this.state.lines.length; i++) {\n\t\t\tconst line = this.state.lines[i] || \"\";\n\t\t\tconst isCurrentLine = i === this.state.cursorLine;\n\t\t\tconst maxLineLength = contentWidth;\n\n\t\t\tif (line.length <= maxLineLength) {\n\t\t\t\t// Line fits in one layout line\n\t\t\t\tif (isCurrentLine) {\n\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\ttext: line,\n\t\t\t\t\t\thasCursor: true,\n\t\t\t\t\t\tcursorPos: this.state.cursorCol,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\ttext: line,\n\t\t\t\t\t\thasCursor: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Line needs wrapping\n\t\t\t\tconst chunks = [];\n\t\t\t\tfor (let pos = 0; pos < line.length; pos += maxLineLength) {\n\t\t\t\t\tchunks.push(line.slice(pos, pos + maxLineLength));\n\t\t\t\t}\n\n\t\t\t\tfor (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n\t\t\t\t\tconst chunk = chunks[chunkIndex];\n\t\t\t\t\tif (!chunk) continue;\n\n\t\t\t\t\tconst chunkStart = chunkIndex * maxLineLength;\n\t\t\t\t\tconst chunkEnd = chunkStart + chunk.length;\n\t\t\t\t\tconst cursorPos = this.state.cursorCol;\n\t\t\t\t\tconst hasCursorInChunk = isCurrentLine && cursorPos >= chunkStart && cursorPos <= chunkEnd;\n\n\t\t\t\t\tif (hasCursorInChunk) {\n\t\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\t\ttext: chunk,\n\t\t\t\t\t\t\thasCursor: true,\n\t\t\t\t\t\t\tcursorPos: cursorPos - chunkStart,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlayoutLines.push({\n\t\t\t\t\t\t\ttext: chunk,\n\t\t\t\t\t\t\thasCursor: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn layoutLines;\n\t}\n\n\tgetText(): string {\n\t\treturn this.state.lines.join(\"\\n\");\n\t}\n\n\tsetText(text: string): void {\n\t\t// Split text into lines, handling different line endings\n\t\tconst lines = text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\n\t\t// Ensure at least one empty line\n\t\tthis.state.lines = lines.length === 0 ? [\"\"] : lines;\n\n\t\t// Reset cursor to end of text\n\t\tthis.state.cursorLine = this.state.lines.length - 1;\n\t\tthis.state.cursorCol = this.state.lines[this.state.cursorLine]?.length || 0;\n\n\t\t// Notify of change\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\t// All the editor methods from before...\n\tprivate insertCharacter(char: string): void {\n\t\tconst line = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tconst before = line.slice(0, this.state.cursorCol);\n\t\tconst after = line.slice(this.state.cursorCol);\n\n\t\tthis.state.lines[this.state.cursorLine] = before + char + after;\n\t\tthis.state.cursorCol += char.length; // Fix: increment by the length of the inserted string\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\n\t\t// Check if we should trigger or update autocomplete\n\t\tif (!this.isAutocompleting) {\n\t\t\t// Auto-trigger for \"/\" at the start of a line (slash commands)\n\t\t\tif (char === \"/\" && this.isAtStartOfMessage()) {\n\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t}\n\t\t\t// Also auto-trigger when typing letters in a slash command context\n\t\t\telse if (/[a-zA-Z0-9]/.test(char)) {\n\t\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\tconst textBeforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\t\t\t// Check if we're in a slash command with a space (i.e., typing arguments)\n\t\t\t\tif (textBeforeCursor.startsWith(\"/\") && textBeforeCursor.includes(\" \")) {\n\t\t\t\t\tthis.tryTriggerAutocomplete();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis.updateAutocomplete();\n\t\t}\n\t}\n\n\tprivate handlePaste(pastedText: string): void {\n\t\t// Clean the pasted text\n\t\tconst cleanText = pastedText.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\t\t// Convert tabs to spaces (4 spaces per tab)\n\t\tconst tabExpandedText = cleanText.replace(/\\t/g, \" \");\n\n\t\t// Filter out non-printable characters except newlines\n\t\tconst filteredText = tabExpandedText\n\t\t\t.split(\"\")\n\t\t\t.filter((char) => char === \"\\n\" || (char >= \" \" && char <= \"~\"))\n\t\t\t.join(\"\");\n\n\t\t// Split into lines\n\t\tconst pastedLines = filteredText.split(\"\\n\");\n\n\t\t// Check if this is a large paste (> 10 lines)\n\t\tif (pastedLines.length > 10) {\n\t\t\t// Store the paste and insert a marker\n\t\t\tthis.pasteCounter++;\n\t\t\tconst pasteId = this.pasteCounter;\n\t\t\tthis.pastes.set(pasteId, filteredText);\n\n\t\t\t// Insert marker like \"[paste #1 +123 lines]\"\n\t\t\tconst marker = `[paste #${pasteId} +${pastedLines.length} lines]`;\n\t\t\tfor (const char of marker) {\n\t\t\t\tthis.insertCharacter(char);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (pastedLines.length === 1) {\n\t\t\t// Single line - just insert each character\n\t\t\tconst text = pastedLines[0] || \"\";\n\t\t\tfor (const char of text) {\n\t\t\t\tthis.insertCharacter(char);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Multi-line paste - be very careful with array manipulation\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\t\tconst afterCursor = currentLine.slice(this.state.cursorCol);\n\n\t\t// Build the new lines array step by step\n\t\tconst newLines: string[] = [];\n\n\t\t// Add all lines before current line\n\t\tfor (let i = 0; i < this.state.cursorLine; i++) {\n\t\t\tnewLines.push(this.state.lines[i] || \"\");\n\t\t}\n\n\t\t// Add the first pasted line merged with before cursor text\n\t\tnewLines.push(beforeCursor + (pastedLines[0] || \"\"));\n\n\t\t// Add all middle pasted lines\n\t\tfor (let i = 1; i < pastedLines.length - 1; i++) {\n\t\t\tnewLines.push(pastedLines[i] || \"\");\n\t\t}\n\n\t\t// Add the last pasted line with after cursor text\n\t\tnewLines.push((pastedLines[pastedLines.length - 1] || \"\") + afterCursor);\n\n\t\t// Add all lines after current line\n\t\tfor (let i = this.state.cursorLine + 1; i < this.state.lines.length; i++) {\n\t\t\tnewLines.push(this.state.lines[i] || \"\");\n\t\t}\n\n\t\t// Replace the entire lines array\n\t\tthis.state.lines = newLines;\n\n\t\t// Update cursor position to end of pasted content\n\t\tthis.state.cursorLine += pastedLines.length - 1;\n\t\tthis.state.cursorCol = (pastedLines[pastedLines.length - 1] || \"\").length;\n\n\t\t// Notify of change\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate addNewLine(): void {\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\tconst after = currentLine.slice(this.state.cursorCol);\n\n\t\t// Split current line\n\t\tthis.state.lines[this.state.cursorLine] = before;\n\t\tthis.state.lines.splice(this.state.cursorLine + 1, 0, after);\n\n\t\t// Move cursor to start of new line\n\t\tthis.state.cursorLine++;\n\t\tthis.state.cursorCol = 0;\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate handleBackspace(): void {\n\t\tif (this.state.cursorCol > 0) {\n\t\t\t// Delete character in current line\n\t\t\tconst line = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\t\tconst before = line.slice(0, this.state.cursorCol - 1);\n\t\t\tconst after = line.slice(this.state.cursorCol);\n\n\t\t\tthis.state.lines[this.state.cursorLine] = before + after;\n\t\t\tthis.state.cursorCol--;\n\t\t} else if (this.state.cursorLine > 0) {\n\t\t\t// Merge with previous line\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst previousLine = this.state.lines[this.state.cursorLine - 1] || \"\";\n\n\t\t\tthis.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine, 1);\n\n\t\t\tthis.state.cursorLine--;\n\t\t\tthis.state.cursorCol = previousLine.length;\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\n\t\t// Update autocomplete after backspace\n\t\tif (this.isAutocompleting) {\n\t\t\tthis.updateAutocomplete();\n\t\t}\n\t}\n\n\tprivate moveToLineStart(): void {\n\t\tthis.state.cursorCol = 0;\n\t}\n\n\tprivate moveToLineEnd(): void {\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tthis.state.cursorCol = currentLine.length;\n\t}\n\n\tprivate handleForwardDelete(): void {\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\n\t\tif (this.state.cursorCol < currentLine.length) {\n\t\t\t// Delete character at cursor position (forward delete)\n\t\t\tconst before = currentLine.slice(0, this.state.cursorCol);\n\t\t\tconst after = currentLine.slice(this.state.cursorCol + 1);\n\t\t\tthis.state.lines[this.state.cursorLine] = before + after;\n\t\t} else if (this.state.cursorLine < this.state.lines.length - 1) {\n\t\t\t// At end of line - merge with next line\n\t\t\tconst nextLine = this.state.lines[this.state.cursorLine + 1] || \"\";\n\t\t\tthis.state.lines[this.state.cursorLine] = currentLine + nextLine;\n\t\t\tthis.state.lines.splice(this.state.cursorLine + 1, 1);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate deleteCurrentLine(): void {\n\t\tif (this.state.lines.length === 1) {\n\t\t\t// Only one line - just clear it\n\t\t\tthis.state.lines[0] = \"\";\n\t\t\tthis.state.cursorCol = 0;\n\t\t} else {\n\t\t\t// Multiple lines - remove current line\n\t\t\tthis.state.lines.splice(this.state.cursorLine, 1);\n\n\t\t\t// Adjust cursor position\n\t\t\tif (this.state.cursorLine >= this.state.lines.length) {\n\t\t\t\t// Was on last line, move to new last line\n\t\t\t\tthis.state.cursorLine = this.state.lines.length - 1;\n\t\t\t}\n\n\t\t\t// Clamp cursor column to new line length\n\t\t\tconst newLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tthis.state.cursorCol = Math.min(this.state.cursorCol, newLine.length);\n\t\t}\n\n\t\tif (this.onChange) {\n\t\t\tthis.onChange(this.getText());\n\t\t}\n\t}\n\n\tprivate moveCursor(deltaLine: number, deltaCol: number): void {\n\t\tif (deltaLine !== 0) {\n\t\t\tconst newLine = this.state.cursorLine + deltaLine;\n\t\t\tif (newLine >= 0 && newLine < this.state.lines.length) {\n\t\t\t\tthis.state.cursorLine = newLine;\n\t\t\t\t// Clamp cursor column to new line length\n\t\t\t\tconst line = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\t\tthis.state.cursorCol = Math.min(this.state.cursorCol, line.length);\n\t\t\t}\n\t\t}\n\n\t\tif (deltaCol !== 0) {\n\t\t\t// Move column\n\t\t\tconst newCol = this.state.cursorCol + deltaCol;\n\t\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\t\tconst maxCol = currentLine.length;\n\t\t\tthis.state.cursorCol = Math.max(0, Math.min(maxCol, newCol));\n\t\t}\n\t}\n\n\t// Helper method to check if cursor is at start of message (for slash command detection)\n\tprivate isAtStartOfMessage(): boolean {\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\n\t\t// At start if line is empty, only contains whitespace, or is just \"/\"\n\t\treturn beforeCursor.trim() === \"\" || beforeCursor.trim() === \"/\";\n\t}\n\n\t// Autocomplete methods\n\tprivate tryTriggerAutocomplete(explicitTab: boolean = false): void {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\t// Check if we should trigger file completion on Tab\n\t\tif (explicitTab) {\n\t\t\tconst provider = this.autocompleteProvider as CombinedAutocompleteProvider;\n\t\t\tconst shouldTrigger =\n\t\t\t\t!provider.shouldTriggerFileCompletion ||\n\t\t\t\tprovider.shouldTriggerFileCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol);\n\t\t\tif (!shouldTrigger) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst suggestions = this.autocompleteProvider.getSuggestions(\n\t\t\tthis.state.lines,\n\t\t\tthis.state.cursorLine,\n\t\t\tthis.state.cursorCol,\n\t\t);\n\n\t\tif (suggestions && suggestions.items.length > 0) {\n\t\t\tthis.autocompletePrefix = suggestions.prefix;\n\t\t\tthis.autocompleteList = new SelectList(suggestions.items, 5);\n\t\t\tthis.isAutocompleting = true;\n\t\t} else {\n\t\t\tthis.cancelAutocomplete();\n\t\t}\n\t}\n\n\tprivate handleTabCompletion(): void {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\tconst currentLine = this.state.lines[this.state.cursorLine] || \"\";\n\t\tconst beforeCursor = currentLine.slice(0, this.state.cursorCol);\n\n\t\t// Check if we're in a slash command context\n\t\tif (beforeCursor.trimStart().startsWith(\"/\")) {\n\t\t\tthis.handleSlashCommandCompletion();\n\t\t} else {\n\t\t\tthis.forceFileAutocomplete();\n\t\t}\n\t}\n\n\tprivate handleSlashCommandCompletion(): void {\n\t\t// For now, fall back to regular autocomplete (slash commands)\n\t\t// This can be extended later to handle command-specific argument completion\n\t\tthis.tryTriggerAutocomplete(true);\n\t}\n\n\tprivate forceFileAutocomplete(): void {\n\t\tif (!this.autocompleteProvider) return;\n\n\t\t// Check if provider has the force method\n\t\tconst provider = this.autocompleteProvider as any;\n\t\tif (!provider.getForceFileSuggestions) {\n\t\t\tthis.tryTriggerAutocomplete(true);\n\t\t\treturn;\n\t\t}\n\n\t\tconst suggestions = provider.getForceFileSuggestions(\n\t\t\tthis.state.lines,\n\t\t\tthis.state.cursorLine,\n\t\t\tthis.state.cursorCol,\n\t\t);\n\n\t\tif (suggestions && suggestions.items.length > 0) {\n\t\t\tthis.autocompletePrefix = suggestions.prefix;\n\t\t\tthis.autocompleteList = new SelectList(suggestions.items, 5);\n\t\t\tthis.isAutocompleting = true;\n\t\t} else {\n\t\t\tthis.cancelAutocomplete();\n\t\t}\n\t}\n\n\tprivate cancelAutocomplete(): void {\n\t\tthis.isAutocompleting = false;\n\t\tthis.autocompleteList = undefined as any;\n\t\tthis.autocompletePrefix = \"\";\n\t}\n\n\tpublic isShowingAutocomplete(): boolean {\n\t\treturn this.isAutocompleting;\n\t}\n\n\tprivate updateAutocomplete(): void {\n\t\tif (!this.isAutocompleting || !this.autocompleteProvider) return;\n\n\t\tconst suggestions = this.autocompleteProvider.getSuggestions(\n\t\t\tthis.state.lines,\n\t\t\tthis.state.cursorLine,\n\t\t\tthis.state.cursorCol,\n\t\t);\n\n\t\tif (suggestions && suggestions.items.length > 0) {\n\t\t\tthis.autocompletePrefix = suggestions.prefix;\n\t\t\tif (this.autocompleteList) {\n\t\t\t\t// Update the existing list with new items\n\t\t\t\tthis.autocompleteList = new SelectList(suggestions.items, 5);\n\t\t\t}\n\t\t} else {\n\t\t\t// No more matches, cancel autocomplete\n\t\t\tthis.cancelAutocomplete();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Component } from "../tui.js";
|
|
2
|
+
/**
|
|
3
|
+
* Input component - single-line text input with horizontal scrolling
|
|
4
|
+
*/
|
|
5
|
+
export declare class Input implements Component {
|
|
6
|
+
private value;
|
|
7
|
+
private cursor;
|
|
8
|
+
onSubmit?: (value: string) => void;
|
|
9
|
+
getValue(): string;
|
|
10
|
+
setValue(value: string): void;
|
|
11
|
+
handleInput(data: string): void;
|
|
12
|
+
render(width: number): string[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;GAEG;AACH,qBAAa,KAAM,YAAW,SAAS;IACtC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAa;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C,QAAQ,IAAI,MAAM,CAEjB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA4D9B;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqD9B;CACD","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { visibleWidth } from \"../utils.js\";\n\n/**\n * Input component - single-line text input with horizontal scrolling\n */\nexport class Input implements Component {\n\tprivate value: string = \"\";\n\tprivate cursor: number = 0; // Cursor position in the value\n\tpublic onSubmit?: (value: string) => void;\n\n\tgetValue(): string {\n\t\treturn this.value;\n\t}\n\n\tsetValue(value: string): void {\n\t\tthis.value = value;\n\t\tthis.cursor = Math.min(this.cursor, value.length);\n\t}\n\n\thandleInput(data: string): void {\n\t\t// Handle special keys\n\t\tif (data === \"\\r\" || data === \"\\n\") {\n\t\t\t// Enter - submit\n\t\t\tif (this.onSubmit) {\n\t\t\t\tthis.onSubmit(this.value);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x7f\" || data === \"\\x08\") {\n\t\t\t// Backspace\n\t\t\tif (this.cursor > 0) {\n\t\t\t\tthis.value = this.value.slice(0, this.cursor - 1) + this.value.slice(this.cursor);\n\t\t\t\tthis.cursor--;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[D\") {\n\t\t\t// Left arrow\n\t\t\tif (this.cursor > 0) {\n\t\t\t\tthis.cursor--;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[C\") {\n\t\t\t// Right arrow\n\t\t\tif (this.cursor < this.value.length) {\n\t\t\t\tthis.cursor++;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[3~\") {\n\t\t\t// Delete\n\t\t\tif (this.cursor < this.value.length) {\n\t\t\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + 1);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x01\") {\n\t\t\t// Ctrl+A - beginning of line\n\t\t\tthis.cursor = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x05\") {\n\t\t\t// Ctrl+E - end of line\n\t\t\tthis.cursor = this.value.length;\n\t\t\treturn;\n\t\t}\n\n\t\t// Regular character input\n\t\tif (data.length === 1 && data >= \" \" && data <= \"~\") {\n\t\t\tthis.value = this.value.slice(0, this.cursor) + data + this.value.slice(this.cursor);\n\t\t\tthis.cursor++;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate visible window\n\t\tconst prompt = \"> \";\n\t\tconst availableWidth = width - prompt.length;\n\n\t\tif (availableWidth <= 0) {\n\t\t\treturn [prompt];\n\t\t}\n\n\t\tlet visibleText = \"\";\n\t\tlet cursorDisplay = this.cursor;\n\n\t\tif (this.value.length < availableWidth) {\n\t\t\t// Everything fits (leave room for cursor at end)\n\t\t\tvisibleText = this.value;\n\t\t} else {\n\t\t\t// Need horizontal scrolling\n\t\t\t// Reserve one character for cursor if it's at the end\n\t\t\tconst scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;\n\t\t\tconst halfWidth = Math.floor(scrollWidth / 2);\n\n\t\t\tif (this.cursor < halfWidth) {\n\t\t\t\t// Cursor near start\n\t\t\t\tvisibleText = this.value.slice(0, scrollWidth);\n\t\t\t\tcursorDisplay = this.cursor;\n\t\t\t} else if (this.cursor > this.value.length - halfWidth) {\n\t\t\t\t// Cursor near end\n\t\t\t\tvisibleText = this.value.slice(this.value.length - scrollWidth);\n\t\t\t\tcursorDisplay = scrollWidth - (this.value.length - this.cursor);\n\t\t\t} else {\n\t\t\t\t// Cursor in middle\n\t\t\t\tconst start = this.cursor - halfWidth;\n\t\t\t\tvisibleText = this.value.slice(start, start + scrollWidth);\n\t\t\t\tcursorDisplay = halfWidth;\n\t\t\t}\n\t\t}\n\n\t\t// Build line with fake cursor\n\t\t// Insert cursor character at cursor position\n\t\tconst beforeCursor = visibleText.slice(0, cursorDisplay);\n\t\tconst atCursor = visibleText[cursorDisplay] || \" \"; // Character at cursor, or space if at end\n\t\tconst afterCursor = visibleText.slice(cursorDisplay + 1);\n\n\t\t// Use inverse video to show cursor\n\t\tconst cursorChar = `\\x1b[7m${atCursor}\\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal\n\t\tconst textWithCursor = beforeCursor + cursorChar + afterCursor;\n\n\t\t// Calculate visual width\n\t\tconst visualLength = visibleWidth(textWithCursor);\n\t\tconst padding = \" \".repeat(Math.max(0, availableWidth - visualLength));\n\t\tconst line = prompt + textWithCursor + padding;\n\n\t\treturn [line];\n\t}\n}\n"]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { visibleWidth } from "../utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* Input component - single-line text input with horizontal scrolling
|
|
4
|
+
*/
|
|
5
|
+
export class Input {
|
|
6
|
+
value = "";
|
|
7
|
+
cursor = 0; // Cursor position in the value
|
|
8
|
+
onSubmit;
|
|
9
|
+
getValue() {
|
|
10
|
+
return this.value;
|
|
11
|
+
}
|
|
12
|
+
setValue(value) {
|
|
13
|
+
this.value = value;
|
|
14
|
+
this.cursor = Math.min(this.cursor, value.length);
|
|
15
|
+
}
|
|
16
|
+
handleInput(data) {
|
|
17
|
+
// Handle special keys
|
|
18
|
+
if (data === "\r" || data === "\n") {
|
|
19
|
+
// Enter - submit
|
|
20
|
+
if (this.onSubmit) {
|
|
21
|
+
this.onSubmit(this.value);
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (data === "\x7f" || data === "\x08") {
|
|
26
|
+
// Backspace
|
|
27
|
+
if (this.cursor > 0) {
|
|
28
|
+
this.value = this.value.slice(0, this.cursor - 1) + this.value.slice(this.cursor);
|
|
29
|
+
this.cursor--;
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (data === "\x1b[D") {
|
|
34
|
+
// Left arrow
|
|
35
|
+
if (this.cursor > 0) {
|
|
36
|
+
this.cursor--;
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (data === "\x1b[C") {
|
|
41
|
+
// Right arrow
|
|
42
|
+
if (this.cursor < this.value.length) {
|
|
43
|
+
this.cursor++;
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (data === "\x1b[3~") {
|
|
48
|
+
// Delete
|
|
49
|
+
if (this.cursor < this.value.length) {
|
|
50
|
+
this.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + 1);
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (data === "\x01") {
|
|
55
|
+
// Ctrl+A - beginning of line
|
|
56
|
+
this.cursor = 0;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (data === "\x05") {
|
|
60
|
+
// Ctrl+E - end of line
|
|
61
|
+
this.cursor = this.value.length;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Regular character input
|
|
65
|
+
if (data.length === 1 && data >= " " && data <= "~") {
|
|
66
|
+
this.value = this.value.slice(0, this.cursor) + data + this.value.slice(this.cursor);
|
|
67
|
+
this.cursor++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
render(width) {
|
|
71
|
+
// Calculate visible window
|
|
72
|
+
const prompt = "> ";
|
|
73
|
+
const availableWidth = width - prompt.length;
|
|
74
|
+
if (availableWidth <= 0) {
|
|
75
|
+
return [prompt];
|
|
76
|
+
}
|
|
77
|
+
let visibleText = "";
|
|
78
|
+
let cursorDisplay = this.cursor;
|
|
79
|
+
if (this.value.length < availableWidth) {
|
|
80
|
+
// Everything fits (leave room for cursor at end)
|
|
81
|
+
visibleText = this.value;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Need horizontal scrolling
|
|
85
|
+
// Reserve one character for cursor if it's at the end
|
|
86
|
+
const scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;
|
|
87
|
+
const halfWidth = Math.floor(scrollWidth / 2);
|
|
88
|
+
if (this.cursor < halfWidth) {
|
|
89
|
+
// Cursor near start
|
|
90
|
+
visibleText = this.value.slice(0, scrollWidth);
|
|
91
|
+
cursorDisplay = this.cursor;
|
|
92
|
+
}
|
|
93
|
+
else if (this.cursor > this.value.length - halfWidth) {
|
|
94
|
+
// Cursor near end
|
|
95
|
+
visibleText = this.value.slice(this.value.length - scrollWidth);
|
|
96
|
+
cursorDisplay = scrollWidth - (this.value.length - this.cursor);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Cursor in middle
|
|
100
|
+
const start = this.cursor - halfWidth;
|
|
101
|
+
visibleText = this.value.slice(start, start + scrollWidth);
|
|
102
|
+
cursorDisplay = halfWidth;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Build line with fake cursor
|
|
106
|
+
// Insert cursor character at cursor position
|
|
107
|
+
const beforeCursor = visibleText.slice(0, cursorDisplay);
|
|
108
|
+
const atCursor = visibleText[cursorDisplay] || " "; // Character at cursor, or space if at end
|
|
109
|
+
const afterCursor = visibleText.slice(cursorDisplay + 1);
|
|
110
|
+
// Use inverse video to show cursor
|
|
111
|
+
const cursorChar = `\x1b[7m${atCursor}\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal
|
|
112
|
+
const textWithCursor = beforeCursor + cursorChar + afterCursor;
|
|
113
|
+
// Calculate visual width
|
|
114
|
+
const visualLength = visibleWidth(textWithCursor);
|
|
115
|
+
const padding = " ".repeat(Math.max(0, availableWidth - visualLength));
|
|
116
|
+
const line = prompt + textWithCursor + padding;
|
|
117
|
+
return [line];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { visibleWidth } from \"../utils.js\";\n\n/**\n * Input component - single-line text input with horizontal scrolling\n */\nexport class Input implements Component {\n\tprivate value: string = \"\";\n\tprivate cursor: number = 0; // Cursor position in the value\n\tpublic onSubmit?: (value: string) => void;\n\n\tgetValue(): string {\n\t\treturn this.value;\n\t}\n\n\tsetValue(value: string): void {\n\t\tthis.value = value;\n\t\tthis.cursor = Math.min(this.cursor, value.length);\n\t}\n\n\thandleInput(data: string): void {\n\t\t// Handle special keys\n\t\tif (data === \"\\r\" || data === \"\\n\") {\n\t\t\t// Enter - submit\n\t\t\tif (this.onSubmit) {\n\t\t\t\tthis.onSubmit(this.value);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x7f\" || data === \"\\x08\") {\n\t\t\t// Backspace\n\t\t\tif (this.cursor > 0) {\n\t\t\t\tthis.value = this.value.slice(0, this.cursor - 1) + this.value.slice(this.cursor);\n\t\t\t\tthis.cursor--;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[D\") {\n\t\t\t// Left arrow\n\t\t\tif (this.cursor > 0) {\n\t\t\t\tthis.cursor--;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[C\") {\n\t\t\t// Right arrow\n\t\t\tif (this.cursor < this.value.length) {\n\t\t\t\tthis.cursor++;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x1b[3~\") {\n\t\t\t// Delete\n\t\t\tif (this.cursor < this.value.length) {\n\t\t\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + 1);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x01\") {\n\t\t\t// Ctrl+A - beginning of line\n\t\t\tthis.cursor = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (data === \"\\x05\") {\n\t\t\t// Ctrl+E - end of line\n\t\t\tthis.cursor = this.value.length;\n\t\t\treturn;\n\t\t}\n\n\t\t// Regular character input\n\t\tif (data.length === 1 && data >= \" \" && data <= \"~\") {\n\t\t\tthis.value = this.value.slice(0, this.cursor) + data + this.value.slice(this.cursor);\n\t\t\tthis.cursor++;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate visible window\n\t\tconst prompt = \"> \";\n\t\tconst availableWidth = width - prompt.length;\n\n\t\tif (availableWidth <= 0) {\n\t\t\treturn [prompt];\n\t\t}\n\n\t\tlet visibleText = \"\";\n\t\tlet cursorDisplay = this.cursor;\n\n\t\tif (this.value.length < availableWidth) {\n\t\t\t// Everything fits (leave room for cursor at end)\n\t\t\tvisibleText = this.value;\n\t\t} else {\n\t\t\t// Need horizontal scrolling\n\t\t\t// Reserve one character for cursor if it's at the end\n\t\t\tconst scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;\n\t\t\tconst halfWidth = Math.floor(scrollWidth / 2);\n\n\t\t\tif (this.cursor < halfWidth) {\n\t\t\t\t// Cursor near start\n\t\t\t\tvisibleText = this.value.slice(0, scrollWidth);\n\t\t\t\tcursorDisplay = this.cursor;\n\t\t\t} else if (this.cursor > this.value.length - halfWidth) {\n\t\t\t\t// Cursor near end\n\t\t\t\tvisibleText = this.value.slice(this.value.length - scrollWidth);\n\t\t\t\tcursorDisplay = scrollWidth - (this.value.length - this.cursor);\n\t\t\t} else {\n\t\t\t\t// Cursor in middle\n\t\t\t\tconst start = this.cursor - halfWidth;\n\t\t\t\tvisibleText = this.value.slice(start, start + scrollWidth);\n\t\t\t\tcursorDisplay = halfWidth;\n\t\t\t}\n\t\t}\n\n\t\t// Build line with fake cursor\n\t\t// Insert cursor character at cursor position\n\t\tconst beforeCursor = visibleText.slice(0, cursorDisplay);\n\t\tconst atCursor = visibleText[cursorDisplay] || \" \"; // Character at cursor, or space if at end\n\t\tconst afterCursor = visibleText.slice(cursorDisplay + 1);\n\n\t\t// Use inverse video to show cursor\n\t\tconst cursorChar = `\\x1b[7m${atCursor}\\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal\n\t\tconst textWithCursor = beforeCursor + cursorChar + afterCursor;\n\n\t\t// Calculate visual width\n\t\tconst visualLength = visibleWidth(textWithCursor);\n\t\tconst padding = \" \".repeat(Math.max(0, availableWidth - visualLength));\n\t\tconst line = prompt + textWithCursor + padding;\n\n\t\treturn [line];\n\t}\n}\n"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { TUI } from "../tui.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Text } from "./text.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* Simulates the animation component that causes flicker in single-buffer mode
|
|
4
|
+
* Loader component that updates every 80ms with spinning animation
|
|
6
5
|
*/
|
|
7
|
-
export declare class
|
|
6
|
+
export declare class Loader extends Text {
|
|
8
7
|
private message;
|
|
9
8
|
private frames;
|
|
10
9
|
private currentFrame;
|
|
11
10
|
private intervalId;
|
|
12
11
|
private ui;
|
|
13
12
|
constructor(ui: TUI, message?: string);
|
|
13
|
+
render(width: number): string[];
|
|
14
14
|
start(): void;
|
|
15
15
|
stop(): void;
|
|
16
16
|
setMessage(message: string): void;
|
|
17
17
|
private updateDisplay;
|
|
18
18
|
}
|
|
19
|
-
//# sourceMappingURL=
|
|
19
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;GAEG;AACH,qBAAa,MAAO,SAAQ,IAAI;IAQ9B,OAAO,CAAC,OAAO;IAPhB,OAAO,CAAC,MAAM,CAAsD;IACpE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,EAAE,CAAoB;IAE9B,YACC,EAAE,EAAE,GAAG,EACC,OAAO,GAAE,MAAqB,EAKtC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;IAED,KAAK,SAMJ;IAED,IAAI,SAKH;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,QAGzB;IAED,OAAO,CAAC,aAAa;CAOrB","sourcesContent":["import chalk from \"chalk\";\nimport type { TUI } from \"../tui.js\";\nimport { Text } from \"./text.js\";\n\n/**\n * Loader component that updates every 80ms with spinning animation\n */\nexport class Loader extends Text {\n\tprivate frames = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\tprivate currentFrame = 0;\n\tprivate intervalId: NodeJS.Timeout | null = null;\n\tprivate ui: TUI | null = null;\n\n\tconstructor(\n\t\tui: TUI,\n\t\tprivate message: string = \"Loading...\",\n\t) {\n\t\tsuper(\"\", 1, 0);\n\t\tthis.ui = ui;\n\t\tthis.start();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [\"\", ...super.render(width)];\n\t}\n\n\tstart() {\n\t\tthis.updateDisplay();\n\t\tthis.intervalId = setInterval(() => {\n\t\t\tthis.currentFrame = (this.currentFrame + 1) % this.frames.length;\n\t\t\tthis.updateDisplay();\n\t\t}, 80);\n\t}\n\n\tstop() {\n\t\tif (this.intervalId) {\n\t\t\tclearInterval(this.intervalId);\n\t\t\tthis.intervalId = null;\n\t\t}\n\t}\n\n\tsetMessage(message: string) {\n\t\tthis.message = message;\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay() {\n\t\tconst frame = this.frames[this.currentFrame];\n\t\tthis.setText(`${chalk.cyan(frame)} ${chalk.dim(this.message)}`);\n\t\tif (this.ui) {\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n}\n"]}
|