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.
Files changed (44) hide show
  1. package/dist/shell/interactiveShell.d.ts +2 -0
  2. package/dist/shell/interactiveShell.d.ts.map +1 -1
  3. package/dist/shell/interactiveShell.js +32 -13
  4. package/dist/shell/interactiveShell.js.map +1 -1
  5. package/dist/shell/terminalInput.d.ts +32 -19
  6. package/dist/shell/terminalInput.d.ts.map +1 -1
  7. package/dist/shell/terminalInput.js +187 -227
  8. package/dist/shell/terminalInput.js.map +1 -1
  9. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  10. package/dist/shell/terminalInputAdapter.js +5 -2
  11. package/dist/shell/terminalInputAdapter.js.map +1 -1
  12. package/dist/subagents/taskRunner.d.ts.map +1 -1
  13. package/dist/subagents/taskRunner.js +7 -25
  14. package/dist/subagents/taskRunner.js.map +1 -1
  15. package/dist/tools/learnTools.js +127 -4
  16. package/dist/tools/learnTools.js.map +1 -1
  17. package/dist/ui/ShellUIAdapter.d.ts +27 -0
  18. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  19. package/dist/ui/ShellUIAdapter.js +175 -9
  20. package/dist/ui/ShellUIAdapter.js.map +1 -1
  21. package/dist/ui/compactRenderer.d.ts +139 -0
  22. package/dist/ui/compactRenderer.d.ts.map +1 -0
  23. package/dist/ui/compactRenderer.js +398 -0
  24. package/dist/ui/compactRenderer.js.map +1 -0
  25. package/dist/ui/inPlaceUpdater.d.ts +181 -0
  26. package/dist/ui/inPlaceUpdater.d.ts.map +1 -0
  27. package/dist/ui/inPlaceUpdater.js +515 -0
  28. package/dist/ui/inPlaceUpdater.js.map +1 -0
  29. package/dist/ui/theme.d.ts +108 -3
  30. package/dist/ui/theme.d.ts.map +1 -1
  31. package/dist/ui/theme.js +124 -3
  32. package/dist/ui/theme.js.map +1 -1
  33. package/dist/ui/toolDisplay.d.ts +44 -7
  34. package/dist/ui/toolDisplay.d.ts.map +1 -1
  35. package/dist/ui/toolDisplay.js +163 -32
  36. package/dist/ui/toolDisplay.js.map +1 -1
  37. package/dist/ui/unified/index.d.ts +11 -0
  38. package/dist/ui/unified/index.d.ts.map +1 -1
  39. package/dist/ui/unified/index.js +16 -0
  40. package/dist/ui/unified/index.js.map +1 -1
  41. package/dist/ui/unified/layout.d.ts.map +1 -1
  42. package/dist/ui/unified/layout.js +32 -47
  43. package/dist/ui/unified/layout.js.map +1 -1
  44. 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+d';
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, mouse events, and escape sequences)
178
- * Returns true if the data was consumed (paste sequence, mouse event, etc.)
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
- else if (button === 65) {
233
+ if (button === 65) {
242
234
  // Scroll down (3 lines per wheel tick)
243
235
  this.scrollDown(3);
236
+ return;
244
237
  }
245
- // Ignore other mouse events (clicks, drags, etc.) for now
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 and toggle states.
704
- * Shows below the chat box input area with spelled-out key names.
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 parts = [];
709
- // Streaming status with stop hint
717
+ const leftParts = [];
718
+ const rightParts = [];
719
+ // Streaming indicator with animated spinner
710
720
  if (this.streamingLabel) {
711
- parts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
712
- parts.push({ text: `Esc:stop`, tone: 'warn' });
713
- }
714
- // === COMPACT STATUS ICONS (like banner style) ===
715
- // Format: ✓verify · ○auto · ◐bal · ⚒43
716
- // Verify status - compact icon format
717
- if (this.verificationEnabled) {
718
- parts.push({ text: '✓verify', tone: 'success' });
719
- }
720
- else {
721
- parts.push({ text: '○verify', tone: 'muted' });
722
- }
723
- // Auto-continue status
724
- if (this.autoContinueEnabled) {
725
- parts.push({ text: '✓auto', tone: 'success' });
726
- }
727
- else {
728
- parts.push({ text: '○auto', tone: 'muted' });
729
- }
730
- // Thinking mode (show as ◐min/bal/ext)
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 thinkingShort = this.thinkingModeLabel === 'minimal' ? 'min' :
733
- this.thinkingModeLabel === 'balanced' ? 'bal' :
734
- this.thinkingModeLabel === 'extended' ? 'ext' : this.thinkingModeLabel;
735
- parts.push({ text: `◐${thinkingShort}`, tone: 'info' });
736
- }
737
- // Tool count (⚒43) - from the toolCount if available
738
- if (this.toolCount !== null && this.toolCount > 0) {
739
- parts.push({ text: `⚒${this.toolCount}`, tone: 'info' });
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 INFO ===
742
- // Context remaining
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 ? 'warn' : contextRemaining <= 30 ? 'info' : 'muted';
746
- parts.push({ text: `⊛${contextRemaining}%`, tone });
747
- }
748
- // Token usage
749
- if (this.metaTokensUsed !== null) {
750
- const used = this.formatTokenCount(this.metaTokensUsed);
751
- const limit = this.metaTokenLimit ? `/${this.formatTokenCount(this.metaTokenLimit)}` : '';
752
- parts.push({ text: `${used}${limit}tk`, tone: 'muted' });
753
- }
754
- // Elapsed time during streaming
755
- if (this.metaElapsedSeconds !== null && this.streamingLabel) {
756
- parts.push({ text: `⏱${this.formatElapsedLabel(this.metaElapsedSeconds)}`, tone: 'muted' });
757
- }
758
- return renderStatusLine(parts, width);
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: 'Shift',
768
- sh: 'Shift',
769
- alt: 'Alt',
770
- option: 'Alt',
771
- opt: 'Alt',
772
- ctrl: 'Ctrl',
773
- control: 'Ctrl',
774
- cmd: 'Cmd',
775
- meta: 'Cmd',
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 label = map[part];
789
- if (label)
790
- return label;
791
- return part.length === 1 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1);
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 `[${formatted}]`;
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 5; // Fixed: divider + input + status + buffer
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 and clear it.
997
- * This gives us full control over the terminal without affecting user's history.
1047
+ * Enter alternate screen buffer.
1048
+ * DISABLED: Using terminal-native mode for proper scrollback and text selection.
998
1049
  */
