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.
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +36 -16
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +34 -5
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +168 -99
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/dist/ui/animatedStatus.d.ts +129 -0
- package/dist/ui/animatedStatus.d.ts.map +1 -0
- package/dist/ui/animatedStatus.js +384 -0
- package/dist/ui/animatedStatus.js.map +1 -0
- package/package.json +1 -1
|
@@ -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":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"UnifiedUIRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/UnifiedUIRenderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 {
|
|
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
|
|
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
|
-
//
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
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
|
-
//
|
|
1065
|
-
for (
|
|
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
|
-
|
|
1071
|
-
|
|
1072
|
-
}
|
|
1127
|
+
this.write(line);
|
|
1128
|
+
this.write('\n');
|
|
1073
1129
|
}
|
|
1074
|
-
// Position cursor at prompt
|
|
1075
|
-
|
|
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;
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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('
|
|
1386
|
+
return this.truncateLine(`${theme.primary('> ')}${theme.ui.muted(summary)}`, this.safeWidth());
|
|
1317
1387
|
}
|
|
1318
|
-
|
|
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
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
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) {
|