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,372 +1,370 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Enhanced Pinned Chat Box - Persistent chat input during AI streaming
|
|
3
|
+
*
|
|
4
|
+
* Enhanced features:
|
|
5
|
+
* - Always visible at bottom during AI streaming
|
|
6
|
+
* - Supports typing while AI is streaming responses
|
|
7
|
+
* - Gracefully handles long pastes without errors
|
|
8
|
+
* - Only submits to AI when user hits enter key
|
|
9
|
+
* - Integrates with existing multiline paste handler
|
|
10
|
+
* - Shows paste summaries for large content
|
|
11
|
+
* - Input history navigation with up/down arrows
|
|
12
|
+
* - Visual feedback during streaming
|
|
4
13
|
*/
|
|
5
14
|
import { theme } from './theme.js';
|
|
6
|
-
import {
|
|
15
|
+
import { processPaste, isMultilinePaste } from '../core/multilinePasteHandler.js';
|
|
7
16
|
export class EnhancedPinnedChatBox {
|
|
8
17
|
writeStream;
|
|
18
|
+
config;
|
|
9
19
|
state;
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
onSubmit;
|
|
21
|
+
onCancel;
|
|
22
|
+
isActive = false;
|
|
12
23
|
inputBuffer = '';
|
|
13
24
|
cursorPosition = 0;
|
|
14
|
-
|
|
15
|
-
onCommandQueued;
|
|
16
|
-
onInputSubmit;
|
|
17
|
-
renderScheduled = false;
|
|
18
|
-
isEnabled = true;
|
|
19
|
-
isDisposed = false;
|
|
20
|
-
lastRenderTime = 0;
|
|
21
|
-
renderThrottleMs = 16;
|
|
22
|
-
maxInputLength = 10000;
|
|
23
|
-
maxQueueSize = 100;
|
|
24
|
-
ansiPattern = /[\u001B\u009B][[\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
25
|
-
oscPattern = /\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g;
|
|
26
|
-
maxStatusMessageLength = 200;
|
|
27
|
-
// Scroll region management
|
|
28
|
-
scrollRegionActive = false;
|
|
29
|
-
// Input history
|
|
30
|
-
inputHistory = [];
|
|
25
|
+
history = [];
|
|
31
26
|
historyIndex = -1;
|
|
32
27
|
tempCurrentInput = '';
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
maxPasteLines = 50;
|
|
40
|
-
outputInterceptorCleanup;
|
|
41
|
-
constructor(writeStream, _promptText = '> ', options = {}) {
|
|
28
|
+
isPasteMode = false;
|
|
29
|
+
pasteBuffer = '';
|
|
30
|
+
renderScheduled = false;
|
|
31
|
+
lastRenderTime = 0;
|
|
32
|
+
renderThrottleMs = 16; // ~60fps max
|
|
33
|
+
constructor(writeStream, onSubmit, onCancel, config = {}) {
|
|
42
34
|
this.writeStream = writeStream;
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
46
|
-
|
|
35
|
+
this.onSubmit = onSubmit;
|
|
36
|
+
this.onCancel = onCancel;
|
|
37
|
+
this.config = {
|
|
38
|
+
enabled: true,
|
|
39
|
+
position: 1,
|
|
40
|
+
maxLines: 3,
|
|
41
|
+
showPasteSummaries: true,
|
|
42
|
+
autoScroll: true,
|
|
43
|
+
maxInputLength: 10000,
|
|
44
|
+
maxHistorySize: 100,
|
|
45
|
+
...config,
|
|
46
|
+
};
|
|
47
47
|
this.state = {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
input: '',
|
|
49
|
+
isStreaming: false,
|
|
50
|
+
isTyping: false,
|
|
51
|
+
isPasteMode: false,
|
|
52
|
+
cursorPosition: 0,
|
|
53
|
+
history: [],
|
|
54
|
+
historyIndex: -1,
|
|
52
55
|
statusMessage: null,
|
|
53
|
-
|
|
56
|
+
isProcessing: false,
|
|
54
57
|
};
|
|
55
58
|
}
|
|
56
59
|
/**
|
|
57
|
-
*
|
|
60
|
+
* Handle character input
|
|
58
61
|
*/
|
|
59
|
-
|
|
60
|
-
if (this.
|
|
62
|
+
handleInput(char) {
|
|
63
|
+
if (!this.isActive)
|
|
64
|
+
return;
|
|
65
|
+
// Detect paste by checking for multiple lines or large content
|
|
66
|
+
if (char.includes('\n') && char.length > 100) {
|
|
67
|
+
this.handlePaste(char);
|
|
61
68
|
return;
|
|
62
|
-
const lines = content.split('\n');
|
|
63
|
-
const lineCount = lines.length;
|
|
64
|
-
const charCount = content.length;
|
|
65
|
-
this.pasteStartTime = Date.now();
|
|
66
|
-
this.pasteLineCount = lineCount;
|
|
67
|
-
// For large pastes, show summary instead of full content
|
|
68
|
-
if (lineCount > this.maxPasteLines || charCount > 2000) {
|
|
69
|
-
this.isPastedBlock = true;
|
|
70
|
-
this.pastedFullContent = content;
|
|
71
|
-
const summaryLines = lines.slice(0, 5);
|
|
72
|
-
const remainingLines = lineCount - 5;
|
|
73
|
-
const summary = summaryLines.join('\n') +
|
|
74
|
-
(remainingLines > 0 ? `\n... (${remainingLines} more lines)` : '');
|
|
75
|
-
this.inputBuffer = summary;
|
|
76
|
-
this.cursorPosition = summary.length;
|
|
77
|
-
this.state.currentInput = summary;
|
|
78
|
-
this.setStatusMessage(`📋 Pasted ${lineCount} lines, ${charCount} chars - hit Enter to submit`);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
this.isPastedBlock = false;
|
|
82
|
-
this.pastedFullContent = '';
|
|
83
|
-
this.inputBuffer = content;
|
|
84
|
-
this.cursorPosition = content.length;
|
|
85
|
-
this.state.currentInput = content;
|
|
86
69
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Clear paste state
|
|
91
|
-
*/
|
|
92
|
-
clearPastedBlock() {
|
|
93
|
-
this.isPastedBlock = false;
|
|
94
|
-
this.pastedFullContent = '';
|
|
95
|
-
this.pasteStartTime = 0;
|
|
96
|
-
this.pasteLineCount = 0;
|
|
70
|
+
// Normal character input
|
|
71
|
+
this.handleNormalInput(char);
|
|
97
72
|
}
|
|
98
73
|
/**
|
|
99
|
-
* Handle character input
|
|
74
|
+
* Handle normal character input
|
|
100
75
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
handleNormalInput(char) {
|
|
77
|
+
// Respect max input length
|
|
78
|
+
const availableSpace = this.config.maxInputLength - this.inputBuffer.length;
|
|
79
|
+
if (availableSpace <= 0)
|
|
103
80
|
return;
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
// Rapid input detected - treat as paste continuation
|
|
107
|
-
this.inputBuffer = this.inputBuffer.slice(0, this.cursorPosition) +
|
|
108
|
-
char +
|
|
109
|
-
this.inputBuffer.slice(this.cursorPosition);
|
|
110
|
-
this.cursorPosition += char.length;
|
|
111
|
-
this.state.currentInput = this.inputBuffer;
|
|
112
|
-
this.scheduleRender();
|
|
81
|
+
const chunk = char.slice(0, availableSpace);
|
|
82
|
+
if (!chunk)
|
|
113
83
|
return;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.cursorPosition
|
|
120
|
-
this.
|
|
121
|
-
this.clearPastedBlock();
|
|
84
|
+
// Insert character at cursor position
|
|
85
|
+
this.inputBuffer =
|
|
86
|
+
this.inputBuffer.slice(0, this.cursorPosition) +
|
|
87
|
+
chunk +
|
|
88
|
+
this.inputBuffer.slice(this.cursorPosition);
|
|
89
|
+
this.cursorPosition = Math.min(this.cursorPosition + chunk.length, this.inputBuffer.length);
|
|
90
|
+
this.updateState();
|
|
122
91
|
this.scheduleRender();
|
|
123
92
|
}
|
|
124
93
|
/**
|
|
125
|
-
* Handle
|
|
94
|
+
* Handle paste input
|
|
126
95
|
*/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
input = this.sanitizeCommandText(this.pastedFullContent);
|
|
96
|
+
handlePaste(content) {
|
|
97
|
+
this.isPasteMode = true;
|
|
98
|
+
this.pasteBuffer = content;
|
|
99
|
+
if (this.config.showPasteSummaries && isMultilinePaste(content)) {
|
|
100
|
+
const processed = processPaste(content);
|
|
101
|
+
this.setStatusMessage(processed.displaySummary);
|
|
134
102
|
}
|
|
135
103
|
else {
|
|
136
|
-
|
|
104
|
+
this.setStatusMessage('📋 Paste detected - Press Enter to submit');
|
|
137
105
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// Add to history before processing
|
|
141
|
-
const historyEntry = this.isPastedBlock
|
|
142
|
-
? `[Pasted ${this.pasteLineCount} lines] ${input.slice(0, 100)}...`
|
|
143
|
-
: input;
|
|
144
|
-
this.addToHistory(historyEntry);
|
|
145
|
-
// If processing, queue the command instead of submitting
|
|
146
|
-
if (this.state.isProcessing) {
|
|
147
|
-
const type = input.startsWith('/') ? 'slash' : 'request';
|
|
148
|
-
const queued = this.queueCommand(input, type);
|
|
149
|
-
if (!queued) {
|
|
150
|
-
this.setStatusMessage(`Queue is full (${this.maxQueueSize}). Submit after current task or clear queued items.`);
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
this.clearInput();
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
// Clear input and paste state, then notify
|
|
157
|
-
this.clearInput();
|
|
158
|
-
if (this.onInputSubmit) {
|
|
159
|
-
this.onInputSubmit(input);
|
|
160
|
-
}
|
|
161
|
-
return input;
|
|
106
|
+
this.updateState();
|
|
107
|
+
this.scheduleRender();
|
|
162
108
|
}
|
|
163
109
|
/**
|
|
164
|
-
*
|
|
110
|
+
* Submit current input
|
|
165
111
|
*/
|
|
166
|
-
|
|
167
|
-
if (!this.
|
|
168
|
-
return;
|
|
169
|
-
// ALWAYS render during processing - use persistent input mode
|
|
170
|
-
if (this.state.isProcessing) {
|
|
171
|
-
this.renderPersistentInput();
|
|
112
|
+
submit() {
|
|
113
|
+
if (!this.isActive)
|
|
172
114
|
return;
|
|
115
|
+
let inputToSubmit;
|
|
116
|
+
if (this.isPasteMode) {
|
|
117
|
+
// Submit paste content
|
|
118
|
+
inputToSubmit = this.pasteBuffer;
|
|
119
|
+
this.clearPasteMode();
|
|
173
120
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const separatorWidth = Math.min(cols - 2, 72);
|
|
179
|
-
// Build status message
|
|
180
|
-
const statusParts = [];
|
|
181
|
-
// Processing status
|
|
182
|
-
if (this.state.statusMessage) {
|
|
183
|
-
const maxLen = Math.max(20, cols - 30);
|
|
184
|
-
const msg = this.state.statusMessage.length > maxLen
|
|
185
|
-
? `${this.state.statusMessage.slice(0, maxLen - 3)}...`
|
|
186
|
-
: this.state.statusMessage;
|
|
187
|
-
statusParts.push(theme.ui.muted(msg));
|
|
188
|
-
}
|
|
189
|
-
if (statusParts.length === 0) {
|
|
190
|
-
// Not processing and no status - nothing to show
|
|
121
|
+
else {
|
|
122
|
+
// Submit normal input
|
|
123
|
+
inputToSubmit = this.inputBuffer.trim();
|
|
124
|
+
if (!inputToSubmit)
|
|
191
125
|
return;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
126
|
+
// Add to history
|
|
127
|
+
this.addToHistory(inputToSubmit);
|
|
128
|
+
}
|
|
129
|
+
// Clear input
|
|
130
|
+
this.clearInput();
|
|
131
|
+
// Submit to AI
|
|
132
|
+
this.onSubmit(inputToSubmit);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Cancel current input or paste
|
|
136
|
+
*/
|
|
137
|
+
cancel() {
|
|
138
|
+
if (this.isPasteMode) {
|
|
139
|
+
// Cancel paste mode
|
|
140
|
+
this.clearPasteMode();
|
|
141
|
+
this.setStatusMessage('Paste cancelled');
|
|
200
142
|
}
|
|
201
|
-
|
|
202
|
-
//
|
|
143
|
+
else {
|
|
144
|
+
// Call cancel callback
|
|
145
|
+
this.onCancel();
|
|
203
146
|
}
|
|
204
147
|
}
|
|
205
148
|
/**
|
|
206
|
-
*
|
|
149
|
+
* Handle history navigation - up
|
|
207
150
|
*/
|
|
208
|
-
|
|
209
|
-
if (
|
|
151
|
+
navigateHistoryUp() {
|
|
152
|
+
if (this.history.length === 0)
|
|
210
153
|
return;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// Move to the reserved bottom area (outside scroll region)
|
|
216
|
-
this.safeWrite(ANSI.CURSOR_TO_BOTTOM(rows - 1));
|
|
217
|
-
// Build status line
|
|
218
|
-
const queueCount = this.state.queuedCommands.length;
|
|
219
|
-
let statusText = '';
|
|
220
|
-
if (this.state.isProcessing) {
|
|
221
|
-
const queuePart = queueCount > 0 ? ` (${queueCount} queued)` : '';
|
|
222
|
-
statusText = `🔄 AI is streaming...${queuePart} [Enter: queue additional prompts]`;
|
|
154
|
+
if (this.historyIndex === -1) {
|
|
155
|
+
// Start navigating history, save current input
|
|
156
|
+
this.tempCurrentInput = this.inputBuffer;
|
|
157
|
+
this.historyIndex = 0;
|
|
223
158
|
}
|
|
224
|
-
else if (this.
|
|
225
|
-
|
|
159
|
+
else if (this.historyIndex < this.history.length - 1) {
|
|
160
|
+
this.historyIndex++;
|
|
226
161
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const currentInput = this.inputBuffer;
|
|
241
|
-
const maxInputDisplay = cols - 4;
|
|
242
|
-
// Truncate input if too long, showing end of input
|
|
243
|
-
let displayInput = currentInput;
|
|
244
|
-
if (displayInput.length > maxInputDisplay) {
|
|
245
|
-
displayInput = '…' + displayInput.slice(-(maxInputDisplay - 1));
|
|
162
|
+
const historyItem = this.history[this.historyIndex];
|
|
163
|
+
this.setInput(historyItem);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Handle history navigation - down
|
|
167
|
+
*/
|
|
168
|
+
navigateHistoryDown() {
|
|
169
|
+
if (this.historyIndex === -1)
|
|
170
|
+
return;
|
|
171
|
+
if (this.historyIndex > 0) {
|
|
172
|
+
this.historyIndex--;
|
|
173
|
+
const historyItem = this.history[this.historyIndex];
|
|
174
|
+
this.setInput(historyItem);
|
|
246
175
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const padding = cols - 3 - currentInput.length - statusText.length - 3;
|
|
253
|
-
if (padding > 3) {
|
|
254
|
-
this.safeWrite(' '.repeat(padding));
|
|
255
|
-
this.safeWrite(theme.ui.muted(statusText.slice(0, cols - currentInput.length - 6)));
|
|
256
|
-
}
|
|
176
|
+
else if (this.historyIndex === 0) {
|
|
177
|
+
// Back to original input
|
|
178
|
+
this.historyIndex = -1;
|
|
179
|
+
this.setInput(this.tempCurrentInput);
|
|
180
|
+
this.tempCurrentInput = '';
|
|
257
181
|
}
|
|
258
|
-
// Restore cursor to scroll region
|
|
259
|
-
this.safeWrite(ANSI.RESTORE_CURSOR);
|
|
260
182
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Set input text
|
|
185
|
+
*/
|
|
186
|
+
setInput(text, cursorPos) {
|
|
187
|
+
this.inputBuffer = text.slice(0, this.config.maxInputLength);
|
|
188
|
+
this.cursorPosition = typeof cursorPos === 'number'
|
|
189
|
+
? Math.max(0, Math.min(cursorPos, this.inputBuffer.length))
|
|
190
|
+
: this.inputBuffer.length;
|
|
191
|
+
this.updateState();
|
|
192
|
+
this.scheduleRender();
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Clear input
|
|
196
|
+
*/
|
|
197
|
+
clearInput() {
|
|
198
|
+
this.inputBuffer = '';
|
|
199
|
+
this.cursorPosition = 0;
|
|
200
|
+
this.historyIndex = -1;
|
|
201
|
+
this.tempCurrentInput = '';
|
|
202
|
+
this.updateState();
|
|
203
|
+
this.scheduleRender();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Clear paste mode
|
|
207
|
+
*/
|
|
208
|
+
clearPasteMode() {
|
|
209
|
+
this.isPasteMode = false;
|
|
210
|
+
this.pasteBuffer = '';
|
|
211
|
+
this.clearStatusMessage();
|
|
267
212
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Add input to history
|
|
215
|
+
*/
|
|
216
|
+
addToHistory(input) {
|
|
217
|
+
if (input.trim() && this.history[0] !== input.trim()) {
|
|
218
|
+
this.history.unshift(input.trim());
|
|
219
|
+
if (this.history.length > this.config.maxHistorySize) {
|
|
220
|
+
this.history.pop();
|
|
272
221
|
}
|
|
273
222
|
}
|
|
274
|
-
catch {
|
|
275
|
-
// Swallow write errors
|
|
276
|
-
}
|
|
277
223
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Set status message
|
|
226
|
+
*/
|
|
227
|
+
setStatusMessage(message) {
|
|
228
|
+
this.state.statusMessage = message;
|
|
229
|
+
this.updateState();
|
|
230
|
+
this.scheduleRender();
|
|
283
231
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Clear status message
|
|
234
|
+
*/
|
|
235
|
+
clearStatusMessage() {
|
|
236
|
+
this.state.statusMessage = null;
|
|
237
|
+
this.updateState();
|
|
238
|
+
this.scheduleRender();
|
|
291
239
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
this.
|
|
300
|
-
if (this.inputHistory.length > this.maxHistorySize) {
|
|
301
|
-
this.inputHistory = this.inputHistory.slice(-this.maxHistorySize);
|
|
302
|
-
}
|
|
240
|
+
/**
|
|
241
|
+
* Update internal state
|
|
242
|
+
*/
|
|
243
|
+
updateState() {
|
|
244
|
+
this.state.input = this.inputBuffer;
|
|
245
|
+
this.state.cursorPosition = this.cursorPosition;
|
|
246
|
+
this.state.isPasteMode = this.isPasteMode;
|
|
247
|
+
this.state.historyIndex = this.historyIndex;
|
|
303
248
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.isEnabled = enabled;
|
|
311
|
-
if (enabled) {
|
|
312
|
-
this.scheduleRender();
|
|
313
|
-
}
|
|
249
|
+
/**
|
|
250
|
+
* Set streaming state
|
|
251
|
+
*/
|
|
252
|
+
setStreaming(isStreaming) {
|
|
253
|
+
this.state.isStreaming = isStreaming;
|
|
254
|
+
this.scheduleRender();
|
|
314
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Set processing state
|
|
258
|
+
*/
|
|
315
259
|
setProcessing(isProcessing) {
|
|
316
|
-
if (this.isDisposed)
|
|
317
|
-
return;
|
|
318
260
|
this.state.isProcessing = isProcessing;
|
|
319
261
|
this.scheduleRender();
|
|
320
262
|
}
|
|
321
|
-
|
|
322
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Activate the chat box
|
|
265
|
+
*/
|
|
266
|
+
activate() {
|
|
267
|
+
if (this.isActive)
|
|
323
268
|
return;
|
|
324
|
-
|
|
325
|
-
this.
|
|
269
|
+
this.isActive = true;
|
|
270
|
+
this.clearInput();
|
|
271
|
+
this.setStatusMessage('Type while AI streams - Press Enter to send');
|
|
272
|
+
// Initial render
|
|
273
|
+
this.forceRender();
|
|
326
274
|
}
|
|
327
|
-
|
|
328
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Deactivate the chat box
|
|
277
|
+
*/
|
|
278
|
+
deactivate() {
|
|
279
|
+
this.isActive = false;
|
|
280
|
+
this.clearStatusMessage();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Schedule render (throttled)
|
|
284
|
+
*/
|
|
285
|
+
scheduleRender() {
|
|
286
|
+
if (this.renderScheduled)
|
|
329
287
|
return;
|
|
330
|
-
this.
|
|
331
|
-
|
|
288
|
+
this.renderScheduled = true;
|
|
289
|
+
setTimeout(() => {
|
|
290
|
+
this.renderScheduled = false;
|
|
291
|
+
this.render();
|
|
292
|
+
}, this.renderThrottleMs);
|
|
332
293
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
294
|
+
/**
|
|
295
|
+
* Force immediate render
|
|
296
|
+
*/
|
|
297
|
+
forceRender() {
|
|
298
|
+
this.render();
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Render the chat box
|
|
302
|
+
*/
|
|
303
|
+
render() {
|
|
304
|
+
if (!this.isActive)
|
|
305
|
+
return;
|
|
306
|
+
const now = Date.now();
|
|
307
|
+
if (now - this.lastRenderTime < this.renderThrottleMs) {
|
|
308
|
+
return;
|
|
349
309
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.scheduleRender();
|
|
361
|
-
if (this.onCommandQueued) {
|
|
362
|
-
this.onCommandQueued(cmd);
|
|
310
|
+
this.lastRenderTime = now;
|
|
311
|
+
// Clear the status area
|
|
312
|
+
this.writeStream.write('\x1B[s'); // Save cursor position
|
|
313
|
+
// Move to bottom position
|
|
314
|
+
this.writeStream.write(`\x1B[${this.config.position}B`);
|
|
315
|
+
this.writeStream.write('\x1B[2K\r'); // Clear line
|
|
316
|
+
// Render status message if present
|
|
317
|
+
if (this.state.statusMessage) {
|
|
318
|
+
this.writeStream.write(`${theme.info(this.state.statusMessage)}\n`);
|
|
319
|
+
this.writeStream.write('\x1B[2K\r'); // Clear next line
|
|
363
320
|
}
|
|
364
|
-
|
|
321
|
+
// Render input prompt
|
|
322
|
+
let prompt;
|
|
323
|
+
if (this.state.isStreaming) {
|
|
324
|
+
prompt = `${theme.info('◉')} ${theme.ui.muted('Type while AI streams (Enter to send)')}: `;
|
|
325
|
+
}
|
|
326
|
+
else if (this.state.isProcessing) {
|
|
327
|
+
prompt = `${theme.warning('◐')} ${theme.ui.muted('Processing...')}: `;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
prompt = `${theme.success('>')} `;
|
|
331
|
+
}
|
|
332
|
+
// Render input with cursor
|
|
333
|
+
const displayInput = this.isPasteMode
|
|
334
|
+
? `${theme.accent(this.inputBuffer)}`
|
|
335
|
+
: this.inputBuffer;
|
|
336
|
+
this.writeStream.write(`${prompt}${displayInput}`);
|
|
337
|
+
// Position cursor
|
|
338
|
+
const cursorOffset = prompt.length + this.cursorPosition;
|
|
339
|
+
this.writeStream.write(`\x1B[${cursorOffset}G`);
|
|
340
|
+
// Restore cursor position
|
|
341
|
+
this.writeStream.write('\x1B[u');
|
|
365
342
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Get current state
|
|
345
|
+
*/
|
|
346
|
+
getState() {
|
|
347
|
+
return { ...this.state };
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Get config
|
|
351
|
+
*/
|
|
352
|
+
getConfig() {
|
|
353
|
+
return { ...this.config };
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Update config
|
|
357
|
+
*/
|
|
358
|
+
updateConfig(config) {
|
|
359
|
+
this.config = { ...this.config, ...config };
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Dispose of resources
|
|
363
|
+
*/
|
|
364
|
+
dispose() {
|
|
365
|
+
this.deactivate();
|
|
366
|
+
this.clearInput();
|
|
367
|
+
this.clearStatusMessage();
|
|
370
368
|
}
|
|
371
369
|
}
|
|
372
|
-
//# sourceMappingURL=
|
|
370
|
+
//# sourceMappingURL=EnhancedPinnedChatBox.js.map
|