999
1050
  enterAlternateScreen() {
1000
- writeLock.lock('enterAltScreen');
1001
- try {
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
- * Restores the user's previous terminal content.
1056
+ * DISABLED: Using terminal-native mode.
1015
1057
  */
1016
1058
  exitAlternateScreen() {
1017
- writeLock.lock('exitAltScreen');
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 this.alternateScreenActive;
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 entire terminal screen and reset content position.
1040
- * This removes all content including the launching command.
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 'd':
1147
- // Alt+D: Toggle verification/double-check mode (auto-tests after edits)
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 (Claude Code style).
1215
- * Shows scroll position when in scrollback mode, or history size hint when idle.
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
- const bufferSize = this.scrollbackBuffer.length;
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
- this.scrollUp(20); // Scroll up by 20 lines
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
- this.scrollDown(20); // Scroll down by 20 lines
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(lines = 10) {
1693
- const { rows } = this.getSize();
1694
- const chatBoxHeight = this.getChatBoxHeight();
1695
- const visibleLines = Math.max(1, rows - chatBoxHeight);
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(lines = 10) {
1708
- this.scrollbackOffset = Math.max(0, this.scrollbackOffset - lines);
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
- const { rows } = this.getSize();
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
- if (this.isInScrollbackMode) {
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 with enhanced visuals
1751
- * Features:
1752
- * - Visual scroll position indicator
1753
- * - Progress bar showing position in history
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
- const { rows, cols } = this.getSize();
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