erosolar-cli 1.7.366 → 1.7.368
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/capabilities/learnCapability.d.ts +2 -0
- package/dist/capabilities/learnCapability.d.ts.map +1 -1
- package/dist/capabilities/learnCapability.js +9 -2
- package/dist/capabilities/learnCapability.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +2 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +32 -15
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/terminalInput.d.ts +32 -19
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +203 -255
- package/dist/shell/terminalInput.js.map +1 -1
- package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
- package/dist/shell/terminalInputAdapter.js +5 -2
- package/dist/shell/terminalInputAdapter.js.map +1 -1
- package/dist/subagents/taskRunner.d.ts.map +1 -1
- package/dist/subagents/taskRunner.js +7 -25
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/learnTools.js +127 -4
- package/dist/tools/learnTools.js.map +1 -1
- package/dist/tools/localExplore.d.ts +169 -0
- package/dist/tools/localExplore.d.ts.map +1 -0
- package/dist/tools/localExplore.js +1141 -0
- package/dist/tools/localExplore.js.map +1 -0
- package/dist/ui/ShellUIAdapter.d.ts +27 -0
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +175 -9
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/compactRenderer.d.ts +139 -0
- package/dist/ui/compactRenderer.d.ts.map +1 -0
- package/dist/ui/compactRenderer.js +398 -0
- package/dist/ui/compactRenderer.js.map +1 -0
- package/dist/ui/inPlaceUpdater.d.ts +181 -0
- package/dist/ui/inPlaceUpdater.d.ts.map +1 -0
- package/dist/ui/inPlaceUpdater.js +515 -0
- package/dist/ui/inPlaceUpdater.js.map +1 -0
- package/dist/ui/theme.d.ts +108 -3
- package/dist/ui/theme.d.ts.map +1 -1
- package/dist/ui/theme.js +124 -3
- package/dist/ui/theme.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts +44 -7
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +168 -84
- package/dist/ui/toolDisplay.js.map +1 -1
- package/dist/ui/unified/index.d.ts +11 -0
- package/dist/ui/unified/index.d.ts.map +1 -1
- package/dist/ui/unified/index.js +16 -0
- package/dist/ui/unified/index.js.map +1 -1
- package/dist/ui/unified/layout.d.ts.map +1 -1
- package/dist/ui/unified/layout.js +32 -47
- package/dist/ui/unified/layout.js.map +1 -1
- package/package.json +1 -1
|
@@ -92,7 +92,6 @@ export class TerminalInput extends EventEmitter {
|
|
|
92
92
|
metaTokenLimit = null; // Optional token window
|
|
93
93
|
metaThinkingMs = null; // Optional thinking duration
|
|
94
94
|
metaThinkingHasContent = false; // Whether collapsed thinking content exists
|
|
95
|
-
toolCount = null; // Total tool count for status display
|
|
96
95
|
lastRenderContent = '';
|
|
97
96
|
lastRenderCursor = -1;
|
|
98
97
|
renderDirty = false;
|
|
@@ -118,7 +117,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
118
117
|
editMode = 'display-edits';
|
|
119
118
|
verificationEnabled = true;
|
|
120
119
|
autoContinueEnabled = false;
|
|
121
|
-
verificationHotkey = 'alt+
|
|
120
|
+
verificationHotkey = 'alt+v';
|
|
122
121
|
autoContinueHotkey = 'alt+c';
|
|
123
122
|
thinkingHotkey = '/thinking';
|
|
124
123
|
modelLabel = null;
|
|
@@ -174,60 +173,63 @@ export class TerminalInput extends EventEmitter {
|
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
/**
|
|
177
|
-
* Process raw terminal data (handles bracketed paste sequences
|
|
178
|
-
* Returns true if the data was consumed (paste sequence
|
|
176
|
+
* Process raw terminal data (handles bracketed paste sequences and mouse events)
|
|
177
|
+
* Returns true if the data was consumed (paste sequence)
|
|
179
178
|
*/
|
|
180
179
|
processRawData(data) {
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return { consumed: filtered.length !== data.length, passthrough: filtered };
|
|
180
|
+
// Handle ALL mouse events in the data (SGR mode: \x1b[<button;x;yM or m)
|
|
181
|
+
// Process repeatedly until no more mouse events
|
|
182
|
+
const mousePattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
183
|
+
let remaining = data;
|
|
184
|
+
let hadMouseEvents = false;
|
|
185
|
+
// Remove all mouse escape sequences from the data
|
|
186
|
+
let match;
|
|
187
|
+
while ((match = mousePattern.exec(data)) !== null) {
|
|
188
|
+
const button = parseInt(match[1], 10);
|
|
189
|
+
this.handleMouseEvent(button, 0, 0, match[4]);
|
|
190
|
+
hadMouseEvents = true;
|
|
191
|
+
}
|
|
192
|
+
if (hadMouseEvents) {
|
|
193
|
+
// Strip all mouse sequences from the data
|
|
194
|
+
remaining = data.replace(/\x1b\[<\d+;\d+;\d+[Mm]/g, '');
|
|
195
|
+
if (!remaining) {
|
|
196
|
+
return { consumed: true, passthrough: '' };
|
|
197
|
+
}
|
|
198
|
+
// If there's remaining content, continue processing it
|
|
201
199
|
}
|
|
202
200
|
// Check for paste start
|
|
203
|
-
if (
|
|
201
|
+
if (remaining.includes(ESC.PASTE_START)) {
|
|
204
202
|
this.isPasting = true;
|
|
205
203
|
this.pasteBuffer = '';
|
|
206
204
|
// Extract content after paste start
|
|
207
|
-
const startIdx =
|
|
208
|
-
const
|
|
205
|
+
const startIdx = remaining.indexOf(ESC.PASTE_START) + ESC.PASTE_START.length;
|
|
206
|
+
const afterStart = remaining.slice(startIdx);
|
|
209
207
|
// Check if paste end is also in this chunk
|
|
210
|
-
if (
|
|
211
|
-
const endIdx =
|
|
212
|
-
this.pasteBuffer =
|
|
208
|
+
if (afterStart.includes(ESC.PASTE_END)) {
|
|
209
|
+
const endIdx = afterStart.indexOf(ESC.PASTE_END);
|
|
210
|
+
this.pasteBuffer = afterStart.slice(0, endIdx);
|
|
213
211
|
this.finishPaste();
|
|
214
|
-
return { consumed: true, passthrough:
|
|
212
|
+
return { consumed: true, passthrough: afterStart.slice(endIdx + ESC.PASTE_END.length) };
|
|
215
213
|
}
|
|
216
|
-
this.pasteBuffer =
|
|
214
|
+
this.pasteBuffer = afterStart;
|
|
217
215
|
return { consumed: true, passthrough: '' };
|
|
218
216
|
}
|
|
219
217
|
// Accumulating paste
|
|
220
218
|
if (this.isPasting) {
|
|
221
|
-
if (
|
|
222
|
-
const endIdx =
|
|
223
|
-
this.pasteBuffer +=
|
|
219
|
+
if (remaining.includes(ESC.PASTE_END)) {
|
|
220
|
+
const endIdx = remaining.indexOf(ESC.PASTE_END);
|
|
221
|
+
this.pasteBuffer += remaining.slice(0, endIdx);
|
|
224
222
|
this.finishPaste();
|
|
225
|
-
return { consumed: true, passthrough:
|
|
223
|
+
return { consumed: true, passthrough: remaining.slice(endIdx + ESC.PASTE_END.length) };
|
|
226
224
|
}
|
|
227
|
-
this.pasteBuffer +=
|
|
225
|
+
this.pasteBuffer += remaining;
|
|
228
226
|
return { consumed: true, passthrough: '' };
|
|
229
227
|
}
|
|
230
|
-
|
|
228
|
+
// If we processed mouse events but have remaining text, return it as passthrough
|
|
229
|
+
if (hadMouseEvents && remaining) {
|
|
230
|
+
return { consumed: true, passthrough: remaining };
|
|
231
|
+
}
|
|
232
|
+
return { consumed: false, passthrough: remaining };
|
|
231
233
|
}
|
|
232
234
|
/**
|
|
233
235
|
* Handle mouse events (button, x, y coordinates, action)
|
|
@@ -237,12 +239,30 @@ export class TerminalInput extends EventEmitter {
|
|
|
237
239
|
if (button === 64) {
|
|
238
240
|
// Scroll up (3 lines per wheel tick)
|
|
239
241
|
this.scrollUp(3);
|
|
242
|
+
return;
|
|
240
243
|
}
|
|
241
|
-
|
|
244
|
+
if (button === 65) {
|
|
242
245
|
// Scroll down (3 lines per wheel tick)
|
|
243
246
|
this.scrollDown(3);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Left button (0), middle button (1), right button (2)
|
|
250
|
+
// These are captured by SGR mouse tracking but we don't need to handle them
|
|
251
|
+
// Just consume silently - the escape sequence has already been processed
|
|
252
|
+
// The 'M' action is press, 'm' is release
|
|
253
|
+
if (button <= 2) {
|
|
254
|
+
// Silently consume click events to prevent artifacts
|
|
255
|
+
// Left clicks (button=0) in the input area could focus the input
|
|
256
|
+
// but terminal apps typically handle this natively
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// Button with motion flag (button + 32) - drag events
|
|
260
|
+
if (button >= 32 && button <= 34) {
|
|
261
|
+
// Drag events - consume silently
|
|
262
|
+
return;
|
|
244
263
|
}
|
|
245
|
-
//
|
|
264
|
+
// Any other unrecognized button - log for debugging in dev mode
|
|
265
|
+
// but don't output anything to prevent artifacts
|
|
246
266
|
}
|
|
247
267
|
/**
|
|
248
268
|
* Handle a keypress event
|
|
@@ -448,14 +468,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
448
468
|
const nextAutoHotkey = options.autoContinueHotkey ?? this.autoContinueHotkey;
|
|
449
469
|
const nextThinkingHotkey = options.thinkingHotkey ?? this.thinkingHotkey;
|
|
450
470
|
const nextThinkingLabel = options.thinkingModeLabel === undefined ? this.thinkingModeLabel : (options.thinkingModeLabel || null);
|
|
451
|
-
const nextToolCount = options.toolCount === undefined ? this.toolCount : (options.toolCount ?? null);
|
|
452
471
|
if (this.verificationEnabled === nextVerification &&
|
|
453
472
|
this.autoContinueEnabled === nextAutoContinue &&
|
|
454
473
|
this.verificationHotkey === nextVerifyHotkey &&
|
|
455
474
|
this.autoContinueHotkey === nextAutoHotkey &&
|
|
456
475
|
this.thinkingHotkey === nextThinkingHotkey &&
|
|
457
|
-
this.thinkingModeLabel === nextThinkingLabel
|
|
458
|
-
this.toolCount === nextToolCount) {
|
|
476
|
+
this.thinkingModeLabel === nextThinkingLabel) {
|
|
459
477
|
return;
|
|
460
478
|
}
|
|
461
479
|
this.verificationEnabled = nextVerification;
|
|
@@ -464,7 +482,6 @@ export class TerminalInput extends EventEmitter {
|
|
|
464
482
|
this.autoContinueHotkey = nextAutoHotkey;
|
|
465
483
|
this.thinkingHotkey = nextThinkingHotkey;
|
|
466
484
|
this.thinkingModeLabel = nextThinkingLabel;
|
|
467
|
-
this.toolCount = nextToolCount;
|
|
468
485
|
this.scheduleRender();
|
|
469
486
|
}
|
|
470
487
|
/**
|
|
@@ -700,101 +717,123 @@ export class TerminalInput extends EventEmitter {
|
|
|
700
717
|
return [`${leftText}${' '.repeat(spacing)}${rightText}`];
|
|
701
718
|
}
|
|
702
719
|
/**
|
|
703
|
-
* Build mode controls line with all keyboard shortcuts
|
|
704
|
-
* Shows
|
|
720
|
+
* Build mode controls line with all keyboard shortcuts.
|
|
721
|
+
* Shows status, all toggles, and contextual information.
|
|
722
|
+
* Enhanced with comprehensive feature status display.
|
|
723
|
+
*
|
|
724
|
+
* Format: [Key] Label:status · [Key] Label:status · context% · model
|
|
705
725
|
*/
|
|
706
726
|
buildModeControls(cols) {
|
|
707
727
|
const width = Math.max(8, cols - 2);
|
|
708
|
-
const
|
|
709
|
-
|
|
728
|
+
const leftParts = [];
|
|
729
|
+
const rightParts = [];
|
|
730
|
+
// Streaming indicator with animated spinner
|
|
710
731
|
if (this.streamingLabel) {
|
|
711
|
-
|
|
712
|
-
parts.push({ text: `Esc:stop`, tone: 'warn' });
|
|
732
|
+
leftParts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
|
|
713
733
|
}
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const editIcon = this.editMode === 'display-edits' ? '✓' : '?';
|
|
718
|
-
const editTone = this.editMode === 'display-edits' ? 'success' : 'muted';
|
|
719
|
-
parts.push({ text: `⇧Tab:edits${editIcon}`, tone: editTone });
|
|
720
|
-
// Verify mode toggle (Alt+D)
|
|
721
|
-
const verifyIcon = this.verificationEnabled ? '✓' : '○';
|
|
722
|
-
const verifyTone = this.verificationEnabled ? 'success' : 'muted';
|
|
723
|
-
parts.push({ text: `⌥D:verify${verifyIcon}`, tone: verifyTone });
|
|
724
|
-
// Auto-continue toggle (Alt+C)
|
|
725
|
-
const autoIcon = this.autoContinueEnabled ? '✓' : '○';
|
|
726
|
-
const autoTone = this.autoContinueEnabled ? 'success' : 'muted';
|
|
727
|
-
parts.push({ text: `⌥C:auto${autoIcon}`, tone: autoTone });
|
|
728
|
-
// Thinking mode toggle (Alt+T)
|
|
729
|
-
if (this.thinkingModeLabel) {
|
|
730
|
-
const thinkingShort = this.thinkingModeLabel === 'minimal' ? 'min' :
|
|
731
|
-
this.thinkingModeLabel === 'balanced' ? 'bal' :
|
|
732
|
-
this.thinkingModeLabel === 'extended' ? 'ext' : this.thinkingModeLabel;
|
|
733
|
-
parts.push({ text: `⌥T:${thinkingShort}`, tone: 'info' });
|
|
734
|
+
// Override status (warnings, errors)
|
|
735
|
+
if (this.overrideStatusMessage) {
|
|
736
|
+
leftParts.push({ text: `⚠ ${this.overrideStatusMessage}`, tone: 'warn' });
|
|
734
737
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
// Clear context (Alt+X)
|
|
739
|
-
parts.push({ text: `⌥X:clear`, tone: 'muted' });
|
|
740
|
-
// === STATUS INFO ===
|
|
741
|
-
// Context remaining percentage
|
|
742
|
-
const contextRemaining = this.computeContextRemaining();
|
|
743
|
-
if (contextRemaining !== null) {
|
|
744
|
-
const tone = contextRemaining <= 10 ? 'warn' : contextRemaining <= 30 ? 'info' : 'muted';
|
|
745
|
-
parts.push({ text: `ctx:${contextRemaining}%`, tone });
|
|
746
|
-
}
|
|
747
|
-
// Token usage
|
|
748
|
-
if (this.metaTokensUsed !== null) {
|
|
749
|
-
const used = this.formatTokenCount(this.metaTokensUsed);
|
|
750
|
-
const limit = this.metaTokenLimit ? `/${this.formatTokenCount(this.metaTokenLimit)}` : '';
|
|
751
|
-
parts.push({ text: `${used}${limit}tk`, tone: 'muted' });
|
|
738
|
+
// Main status message
|
|
739
|
+
if (this.statusMessage) {
|
|
740
|
+
leftParts.push({ text: this.statusMessage, tone: this.streamingLabel ? 'muted' : 'info' });
|
|
752
741
|
}
|
|
753
|
-
//
|
|
754
|
-
|
|
755
|
-
|
|
742
|
+
// === KEYBOARD SHORTCUTS (Compact format: Key·Status) ===
|
|
743
|
+
// Interrupt shortcut (during streaming)
|
|
744
|
+
if (this.mode === 'streaming' || this.scrollRegionActive) {
|
|
745
|
+
leftParts.push({ text: `Esc·Stop`, tone: 'warn' });
|
|
756
746
|
}
|
|
757
|
-
//
|
|
758
|
-
|
|
759
|
-
|
|
747
|
+
// Edit mode toggle (Shift+Tab) - Compact display
|
|
748
|
+
const editStatus = this.editMode === 'display-edits' ? '✓' : '?';
|
|
749
|
+
const editTone = this.editMode === 'display-edits' ? 'success' : 'muted';
|
|
750
|
+
leftParts.push({ text: `⇧Tab·${editStatus}edits`, tone: editTone });
|
|
751
|
+
// Verification toggle (Alt+V) - Compact display
|
|
752
|
+
const verifyStatus = this.verificationEnabled ? '✓' : '○';
|
|
753
|
+
leftParts.push({
|
|
754
|
+
text: `⌥V·${verifyStatus}verify`,
|
|
755
|
+
tone: this.verificationEnabled ? 'success' : 'muted'
|
|
756
|
+
});
|
|
757
|
+
// Auto-continue toggle (Alt+C) - Compact display
|
|
758
|
+
const autoStatus = this.autoContinueEnabled ? '↻' : '○';
|
|
759
|
+
leftParts.push({
|
|
760
|
+
text: `⌥C·${autoStatus}cont`,
|
|
761
|
+
tone: this.autoContinueEnabled ? 'info' : 'muted'
|
|
762
|
+
});
|
|
763
|
+
// Thinking mode toggle (if available)
|
|
764
|
+
if (this.thinkingModeLabel) {
|
|
765
|
+
const shortThinking = this.thinkingModeLabel.length > 8
|
|
766
|
+
? this.thinkingModeLabel.slice(0, 6) + '..'
|
|
767
|
+
: this.thinkingModeLabel;
|
|
768
|
+
rightParts.push({ text: `⌥T·${shortThinking}`, tone: 'info' });
|
|
769
|
+
}
|
|
770
|
+
// === CONTEXTUAL INFO ===
|
|
771
|
+
// Queued commands
|
|
772
|
+
if (this.queue.length > 0) {
|
|
773
|
+
const queueIcon = this.mode === 'streaming' ? '⏳' : '▸';
|
|
774
|
+
leftParts.push({ text: `${queueIcon}${this.queue.length} queued`, tone: 'info' });
|
|
775
|
+
}
|
|
776
|
+
// Multi-line indicator
|
|
777
|
+
if (this.buffer.includes('\n')) {
|
|
778
|
+
const lineCount = this.buffer.split('\n').length;
|
|
779
|
+
rightParts.push({ text: `${lineCount}L`, tone: 'muted' });
|
|
780
|
+
}
|
|
781
|
+
// Paste indicator
|
|
782
|
+
if (this.pastePlaceholders.length > 0) {
|
|
783
|
+
const latest = this.pastePlaceholders[this.pastePlaceholders.length - 1];
|
|
784
|
+
rightParts.push({
|
|
785
|
+
text: `📋${latest.lineCount}L`,
|
|
786
|
+
tone: 'info',
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
// Note: Model, timer, and context are shown in the meta line above
|
|
790
|
+
// Controls line focuses on keyboard shortcuts only
|
|
791
|
+
// Render: left-aligned shortcuts, right-aligned context info
|
|
792
|
+
if (!rightParts.length || width < 60) {
|
|
793
|
+
const merged = rightParts.length ? [...leftParts, ...rightParts] : leftParts;
|
|
794
|
+
return renderStatusLine(merged, width);
|
|
760
795
|
}
|
|
761
|
-
|
|
796
|
+
const leftWidth = Math.max(12, Math.floor(width * 0.6));
|
|
797
|
+
const rightWidth = Math.max(14, width - leftWidth - 1);
|
|
798
|
+
const leftText = renderStatusLine(leftParts, leftWidth);
|
|
799
|
+
const rightText = renderStatusLine(rightParts, rightWidth);
|
|
800
|
+
const spacing = Math.max(1, width - this.visibleLength(leftText) - this.visibleLength(rightText));
|
|
801
|
+
return `${leftText}${' '.repeat(spacing)}${rightText}`;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Render a mini context usage bar (5 chars)
|
|
805
|
+
*/
|
|
806
|
+
renderMiniContextBar(percentRemaining) {
|
|
807
|
+
const bars = 5;
|
|
808
|
+
const filled = Math.round((percentRemaining / 100) * bars);
|
|
809
|
+
const empty = bars - filled;
|
|
810
|
+
return '█'.repeat(filled) + '░'.repeat(empty);
|
|
762
811
|
}
|
|
763
812
|
formatHotkey(hotkey) {
|
|
764
813
|
const normalized = hotkey.trim().toLowerCase();
|
|
765
814
|
if (!normalized)
|
|
766
815
|
return hotkey;
|
|
767
816
|
const parts = normalized.split('+').filter(Boolean);
|
|
768
|
-
// Use readable key names instead of symbols for better terminal compatibility
|
|
769
817
|
const map = {
|
|
770
|
-
shift: '
|
|
771
|
-
sh: '
|
|
772
|
-
alt: '
|
|
773
|
-
option: '
|
|
774
|
-
opt: '
|
|
775
|
-
ctrl: '
|
|
776
|
-
control: '
|
|
777
|
-
cmd: '
|
|
778
|
-
meta: '
|
|
779
|
-
esc: 'Esc',
|
|
780
|
-
escape: 'Esc',
|
|
781
|
-
tab: 'Tab',
|
|
782
|
-
return: 'Enter',
|
|
783
|
-
enter: 'Enter',
|
|
784
|
-
pageup: 'PgUp',
|
|
785
|
-
pagedown: 'PgDn',
|
|
786
|
-
home: 'Home',
|
|
787
|
-
end: 'End',
|
|
818
|
+
shift: '⇧',
|
|
819
|
+
sh: '⇧',
|
|
820
|
+
alt: '⌥',
|
|
821
|
+
option: '⌥',
|
|
822
|
+
opt: '⌥',
|
|
823
|
+
ctrl: '⌃',
|
|
824
|
+
control: '⌃',
|
|
825
|
+
cmd: '⌘',
|
|
826
|
+
meta: '⌘',
|
|
788
827
|
};
|
|
789
828
|
const formatted = parts
|
|
790
829
|
.map((part) => {
|
|
791
|
-
const
|
|
792
|
-
if (
|
|
793
|
-
return
|
|
794
|
-
return part.length === 1 ? part.toUpperCase() : part.
|
|
830
|
+
const symbol = map[part];
|
|
831
|
+
if (symbol)
|
|
832
|
+
return symbol;
|
|
833
|
+
return part.length === 1 ? part.toUpperCase() : part.toUpperCase();
|
|
795
834
|
})
|
|
796
|
-
.join('
|
|
797
|
-
return
|
|
835
|
+
.join('');
|
|
836
|
+
return formatted || hotkey;
|
|
798
837
|
}
|
|
799
838
|
computeContextRemaining() {
|
|
800
839
|
if (this.contextUsage === null) {
|
|
@@ -960,7 +999,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
960
999
|
* Calculate chat box height.
|
|
961
1000
|
*/
|
|
962
1001
|
getChatBoxHeight() {
|
|
963
|
-
return
|
|
1002
|
+
return 6; // Fixed: meta + divider + input + controls + buffer
|
|
964
1003
|
}
|
|
965
1004
|
/**
|
|
966
1005
|
* @deprecated Use streamContent() instead
|
|
@@ -996,41 +1035,26 @@ export class TerminalInput extends EventEmitter {
|
|
|
996
1035
|
}
|
|
997
1036
|
}
|
|
998
1037
|
/**
|
|
999
|
-
* Enter alternate screen buffer
|
|
1000
|
-
*
|
|
1038
|
+
* Enter alternate screen buffer.
|
|
1039
|
+
* DISABLED: Using terminal-native mode for proper scrollback and text selection.
|
|
1001
1040
|
*/
|
|
1002
1041
|
enterAlternateScreen() {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
this.write(ESC.ENTER_ALT_SCREEN);
|
|
1006
|
-
this.write(ESC.HOME);
|
|
1007
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1008
|
-
this.contentRow = 1;
|
|
1009
|
-
this.alternateScreenActive = true;
|
|
1010
|
-
}
|
|
1011
|
-
finally {
|
|
1012
|
-
writeLock.unlock();
|
|
1013
|
-
}
|
|
1042
|
+
// Disabled - using terminal-native mode
|
|
1043
|
+
this.contentRow = 1;
|
|
1014
1044
|
}
|
|
1015
1045
|
/**
|
|
1016
1046
|
* Exit alternate screen buffer.
|
|
1017
|
-
*
|
|
1047
|
+
* DISABLED: Using terminal-native mode.
|
|
1018
1048
|
*/
|
|
1019
1049
|
exitAlternateScreen() {
|
|
1020
|
-
|
|
1021
|
-
try {
|
|
1022
|
-
this.write(ESC.EXIT_ALT_SCREEN);
|
|
1023
|
-
this.alternateScreenActive = false;
|
|
1024
|
-
}
|
|
1025
|
-
finally {
|
|
1026
|
-
writeLock.unlock();
|
|
1027
|
-
}
|
|
1050
|
+
// Disabled - using terminal-native mode
|
|
1028
1051
|
}
|
|
1029
1052
|
/**
|
|
1030
1053
|
* Check if alternate screen buffer is currently active.
|
|
1054
|
+
* Always returns false - using terminal-native mode.
|
|
1031
1055
|
*/
|
|
1032
1056
|
isAlternateScreenActive() {
|
|
1033
|
-
return
|
|
1057
|
+
return false;
|
|
1034
1058
|
}
|
|
1035
1059
|
/**
|
|
1036
1060
|
* Get a snapshot of the scrollback buffer (for display on exit).
|
|
@@ -1039,14 +1063,17 @@ export class TerminalInput extends EventEmitter {
|
|
|
1039
1063
|
return [...this.scrollbackBuffer];
|
|
1040
1064
|
}
|
|
1041
1065
|
/**
|
|
1042
|
-
* Clear the
|
|
1043
|
-
*
|
|
1066
|
+
* Clear the visible terminal area and reset content position.
|
|
1067
|
+
* In terminal-native mode, this just adds newlines to scroll past content
|
|
1068
|
+
* rather than clearing history (preserving scrollback).
|
|
1044
1069
|
*/
|
|
1045
1070
|
clearScreen() {
|
|
1046
1071
|
writeLock.lock('clearScreen');
|
|
1047
1072
|
try {
|
|
1073
|
+
// In native mode, scroll past existing content rather than clearing
|
|
1074
|
+
const { rows } = this.getSize();
|
|
1075
|
+
this.write('\n'.repeat(rows));
|
|
1048
1076
|
this.write(ESC.HOME);
|
|
1049
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1050
1077
|
this.contentRow = 1;
|
|
1051
1078
|
}
|
|
1052
1079
|
finally {
|
|
@@ -1146,8 +1173,8 @@ export class TerminalInput extends EventEmitter {
|
|
|
1146
1173
|
this.insertNewline();
|
|
1147
1174
|
break;
|
|
1148
1175
|
// === MODE TOGGLES ===
|
|
1149
|
-
case '
|
|
1150
|
-
// Alt+
|
|
1176
|
+
case 'v':
|
|
1177
|
+
// Alt+V: Toggle verification mode (auto-tests after edits)
|
|
1151
1178
|
this.emit('toggleVerify');
|
|
1152
1179
|
break;
|
|
1153
1180
|
case 'c':
|
|
@@ -1214,25 +1241,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
1214
1241
|
return Math.max(5, rows - chatBoxHeight - 2);
|
|
1215
1242
|
}
|
|
1216
1243
|
/**
|
|
1217
|
-
* Build scroll indicator for the divider line
|
|
1218
|
-
*
|
|
1244
|
+
* Build scroll indicator for the divider line.
|
|
1245
|
+
* Scrollback navigation is disabled in alternate screen mode.
|
|
1246
|
+
* This returns null - no scroll indicator is shown.
|
|
1219
1247
|
*/
|
|
1220
1248
|
buildScrollIndicator() {
|
|
1221
|
-
|
|
1222
|
-
// In scrollback mode - show position
|
|
1223
|
-
if (this.isInScrollbackMode && this.scrollbackOffset > 0) {
|
|
1224
|
-
const { rows } = this.getSize();
|
|
1225
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1226
|
-
const viewportHeight = Math.max(1, rows - chatBoxHeight);
|
|
1227
|
-
const currentPos = Math.max(0, bufferSize - this.scrollbackOffset - viewportHeight);
|
|
1228
|
-
const pct = bufferSize > 0 ? Math.round((currentPos / bufferSize) * 100) : 0;
|
|
1229
|
-
return `↑${this.scrollbackOffset} · ${pct}% · PgUp/Dn`;
|
|
1230
|
-
}
|
|
1231
|
-
// Not in scrollback - show hint if there's history
|
|
1232
|
-
if (bufferSize > 20) {
|
|
1233
|
-
const sizeLabel = bufferSize >= 1000 ? `${Math.floor(bufferSize / 1000)}k` : `${bufferSize}`;
|
|
1234
|
-
return `↕${sizeLabel}L · PgUp`;
|
|
1235
|
-
}
|
|
1249
|
+
// Scrollback navigation disabled - no indicator needed
|
|
1236
1250
|
return null;
|
|
1237
1251
|
}
|
|
1238
1252
|
handleSpecialKey(_str, key) {
|
|
@@ -1294,10 +1308,11 @@ export class TerminalInput extends EventEmitter {
|
|
|
1294
1308
|
}
|
|
1295
1309
|
return true;
|
|
1296
1310
|
case 'pageup':
|
|
1297
|
-
|
|
1311
|
+
// Scrollback disabled in alternate screen mode
|
|
1312
|
+
// Users should use terminal's native scrollback if available
|
|
1298
1313
|
return true;
|
|
1299
1314
|
case 'pagedown':
|
|
1300
|
-
|
|
1315
|
+
// Scrollback disabled in alternate screen mode
|
|
1301
1316
|
return true;
|
|
1302
1317
|
case 'tab':
|
|
1303
1318
|
if (key.shift) {
|
|
@@ -1691,120 +1706,53 @@ export class TerminalInput extends EventEmitter {
|
|
|
1691
1706
|
}
|
|
1692
1707
|
/**
|
|
1693
1708
|
* Scroll up by a number of lines (PageUp)
|
|
1709
|
+
* Note: Scrollback is disabled in alternate screen mode to avoid display corruption.
|
|
1710
|
+
* Users should use their terminal's native scrollback or copy/paste features.
|
|
1694
1711
|
*/
|
|
1695
|
-
scrollUp(
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
// Calculate max scroll offset
|
|
1700
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1701
|
-
this.scrollbackOffset = Math.min(this.scrollbackOffset + lines, maxOffset);
|
|
1702
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1703
|
-
if (this.isInScrollbackMode) {
|
|
1704
|
-
this.renderScrollbackView();
|
|
1705
|
-
}
|
|
1712
|
+
scrollUp(_lines = 10) {
|
|
1713
|
+
// Scrollback disabled - alternate screen buffer doesn't support it well
|
|
1714
|
+
// The scrollback buffer is still maintained for potential future use
|
|
1715
|
+
// Users can select and copy text normally since mouse tracking is off
|
|
1706
1716
|
}
|
|
1707
1717
|
/**
|
|
1708
1718
|
* Scroll down by a number of lines (PageDown)
|
|
1719
|
+
* Note: Scrollback disabled - see scrollUp comment
|
|
1709
1720
|
*/
|
|
1710
|
-
scrollDown(
|
|
1711
|
-
|
|
1712
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1713
|
-
if (this.isInScrollbackMode) {
|
|
1714
|
-
this.renderScrollbackView();
|
|
1715
|
-
}
|
|
1716
|
-
else {
|
|
1717
|
-
// Returned to live mode - force re-render
|
|
1718
|
-
this.forceRender();
|
|
1719
|
-
}
|
|
1721
|
+
scrollDown(_lines = 10) {
|
|
1722
|
+
// Scrollback disabled
|
|
1720
1723
|
}
|
|
1721
1724
|
/**
|
|
1722
1725
|
* Jump to the top of scrollback buffer
|
|
1726
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1727
|
+
* The scrollback buffer is maintained but cannot be rendered properly.
|
|
1723
1728
|
*/
|
|
1724
1729
|
scrollToTop() {
|
|
1725
|
-
|
|
1726
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1727
|
-
const visibleLines = Math.max(1, rows - chatBoxHeight);
|
|
1728
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1729
|
-
this.scrollbackOffset = maxOffset;
|
|
1730
|
-
this.isInScrollbackMode = true;
|
|
1731
|
-
this.renderScrollbackView();
|
|
1730
|
+
// Disabled - causes display corruption in alternate screen buffer
|
|
1732
1731
|
}
|
|
1733
1732
|
/**
|
|
1734
1733
|
* Jump to the bottom (live mode)
|
|
1734
|
+
* DISABLED: Scrollback navigation causes display corruption.
|
|
1735
1735
|
*/
|
|
1736
1736
|
scrollToBottom() {
|
|
1737
|
+
// Reset scrollback state in case it was somehow enabled
|
|
1737
1738
|
this.scrollbackOffset = 0;
|
|
1738
1739
|
this.isInScrollbackMode = false;
|
|
1739
|
-
this.forceRender();
|
|
1740
1740
|
}
|
|
1741
1741
|
/**
|
|
1742
1742
|
* Toggle scrollback mode on/off (Alt+S hotkey)
|
|
1743
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1743
1744
|
*/
|
|
1744
1745
|
toggleScrollbackMode() {
|
|
1745
|
-
|
|
1746
|
-
this.scrollToBottom();
|
|
1747
|
-
}
|
|
1748
|
-
else if (this.scrollbackBuffer.length > 0) {
|
|
1749
|
-
this.scrollUp(20);
|
|
1750
|
-
}
|
|
1746
|
+
// Disabled - alternate screen buffer doesn't support manual scrollback rendering
|
|
1751
1747
|
}
|
|
1752
1748
|
/**
|
|
1753
|
-
* Render the scrollback buffer view
|
|
1754
|
-
*
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1757
|
-
* - Keyboard navigation hints
|
|
1758
|
-
* - Animated indicators
|
|
1749
|
+
* Render the scrollback buffer view.
|
|
1750
|
+
* DISABLED: This causes display corruption in alternate screen mode.
|
|
1751
|
+
* The alternate screen buffer has its own rendering model that conflicts with
|
|
1752
|
+
* manual scroll region manipulation.
|
|
1759
1753
|
*/
|
|
1760
1754
|
renderScrollbackView() {
|
|
1761
|
-
|
|
1762
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1763
|
-
const contentHeight = Math.max(1, rows - chatBoxHeight);
|
|
1764
|
-
writeLock.lock('renderScrollback');
|
|
1765
|
-
try {
|
|
1766
|
-
this.write(ESC.SAVE);
|
|
1767
|
-
this.write(ESC.HIDE);
|
|
1768
|
-
// Clear content area
|
|
1769
|
-
for (let i = 1; i <= contentHeight; i++) {
|
|
1770
|
-
this.write(ESC.TO(i, 1));
|
|
1771
|
-
this.write(ESC.CLEAR_LINE);
|
|
1772
|
-
}
|
|
1773
|
-
// Calculate which lines to show
|
|
1774
|
-
const totalLines = this.scrollbackBuffer.length;
|
|
1775
|
-
const startIdx = Math.max(0, totalLines - this.scrollbackOffset - contentHeight);
|
|
1776
|
-
const endIdx = Math.max(0, totalLines - this.scrollbackOffset);
|
|
1777
|
-
const visibleLines = this.scrollbackBuffer.slice(startIdx, endIdx);
|
|
1778
|
-
// Build header bar with navigation hints
|
|
1779
|
-
const headerInfo = this.buildScrollbackHeader(cols, totalLines, startIdx, endIdx);
|
|
1780
|
-
this.write(ESC.TO(1, 1));
|
|
1781
|
-
this.write(headerInfo);
|
|
1782
|
-
// Render visible lines with line numbers and visual guides
|
|
1783
|
-
const lineNumWidth = String(totalLines).length + 1;
|
|
1784
|
-
const contentStart = 2; // Start after header
|
|
1785
|
-
for (let i = 0; i < Math.min(visibleLines.length, contentHeight - 1); i++) {
|
|
1786
|
-
const line = visibleLines[i] ?? '';
|
|
1787
|
-
const lineNum = startIdx + i + 1;
|
|
1788
|
-
this.write(ESC.TO(contentStart + i, 1));
|
|
1789
|
-
// Line number gutter
|
|
1790
|
-
const numStr = String(lineNum).padStart(lineNumWidth, ' ');
|
|
1791
|
-
this.write(theme.ui.muted(`${numStr} │ `));
|
|
1792
|
-
// Content with truncation
|
|
1793
|
-
const gutterWidth = lineNumWidth + 4;
|
|
1794
|
-
const maxLen = cols - gutterWidth - 2;
|
|
1795
|
-
const displayLine = line.length > maxLen ? line.slice(0, maxLen - 3) + '...' : line;
|
|
1796
|
-
this.write(displayLine);
|
|
1797
|
-
}
|
|
1798
|
-
// Add visual scroll track on the right edge
|
|
1799
|
-
this.renderScrollTrack(cols, contentHeight, totalLines, startIdx, endIdx);
|
|
1800
|
-
this.write(ESC.RESTORE);
|
|
1801
|
-
this.write(ESC.SHOW);
|
|
1802
|
-
}
|
|
1803
|
-
finally {
|
|
1804
|
-
writeLock.unlock();
|
|
1805
|
-
}
|
|
1806
|
-
// Re-render chat box
|
|
1807
|
-
this.forceRender();
|
|
1755
|
+
// Disabled - causes display corruption
|
|
1808
1756
|
}
|
|
1809
1757
|
/**
|
|
1810
1758
|
* Build scrollback header with navigation hints
|