erosolar-cli 2.1.63 → 2.1.66

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.
@@ -53,16 +53,25 @@ export declare class UnifiedUIRenderer extends EventEmitter {
53
53
  private statusMessage;
54
54
  private statusOverride;
55
55
  private statusStreaming;
56
+ private streamingSpinner;
57
+ private thinkingIndicator;
58
+ private contextMeter;
59
+ private spinnerFrame;
60
+ private spinnerInterval;
61
+ private compactingStatusMessage;
62
+ private compactingStatusFrame;
63
+ private compactingStatusInterval;
64
+ private readonly compactingSpinnerFrames;
65
+ private activityMessage;
66
+ private activityStarFrame;
67
+ private readonly activityStarFrames;
56
68
  private statusMeta;
57
69
  private toggleState;
58
70
  private formatHotkey;
59
71
  private lastPromptEvent;
60
72
  private promptHeight;
61
73
  private lastOverlayHeight;
62
- private lastPromptIndex;
63
- private readonly overlayBottomPadding;
64
74
  private inlinePanel;
65
- private overlayInvalidated;
66
75
  private hasConversationContent;
67
76
  private isPromptActive;
68
77
  private inputRenderOffset;
@@ -83,7 +92,6 @@ export declare class UnifiedUIRenderer extends EventEmitter {
83
92
  private hasRenderedPrompt;
84
93
  private hasEverRenderedOverlay;
85
94
  private lastOverlay;
86
- private contentRowCount;
87
95
  private allowPromptRender;
88
96
  private inputCapture;
89
97
  constructor(output?: NodeJS.WriteStream, input?: NodeJS.ReadStream, options?: UnifiedUIRendererOptions);
@@ -132,6 +140,19 @@ export declare class UnifiedUIRenderer extends EventEmitter {
132
140
  */
133
141
  addCompactBlock(content: string, label?: string): void;
134
142
  setMode(mode: 'idle' | 'streaming'): void;
143
+ /**
144
+ * Start the animated spinner for streaming status
145
+ */
146
+ private startSpinnerAnimation;
147
+ /**
148
+ * Stop the animated spinner
149
+ */
150
+ private stopSpinnerAnimation;
151
+ /**
152
+ * Set the activity message displayed with animated star
153
+ * Example: "Ruminating…" shows as "✳ Ruminating… (esc to interrupt)"
154
+ */
155
+ setActivity(message: string | null): void;
135
156
  getMode(): 'idle' | 'streaming';
136
157
  updateStatus(message: string | null): void;
137
158
  updateStatusBundle(status: {
@@ -187,12 +208,20 @@ export declare class UnifiedUIRenderer extends EventEmitter {
187
208
  private visibleLength;
188
209
  private stripAnsi;
189
210
  private truncateLine;
190
- private clearOverlayRows;
191
211
  getBuffer(): string;
192
212
  getCursor(): number;
193
213
  setBuffer(text: string, cursorPos?: number): void;
194
214
  clearBuffer(): void;
195
215
  setModeStatus(status: string | null): void;
216
+ /**
217
+ * Show a compacting status with animated spinner (Claude Code style)
218
+ * Uses ✻ character with animation to indicate context compaction in progress
219
+ */
220
+ showCompactingStatus(message: string): void;
221
+ /**
222
+ * Hide the compacting status and stop spinner animation
223
+ */
224
+ hideCompactingStatus(): void;
196
225
  emitPrompt(content: string): void;
197
226
  /**
198
227
  * Display user prompt immediately in scrollback (Claude Code style)
@@ -1 +1 @@
1
- {"version":3,"file":"UnifiedUIRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/UnifiedUIRenderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UnifiedUIRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/UnifiedUIRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnG,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,KAAK,GACL,QAAQ,GACR,OAAO,GACP,WAAW,GACX,WAAW,GACX,aAAa,CAAC;AAUlB,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAOD,UAAU,eAAe;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC3C,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAoBD,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IAEtC,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,eAAe,CAAuB;IAE9C,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,iBAAiB,CAAS;IAElC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAqE;IACxF,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,cAAc,CAA+D;IAErF,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,eAAe,CAAuB;IAG9C,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAA+B;IAGtD,OAAO,CAAC,uBAAuB,CAAM;IACrC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,wBAAwB,CAA+B;IAC/D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAwB;IAGhE,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkC;IAErE,OAAO,CAAC,UAAU,CAcX;IACP,OAAO,CAAC,WAAW,CAIjB;IAIF,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IACzC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAM;IAC7C,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,iBAAiB,CAA2C;IACpE,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,0BAA0B,CAAQ;IAC1C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,WAAW,CAAyD;IAC5E,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,YAAY,CAMJ;gBAGd,MAAM,GAAE,MAAM,CAAC,WAA4B,EAC3C,KAAK,GAAE,MAAM,CAAC,UAA0B,EACxC,OAAO,CAAC,EAAE,wBAAwB;IA+BpC,UAAU,IAAI,IAAI;IA6BlB,OAAO,IAAI,IAAI;IAwCf,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,cAAc;IAmKtB,OAAO,CAAC,oBAAoB;IAqC5B,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,gBAAgB;IA4DxB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,wBAAwB;IAShC,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,UAAU;IA8ClB,OAAO,CAAC,iBAAiB;IAkCzB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI;IAK7D,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,mBAAmB;IAgB3B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;YAyD1C,YAAY;IAuC1B;;;;OAIG;IACG,WAAW,CAAC,SAAS,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAiB3C,WAAW;IA0CzB,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,aAAa;IAgErB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAkB,GAAG,IAAI;IAgBjE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAsBzC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAU5B;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAOzC,OAAO,IAAI,MAAM,GAAG,WAAW;IAI/B,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI1C,kBAAkB,CAChB,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EACrF,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GACjC,IAAI;IAuCP,gBAAgB,CACd,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,EACD,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GACjC,IAAI;IAUP,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAaxD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAWrC,gBAAgB,IAAI,IAAI;IAQxB,OAAO,CAAC,YAAY;IAmFpB,OAAO,CAAC,iBAAiB;IA2CzB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,cAAc;IA0CtB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,eAAe;IAyDvB,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,oBAAoB;IAU5B,YAAY,CAAC,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IA0B5G,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,YAAY;IAyCpB,SAAS,IAAI,MAAM;IAInB,SAAS,IAAI,MAAM;IAInB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IASjD,WAAW,IAAI,IAAI;IAWnB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI1C;;;OAGG;IACH,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAW3C;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAS5B,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAWzB,MAAM,IAAI,IAAI;IAId,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,kBAAkB;CAM3B"}
@@ -11,9 +11,9 @@
11
11
  import * as readline from 'node:readline';
12
12
  import { EventEmitter } from 'node:events';
13
13
  import { homedir } from 'node:os';
14
- import { theme } from './theme.js';
14
+ import { theme, spinnerFrames } from './theme.js';
15
15
  import { isPlainOutputMode } from './outputMode.js';
16
- import { renderDivider } from './unified/layout.js';
16
+ import { ContextMeter, disposeAnimations } from './animatedStatus.js';
17
17
  const ESC = {
18
18
  HIDE_CURSOR: '\x1b[?25l',
19
19
  SHOW_CURSOR: '\x1b[?25h',
@@ -54,6 +54,21 @@ export class UnifiedUIRenderer extends EventEmitter {
54
54
  statusMessage = null;
55
55
  statusOverride = null;
56
56
  statusStreaming = null;
57
+ // Animated UI components
58
+ streamingSpinner = null;
59
+ thinkingIndicator = null;
60
+ contextMeter;
61
+ spinnerFrame = 0;
62
+ spinnerInterval = null;
63
+ // Compacting status animation
64
+ compactingStatusMessage = '';
65
+ compactingStatusFrame = 0;
66
+ compactingStatusInterval = null;
67
+ compactingSpinnerFrames = ['✻', '✼', '✻', '✺'];
68
+ // Animated activity line (e.g., "✳ Ruminating… (esc to interrupt)")
69
+ activityMessage = null;
70
+ activityStarFrame = 0;
71
+ activityStarFrames = ['✳', '✴', '✵', '✶', '✷', '✸'];
57
72
  statusMeta = {};
58
73
  toggleState = {
59
74
  verificationEnabled: false,
@@ -69,10 +84,7 @@ export class UnifiedUIRenderer extends EventEmitter {
69
84
  lastPromptEvent = null;
70
85
  promptHeight = 0;
71
86
  lastOverlayHeight = 0;
72
- lastPromptIndex = 0;
73
- overlayBottomPadding = 1;
74
87
  inlinePanel = [];
75
- overlayInvalidated = false;
76
88
  hasConversationContent = false;
77
89
  isPromptActive = false;
78
90
  inputRenderOffset = 0;
@@ -91,9 +103,8 @@ export class UnifiedUIRenderer extends EventEmitter {
91
103
  lastRenderedEventKey = null;
92
104
  lastOutputEndedWithNewline = true;
93
105
  hasRenderedPrompt = false;
94
- hasEverRenderedOverlay = false; // Track if we've ever rendered to prevent first-render scrollback pollution
106
+ hasEverRenderedOverlay = false; // Track if we've ever rendered for inline clearing
95
107
  lastOverlay = null;
96
- contentRowCount = 0; // Track how many rows of content have been written (for flow-based positioning)
97
108
  allowPromptRender = true;
98
109
  inputCapture = null;
99
110
  constructor(output = process.stdout, input = process.stdin, options) {
@@ -102,6 +113,8 @@ export class UnifiedUIRenderer extends EventEmitter {
102
113
  this.input = input;
103
114
  this.interactive = Boolean(this.output.isTTY && this.input.isTTY && !process.env['CI']);
104
115
  this.plainMode = isPlainOutputMode() || !this.interactive;
116
+ // Initialize animated components
117
+ this.contextMeter = new ContextMeter();
105
118
  this.rl = readline.createInterface({
106
119
  input: this.input,
107
120
  output: this.output,
@@ -148,6 +161,21 @@ export class UnifiedUIRenderer extends EventEmitter {
148
161
  cleanup() {
149
162
  this.cancelInputCapture(new Error('Renderer disposed'));
150
163
  this.cancelPlainPasteCapture();
164
+ // Stop any running animations
165
+ if (this.spinnerInterval) {
166
+ clearInterval(this.spinnerInterval);
167
+ this.spinnerInterval = null;
168
+ }
169
+ if (this.streamingSpinner) {
170
+ this.streamingSpinner.stop();
171
+ this.streamingSpinner = null;
172
+ }
173
+ if (this.thinkingIndicator) {
174
+ this.thinkingIndicator.stop();
175
+ this.thinkingIndicator = null;
176
+ }
177
+ this.contextMeter.dispose();
178
+ disposeAnimations();
151
179
  if (!this.interactive) {
152
180
  this.rl.close();
153
181
  return;
@@ -801,11 +829,6 @@ export class UnifiedUIRenderer extends EventEmitter {
801
829
  }
802
830
  this.output.write(formatted);
803
831
  this.lastOutputEndedWithNewline = formatted.endsWith('\n');
804
- // Track content row count for flow-based overlay positioning
805
- const newlines = (formatted.match(/\n/g) || []).length;
806
- this.contentRowCount += newlines + (this.lastOutputEndedWithNewline ? 0 : 1);
807
- // Overlay must be re-anchored after new scrollback is written
808
- this.overlayInvalidated = true;
809
832
  // Don't re-render prompt after every event - wait for queue to finish
810
833
  // This prevents premature prompt rendering that cuts off responses
811
834
  }
@@ -851,10 +874,19 @@ export class UnifiedUIRenderer extends EventEmitter {
851
874
  case 'prompt':
852
875
  return `\n> ${event.content}\n`; // Plain > like Claude Code
853
876
  case 'thought': {
854
- // Claude Code style: first line, then indented continuation
877
+ // Enhanced thought display with animated thinking indicator style
878
+ // Uses ◐ symbol for thinking with muted styling to distinguish from content
879
+ const thinkingIcon = theme.thinking?.icon ? theme.thinking.icon('◐') : theme.ui.muted('◐');
880
+ const thinkingLabel = theme.thinking?.label ? theme.thinking.label('Thinking') : theme.ui.muted('Thinking');
855
881
  const lines = event.content.split('\n');
882
+ // Show thinking header with first line, then indent continuation
856
883
  const formatted = lines
857
- .map((line, i) => (i === 0 ? `${bullet} ${line}` : ` ${line}`))
884
+ .map((line, i) => {
885
+ if (i === 0) {
886
+ return `${thinkingIcon} ${thinkingLabel}: ${theme.ui.muted(line)}`;
887
+ }
888
+ return ` ${theme.ui.muted(line)}`;
889
+ })
858
890
  .join('\n');
859
891
  return `\n${formatted}\n`;
860
892
  }
@@ -912,6 +944,13 @@ export class UnifiedUIRenderer extends EventEmitter {
912
944
  setMode(mode) {
913
945
  const wasStreaming = this.mode === 'streaming';
914
946
  this.mode = mode;
947
+ // Start/stop spinner animation based on streaming state
948
+ if (mode === 'streaming' && !wasStreaming) {
949
+ this.startSpinnerAnimation();
950
+ }
951
+ else if (mode === 'idle' && wasStreaming) {
952
+ this.stopSpinnerAnimation();
953
+ }
915
954
  if (wasStreaming && mode === 'idle' && !this.lastOutputEndedWithNewline) {
916
955
  // Finish streaming on a fresh line so the next prompt/event doesn't collide
917
956
  this.write('\n');
@@ -922,6 +961,45 @@ export class UnifiedUIRenderer extends EventEmitter {
922
961
  this.renderPrompt();
923
962
  }
924
963
  }
964
+ /**
965
+ * Start the animated spinner for streaming status
966
+ */
967
+ startSpinnerAnimation() {
968
+ if (this.spinnerInterval)
969
+ return; // Already running
970
+ this.spinnerFrame = 0;
971
+ this.activityStarFrame = 0;
972
+ this.spinnerInterval = setInterval(() => {
973
+ this.spinnerFrame = (this.spinnerFrame + 1) % spinnerFrames.braille.length;
974
+ this.activityStarFrame = (this.activityStarFrame + 1) % this.activityStarFrames.length;
975
+ // Re-render to show updated spinner/star frame
976
+ if (!this.plainMode && this.mode === 'streaming') {
977
+ this.renderPrompt();
978
+ }
979
+ }, 80); // ~12 FPS for smooth spinner animation
980
+ }
981
+ /**
982
+ * Stop the animated spinner
983
+ */
984
+ stopSpinnerAnimation() {
985
+ if (this.spinnerInterval) {
986
+ clearInterval(this.spinnerInterval);
987
+ this.spinnerInterval = null;
988
+ }
989
+ this.spinnerFrame = 0;
990
+ this.activityStarFrame = 0;
991
+ this.activityMessage = null;
992
+ }
993
+ /**
994
+ * Set the activity message displayed with animated star
995
+ * Example: "Ruminating…" shows as "✳ Ruminating… (esc to interrupt)"
996
+ */
997
+ setActivity(message) {
998
+ this.activityMessage = message;
999
+ if (!this.plainMode) {
1000
+ this.renderPrompt();
1001
+ }
1002
+ }
925
1003
  getMode() {
926
1004
  return this.mode;
927
1005
  }
@@ -1017,13 +1095,9 @@ export class UnifiedUIRenderer extends EventEmitter {
1017
1095
  if (!this.allowPromptRender) {
1018
1096
  return;
1019
1097
  }
1020
- // Rich mode: inline overlay anchored to current scrollback (no full-screen clear)
1098
+ // Rich mode: inline rendering (no full-screen overlay)
1021
1099
  this.updateTerminalSize();
1022
1100
  const maxWidth = this.safeWidth();
1023
- if (this.lastRenderWidth !== null && maxWidth !== this.lastRenderWidth) {
1024
- // Terminal resized; force a clean anchor so the overlay doesn't jitter.
1025
- this.overlayInvalidated = true;
1026
- }
1027
1101
  this.lastRenderWidth = maxWidth;
1028
1102
  const overlay = this.buildOverlayLines();
1029
1103
  if (!overlay.lines.length) {
@@ -1035,53 +1109,39 @@ export class UnifiedUIRenderer extends EventEmitter {
1035
1109
  }
1036
1110
  let promptIndex = Math.max(0, Math.min(overlay.promptIndex, renderedLines.length - 1));
1037
1111
  let height = renderedLines.length;
1038
- // Keep at least one free line below the overlay so typing always has breathing room
1039
- const bottomPadding = this.overlayBottomPadding;
1040
1112
  const totalRows = this.rows || 24;
1041
- const availableRows = Math.max(1, totalRows - bottomPadding);
1042
- if (height > availableRows) {
1043
- renderedLines.splice(availableRows);
1044
- height = renderedLines.length;
1045
- promptIndex = Math.max(0, Math.min(promptIndex, height - 1));
1046
- }
1047
- // Position overlay immediately after content (flow-based) rather than pinned to bottom
1048
- // Only pin to bottom if content + overlay would exceed screen height
1049
- const contentEndRow = Math.min(this.contentRowCount + 1, totalRows);
1050
- const flowBasedStartRow = contentEndRow;
1051
- const bottomPinnedStartRow = Math.max(1, availableRows - height + 1);
1052
- // Use flow-based positioning when content is small, bottom-pinned when content fills screen
1053
- const startRow = (flowBasedStartRow + height <= availableRows)
1054
- ? flowBasedStartRow
1055
- : bottomPinnedStartRow;
1056
- const promptRow = startRow + promptIndex;
1057
- const promptCol = Math.min(Math.max(1, 3 + this.cursor), this.cols || 80);
1058
- // Clear any previous overlay footprint (status, prompt, controls) to avoid leaking into scrollback
1059
- this.clearOverlayRows(height, startRow);
1060
- if (bottomPadding > 0 && startRow + height <= totalRows) {
1061
- this.write(ESC.TO(startRow + height, 1));
1062
- this.write(ESC.CLEAR_LINE);
1113
+ // PURE INLINE MODE: Always render the prompt area inline, flowing naturally.
1114
+ // No overlay mode - content flows like Claude Code.
1115
+ // Clear any previous inline render by moving up and clearing
1116
+ if (this.hasEverRenderedOverlay && this.lastOverlayHeight > 0) {
1117
+ // Move to start of previous prompt and clear it
1118
+ const linesToClear = this.lastOverlayHeight;
1119
+ for (let i = 0; i < linesToClear; i++) {
1120
+ this.write('\x1b[A'); // Move up
1121
+ this.write(ESC.CLEAR_LINE);
1122
+ }
1063
1123
  }
1064
- // Render overlay lines in place without pushing scrollback
1065
- for (let idx = 0; idx < height; idx++) {
1066
- const row = startRow + idx;
1067
- const line = renderedLines[idx] ?? '';
1068
- this.write(ESC.TO(row, 1));
1124
+ // Write prompt lines directly to scrollback (no absolute positioning)
1125
+ for (const line of renderedLines) {
1069
1126
  this.write(ESC.CLEAR_LINE);
1070
- if (line) {
1071
- this.write(line);
1072
- }
1127
+ this.write(line);
1128
+ this.write('\n');
1073
1129
  }
1074
- // Position cursor at prompt row/col
1075
- this.write(ESC.TO(promptRow, promptCol));
1130
+ // Position cursor at prompt input position
1131
+ const promptCol = Math.min(Math.max(1, 3 + this.cursor), this.cols || 80);
1132
+ // Move up to the prompt line and position cursor
1133
+ const linesToMoveUp = height - promptIndex - 1;
1134
+ if (linesToMoveUp > 0) {
1135
+ this.write(`\x1b[${linesToMoveUp}A`); // Move cursor up
1136
+ }
1137
+ this.write(`\x1b[${promptCol}G`); // Move to column
1076
1138
  this.cursorVisibleColumn = promptCol;
1077
1139
  this.hasRenderedPrompt = true;
1078
- this.hasEverRenderedOverlay = true; // Mark that we've rendered at least once
1140
+ this.hasEverRenderedOverlay = true;
1079
1141
  this.isPromptActive = true;
1080
1142
  this.lastOverlayHeight = height;
1081
- this.lastPromptIndex = promptIndex;
1082
1143
  this.lastOverlay = { lines: renderedLines, promptIndex };
1083
- this.overlayInvalidated = false;
1084
- this.lastOutputEndedWithNewline = true;
1144
+ this.lastOutputEndedWithNewline = false;
1085
1145
  this.promptHeight = height;
1086
1146
  }
1087
1147
  buildOverlayLines() {
@@ -1091,7 +1151,9 @@ export class UnifiedUIRenderer extends EventEmitter {
1091
1151
  for (const line of chromeLines) {
1092
1152
  lines.push(this.truncateLine(line, maxWidth));
1093
1153
  }
1094
- const divider = renderDivider(Math.min(maxWidth, 96), 'prompt');
1154
+ // Simple divider without label (Claude Code style)
1155
+ const dividerWidth = Math.min(maxWidth, 48);
1156
+ const divider = theme.ui.muted('─'.repeat(dividerWidth));
1095
1157
  lines.push(this.truncateLine(divider, maxWidth));
1096
1158
  const promptIndex = lines.length;
1097
1159
  lines.push(this.truncateLine(this.buildInputLine(), maxWidth));
@@ -1099,10 +1161,11 @@ export class UnifiedUIRenderer extends EventEmitter {
1099
1161
  for (let index = 0; index < this.suggestions.length; index++) {
1100
1162
  const suggestion = this.suggestions[index];
1101
1163
  const isActive = index === this.suggestionIndex;
1102
- const marker = isActive ? theme.primary('›') : theme.ui.muted('›');
1164
+ // Use simple arrow marker like Claude Code
1165
+ const marker = isActive ? theme.primary('>') : theme.ui.muted(' ');
1103
1166
  const cmdText = isActive ? theme.primary(suggestion.command) : theme.ui.muted(suggestion.command);
1104
1167
  const descText = isActive ? suggestion.description : theme.ui.muted(suggestion.description);
1105
- lines.push(this.truncateLine(`${marker} ${cmdText} — ${descText}`, maxWidth));
1168
+ lines.push(this.truncateLine(` ${marker} ${cmdText} — ${descText}`, maxWidth));
1106
1169
  }
1107
1170
  }
1108
1171
  if (this.inlinePanel.length > 0) {
@@ -1135,15 +1198,22 @@ export class UnifiedUIRenderer extends EventEmitter {
1135
1198
  return [];
1136
1199
  }
1137
1200
  const segments = [];
1138
- segments.push(`${theme.ui.muted('status')} ${this.applyTone(statusLabel.text, statusLabel.tone)}`);
1201
+ // Add animated spinner when streaming for dynamic visual feedback
1202
+ if (this.mode === 'streaming') {
1203
+ const spinnerChars = spinnerFrames.braille;
1204
+ const spinnerChar = spinnerChars[this.spinnerFrame % spinnerChars.length] ?? '⠋';
1205
+ segments.push(`${theme.info(spinnerChar)} ${this.applyTone(statusLabel.text, statusLabel.tone)}`);
1206
+ }
1207
+ else {
1208
+ segments.push(`${theme.ui.muted('status')} ${this.applyTone(statusLabel.text, statusLabel.tone)}`);
1209
+ }
1139
1210
  if (this.statusMeta.sessionTime) {
1140
1211
  segments.push(`${theme.ui.muted('runtime')} ${theme.ui.muted(this.statusMeta.sessionTime)}`);
1141
1212
  }
1142
1213
  if (this.statusMeta.contextPercent !== undefined) {
1143
- const ctx = this.statusMeta.contextPercent;
1144
- const tone = ctx > 90 ? 'error' : ctx > 70 ? 'warn' : 'muted';
1145
- const color = tone === 'error' ? theme.error : tone === 'warn' ? theme.warning : theme.ui.muted;
1146
- segments.push(`${theme.ui.muted('ctx')} ${color(`${ctx}%`)}`);
1214
+ // Use animated context meter for smooth color transitions
1215
+ this.contextMeter.update(this.statusMeta.contextPercent);
1216
+ segments.push(this.contextMeter.render());
1147
1217
  }
1148
1218
  return this.wrapSegments(segments, maxWidth);
1149
1219
  }
@@ -1313,9 +1383,10 @@ export class UnifiedUIRenderer extends EventEmitter {
1313
1383
  buildInputLine() {
1314
1384
  if (this.collapsedPaste) {
1315
1385
  const summary = `[pasted ${this.collapsedPaste.lines} lines, ${this.collapsedPaste.chars} chars] (Ctrl+L insert, Backspace discard)`;
1316
- return this.truncateLine(`${theme.primary(' ')}${theme.ui.muted(summary)}`, this.safeWidth());
1386
+ return this.truncateLine(`${theme.primary('> ')}${theme.ui.muted(summary)}`, this.safeWidth());
1317
1387
  }
1318
- const prompt = theme.primary(' ');
1388
+ // Claude Code uses simple '>' prompt
1389
+ const prompt = theme.primary('> ');
1319
1390
  const promptWidth = this.visibleLength(prompt);
1320
1391
  const maxWidth = this.safeWidth();
1321
1392
  const available = Math.max(1, maxWidth - promptWidth);
@@ -1452,17 +1523,6 @@ export class UnifiedUIRenderer extends EventEmitter {
1452
1523
  }
1453
1524
  return result;
1454
1525
  }
1455
- clearOverlayRows(rows, startRow) {
1456
- const totalRows = this.rows || 24;
1457
- const limit = Math.max(0, Math.min(rows, totalRows));
1458
- for (let idx = 0; idx < limit; idx++) {
1459
- const row = startRow + idx;
1460
- if (row < 1 || row > totalRows)
1461
- continue;
1462
- this.write(ESC.TO(row, 1));
1463
- this.write(ESC.CLEAR_LINE);
1464
- }
1465
- }
1466
1526
  getBuffer() {
1467
1527
  return this.buffer;
1468
1528
  }
@@ -1490,6 +1550,31 @@ export class UnifiedUIRenderer extends EventEmitter {
1490
1550
  setModeStatus(status) {
1491
1551
  this.updateStatus(status);
1492
1552
  }
1553
+ /**
1554
+ * Show a compacting status with animated spinner (Claude Code style)
1555
+ * Uses ✻ character with animation to indicate context compaction in progress
1556
+ */
1557
+ showCompactingStatus(message) {
1558
+ this.statusMessage = message;
1559
+ if (!this.spinnerInterval) {
1560
+ this.spinnerInterval = setInterval(() => {
1561
+ this.spinnerFrame++;
1562
+ this.renderPrompt();
1563
+ }, 80);
1564
+ }
1565
+ this.renderPrompt();
1566
+ }
1567
+ /**
1568
+ * Hide the compacting status and stop spinner animation
1569
+ */
1570
+ hideCompactingStatus() {
1571
+ if (this.spinnerInterval) {
1572
+ clearInterval(this.spinnerInterval);
1573
+ this.spinnerInterval = null;
1574
+ }
1575
+ this.statusMessage = null;
1576
+ this.renderPrompt();
1577
+ }
1493
1578
  emitPrompt(content) {
1494
1579
  this.pushPromptEvent(content);
1495
1580
  }
@@ -1533,31 +1618,15 @@ export class UnifiedUIRenderer extends EventEmitter {
1533
1618
  const height = this.lastOverlay?.lines.length ?? this.promptHeight ?? 0;
1534
1619
  if (height === 0)
1535
1620
  return;
1536
- this.updateTerminalSize();
1537
- const totalRows = this.rows || 24;
1538
- const bottomPadding = this.overlayBottomPadding;
1539
- const availableRows = Math.max(1, totalRows - bottomPadding);
1540
- // Use same flow-based positioning logic as renderPrompt for consistency
1541
- const contentEndRow = Math.min(this.contentRowCount + 1, totalRows);
1542
- const flowBasedStartRow = contentEndRow;
1543
- const bottomPinnedStartRow = Math.max(1, availableRows - height + 1);
1544
- const startRow = (flowBasedStartRow + height <= availableRows)
1545
- ? flowBasedStartRow
1546
- : bottomPinnedStartRow;
1547
- this.clearOverlayRows(height, startRow);
1548
- // Keep the padding row clean as well
1549
- const paddingRow = startRow + height;
1550
- if (this.overlayBottomPadding > 0 && paddingRow <= totalRows) {
1551
- this.write(ESC.TO(paddingRow, 1));
1621
+ // Pure inline mode: clear by moving up and clearing lines
1622
+ for (let i = 0; i < height; i++) {
1623
+ this.write('\x1b[A'); // Move up
1552
1624
  this.write(ESC.CLEAR_LINE);
1553
1625
  }
1554
- // Move cursor to the bottom ready for new scrollback output
1555
- this.write(ESC.TO(totalRows, 1));
1556
- this.lastOverlayHeight = height;
1557
- this.lastPromptIndex = this.lastOverlay?.promptIndex ?? this.lastPromptIndex;
1558
1626
  this.lastOverlay = null;
1559
- this.overlayInvalidated = true;
1560
1627
  this.promptHeight = 0;
1628
+ this.lastOverlayHeight = 0;
1629
+ this.isPromptActive = false;
1561
1630
  }
1562
1631
  updateTerminalSize() {
1563
1632
  if (this.output.isTTY) {