erosolar-cli 1.7.194 → 1.7.196

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.
Files changed (68) hide show
  1. package/dist/core/agent.d.ts +6 -0
  2. package/dist/core/agent.d.ts.map +1 -1
  3. package/dist/core/agent.js +10 -1
  4. package/dist/core/agent.js.map +1 -1
  5. package/dist/core/errors/errorUtils.d.ts +87 -0
  6. package/dist/core/errors/errorUtils.d.ts.map +1 -0
  7. package/dist/core/errors/errorUtils.js +158 -0
  8. package/dist/core/errors/errorUtils.js.map +1 -0
  9. package/dist/core/resultVerification.js.map +1 -1
  10. package/dist/core/toolValidation.d.ts +117 -0
  11. package/dist/core/toolValidation.d.ts.map +1 -0
  12. package/dist/core/toolValidation.js +282 -0
  13. package/dist/core/toolValidation.js.map +1 -0
  14. package/dist/core/types/utilityTypes.d.ts +192 -0
  15. package/dist/core/types/utilityTypes.d.ts.map +1 -0
  16. package/dist/core/types/utilityTypes.js +272 -0
  17. package/dist/core/types/utilityTypes.js.map +1 -0
  18. package/dist/shell/interactiveShell.d.ts +9 -0
  19. package/dist/shell/interactiveShell.d.ts.map +1 -1
  20. package/dist/shell/interactiveShell.js +69 -1
  21. package/dist/shell/interactiveShell.js.map +1 -1
  22. package/dist/shell/systemPrompt.d.ts.map +1 -1
  23. package/dist/shell/systemPrompt.js +5 -0
  24. package/dist/shell/systemPrompt.js.map +1 -1
  25. package/dist/shell/terminalInput.d.ts +1 -0
  26. package/dist/shell/terminalInput.d.ts.map +1 -1
  27. package/dist/shell/terminalInput.js +9 -2
  28. package/dist/shell/terminalInput.js.map +1 -1
  29. package/dist/shell/terminalInputAdapter.d.ts +4 -0
  30. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  31. package/dist/shell/terminalInputAdapter.js +6 -0
  32. package/dist/shell/terminalInputAdapter.js.map +1 -1
  33. package/dist/tools/planningTools.d.ts +1 -0
  34. package/dist/tools/planningTools.d.ts.map +1 -1
  35. package/dist/tools/planningTools.js +48 -0
  36. package/dist/tools/planningTools.js.map +1 -1
  37. package/dist/ui/display.d.ts +5 -49
  38. package/dist/ui/display.d.ts.map +1 -1
  39. package/dist/ui/display.js +36 -335
  40. package/dist/ui/display.js.map +1 -1
  41. package/dist/ui/toolDisplay.d.ts.map +1 -1
  42. package/dist/ui/toolDisplay.js +17 -0
  43. package/dist/ui/toolDisplay.js.map +1 -1
  44. package/dist/utils/planFormatter.d.ts +34 -0
  45. package/dist/utils/planFormatter.d.ts.map +1 -0
  46. package/dist/utils/planFormatter.js +140 -0
  47. package/dist/utils/planFormatter.js.map +1 -0
  48. package/package.json +2 -2
  49. package/dist/shell/bracketedPasteManager.d.ts +0 -128
  50. package/dist/shell/bracketedPasteManager.d.ts.map +0 -1
  51. package/dist/shell/bracketedPasteManager.enhanced.d.ts +0 -2
  52. package/dist/shell/bracketedPasteManager.enhanced.d.ts.map +0 -1
  53. package/dist/shell/bracketedPasteManager.enhanced.js +0 -4
  54. package/dist/shell/bracketedPasteManager.enhanced.js.map +0 -1
  55. package/dist/shell/bracketedPasteManager.js +0 -372
  56. package/dist/shell/bracketedPasteManager.js.map +0 -1
  57. package/dist/shell/chatBox.d.ts +0 -228
  58. package/dist/shell/chatBox.d.ts.map +0 -1
  59. package/dist/shell/chatBox.js +0 -811
  60. package/dist/shell/chatBox.js.map +0 -1
  61. package/dist/shell/unifiedChatBox.d.ts +0 -194
  62. package/dist/shell/unifiedChatBox.d.ts.map +0 -1
  63. package/dist/shell/unifiedChatBox.js +0 -585
  64. package/dist/shell/unifiedChatBox.js.map +0 -1
  65. package/dist/ui/persistentPrompt.d.ts +0 -545
  66. package/dist/ui/persistentPrompt.d.ts.map +0 -1
  67. package/dist/ui/persistentPrompt.js +0 -1529
  68. package/dist/ui/persistentPrompt.js.map +0 -1
