erosolar-cli 1.7.363 → 1.7.364
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/interactiveShell.d.ts +2 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +32 -13
- 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 +187 -227
- 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/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 +163 -32
- 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,8 +173,8 @@ 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
180
|
// Check for mouse events (SGR mode: \x1b[<button;x;yM or m)
|
|
@@ -191,14 +190,6 @@ export class TerminalInput extends EventEmitter {
|
|
|
191
190
|
const remaining = data.slice(mouseEventEnd);
|
|
192
191
|
return { consumed: true, passthrough: remaining };
|
|
193
192
|
}
|
|
194
|
-
// Filter out arrow key escape sequences (they're handled by keypress events)
|
|
195
|
-
// Arrow keys: \x1b[A (up), \x1b[B (down), \x1b[C (right), \x1b[D (left)
|
|
196
|
-
const arrowMatch = data.match(/\x1b\[[ABCD]/);
|
|
197
|
-
if (arrowMatch) {
|
|
198
|
-
// Arrow keys should be handled by keypress handler, strip them from passthrough
|
|
199
|
-
const filtered = data.replace(/\x1b\[[ABCD]/g, '');
|
|
200
|
-
return { consumed: filtered.length !== data.length, passthrough: filtered };
|
|
201
|
-
}
|
|
202
193
|
// Check for paste start
|
|
203
194
|
if (data.includes(ESC.PASTE_START)) {
|
|
204
195
|
this.isPasting = true;
|
|
@@ -237,12 +228,30 @@ export class TerminalInput extends EventEmitter {
|
|
|
237
228
|
if (button === 64) {
|
|
238
229
|
// Scroll up (3 lines per wheel tick)
|
|
239
230
|
this.scrollUp(3);
|
|
231
|
+
return;
|
|
240
232
|
}
|
|
241
|
-
|
|
233
|
+
if (button === 65) {
|
|
242
234
|
// Scroll down (3 lines per wheel tick)
|
|
243
235
|
this.scrollDown(3);
|
|
236
|
+
return;
|
|
244
237
|
}
|
|
245
|
-
//
|
|
238
|
+
// Left button (0), middle button (1), right button (2)
|
|
239
|
+
// These are captured by SGR mouse tracking but we don't need to handle them
|
|
240
|
+
// Just consume silently - the escape sequence has already been processed
|
|
241
|
+
// The 'M' action is press, 'm' is release
|
|
242
|
+
if (button <= 2) {
|
|
243
|
+
// Silently consume click events to prevent artifacts
|
|
244
|
+
// Left clicks (button=0) in the input area could focus the input
|
|
245
|
+
// but terminal apps typically handle this natively
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Button with motion flag (button + 32) - drag events
|
|
249
|
+
if (button >= 32 && button <= 34) {
|
|
250
|
+
// Drag events - consume silently
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// Any other unrecognized button - log for debugging in dev mode
|
|
254
|
+
// but don't output anything to prevent artifacts
|
|
246
255
|
}
|
|
247
256
|
/**
|
|
248
257
|
* Handle a keypress event
|
|
@@ -448,14 +457,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
448
457
|
const nextAutoHotkey = options.autoContinueHotkey ?? this.autoContinueHotkey;
|
|
449
458
|
const nextThinkingHotkey = options.thinkingHotkey ?? this.thinkingHotkey;
|
|
450
459
|
const nextThinkingLabel = options.thinkingModeLabel === undefined ? this.thinkingModeLabel : (options.thinkingModeLabel || null);
|
|
451
|
-
const nextToolCount = options.toolCount === undefined ? this.toolCount : (options.toolCount ?? null);
|
|
452
460
|
if (this.verificationEnabled === nextVerification &&
|
|
453
461
|
this.autoContinueEnabled === nextAutoContinue &&
|
|
454
462
|
this.verificationHotkey === nextVerifyHotkey &&
|
|
455
463
|
this.autoContinueHotkey === nextAutoHotkey &&
|
|
456
464
|
this.thinkingHotkey === nextThinkingHotkey &&
|
|
457
|
-
this.thinkingModeLabel === nextThinkingLabel
|
|
458
|
-
this.toolCount === nextToolCount) {
|
|
465
|
+
this.thinkingModeLabel === nextThinkingLabel) {
|
|
459
466
|
return;
|
|
460
467
|
}
|
|
461
468
|
this.verificationEnabled = nextVerification;
|
|
@@ -464,7 +471,6 @@ export class TerminalInput extends EventEmitter {
|
|
|
464
471
|
this.autoContinueHotkey = nextAutoHotkey;
|
|
465
472
|
this.thinkingHotkey = nextThinkingHotkey;
|
|
466
473
|
this.thinkingModeLabel = nextThinkingLabel;
|
|
467
|
-
this.toolCount = nextToolCount;
|
|
468
474
|
this.scheduleRender();
|
|
469
475
|
}
|
|
470
476
|
/**
|
|
@@ -700,98 +706,143 @@ export class TerminalInput extends EventEmitter {
|
|
|
700
706
|
return [`${leftText}${' '.repeat(spacing)}${rightText}`];
|
|
701
707
|
}
|
|
702
708
|
/**
|
|
703
|
-
* Build mode controls line with all keyboard shortcuts
|
|
704
|
-
* Shows
|
|
709
|
+
* Build mode controls line with all keyboard shortcuts.
|
|
710
|
+
* Shows status, all toggles, and contextual information.
|
|
711
|
+
* Enhanced with comprehensive feature status display.
|
|
712
|
+
*
|
|
713
|
+
* Format: [Key] Label:status · [Key] Label:status · context% · model
|
|
705
714
|
*/
|
|
706
715
|
buildModeControls(cols) {
|
|
707
716
|
const width = Math.max(8, cols - 2);
|
|
708
|
-
const
|
|
709
|
-
|
|
717
|
+
const leftParts = [];
|
|
718
|
+
const rightParts = [];
|
|
719
|
+
// Streaming indicator with animated spinner
|
|
710
720
|
if (this.streamingLabel) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
//
|
|
721
|
+
leftParts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
|
|
722
|
+
}
|
|
723
|
+
// Override status (warnings, errors)
|
|
724
|
+
if (this.overrideStatusMessage) {
|
|
725
|
+
leftParts.push({ text: `⚠ ${this.overrideStatusMessage}`, tone: 'warn' });
|
|
726
|
+
}
|
|
727
|
+
// Main status message
|
|
728
|
+
if (this.statusMessage) {
|
|
729
|
+
leftParts.push({ text: this.statusMessage, tone: this.streamingLabel ? 'muted' : 'info' });
|
|
730
|
+
}
|
|
731
|
+
// === KEYBOARD SHORTCUTS (Compact format: Key·Status) ===
|
|
732
|
+
// Interrupt shortcut (during streaming)
|
|
733
|
+
if (this.mode === 'streaming' || this.scrollRegionActive) {
|
|
734
|
+
leftParts.push({ text: `Esc·Stop`, tone: 'warn' });
|
|
735
|
+
}
|
|
736
|
+
// Edit mode toggle (Shift+Tab) - Compact display
|
|
737
|
+
const editStatus = this.editMode === 'display-edits' ? '✓' : '?';
|
|
738
|
+
const editTone = this.editMode === 'display-edits' ? 'success' : 'muted';
|
|
739
|
+
leftParts.push({ text: `⇧Tab·${editStatus}edits`, tone: editTone });
|
|
740
|
+
// Verification toggle (Alt+V) - Compact display
|
|
741
|
+
const verifyStatus = this.verificationEnabled ? '✓' : '○';
|
|
742
|
+
leftParts.push({
|
|
743
|
+
text: `⌥V·${verifyStatus}verify`,
|
|
744
|
+
tone: this.verificationEnabled ? 'success' : 'muted'
|
|
745
|
+
});
|
|
746
|
+
// Auto-continue toggle (Alt+C) - Compact display
|
|
747
|
+
const autoStatus = this.autoContinueEnabled ? '↻' : '○';
|
|
748
|
+
leftParts.push({
|
|
749
|
+
text: `⌥C·${autoStatus}cont`,
|
|
750
|
+
tone: this.autoContinueEnabled ? 'info' : 'muted'
|
|
751
|
+
});
|
|
752
|
+
// Thinking mode toggle (if available)
|
|
731
753
|
if (this.thinkingModeLabel) {
|
|
732
|
-
const
|
|
733
|
-
this.thinkingModeLabel
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
754
|
+
const shortThinking = this.thinkingModeLabel.length > 8
|
|
755
|
+
? this.thinkingModeLabel.slice(0, 6) + '..'
|
|
756
|
+
: this.thinkingModeLabel;
|
|
757
|
+
rightParts.push({ text: `⌥T·${shortThinking}`, tone: 'info' });
|
|
758
|
+
}
|
|
759
|
+
// === CONTEXTUAL INFO ===
|
|
760
|
+
// Queued commands
|
|
761
|
+
if (this.queue.length > 0) {
|
|
762
|
+
const queueIcon = this.mode === 'streaming' ? '⏳' : '▸';
|
|
763
|
+
leftParts.push({ text: `${queueIcon}${this.queue.length} queued`, tone: 'info' });
|
|
764
|
+
}
|
|
765
|
+
// Multi-line indicator
|
|
766
|
+
if (this.buffer.includes('\n')) {
|
|
767
|
+
const lineCount = this.buffer.split('\n').length;
|
|
768
|
+
rightParts.push({ text: `${lineCount}L`, tone: 'muted' });
|
|
769
|
+
}
|
|
770
|
+
// Paste indicator
|
|
771
|
+
if (this.pastePlaceholders.length > 0) {
|
|
772
|
+
const latest = this.pastePlaceholders[this.pastePlaceholders.length - 1];
|
|
773
|
+
rightParts.push({
|
|
774
|
+
text: `📋${latest.lineCount}L`,
|
|
775
|
+
tone: 'info',
|
|
776
|
+
});
|
|
740
777
|
}
|
|
741
|
-
// === STATUS
|
|
742
|
-
// Context
|
|
778
|
+
// === STATUS INDICATORS (Right side) ===
|
|
779
|
+
// Context usage with visual bar indicator
|
|
743
780
|
const contextRemaining = this.computeContextRemaining();
|
|
744
781
|
if (contextRemaining !== null) {
|
|
745
|
-
const tone = contextRemaining <= 10 ? '
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
|
|
782
|
+
const tone = contextRemaining <= 10 ? 'error' : contextRemaining <= 30 ? 'warn' : contextRemaining <= 60 ? 'info' : 'success';
|
|
783
|
+
const bar = this.renderMiniContextBar(contextRemaining);
|
|
784
|
+
const usedPct = this.contextUsage !== null ? this.contextUsage : (100 - contextRemaining);
|
|
785
|
+
const label = contextRemaining === 0
|
|
786
|
+
? `⚠compact!`
|
|
787
|
+
: `${bar} ${usedPct}% used`;
|
|
788
|
+
rightParts.push({ text: label, tone });
|
|
789
|
+
}
|
|
790
|
+
else if (this.contextUsage !== null) {
|
|
791
|
+
// Fallback: just show raw context usage percentage
|
|
792
|
+
const tone = this.contextUsage >= 90 ? 'error' : this.contextUsage >= 70 ? 'warn' : 'muted';
|
|
793
|
+
rightParts.push({ text: `⊛${this.contextUsage}%`, tone });
|
|
794
|
+
}
|
|
795
|
+
// Model/provider quick reference (compact)
|
|
796
|
+
if (this.modelLabel && !this.streamingLabel) {
|
|
797
|
+
const shortModel = this.modelLabel.length > 15 ? this.modelLabel.slice(0, 13) + '..' : this.modelLabel;
|
|
798
|
+
rightParts.push({ text: `⚙${shortModel}`, tone: 'muted' });
|
|
799
|
+
}
|
|
800
|
+
// Render: left-aligned shortcuts, right-aligned context info
|
|
801
|
+
if (!rightParts.length || width < 60) {
|
|
802
|
+
const merged = rightParts.length ? [...leftParts, ...rightParts] : leftParts;
|
|
803
|
+
return renderStatusLine(merged, width);
|
|
804
|
+
}
|
|
805
|
+
const leftWidth = Math.max(12, Math.floor(width * 0.6));
|
|
806
|
+
const rightWidth = Math.max(14, width - leftWidth - 1);
|
|
807
|
+
const leftText = renderStatusLine(leftParts, leftWidth);
|
|
808
|
+
const rightText = renderStatusLine(rightParts, rightWidth);
|
|
809
|
+
const spacing = Math.max(1, width - this.visibleLength(leftText) - this.visibleLength(rightText));
|
|
810
|
+
return `${leftText}${' '.repeat(spacing)}${rightText}`;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Render a mini context usage bar (5 chars)
|
|
814
|
+
*/
|
|
815
|
+
renderMiniContextBar(percentRemaining) {
|
|
816
|
+
const bars = 5;
|
|
817
|
+
const filled = Math.round((percentRemaining / 100) * bars);
|
|
818
|
+
const empty = bars - filled;
|
|
819
|
+
return '█'.repeat(filled) + '░'.repeat(empty);
|
|
759
820
|
}
|
|
760
821
|
formatHotkey(hotkey) {
|
|
761
822
|
const normalized = hotkey.trim().toLowerCase();
|
|
762
823
|
if (!normalized)
|
|
763
824
|
return hotkey;
|
|
764
825
|
const parts = normalized.split('+').filter(Boolean);
|
|
765
|
-
// Use readable key names instead of symbols for better terminal compatibility
|
|
766
826
|
const map = {
|
|
767
|
-
shift: '
|
|
768
|
-
sh: '
|
|
769
|
-
alt: '
|
|
770
|
-
option: '
|
|
771
|
-
opt: '
|
|
772
|
-
ctrl: '
|
|
773
|
-
control: '
|
|
774
|
-
cmd: '
|
|
775
|
-
meta: '
|
|
776
|
-
esc: 'Esc',
|
|
777
|
-
escape: 'Esc',
|
|
778
|
-
tab: 'Tab',
|
|
779
|
-
return: 'Enter',
|
|
780
|
-
enter: 'Enter',
|
|
781
|
-
pageup: 'PgUp',
|
|
782
|
-
pagedown: 'PgDn',
|
|
783
|
-
home: 'Home',
|
|
784
|
-
end: 'End',
|
|
827
|
+
shift: '⇧',
|
|
828
|
+
sh: '⇧',
|
|
829
|
+
alt: '⌥',
|
|
830
|
+
option: '⌥',
|
|
831
|
+
opt: '⌥',
|
|
832
|
+
ctrl: '⌃',
|
|
833
|
+
control: '⌃',
|
|
834
|
+
cmd: '⌘',
|
|
835
|
+
meta: '⌘',
|
|
785
836
|
};
|
|
786
837
|
const formatted = parts
|
|
787
838
|
.map((part) => {
|
|
788
|
-
const
|
|
789
|
-
if (
|
|
790
|
-
return
|
|
791
|
-
return part.length === 1 ? part.toUpperCase() : part.
|
|
839
|
+
const symbol = map[part];
|
|
840
|
+
if (symbol)
|
|
841
|
+
return symbol;
|
|
842
|
+
return part.length === 1 ? part.toUpperCase() : part.toUpperCase();
|
|
792
843
|
})
|
|
793
|
-
.join('
|
|
794
|
-
return
|
|
844
|
+
.join('');
|
|
845
|
+
return formatted || hotkey;
|
|
795
846
|
}
|
|
796
847
|
computeContextRemaining() {
|
|
797
848
|
if (this.contextUsage === null) {
|
|
@@ -957,7 +1008,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
957
1008
|
* Calculate chat box height.
|
|
958
1009
|
*/
|
|
959
1010
|
getChatBoxHeight() {
|
|
960
|
-
return
|
|
1011
|
+
return 6; // Fixed: meta + divider + input + controls + buffer
|
|
961
1012
|
}
|
|
962
1013
|
/**
|
|
963
1014
|
* @deprecated Use streamContent() instead
|
|
@@ -993,41 +1044,26 @@ export class TerminalInput extends EventEmitter {
|
|
|
993
1044
|
}
|
|
994
1045
|
}
|
|
995
1046
|
/**
|
|
996
|
-
* Enter alternate screen buffer
|
|
997
|
-
*
|
|
1047
|
+
* Enter alternate screen buffer.
|
|
1048
|
+
* DISABLED: Using terminal-native mode for proper scrollback and text selection.
|
|
998
1049
|
*/
|
|
999
1050
|
enterAlternateScreen() {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
this.write(ESC.ENTER_ALT_SCREEN);
|
|
1003
|
-
this.write(ESC.HOME);
|
|
1004
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1005
|
-
this.contentRow = 1;
|
|
1006
|
-
this.alternateScreenActive = true;
|
|
1007
|
-
}
|
|
1008
|
-
finally {
|
|
1009
|
-
writeLock.unlock();
|
|
1010
|
-
}
|
|
1051
|
+
// Disabled - using terminal-native mode
|
|
1052
|
+
this.contentRow = 1;
|
|
1011
1053
|
}
|
|
1012
1054
|
/**
|
|
1013
1055
|
* Exit alternate screen buffer.
|
|
1014
|
-
*
|
|
1056
|
+
* DISABLED: Using terminal-native mode.
|
|
1015
1057
|
*/
|
|
1016
1058
|
exitAlternateScreen() {
|
|
1017
|
-
|
|
1018
|
-
try {
|
|
1019
|
-
this.write(ESC.EXIT_ALT_SCREEN);
|
|
1020
|
-
this.alternateScreenActive = false;
|
|
1021
|
-
}
|
|
1022
|
-
finally {
|
|
1023
|
-
writeLock.unlock();
|
|
1024
|
-
}
|
|
1059
|
+
// Disabled - using terminal-native mode
|
|
1025
1060
|
}
|
|
1026
1061
|
/**
|
|
1027
1062
|
* Check if alternate screen buffer is currently active.
|
|
1063
|
+
* Always returns false - using terminal-native mode.
|
|
1028
1064
|
*/
|
|
1029
1065
|
isAlternateScreenActive() {
|
|
1030
|
-
return
|
|
1066
|
+
return false;
|
|
1031
1067
|
}
|
|
1032
1068
|
/**
|
|
1033
1069
|
* Get a snapshot of the scrollback buffer (for display on exit).
|
|
@@ -1036,14 +1072,17 @@ export class TerminalInput extends EventEmitter {
|
|
|
1036
1072
|
return [...this.scrollbackBuffer];
|
|
1037
1073
|
}
|
|
1038
1074
|
/**
|
|
1039
|
-
* Clear the
|
|
1040
|
-
*
|
|
1075
|
+
* Clear the visible terminal area and reset content position.
|
|
1076
|
+
* In terminal-native mode, this just adds newlines to scroll past content
|
|
1077
|
+
* rather than clearing history (preserving scrollback).
|
|
1041
1078
|
*/
|
|
1042
1079
|
clearScreen() {
|
|
1043
1080
|
writeLock.lock('clearScreen');
|
|
1044
1081
|
try {
|
|
1082
|
+
// In native mode, scroll past existing content rather than clearing
|
|
1083
|
+
const { rows } = this.getSize();
|
|
1084
|
+
this.write('\n'.repeat(rows));
|
|
1045
1085
|
this.write(ESC.HOME);
|
|
1046
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1047
1086
|
this.contentRow = 1;
|
|
1048
1087
|
}
|
|
1049
1088
|
finally {
|
|
@@ -1143,8 +1182,8 @@ export class TerminalInput extends EventEmitter {
|
|
|
1143
1182
|
this.insertNewline();
|
|
1144
1183
|
break;
|
|
1145
1184
|
// === MODE TOGGLES ===
|
|
1146
|
-
case '
|
|
1147
|
-
// Alt+
|
|
1185
|
+
case 'v':
|
|
1186
|
+
// Alt+V: Toggle verification mode (auto-tests after edits)
|
|
1148
1187
|
this.emit('toggleVerify');
|
|
1149
1188
|
break;
|
|
1150
1189
|
case 'c':
|
|
@@ -1211,25 +1250,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
1211
1250
|
return Math.max(5, rows - chatBoxHeight - 2);
|
|
1212
1251
|
}
|
|
1213
1252
|
/**
|
|
1214
|
-
* Build scroll indicator for the divider line
|
|
1215
|
-
*
|
|
1253
|
+
* Build scroll indicator for the divider line.
|
|
1254
|
+
* Scrollback navigation is disabled in alternate screen mode.
|
|
1255
|
+
* This returns null - no scroll indicator is shown.
|
|
1216
1256
|
*/
|
|
1217
1257
|
buildScrollIndicator() {
|
|
1218
|
-
|
|
1219
|
-
// In scrollback mode - show position
|
|
1220
|
-
if (this.isInScrollbackMode && this.scrollbackOffset > 0) {
|
|
1221
|
-
const { rows } = this.getSize();
|
|
1222
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1223
|
-
const viewportHeight = Math.max(1, rows - chatBoxHeight);
|
|
1224
|
-
const currentPos = Math.max(0, bufferSize - this.scrollbackOffset - viewportHeight);
|
|
1225
|
-
const pct = bufferSize > 0 ? Math.round((currentPos / bufferSize) * 100) : 0;
|
|
1226
|
-
return `↑${this.scrollbackOffset} · ${pct}% · PgUp/Dn`;
|
|
1227
|
-
}
|
|
1228
|
-
// Not in scrollback - show hint if there's history
|
|
1229
|
-
if (bufferSize > 20) {
|
|
1230
|
-
const sizeLabel = bufferSize >= 1000 ? `${Math.floor(bufferSize / 1000)}k` : `${bufferSize}`;
|
|
1231
|
-
return `↕${sizeLabel}L · PgUp`;
|
|
1232
|
-
}
|
|
1258
|
+
// Scrollback navigation disabled - no indicator needed
|
|
1233
1259
|
return null;
|
|
1234
1260
|
}
|
|
1235
1261
|
handleSpecialKey(_str, key) {
|
|
@@ -1291,10 +1317,11 @@ export class TerminalInput extends EventEmitter {
|
|
|
1291
1317
|
}
|
|
1292
1318
|
return true;
|
|
1293
1319
|
case 'pageup':
|
|
1294
|
-
|
|
1320
|
+
// Scrollback disabled in alternate screen mode
|
|
1321
|
+
// Users should use terminal's native scrollback if available
|
|
1295
1322
|
return true;
|
|
1296
1323
|
case 'pagedown':
|
|
1297
|
-
|
|
1324
|
+
// Scrollback disabled in alternate screen mode
|
|
1298
1325
|
return true;
|
|
1299
1326
|
case 'tab':
|
|
1300
1327
|
if (key.shift) {
|
|
@@ -1688,120 +1715,53 @@ export class TerminalInput extends EventEmitter {
|
|
|
1688
1715
|
}
|
|
1689
1716
|
/**
|
|
1690
1717
|
* Scroll up by a number of lines (PageUp)
|
|
1718
|
+
* Note: Scrollback is disabled in alternate screen mode to avoid display corruption.
|
|
1719
|
+
* Users should use their terminal's native scrollback or copy/paste features.
|
|
1691
1720
|
*/
|
|
1692
|
-
scrollUp(
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
// Calculate max scroll offset
|
|
1697
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1698
|
-
this.scrollbackOffset = Math.min(this.scrollbackOffset + lines, maxOffset);
|
|
1699
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1700
|
-
if (this.isInScrollbackMode) {
|
|
1701
|
-
this.renderScrollbackView();
|
|
1702
|
-
}
|
|
1721
|
+
scrollUp(_lines = 10) {
|
|
1722
|
+
// Scrollback disabled - alternate screen buffer doesn't support it well
|
|
1723
|
+
// The scrollback buffer is still maintained for potential future use
|
|
1724
|
+
// Users can select and copy text normally since mouse tracking is off
|
|
1703
1725
|
}
|
|
1704
1726
|
/**
|
|
1705
1727
|
* Scroll down by a number of lines (PageDown)
|
|
1728
|
+
* Note: Scrollback disabled - see scrollUp comment
|
|
1706
1729
|
*/
|
|
1707
|
-
scrollDown(
|
|
1708
|
-
|
|
1709
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1710
|
-
if (this.isInScrollbackMode) {
|
|
1711
|
-
this.renderScrollbackView();
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
1714
|
-
// Returned to live mode - force re-render
|
|
1715
|
-
this.forceRender();
|
|
1716
|
-
}
|
|
1730
|
+
scrollDown(_lines = 10) {
|
|
1731
|
+
// Scrollback disabled
|
|
1717
1732
|
}
|
|
1718
1733
|
/**
|
|
1719
1734
|
* Jump to the top of scrollback buffer
|
|
1735
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1736
|
+
* The scrollback buffer is maintained but cannot be rendered properly.
|
|
1720
1737
|
*/
|
|
1721
1738
|
scrollToTop() {
|
|
1722
|
-
|
|
1723
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1724
|
-
const visibleLines = Math.max(1, rows - chatBoxHeight);
|
|
1725
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1726
|
-
this.scrollbackOffset = maxOffset;
|
|
1727
|
-
this.isInScrollbackMode = true;
|
|
1728
|
-
this.renderScrollbackView();
|
|
1739
|
+
// Disabled - causes display corruption in alternate screen buffer
|
|
1729
1740
|
}
|
|
1730
1741
|
/**
|
|
1731
1742
|
* Jump to the bottom (live mode)
|
|
1743
|
+
* DISABLED: Scrollback navigation causes display corruption.
|
|
1732
1744
|
*/
|
|
1733
1745
|
scrollToBottom() {
|
|
1746
|
+
// Reset scrollback state in case it was somehow enabled
|
|
1734
1747
|
this.scrollbackOffset = 0;
|
|
1735
1748
|
this.isInScrollbackMode = false;
|
|
1736
|
-
this.forceRender();
|
|
1737
1749
|
}
|
|
1738
1750
|
/**
|
|
1739
1751
|
* Toggle scrollback mode on/off (Alt+S hotkey)
|
|
1752
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1740
1753
|
*/
|
|
1741
1754
|
toggleScrollbackMode() {
|
|
1742
|
-
|
|
1743
|
-
this.scrollToBottom();
|
|
1744
|
-
}
|
|
1745
|
-
else if (this.scrollbackBuffer.length > 0) {
|
|
1746
|
-
this.scrollUp(20);
|
|
1747
|
-
}
|
|
1755
|
+
// Disabled - alternate screen buffer doesn't support manual scrollback rendering
|
|
1748
1756
|
}
|
|
1749
1757
|
/**
|
|
1750
|
-
* Render the scrollback buffer view
|
|
1751
|
-
*
|
|
1752
|
-
*
|
|
1753
|
-
*
|
|
1754
|
-
* - Keyboard navigation hints
|
|
1755
|
-
* - Animated indicators
|
|
1758
|
+
* Render the scrollback buffer view.
|
|
1759
|
+
* DISABLED: This causes display corruption in alternate screen mode.
|
|
1760
|
+
* The alternate screen buffer has its own rendering model that conflicts with
|
|
1761
|
+
* manual scroll region manipulation.
|
|
1756
1762
|
*/
|
|
1757
1763
|
renderScrollbackView() {
|
|
1758
|
-
|
|
1759
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1760
|
-
const contentHeight = Math.max(1, rows - chatBoxHeight);
|
|
1761
|
-
writeLock.lock('renderScrollback');
|
|
1762
|
-
try {
|
|
1763
|
-
this.write(ESC.SAVE);
|
|
1764
|
-
this.write(ESC.HIDE);
|
|
1765
|
-
// Clear content area
|
|
1766
|
-
for (let i = 1; i <= contentHeight; i++) {
|
|
1767
|
-
this.write(ESC.TO(i, 1));
|
|
1768
|
-
this.write(ESC.CLEAR_LINE);
|
|
1769
|
-
}
|
|
1770
|
-
// Calculate which lines to show
|
|
1771
|
-
const totalLines = this.scrollbackBuffer.length;
|
|
1772
|
-
const startIdx = Math.max(0, totalLines - this.scrollbackOffset - contentHeight);
|
|
1773
|
-
const endIdx = Math.max(0, totalLines - this.scrollbackOffset);
|
|
1774
|
-
const visibleLines = this.scrollbackBuffer.slice(startIdx, endIdx);
|
|
1775
|
-
// Build header bar with navigation hints
|
|
1776
|
-
const headerInfo = this.buildScrollbackHeader(cols, totalLines, startIdx, endIdx);
|
|
1777
|
-
this.write(ESC.TO(1, 1));
|
|
1778
|
-
this.write(headerInfo);
|
|
1779
|
-
// Render visible lines with line numbers and visual guides
|
|
1780
|
-
const lineNumWidth = String(totalLines).length + 1;
|
|
1781
|
-
const contentStart = 2; // Start after header
|
|
1782
|
-
for (let i = 0; i < Math.min(visibleLines.length, contentHeight - 1); i++) {
|
|
1783
|
-
const line = visibleLines[i] ?? '';
|
|
1784
|
-
const lineNum = startIdx + i + 1;
|
|
1785
|
-
this.write(ESC.TO(contentStart + i, 1));
|
|
1786
|
-
// Line number gutter
|
|
1787
|
-
const numStr = String(lineNum).padStart(lineNumWidth, ' ');
|
|
1788
|
-
this.write(theme.ui.muted(`${numStr} │ `));
|
|
1789
|
-
// Content with truncation
|
|
1790
|
-
const gutterWidth = lineNumWidth + 4;
|
|
1791
|
-
const maxLen = cols - gutterWidth - 2;
|
|
1792
|
-
const displayLine = line.length > maxLen ? line.slice(0, maxLen - 3) + '...' : line;
|
|
1793
|
-
this.write(displayLine);
|
|
1794
|
-
}
|
|
1795
|
-
// Add visual scroll track on the right edge
|
|
1796
|
-
this.renderScrollTrack(cols, contentHeight, totalLines, startIdx, endIdx);
|
|
1797
|
-
this.write(ESC.RESTORE);
|
|
1798
|
-
this.write(ESC.SHOW);
|
|
1799
|
-
}
|
|
1800
|
-
finally {
|
|
1801
|
-
writeLock.unlock();
|
|
1802
|
-
}
|
|
1803
|
-
// Re-render chat box
|
|
1804
|
-
this.forceRender();
|
|
1764
|
+
// Disabled - causes display corruption
|
|
1805
1765
|
}
|
|
1806
1766
|
/**
|
|
1807
1767
|
* Build scrollback header with navigation hints
|