erosolar-cli 1.7.116 → 1.7.118
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/dist/shell/enhancedInteractiveShell.d.ts +36 -22
- package/dist/shell/enhancedInteractiveShell.d.ts.map +1 -1
- package/dist/shell/enhancedInteractiveShell.js +76 -105
- package/dist/shell/enhancedInteractiveShell.js.map +1 -1
- package/dist/shell/enhancedShellApp.d.ts +6 -3
- package/dist/shell/enhancedShellApp.d.ts.map +1 -1
- package/dist/shell/enhancedShellApp.js +66 -53
- package/dist/shell/enhancedShellApp.js.map +1 -1
- package/dist/ui/enhancedPinnedChatBox.d.ts +143 -65
- package/dist/ui/enhancedPinnedChatBox.d.ts.map +1 -1
- package/dist/ui/enhancedPinnedChatBox.js +301 -303
- package/dist/ui/enhancedPinnedChatBox.js.map +1 -1
- package/dist/ui/persistentChatBox.d.ts +117 -83
- package/dist/ui/persistentChatBox.d.ts.map +1 -1
- package/dist/ui/persistentChatBox.js +239 -331
- package/dist/ui/persistentChatBox.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +7 -2
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +51 -16
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,390 +1,298 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Persistent Chat Box - Stays at bottom during AI streaming
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - Always visible at bottom
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
4
|
+
* Features:
|
|
5
|
+
* - Always visible at bottom of terminal
|
|
6
|
+
* - Supports typing while AI is streaming
|
|
7
|
+
* - Gracefully handles long pastes without errors
|
|
8
|
+
* - Only submits to AI when user hits enter key
|
|
9
|
+
* - Integrates with existing multiline paste handler
|
|
9
10
|
*/
|
|
11
|
+
import readline from 'node:readline';
|
|
12
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
10
13
|
import { theme } from './theme.js';
|
|
11
|
-
import {
|
|
14
|
+
import { processPaste, isMultilinePaste } from '../core/multilinePasteHandler.js';
|
|
15
|
+
import { display } from './display.js';
|
|
12
16
|
export class PersistentChatBox {
|
|
13
|
-
|
|
17
|
+
rl;
|
|
18
|
+
config;
|
|
14
19
|
state;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Scroll region management for persistent bottom input
|
|
33
|
-
scrollRegionActive = false;
|
|
34
|
-
// Input history for up/down navigation
|
|
35
|
-
inputHistory = [];
|
|
36
|
-
historyIndex = -1;
|
|
37
|
-
tempCurrentInput = ''; // Stores current input when navigating history
|
|
38
|
-
maxHistorySize = 100;
|
|
39
|
-
// Enhanced paste tracking for summary display
|
|
40
|
-
isPastedBlock = false;
|
|
41
|
-
pastedFullContent = '';
|
|
42
|
-
pasteStartTime = 0;
|
|
43
|
-
pasteLineCount = 0;
|
|
44
|
-
maxPasteLines = 50;
|
|
45
|
-
/** Cleanup function for output interceptor registration */
|
|
46
|
-
outputInterceptorCleanup;
|
|
47
|
-
// Track if we're currently in streaming mode
|
|
48
|
-
isStreaming = false;
|
|
49
|
-
constructor(writeStream, options = {}) {
|
|
50
|
-
this.writeStream = writeStream;
|
|
51
|
-
this.onCommandQueued = options.onCommandQueued;
|
|
52
|
-
this.onInputSubmit = options.onInputSubmit;
|
|
53
|
-
this.maxInputLength = options.maxInputLength ?? this.maxInputLength;
|
|
54
|
-
this.maxQueueSize = options.maxQueueSize ?? this.maxQueueSize;
|
|
20
|
+
onSubmit;
|
|
21
|
+
onCancel;
|
|
22
|
+
isActive = false;
|
|
23
|
+
originalPrompt = '';
|
|
24
|
+
pasteBuffer = '';
|
|
25
|
+
pasteMode = false;
|
|
26
|
+
constructor(onSubmit, onCancel, config = {}) {
|
|
27
|
+
this.onSubmit = onSubmit;
|
|
28
|
+
this.onCancel = onCancel;
|
|
29
|
+
this.config = {
|
|
30
|
+
enabled: true,
|
|
31
|
+
position: 1,
|
|
32
|
+
maxLines: 3,
|
|
33
|
+
showPasteSummaries: true,
|
|
34
|
+
autoScroll: true,
|
|
35
|
+
...config,
|
|
36
|
+
};
|
|
55
37
|
this.state = {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
38
|
+
input: '',
|
|
39
|
+
isStreaming: false,
|
|
40
|
+
isTyping: false,
|
|
41
|
+
isPasteMode: false,
|
|
42
|
+
cursorPosition: 0,
|
|
43
|
+
history: [],
|
|
44
|
+
historyIndex: -1,
|
|
62
45
|
};
|
|
46
|
+
// Create readline interface with custom prompt handling
|
|
47
|
+
this.rl = readline.createInterface({
|
|
48
|
+
input,
|
|
49
|
+
output,
|
|
50
|
+
prompt: '',
|
|
51
|
+
terminal: true,
|
|
52
|
+
historySize: 100,
|
|
53
|
+
});
|
|
54
|
+
this.setupEventHandlers();
|
|
63
55
|
}
|
|
64
56
|
/**
|
|
65
|
-
*
|
|
57
|
+
* Setup readline event handlers
|
|
66
58
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
59
|
+
setupEventHandlers() {
|
|
60
|
+
this.rl.on('line', (line) => {
|
|
61
|
+
this.handleLineInput(line);
|
|
62
|
+
});
|
|
63
|
+
this.rl.on('close', () => {
|
|
64
|
+
this.handleClose();
|
|
65
|
+
});
|
|
66
|
+
// Handle paste detection
|
|
67
|
+
this.rl.input.on('data', (data) => {
|
|
68
|
+
this.handleDataInput(data.toString());
|
|
69
|
+
});
|
|
70
|
+
// Handle keyboard shortcuts
|
|
71
|
+
this.rl.input.on('keypress', (str, key) => {
|
|
72
|
+
if (key.ctrl && key.name === 'c') {
|
|
73
|
+
this.handleCancel();
|
|
74
|
+
}
|
|
75
|
+
else if (key.name === 'up') {
|
|
76
|
+
this.handleHistoryUp();
|
|
77
|
+
}
|
|
78
|
+
else if (key.name === 'down') {
|
|
79
|
+
this.handleHistoryDown();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
|
-
*
|
|
84
|
+
* Handle line input (when user presses enter)
|
|
75
85
|
*/
|
|
76
|
-
|
|
77
|
-
if (this.
|
|
86
|
+
handleLineInput(line) {
|
|
87
|
+
if (!this.isActive)
|
|
78
88
|
return;
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.isPastedBlock = true;
|
|
88
|
-
this.pastedFullContent = content;
|
|
89
|
-
// Create summary for display
|
|
90
|
-
const summaryLines = lines.slice(0, 5); // Show first 5 lines
|
|
91
|
-
const remainingLines = lineCount - 5;
|
|
92
|
-
const summary = summaryLines.join('\n') +
|
|
93
|
-
(remainingLines > 0 ? `\n... (${remainingLines} more lines)` : '');
|
|
94
|
-
this.inputBuffer = summary;
|
|
95
|
-
this.cursorPosition = summary.length;
|
|
96
|
-
this.state.currentInput = summary;
|
|
97
|
-
// Show paste status
|
|
98
|
-
this.setStatusMessage(`📋 Pasted ${lineCount} lines, ${charCount} chars - hit Enter to submit`);
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
if (this.pasteMode) {
|
|
91
|
+
// Handle paste completion
|
|
92
|
+
this.handlePasteComplete(trimmed);
|
|
93
|
+
}
|
|
94
|
+
else if (trimmed.length > 0) {
|
|
95
|
+
// Handle normal input
|
|
96
|
+
this.handleNormalInput(trimmed);
|
|
99
97
|
}
|
|
100
98
|
else {
|
|
101
|
-
//
|
|
102
|
-
this.
|
|
103
|
-
this.pastedFullContent = '';
|
|
104
|
-
this.inputBuffer = content;
|
|
105
|
-
this.cursorPosition = content.length;
|
|
106
|
-
this.state.currentInput = content;
|
|
99
|
+
// Empty input - just clear and reset
|
|
100
|
+
this.clearInput();
|
|
107
101
|
}
|
|
108
|
-
this.scheduleRender();
|
|
109
102
|
}
|
|
110
103
|
/**
|
|
111
|
-
*
|
|
104
|
+
* Handle normal text input
|
|
112
105
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
handleNormalInput(text) {
|
|
107
|
+
// Add to history
|
|
108
|
+
if (this.state.history[0] !== text) {
|
|
109
|
+
this.state.history.unshift(text);
|
|
110
|
+
if (this.state.history.length > 100) {
|
|
111
|
+
this.state.history.pop();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Submit to AI
|
|
115
|
+
this.onSubmit(text);
|
|
116
|
+
// Clear input
|
|
117
|
+
this.clearInput();
|
|
118
|
+
this.state.historyIndex = -1;
|
|
118
119
|
}
|
|
119
120
|
/**
|
|
120
|
-
* Handle
|
|
121
|
+
* Handle paste completion
|
|
121
122
|
*/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (this.pasteStartTime > 0 && now - this.pasteStartTime < 100) {
|
|
128
|
-
// Rapid input detected - treat as paste continuation
|
|
129
|
-
this.inputBuffer = this.inputBuffer.slice(0, this.cursorPosition) +
|
|
130
|
-
char +
|
|
131
|
-
this.inputBuffer.slice(this.cursorPosition);
|
|
132
|
-
this.cursorPosition += char.length;
|
|
133
|
-
this.state.currentInput = this.inputBuffer;
|
|
134
|
-
this.scheduleRender();
|
|
135
|
-
return;
|
|
123
|
+
handlePasteComplete(text) {
|
|
124
|
+
const fullPaste = this.pasteBuffer + text;
|
|
125
|
+
if (this.config.showPasteSummaries && isMultilinePaste(fullPaste)) {
|
|
126
|
+
const processed = processPaste(fullPaste);
|
|
127
|
+
display.showSystemMessage(processed.displaySummary);
|
|
136
128
|
}
|
|
137
|
-
//
|
|
138
|
-
this.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
-
// Clear any paste state for normal typing
|
|
144
|
-
this.clearPastedBlock();
|
|
145
|
-
this.scheduleRender();
|
|
129
|
+
// Submit the full paste content
|
|
130
|
+
this.onSubmit(fullPaste);
|
|
131
|
+
// Reset paste state
|
|
132
|
+
this.pasteMode = false;
|
|
133
|
+
this.pasteBuffer = '';
|
|
134
|
+
this.clearInput();
|
|
146
135
|
}
|
|
147
136
|
/**
|
|
148
|
-
* Handle
|
|
137
|
+
* Handle raw data input for paste detection
|
|
149
138
|
*/
|
|
150
|
-
|
|
151
|
-
if (this.
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
input = this.sanitizeCommandText(this.inputBuffer);
|
|
139
|
+
handleDataInput(data) {
|
|
140
|
+
if (!this.isActive)
|
|
141
|
+
return;
|
|
142
|
+
// Detect paste by checking for multiple lines or large content
|
|
143
|
+
if (data.includes('\n') && data.length > 100) {
|
|
144
|
+
this.pasteMode = true;
|
|
145
|
+
this.pasteBuffer = data;
|
|
146
|
+
// Show paste indicator
|
|
147
|
+
this.updatePrompt('📋 Paste mode - Press Enter to submit or Ctrl+C to cancel');
|
|
160
148
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const type = input.startsWith('/') ? 'slash' : 'request';
|
|
171
|
-
const queued = this.queueCommand(input, type);
|
|
172
|
-
if (!queued) {
|
|
173
|
-
this.setStatusMessage(`Queue is full (${this.maxQueueSize}). Submit after current task or clear queued items.`);
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Handle cancel (Ctrl+C)
|
|
152
|
+
*/
|
|
153
|
+
handleCancel() {
|
|
154
|
+
if (this.pasteMode) {
|
|
155
|
+
// Cancel paste mode
|
|
156
|
+
this.pasteMode = false;
|
|
157
|
+
this.pasteBuffer = '';
|
|
176
158
|
this.clearInput();
|
|
177
|
-
|
|
159
|
+
display.showSystemMessage('Paste cancelled');
|
|
178
160
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
this.onInputSubmit(input);
|
|
161
|
+
else {
|
|
162
|
+
// Call cancel callback
|
|
163
|
+
this.onCancel();
|
|
183
164
|
}
|
|
184
|
-
return input;
|
|
185
165
|
}
|
|
186
166
|
/**
|
|
187
|
-
*
|
|
167
|
+
* Handle close
|
|
168
|
+
*/
|
|
169
|
+
handleClose() {
|
|
170
|
+
this.isActive = false;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Handle history navigation - up
|
|
188
174
|
*/
|
|
189
|
-
|
|
190
|
-
if (
|
|
175
|
+
handleHistoryUp() {
|
|
176
|
+
if (this.state.history.length === 0)
|
|
191
177
|
return;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const separatorWidth = Math.min(cols - 2, 72);
|
|
197
|
-
// Build status message
|
|
198
|
-
const statusParts = [];
|
|
199
|
-
// Show streaming status if active
|
|
200
|
-
if (this.isStreaming) {
|
|
201
|
-
statusParts.push(theme.ui.info('🔄 AI is streaming...'));
|
|
202
|
-
}
|
|
203
|
-
// Processing status
|
|
204
|
-
if (this.state.statusMessage) {
|
|
205
|
-
const maxLen = Math.max(20, cols - 30);
|
|
206
|
-
const msg = this.state.statusMessage.length > maxLen
|
|
207
|
-
? `${this.state.statusMessage.slice(0, maxLen - 3)}...`
|
|
208
|
-
: this.state.statusMessage;
|
|
209
|
-
statusParts.push(theme.ui.muted(msg));
|
|
210
|
-
}
|
|
211
|
-
// Queue status
|
|
212
|
-
if (this.state.queuedCommands.length > 0) {
|
|
213
|
-
statusParts.push(theme.ui.warning(`📋 ${this.state.queuedCommands.length} queued`));
|
|
214
|
-
}
|
|
215
|
-
const statusLine = statusParts.join(' │ ');
|
|
216
|
-
const separator = theme.ui.border('─'.repeat(separatorWidth));
|
|
217
|
-
// Write the persistent chat box
|
|
218
|
-
this.safeWrite(`\r${ANSI.CLEAR_LINE}${separator}\n`);
|
|
219
|
-
this.safeWrite(`${ANSI.CLEAR_LINE}${statusLine}\n`);
|
|
220
|
-
// Show input line with cursor
|
|
221
|
-
const displayInput = this.state.currentInput || '';
|
|
222
|
-
const prompt = theme.ui.muted('> ');
|
|
223
|
-
this.safeWrite(`${ANSI.CLEAR_LINE}${prompt}${displayInput}`);
|
|
224
|
-
// Position cursor if needed
|
|
225
|
-
if (this.cursorPosition >= 0) {
|
|
226
|
-
const cursorPos = prompt.length + this.cursorPosition;
|
|
227
|
-
this.safeWrite(`\r${ANSI.CURSOR_FORWARD(cursorPos)}`);
|
|
228
|
-
}
|
|
229
|
-
this._lastRenderedHeight = 3;
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
// Silently handle render errors
|
|
178
|
+
if (this.state.historyIndex < this.state.history.length - 1) {
|
|
179
|
+
this.state.historyIndex++;
|
|
180
|
+
const historyItem = this.state.history[this.state.historyIndex];
|
|
181
|
+
this.setInput(historyItem);
|
|
233
182
|
}
|
|
234
183
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (this.writeStream.writable) {
|
|
244
|
-
this.writeStream.write(content);
|
|
245
|
-
}
|
|
184
|
+
/**
|
|
185
|
+
* Handle history navigation - down
|
|
186
|
+
*/
|
|
187
|
+
handleHistoryDown() {
|
|
188
|
+
if (this.state.historyIndex > 0) {
|
|
189
|
+
this.state.historyIndex--;
|
|
190
|
+
const historyItem = this.state.history[this.state.historyIndex];
|
|
191
|
+
this.setInput(historyItem);
|
|
246
192
|
}
|
|
247
|
-
|
|
248
|
-
|
|
193
|
+
else if (this.state.historyIndex === 0) {
|
|
194
|
+
this.state.historyIndex = -1;
|
|
195
|
+
this.clearInput();
|
|
249
196
|
}
|
|
250
197
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Update the readline prompt
|
|
200
|
+
*/
|
|
201
|
+
updatePrompt(prompt) {
|
|
202
|
+
this.rl.setPrompt(prompt);
|
|
203
|
+
this.rl.prompt();
|
|
256
204
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Set input text
|
|
207
|
+
*/
|
|
208
|
+
setInput(text) {
|
|
209
|
+
this.state.input = text;
|
|
210
|
+
this.state.cursorPosition = text.length;
|
|
211
|
+
// Update the readline line
|
|
212
|
+
this.rl.write('\x1B[2K\r'); // Clear line
|
|
213
|
+
this.rl.write(text);
|
|
264
214
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
this.inputHistory.push(entry);
|
|
274
|
-
// Trim history if needed
|
|
275
|
-
if (this.inputHistory.length > this.maxHistorySize) {
|
|
276
|
-
this.inputHistory = this.inputHistory.slice(-this.maxHistorySize);
|
|
277
|
-
}
|
|
215
|
+
/**
|
|
216
|
+
* Clear input
|
|
217
|
+
*/
|
|
218
|
+
clearInput() {
|
|
219
|
+
this.state.input = '';
|
|
220
|
+
this.state.cursorPosition = 0;
|
|
221
|
+
this.updatePrompt(this.getDefaultPrompt());
|
|
278
222
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Get default prompt based on streaming state
|
|
225
|
+
*/
|
|
226
|
+
getDefaultPrompt() {
|
|
227
|
+
if (this.state.isStreaming) {
|
|
228
|
+
return `${theme.info('◉')} ${theme.ui.muted('Type while AI streams (Enter to send)')}: `;
|
|
284
229
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
this.scheduleRender();
|
|
230
|
+
else {
|
|
231
|
+
return `${theme.success('>')} `;
|
|
288
232
|
}
|
|
289
233
|
}
|
|
290
|
-
|
|
291
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Activate the chat box
|
|
236
|
+
*/
|
|
237
|
+
activate() {
|
|
238
|
+
if (this.isActive)
|
|
292
239
|
return;
|
|
293
|
-
this.
|
|
294
|
-
this.
|
|
240
|
+
this.isActive = true;
|
|
241
|
+
this.clearInput();
|
|
242
|
+
// Show initial prompt
|
|
243
|
+
display.showSystemMessage('Persistent chat box activated - Type while AI streams, press Enter to send');
|
|
295
244
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
this.
|
|
245
|
+
/**
|
|
246
|
+
* Deactivate the chat box
|
|
247
|
+
*/
|
|
248
|
+
deactivate() {
|
|
249
|
+
this.isActive = false;
|
|
250
|
+
this.rl.pause();
|
|
301
251
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this.
|
|
252
|
+
/**
|
|
253
|
+
* Set streaming state
|
|
254
|
+
*/
|
|
255
|
+
setStreaming(isStreaming) {
|
|
256
|
+
this.state.isStreaming = isStreaming;
|
|
257
|
+
if (this.isActive) {
|
|
258
|
+
this.updatePrompt(this.getDefaultPrompt());
|
|
259
|
+
}
|
|
307
260
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return null;
|
|
317
|
-
// Check queue size limit
|
|
318
|
-
if (this.state.queuedCommands.length >= this.maxQueueSize) {
|
|
319
|
-
// Remove oldest non-slash commands to make room
|
|
320
|
-
const idx = this.state.queuedCommands.findIndex(c => c.type !== 'slash');
|
|
321
|
-
if (idx >= 0) {
|
|
322
|
-
this.state.queuedCommands.splice(idx, 1);
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
// Queue is full of slash commands, reject
|
|
326
|
-
return null;
|
|
261
|
+
/**
|
|
262
|
+
* Add message to history
|
|
263
|
+
*/
|
|
264
|
+
addToHistory(message) {
|
|
265
|
+
if (message.trim() && this.state.history[0] !== message.trim()) {
|
|
266
|
+
this.state.history.unshift(message.trim());
|
|
267
|
+
if (this.state.history.length > 100) {
|
|
268
|
+
this.state.history.pop();
|
|
327
269
|
}
|
|
328
270
|
}
|
|
329
|
-
// Sanitize and truncate command text
|
|
330
|
-
const truncated = sanitizedText.slice(0, this.maxInputLength);
|
|
331
|
-
const preview = truncated.length > 60 ? `${truncated.slice(0, 57)}...` : truncated;
|
|
332
|
-
const cmd = {
|
|
333
|
-
id: `cmd-${++this.commandIdCounter}`,
|
|
334
|
-
text: truncated,
|
|
335
|
-
type,
|
|
336
|
-
timestamp: Date.now(),
|
|
337
|
-
preview,
|
|
338
|
-
};
|
|
339
|
-
this.state.queuedCommands.push(cmd);
|
|
340
|
-
this.scheduleRender();
|
|
341
|
-
if (this.onCommandQueued) {
|
|
342
|
-
this.onCommandQueued(cmd);
|
|
343
|
-
}
|
|
344
|
-
return cmd;
|
|
345
271
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.
|
|
272
|
+
/**
|
|
273
|
+
* Get current state
|
|
274
|
+
*/
|
|
275
|
+
getState() {
|
|
276
|
+
return { ...this.state };
|
|
351
277
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
this.
|
|
357
|
-
this.state.currentInput = '';
|
|
358
|
-
// Reset history navigation
|
|
359
|
-
this.historyIndex = -1;
|
|
360
|
-
this.tempCurrentInput = '';
|
|
361
|
-
// Clear paste state
|
|
362
|
-
this.clearPastedBlock();
|
|
363
|
-
this.scheduleRender();
|
|
278
|
+
/**
|
|
279
|
+
* Get config
|
|
280
|
+
*/
|
|
281
|
+
getConfig() {
|
|
282
|
+
return { ...this.config };
|
|
364
283
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this.safeWrite(ANSI.MOVE_UP(this._lastRenderedHeight - 1));
|
|
371
|
-
}
|
|
372
|
-
this.safeWrite(`\r${ANSI.CLEAR_TO_END}`);
|
|
373
|
-
this._lastRenderedHeight = 0;
|
|
284
|
+
/**
|
|
285
|
+
* Update config
|
|
286
|
+
*/
|
|
287
|
+
updateConfig(config) {
|
|
288
|
+
this.config = { ...this.config, ...config };
|
|
374
289
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
this.renderScheduled = true;
|
|
382
|
-
setTimeout(() => {
|
|
383
|
-
this.renderScheduled = false;
|
|
384
|
-
if (!)
|
|
385
|
-
;
|
|
386
|
-
});
|
|
387
|
-
}
|
|
290
|
+
/**
|
|
291
|
+
* Dispose of resources
|
|
292
|
+
*/
|
|
293
|
+
dispose() {
|
|
294
|
+
this.deactivate();
|
|
295
|
+
this.rl.close();
|
|
388
296
|
}
|
|
389
297
|
}
|
|
390
|
-
//# sourceMappingURL=
|
|
298
|
+
//# sourceMappingURL=PersistentChatBox.js.map
|