erosolar-cli 1.7.362 → 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 -17
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +185 -219
- 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 -0
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +147 -1
- 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 +29 -44
- package/dist/ui/unified/layout.js.map +1 -1
- package/package.json +1 -1
|
@@ -117,7 +117,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
117
117
|
editMode = 'display-edits';
|
|
118
118
|
verificationEnabled = true;
|
|
119
119
|
autoContinueEnabled = false;
|
|
120
|
-
verificationHotkey = 'alt+
|
|
120
|
+
verificationHotkey = 'alt+v';
|
|
121
121
|
autoContinueHotkey = 'alt+c';
|
|
122
122
|
thinkingHotkey = '/thinking';
|
|
123
123
|
modelLabel = null;
|
|
@@ -173,8 +173,8 @@ export class TerminalInput extends EventEmitter {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
|
-
* Process raw terminal data (handles bracketed paste sequences
|
|
177
|
-
* 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)
|
|
178
178
|
*/
|
|
179
179
|
processRawData(data) {
|
|
180
180
|
// Check for mouse events (SGR mode: \x1b[<button;x;yM or m)
|
|
@@ -190,14 +190,6 @@ export class TerminalInput extends EventEmitter {
|
|
|
190
190
|
const remaining = data.slice(mouseEventEnd);
|
|
191
191
|
return { consumed: true, passthrough: remaining };
|
|
192
192
|
}
|
|
193
|
-
// Filter out arrow key escape sequences (they're handled by keypress events)
|
|
194
|
-
// Arrow keys: \x1b[A (up), \x1b[B (down), \x1b[C (right), \x1b[D (left)
|
|
195
|
-
const arrowMatch = data.match(/\x1b\[[ABCD]/);
|
|
196
|
-
if (arrowMatch) {
|
|
197
|
-
// Arrow keys should be handled by keypress handler, strip them from passthrough
|
|
198
|
-
const filtered = data.replace(/\x1b\[[ABCD]/g, '');
|
|
199
|
-
return { consumed: filtered.length !== data.length, passthrough: filtered };
|
|
200
|
-
}
|
|
201
193
|
// Check for paste start
|
|
202
194
|
if (data.includes(ESC.PASTE_START)) {
|
|
203
195
|
this.isPasting = true;
|
|
@@ -236,12 +228,30 @@ export class TerminalInput extends EventEmitter {
|
|
|
236
228
|
if (button === 64) {
|
|
237
229
|
// Scroll up (3 lines per wheel tick)
|
|
238
230
|
this.scrollUp(3);
|
|
231
|
+
return;
|
|
239
232
|
}
|
|
240
|
-
|
|
233
|
+
if (button === 65) {
|
|
241
234
|
// Scroll down (3 lines per wheel tick)
|
|
242
235
|
this.scrollDown(3);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
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;
|
|
243
247
|
}
|
|
244
|
-
//
|
|
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
|
|
245
255
|
}
|
|
246
256
|
/**
|
|
247
257
|
* Handle a keypress event
|
|
@@ -696,96 +706,143 @@ export class TerminalInput extends EventEmitter {
|
|
|
696
706
|
return [`${leftText}${' '.repeat(spacing)}${rightText}`];
|
|
697
707
|
}
|
|
698
708
|
/**
|
|
699
|
-
* Build mode controls line with all keyboard shortcuts
|
|
700
|
-
* 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
|
|
701
714
|
*/
|
|
702
715
|
buildModeControls(cols) {
|
|
703
716
|
const width = Math.max(8, cols - 2);
|
|
704
|
-
const
|
|
705
|
-
|
|
717
|
+
const leftParts = [];
|
|
718
|
+
const rightParts = [];
|
|
719
|
+
// Streaming indicator with animated spinner
|
|
706
720
|
if (this.streamingLabel) {
|
|
707
|
-
|
|
708
|
-
parts.push({ text: `Esc:stop`, tone: 'warn' });
|
|
721
|
+
leftParts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
|
|
709
722
|
}
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
|
|
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' ? '✓' : '?';
|
|
713
738
|
const editTone = this.editMode === 'display-edits' ? 'success' : 'muted';
|
|
714
|
-
|
|
715
|
-
//
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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)
|
|
724
753
|
if (this.thinkingModeLabel) {
|
|
725
|
-
const
|
|
726
|
-
this.thinkingModeLabel
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
//
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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
|
+
});
|
|
777
|
+
}
|
|
778
|
+
// === STATUS INDICATORS (Right side) ===
|
|
779
|
+
// Context usage with visual bar indicator
|
|
736
780
|
const contextRemaining = this.computeContextRemaining();
|
|
737
781
|
if (contextRemaining !== null) {
|
|
738
|
-
const tone = contextRemaining <= 10 ? '
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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);
|
|
753
820
|
}
|
|
754
821
|
formatHotkey(hotkey) {
|
|
755
822
|
const normalized = hotkey.trim().toLowerCase();
|
|
756
823
|
if (!normalized)
|
|
757
824
|
return hotkey;
|
|
758
825
|
const parts = normalized.split('+').filter(Boolean);
|
|
759
|
-
// Use readable key names instead of symbols for better terminal compatibility
|
|
760
826
|
const map = {
|
|
761
|
-
shift: '
|
|
762
|
-
sh: '
|
|
763
|
-
alt: '
|
|
764
|
-
option: '
|
|
765
|
-
opt: '
|
|
766
|
-
ctrl: '
|
|
767
|
-
control: '
|
|
768
|
-
cmd: '
|
|
769
|
-
meta: '
|
|
770
|
-
esc: 'Esc',
|
|
771
|
-
escape: 'Esc',
|
|
772
|
-
tab: 'Tab',
|
|
773
|
-
return: 'Enter',
|
|
774
|
-
enter: 'Enter',
|
|
775
|
-
pageup: 'PgUp',
|
|
776
|
-
pagedown: 'PgDn',
|
|
777
|
-
home: 'Home',
|
|
778
|
-
end: 'End',
|
|
827
|
+
shift: '⇧',
|
|
828
|
+
sh: '⇧',
|
|
829
|
+
alt: '⌥',
|
|
830
|
+
option: '⌥',
|
|
831
|
+
opt: '⌥',
|
|
832
|
+
ctrl: '⌃',
|
|
833
|
+
control: '⌃',
|
|
834
|
+
cmd: '⌘',
|
|
835
|
+
meta: '⌘',
|
|
779
836
|
};
|
|
780
837
|
const formatted = parts
|
|
781
838
|
.map((part) => {
|
|
782
|
-
const
|
|
783
|
-
if (
|
|
784
|
-
return
|
|
785
|
-
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();
|
|
786
843
|
})
|
|
787
|
-
.join('
|
|
788
|
-
return
|
|
844
|
+
.join('');
|
|
845
|
+
return formatted || hotkey;
|
|
789
846
|
}
|
|
790
847
|
computeContextRemaining() {
|
|
791
848
|
if (this.contextUsage === null) {
|
|
@@ -951,7 +1008,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
951
1008
|
* Calculate chat box height.
|
|
952
1009
|
*/
|
|
953
1010
|
getChatBoxHeight() {
|
|
954
|
-
return
|
|
1011
|
+
return 6; // Fixed: meta + divider + input + controls + buffer
|
|
955
1012
|
}
|
|
956
1013
|
/**
|
|
957
1014
|
* @deprecated Use streamContent() instead
|
|
@@ -987,41 +1044,26 @@ export class TerminalInput extends EventEmitter {
|
|
|
987
1044
|
}
|
|
988
1045
|
}
|
|
989
1046
|
/**
|
|
990
|
-
* Enter alternate screen buffer
|
|
991
|
-
*
|
|
1047
|
+
* Enter alternate screen buffer.
|
|
1048
|
+
* DISABLED: Using terminal-native mode for proper scrollback and text selection.
|
|
992
1049
|
*/
|
|
993
1050
|
enterAlternateScreen() {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
this.write(ESC.ENTER_ALT_SCREEN);
|
|
997
|
-
this.write(ESC.HOME);
|
|
998
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
999
|
-
this.contentRow = 1;
|
|
1000
|
-
this.alternateScreenActive = true;
|
|
1001
|
-
}
|
|
1002
|
-
finally {
|
|
1003
|
-
writeLock.unlock();
|
|
1004
|
-
}
|
|
1051
|
+
// Disabled - using terminal-native mode
|
|
1052
|
+
this.contentRow = 1;
|
|
1005
1053
|
}
|
|
1006
1054
|
/**
|
|
1007
1055
|
* Exit alternate screen buffer.
|
|
1008
|
-
*
|
|
1056
|
+
* DISABLED: Using terminal-native mode.
|
|
1009
1057
|
*/
|
|
1010
1058
|
exitAlternateScreen() {
|
|
1011
|
-
|
|
1012
|
-
try {
|
|
1013
|
-
this.write(ESC.EXIT_ALT_SCREEN);
|
|
1014
|
-
this.alternateScreenActive = false;
|
|
1015
|
-
}
|
|
1016
|
-
finally {
|
|
1017
|
-
writeLock.unlock();
|
|
1018
|
-
}
|
|
1059
|
+
// Disabled - using terminal-native mode
|
|
1019
1060
|
}
|
|
1020
1061
|
/**
|
|
1021
1062
|
* Check if alternate screen buffer is currently active.
|
|
1063
|
+
* Always returns false - using terminal-native mode.
|
|
1022
1064
|
*/
|
|
1023
1065
|
isAlternateScreenActive() {
|
|
1024
|
-
return
|
|
1066
|
+
return false;
|
|
1025
1067
|
}
|
|
1026
1068
|
/**
|
|
1027
1069
|
* Get a snapshot of the scrollback buffer (for display on exit).
|
|
@@ -1030,14 +1072,17 @@ export class TerminalInput extends EventEmitter {
|
|
|
1030
1072
|
return [...this.scrollbackBuffer];
|
|
1031
1073
|
}
|
|
1032
1074
|
/**
|
|
1033
|
-
* Clear the
|
|
1034
|
-
*
|
|
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).
|
|
1035
1078
|
*/
|
|
1036
1079
|
clearScreen() {
|
|
1037
1080
|
writeLock.lock('clearScreen');
|
|
1038
1081
|
try {
|
|
1082
|
+
// In native mode, scroll past existing content rather than clearing
|
|
1083
|
+
const { rows } = this.getSize();
|
|
1084
|
+
this.write('\n'.repeat(rows));
|
|
1039
1085
|
this.write(ESC.HOME);
|
|
1040
|
-
this.write(ESC.CLEAR_SCREEN);
|
|
1041
1086
|
this.contentRow = 1;
|
|
1042
1087
|
}
|
|
1043
1088
|
finally {
|
|
@@ -1137,8 +1182,8 @@ export class TerminalInput extends EventEmitter {
|
|
|
1137
1182
|
this.insertNewline();
|
|
1138
1183
|
break;
|
|
1139
1184
|
// === MODE TOGGLES ===
|
|
1140
|
-
case '
|
|
1141
|
-
// Alt+
|
|
1185
|
+
case 'v':
|
|
1186
|
+
// Alt+V: Toggle verification mode (auto-tests after edits)
|
|
1142
1187
|
this.emit('toggleVerify');
|
|
1143
1188
|
break;
|
|
1144
1189
|
case 'c':
|
|
@@ -1205,25 +1250,12 @@ export class TerminalInput extends EventEmitter {
|
|
|
1205
1250
|
return Math.max(5, rows - chatBoxHeight - 2);
|
|
1206
1251
|
}
|
|
1207
1252
|
/**
|
|
1208
|
-
* Build scroll indicator for the divider line
|
|
1209
|
-
*
|
|
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.
|
|
1210
1256
|
*/
|
|
1211
1257
|
buildScrollIndicator() {
|
|
1212
|
-
|
|
1213
|
-
// In scrollback mode - show position
|
|
1214
|
-
if (this.isInScrollbackMode && this.scrollbackOffset > 0) {
|
|
1215
|
-
const { rows } = this.getSize();
|
|
1216
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1217
|
-
const viewportHeight = Math.max(1, rows - chatBoxHeight);
|
|
1218
|
-
const currentPos = Math.max(0, bufferSize - this.scrollbackOffset - viewportHeight);
|
|
1219
|
-
const pct = bufferSize > 0 ? Math.round((currentPos / bufferSize) * 100) : 0;
|
|
1220
|
-
return `↑${this.scrollbackOffset} · ${pct}% · PgUp/Dn`;
|
|
1221
|
-
}
|
|
1222
|
-
// Not in scrollback - show hint if there's history
|
|
1223
|
-
if (bufferSize > 20) {
|
|
1224
|
-
const sizeLabel = bufferSize >= 1000 ? `${Math.floor(bufferSize / 1000)}k` : `${bufferSize}`;
|
|
1225
|
-
return `↕${sizeLabel}L · PgUp`;
|
|
1226
|
-
}
|
|
1258
|
+
// Scrollback navigation disabled - no indicator needed
|
|
1227
1259
|
return null;
|
|
1228
1260
|
}
|
|
1229
1261
|
handleSpecialKey(_str, key) {
|
|
@@ -1285,10 +1317,11 @@ export class TerminalInput extends EventEmitter {
|
|
|
1285
1317
|
}
|
|
1286
1318
|
return true;
|
|
1287
1319
|
case 'pageup':
|
|
1288
|
-
|
|
1320
|
+
// Scrollback disabled in alternate screen mode
|
|
1321
|
+
// Users should use terminal's native scrollback if available
|
|
1289
1322
|
return true;
|
|
1290
1323
|
case 'pagedown':
|
|
1291
|
-
|
|
1324
|
+
// Scrollback disabled in alternate screen mode
|
|
1292
1325
|
return true;
|
|
1293
1326
|
case 'tab':
|
|
1294
1327
|
if (key.shift) {
|
|
@@ -1682,120 +1715,53 @@ export class TerminalInput extends EventEmitter {
|
|
|
1682
1715
|
}
|
|
1683
1716
|
/**
|
|
1684
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.
|
|
1685
1720
|
*/
|
|
1686
|
-
scrollUp(
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
// Calculate max scroll offset
|
|
1691
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1692
|
-
this.scrollbackOffset = Math.min(this.scrollbackOffset + lines, maxOffset);
|
|
1693
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1694
|
-
if (this.isInScrollbackMode) {
|
|
1695
|
-
this.renderScrollbackView();
|
|
1696
|
-
}
|
|
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
|
|
1697
1725
|
}
|
|
1698
1726
|
/**
|
|
1699
1727
|
* Scroll down by a number of lines (PageDown)
|
|
1728
|
+
* Note: Scrollback disabled - see scrollUp comment
|
|
1700
1729
|
*/
|
|
1701
|
-
scrollDown(
|
|
1702
|
-
|
|
1703
|
-
this.isInScrollbackMode = this.scrollbackOffset > 0;
|
|
1704
|
-
if (this.isInScrollbackMode) {
|
|
1705
|
-
this.renderScrollbackView();
|
|
1706
|
-
}
|
|
1707
|
-
else {
|
|
1708
|
-
// Returned to live mode - force re-render
|
|
1709
|
-
this.forceRender();
|
|
1710
|
-
}
|
|
1730
|
+
scrollDown(_lines = 10) {
|
|
1731
|
+
// Scrollback disabled
|
|
1711
1732
|
}
|
|
1712
1733
|
/**
|
|
1713
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.
|
|
1714
1737
|
*/
|
|
1715
1738
|
scrollToTop() {
|
|
1716
|
-
|
|
1717
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1718
|
-
const visibleLines = Math.max(1, rows - chatBoxHeight);
|
|
1719
|
-
const maxOffset = Math.max(0, this.scrollbackBuffer.length - visibleLines);
|
|
1720
|
-
this.scrollbackOffset = maxOffset;
|
|
1721
|
-
this.isInScrollbackMode = true;
|
|
1722
|
-
this.renderScrollbackView();
|
|
1739
|
+
// Disabled - causes display corruption in alternate screen buffer
|
|
1723
1740
|
}
|
|
1724
1741
|
/**
|
|
1725
1742
|
* Jump to the bottom (live mode)
|
|
1743
|
+
* DISABLED: Scrollback navigation causes display corruption.
|
|
1726
1744
|
*/
|
|
1727
1745
|
scrollToBottom() {
|
|
1746
|
+
// Reset scrollback state in case it was somehow enabled
|
|
1728
1747
|
this.scrollbackOffset = 0;
|
|
1729
1748
|
this.isInScrollbackMode = false;
|
|
1730
|
-
this.forceRender();
|
|
1731
1749
|
}
|
|
1732
1750
|
/**
|
|
1733
1751
|
* Toggle scrollback mode on/off (Alt+S hotkey)
|
|
1752
|
+
* DISABLED: Scrollback navigation causes display corruption in alternate screen mode.
|
|
1734
1753
|
*/
|
|
1735
1754
|
toggleScrollbackMode() {
|
|
1736
|
-
|
|
1737
|
-
this.scrollToBottom();
|
|
1738
|
-
}
|
|
1739
|
-
else if (this.scrollbackBuffer.length > 0) {
|
|
1740
|
-
this.scrollUp(20);
|
|
1741
|
-
}
|
|
1755
|
+
// Disabled - alternate screen buffer doesn't support manual scrollback rendering
|
|
1742
1756
|
}
|
|
1743
1757
|
/**
|
|
1744
|
-
* Render the scrollback buffer view
|
|
1745
|
-
*
|
|
1746
|
-
*
|
|
1747
|
-
*
|
|
1748
|
-
* - Keyboard navigation hints
|
|
1749
|
-
* - 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.
|
|
1750
1762
|
*/
|
|
1751
1763
|
renderScrollbackView() {
|
|
1752
|
-
|
|
1753
|
-
const chatBoxHeight = this.getChatBoxHeight();
|
|
1754
|
-
const contentHeight = Math.max(1, rows - chatBoxHeight);
|
|
1755
|
-
writeLock.lock('renderScrollback');
|
|
1756
|
-
try {
|
|
1757
|
-
this.write(ESC.SAVE);
|
|
1758
|
-
this.write(ESC.HIDE);
|
|
1759
|
-
// Clear content area
|
|
1760
|
-
for (let i = 1; i <= contentHeight; i++) {
|
|
1761
|
-
this.write(ESC.TO(i, 1));
|
|
1762
|
-
this.write(ESC.CLEAR_LINE);
|
|
1763
|
-
}
|
|
1764
|
-
// Calculate which lines to show
|
|
1765
|
-
const totalLines = this.scrollbackBuffer.length;
|
|
1766
|
-
const startIdx = Math.max(0, totalLines - this.scrollbackOffset - contentHeight);
|
|
1767
|
-
const endIdx = Math.max(0, totalLines - this.scrollbackOffset);
|
|
1768
|
-
const visibleLines = this.scrollbackBuffer.slice(startIdx, endIdx);
|
|
1769
|
-
// Build header bar with navigation hints
|
|
1770
|
-
const headerInfo = this.buildScrollbackHeader(cols, totalLines, startIdx, endIdx);
|
|
1771
|
-
this.write(ESC.TO(1, 1));
|
|
1772
|
-
this.write(headerInfo);
|
|
1773
|
-
// Render visible lines with line numbers and visual guides
|
|
1774
|
-
const lineNumWidth = String(totalLines).length + 1;
|
|
1775
|
-
const contentStart = 2; // Start after header
|
|
1776
|
-
for (let i = 0; i < Math.min(visibleLines.length, contentHeight - 1); i++) {
|
|
1777
|
-
const line = visibleLines[i] ?? '';
|
|
1778
|
-
const lineNum = startIdx + i + 1;
|
|
1779
|
-
this.write(ESC.TO(contentStart + i, 1));
|
|
1780
|
-
// Line number gutter
|
|
1781
|
-
const numStr = String(lineNum).padStart(lineNumWidth, ' ');
|
|
1782
|
-
this.write(theme.ui.muted(`${numStr} │ `));
|
|
1783
|
-
// Content with truncation
|
|
1784
|
-
const gutterWidth = lineNumWidth + 4;
|
|
1785
|
-
const maxLen = cols - gutterWidth - 2;
|
|
1786
|
-
const displayLine = line.length > maxLen ? line.slice(0, maxLen - 3) + '...' : line;
|
|
1787
|
-
this.write(displayLine);
|
|
1788
|
-
}
|
|
1789
|
-
// Add visual scroll track on the right edge
|
|
1790
|
-
this.renderScrollTrack(cols, contentHeight, totalLines, startIdx, endIdx);
|
|
1791
|
-
this.write(ESC.RESTORE);
|
|
1792
|
-
this.write(ESC.SHOW);
|
|
1793
|
-
}
|
|
1794
|
-
finally {
|
|
1795
|
-
writeLock.unlock();
|
|
1796
|
-
}
|
|
1797
|
-
// Re-render chat box
|
|
1798
|
-
this.forceRender();
|
|
1764
|
+
// Disabled - causes display corruption
|
|
1799
1765
|
}
|
|
1800
1766
|
/**
|
|
1801
1767
|
* Build scrollback header with navigation hints
|