@@ -1,811 +0,0 @@
1
- /**
2
- * ChatBox - Claude Code style input box at the bottom of terminal
3
- *
4
- * This is the ONLY place the cursor can ever be. Features:
5
- * - Multi-line text input
6
- * - Cursor navigation (arrows, home, end, word jump)
7
- * - Line wrapping for long lines
8
- * - Scrolling for content exceeding visible height
9
- * - Always visible at bottom of terminal
10
- * - Scroll region management for output area above
11
- */
12
- import { EventEmitter } from 'node:events';
13
- import * as readline from 'node:readline';
14
- import { theme } from '../ui/theme.js';
15
- /**
16
- * ANSI escape codes for terminal control
17
- */
18
- const ANSI = {
19
- SAVE_CURSOR: '\u001b7',
20
- RESTORE_CURSOR: '\u001b8',
21
- CURSOR_TO: (row, col) => `\u001b[${row};${col}H`,
22
- CURSOR_UP: (n) => `\u001b[${n}A`,
23
- CURSOR_DOWN: (n) => `\u001b[${n}B`,
24
- CURSOR_FORWARD: (n) => `\u001b[${n}C`,
25
- CURSOR_BACK: (n) => `\u001b[${n}D`,
26
- CLEAR_LINE: '\u001b[2K',
27
- CLEAR_TO_END: '\u001b[0J',
28
- CLEAR_TO_START: '\u001b[1J',
29
- HIDE_CURSOR: '\u001b[?25l',
30
- SHOW_CURSOR: '\u001b[?25h',
31
- SET_SCROLL_REGION: (top, bottom) => `\u001b[${top};${bottom}r`,
32
- RESET_SCROLL_REGION: '\u001b[r',
33
- };
34
- /**
35
- * ChatBox - A multi-line text input widget fixed at the bottom of the terminal
36
- */
37
- export class ChatBox extends EventEmitter {
38
- config;
39
- writeStream;
40
- readStream;
41
- state = {
42
- lines: [''],
43
- cursorRow: 0,
44
- cursorCol: 0,
45
- scrollOffset: 0,
46
- isActive: false,
47
- };
48
- keypressHandler = null;
49
- resizeHandler = null;
50
- scrollRegionActive = false;
51
- originalStdoutWrite = null;
52
- outputIntercepted = false;
53
- constructor(config = {}, writeStream = process.stdout, readStream = process.stdin) {
54
- super();
55
- this.writeStream = writeStream;
56
- this.readStream = readStream;
57
- this.config = {
58
- visibleLines: config.visibleLines ?? 3,
59
- maxLines: config.maxLines ?? 100,
60
- prompt: config.prompt ?? '> ',
61
- showLineNumbers: config.showLineNumbers ?? false,
62
- };
63
- }
64
- /**
65
- * Get terminal dimensions
66
- */
67
- getTerminalSize() {
68
- return {
69
- rows: this.writeStream.rows || 24,
70
- cols: this.writeStream.columns || 80,
71
- };
72
- }
73
- /**
74
- * Safely get a line from the buffer, returning empty string if undefined
75
- */
76
- getLine(index) {
77
- return this.state.lines[index] ?? '';
78
- }
79
- /**
80
- * Safely set a line in the buffer
81
- */
82
- setLine(index, value) {
83
- if (index >= 0 && index < this.state.lines.length) {
84
- this.state.lines[index] = value;
85
- }
86
- }
87
- /**
88
- * Calculate the row where the chat box starts (from terminal top)
89
- */
90
- getChatBoxStartRow() {
91
- const { rows } = this.getTerminalSize();
92
- // Chat box occupies bottom N lines: separator (1) + visible lines + prompt indicator
93
- const boxHeight = 1 + this.config.visibleLines;
94
- return Math.max(1, rows - boxHeight);
95
- }
96
- /**
97
- * Set up scroll region to protect the chat box area at bottom.
98
- * All output written to stdout will scroll within the region above the chat box.
99
- */
100
- setupScrollRegion() {
101
- if (this.scrollRegionActive)
102
- return;
103
- const chatBoxStart = this.getChatBoxStartRow();
104
- // Save cursor, set scroll region (line 1 to line above chat box), restore cursor
105
- this.writeStream.write(ANSI.SAVE_CURSOR);
106
- this.writeStream.write(ANSI.SET_SCROLL_REGION(1, chatBoxStart - 1));
107
- this.writeStream.write(ANSI.RESTORE_CURSOR);
108
- this.scrollRegionActive = true;
109
- // Intercept stdout to ensure output goes to scroll region
110
- this.interceptOutput();
111
- }
112
- /**
113
- * Reset scroll region to full terminal
114
- */
115
- resetScrollRegion() {
116
- if (!this.scrollRegionActive)
117
- return;
118
- this.writeStream.write(ANSI.SAVE_CURSOR);
119
- this.writeStream.write(ANSI.RESET_SCROLL_REGION);
120
- this.writeStream.write(ANSI.RESTORE_CURSOR);
121
- this.scrollRegionActive = false;
122
- this.restoreOutput();
123
- }
124
- /**
125
- * Intercept stdout to position output correctly in scroll region
126
- */
127
- interceptOutput() {
128
- if (this.outputIntercepted)
129
- return;
130
- this.originalStdoutWrite = this.writeStream.write.bind(this.writeStream);
131
- const self = this;
132
- const chatBoxStart = this.getChatBoxStartRow();
133
- // Replace stdout.write to redirect output to scroll region
134
- this.writeStream.write = function (chunk, encodingOrCallback, callback) {
135
- if (!self.originalStdoutWrite || !self.state.isActive) {
136
- if (self.originalStdoutWrite) {
137
- if (typeof encodingOrCallback === 'function') {
138
- return self.originalStdoutWrite(chunk, encodingOrCallback);
139
- }
140
- return self.originalStdoutWrite(chunk, encodingOrCallback, callback);
141
- }
142
- return true;
143
- }
144
- // Save cursor, move to output area, write, restore cursor to chat box
145
- const output = typeof chunk === 'string' ? chunk : chunk.toString();
146
- // Skip if it's just cursor positioning
147
- if (/^\x1b\[\d*[ABCDGHJ]/.test(output) && output.length < 10) {
148
- return true;
149
- }
150
- // Position at end of scroll region for output
151
- self.originalStdoutWrite(ANSI.SAVE_CURSOR);
152
- self.originalStdoutWrite(ANSI.CURSOR_TO(chatBoxStart - 1, 1));
153
- // Write the actual content
154
- let result;
155
- if (typeof encodingOrCallback === 'function') {
156
- result = self.originalStdoutWrite(chunk, encodingOrCallback);
157
- }
158
- else {
159
- result = self.originalStdoutWrite(chunk, encodingOrCallback, callback);
160
- }
161
- // Restore cursor to chat box
162
- self.originalStdoutWrite(ANSI.RESTORE_CURSOR);
163
- return result;
164
- };
165
- this.outputIntercepted = true;
166
- }
167
- /**
168
- * Restore original stdout.write
169
- */
170
- restoreOutput() {
171
- if (!this.outputIntercepted || !this.originalStdoutWrite)
172
- return;
173
- this.writeStream.write = this.originalStdoutWrite;
174
- this.originalStdoutWrite = null;
175
- this.outputIntercepted = false;
176
- }
177
- /**
178
- * Write to the output area (above the chat box)
179
- * This is the proper way for other code to output while chat box is active
180
- */
181
- writeToOutput(text) {
182
- if (!this.state.isActive) {
183
- this.writeStream.write(text);
184
- return;
185
- }
186
- const chatBoxStart = this.getChatBoxStartRow();
187
- // Save cursor, move to scroll region, write, restore cursor
188
- this.writeStream.write(ANSI.SAVE_CURSOR);
189
- this.writeStream.write(ANSI.CURSOR_TO(chatBoxStart - 1, 1));
190
- // Use original write if intercepted
191
- if (this.originalStdoutWrite) {
192
- this.originalStdoutWrite(text);
193
- }
194
- else {
195
- this.writeStream.write(text);
196
- }
197
- this.writeStream.write(ANSI.RESTORE_CURSOR);
198
- this.render(); // Re-render chat box to ensure cursor position
199
- }
200
- /**
201
- * Activate the chat box - start listening for input
202
- */
203
- activate() {
204
- if (this.state.isActive)
205
- return;
206
- this.state.isActive = true;
207
- // Set up raw mode for capturing all keypresses
208
- if (this.readStream.isTTY) {
209
- this.readStream.setRawMode(true);
210
- }
211
- readline.emitKeypressEvents(this.readStream);
212
- // Handle keypresses
213
- this.keypressHandler = (str, key) => this.handleKeypress(str, key);
214
- this.readStream.on('keypress', this.keypressHandler);
215
- // Handle terminal resize
216
- this.resizeHandler = () => {
217
- // On resize, need to reset and re-setup scroll region
218
- if (this.scrollRegionActive) {
219
- this.resetScrollRegion();
220
- this.setupScrollRegion();
221
- }
222
- this.render();
223
- };
224
- this.writeStream.on('resize', this.resizeHandler);
225
- // Clear screen and set up scroll region
226
- this.clearAndSetup();
227
- // Initial render
228
- this.render();
229
- }
230
- /**
231
- * Clear screen and set up the chat box at bottom
232
- */
233
- clearAndSetup() {
234
- const chatBoxStart = this.getChatBoxStartRow();
235
- // Position cursor at start of chat box area
236
- this.writeStream.write(ANSI.CURSOR_TO(chatBoxStart, 1));
237
- // Set up scroll region
238
- this.setupScrollRegion();
239
- }
240
- /**
241
- * Deactivate the chat box - stop listening for input
242
- */
243
- deactivate() {
244
- if (!this.state.isActive)
245
- return;
246
- this.state.isActive = false;
247
- // Reset scroll region
248
- this.resetScrollRegion();
249
- // Remove event handlers
250
- if (this.keypressHandler) {
251
- this.readStream.off('keypress', this.keypressHandler);
252
- this.keypressHandler = null;
253
- }
254
- if (this.resizeHandler) {
255
- this.writeStream.off('resize', this.resizeHandler);
256
- this.resizeHandler = null;
257
- }
258
- // Restore raw mode
259
- if (this.readStream.isTTY) {
260
- this.readStream.setRawMode(false);
261
- }
262
- }
263
- /**
264
- * Handle keypress events
265
- */
266
- handleKeypress(str, key) {
267
- if (!key)
268
- return;
269
- // Handle special keys
270
- if (key.ctrl) {
271
- switch (key.name) {
272
- case 'c':
273
- this.emit('interrupt');
274
- return;
275
- case 'd':
276
- if (this.getText().length === 0) {
277
- this.emit('cancel');
278
- }
279
- return;
280
- case 'a': // Home
281
- this.cursorToLineStart();
282
- break;
283
- case 'e': // End
284
- this.cursorToLineEnd();
285
- break;
286
- case 'k': // Kill to end of line
287
- this.killToEndOfLine();
288
- break;
289
- case 'u': // Kill to start of line
290
- this.killToStartOfLine();
291
- break;
292
- case 'w': // Delete word backward
293
- this.deleteWordBackward();
294
- break;
295
- case 'left': // Word left
296
- this.cursorWordLeft();
297
- break;
298
- case 'right': // Word right
299
- this.cursorWordRight();
300
- break;
301
- case 'up': // Scroll up
302
- this.scrollUp();
303
- break;
304
- case 'down': // Scroll down
305
- this.scrollDown();
306
- break;
307
- }
308
- this.render();
309
- return;
310
- }
311
- if (key.meta) {
312
- switch (key.name) {
313
- case 'left': // Word left (alt+left)
314
- this.cursorWordLeft();
315
- break;
316
- case 'right': // Word right (alt+right)
317
- this.cursorWordRight();
318
- break;
319
- case 'backspace': // Delete word backward (alt+backspace)
320
- this.deleteWordBackward();
321
- break;
322
- }
323
- this.render();
324
- return;
325
- }
326
- switch (key.name) {
327
- case 'return':
328
- if (key.shift) {
329
- // Shift+Enter: insert newline
330
- this.insertNewline();
331
- }
332
- else {
333
- // Enter: submit
334
- this.submit();
335
- return;
336
- }
337
- break;
338
- case 'backspace':
339
- this.deleteBackward();
340
- break;
341
- case 'delete':
342
- this.deleteForward();
343
- break;
344
- case 'up':
345
- this.cursorUp();
346
- break;
347
- case 'down':
348
- this.cursorDown();
349
- break;
350
- case 'left':
351
- this.cursorLeft();
352
- break;
353
- case 'right':
354
- this.cursorRight();
355
- break;
356
- case 'home':
357
- this.cursorToLineStart();
358
- break;
359
- case 'end':
360
- this.cursorToLineEnd();
361
- break;
362
- case 'pageup':
363
- this.pageUp();
364
- break;
365
- case 'pagedown':
366
- this.pageDown();
367
- break;
368
- case 'tab':
369
- // Insert 2 spaces for tab
370
- this.insertText(' ');
371
- break;
372
- case 'escape':
373
- this.emit('cancel');
374
- return;
375
- default:
376
- // Insert printable characters
377
- if (str && str.length > 0 && !key.ctrl && !key.meta) {
378
- this.insertText(str);
379
- }
380
- break;
381
- }
382
- this.render();
383
- this.emit('change', this.getText());
384
- }
385
- /**
386
- * Insert text at cursor position
387
- */
388
- insertText(text) {
389
- const { cursorRow, cursorCol, lines } = this.state;
390
- const line = this.getLine(cursorRow);
391
- // Handle multi-character paste (may contain newlines)
392
- const parts = text.split('\n');
393
- if (parts.length === 1) {
394
- // Single line insert
395
- this.setLine(cursorRow, line.slice(0, cursorCol) + text + line.slice(cursorCol));
396
- this.state.cursorCol += text.length;
397
- }
398
- else {
399
- // Multi-line paste
400
- const before = line.slice(0, cursorCol);
401
- const after = line.slice(cursorCol);
402
- const firstPart = parts[0] ?? '';
403
- this.setLine(cursorRow, before + firstPart);
404
- for (let i = 1; i < parts.length; i++) {
405
- lines.splice(cursorRow + i, 0, parts[i] ?? '');
406
- }
407
- const lastIdx = cursorRow + parts.length - 1;
408
- this.setLine(lastIdx, this.getLine(lastIdx) + after);
409
- this.state.cursorRow += parts.length - 1;
410
- const lastPart = parts[parts.length - 1];
411
- this.state.cursorCol = lastPart?.length ?? 0;
412
- }
413
- // Enforce max lines
414
- while (lines.length > this.config.maxLines) {
415
- lines.shift();
416
- if (this.state.cursorRow > 0)
417
- this.state.cursorRow--;
418
- }
419
- this.ensureCursorVisible();
420
- }
421
- /**
422
- * Insert a newline at cursor position
423
- */
424
- insertNewline() {
425
- const { cursorRow, cursorCol, lines } = this.state;
426
- const line = this.getLine(cursorRow);
427
- const before = line.slice(0, cursorCol);
428
- const after = line.slice(cursorCol);
429
- this.setLine(cursorRow, before);
430
- lines.splice(cursorRow + 1, 0, after);
431
- this.state.cursorRow++;
432
- this.state.cursorCol = 0;
433
- // Enforce max lines
434
- if (lines.length > this.config.maxLines) {
435
- lines.shift();
436
- this.state.cursorRow--;
437
- }
438
- this.ensureCursorVisible();
439
- }
440
- /**
441
- * Delete character before cursor
442
- */
443
- deleteBackward() {
444
- const { cursorRow, cursorCol, lines } = this.state;
445
- const currentLine = this.getLine(cursorRow);
446
- if (cursorCol > 0) {
447
- // Delete within line
448
- this.setLine(cursorRow, currentLine.slice(0, cursorCol - 1) + currentLine.slice(cursorCol));
449
- this.state.cursorCol--;
450
- }
451
- else if (cursorRow > 0) {
452
- // Join with previous line
453
- const prevLine = this.getLine(cursorRow - 1);
454
- this.setLine(cursorRow - 1, prevLine + currentLine);
455
- lines.splice(cursorRow, 1);
456
- this.state.cursorRow--;
457
- this.state.cursorCol = prevLine.length;
458
- }
459
- this.ensureCursorVisible();
460
- }
461
- /**
462
- * Delete character after cursor
463
- */
464
- deleteForward() {
465
- const { cursorRow, cursorCol, lines } = this.state;
466
- const line = this.getLine(cursorRow);
467
- if (cursorCol < line.length) {
468
- // Delete within line
469
- this.setLine(cursorRow, line.slice(0, cursorCol) + line.slice(cursorCol + 1));
470
- }
471
- else if (cursorRow < lines.length - 1) {
472
- // Join with next line
473
- this.setLine(cursorRow, line + this.getLine(cursorRow + 1));
474
- lines.splice(cursorRow + 1, 1);
475
- }
476
- }
477
- /**
478
- * Delete word backward
479
- */
480
- deleteWordBackward() {
481
- const { cursorRow, cursorCol } = this.state;
482
- const line = this.getLine(cursorRow);
483
- if (cursorCol === 0) {
484
- if (cursorRow > 0) {
485
- this.deleteBackward();
486
- }
487
- return;
488
- }
489
- // Find word boundary
490
- let pos = cursorCol;
491
- // Skip spaces
492
- while (pos > 0 && line.charAt(pos - 1) === ' ')
493
- pos--;
494
- // Skip word
495
- while (pos > 0 && line.charAt(pos - 1) !== ' ')
496
- pos--;
497
- this.setLine(cursorRow, line.slice(0, pos) + line.slice(cursorCol));
498
- this.state.cursorCol = pos;
499
- }
500
- /**
501
- * Kill (delete) to end of line
502
- */
503
- killToEndOfLine() {
504
- const { cursorRow, cursorCol } = this.state;
505
- this.setLine(cursorRow, this.getLine(cursorRow).slice(0, cursorCol));
506
- }
507
- /**
508
- * Kill (delete) to start of line
509
- */
510
- killToStartOfLine() {
511
- const { cursorRow, cursorCol } = this.state;
512
- this.setLine(cursorRow, this.getLine(cursorRow).slice(cursorCol));
513
- this.state.cursorCol = 0;
514
- }
515
- /**
516
- * Move cursor left
517
- */
518
- cursorLeft() {
519
- if (this.state.cursorCol > 0) {
520
- this.state.cursorCol--;
521
- }
522
- else if (this.state.cursorRow > 0) {
523
- this.state.cursorRow--;
524
- this.state.cursorCol = this.getLine(this.state.cursorRow).length;
525
- }
526
- this.ensureCursorVisible();
527
- }
528
- /**
529
- * Move cursor right
530
- */
531
- cursorRight() {
532
- const line = this.getLine(this.state.cursorRow);
533
- if (this.state.cursorCol < line.length) {
534
- this.state.cursorCol++;
535
- }
536
- else if (this.state.cursorRow < this.state.lines.length - 1) {
537
- this.state.cursorRow++;
538
- this.state.cursorCol = 0;
539
- }
540
- this.ensureCursorVisible();
541
- }
542
- /**
543
- * Move cursor up
544
- */
545
- cursorUp() {
546
- if (this.state.cursorRow > 0) {
547
- this.state.cursorRow--;
548
- // Clamp column to line length
549
- this.state.cursorCol = Math.min(this.state.cursorCol, this.getLine(this.state.cursorRow).length);
550
- }
551
- this.ensureCursorVisible();
552
- }
553
- /**
554
- * Move cursor down
555
- */
556
- cursorDown() {
557
- if (this.state.cursorRow < this.state.lines.length - 1) {
558
- this.state.cursorRow++;
559
- // Clamp column to line length
560
- this.state.cursorCol = Math.min(this.state.cursorCol, this.getLine(this.state.cursorRow).length);
561
- }
562
- this.ensureCursorVisible();
563
- }
564
- /**
565
- * Move cursor to start of line
566
- */
567
- cursorToLineStart() {
568
- this.state.cursorCol = 0;
569
- }
570
- /**
571
- * Move cursor to end of line
572
- */
573
- cursorToLineEnd() {
574
- this.state.cursorCol = this.getLine(this.state.cursorRow).length;
575
- }
576
- /**
577
- * Move cursor one word left
578
- */
579
- cursorWordLeft() {
580
- const { cursorRow, cursorCol } = this.state;
581
- const line = this.getLine(cursorRow);
582
- if (cursorCol === 0) {
583
- if (cursorRow > 0) {
584
- this.state.cursorRow--;
585
- this.state.cursorCol = this.getLine(this.state.cursorRow).length;
586
- }
587
- return;
588
- }
589
- let pos = cursorCol;
590
- // Skip spaces
591
- while (pos > 0 && line.charAt(pos - 1) === ' ')
592
- pos--;
593
- // Skip word
594
- while (pos > 0 && line.charAt(pos - 1) !== ' ')
595
- pos--;
596
- this.state.cursorCol = pos;
597
- this.ensureCursorVisible();
598
- }
599
- /**
600
- * Move cursor one word right
601
- */
602
- cursorWordRight() {
603
- const { cursorRow, cursorCol, lines } = this.state;
604
- const line = this.getLine(cursorRow);
605
- if (cursorCol >= line.length) {
606
- if (cursorRow < lines.length - 1) {
607
- this.state.cursorRow++;
608
- this.state.cursorCol = 0;
609
- }
610
- return;
611
- }
612
- let pos = cursorCol;
613
- // Skip word
614
- while (pos < line.length && line.charAt(pos) !== ' ')
615
- pos++;
616
- // Skip spaces
617
- while (pos < line.length && line.charAt(pos) === ' ')
618
- pos++;
619
- this.state.cursorCol = pos;
620
- this.ensureCursorVisible();
621
- }
622
- /**
623
- * Page up
624
- */
625
- pageUp() {
626
- const linesToMove = this.config.visibleLines;
627
- this.state.cursorRow = Math.max(0, this.state.cursorRow - linesToMove);
628
- this.state.cursorCol = Math.min(this.state.cursorCol, this.getLine(this.state.cursorRow).length);
629
- this.ensureCursorVisible();
630
- }
631
- /**
632
- * Page down
633
- */
634
- pageDown() {
635
- const linesToMove = this.config.visibleLines;
636
- this.state.cursorRow = Math.min(this.state.lines.length - 1, this.state.cursorRow + linesToMove);
637
- this.state.cursorCol = Math.min(this.state.cursorCol, this.getLine(this.state.cursorRow).length);
638
- this.ensureCursorVisible();
639
- }
640
- /**
641
- * Scroll up (without moving cursor)
642
- */
643
- scrollUp() {
644
- if (this.state.scrollOffset > 0) {
645
- this.state.scrollOffset--;
646
- }
647
- }
648
- /**
649
- * Scroll down (without moving cursor)
650
- */
651
- scrollDown() {
652
- const maxScroll = Math.max(0, this.state.lines.length - this.config.visibleLines);
653
- if (this.state.scrollOffset < maxScroll) {
654
- this.state.scrollOffset++;
655
- }
656
- }
657
- /**
658
- * Ensure cursor is visible (adjust scroll if needed)
659
- */
660
- ensureCursorVisible() {
661
- const { cursorRow, scrollOffset } = this.state;
662
- const { visibleLines } = this.config;
663
- // Scroll up if cursor is above visible area
664
- if (cursorRow < scrollOffset) {
665
- this.state.scrollOffset = cursorRow;
666
- }
667
- // Scroll down if cursor is below visible area
668
- if (cursorRow >= scrollOffset + visibleLines) {
669
- this.state.scrollOffset = cursorRow - visibleLines + 1;
670
- }
671
- }
672
- /**
673
- * Submit the current content
674
- */
675
- submit() {
676
- const text = this.getText();
677
- this.clear();
678
- this.emit('submit', text);
679
- }
680
- /**
681
- * Get the full text content
682
- */
683
- getText() {
684
- return this.state.lines.join('\n');
685
- }
686
- /**
687
- * Set the text content
688
- */
689
- setText(text) {
690
- this.state.lines = text.split('\n');
691
- if (this.state.lines.length === 0) {
692
- this.state.lines = [''];
693
- }
694
- this.state.cursorRow = this.state.lines.length - 1;
695
- this.state.cursorCol = this.getLine(this.state.cursorRow).length;
696
- this.state.scrollOffset = 0;
697
- this.ensureCursorVisible();
698
- this.render();
699
- }
700
- /**
701
- * Clear the content
702
- */
703
- clear() {
704
- this.state.lines = [''];
705
- this.state.cursorRow = 0;
706
- this.state.cursorCol = 0;
707
- this.state.scrollOffset = 0;
708
- this.render();
709
- }
710
- /**
711
- * Render the chat box
712
- */
713
- render() {
714
- if (!this.state.isActive)
715
- return;
716
- const { cols } = this.getTerminalSize();
717
- const startRow = this.getChatBoxStartRow();
718
- const { lines, cursorRow, cursorCol, scrollOffset } = this.state;
719
- const { visibleLines, prompt } = this.config;
720
- // Hide cursor during render
721
- this.writeStream.write(ANSI.HIDE_CURSOR);
722
- // Move to start of chat box area
723
- this.writeStream.write(ANSI.CURSOR_TO(startRow, 1));
724
- // Draw separator line
725
- const separatorWidth = Math.min(cols - 2, 55);
726
- this.writeStream.write(ANSI.CLEAR_LINE);
727
- this.writeStream.write(theme.ui.border('─'.repeat(separatorWidth)));
728
- // Draw visible lines
729
- const promptWidth = prompt.length;
730
- const contentWidth = cols - promptWidth - 1;
731
- for (let i = 0; i < visibleLines; i++) {
732
- const lineIndex = scrollOffset + i;
733
- const lineContent = lineIndex < lines.length ? (lines[lineIndex] ?? '') : '';
734
- this.writeStream.write(ANSI.CURSOR_TO(startRow + 1 + i, 1));
735
- this.writeStream.write(ANSI.CLEAR_LINE);
736
- // Draw prompt for first visible line or continuation indicator
737
- if (i === 0 && scrollOffset === 0) {
738
- this.writeStream.write(theme.user(prompt));
739
- }
740
- else if (lineIndex < lines.length) {
741
- // Continuation line
742
- this.writeStream.write(theme.ui.muted(' '));
743
- }
744
- else {
745
- // Empty line
746
- this.writeStream.write(' ');
747
- }
748
- // Draw line content (truncate if needed)
749
- let displayContent = lineContent;
750
- if (displayContent.length > contentWidth) {
751
- displayContent = displayContent.slice(0, contentWidth - 1) + '…';
752
- }
753
- this.writeStream.write(displayContent);
754
- }
755
- // Calculate actual cursor position on screen
756
- const cursorScreenRow = startRow + 1 + (cursorRow - scrollOffset);
757
- const cursorScreenCol = promptWidth + cursorCol + 1;
758
- // Position cursor
759
- this.writeStream.write(ANSI.CURSOR_TO(cursorScreenRow, Math.min(cursorScreenCol, cols)));
760
- // Show cursor
761
- this.writeStream.write(ANSI.SHOW_CURSOR);
762
- }
763
- /**
764
- * Clear the chat box area from screen
765
- */
766
- clearScreen() {
767
- const startRow = this.getChatBoxStartRow();
768
- const { visibleLines } = this.config;
769
- for (let i = 0; i <= visibleLines; i++) {
770
- this.writeStream.write(ANSI.CURSOR_TO(startRow + i, 1));
771
- this.writeStream.write(ANSI.CLEAR_LINE);
772
- }
773
- }
774
- /**
775
- * Dispose and cleanup
776
- */
777
- dispose() {
778
- this.deactivate();
779
- this.clearScreen();
780
- this.restoreOutput();
781
- }
782
- /**
783
- * Check if the chat box is currently active
784
- */
785
- isActive() {
786
- return this.state.isActive;
787
- }
788
- /**
789
- * Get current line count
790
- */
791
- getLineCount() {
792
- return this.state.lines.length;
793
- }
794
- }
795
- /**
796
- * Singleton instance
797
- */
798
- let globalChatBox = null;
799
- export function getChatBox(config) {
800
- if (!globalChatBox) {
801
- globalChatBox = new ChatBox(config);
802
- }
803
- return globalChatBox;
804
- }
805
- export function resetChatBox() {
806
- if (globalChatBox) {
807
- globalChatBox.dispose();
808
- globalChatBox = null;
809
- }
810
- }
811
- //# sourceMappingURL=chatBox.js.map