erosolar-cli 1.7.367 → 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 -20
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +203 -257
- 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,103 +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
|
|
705
|
-
*
|
|
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
|
|
706
725
|
*/
|
|
707
726
|
buildModeControls(cols) {
|
|
708
727
|
const width = Math.max(8, cols - 2);
|
|
709
|
-
const
|
|
710
|
-
|
|
728
|
+
const leftParts = [];
|
|
729
|
+
const rightParts = [];
|
|
730
|
+
// Streaming indicator with animated spinner
|
|
711
731
|
if (this.streamingLabel) {
|
|
712
|
-
|
|
713
|
-
parts.push({ text: `Esc:stop`, tone: 'warn' });
|
|
732
|
+
leftParts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
|
|
714
733
|
}
|
|
715
|
-
//
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const editTone = this.editMode === 'display-edits' ? 'success' : 'muted';
|
|
719
|
-
parts.push({ text: `Shift+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: `Alt+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: `Alt+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: `Alt+T:think[${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: `Alt+X:clear`, tone: 'muted' });
|
|
740
|
-
// Scroll hint
|
|
741
|
-
parts.push({ text: `PgUp/Dn:scroll`, tone: 'muted' });
|
|
742
|
-
// === STATUS INFO ===
|
|
743
|
-
// Context remaining percentage
|
|
744
|
-
const contextRemaining = this.computeContextRemaining();
|
|
745
|
-
if (contextRemaining !== null) {
|
|
746
|
-
const tone = contextRemaining <= 10 ? 'warn' : contextRemaining <= 30 ? 'info' : 'muted';
|
|
747
|
-
parts.push({ text: `ctx:${contextRemaining}%`, tone });
|
|
748
|
-
}
|
|
749
|
-
// Token usage
|
|
750
|
-
if (this.metaTokensUsed !== null) {
|
|
751
|
-
const used = this.formatTokenCount(this.metaTokensUsed);
|
|
752
|
-
const limit = this.metaTokenLimit ? `/${this.formatTokenCount(this.metaTokenLimit)}` : '';
|
|
753
|
-
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' });
|
|
754
741
|
}
|
|
755
|
-
//
|
|
756
|
-
|
|
757
|
-
|
|
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' });
|
|
758
746
|
}
|
|
759
|
-
//
|
|
760
|
-
|
|
761
|
-
|
|
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);
|
|
762
795
|
}
|
|
763
|
-
|
|
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);
|
|
764
811
|
}
|
|
765
812
|
formatHotkey(hotkey) {
|
|
766
813
|
const normalized = hotkey.trim().toLowerCase();
|
|
767
814
|
if (!normalized)
|
|
768
815
|
return hotkey;
|
|
769
816
|
const parts = normalized.split('+').filter(Boolean);
|
|
770
|
-
// Use readable key names instead of symbols for better terminal compatibility
|
|
771
817
|
const map = {
|
|
772
|
-
shift: '
|
|
773
|
-
sh: '
|
|
774
|
-
alt: '
|
|
775
|
-
option: '
|
|
776
|
-
opt: '
|
|
777
|
-
ctrl: '
|
|
778
|
-
control: '
|
|
779
|
-
cmd: '
|
|
780
|
-
meta: '
|
|
781
|
-
esc: 'Esc',
|
|
782
|
-
escape: 'Esc',
|
|
783
|
-
tab: 'Tab',
|
|
784
|
-
return: 'Enter',
|
|
785
|
-
enter: 'Enter',
|
|
786
|
-
pageup: 'PgUp',
|
|
787
|
-
pagedown: 'PgDn',
|
|
788
|
-
home: 'Home',
|
|
789
|
-
end: 'End',
|
|
818
|
+
shift: '⇧',
|
|
819
|
+
sh: '⇧',
|
|
820
|
+
alt: '⌥',
|
|
821
|
+
option: '⌥',
|
|
822
|
+
opt: '⌥',
|
|
823
|
+
ctrl: '⌃',
|
|
824
|
+
control: '⌃',
|
|
825
|
+
cmd: '⌘',
|
|
826
|
+
meta: '⌘',
|
|
790
827
|
};
|
|
791
828
|
const formatted = parts
|
|
792
829
|
.map((part) => {
|
|
793
|
-
const
|
|
794
|
-
if (
|
|
795
|
-
return
|
|
796
|
-
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();
|
|
797
834
|
})
|
|
798
|
-
.join('
|
|
799
|
-
return
|
|
835
|
+
.join('');
|
|
836
|
+
return formatted || hotkey;
|
|
800
837
|
}
|
|
801
838
|
computeContextRemaining() {
|
|
802
839
|
if (this.contextUsage === null) {
|
|
@@ -962,7 +999,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
962
999
|
* Calculate chat box height.
|
|
963
1000
|
*/
|
|
964
1001
|
getChatBoxHeight() {
|
|
965
|
-
return
|
|
1002
|
+
return 6; // Fixed: meta + divider + input + controls + buffer
|
|
966
1003
|
}
|
|
967
1004
|
/**
|
|
968
1005
|
* @deprecated Use streamContent() instead
|
|
@@ -998,41 +1035,26 @@ export class TerminalInput extends EventEmitter {
|
|
|
998
1035
|
}
|
|
999
1036
|
}
|
|
1000
1037
|
/**
|
|
1001
|
-
* Enter alternate screen buffer
|
|
1002
|
-
*
|
|
1038
|
+
* Enter alternate screen buffer.
|
|
1039
|
+
* DISABLED: Using terminal-native mode for proper scrollback and text selection.
|
|
1003
1040
|
*/
|
|
1004
1041
|
enterAlternateScreen() {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
this.write(ESC.ENTER_ALT_SCREEN);
|
|
1008
|
-
this.write(ESC.HOME);
|
|
1009
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1010
|
-
this.contentRow = 1;
|
|
1011
|
-
this.alternateScreenActive = true;
|
|
1012
|
-
}
|
|
1013
|
-
finally {
|
|
1014
|
-
writeLock.unlock();
|
|
1015
|
-
}
|
|
1042
|
+
// Disabled - using terminal-native mode
|
|
1043
|
+
this.contentRow = 1;
|
|
1016
1044
|
}
|
|
1017
1045
|
/**
|
|
1018
1046
|
* Exit alternate screen buffer.
|
|
1019
|
-
*
|
|
1047
|
+
* DISABLED: Using terminal-native mode.
|
|
1020
1048
|
*/
|
|
1021
1049
|
exitAlternateScreen() {
|
|
1022
|
-
|
|
1023
|
-
try {
|
|
1024
|
-
this.write(ESC.EXIT_ALT_SCREEN);
|
|
1025
|
-
this.alternateScreenActive = false;
|
|
1026
|
-
}
|
|
1027
|
-
finally {
|
|
1028
|
-
writeLock.unlock();
|
|
1029
|
-
}
|
|
1050
|
+
// Disabled - using terminal-native mode
|
|
1030
1051
|
}
|
|
1031
1052
|
/**
|
|
1032
1053
|
* Check if alternate screen buffer is currently active.
|
|
1054
|
+
* Always returns false - using terminal-native mode.
|
|
1033
1055
|
*/
|
|
1034
1056
|
isAlternateScreenActive() {
|
|
1035
|
-
return
|
|
1057
|
+
return false;
|
|
1036
1058
|
}
|
|
1037
1059
|
/**
|
|
1038
1060
|
* Get a snapshot of the scrollback buffer (for display on exit).
|
|
@@ -1041,14 +1063,17 @@ export class TerminalInput extends EventEmitter {
|
|
|
1041
1063
|
return [...this.scrollbackBuffer];
|
|
1042
1064
|
}
|
|
1043
1065
|
/**
|
|
1044
|
-
* Clear the
|
|
1045
|
-
*
|
|
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).
|
|
1046
1069
|
*/
|
|
1047
1070
|
clearScreen() {
|
|
1048
1071
|
writeLock.lock('clearScreen');
|
|
1049
1072
|
try {
|
|
1073
|
+
// In native mode, scroll past existing content rather than clearing
|
|
1074
|
+
const { rows } = this.getSize();
|
|
1075
|
+
this.write('\n'.repeat(rows));
|
|
1050
1076
|
this.write(ESC.HOME);
|
|
1051
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1052
1077
|
this.contentRow = 1;
|
|
1053
1078
|
}
|
|
1054
1079
|
finally {
|
|
@@ -1148,8 +1173,8 @@ export class TerminalInput extends EventEmitter {
|
|
|
1148
1173
|
this.insertNewline();
|
|
1149
1174
|
break;
|
|
1150
1175
|
// === MODE TOGGLES ===
|
|
1151
|
-
case '
|
|
1152
|
-
// Alt+
|
|
1176
|
+
case 'v':
|
|
1177
|
+
// Alt+V: Toggle verification mode (auto-tests after edits)
|
|
1153
1178
|
this.emit('toggleVerify');
|
|
1154
1179
|
break;
|
|
1155
1180
|
case 'c':
|
|
@@ -1216,25 +1241,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
1216
1241
|
return Math.max(5, rows - chatBoxHeight - 2);
|
|
1217
1242
|
}
|
|
1218
1243
|
/**
|
|
1219
|
-
* Build scroll indicator for the divider line
|
|
1220
|
-
*
|
|
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.
|
|
1221
1247
|
*/
|
|
1222
1248
|
buildScrollIndicator() {
|
|
1223
|
-
|
|
1224
|
-
// In scrollback mode - show position
|
|
1225
|
-
if (this.isInScrollbackMode && this.scrollbackOffset > 0) {
|
|
1226
|
-
const { rows } = this.getSize();
|
|
1227
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1228
|
-
const viewportHeight = Math.max(1, rows - chatBoxHeight);
|
|
1229
|
-
const currentPos = Math.max(0, bufferSize - this.scrollbackOffset - viewportHeight);
|
|
1230
|
-
const pct = bufferSize > 0 ? Math.round((currentPos / bufferSize) * 100) : 0;
|
|
1231
|
-
return `↑${this.scrollbackOffset} · ${pct}% · PgUp/Dn`;
|
|
1232
|
-
}
|
|
1233
|
-
// Not in scrollback - show hint if there's history
|
|
1234
|
-
if (bufferSize > 20) {
|
|
1235
|
-
const sizeLabel = bufferSize >= 1000 ? `${Math.floor(bufferSize / 1000)}k` : `${bufferSize}`;
|
|
1236
|
-
return `↕${sizeLabel}L · PgUp`;
|
|
1237
|
-
}
|
|
1249
|
+
// Scrollback navigation disabled - no indicator needed
|
|
1238
1250
|
return null;
|
|
1239
1251
|
}
|
|
1240
1252
|
handleSpecialKey(_str, key) {
|
|
@@ -1296,10 +1308,11 @@ export class TerminalInput extends EventEmitter {
|
|
|
1296
1308
|
}
|
|
1297
1309
|
return true;
|
|
1298
1310
|
case 'pageup':
|
|
1299
|
-
|
|
1311
|
+
// Scrollback disabled in alternate screen mode
|
|
1312
|
+
// Users should use terminal's native scrollback if available
|
|
1300
1313
|
return true;
|
|
1301
1314
|
case 'pagedown':
|
|
1302
|
-
|
|
1315
|
+
// Scrollback disabled in alternate screen mode
|
|
1303
1316
|
return true;
|
|
1304
1317
|
case 'tab':
|
|
1305
1318
|
if (key.shift) {
|
|
@@ -1693,120 +1706,53 @@ export class TerminalInput extends EventEmitter {
|
|
|
1693
1706
|
}
|
|
1694
1707
|
/**
|
|
1695
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.
|
|
1696
1711
|
*/
|
|
1697
|
-
scrollUp(
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
// Calculate max scroll offset
|
|
1702
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1703
|
-
this.scrollbackOffset = Math.min(this.scrollbackOffset + lines, maxOffset);
|
|
1704
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1705
|
-
if (this.isInScrollbackMode) {
|
|
1706
|
-
this.renderScrollbackView();
|
|
1707
|
-
}
|
|
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
|
|
1708
1716
|
}
|
|
1709
1717
|
/**
|
|
1710
1718
|
* Scroll down by a number of lines (PageDown)
|
|
1719
|
+
* Note: Scrollback disabled - see scrollUp comment
|
|
1711
1720
|
*/
|
|
1712
|
-
scrollDown(
|
|
1713
|
-
|
|
1714
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1715
|
-
if (this.isInScrollbackMode) {
|
|
1716
|
-
this.renderScrollbackView();
|
|
1717
|
-
}
|
|
1718
|
-
else {
|
|
1719
|
-
// Returned to live mode - force re-render
|
|
1720
|
-
this.forceRender();
|
|
1721
|
-
}
|
|
1721
|
+
scrollDown(_lines = 10) {
|
|
1722
|
+
// Scrollback disabled
|
|
1722
1723
|
}
|
|
1723
1724
|
/**
|
|
1724
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.
|
|
1725
1728
|
*/
|
|
1726
1729
|
scrollToTop() {
|
|
1727
|
-
|
|
1728
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1729
|
-
const visibleLines = Math.max(1, rows - chatBoxHeight);
|
|
1730
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1731
|
-
this.scrollbackOffset = maxOffset;
|
|
1732
|
-
this.isInScrollbackMode = true;
|
|
1733
|
-
this.renderScrollbackView();
|
|
1730
|
+
// Disabled - causes display corruption in alternate screen buffer
|
|
1734
1731
|
}
|
|
1735
1732
|
/**
|
|
1736
1733
|
* Jump to the bottom (live mode)
|
|
1734
|
+
* DISABLED: Scrollback navigation causes display corruption.
|
|
1737
1735
|
*/
|
|
1738
1736
|
scrollToBottom() {
|
|
1737
|
+
// Reset scrollback state in case it was somehow enabled
|
|
1739
1738
|
this.scrollbackOffset = 0;
|
|
1740
1739
|
this.isInScrollbackMode = false;
|
|
1741
|
-
this.forceRender();
|
|
1742
1740
|
}
|
|
1743
1741
|
/**
|
|
1744
1742
|
* Toggle scrollback mode on/off (Alt+S hotkey)
|
|
1743
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1745
1744
|
*/
|
|
1746
1745
|
toggleScrollbackMode() {
|
|
1747
|
-
|
|
1748
|
-
this.scrollToBottom();
|
|
1749
|
-
}
|
|
1750
|
-
else if (this.scrollbackBuffer.length > 0) {
|
|
1751
|
-
this.scrollUp(20);
|
|
1752
|
-
}
|
|
1746
|
+
// Disabled - alternate screen buffer doesn't support manual scrollback rendering
|
|
1753
1747
|
}
|
|
1754
1748
|
/**
|
|
1755
|
-
* Render the scrollback buffer view
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1759
|
-
* - Keyboard navigation hints
|
|
1760
|
-
* - 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.
|
|
1761
1753
|
*/
|
|
1762
1754
|
renderScrollbackView() {
|
|
1763
|
-
|
|
1764
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1765
|
-
const contentHeight = Math.max(1, rows - chatBoxHeight);
|
|
1766
|
-
writeLock.lock('renderScrollback');
|
|
1767
|
-
try {
|
|
1768
|
-
this.write(ESC.SAVE);
|
|
1769
|
-
this.write(ESC.HIDE);
|
|
1770
|
-
// Clear content area
|
|
1771
|
-
for (let i = 1; i <= contentHeight; i++) {
|
|
1772
|
-
this.write(ESC.TO(i, 1));
|
|
1773
|
-
this.write(ESC.CLEAR_LINE);
|
|
1774
|
-
}
|
|
1775
|
-
// Calculate which lines to show
|
|
1776
|
-
const totalLines = this.scrollbackBuffer.length;
|
|
1777
|
-
const startIdx = Math.max(0, totalLines - this.scrollbackOffset - contentHeight);
|
|
1778
|
-
const endIdx = Math.max(0, totalLines - this.scrollbackOffset);
|
|
1779
|
-
const visibleLines = this.scrollbackBuffer.slice(startIdx, endIdx);
|
|
1780
|
-
// Build header bar with navigation hints
|
|
1781
|
-
const headerInfo = this.buildScrollbackHeader(cols, totalLines, startIdx, endIdx);
|
|
1782
|
-
this.write(ESC.TO(1, 1));
|
|
1783
|
-
this.write(headerInfo);
|
|
1784
|
-
// Render visible lines with line numbers and visual guides
|
|
1785
|
-
const lineNumWidth = String(totalLines).length + 1;
|
|
1786
|
-
const contentStart = 2; // Start after header
|
|
1787
|
-
for (let i = 0; i < Math.min(visibleLines.length, contentHeight - 1); i++) {
|
|
1788
|
-
const line = visibleLines[i] ?? '';
|
|
1789
|
-
const lineNum = startIdx + i + 1;
|
|
1790
|
-
this.write(ESC.TO(contentStart + i, 1));
|
|
1791
|
-
// Line number gutter
|
|
1792
|
-
const numStr = String(lineNum).padStart(lineNumWidth, ' ');
|
|
1793
|
-
this.write(theme.ui.muted(`${numStr} │ `));
|
|
1794
|
-
// Content with truncation
|
|
1795
|
-
const gutterWidth = lineNumWidth + 4;
|
|
1796
|
-
const maxLen = cols - gutterWidth - 2;
|
|
1797
|
-
const displayLine = line.length > maxLen ? line.slice(0, maxLen - 3) + '...' : line;
|
|
1798
|
-
this.write(displayLine);
|
|
1799
|
-
}
|
|
1800
|
-
// Add visual scroll track on the right edge
|
|
1801
|
-
this.renderScrollTrack(cols, contentHeight, totalLines, startIdx, endIdx);
|
|
1802
|
-
this.write(ESC.RESTORE);
|
|
1803
|
-
this.write(ESC.SHOW);
|
|
1804
|
-
}
|
|
1805
|
-
finally {
|
|
1806
|
-
writeLock.unlock();
|
|
1807
|
-
}
|
|
1808
|
-
// Re-render chat box
|
|
1809
|
-
this.forceRender();
|
|
1755
|
+
// Disabled - causes display corruption
|
|
1810
1756
|
}
|
|
1811
1757
|
/**
|
|
1812
1758
|
* Build scrollback header with navigation hints
|