erosolar-cli 2.1.92 → 2.1.94
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/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +22 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +6 -1
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/UnifiedUIRenderer.d.ts +49 -1
- package/dist/ui/UnifiedUIRenderer.d.ts.map +1 -1
- package/dist/ui/UnifiedUIRenderer.js +276 -39
- package/dist/ui/UnifiedUIRenderer.js.map +1 -1
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ export interface CommandSuggestion {
|
|
|
14
14
|
description: string;
|
|
15
15
|
category?: string;
|
|
16
16
|
}
|
|
17
|
-
type CanonicalEventType = 'prompt' | 'thought' | 'stream' | 'tool' | 'build' | 'test' | 'response';
|
|
17
|
+
type CanonicalEventType = 'prompt' | 'thought' | 'stream' | 'tool' | 'tool-result' | 'build' | 'test' | 'response';
|
|
18
18
|
export type RendererEventType = CanonicalEventType | 'raw' | 'banner' | 'error' | 'streaming' | 'tool-call' | 'tool-result';
|
|
19
19
|
export interface UnifiedUIRendererOptions {
|
|
20
20
|
debug?: boolean;
|
|
@@ -50,6 +50,7 @@ export declare class UnifiedUIRenderer extends EventEmitter {
|
|
|
50
50
|
private hotkeysInToggleLine;
|
|
51
51
|
private collapsedPaste;
|
|
52
52
|
private mode;
|
|
53
|
+
private streamingStartTime;
|
|
53
54
|
private statusMessage;
|
|
54
55
|
private statusOverride;
|
|
55
56
|
private statusStreaming;
|
|
@@ -129,6 +130,53 @@ export declare class UnifiedUIRenderer extends EventEmitter {
|
|
|
129
130
|
private renderEvent;
|
|
130
131
|
private normalizeEventType;
|
|
131
132
|
private formatContent;
|
|
133
|
+
/**
|
|
134
|
+
* Format text in Claude Code style: ⏺ prefix with wrapped continuation lines
|
|
135
|
+
* Example:
|
|
136
|
+
* ⏺ The AI ran tools but gave no response. Need to fix
|
|
137
|
+
* the response handling. Let me check where the AI's
|
|
138
|
+
* text response should be displayed:
|
|
139
|
+
*/
|
|
140
|
+
private formatClaudeCodeBlock;
|
|
141
|
+
/**
|
|
142
|
+
* Format a tool call in Claude Code style:
|
|
143
|
+
* ⏺ Search(pattern: "foo", path: "src",
|
|
144
|
+
* output_mode: "content", head_limit: 30)
|
|
145
|
+
*/
|
|
146
|
+
private formatToolCall;
|
|
147
|
+
/**
|
|
148
|
+
* Parse tool arguments from string like: key: "value", key2: value2
|
|
149
|
+
*/
|
|
150
|
+
private parseToolArgs;
|
|
151
|
+
/**
|
|
152
|
+
* Format an argument value (truncate long strings)
|
|
153
|
+
*/
|
|
154
|
+
private formatArgValue;
|
|
155
|
+
/**
|
|
156
|
+
* Format a tool result in Claude Code style:
|
|
157
|
+
* ⎿ Found 12 lines (ctrl+o to expand)
|
|
158
|
+
*/
|
|
159
|
+
private formatToolResult;
|
|
160
|
+
/**
|
|
161
|
+
* Format a compact tool call: ⏺ Read → file.ts
|
|
162
|
+
*/
|
|
163
|
+
private formatCompactToolCall;
|
|
164
|
+
/**
|
|
165
|
+
* Extract a short summary from tool args
|
|
166
|
+
*/
|
|
167
|
+
private extractToolSummary;
|
|
168
|
+
/**
|
|
169
|
+
* Format a compact tool result: └─ 42 lines
|
|
170
|
+
*/
|
|
171
|
+
private formatCompactToolResult;
|
|
172
|
+
/**
|
|
173
|
+
* Format a compact response with bullet on first line
|
|
174
|
+
*/
|
|
175
|
+
private formatCompactResponse;
|
|
176
|
+
/**
|
|
177
|
+
* Format streaming elapsed time in Claude Code style: 3m 30s
|
|
178
|
+
*/
|
|
179
|
+
private formatStreamingElapsed;
|
|
132
180
|
/**
|
|
133
181
|
* Format a compact conversation block (Claude Code style)
|
|
134
182
|
* Shows a visual separator with "history" label and ctrl+o hint
|
|
@@ -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;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;
|
|
1
|
+
{"version":3,"file":"UnifiedUIRenderer.d.ts","sourceRoot":"","sources":["../../src/ui/UnifiedUIRenderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,6 +56,7 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
56
56
|
hotkeysInToggleLine = new Set();
|
|
57
57
|
collapsedPaste = null;
|
|
58
58
|
mode = 'idle';
|
|
59
|
+
streamingStartTime = null;
|
|
59
60
|
statusMessage = null;
|
|
60
61
|
statusOverride = null;
|
|
61
62
|
statusStreaming = null;
|
|
@@ -712,6 +713,7 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
712
713
|
normalized === 'thought' ||
|
|
713
714
|
normalized === 'stream' ||
|
|
714
715
|
normalized === 'tool' ||
|
|
716
|
+
normalized === 'tool-result' ||
|
|
715
717
|
normalized === 'build' ||
|
|
716
718
|
normalized === 'test') {
|
|
717
719
|
this.hasConversationContent = true;
|
|
@@ -862,8 +864,9 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
862
864
|
return 'stream';
|
|
863
865
|
case 'tool':
|
|
864
866
|
case 'tool-call':
|
|
865
|
-
case 'tool-result':
|
|
866
867
|
return 'tool';
|
|
868
|
+
case 'tool-result':
|
|
869
|
+
return 'tool-result';
|
|
867
870
|
case 'build':
|
|
868
871
|
return 'build';
|
|
869
872
|
case 'test':
|
|
@@ -889,62 +892,291 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
889
892
|
const lines = event.content.split('\n').map(line => line.trimEnd());
|
|
890
893
|
return `${lines.join('\n')}\n`;
|
|
891
894
|
}
|
|
892
|
-
//
|
|
895
|
+
// Compact, user-friendly formatting
|
|
893
896
|
switch (event.type) {
|
|
894
897
|
case 'prompt':
|
|
895
|
-
// User prompt
|
|
898
|
+
// User prompt - just the text (prompt box handles styling)
|
|
896
899
|
return `${theme.primary('>')} ${event.content}\n`;
|
|
897
900
|
case 'thought': {
|
|
898
901
|
// Filter out generic/useless thought messages
|
|
899
902
|
const contentLower = event.content.toLowerCase();
|
|
900
903
|
const uselessPatterns = [
|
|
901
|
-
'ready to assist',
|
|
902
|
-
'
|
|
903
|
-
'
|
|
904
|
-
'
|
|
905
|
-
'standing by',
|
|
906
|
-
'here to help',
|
|
907
|
-
'how can i help',
|
|
908
|
-
'what would you like',
|
|
904
|
+
'ready to assist', 'no files read', 'no task specified',
|
|
905
|
+
'waiting for', 'standing by', 'here to help',
|
|
906
|
+
'how can i help', 'what would you like', 'thinking',
|
|
907
|
+
'set_status', 'completed',
|
|
909
908
|
];
|
|
910
909
|
if (uselessPatterns.some(p => contentLower.includes(p))) {
|
|
911
910
|
return ''; // Don't display useless thoughts
|
|
912
911
|
}
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
return `${thinkIcon} ${theme.ui.muted(event.content)}\n`;
|
|
912
|
+
// Compact thought: just the text, no extra spacing
|
|
913
|
+
return `${theme.ui.muted('○')} ${theme.ui.muted(event.content)}\n`;
|
|
916
914
|
}
|
|
917
915
|
case 'tool': {
|
|
918
|
-
//
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
const toolArgs = toolMatch[2] ? theme.ui.muted(toolMatch[2]) : '';
|
|
926
|
-
return `${toolIcon} ${toolName}${toolArgs}\n`;
|
|
927
|
-
}
|
|
928
|
-
return `${bullet} ${content}\n`;
|
|
916
|
+
// Compact tool display: ⚡ToolName → result
|
|
917
|
+
const content = event.content.replace(/^[⏺⚙○]\s*/, '');
|
|
918
|
+
return this.formatCompactToolCall(content);
|
|
919
|
+
}
|
|
920
|
+
case 'tool-result': {
|
|
921
|
+
// Inline result: └─ summary
|
|
922
|
+
return this.formatCompactToolResult(event.content);
|
|
929
923
|
}
|
|
930
924
|
case 'build':
|
|
931
|
-
|
|
932
|
-
return `${theme.warning('🔨')} ${event.content}\n`;
|
|
925
|
+
return `${bullet} ${theme.warning('Build')} ${theme.ui.muted('→')} ${event.content}\n`;
|
|
933
926
|
case 'test':
|
|
934
|
-
|
|
935
|
-
return `${theme.info('🧪')} ${event.content}\n`;
|
|
927
|
+
return `${bullet} ${theme.info('Test')} ${theme.ui.muted('→')} ${event.content}\n`;
|
|
936
928
|
case 'stream':
|
|
937
929
|
return event.content;
|
|
938
930
|
case 'response':
|
|
939
931
|
default: {
|
|
940
|
-
//
|
|
941
|
-
|
|
942
|
-
return (lines
|
|
943
|
-
.map((line, i) => (i === 0 ? `${bullet} ${line}` : ` ${line}`))
|
|
944
|
-
.join('\n') + '\n');
|
|
932
|
+
// Clean response without excessive bullets
|
|
933
|
+
return this.formatCompactResponse(event.content);
|
|
945
934
|
}
|
|
946
935
|
}
|
|
947
936
|
}
|
|
937
|
+
/**
|
|
938
|
+
* Format text in Claude Code style: ⏺ prefix with wrapped continuation lines
|
|
939
|
+
* Example:
|
|
940
|
+
* ⏺ The AI ran tools but gave no response. Need to fix
|
|
941
|
+
* the response handling. Let me check where the AI's
|
|
942
|
+
* text response should be displayed:
|
|
943
|
+
*/
|
|
944
|
+
formatClaudeCodeBlock(content) {
|
|
945
|
+
const bullet = '⏺';
|
|
946
|
+
const maxWidth = Math.min(this.cols - 4, 56); // Leave room for prefix and margins
|
|
947
|
+
const lines = content.split('\n');
|
|
948
|
+
const result = [];
|
|
949
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
950
|
+
const line = lines[lineIdx];
|
|
951
|
+
if (!line.trim()) {
|
|
952
|
+
result.push('');
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
// Word-wrap each line
|
|
956
|
+
const words = line.split(/(\s+)/);
|
|
957
|
+
let currentLine = '';
|
|
958
|
+
for (const word of words) {
|
|
959
|
+
if ((currentLine + word).length > maxWidth && currentLine.trim()) {
|
|
960
|
+
// First line of this paragraph gets ⏺, rest get indent
|
|
961
|
+
const prefix = result.length === 0 && lineIdx === 0 ? `${bullet} ` : ' ';
|
|
962
|
+
result.push(`${prefix}${currentLine.trimEnd()}`);
|
|
963
|
+
currentLine = word.trimStart();
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
currentLine += word;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (currentLine.trim()) {
|
|
970
|
+
const prefix = result.length === 0 && lineIdx === 0 ? `${bullet} ` : ' ';
|
|
971
|
+
result.push(`${prefix}${currentLine.trimEnd()}`);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return result.join('\n') + '\n';
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Format a tool call in Claude Code style:
|
|
978
|
+
* ⏺ Search(pattern: "foo", path: "src",
|
|
979
|
+
* output_mode: "content", head_limit: 30)
|
|
980
|
+
*/
|
|
981
|
+
formatToolCall(content) {
|
|
982
|
+
const bullet = '⏺';
|
|
983
|
+
// Parse tool name and arguments
|
|
984
|
+
const match = content.match(/^(\w+)\((.*)\)$/s);
|
|
985
|
+
if (!match) {
|
|
986
|
+
// Simple format without args
|
|
987
|
+
const nameMatch = content.match(/^(\w+)/);
|
|
988
|
+
if (nameMatch) {
|
|
989
|
+
return `${bullet} ${theme.info(nameMatch[1])}\n`;
|
|
990
|
+
}
|
|
991
|
+
return `${bullet} ${content}\n`;
|
|
992
|
+
}
|
|
993
|
+
const toolName = match[1];
|
|
994
|
+
const argsStr = match[2];
|
|
995
|
+
const maxWidth = Math.min(this.cols - 4, 56);
|
|
996
|
+
// Format: ⏺ ToolName(args...)
|
|
997
|
+
const prefix = `${bullet} ${theme.info(toolName)}(`;
|
|
998
|
+
const prefixLen = toolName.length + 3; // "⏺ ToolName(" visible length
|
|
999
|
+
const indent = ' '.repeat(prefixLen + 4); // Extra indent for wrapped args
|
|
1000
|
+
// Parse and format arguments
|
|
1001
|
+
const args = this.parseToolArgs(argsStr);
|
|
1002
|
+
if (args.length === 0) {
|
|
1003
|
+
return `${prefix})\n`;
|
|
1004
|
+
}
|
|
1005
|
+
const lines = [];
|
|
1006
|
+
let currentLine = prefix;
|
|
1007
|
+
for (let i = 0; i < args.length; i++) {
|
|
1008
|
+
const arg = args[i];
|
|
1009
|
+
const argText = `${theme.ui.muted(arg.key + ':')} ${this.formatArgValue(arg.value)}`;
|
|
1010
|
+
const separator = i < args.length - 1 ? ', ' : ')';
|
|
1011
|
+
// Check if this arg fits on current line
|
|
1012
|
+
const testLine = currentLine + argText + separator;
|
|
1013
|
+
if (this.stripAnsi(testLine).length > maxWidth && currentLine !== prefix) {
|
|
1014
|
+
lines.push(currentLine.trimEnd());
|
|
1015
|
+
currentLine = indent + argText + separator;
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
currentLine += argText + separator;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (currentLine.trim()) {
|
|
1022
|
+
lines.push(currentLine.trimEnd());
|
|
1023
|
+
}
|
|
1024
|
+
return lines.join('\n') + '\n';
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Parse tool arguments from string like: key: "value", key2: value2
|
|
1028
|
+
*/
|
|
1029
|
+
parseToolArgs(argsStr) {
|
|
1030
|
+
const args = [];
|
|
1031
|
+
// Simple regex to extract key: value pairs
|
|
1032
|
+
const regex = /(\w+):\s*("(?:[^"\\]|\\.)*"|[^,\)]+)/g;
|
|
1033
|
+
let match;
|
|
1034
|
+
while ((match = regex.exec(argsStr)) !== null) {
|
|
1035
|
+
args.push({ key: match[1], value: match[2].trim() });
|
|
1036
|
+
}
|
|
1037
|
+
return args;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Format an argument value (truncate long strings)
|
|
1041
|
+
*/
|
|
1042
|
+
formatArgValue(value) {
|
|
1043
|
+
// Remove surrounding quotes if present
|
|
1044
|
+
const isQuoted = value.startsWith('"') && value.endsWith('"');
|
|
1045
|
+
const inner = isQuoted ? value.slice(1, -1) : value;
|
|
1046
|
+
// Truncate long values
|
|
1047
|
+
const maxLen = 40;
|
|
1048
|
+
const truncated = inner.length > maxLen ? inner.slice(0, maxLen - 3) + '...' : inner;
|
|
1049
|
+
return isQuoted ? `"${truncated}"` : truncated;
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Format a tool result in Claude Code style:
|
|
1053
|
+
* ⎿ Found 12 lines (ctrl+o to expand)
|
|
1054
|
+
*/
|
|
1055
|
+
formatToolResult(content) {
|
|
1056
|
+
// Check if this is a summary line (e.g., "Found X lines")
|
|
1057
|
+
const summaryMatch = content.match(/^(Found \d+ (?:lines?|files?|matches?)|Read \d+ lines?|Wrote \d+ lines?|Edited|Created|Deleted)/i);
|
|
1058
|
+
if (summaryMatch) {
|
|
1059
|
+
return ` ${theme.ui.muted('⎿')} ${content} ${theme.ui.muted('(ctrl+o to expand)')}\n`;
|
|
1060
|
+
}
|
|
1061
|
+
// For other results, show truncated preview
|
|
1062
|
+
const lines = content.split('\n');
|
|
1063
|
+
if (lines.length > 3) {
|
|
1064
|
+
const preview = lines.slice(0, 2).join('\n');
|
|
1065
|
+
return ` ${theme.ui.muted('⎿')} ${preview}\n ${theme.ui.muted(`... ${lines.length - 2} more lines (ctrl+o to expand)`)}\n`;
|
|
1066
|
+
}
|
|
1067
|
+
return ` ${theme.ui.muted('⎿')} ${content}\n`;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Format a compact tool call: ⏺ Read → file.ts
|
|
1071
|
+
*/
|
|
1072
|
+
formatCompactToolCall(content) {
|
|
1073
|
+
const bullet = '⏺';
|
|
1074
|
+
// Parse tool name and args
|
|
1075
|
+
const match = content.match(/^(\w+)\s*(?:\((.*)\))?$/s);
|
|
1076
|
+
if (!match) {
|
|
1077
|
+
return `${bullet} ${content}\n`;
|
|
1078
|
+
}
|
|
1079
|
+
const toolName = match[1];
|
|
1080
|
+
const argsStr = match[2] || '';
|
|
1081
|
+
// Extract the most important arg for summary
|
|
1082
|
+
const summary = this.extractToolSummary(toolName, argsStr);
|
|
1083
|
+
if (summary) {
|
|
1084
|
+
return `${bullet} ${theme.info(toolName)} ${theme.ui.muted('→')} ${summary}\n`;
|
|
1085
|
+
}
|
|
1086
|
+
return `${bullet} ${theme.info(toolName)}\n`;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Extract a short summary from tool args
|
|
1090
|
+
*/
|
|
1091
|
+
extractToolSummary(toolName, argsStr) {
|
|
1092
|
+
const tool = toolName.toLowerCase();
|
|
1093
|
+
// Extract path/file for file operations
|
|
1094
|
+
if (['read', 'write', 'edit', 'glob', 'grep', 'search'].includes(tool)) {
|
|
1095
|
+
const pathMatch = argsStr.match(/(?:path|file_path|pattern):\s*"([^"]+)"/);
|
|
1096
|
+
if (pathMatch) {
|
|
1097
|
+
const path = pathMatch[1];
|
|
1098
|
+
// Shorten long paths
|
|
1099
|
+
const short = path.length > 30 ? '…' + path.slice(-28) : path;
|
|
1100
|
+
return theme.ui.muted(short);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
// Extract command for bash
|
|
1104
|
+
if (tool === 'bash') {
|
|
1105
|
+
const cmdMatch = argsStr.match(/command:\s*"([^"]+)"/);
|
|
1106
|
+
if (cmdMatch) {
|
|
1107
|
+
const cmd = cmdMatch[1];
|
|
1108
|
+
const short = cmd.length > 40 ? cmd.slice(0, 37) + '…' : cmd;
|
|
1109
|
+
return theme.ui.muted(short);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Format a compact tool result: └─ 42 lines
|
|
1116
|
+
*/
|
|
1117
|
+
formatCompactToolResult(content) {
|
|
1118
|
+
// Parse common result patterns
|
|
1119
|
+
const lineMatch = content.match(/(\d+)\s*lines?/i);
|
|
1120
|
+
const fileMatch = content.match(/(\d+)\s*(?:files?|matches?)/i);
|
|
1121
|
+
const successMatch = content.match(/^(success|ok|done|completed|written|edited|created)/i);
|
|
1122
|
+
if (lineMatch) {
|
|
1123
|
+
return ` ${theme.ui.muted('└─')} ${theme.success(lineMatch[1])} ${theme.ui.muted('lines')}\n`;
|
|
1124
|
+
}
|
|
1125
|
+
if (fileMatch) {
|
|
1126
|
+
return ` ${theme.ui.muted('└─')} ${theme.success(fileMatch[1])} ${theme.ui.muted('files')}\n`;
|
|
1127
|
+
}
|
|
1128
|
+
if (successMatch) {
|
|
1129
|
+
return ` ${theme.ui.muted('└─')} ${theme.success('✓')}\n`;
|
|
1130
|
+
}
|
|
1131
|
+
// Truncate long results
|
|
1132
|
+
const short = content.length > 50 ? content.slice(0, 47) + '…' : content;
|
|
1133
|
+
return ` ${theme.ui.muted('└─')} ${theme.ui.muted(short)}\n`;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Format a compact response with bullet on first line
|
|
1137
|
+
*/
|
|
1138
|
+
formatCompactResponse(content) {
|
|
1139
|
+
const bullet = '⏺';
|
|
1140
|
+
const trimmed = content.trim();
|
|
1141
|
+
if (!trimmed)
|
|
1142
|
+
return '';
|
|
1143
|
+
// Single line responses - bullet prefix
|
|
1144
|
+
if (!trimmed.includes('\n') && trimmed.length < 80) {
|
|
1145
|
+
return `${bullet} ${trimmed}\n`;
|
|
1146
|
+
}
|
|
1147
|
+
// Multi-line: bullet on first, indent continuation
|
|
1148
|
+
const lines = trimmed.split('\n');
|
|
1149
|
+
const result = [];
|
|
1150
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1151
|
+
const line = lines[i].trimEnd();
|
|
1152
|
+
if (!line) {
|
|
1153
|
+
result.push('');
|
|
1154
|
+
}
|
|
1155
|
+
else if (i === 0) {
|
|
1156
|
+
result.push(`${bullet} ${line}`);
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
result.push(` ${line}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return result.join('\n') + '\n';
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Format streaming elapsed time in Claude Code style: 3m 30s
|
|
1166
|
+
*/
|
|
1167
|
+
formatStreamingElapsed() {
|
|
1168
|
+
if (!this.streamingStartTime)
|
|
1169
|
+
return null;
|
|
1170
|
+
const elapsed = Math.floor((Date.now() - this.streamingStartTime) / 1000);
|
|
1171
|
+
if (elapsed < 5)
|
|
1172
|
+
return null; // Don't show for very short durations
|
|
1173
|
+
const mins = Math.floor(elapsed / 60);
|
|
1174
|
+
const secs = elapsed % 60;
|
|
1175
|
+
if (mins > 0) {
|
|
1176
|
+
return `${mins}m ${secs}s`;
|
|
1177
|
+
}
|
|
1178
|
+
return `${secs}s`;
|
|
1179
|
+
}
|
|
948
1180
|
/**
|
|
949
1181
|
* Format a compact conversation block (Claude Code style)
|
|
950
1182
|
* Shows a visual separator with "history" label and ctrl+o hint
|
|
@@ -980,11 +1212,13 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
980
1212
|
setMode(mode) {
|
|
981
1213
|
const wasStreaming = this.mode === 'streaming';
|
|
982
1214
|
this.mode = mode;
|
|
983
|
-
//
|
|
1215
|
+
// Track streaming start time for elapsed display
|
|
984
1216
|
if (mode === 'streaming' && !wasStreaming) {
|
|
1217
|
+
this.streamingStartTime = Date.now();
|
|
985
1218
|
this.startSpinnerAnimation();
|
|
986
1219
|
}
|
|
987
1220
|
else if (mode === 'idle' && wasStreaming) {
|
|
1221
|
+
this.streamingStartTime = null;
|
|
988
1222
|
this.stopSpinnerAnimation();
|
|
989
1223
|
}
|
|
990
1224
|
if (wasStreaming && mode === 'idle' && !this.lastOutputEndedWithNewline) {
|
|
@@ -1204,11 +1438,14 @@ export class UnifiedUIRenderer extends EventEmitter {
|
|
|
1204
1438
|
const maxWidth = this.safeWidth();
|
|
1205
1439
|
// Simple horizontal divider - clean and reliable
|
|
1206
1440
|
const divider = theme.ui.muted('─'.repeat(Math.min(maxWidth, 56)));
|
|
1207
|
-
// Activity line (only when streaming) -
|
|
1441
|
+
// Activity line (only when streaming) - Claude Code style: ✻ Determining…
|
|
1208
1442
|
if (this.mode === 'streaming' && this.activityMessage) {
|
|
1209
|
-
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1443
|
+
// Animated sparkle like Claude Code
|
|
1444
|
+
const spinnerChars = ['✱', '✲', '✳', '✴', '✵', '✶', '✷', '✸'];
|
|
1445
|
+
const spinnerChar = spinnerChars[this.spinnerFrame % spinnerChars.length] ?? '✻';
|
|
1446
|
+
const elapsed = this.formatStreamingElapsed();
|
|
1447
|
+
const elapsedStr = elapsed ? ` ${theme.ui.muted(`· ${elapsed}`)}` : '';
|
|
1448
|
+
const activityLine = `${theme.info(spinnerChar)} ${this.activityMessage}…${elapsedStr} ${theme.ui.muted('(esc to interrupt)')}`;
|
|
1212
1449
|
lines.push(this.truncateLine(activityLine, maxWidth));
|
|
1213
1450
|
}
|
|
1214
1451
|
// Top divider
|