@wengine-ai/claude-code-router 2.0.53 → 2.0.54
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/package.json +5 -4
- package/LICENSE +0 -21
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -1193
- package/dist/cli.js.map +0 -1
- package/dist/index.html +0 -225
- package/dist/tiktoken_bg.wasm +0 -0
- package/dist/utils/activateCommand.d.ts +0 -4
- package/dist/utils/activateCommand.js +0 -24
- package/dist/utils/activateCommand.js.map +0 -1
- package/dist/utils/claudeSettings.d.ts +0 -12
- package/dist/utils/claudeSettings.js +0 -188
- package/dist/utils/claudeSettings.js.map +0 -1
- package/dist/utils/codeCommand.d.ts +0 -13
- package/dist/utils/codeCommand.js +0 -88
- package/dist/utils/codeCommand.js.map +0 -1
- package/dist/utils/createEnvVariables.d.ts +0 -5
- package/dist/utils/createEnvVariables.js +0 -83
- package/dist/utils/createEnvVariables.js.map +0 -1
- package/dist/utils/index.d.ts +0 -15
- package/dist/utils/index.js +0 -271
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/installCommand.d.ts +0 -9
- package/dist/utils/installCommand.js +0 -47
- package/dist/utils/installCommand.js.map +0 -1
- package/dist/utils/modelSelector.d.ts +0 -1
- package/dist/utils/modelSelector.js +0 -402
- package/dist/utils/modelSelector.js.map +0 -1
- package/dist/utils/preset/commands.d.ts +0 -8
- package/dist/utils/preset/commands.js +0 -267
- package/dist/utils/preset/commands.js.map +0 -1
- package/dist/utils/preset/export.d.ts +0 -11
- package/dist/utils/preset/export.js +0 -97
- package/dist/utils/preset/export.js.map +0 -1
- package/dist/utils/preset/index.d.ts +0 -8
- package/dist/utils/preset/index.js +0 -32
- package/dist/utils/preset/index.js.map +0 -1
- package/dist/utils/preset/install-github.d.ts +0 -13
- package/dist/utils/preset/install-github.js +0 -161
- package/dist/utils/preset/install-github.js.map +0 -1
- package/dist/utils/preset/install.d.ts +0 -19
- package/dist/utils/preset/install.js +0 -239
- package/dist/utils/preset/install.js.map +0 -1
- package/dist/utils/processCheck.d.ts +0 -17
- package/dist/utils/processCheck.js +0 -161
- package/dist/utils/processCheck.js.map +0 -1
- package/dist/utils/prompt/schema-input.d.ts +0 -26
- package/dist/utils/prompt/schema-input.js +0 -183
- package/dist/utils/prompt/schema-input.js.map +0 -1
- package/dist/utils/status.d.ts +0 -1
- package/dist/utils/status.js +0 -28
- package/dist/utils/status.js.map +0 -1
- package/dist/utils/statusline.d.ts +0 -61
- package/dist/utils/statusline.js +0 -924
- package/dist/utils/statusline.js.map +0 -1
- package/dist/utils/update.d.ts +0 -18
- package/dist/utils/update.js +0 -74
- package/dist/utils/update.js.map +0 -1
package/dist/utils/statusline.js
DELETED
|
@@ -1,924 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.parseStatusLineData = parseStatusLineData;
|
|
7
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const child_process_1 = require("child_process");
|
|
10
|
-
const node_os_1 = require("node:os");
|
|
11
|
-
const claude_code_router_shared_1 = require("@wengine-ai/claude-code-router-shared");
|
|
12
|
-
const json5_1 = __importDefault(require("json5"));
|
|
13
|
-
// ANSI Color codes
|
|
14
|
-
const COLORS = {
|
|
15
|
-
reset: "\x1b[0m",
|
|
16
|
-
bold: "\x1b[1m",
|
|
17
|
-
dim: "\x1b[2m",
|
|
18
|
-
// Standard colors
|
|
19
|
-
black: "\x1b[30m",
|
|
20
|
-
red: "\x1b[31m",
|
|
21
|
-
green: "\x1b[32m",
|
|
22
|
-
yellow: "\x1b[33m",
|
|
23
|
-
blue: "\x1b[34m",
|
|
24
|
-
magenta: "\x1b[35m",
|
|
25
|
-
cyan: "\x1b[36m",
|
|
26
|
-
white: "\x1b[37m",
|
|
27
|
-
// Bright colors
|
|
28
|
-
bright_black: "\x1b[90m",
|
|
29
|
-
bright_red: "\x1b[91m",
|
|
30
|
-
bright_green: "\x1b[92m",
|
|
31
|
-
bright_yellow: "\x1b[93m",
|
|
32
|
-
bright_blue: "\x1b[94m",
|
|
33
|
-
bright_magenta: "\x1b[95m",
|
|
34
|
-
bright_cyan: "\x1b[96m",
|
|
35
|
-
bright_white: "\x1b[97m",
|
|
36
|
-
// Background colors
|
|
37
|
-
bg_black: "\x1b[40m",
|
|
38
|
-
bg_red: "\x1b[41m",
|
|
39
|
-
bg_green: "\x1b[42m",
|
|
40
|
-
bg_yellow: "\x1b[43m",
|
|
41
|
-
bg_blue: "\x1b[44m",
|
|
42
|
-
bg_magenta: "\x1b[45m",
|
|
43
|
-
bg_cyan: "\x1b[46m",
|
|
44
|
-
bg_white: "\x1b[47m",
|
|
45
|
-
// Bright background colors
|
|
46
|
-
bg_bright_black: "\x1b[100m",
|
|
47
|
-
bg_bright_red: "\x1b[101m",
|
|
48
|
-
bg_bright_green: "\x1b[102m",
|
|
49
|
-
bg_bright_yellow: "\x1b[103m",
|
|
50
|
-
bg_bright_blue: "\x1b[104m",
|
|
51
|
-
bg_bright_magenta: "\x1b[105m",
|
|
52
|
-
bg_bright_cyan: "\x1b[106m",
|
|
53
|
-
bg_bright_white: "\x1b[107m",
|
|
54
|
-
};
|
|
55
|
-
// Use TrueColor (24-bit color) to support hexadecimal colors
|
|
56
|
-
const TRUE_COLOR_PREFIX = "\x1b[38;2;";
|
|
57
|
-
const TRUE_COLOR_BG_PREFIX = "\x1b[48;2;";
|
|
58
|
-
// Convert hexadecimal color to RGB format
|
|
59
|
-
function hexToRgb(hex) {
|
|
60
|
-
// Remove # and spaces
|
|
61
|
-
hex = hex.replace(/^#/, '').trim();
|
|
62
|
-
// Handle shorthand form (#RGB -> #RRGGBB)
|
|
63
|
-
if (hex.length === 3) {
|
|
64
|
-
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
65
|
-
}
|
|
66
|
-
if (hex.length !== 6) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
const r = parseInt(hex.substring(0, 2), 16);
|
|
70
|
-
const g = parseInt(hex.substring(2, 4), 16);
|
|
71
|
-
const b = parseInt(hex.substring(4, 6), 16);
|
|
72
|
-
// Validate RGB values
|
|
73
|
-
if (isNaN(r) || isNaN(g) || isNaN(b) || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
return { r, g, b };
|
|
77
|
-
}
|
|
78
|
-
// Get color code
|
|
79
|
-
function getColorCode(colorName) {
|
|
80
|
-
// Check if it's a named ANSI color from COLORS dictionary
|
|
81
|
-
if (COLORS[colorName]) {
|
|
82
|
-
return COLORS[colorName];
|
|
83
|
-
}
|
|
84
|
-
// Check if it's a hexadecimal color
|
|
85
|
-
if (colorName.startsWith('#') || /^[0-9a-fA-F]{6}$/.test(colorName) || /^[0-9a-fA-F]{3}$/.test(colorName)) {
|
|
86
|
-
const rgb = hexToRgb(colorName);
|
|
87
|
-
if (rgb) {
|
|
88
|
-
return `${TRUE_COLOR_PREFIX}${rgb.r};${rgb.g};${rgb.b}m`;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// Default to empty string
|
|
92
|
-
return "";
|
|
93
|
-
}
|
|
94
|
-
// Variable replacement function, supports {{var}} format variable replacement
|
|
95
|
-
function replaceVariables(text, variables) {
|
|
96
|
-
return text.replace(/\{\{(\w+)\}\}/g, (_match, varName) => {
|
|
97
|
-
return variables[varName] || "";
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
// Execute script and get output
|
|
101
|
-
async function executeScript(scriptPath, variables, options) {
|
|
102
|
-
try {
|
|
103
|
-
// Check if file exists
|
|
104
|
-
await promises_1.default.access(scriptPath);
|
|
105
|
-
// Use require to dynamically load script module
|
|
106
|
-
const scriptModule = require(scriptPath);
|
|
107
|
-
// If export is a function, call it with variables
|
|
108
|
-
if (typeof scriptModule === 'function') {
|
|
109
|
-
const result = scriptModule(variables, options);
|
|
110
|
-
// If returns a Promise, wait for it to complete
|
|
111
|
-
if (result instanceof Promise) {
|
|
112
|
-
return await result;
|
|
113
|
-
}
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
// If export is a default function, call it
|
|
117
|
-
if (scriptModule.default && typeof scriptModule.default === 'function') {
|
|
118
|
-
const result = scriptModule.default(variables);
|
|
119
|
-
// If returns a Promise, wait for it to complete
|
|
120
|
-
if (result instanceof Promise) {
|
|
121
|
-
return await result;
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
// If export is a string, return directly
|
|
126
|
-
if (typeof scriptModule === 'string') {
|
|
127
|
-
return scriptModule;
|
|
128
|
-
}
|
|
129
|
-
// If export is a default string, return it
|
|
130
|
-
if (scriptModule.default && typeof scriptModule.default === 'string') {
|
|
131
|
-
return scriptModule.default;
|
|
132
|
-
}
|
|
133
|
-
// Default to empty string
|
|
134
|
-
return "";
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
console.error(`Error executing script ${scriptPath}:`, error);
|
|
138
|
-
return "";
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Default theme configuration - using Nerd Fonts icons and beautiful color scheme
|
|
142
|
-
const DEFAULT_THEME = {
|
|
143
|
-
modules: [
|
|
144
|
-
{
|
|
145
|
-
type: "workDir",
|
|
146
|
-
icon: "", // nf-md-folder_outline
|
|
147
|
-
text: "{{workDirName}}",
|
|
148
|
-
color: "bright_blue"
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
type: "gitBranch",
|
|
152
|
-
icon: "", // nf-dev-git_branch
|
|
153
|
-
text: "{{gitBranch}}",
|
|
154
|
-
color: "bright_magenta"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
type: "model",
|
|
158
|
-
icon: "", // nf-md-robot_outline
|
|
159
|
-
text: "{{model}}",
|
|
160
|
-
color: "bright_cyan"
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
type: "contextCircle",
|
|
164
|
-
icon: "○",
|
|
165
|
-
text: "{{contextPercent}}%",
|
|
166
|
-
color: "#22c55e"
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
type: "usage",
|
|
170
|
-
icon: "↑", // Up arrow
|
|
171
|
-
text: "{{inputTokens}}",
|
|
172
|
-
color: "bright_green"
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
type: "usage",
|
|
176
|
-
icon: "↓", // Down arrow
|
|
177
|
-
text: "{{outputTokens}}",
|
|
178
|
-
color: "bright_yellow"
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
type: "totalTokens",
|
|
182
|
-
icon: "📋",
|
|
183
|
-
text: "{{totalTokens}}",
|
|
184
|
-
color: "bright_white"
|
|
185
|
-
}
|
|
186
|
-
]
|
|
187
|
-
};
|
|
188
|
-
// Powerline style theme configuration
|
|
189
|
-
const POWERLINE_THEME = {
|
|
190
|
-
modules: [
|
|
191
|
-
{
|
|
192
|
-
type: "workDir",
|
|
193
|
-
icon: "", // nf-md-folder_outline
|
|
194
|
-
text: "{{workDirName}}",
|
|
195
|
-
color: "white",
|
|
196
|
-
background: "bg_bright_blue"
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
type: "gitBranch",
|
|
200
|
-
icon: "", // nf-dev-git_branch
|
|
201
|
-
text: "{{gitBranch}}",
|
|
202
|
-
color: "white",
|
|
203
|
-
background: "bg_bright_magenta"
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
type: "model",
|
|
207
|
-
icon: "", // nf-md-robot_outline
|
|
208
|
-
text: "{{model}}",
|
|
209
|
-
color: "white",
|
|
210
|
-
background: "bg_bright_cyan"
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
type: "contextCircle",
|
|
214
|
-
icon: "○",
|
|
215
|
-
text: "{{contextPercent}}%",
|
|
216
|
-
color: "#22c55e",
|
|
217
|
-
background: "bg_bright_black"
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
type: "usage",
|
|
221
|
-
icon: "↑", // Up arrow
|
|
222
|
-
text: "{{inputTokens}}",
|
|
223
|
-
color: "white",
|
|
224
|
-
background: "bg_bright_green"
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
type: "usage",
|
|
228
|
-
icon: "↓", // Down arrow
|
|
229
|
-
text: "{{outputTokens}}",
|
|
230
|
-
color: "white",
|
|
231
|
-
background: "bg_bright_yellow"
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
type: "totalTokens",
|
|
235
|
-
icon: "📋",
|
|
236
|
-
text: "{{totalTokens}}",
|
|
237
|
-
color: "white",
|
|
238
|
-
background: "bg_bright_white"
|
|
239
|
-
}
|
|
240
|
-
]
|
|
241
|
-
};
|
|
242
|
-
// Simple text theme configuration - fallback for when icons cannot be displayed
|
|
243
|
-
const SIMPLE_THEME = {
|
|
244
|
-
modules: [
|
|
245
|
-
{
|
|
246
|
-
type: "workDir",
|
|
247
|
-
icon: "",
|
|
248
|
-
text: "{{workDirName}}",
|
|
249
|
-
color: "bright_blue"
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
type: "gitBranch",
|
|
253
|
-
icon: "",
|
|
254
|
-
text: "{{gitBranch}}",
|
|
255
|
-
color: "bright_magenta"
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
type: "model",
|
|
259
|
-
icon: "",
|
|
260
|
-
text: "{{model}}",
|
|
261
|
-
color: "bright_cyan"
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
type: "contextCircle",
|
|
265
|
-
icon: "○",
|
|
266
|
-
text: "{{contextPercent}}%",
|
|
267
|
-
color: "#22c55e"
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
type: "usage",
|
|
271
|
-
icon: "↑",
|
|
272
|
-
text: "{{inputTokens}}",
|
|
273
|
-
color: "bright_green"
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
type: "usage",
|
|
277
|
-
icon: "↓",
|
|
278
|
-
text: "{{outputTokens}}",
|
|
279
|
-
color: "bright_yellow"
|
|
280
|
-
}
|
|
281
|
-
]
|
|
282
|
-
};
|
|
283
|
-
// Full theme configuration - showcasing all available modules
|
|
284
|
-
const FULL_THEME = {
|
|
285
|
-
modules: [
|
|
286
|
-
{
|
|
287
|
-
type: "workDir",
|
|
288
|
-
icon: "",
|
|
289
|
-
text: "{{workDirName}}",
|
|
290
|
-
color: "bright_blue"
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
type: "gitBranch",
|
|
294
|
-
icon: "",
|
|
295
|
-
text: "{{gitBranch}}",
|
|
296
|
-
color: "bright_magenta"
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
type: "model",
|
|
300
|
-
icon: "",
|
|
301
|
-
text: "{{model}}",
|
|
302
|
-
color: "bright_cyan"
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
type: "contextCircle",
|
|
306
|
-
icon: "○",
|
|
307
|
-
text: "{{contextPercent}}%",
|
|
308
|
-
color: "#22c55e"
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
type: "context",
|
|
312
|
-
icon: "🪟",
|
|
313
|
-
text: "{{contextPercent}}% / {{contextWindowSize}}",
|
|
314
|
-
color: "bright_green"
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
type: "speed",
|
|
318
|
-
icon: "⚡",
|
|
319
|
-
text: "{{tokenSpeed}} t/s {{isStreaming}}",
|
|
320
|
-
color: "bright_yellow"
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
type: "cost",
|
|
324
|
-
icon: "💰",
|
|
325
|
-
text: "{{cost}}",
|
|
326
|
-
color: "bright_magenta"
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
type: "duration",
|
|
330
|
-
icon: "⏱️",
|
|
331
|
-
text: "{{duration}}",
|
|
332
|
-
color: "bright_white"
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
type: "lines",
|
|
336
|
-
icon: "📝",
|
|
337
|
-
text: "+{{linesAdded}}/-{{linesRemoved}}",
|
|
338
|
-
color: "bright_cyan"
|
|
339
|
-
}
|
|
340
|
-
]
|
|
341
|
-
};
|
|
342
|
-
// Format token count with auto unit: k -> m -> b
|
|
343
|
-
function formatTokenCount(count) {
|
|
344
|
-
if (count < 1000) {
|
|
345
|
-
return `${count}`;
|
|
346
|
-
}
|
|
347
|
-
if (count < 1_000_000) {
|
|
348
|
-
const val = count / 1000;
|
|
349
|
-
return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}k`;
|
|
350
|
-
}
|
|
351
|
-
if (count < 1_000_000_000) {
|
|
352
|
-
const val = count / 1_000_000;
|
|
353
|
-
return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}m`;
|
|
354
|
-
}
|
|
355
|
-
const val = count / 1_000_000_000;
|
|
356
|
-
return `${val % 1 === 0 ? val.toFixed(0) : val.toFixed(1)}b`;
|
|
357
|
-
}
|
|
358
|
-
// Format usage information with auto unit
|
|
359
|
-
function formatUsage(input_tokens, output_tokens) {
|
|
360
|
-
return `${formatTokenCount(input_tokens)} ${formatTokenCount(output_tokens)}`;
|
|
361
|
-
}
|
|
362
|
-
// Calculate context window usage percentage
|
|
363
|
-
function calculateContextPercent(context_window) {
|
|
364
|
-
if (!context_window || !context_window.current_usage) {
|
|
365
|
-
return 0;
|
|
366
|
-
}
|
|
367
|
-
const { current_usage, context_window_size } = context_window;
|
|
368
|
-
const currentTokens = current_usage.input_tokens +
|
|
369
|
-
current_usage.cache_creation_input_tokens +
|
|
370
|
-
current_usage.cache_read_input_tokens;
|
|
371
|
-
return Math.round((currentTokens / context_window_size) * 100);
|
|
372
|
-
}
|
|
373
|
-
function getContextUsageColor(contextPercent) {
|
|
374
|
-
const percent = parseInt(contextPercent || "0", 10);
|
|
375
|
-
if (percent > 75) {
|
|
376
|
-
return "#ef4444";
|
|
377
|
-
}
|
|
378
|
-
if (percent > 50) {
|
|
379
|
-
return "#eab308";
|
|
380
|
-
}
|
|
381
|
-
return "#22c55e";
|
|
382
|
-
}
|
|
383
|
-
// Format cost display
|
|
384
|
-
function formatCost(cost_usd) {
|
|
385
|
-
if (cost_usd < 0.01) {
|
|
386
|
-
return `${(cost_usd * 100).toFixed(2)}¢`;
|
|
387
|
-
}
|
|
388
|
-
return `$${cost_usd.toFixed(2)}`;
|
|
389
|
-
}
|
|
390
|
-
// Format duration
|
|
391
|
-
function formatDuration(ms) {
|
|
392
|
-
if (Number.isNaN(ms)) {
|
|
393
|
-
return '';
|
|
394
|
-
}
|
|
395
|
-
if (ms < 1000) {
|
|
396
|
-
return `${ms}ms`;
|
|
397
|
-
}
|
|
398
|
-
else if (ms < 60000) {
|
|
399
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
const minutes = Math.floor(ms / 60000);
|
|
403
|
-
const seconds = ((ms % 60000) / 1000).toFixed(0);
|
|
404
|
-
if (Number.isNaN(minutes) || Number.isNaN(seconds)) {
|
|
405
|
-
return '';
|
|
406
|
-
}
|
|
407
|
-
return `${minutes}m${seconds}s`;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// Read token-speed statistics from temp file
|
|
411
|
-
async function getTokenSpeedStats(sessionId) {
|
|
412
|
-
try {
|
|
413
|
-
// Use system temp directory
|
|
414
|
-
const tempDir = node_path_1.default.join((0, node_os_1.tmpdir)(), 'claude-code-router');
|
|
415
|
-
// Check if temp directory exists
|
|
416
|
-
try {
|
|
417
|
-
await promises_1.default.access(tempDir);
|
|
418
|
-
}
|
|
419
|
-
catch {
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
const statsFilePath = node_path_1.default.join(tempDir, `session-${sessionId}.json`);
|
|
423
|
-
try {
|
|
424
|
-
await promises_1.default.access(statsFilePath);
|
|
425
|
-
}
|
|
426
|
-
catch {
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
|
-
// Read stats file
|
|
430
|
-
const content = await promises_1.default.readFile(statsFilePath, 'utf-8');
|
|
431
|
-
const data = JSON.parse(content);
|
|
432
|
-
// Check if data has tokensPerSecond
|
|
433
|
-
if (data.tokensPerSecond !== undefined && data.tokensPerSecond > 0) {
|
|
434
|
-
// Check if timestamp is within last 3 seconds
|
|
435
|
-
const now = Date.now();
|
|
436
|
-
const timestamp = data.timestamp || 0;
|
|
437
|
-
const ageInSeconds = (now - timestamp) / 1000;
|
|
438
|
-
// If data is older than 3 seconds, return 0 speed
|
|
439
|
-
if (ageInSeconds > 3) {
|
|
440
|
-
return {
|
|
441
|
-
tokensPerSecond: 0,
|
|
442
|
-
timeToFirstToken: data.timeToFirstToken
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
const result = {
|
|
446
|
-
tokensPerSecond: parseInt(data.tokensPerSecond),
|
|
447
|
-
timeToFirstToken: data.timeToFirstToken
|
|
448
|
-
};
|
|
449
|
-
return result;
|
|
450
|
-
}
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
catch (error) {
|
|
454
|
-
// Silently fail on error
|
|
455
|
-
return null;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
// Read theme configuration from user home directory
|
|
459
|
-
async function getProjectThemeConfig() {
|
|
460
|
-
try {
|
|
461
|
-
// Only use fixed configuration file in home directory
|
|
462
|
-
const configPath = claude_code_router_shared_1.CONFIG_FILE;
|
|
463
|
-
// Check if configuration file exists
|
|
464
|
-
try {
|
|
465
|
-
await promises_1.default.access(configPath);
|
|
466
|
-
}
|
|
467
|
-
catch {
|
|
468
|
-
return { theme: null, style: 'default' };
|
|
469
|
-
}
|
|
470
|
-
const configContent = await promises_1.default.readFile(configPath, "utf-8");
|
|
471
|
-
const config = json5_1.default.parse(configContent);
|
|
472
|
-
// Check if there's StatusLine configuration
|
|
473
|
-
if (config.StatusLine) {
|
|
474
|
-
// Get current style, default to 'default'
|
|
475
|
-
const currentStyle = config.StatusLine.currentStyle || 'default';
|
|
476
|
-
// Check if there's configuration for the corresponding style
|
|
477
|
-
if (config.StatusLine[currentStyle] && config.StatusLine[currentStyle].modules) {
|
|
478
|
-
return { theme: config.StatusLine[currentStyle], style: currentStyle };
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
catch (error) {
|
|
483
|
-
// Return null if reading fails
|
|
484
|
-
// console.error("Failed to read theme config:", error);
|
|
485
|
-
}
|
|
486
|
-
return { theme: null, style: 'default' };
|
|
487
|
-
}
|
|
488
|
-
// Read theme configuration from preset
|
|
489
|
-
async function getPresetThemeConfig(presetName) {
|
|
490
|
-
try {
|
|
491
|
-
// Read preset manifest
|
|
492
|
-
const manifest = await (0, claude_code_router_shared_1.readPresetFile)(presetName);
|
|
493
|
-
if (!manifest) {
|
|
494
|
-
return { theme: null, style: 'default' };
|
|
495
|
-
}
|
|
496
|
-
// Load preset configuration (applies userValues if present)
|
|
497
|
-
const presetDir = (0, claude_code_router_shared_1.getPresetDir)(presetName);
|
|
498
|
-
const config = (0, claude_code_router_shared_1.loadConfigFromManifest)(manifest, presetDir);
|
|
499
|
-
// Check if there's StatusLine configuration in preset
|
|
500
|
-
if (config.StatusLine) {
|
|
501
|
-
// Get current style, default to 'default'
|
|
502
|
-
const currentStyle = config.StatusLine.currentStyle || 'default';
|
|
503
|
-
// Check if there's configuration for the corresponding style
|
|
504
|
-
if (config.StatusLine[currentStyle] && config.StatusLine[currentStyle].modules) {
|
|
505
|
-
return { theme: config.StatusLine[currentStyle], style: currentStyle };
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
catch (error) {
|
|
510
|
-
// Return null if reading fails
|
|
511
|
-
// console.error("Failed to read preset theme config:", error);
|
|
512
|
-
}
|
|
513
|
-
return { theme: null, style: 'default' };
|
|
514
|
-
}
|
|
515
|
-
// Check if simple theme should be used (fallback scheme)
|
|
516
|
-
// When environment variable USE_SIMPLE_ICONS is set, or when a terminal that might not support Nerd Fonts is detected
|
|
517
|
-
function shouldUseSimpleTheme() {
|
|
518
|
-
// Check environment variable
|
|
519
|
-
if (process.env.USE_SIMPLE_ICONS === 'true') {
|
|
520
|
-
return true;
|
|
521
|
-
}
|
|
522
|
-
// Check terminal type (some common terminals that don't support complex icons)
|
|
523
|
-
const term = process.env.TERM || '';
|
|
524
|
-
const unsupportedTerms = ['dumb', 'unknown'];
|
|
525
|
-
if (unsupportedTerms.includes(term)) {
|
|
526
|
-
return true;
|
|
527
|
-
}
|
|
528
|
-
// By default, assume terminal supports Nerd Fonts
|
|
529
|
-
return false;
|
|
530
|
-
}
|
|
531
|
-
// Check if Nerd Fonts icons can be displayed correctly
|
|
532
|
-
// By checking terminal font information or using heuristic methods
|
|
533
|
-
function canDisplayNerdFonts() {
|
|
534
|
-
// If environment variable explicitly specifies simple icons, Nerd Fonts cannot be displayed
|
|
535
|
-
if (process.env.USE_SIMPLE_ICONS === 'true') {
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
// Check some common terminal environment variables that support Nerd Fonts
|
|
539
|
-
const fontEnvVars = ['NERD_FONT', 'NERDFONT', 'FONT'];
|
|
540
|
-
for (const envVar of fontEnvVars) {
|
|
541
|
-
const value = process.env[envVar];
|
|
542
|
-
if (value && (value.includes('Nerd') || value.includes('nerd'))) {
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
// Check terminal type
|
|
547
|
-
const termProgram = process.env.TERM_PROGRAM || '';
|
|
548
|
-
const supportedTerminals = ['iTerm.app', 'vscode', 'Hyper', 'kitty', 'alacritty'];
|
|
549
|
-
if (supportedTerminals.includes(termProgram)) {
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
// Check COLORTERM environment variable
|
|
553
|
-
const colorTerm = process.env.COLORTERM || '';
|
|
554
|
-
if (colorTerm.includes('truecolor') || colorTerm.includes('24bit')) {
|
|
555
|
-
return true;
|
|
556
|
-
}
|
|
557
|
-
// By default, assume Nerd Fonts can be displayed (but allow users to override via environment variables)
|
|
558
|
-
return process.env.USE_SIMPLE_ICONS !== 'true';
|
|
559
|
-
}
|
|
560
|
-
async function parseStatusLineData(input, presetName) {
|
|
561
|
-
try {
|
|
562
|
-
// Check if simple theme should be used
|
|
563
|
-
const useSimpleTheme = shouldUseSimpleTheme();
|
|
564
|
-
// Check if Nerd Fonts icons can be displayed
|
|
565
|
-
const canDisplayNerd = canDisplayNerdFonts();
|
|
566
|
-
// Determine which theme to use: use simple theme if user forces it or Nerd Fonts cannot be displayed
|
|
567
|
-
const effectiveTheme = useSimpleTheme || !canDisplayNerd ? SIMPLE_THEME : DEFAULT_THEME;
|
|
568
|
-
// Get theme configuration: preset config > home directory config > default theme
|
|
569
|
-
let projectTheme = null;
|
|
570
|
-
let currentStyle = 'default';
|
|
571
|
-
if (presetName) {
|
|
572
|
-
// Try to get theme configuration from preset first
|
|
573
|
-
const presetConfig = await getPresetThemeConfig(presetName);
|
|
574
|
-
projectTheme = presetConfig.theme;
|
|
575
|
-
currentStyle = presetConfig.style;
|
|
576
|
-
}
|
|
577
|
-
// If preset theme not found or no preset specified, try home directory config
|
|
578
|
-
if (!projectTheme) {
|
|
579
|
-
const homeConfig = await getProjectThemeConfig();
|
|
580
|
-
projectTheme = homeConfig.theme;
|
|
581
|
-
currentStyle = homeConfig.style;
|
|
582
|
-
}
|
|
583
|
-
const theme = projectTheme || effectiveTheme;
|
|
584
|
-
// Get current working directory and Git branch
|
|
585
|
-
const workDir = input.workspace.current_dir;
|
|
586
|
-
let gitBranch = "";
|
|
587
|
-
try {
|
|
588
|
-
// Try to get Git branch name
|
|
589
|
-
gitBranch = (0, child_process_1.execSync)("git branch --show-current", {
|
|
590
|
-
cwd: workDir,
|
|
591
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
592
|
-
})
|
|
593
|
-
.toString()
|
|
594
|
-
.trim();
|
|
595
|
-
}
|
|
596
|
-
catch (error) {
|
|
597
|
-
// If not a Git repository or retrieval fails, ignore error
|
|
598
|
-
}
|
|
599
|
-
// Read last assistant message from transcript_path file
|
|
600
|
-
const transcriptContent = await promises_1.default.readFile(input.transcript_path, "utf-8");
|
|
601
|
-
const lines = transcriptContent.trim().split("\n");
|
|
602
|
-
// Traverse in reverse to find last assistant message
|
|
603
|
-
let model = "";
|
|
604
|
-
let inputTokens = 0;
|
|
605
|
-
let outputTokens = 0;
|
|
606
|
-
// Also accumulate total tokens from all assistant messages
|
|
607
|
-
let sessionTotalInputTokens = 0;
|
|
608
|
-
let sessionTotalOutputTokens = 0;
|
|
609
|
-
let sessionTotalCacheCreationTokens = 0;
|
|
610
|
-
let sessionTotalCacheReadTokens = 0;
|
|
611
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
612
|
-
try {
|
|
613
|
-
const message = JSON.parse(lines[i]);
|
|
614
|
-
if (message.type === "assistant" && message.message.model) {
|
|
615
|
-
// Accumulate tokens for session total
|
|
616
|
-
if (message.message.usage) {
|
|
617
|
-
sessionTotalInputTokens += message.message.usage.input_tokens;
|
|
618
|
-
sessionTotalOutputTokens += message.message.usage.output_tokens;
|
|
619
|
-
sessionTotalCacheCreationTokens += message.message.usage.cache_creation_input_tokens || 0;
|
|
620
|
-
sessionTotalCacheReadTokens += message.message.usage.cache_read_input_tokens || 0;
|
|
621
|
-
}
|
|
622
|
-
// Get last message's model and tokens
|
|
623
|
-
if (!model) {
|
|
624
|
-
model = message.message.model;
|
|
625
|
-
if (message.message.usage) {
|
|
626
|
-
inputTokens = message.message.usage.input_tokens;
|
|
627
|
-
outputTokens = message.message.usage.output_tokens;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
catch (parseError) {
|
|
633
|
-
// Ignore parse errors, continue searching
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
// If model name not retrieved from transcript, try to get from configuration file
|
|
638
|
-
if (!model) {
|
|
639
|
-
try {
|
|
640
|
-
// Get project configuration file path
|
|
641
|
-
const projectConfigPath = node_path_1.default.join(workDir, ".claude-code-router", "config.json");
|
|
642
|
-
let configPath = projectConfigPath;
|
|
643
|
-
// Check if project configuration file exists, if not use user home directory configuration file
|
|
644
|
-
try {
|
|
645
|
-
await promises_1.default.access(projectConfigPath);
|
|
646
|
-
}
|
|
647
|
-
catch {
|
|
648
|
-
configPath = claude_code_router_shared_1.CONFIG_FILE;
|
|
649
|
-
}
|
|
650
|
-
// Read configuration file
|
|
651
|
-
const configContent = await promises_1.default.readFile(configPath, "utf-8");
|
|
652
|
-
const config = json5_1.default.parse(configContent);
|
|
653
|
-
// Get model name from Router field's default content
|
|
654
|
-
if (config.Router && config.Router.default) {
|
|
655
|
-
const [, defaultModel] = config.Router.default.split(",");
|
|
656
|
-
if (defaultModel) {
|
|
657
|
-
model = defaultModel.trim();
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
catch (configError) {
|
|
662
|
-
// If configuration file reading fails, ignore error
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
// If still unable to get model name, use display_name from input JSON data's model field
|
|
666
|
-
if (!model) {
|
|
667
|
-
model = input.model.display_name;
|
|
668
|
-
}
|
|
669
|
-
// Get working directory name
|
|
670
|
-
const workDirName = workDir.split("/").pop() || "";
|
|
671
|
-
// Format usage information
|
|
672
|
-
const usage = formatUsage(inputTokens, outputTokens);
|
|
673
|
-
const [formattedInputTokens, formattedOutputTokens] = usage.split(" ");
|
|
674
|
-
// Get token-speed statistics
|
|
675
|
-
const tokenSpeedData = await getTokenSpeedStats(input.session_id);
|
|
676
|
-
const formattedTokenSpeed = tokenSpeedData && tokenSpeedData.tokensPerSecond > 0
|
|
677
|
-
? tokenSpeedData.tokensPerSecond.toString()
|
|
678
|
-
: '';
|
|
679
|
-
// Check if streaming (has active token speed)
|
|
680
|
-
const isStreaming = tokenSpeedData !== null && tokenSpeedData.tokensPerSecond > 0;
|
|
681
|
-
const streamingIndicator = isStreaming ? '[Streaming]' : '';
|
|
682
|
-
// Format time to first token
|
|
683
|
-
let formattedTimeToFirstToken = '';
|
|
684
|
-
if (tokenSpeedData?.timeToFirstToken !== undefined) {
|
|
685
|
-
formattedTimeToFirstToken = formatDuration(tokenSpeedData.timeToFirstToken);
|
|
686
|
-
}
|
|
687
|
-
// Process context window data
|
|
688
|
-
const contextPercent = input.context_window ? calculateContextPercent(input.context_window) : 0;
|
|
689
|
-
// Always use transcript-accumulated values for stable monotonically-increasing totals
|
|
690
|
-
// Fallback to context_window only when transcript has no data
|
|
691
|
-
const totalInputTokens = sessionTotalInputTokens || input.context_window?.total_input_tokens || 0;
|
|
692
|
-
const totalOutputTokens = sessionTotalOutputTokens || input.context_window?.total_output_tokens || 0;
|
|
693
|
-
const totalCacheTokens = sessionTotalCacheCreationTokens + sessionTotalCacheReadTokens;
|
|
694
|
-
const contextWindowSize = input.context_window?.context_window_size || 0;
|
|
695
|
-
// Process cost data
|
|
696
|
-
const totalCost = input.cost?.total_cost_usd || 0;
|
|
697
|
-
const formattedCost = totalCost > 0 ? formatCost(totalCost) : '';
|
|
698
|
-
const totalDuration = input.cost?.total_duration_ms || 0;
|
|
699
|
-
const formattedDuration = totalDuration > 0 ? formatDuration(totalDuration) : '';
|
|
700
|
-
const linesAdded = input.cost?.total_lines_added || 0;
|
|
701
|
-
const linesRemoved = input.cost?.total_lines_removed || 0;
|
|
702
|
-
// Define variable replacement mapping
|
|
703
|
-
const variables = {
|
|
704
|
-
workDirName,
|
|
705
|
-
gitBranch,
|
|
706
|
-
model,
|
|
707
|
-
inputTokens: formattedInputTokens,
|
|
708
|
-
outputTokens: formattedOutputTokens,
|
|
709
|
-
tokenSpeed: formattedTokenSpeed || '0',
|
|
710
|
-
isStreaming: isStreaming ? 'streaming' : '',
|
|
711
|
-
timeToFirstToken: formattedTimeToFirstToken,
|
|
712
|
-
contextPercent: contextPercent.toString(),
|
|
713
|
-
streamingIndicator,
|
|
714
|
-
contextWindowSize: formatTokenCount(contextWindowSize),
|
|
715
|
-
totalInputTokens: formatTokenCount(totalInputTokens),
|
|
716
|
-
totalOutputTokens: formatTokenCount(totalOutputTokens),
|
|
717
|
-
totalTokens: formatTokenCount(totalInputTokens + totalOutputTokens + totalCacheTokens),
|
|
718
|
-
cost: formattedCost || '',
|
|
719
|
-
duration: formattedDuration || '',
|
|
720
|
-
linesAdded: linesAdded.toString(),
|
|
721
|
-
linesRemoved: linesRemoved.toString(),
|
|
722
|
-
netLines: (linesAdded - linesRemoved).toString(),
|
|
723
|
-
version: input.version || '',
|
|
724
|
-
sessionId: input.session_id.substring(0, 8)
|
|
725
|
-
};
|
|
726
|
-
// Determine the style to use
|
|
727
|
-
const isPowerline = currentStyle === 'powerline';
|
|
728
|
-
// Render status line based on style
|
|
729
|
-
if (isPowerline) {
|
|
730
|
-
return await renderPowerlineStyle(theme, variables);
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
return await renderDefaultStyle(theme, variables);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
catch (error) {
|
|
737
|
-
// Return empty string on error
|
|
738
|
-
return "";
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
// Render default style status line
|
|
742
|
-
async function renderDefaultStyle(theme, variables) {
|
|
743
|
-
const modules = theme.modules || DEFAULT_THEME.modules;
|
|
744
|
-
const parts = [];
|
|
745
|
-
// Iterate through module array, rendering each module (maximum 10)
|
|
746
|
-
for (let i = 0; i < modules.length; i++) {
|
|
747
|
-
const module = modules[i];
|
|
748
|
-
const dynamicColor = module.type === "contextCircle"
|
|
749
|
-
? getContextUsageColor(variables.contextPercent)
|
|
750
|
-
: module.color || "";
|
|
751
|
-
const color = dynamicColor ? getColorCode(dynamicColor) : "";
|
|
752
|
-
const background = module.background ? getColorCode(module.background) : "";
|
|
753
|
-
const icon = module.icon || "";
|
|
754
|
-
// If script type, execute script to get text
|
|
755
|
-
let text = "";
|
|
756
|
-
if (module.type === "script" && module.scriptPath) {
|
|
757
|
-
text = await executeScript(module.scriptPath, variables, module.options);
|
|
758
|
-
}
|
|
759
|
-
else {
|
|
760
|
-
text = replaceVariables(module.text, variables);
|
|
761
|
-
}
|
|
762
|
-
// Build display text
|
|
763
|
-
let displayText = "";
|
|
764
|
-
if (icon) {
|
|
765
|
-
displayText += `${icon} `;
|
|
766
|
-
}
|
|
767
|
-
displayText += text;
|
|
768
|
-
// Skip module if displayText is empty or only has icon without actual text
|
|
769
|
-
if (!displayText || !text) {
|
|
770
|
-
continue;
|
|
771
|
-
}
|
|
772
|
-
// Build module string (plain text, Claude Code statusline does not support ANSI)
|
|
773
|
-
parts.push(displayText);
|
|
774
|
-
}
|
|
775
|
-
// Join all parts with spaces
|
|
776
|
-
return parts.join(" ");
|
|
777
|
-
}
|
|
778
|
-
// Powerline symbols
|
|
779
|
-
const SEP_RIGHT = "\uE0B0"; //
|
|
780
|
-
// Color numbers (256-color table)
|
|
781
|
-
const COLOR_MAP = {
|
|
782
|
-
// Basic colors mapped to 256 colors
|
|
783
|
-
black: 0,
|
|
784
|
-
red: 1,
|
|
785
|
-
green: 2,
|
|
786
|
-
yellow: 3,
|
|
787
|
-
blue: 4,
|
|
788
|
-
magenta: 5,
|
|
789
|
-
cyan: 6,
|
|
790
|
-
white: 7,
|
|
791
|
-
bright_black: 8,
|
|
792
|
-
bright_red: 9,
|
|
793
|
-
bright_green: 10,
|
|
794
|
-
bright_yellow: 11,
|
|
795
|
-
bright_blue: 12,
|
|
796
|
-
bright_magenta: 13,
|
|
797
|
-
bright_cyan: 14,
|
|
798
|
-
bright_white: 15,
|
|
799
|
-
// Bright background color mapping
|
|
800
|
-
bg_black: 0,
|
|
801
|
-
bg_red: 1,
|
|
802
|
-
bg_green: 2,
|
|
803
|
-
bg_yellow: 3,
|
|
804
|
-
bg_blue: 4,
|
|
805
|
-
bg_magenta: 5,
|
|
806
|
-
bg_cyan: 6,
|
|
807
|
-
bg_white: 7,
|
|
808
|
-
bg_bright_black: 8,
|
|
809
|
-
bg_bright_red: 9,
|
|
810
|
-
bg_bright_green: 10,
|
|
811
|
-
bg_bright_yellow: 11,
|
|
812
|
-
bg_bright_blue: 12,
|
|
813
|
-
bg_bright_magenta: 13,
|
|
814
|
-
bg_bright_cyan: 14,
|
|
815
|
-
bg_bright_white: 15,
|
|
816
|
-
// Custom color mapping
|
|
817
|
-
bg_bright_orange: 202,
|
|
818
|
-
bg_bright_purple: 129,
|
|
819
|
-
};
|
|
820
|
-
// Get TrueColor RGB value
|
|
821
|
-
function getTrueColorRgb(colorName) {
|
|
822
|
-
// If predefined color, return corresponding RGB
|
|
823
|
-
if (COLOR_MAP[colorName] !== undefined) {
|
|
824
|
-
const color256 = COLOR_MAP[colorName];
|
|
825
|
-
return color256ToRgb(color256);
|
|
826
|
-
}
|
|
827
|
-
// Handle hexadecimal color
|
|
828
|
-
if (colorName.startsWith('#') || /^[0-9a-fA-F]{6}$/.test(colorName) || /^[0-9a-fA-F]{3}$/.test(colorName)) {
|
|
829
|
-
return hexToRgb(colorName);
|
|
830
|
-
}
|
|
831
|
-
// Handle background color hexadecimal
|
|
832
|
-
if (colorName.startsWith('bg_#')) {
|
|
833
|
-
return hexToRgb(colorName.substring(3));
|
|
834
|
-
}
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
// Convert 256-color table index to RGB value
|
|
838
|
-
function color256ToRgb(index) {
|
|
839
|
-
if (index < 0 || index > 255)
|
|
840
|
-
return null;
|
|
841
|
-
// ANSI 256-color table conversion
|
|
842
|
-
if (index < 16) {
|
|
843
|
-
// Basic colors
|
|
844
|
-
const basicColors = [
|
|
845
|
-
[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],
|
|
846
|
-
[0, 0, 128], [128, 0, 128], [0, 128, 128], [192, 192, 192],
|
|
847
|
-
[128, 128, 128], [255, 0, 0], [0, 255, 0], [255, 255, 0],
|
|
848
|
-
[0, 0, 255], [255, 0, 255], [0, 255, 255], [255, 255, 255]
|
|
849
|
-
];
|
|
850
|
-
return { r: basicColors[index][0], g: basicColors[index][1], b: basicColors[index][2] };
|
|
851
|
-
}
|
|
852
|
-
else if (index < 232) {
|
|
853
|
-
// 216 colors: 6×6×6 color cube
|
|
854
|
-
const i = index - 16;
|
|
855
|
-
const r = Math.floor(i / 36);
|
|
856
|
-
const g = Math.floor((i % 36) / 6);
|
|
857
|
-
const b = i % 6;
|
|
858
|
-
const rgb = [0, 95, 135, 175, 215, 255];
|
|
859
|
-
return { r: rgb[r], g: rgb[g], b: rgb[b] };
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
// Grayscale colors
|
|
863
|
-
const gray = 8 + (index - 232) * 10;
|
|
864
|
-
return { r: gray, g: gray, b: gray };
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
// Generate a seamless segment: text displayed on bgN, separator transitions from bgN to nextBgN
|
|
868
|
-
function segment(text, textFg, bgColor, nextBgColor) {
|
|
869
|
-
// Plain text output (Claude Code statusline does not support ANSI)
|
|
870
|
-
const body = ` ${text} `;
|
|
871
|
-
if (nextBgColor != null) {
|
|
872
|
-
return body + ` ${SEP_RIGHT} `;
|
|
873
|
-
}
|
|
874
|
-
return body;
|
|
875
|
-
}
|
|
876
|
-
// Render Powerline style status line
|
|
877
|
-
async function renderPowerlineStyle(theme, variables) {
|
|
878
|
-
const modules = theme.modules || POWERLINE_THEME.modules;
|
|
879
|
-
const segments = [];
|
|
880
|
-
// Iterate through module array, rendering each module (maximum 10)
|
|
881
|
-
for (let i = 0; i < Math.min(modules.length, 10); i++) {
|
|
882
|
-
const module = modules[i];
|
|
883
|
-
const color = module.type === "contextCircle"
|
|
884
|
-
? getContextUsageColor(variables.contextPercent)
|
|
885
|
-
: module.color || "white";
|
|
886
|
-
const backgroundName = module.background || "";
|
|
887
|
-
const icon = module.icon || "";
|
|
888
|
-
// If script type, execute script to get text
|
|
889
|
-
let text = "";
|
|
890
|
-
if (module.type === "script" && module.scriptPath) {
|
|
891
|
-
text = await executeScript(module.scriptPath, variables);
|
|
892
|
-
}
|
|
893
|
-
else if (module.type === "speed") {
|
|
894
|
-
// speed module: use tokenSpeed variable
|
|
895
|
-
text = replaceVariables(module.text, variables);
|
|
896
|
-
}
|
|
897
|
-
else {
|
|
898
|
-
text = replaceVariables(module.text, variables);
|
|
899
|
-
}
|
|
900
|
-
// Build display text
|
|
901
|
-
let displayText = "";
|
|
902
|
-
if (icon) {
|
|
903
|
-
displayText += `${icon} `;
|
|
904
|
-
}
|
|
905
|
-
displayText += text;
|
|
906
|
-
// Skip module if displayText is empty or only has icon without actual text
|
|
907
|
-
if (!displayText || !text) {
|
|
908
|
-
continue;
|
|
909
|
-
}
|
|
910
|
-
// Get next module's background color (for separator)
|
|
911
|
-
let nextBackground = null;
|
|
912
|
-
if (i < modules.length - 1) {
|
|
913
|
-
const nextModule = modules[i + 1];
|
|
914
|
-
nextBackground = nextModule.background || null;
|
|
915
|
-
}
|
|
916
|
-
// Use module-defined background color, or provide default background color for Powerline style
|
|
917
|
-
const actualBackground = backgroundName || "bg_bright_blue";
|
|
918
|
-
// Generate segment, supports hexadecimal colors
|
|
919
|
-
const segmentStr = segment(displayText, color, actualBackground, nextBackground);
|
|
920
|
-
segments.push(segmentStr);
|
|
921
|
-
}
|
|
922
|
-
return segments.join("");
|
|
923
|
-
}
|
|
924
|
-
//# sourceMappingURL=statusline.js.map
|