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.
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 -17
  6. package/dist/shell/terminalInput.d.ts.map +1 -1
  7. package/dist/shell/terminalInput.js +185 -219
  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 -0
  34. package/dist/ui/toolDisplay.d.ts.map +1 -1
  35. package/dist/ui/toolDisplay.js +147 -1
  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 +29 -44
  43. package/dist/ui/unified/layout.js.map +1 -1
  44. 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+d';
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, mouse events, and escape sequences)
177
- * 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)
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
- else if (button === 65) {
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
- // Ignore other mouse events (clicks, drags, etc.) for now
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 and toggle states.
700
- * 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
701
714
  */
702
715
  buildModeControls(cols) {
703
716
  const width = Math.max(8, cols - 2);
704
- const parts = [];
705
- // Streaming status with stop hint
717
+ const leftParts = [];
718
+ const rightParts = [];
719
+ // Streaming indicator with animated spinner
706
720
  if (this.streamingLabel) {
707
- parts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
708
- parts.push({ text: `Esc:stop`, tone: 'warn' });
721
+ leftParts.push({ text: `◐ ${this.streamingLabel}`, tone: 'info' });
709
722
  }
710
- // === ALL MODE TOGGLES ===
711
- // Edit mode (Shift+Tab)
712
- const editIcon = this.editMode === 'display-edits' ? '✓' : '?';
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
- parts.push({ text: `⇧Tab:edits${editIcon}`, tone: editTone });
715
- // Verify mode (Alt+D for double-check)
716
- const verifyIcon = this.verificationEnabled ? '✓' : '○';
717
- const verifyTone = this.verificationEnabled ? 'success' : 'muted';
718
- parts.push({ text: `⌥D:verify${verifyIcon}`, tone: verifyTone });
719
- // Auto-continue (Alt+C)
720
- const autoIcon = this.autoContinueEnabled ? '✓' : '○';
721
- const autoTone = this.autoContinueEnabled ? 'success' : 'muted';
722
- parts.push({ text: `⌥C:auto${autoIcon}`, tone: autoTone });
723
- // Thinking mode (Alt+T) - show current mode
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 thinkingShort = this.thinkingModeLabel === 'minimal' ? 'min' :
726
- this.thinkingModeLabel === 'balanced' ? 'bal' :
727
- this.thinkingModeLabel === 'extended' ? 'ext' : this.thinkingModeLabel;
728
- parts.push({ text: `⌥T:think:${thinkingShort}`, tone: 'info' });
729
- }
730
- // Clear context (Alt+X)
731
- parts.push({ text: `⌥X:clear`, tone: 'muted' });
732
- // Scroll (PgUp/Dn)
733
- parts.push({ text: `PgUp/Dn:scroll`, tone: 'muted' });
734
- // === STATUS INFO ===
735
- // Context remaining
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 ? 'warn' : contextRemaining <= 30 ? 'info' : 'muted';
739
- const icon = contextRemaining <= 20 ? '⚠' : '◉';
740
- parts.push({ text: `${icon}ctx:${contextRemaining}%`, tone });
741
- }
742
- // Token usage
743
- if (this.metaTokensUsed !== null) {
744
- const used = this.formatTokenCount(this.metaTokensUsed);
745
- const limit = this.metaTokenLimit ? `/${this.formatTokenCount(this.metaTokenLimit)}` : '';
746
- parts.push({ text: `⊛${used}${limit}`, tone: 'muted' });
747
- }
748
- // Elapsed time during streaming
749
- if (this.metaElapsedSeconds !== null && this.streamingLabel) {
750
- parts.push({ text: `⏱${this.formatElapsedLabel(this.metaElapsedSeconds)}`, tone: 'muted' });
751
- }
752
- 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);
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: 'Shift',
762
- sh: 'Shift',
763
- alt: 'Alt',
764
- option: 'Alt',
765
- opt: 'Alt',
766
- ctrl: 'Ctrl',
767
- control: 'Ctrl',
768
- cmd: 'Cmd',
769
- meta: 'Cmd',
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 label = map[part];
783
- if (label)
784
- return label;
785
- 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();
786
843
  })
787
- .join('+');
788
- return `[${formatted}]`;
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 5; // Fixed: divider + input + status + buffer
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 and clear it.
991
- * 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.
992
1049
  */
993
1050
  enterAlternateScreen() {
994
- writeLock.lock('enterAltScreen');
995
- try {
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
- * Restores the user's previous terminal content.
1056
+ * DISABLED: Using terminal-native mode.
1009
1057
  */
1010
1058
  exitAlternateScreen() {
1011
- writeLock.lock('exitAltScreen');
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 this.alternateScreenActive;
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 entire terminal screen and reset content position.
1034
- * 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).
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 'd':
1141
- // Alt+D: Toggle verification/double-check mode (auto-tests after edits)
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 (Claude Code style).
1209
- * 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.
1210
1256
  */
1211
1257
  buildScrollIndicator() {
1212
- const bufferSize = this.scrollbackBuffer.length;
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
- 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
1289
1322
  return true;
1290
1323
  case 'pagedown':
1291
- this.scrollDown(20); // Scroll down by 20 lines
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(lines = 10) {
1687
- const { rows } = this.getSize();
1688
- const chatBoxHeight = this.getChatBoxHeight();
1689
- const visibleLines = Math.max(1, rows - chatBoxHeight);
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(lines = 10) {
1702
- this.scrollbackOffset = Math.max(0, this.scrollbackOffset - lines);
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
- const { rows } = this.getSize();
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
- if (this.isInScrollbackMode) {
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 with enhanced visuals
1745
- * Features:
1746
- * - Visual scroll position indicator
1747
- * - Progress bar showing position in history
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
- const { rows, cols } = this.getSize();
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