ccstatusline 1.1.2 → 1.1.3
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/ccstatusline.js +582 -777
- package/package.json +6 -5
package/dist/ccstatusline.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/ccstatusline.ts
|
|
4
|
-
import chalk2 from "chalk";
|
|
5
|
-
import { execSync as execSync2 } from "child_process";
|
|
6
|
-
|
|
7
3
|
// src/tui.tsx
|
|
8
4
|
import { useState, useEffect } from "react";
|
|
9
5
|
import { render, Box, Text, useInput, useApp } from "ink";
|
|
10
6
|
import SelectInput from "ink-select-input";
|
|
11
|
-
import
|
|
12
|
-
import { execSync } from "child_process";
|
|
7
|
+
import chalk2 from "chalk";
|
|
8
|
+
import { execSync as execSync2 } from "child_process";
|
|
13
9
|
|
|
14
|
-
// src/config.ts
|
|
10
|
+
// src/utils/config.ts
|
|
15
11
|
import * as fs from "fs";
|
|
16
12
|
import * as path from "path";
|
|
17
13
|
import * as os from "os";
|
|
@@ -123,7 +119,7 @@ async function saveSettings(settings) {
|
|
|
123
119
|
await writeFile2(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
// src/claude-settings.ts
|
|
122
|
+
// src/utils/claude-settings.ts
|
|
127
123
|
import * as fs2 from "fs";
|
|
128
124
|
import * as path2 from "path";
|
|
129
125
|
import * as os2 from "os";
|
|
@@ -174,20 +170,36 @@ async function getExistingStatusLine() {
|
|
|
174
170
|
}
|
|
175
171
|
|
|
176
172
|
// src/tui.tsx
|
|
177
|
-
import * as
|
|
173
|
+
import * as fs4 from "fs";
|
|
178
174
|
import * as path3 from "path";
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
175
|
+
|
|
176
|
+
// src/utils/renderer.ts
|
|
177
|
+
import chalk from "chalk";
|
|
178
|
+
import { execSync } from "child_process";
|
|
179
|
+
import * as fs3 from "fs";
|
|
180
|
+
import { promisify as promisify3 } from "util";
|
|
181
|
+
var readFile6 = fs3.promises?.readFile || promisify3(fs3.readFile);
|
|
182
|
+
chalk.level = 3;
|
|
183
|
+
function applyColors(text, foregroundColor, backgroundColor, bold) {
|
|
184
|
+
let result = text;
|
|
185
|
+
if (foregroundColor && foregroundColor !== "dim") {
|
|
186
|
+
const fgFunc = chalk[foregroundColor];
|
|
187
|
+
if (fgFunc) {
|
|
188
|
+
result = fgFunc(result);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (backgroundColor && backgroundColor !== "none") {
|
|
192
|
+
const bgFunc = chalk[backgroundColor];
|
|
193
|
+
if (bgFunc) {
|
|
194
|
+
result = bgFunc(result);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (bold) {
|
|
198
|
+
result = chalk.bold(result);
|
|
188
199
|
}
|
|
200
|
+
return result;
|
|
189
201
|
}
|
|
190
|
-
function
|
|
202
|
+
function getItemDefaultColor(type) {
|
|
191
203
|
switch (type) {
|
|
192
204
|
case "model":
|
|
193
205
|
return "cyan";
|
|
@@ -225,7 +237,14 @@ function getDefaultColor(type) {
|
|
|
225
237
|
return "white";
|
|
226
238
|
}
|
|
227
239
|
}
|
|
228
|
-
function
|
|
240
|
+
function formatTokens(count) {
|
|
241
|
+
if (count >= 1e6)
|
|
242
|
+
return `${(count / 1e6).toFixed(1)}M`;
|
|
243
|
+
if (count >= 1000)
|
|
244
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
245
|
+
return count.toString();
|
|
246
|
+
}
|
|
247
|
+
function getTerminalWidth() {
|
|
229
248
|
try {
|
|
230
249
|
const tty = execSync("ps -o tty= -p $(ps -o ppid= -p $$)", {
|
|
231
250
|
encoding: "utf8",
|
|
@@ -240,7 +259,7 @@ function canDetectTerminalWidth() {
|
|
|
240
259
|
}).trim();
|
|
241
260
|
const parsed = parseInt(width, 10);
|
|
242
261
|
if (!isNaN(parsed) && parsed > 0) {
|
|
243
|
-
return
|
|
262
|
+
return parsed;
|
|
244
263
|
}
|
|
245
264
|
}
|
|
246
265
|
} catch {}
|
|
@@ -250,194 +269,453 @@ function canDetectTerminalWidth() {
|
|
|
250
269
|
stdio: ["pipe", "pipe", "ignore"]
|
|
251
270
|
}).trim();
|
|
252
271
|
const parsed = parseInt(width, 10);
|
|
253
|
-
|
|
272
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
273
|
+
return parsed;
|
|
274
|
+
}
|
|
275
|
+
} catch {}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
function getGitBranch() {
|
|
279
|
+
try {
|
|
280
|
+
const branch = execSync("git branch --show-current 2>/dev/null", {
|
|
281
|
+
encoding: "utf8",
|
|
282
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
283
|
+
}).trim();
|
|
284
|
+
return branch || null;
|
|
254
285
|
} catch {
|
|
255
|
-
return
|
|
286
|
+
return null;
|
|
256
287
|
}
|
|
257
288
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
289
|
+
function getGitChanges() {
|
|
290
|
+
try {
|
|
291
|
+
let totalInsertions = 0;
|
|
292
|
+
let totalDeletions = 0;
|
|
293
|
+
const unstagedStat = execSync("git diff --shortstat 2>/dev/null", {
|
|
294
|
+
encoding: "utf8",
|
|
295
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
296
|
+
}).trim();
|
|
297
|
+
const stagedStat = execSync("git diff --cached --shortstat 2>/dev/null", {
|
|
298
|
+
encoding: "utf8",
|
|
299
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
300
|
+
}).trim();
|
|
301
|
+
if (unstagedStat) {
|
|
302
|
+
const insertMatch = unstagedStat.match(/(\d+) insertion/);
|
|
303
|
+
const deleteMatch = unstagedStat.match(/(\d+) deletion/);
|
|
304
|
+
totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
|
|
305
|
+
totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
|
|
264
306
|
}
|
|
307
|
+
if (stagedStat) {
|
|
308
|
+
const insertMatch = stagedStat.match(/(\d+) insertion/);
|
|
309
|
+
const deleteMatch = stagedStat.match(/(\d+) deletion/);
|
|
310
|
+
totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
|
|
311
|
+
totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
|
|
312
|
+
}
|
|
313
|
+
return { insertions: totalInsertions, deletions: totalDeletions };
|
|
314
|
+
} catch {
|
|
315
|
+
return null;
|
|
265
316
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
317
|
+
}
|
|
318
|
+
async function getSessionDuration(transcriptPath) {
|
|
319
|
+
try {
|
|
320
|
+
if (!fs3.existsSync(transcriptPath)) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const content = await readFile6(transcriptPath, "utf-8");
|
|
324
|
+
const lines = content.trim().split(`
|
|
325
|
+
`).filter((line) => line.trim());
|
|
326
|
+
if (lines.length === 0) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
let firstTimestamp = null;
|
|
330
|
+
let lastTimestamp = null;
|
|
331
|
+
for (const line of lines) {
|
|
332
|
+
try {
|
|
333
|
+
const data = JSON.parse(line);
|
|
334
|
+
if (data.timestamp) {
|
|
335
|
+
firstTimestamp = new Date(data.timestamp);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
} catch {}
|
|
339
|
+
}
|
|
340
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
341
|
+
try {
|
|
342
|
+
const data = JSON.parse(lines[i]);
|
|
343
|
+
if (data.timestamp) {
|
|
344
|
+
lastTimestamp = new Date(data.timestamp);
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
} catch {}
|
|
348
|
+
}
|
|
349
|
+
if (!firstTimestamp || !lastTimestamp) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
const durationMs = lastTimestamp.getTime() - firstTimestamp.getTime();
|
|
353
|
+
const totalMinutes = Math.floor(durationMs / (1000 * 60));
|
|
354
|
+
if (totalMinutes < 1) {
|
|
355
|
+
return "<1m";
|
|
356
|
+
}
|
|
357
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
358
|
+
const minutes = totalMinutes % 60;
|
|
359
|
+
if (hours === 0) {
|
|
360
|
+
return `${minutes}m`;
|
|
361
|
+
} else if (minutes === 0) {
|
|
362
|
+
return `${hours}hr`;
|
|
363
|
+
} else {
|
|
364
|
+
return `${hours}hr ${minutes}m`;
|
|
270
365
|
}
|
|
366
|
+
} catch {
|
|
367
|
+
return null;
|
|
271
368
|
}
|
|
272
|
-
|
|
273
|
-
|
|
369
|
+
}
|
|
370
|
+
async function getTokenMetrics(transcriptPath) {
|
|
371
|
+
try {
|
|
372
|
+
if (!fs3.existsSync(transcriptPath)) {
|
|
373
|
+
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
374
|
+
}
|
|
375
|
+
const content = await readFile6(transcriptPath, "utf-8");
|
|
376
|
+
const lines = content.trim().split(`
|
|
377
|
+
`);
|
|
378
|
+
let inputTokens = 0;
|
|
379
|
+
let outputTokens = 0;
|
|
380
|
+
let cachedTokens = 0;
|
|
381
|
+
let contextLength = 0;
|
|
382
|
+
let mostRecentMainChainEntry = null;
|
|
383
|
+
let mostRecentTimestamp = null;
|
|
384
|
+
for (const line of lines) {
|
|
385
|
+
try {
|
|
386
|
+
const data = JSON.parse(line);
|
|
387
|
+
if (data.message?.usage) {
|
|
388
|
+
inputTokens += data.message.usage.input_tokens || 0;
|
|
389
|
+
outputTokens += data.message.usage.output_tokens || 0;
|
|
390
|
+
cachedTokens += data.message.usage.cache_read_input_tokens || 0;
|
|
391
|
+
cachedTokens += data.message.usage.cache_creation_input_tokens || 0;
|
|
392
|
+
if (data.isSidechain !== true && data.timestamp) {
|
|
393
|
+
const entryTime = new Date(data.timestamp);
|
|
394
|
+
if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
|
|
395
|
+
mostRecentTimestamp = entryTime;
|
|
396
|
+
mostRecentMainChainEntry = data;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
} catch {}
|
|
401
|
+
}
|
|
402
|
+
if (mostRecentMainChainEntry?.message?.usage) {
|
|
403
|
+
const usage = mostRecentMainChainEntry.message.usage;
|
|
404
|
+
contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens || 0) + (usage.cache_creation_input_tokens || 0);
|
|
405
|
+
}
|
|
406
|
+
const totalTokens = inputTokens + outputTokens + cachedTokens;
|
|
407
|
+
return { inputTokens, outputTokens, cachedTokens, totalTokens, contextLength };
|
|
408
|
+
} catch {
|
|
409
|
+
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
274
410
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (settings?.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
|
|
411
|
+
}
|
|
412
|
+
function renderStatusLine(items, settings, context) {
|
|
413
|
+
const applyColorsWithOverride = (text, foregroundColor, backgroundColor, bold) => {
|
|
414
|
+
let fgColor = foregroundColor;
|
|
415
|
+
if (settings.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
|
|
281
416
|
fgColor = settings.overrideForegroundColor;
|
|
282
417
|
}
|
|
283
|
-
let bgColor =
|
|
284
|
-
if (settings
|
|
418
|
+
let bgColor = backgroundColor;
|
|
419
|
+
if (settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none") {
|
|
285
420
|
bgColor = settings.overrideBackgroundColor;
|
|
286
421
|
}
|
|
287
|
-
const shouldBold = settings
|
|
422
|
+
const shouldBold = settings.globalBold || bold;
|
|
288
423
|
return applyColors(text, fgColor, bgColor, shouldBold);
|
|
289
424
|
};
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
425
|
+
const detectedWidth = context.terminalWidth || getTerminalWidth();
|
|
426
|
+
let terminalWidth = null;
|
|
427
|
+
if (detectedWidth) {
|
|
428
|
+
const flexMode = settings.flexMode || "full-minus-40";
|
|
429
|
+
if (context.isPreview) {
|
|
430
|
+
if (flexMode === "full") {
|
|
431
|
+
terminalWidth = detectedWidth - 6;
|
|
432
|
+
} else if (flexMode === "full-minus-40") {
|
|
433
|
+
terminalWidth = detectedWidth - 43;
|
|
434
|
+
} else if (flexMode === "full-until-compact") {
|
|
435
|
+
terminalWidth = detectedWidth - 6;
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
if (flexMode === "full") {
|
|
439
|
+
terminalWidth = detectedWidth - 4;
|
|
440
|
+
} else if (flexMode === "full-minus-40") {
|
|
441
|
+
terminalWidth = detectedWidth - 41;
|
|
442
|
+
} else if (flexMode === "full-until-compact") {
|
|
443
|
+
const threshold = settings.compactThreshold || 60;
|
|
444
|
+
const contextPercentage = context.tokenMetrics ? Math.min(100, context.tokenMetrics.contextLength / 200000 * 100) : 0;
|
|
445
|
+
if (contextPercentage >= threshold) {
|
|
446
|
+
terminalWidth = detectedWidth - 40;
|
|
447
|
+
} else {
|
|
448
|
+
terminalWidth = detectedWidth - 4;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
299
451
|
}
|
|
300
452
|
}
|
|
301
|
-
const
|
|
302
|
-
const flexWidth = effectiveWidth;
|
|
303
|
-
const rawElements = [];
|
|
453
|
+
const elements = [];
|
|
304
454
|
let hasFlexSeparator = false;
|
|
305
|
-
|
|
455
|
+
for (const item of items) {
|
|
306
456
|
switch (item.type) {
|
|
307
457
|
case "model":
|
|
308
|
-
|
|
309
|
-
|
|
458
|
+
if (context.isPreview) {
|
|
459
|
+
const modelText = item.rawValue ? "Claude" : "Model: Claude";
|
|
460
|
+
elements.push({ content: applyColorsWithOverride(modelText, item.color || "cyan", item.backgroundColor, item.bold), type: "model", item });
|
|
461
|
+
} else if (context.data?.model) {
|
|
462
|
+
const text = item.rawValue ? context.data.model.display_name : `Model: ${context.data.model.display_name}`;
|
|
463
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "model", item });
|
|
464
|
+
}
|
|
310
465
|
break;
|
|
311
466
|
case "git-branch":
|
|
312
|
-
|
|
313
|
-
|
|
467
|
+
if (context.isPreview) {
|
|
468
|
+
const branchText = item.rawValue ? "main" : "⎇ main";
|
|
469
|
+
elements.push({ content: applyColorsWithOverride(branchText, item.color || "magenta", item.backgroundColor, item.bold), type: "git-branch", item });
|
|
470
|
+
} else {
|
|
471
|
+
const branch = context.gitBranch || getGitBranch();
|
|
472
|
+
if (branch) {
|
|
473
|
+
const text = item.rawValue ? branch : `⎇ ${branch}`;
|
|
474
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "magenta", item.backgroundColor, item.bold), type: "git-branch", item });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
314
477
|
break;
|
|
315
478
|
case "git-changes":
|
|
316
|
-
|
|
317
|
-
|
|
479
|
+
if (context.isPreview) {
|
|
480
|
+
const changesText = "(+42,-10)";
|
|
481
|
+
elements.push({ content: applyColorsWithOverride(changesText, item.color || "yellow", item.backgroundColor, item.bold), type: "git-changes", item });
|
|
482
|
+
} else {
|
|
483
|
+
const changes = context.gitChanges || getGitChanges();
|
|
484
|
+
if (changes !== null) {
|
|
485
|
+
const changeStr = `(+${changes.insertions},-${changes.deletions})`;
|
|
486
|
+
elements.push({ content: applyColorsWithOverride(changeStr, item.color || "yellow", item.backgroundColor, item.bold), type: "git-changes", item });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
318
489
|
break;
|
|
319
490
|
case "tokens-input":
|
|
320
|
-
|
|
321
|
-
|
|
491
|
+
if (context.isPreview) {
|
|
492
|
+
const inputText = item.rawValue ? "15.2k" : "In: 15.2k";
|
|
493
|
+
elements.push({ content: applyColorsWithOverride(inputText, item.color || "blue", item.backgroundColor, item.bold), type: "tokens-input", item });
|
|
494
|
+
} else if (context.tokenMetrics) {
|
|
495
|
+
const text = item.rawValue ? formatTokens(context.tokenMetrics.inputTokens) : `In: ${formatTokens(context.tokenMetrics.inputTokens)}`;
|
|
496
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "blue", item.backgroundColor, item.bold), type: "tokens-input", item });
|
|
497
|
+
}
|
|
322
498
|
break;
|
|
323
499
|
case "tokens-output":
|
|
324
|
-
|
|
325
|
-
|
|
500
|
+
if (context.isPreview) {
|
|
501
|
+
const outputText = item.rawValue ? "3.4k" : "Out: 3.4k";
|
|
502
|
+
elements.push({ content: applyColorsWithOverride(outputText, item.color || "white", item.backgroundColor, item.bold), type: "tokens-output", item });
|
|
503
|
+
} else if (context.tokenMetrics) {
|
|
504
|
+
const text = item.rawValue ? formatTokens(context.tokenMetrics.outputTokens) : `Out: ${formatTokens(context.tokenMetrics.outputTokens)}`;
|
|
505
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "white", item.backgroundColor, item.bold), type: "tokens-output", item });
|
|
506
|
+
}
|
|
326
507
|
break;
|
|
327
508
|
case "tokens-cached":
|
|
328
|
-
|
|
329
|
-
|
|
509
|
+
if (context.isPreview) {
|
|
510
|
+
const cachedText = item.rawValue ? "12k" : "Cached: 12k";
|
|
511
|
+
elements.push({ content: applyColorsWithOverride(cachedText, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-cached", item });
|
|
512
|
+
} else if (context.tokenMetrics) {
|
|
513
|
+
const text = item.rawValue ? formatTokens(context.tokenMetrics.cachedTokens) : `Cached: ${formatTokens(context.tokenMetrics.cachedTokens)}`;
|
|
514
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-cached", item });
|
|
515
|
+
}
|
|
330
516
|
break;
|
|
331
517
|
case "tokens-total":
|
|
332
|
-
|
|
333
|
-
|
|
518
|
+
if (context.isPreview) {
|
|
519
|
+
const totalText = item.rawValue ? "30.6k" : "Total: 30.6k";
|
|
520
|
+
elements.push({ content: applyColorsWithOverride(totalText, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-total", item });
|
|
521
|
+
} else if (context.tokenMetrics) {
|
|
522
|
+
const text = item.rawValue ? formatTokens(context.tokenMetrics.totalTokens) : `Total: ${formatTokens(context.tokenMetrics.totalTokens)}`;
|
|
523
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-total", item });
|
|
524
|
+
}
|
|
334
525
|
break;
|
|
335
526
|
case "context-length":
|
|
336
|
-
|
|
337
|
-
|
|
527
|
+
if (context.isPreview) {
|
|
528
|
+
const ctxText = item.rawValue ? "18.6k" : "Ctx: 18.6k";
|
|
529
|
+
elements.push({ content: applyColorsWithOverride(ctxText, item.color || "gray", item.backgroundColor, item.bold), type: "context-length", item });
|
|
530
|
+
} else if (context.tokenMetrics) {
|
|
531
|
+
const text = item.rawValue ? formatTokens(context.tokenMetrics.contextLength) : `Ctx: ${formatTokens(context.tokenMetrics.contextLength)}`;
|
|
532
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "gray", item.backgroundColor, item.bold), type: "context-length", item });
|
|
533
|
+
}
|
|
338
534
|
break;
|
|
339
535
|
case "context-percentage":
|
|
340
|
-
|
|
341
|
-
|
|
536
|
+
if (context.isPreview) {
|
|
537
|
+
const ctxPctText = item.rawValue ? "9.3%" : "Ctx: 9.3%";
|
|
538
|
+
elements.push({ content: applyColorsWithOverride(ctxPctText, item.color || "blue", item.backgroundColor, item.bold), type: "context-percentage", item });
|
|
539
|
+
} else if (context.tokenMetrics) {
|
|
540
|
+
const percentage = Math.min(100, context.tokenMetrics.contextLength / 200000 * 100);
|
|
541
|
+
const text = item.rawValue ? `${percentage.toFixed(1)}%` : `Ctx: ${percentage.toFixed(1)}%`;
|
|
542
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "blue", item.backgroundColor, item.bold), type: "context-percentage", item });
|
|
543
|
+
}
|
|
342
544
|
break;
|
|
343
545
|
case "context-percentage-usable":
|
|
344
|
-
|
|
345
|
-
|
|
546
|
+
if (context.isPreview) {
|
|
547
|
+
const ctxUsableText = item.rawValue ? "11.6%" : "Ctx(u): 11.6%";
|
|
548
|
+
elements.push({ content: applyColorsWithOverride(ctxUsableText, item.color || "green", item.backgroundColor, item.bold), type: "context-percentage-usable", item });
|
|
549
|
+
} else if (context.tokenMetrics) {
|
|
550
|
+
const percentage = Math.min(100, context.tokenMetrics.contextLength / 160000 * 100);
|
|
551
|
+
const text = item.rawValue ? `${percentage.toFixed(1)}%` : `Ctx(u): ${percentage.toFixed(1)}%`;
|
|
552
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "green", item.backgroundColor, item.bold), type: "context-percentage-usable", item });
|
|
553
|
+
}
|
|
554
|
+
break;
|
|
555
|
+
case "terminal-width":
|
|
556
|
+
const width = terminalWidth || getTerminalWidth();
|
|
557
|
+
if (context.isPreview) {
|
|
558
|
+
const detectedWidth2 = width || "??";
|
|
559
|
+
const termText = item.rawValue ? `${detectedWidth2}` : `Term: ${detectedWidth2}`;
|
|
560
|
+
elements.push({ content: applyColorsWithOverride(termText, item.color || "gray", item.backgroundColor, item.bold), type: "terminal-width", item });
|
|
561
|
+
} else if (width) {
|
|
562
|
+
const text = item.rawValue ? `${width}` : `Term: ${width}`;
|
|
563
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "gray", item.backgroundColor, item.bold), type: "terminal-width", item });
|
|
564
|
+
}
|
|
346
565
|
break;
|
|
347
566
|
case "session-clock":
|
|
348
|
-
|
|
349
|
-
|
|
567
|
+
if (context.isPreview) {
|
|
568
|
+
const sessionText = item.rawValue ? "2hr 15m" : "Session: 2hr 15m";
|
|
569
|
+
elements.push({ content: applyColorsWithOverride(sessionText, item.color || "yellow", item.backgroundColor, item.bold), type: "session-clock", item });
|
|
570
|
+
} else if (context.sessionDuration) {
|
|
571
|
+
const text = item.rawValue ? context.sessionDuration : `Session: ${context.sessionDuration}`;
|
|
572
|
+
elements.push({ content: applyColorsWithOverride(text, item.color || "yellow", item.backgroundColor, item.bold), type: "session-clock", item });
|
|
573
|
+
}
|
|
350
574
|
break;
|
|
351
575
|
case "version":
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
576
|
+
if (context.isPreview) {
|
|
577
|
+
const versionText = item.rawValue ? "1.0.72" : "Version: 1.0.72";
|
|
578
|
+
elements.push({ content: applyColorsWithOverride(versionText, item.color || "green", item.backgroundColor, item.bold), type: "version", item });
|
|
579
|
+
} else if (context.data?.version) {
|
|
580
|
+
const versionString = context.data.version || "Unknown";
|
|
581
|
+
const versionText = item.rawValue ? versionString : `Version: ${versionString}`;
|
|
582
|
+
elements.push({ content: applyColorsWithOverride(versionText, item.color || "green", item.backgroundColor, item.bold), type: "version", item });
|
|
583
|
+
}
|
|
359
584
|
break;
|
|
360
585
|
case "separator":
|
|
361
586
|
const sepChar = item.character || "|";
|
|
362
|
-
let
|
|
587
|
+
let sepText;
|
|
363
588
|
if (sepChar === ",") {
|
|
364
|
-
|
|
589
|
+
sepText = `${sepChar} `;
|
|
365
590
|
} else if (sepChar === " ") {
|
|
366
|
-
|
|
591
|
+
sepText = " ";
|
|
367
592
|
} else {
|
|
368
|
-
|
|
593
|
+
sepText = ` ${sepChar} `;
|
|
369
594
|
}
|
|
370
|
-
|
|
595
|
+
const sepContent = applyColorsWithOverride(sepText, item.color || "gray", item.backgroundColor, item.bold);
|
|
596
|
+
elements.push({ content: sepContent, type: "separator", item });
|
|
371
597
|
break;
|
|
372
598
|
case "flex-separator":
|
|
373
|
-
|
|
599
|
+
elements.push({ content: "FLEX", type: "flex-separator", item });
|
|
374
600
|
hasFlexSeparator = true;
|
|
375
601
|
break;
|
|
376
602
|
case "custom-text":
|
|
377
603
|
const customText = item.customText || "";
|
|
378
|
-
|
|
604
|
+
elements.push({ content: applyColorsWithOverride(customText, item.color || "white", item.backgroundColor, item.bold), type: "custom-text", item });
|
|
379
605
|
break;
|
|
380
606
|
case "custom-command":
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
607
|
+
if (context.isPreview) {
|
|
608
|
+
const cmdText = item.commandPath ? `[cmd: ${item.commandPath.substring(0, 20)}${item.commandPath.length > 20 ? "..." : ""}]` : "[No command]";
|
|
609
|
+
if (!item.preserveColors) {
|
|
610
|
+
elements.push({ content: applyColorsWithOverride(cmdText, item.color || "white", item.backgroundColor, item.bold), type: "custom-command", item });
|
|
611
|
+
} else {
|
|
612
|
+
elements.push({ content: cmdText, type: "custom-command", item });
|
|
613
|
+
}
|
|
614
|
+
} else if (item.commandPath && context.data) {
|
|
615
|
+
try {
|
|
616
|
+
const timeout = item.timeout || 1000;
|
|
617
|
+
const output = execSync(item.commandPath, {
|
|
618
|
+
encoding: "utf8",
|
|
619
|
+
input: JSON.stringify(context.data),
|
|
620
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
621
|
+
timeout
|
|
622
|
+
}).trim();
|
|
623
|
+
if (output) {
|
|
624
|
+
let finalOutput = output;
|
|
625
|
+
if (item.maxWidth && item.maxWidth > 0) {
|
|
626
|
+
const plainLength = output.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
627
|
+
if (plainLength > item.maxWidth) {
|
|
628
|
+
let truncated = "";
|
|
629
|
+
let currentLength = 0;
|
|
630
|
+
let inAnsiCode = false;
|
|
631
|
+
let ansiBuffer = "";
|
|
632
|
+
for (let i = 0;i < output.length; i++) {
|
|
633
|
+
const char = output[i];
|
|
634
|
+
if (char === "\x1B") {
|
|
635
|
+
inAnsiCode = true;
|
|
636
|
+
ansiBuffer = char;
|
|
637
|
+
} else if (inAnsiCode) {
|
|
638
|
+
ansiBuffer += char;
|
|
639
|
+
if (char === "m") {
|
|
640
|
+
truncated += ansiBuffer;
|
|
641
|
+
inAnsiCode = false;
|
|
642
|
+
ansiBuffer = "";
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
if (currentLength < item.maxWidth) {
|
|
646
|
+
truncated += char;
|
|
647
|
+
currentLength++;
|
|
648
|
+
} else {
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
finalOutput = truncated;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (!item.preserveColors) {
|
|
657
|
+
const stripped = finalOutput.replace(/\x1b\[[0-9;]*m/g, "");
|
|
658
|
+
elements.push({ content: applyColorsWithOverride(stripped, item.color || "white", item.backgroundColor, item.bold), type: "custom-command", item });
|
|
659
|
+
} else {
|
|
660
|
+
elements.push({ content: finalOutput, type: "custom-command", item });
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
} catch {}
|
|
664
|
+
}
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (elements.length === 0)
|
|
669
|
+
return "";
|
|
670
|
+
while (elements.length > 0 && elements[elements.length - 1]?.type === "separator") {
|
|
671
|
+
elements.pop();
|
|
672
|
+
}
|
|
673
|
+
const finalElements = [];
|
|
674
|
+
const padding = settings.defaultPadding || "";
|
|
675
|
+
const defaultSep = settings.defaultSeparator || "";
|
|
676
|
+
elements.forEach((elem, index) => {
|
|
677
|
+
const prevElem = index > 0 ? elements[index - 1] : null;
|
|
678
|
+
const shouldAddSeparator = defaultSep && index > 0 && elem.type !== "flex-separator" && prevElem?.type !== "flex-separator";
|
|
679
|
+
if (shouldAddSeparator) {
|
|
680
|
+
if (settings.inheritSeparatorColors && index > 0) {
|
|
681
|
+
const prevElem2 = elements[index - 1];
|
|
682
|
+
if (prevElem2 && prevElem2.item) {
|
|
683
|
+
const itemColor = prevElem2.item.color || getItemDefaultColor(prevElem2.item.type);
|
|
684
|
+
const coloredSep = applyColorsWithOverride(defaultSep, itemColor, prevElem2.item.backgroundColor, prevElem2.item.bold);
|
|
685
|
+
finalElements.push(coloredSep);
|
|
686
|
+
} else {
|
|
687
|
+
finalElements.push(defaultSep);
|
|
688
|
+
}
|
|
689
|
+
} else if (settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none" || settings.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
|
|
690
|
+
const coloredSep = applyColorsWithOverride(defaultSep, undefined, undefined);
|
|
691
|
+
finalElements.push(coloredSep);
|
|
692
|
+
} else {
|
|
693
|
+
finalElements.push(defaultSep);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (elem.type === "separator" || elem.type === "flex-separator") {
|
|
697
|
+
finalElements.push(elem.content);
|
|
698
|
+
} else {
|
|
699
|
+
const hasColorOverride = settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none" || settings.overrideForegroundColor && settings.overrideForegroundColor !== "none";
|
|
700
|
+
if (padding && (elem.item?.backgroundColor || hasColorOverride)) {
|
|
701
|
+
const paddedContent = applyColorsWithOverride(padding, undefined, elem.item?.backgroundColor) + elem.content + applyColorsWithOverride(padding, undefined, elem.item?.backgroundColor);
|
|
702
|
+
finalElements.push(paddedContent);
|
|
422
703
|
} else {
|
|
423
|
-
|
|
704
|
+
finalElements.push(padding + elem.content + padding);
|
|
424
705
|
}
|
|
425
706
|
}
|
|
426
707
|
});
|
|
427
708
|
let statusLine = "";
|
|
428
|
-
if (hasFlexSeparator &&
|
|
709
|
+
if (hasFlexSeparator && terminalWidth) {
|
|
429
710
|
const parts = [[]];
|
|
430
711
|
let currentPart = 0;
|
|
431
|
-
for (let i = 0;i <
|
|
432
|
-
const
|
|
433
|
-
if (
|
|
712
|
+
for (let i = 0;i < finalElements.length; i++) {
|
|
713
|
+
const elem = finalElements[i];
|
|
714
|
+
if (elem === "FLEX") {
|
|
434
715
|
currentPart++;
|
|
435
716
|
parts[currentPart] = [];
|
|
436
717
|
} else {
|
|
437
|
-
|
|
438
|
-
if (element !== "FLEX" && parts[currentPart]) {
|
|
439
|
-
parts[currentPart].push(element);
|
|
440
|
-
}
|
|
718
|
+
parts[currentPart]?.push(elem);
|
|
441
719
|
}
|
|
442
720
|
}
|
|
443
721
|
const partLengths = parts.map((part) => {
|
|
@@ -446,7 +724,7 @@ var renderSingleLine = (items, terminalWidth, widthDetectionAvailable, settings)
|
|
|
446
724
|
});
|
|
447
725
|
const totalContentLength = partLengths.reduce((sum, len) => sum + len, 0);
|
|
448
726
|
const flexCount = parts.length - 1;
|
|
449
|
-
const totalSpace = Math.max(0,
|
|
727
|
+
const totalSpace = Math.max(0, terminalWidth - totalContentLength);
|
|
450
728
|
const spacePerFlex = flexCount > 0 ? Math.floor(totalSpace / flexCount) : 0;
|
|
451
729
|
const extraSpace = flexCount > 0 ? totalSpace % flexCount : 0;
|
|
452
730
|
statusLine = "";
|
|
@@ -461,16 +739,21 @@ var renderSingleLine = (items, terminalWidth, widthDetectionAvailable, settings)
|
|
|
461
739
|
}
|
|
462
740
|
}
|
|
463
741
|
} else {
|
|
464
|
-
|
|
742
|
+
if (hasFlexSeparator && !terminalWidth) {
|
|
743
|
+
statusLine = finalElements.map((e) => e === "FLEX" ? chalk.gray(" | ") : e).join("");
|
|
744
|
+
} else {
|
|
745
|
+
statusLine = finalElements.join("");
|
|
746
|
+
}
|
|
465
747
|
}
|
|
466
|
-
|
|
748
|
+
const maxWidth = terminalWidth || detectedWidth;
|
|
749
|
+
if (maxWidth && maxWidth > 0) {
|
|
467
750
|
const plainLength = statusLine.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
468
|
-
if (plainLength >
|
|
751
|
+
if (plainLength > maxWidth) {
|
|
469
752
|
let truncated = "";
|
|
470
753
|
let currentLength = 0;
|
|
471
754
|
let inAnsiCode = false;
|
|
472
755
|
let ansiBuffer = "";
|
|
473
|
-
const targetLength =
|
|
756
|
+
const targetLength = context.isPreview ? maxWidth - 3 : maxWidth - 3;
|
|
474
757
|
for (let i = 0;i < statusLine.length; i++) {
|
|
475
758
|
const char = statusLine[i];
|
|
476
759
|
if (char === "\x1B") {
|
|
@@ -496,26 +779,72 @@ var renderSingleLine = (items, terminalWidth, widthDetectionAvailable, settings)
|
|
|
496
779
|
}
|
|
497
780
|
}
|
|
498
781
|
return statusLine;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/tui.tsx
|
|
785
|
+
var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src";
|
|
786
|
+
import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
|
|
787
|
+
function getPackageVersion() {
|
|
788
|
+
try {
|
|
789
|
+
const packageJsonPath = path3.join(__dirname, "..", "package.json");
|
|
790
|
+
const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
|
|
791
|
+
return packageJson.version || "";
|
|
792
|
+
} catch {
|
|
793
|
+
return "";
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
var getDefaultColor = getItemDefaultColor;
|
|
797
|
+
function canDetectTerminalWidth() {
|
|
798
|
+
try {
|
|
799
|
+
const tty = execSync2("ps -o tty= -p $(ps -o ppid= -p $$)", {
|
|
800
|
+
encoding: "utf8",
|
|
801
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
802
|
+
shell: "/bin/sh"
|
|
803
|
+
}).trim();
|
|
804
|
+
if (tty && tty !== "??" && tty !== "?") {
|
|
805
|
+
const width = execSync2(`stty size < /dev/${tty} | awk '{print $2}'`, {
|
|
806
|
+
encoding: "utf8",
|
|
807
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
808
|
+
shell: "/bin/sh"
|
|
809
|
+
}).trim();
|
|
810
|
+
const parsed = parseInt(width, 10);
|
|
811
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
} catch {}
|
|
816
|
+
try {
|
|
817
|
+
const width = execSync2("tput cols 2>/dev/null", {
|
|
818
|
+
encoding: "utf8",
|
|
819
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
820
|
+
}).trim();
|
|
821
|
+
const parsed = parseInt(width, 10);
|
|
822
|
+
return !isNaN(parsed) && parsed > 0;
|
|
823
|
+
} catch {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
var renderSingleLine = (items, terminalWidth, widthDetectionAvailable, settings) => {
|
|
828
|
+
const context = {
|
|
829
|
+
terminalWidth,
|
|
830
|
+
isPreview: true
|
|
831
|
+
};
|
|
832
|
+
return renderStatusLine(items, settings || {}, context);
|
|
499
833
|
};
|
|
500
834
|
var StatusLinePreview = ({ lines, terminalWidth, settings }) => {
|
|
501
835
|
const widthDetectionAvailable = canDetectTerminalWidth();
|
|
502
|
-
const
|
|
503
|
-
const topLine = chalk.gray("╭" + "─".repeat(Math.max(0, boxWidth - 2)) + "╮");
|
|
504
|
-
const middleLine = chalk.gray("│") + " > " + " ".repeat(Math.max(0, boxWidth - 5)) + chalk.gray("│");
|
|
505
|
-
const bottomLine = chalk.gray("╰" + "─".repeat(Math.max(0, boxWidth - 2)) + "╯");
|
|
506
|
-
const availableWidth = boxWidth - 2;
|
|
507
|
-
const renderedLines = lines.map((lineItems) => lineItems.length > 0 ? renderSingleLine(lineItems, availableWidth, widthDetectionAvailable, settings) : "").filter((line) => line !== "");
|
|
836
|
+
const renderedLines = lines.map((lineItems) => lineItems.length > 0 ? renderSingleLine(lineItems, terminalWidth, widthDetectionAvailable, settings) : "").filter((line) => line !== "");
|
|
508
837
|
return /* @__PURE__ */ jsxDEV(Box, {
|
|
509
838
|
flexDirection: "column",
|
|
510
839
|
children: [
|
|
511
|
-
/* @__PURE__ */ jsxDEV(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
840
|
+
/* @__PURE__ */ jsxDEV(Box, {
|
|
841
|
+
borderStyle: "round",
|
|
842
|
+
borderColor: "gray",
|
|
843
|
+
width: "100%",
|
|
844
|
+
paddingLeft: 1,
|
|
845
|
+
children: /* @__PURE__ */ jsxDEV(Text, {
|
|
846
|
+
children: ">"
|
|
847
|
+
}, undefined, false, undefined, this)
|
|
519
848
|
}, undefined, false, undefined, this),
|
|
520
849
|
renderedLines.map((line, index) => /* @__PURE__ */ jsxDEV(Text, {
|
|
521
850
|
children: [
|
|
@@ -596,7 +925,7 @@ var MainMenu = ({ onSelect, isClaudeInstalled, hasChanges, initialSelection = 0
|
|
|
596
925
|
const items = [
|
|
597
926
|
{ label: "\uD83D\uDCDD Edit Lines", value: "lines" },
|
|
598
927
|
{ label: "\uD83C\uDFA8 Configure Colors", value: "colors" },
|
|
599
|
-
{ label: "
|
|
928
|
+
{ label: "\uD83D\uDCCF Terminal Width Options", value: "terminalWidth" },
|
|
600
929
|
{ label: "\uD83D\uDD27 Global Options", value: "globalOptions" },
|
|
601
930
|
{ label: isClaudeInstalled ? "\uD83D\uDDD1️ Uninstall from Claude Code" : "\uD83D\uDCE6 Install to Claude Code", value: "install" }
|
|
602
931
|
];
|
|
@@ -661,11 +990,15 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
661
990
|
setTextCursorPos(0);
|
|
662
991
|
} else if (key.ctrl && key.rightArrow) {
|
|
663
992
|
setTextCursorPos(textInput.length);
|
|
664
|
-
} else if (key.backspace
|
|
993
|
+
} else if (key.backspace) {
|
|
665
994
|
if (textCursorPos > 0) {
|
|
666
995
|
setTextInput(textInput.slice(0, textCursorPos - 1) + textInput.slice(textCursorPos));
|
|
667
996
|
setTextCursorPos(textCursorPos - 1);
|
|
668
997
|
}
|
|
998
|
+
} else if (key.delete) {
|
|
999
|
+
if (textCursorPos < textInput.length) {
|
|
1000
|
+
setTextInput(textInput.slice(0, textCursorPos) + textInput.slice(textCursorPos + 1));
|
|
1001
|
+
}
|
|
669
1002
|
} else if (input && input.length === 1) {
|
|
670
1003
|
setTextInput(textInput.slice(0, textCursorPos) + input + textInput.slice(textCursorPos));
|
|
671
1004
|
setTextCursorPos(textCursorPos + 1);
|
|
@@ -693,11 +1026,15 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
693
1026
|
setCommandCursorPos(0);
|
|
694
1027
|
} else if (key.ctrl && key.rightArrow) {
|
|
695
1028
|
setCommandCursorPos(commandInput.length);
|
|
696
|
-
} else if (key.backspace
|
|
1029
|
+
} else if (key.backspace) {
|
|
697
1030
|
if (commandCursorPos > 0) {
|
|
698
1031
|
setCommandInput(commandInput.slice(0, commandCursorPos - 1) + commandInput.slice(commandCursorPos));
|
|
699
1032
|
setCommandCursorPos(commandCursorPos - 1);
|
|
700
1033
|
}
|
|
1034
|
+
} else if (key.delete) {
|
|
1035
|
+
if (commandCursorPos < commandInput.length) {
|
|
1036
|
+
setCommandInput(commandInput.slice(0, commandCursorPos) + commandInput.slice(commandCursorPos + 1));
|
|
1037
|
+
}
|
|
701
1038
|
} else if (input) {
|
|
702
1039
|
setCommandInput(commandInput.slice(0, commandCursorPos) + input + commandInput.slice(commandCursorPos));
|
|
703
1040
|
setCommandCursorPos(commandCursorPos + input.length);
|
|
@@ -721,9 +1058,9 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
721
1058
|
} else if (key.escape) {
|
|
722
1059
|
setEditingMaxWidth(false);
|
|
723
1060
|
setMaxWidthInput("");
|
|
724
|
-
} else if (key.backspace
|
|
1061
|
+
} else if (key.backspace) {
|
|
725
1062
|
setMaxWidthInput(maxWidthInput.slice(0, -1));
|
|
726
|
-
} else if (input && /\d/.test(input)) {
|
|
1063
|
+
} else if (key.delete) {} else if (input && /\d/.test(input)) {
|
|
727
1064
|
setMaxWidthInput(maxWidthInput + input);
|
|
728
1065
|
}
|
|
729
1066
|
} else if (editingTimeout) {
|
|
@@ -745,9 +1082,9 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
745
1082
|
} else if (key.escape) {
|
|
746
1083
|
setEditingTimeout(false);
|
|
747
1084
|
setTimeoutInput("");
|
|
748
|
-
} else if (key.backspace
|
|
1085
|
+
} else if (key.backspace) {
|
|
749
1086
|
setTimeoutInput(timeoutInput.slice(0, -1));
|
|
750
|
-
} else if (input && /\d/.test(input)) {
|
|
1087
|
+
} else if (key.delete) {} else if (input && /\d/.test(input)) {
|
|
751
1088
|
setTimeoutInput(timeoutInput + input);
|
|
752
1089
|
}
|
|
753
1090
|
} else if (moveMode) {
|
|
@@ -926,7 +1263,7 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
926
1263
|
});
|
|
927
1264
|
const getItemDisplay = (item) => {
|
|
928
1265
|
const colorName = item.color || getDefaultColor(item.type);
|
|
929
|
-
const colorFunc =
|
|
1266
|
+
const colorFunc = chalk2[colorName] || chalk2.white;
|
|
930
1267
|
switch (item.type) {
|
|
931
1268
|
case "model":
|
|
932
1269
|
return colorFunc("Model");
|
|
@@ -940,7 +1277,7 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
940
1277
|
return applyColors(`Separator ${charDisplay}`, item.color || "gray", item.backgroundColor, item.bold);
|
|
941
1278
|
}
|
|
942
1279
|
case "flex-separator":
|
|
943
|
-
return
|
|
1280
|
+
return chalk2.yellow("Flex Separator");
|
|
944
1281
|
case "tokens-input":
|
|
945
1282
|
return colorFunc("Tokens Input");
|
|
946
1283
|
case "tokens-output":
|
|
@@ -970,7 +1307,7 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
970
1307
|
if (!item.preserveColors) {
|
|
971
1308
|
return colorFunc(`Custom Command (${truncatedCmd})`);
|
|
972
1309
|
} else {
|
|
973
|
-
return
|
|
1310
|
+
return chalk2.white(`Custom Command (${truncatedCmd}) [preserving colors]`);
|
|
974
1311
|
}
|
|
975
1312
|
}
|
|
976
1313
|
};
|
|
@@ -1138,7 +1475,13 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
|
|
|
1138
1475
|
}, undefined, true, undefined, this);
|
|
1139
1476
|
};
|
|
1140
1477
|
var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
1141
|
-
const
|
|
1478
|
+
const [showSeparators, setShowSeparators] = useState(false);
|
|
1479
|
+
const colorableItems = items.filter((item) => {
|
|
1480
|
+
if (item.type === "separator") {
|
|
1481
|
+
return showSeparators;
|
|
1482
|
+
}
|
|
1483
|
+
return ["model", "git-branch", "git-changes", "tokens-input", "tokens-output", "tokens-cached", "tokens-total", "context-length", "context-percentage", "context-percentage-usable", "session-clock", "terminal-width", "version", "custom-text", "custom-command"].includes(item.type) && !(item.type === "custom-command" && item.preserveColors);
|
|
1484
|
+
});
|
|
1142
1485
|
const [highlightedItemId, setHighlightedItemId] = useState(colorableItems[0]?.id || null);
|
|
1143
1486
|
const [editingBackground, setEditingBackground] = useState(false);
|
|
1144
1487
|
const hasNoItems = colorableItems.length === 0;
|
|
@@ -1153,6 +1496,17 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1153
1496
|
} else {
|
|
1154
1497
|
onBack();
|
|
1155
1498
|
}
|
|
1499
|
+
} else if (input === "s" || input === "S") {
|
|
1500
|
+
setShowSeparators(!showSeparators);
|
|
1501
|
+
const newColorableItems = items.filter((item) => {
|
|
1502
|
+
if (item.type === "separator") {
|
|
1503
|
+
return !showSeparators;
|
|
1504
|
+
}
|
|
1505
|
+
return ["model", "git-branch", "git-changes", "tokens-input", "tokens-output", "tokens-cached", "tokens-total", "context-length", "context-percentage", "context-percentage-usable", "session-clock", "terminal-width", "version", "custom-text", "custom-command"].includes(item.type) && !(item.type === "custom-command" && item.preserveColors);
|
|
1506
|
+
});
|
|
1507
|
+
if (newColorableItems.length > 0) {
|
|
1508
|
+
setHighlightedItemId(newColorableItems[0].id);
|
|
1509
|
+
}
|
|
1156
1510
|
} else if (input === "f" || input === "F") {
|
|
1157
1511
|
if (colorableItems.length > 0) {
|
|
1158
1512
|
setEditingBackground(!editingBackground);
|
|
@@ -1242,6 +1596,8 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1242
1596
|
return "Terminal Width";
|
|
1243
1597
|
case "version":
|
|
1244
1598
|
return "Version";
|
|
1599
|
+
case "separator":
|
|
1600
|
+
return `Separator (${item.character || "|"})`;
|
|
1245
1601
|
case "custom-text":
|
|
1246
1602
|
return `Custom Text (${item.customText || "Empty"})`;
|
|
1247
1603
|
case "custom-command": {
|
|
@@ -1345,14 +1701,14 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1345
1701
|
let colorDisplay;
|
|
1346
1702
|
if (editingBackground) {
|
|
1347
1703
|
if (currentColor === "none") {
|
|
1348
|
-
colorDisplay =
|
|
1704
|
+
colorDisplay = chalk2.gray("(no background)");
|
|
1349
1705
|
} else {
|
|
1350
1706
|
const bgColorName = currentColor.replace(/^bg/, "").toLowerCase();
|
|
1351
|
-
const bgFunc =
|
|
1352
|
-
colorDisplay = bgFunc ? bgFunc(` ${bgColorName} `) :
|
|
1707
|
+
const bgFunc = chalk2[currentColor];
|
|
1708
|
+
colorDisplay = bgFunc ? bgFunc(` ${bgColorName} `) : chalk2.white(currentColor);
|
|
1353
1709
|
}
|
|
1354
1710
|
} else {
|
|
1355
|
-
colorDisplay =
|
|
1711
|
+
colorDisplay = chalk2[currentColor] ? chalk2[currentColor](currentColor) : chalk2.white(currentColor);
|
|
1356
1712
|
}
|
|
1357
1713
|
return /* @__PURE__ */ jsxDEV(Box, {
|
|
1358
1714
|
flexDirection: "column",
|
|
@@ -1361,7 +1717,7 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1361
1717
|
bold: true,
|
|
1362
1718
|
children: [
|
|
1363
1719
|
"Configure Colors ",
|
|
1364
|
-
editingBackground &&
|
|
1720
|
+
editingBackground && chalk2.yellow("[Background Mode]")
|
|
1365
1721
|
]
|
|
1366
1722
|
}, undefined, true, undefined, this),
|
|
1367
1723
|
/* @__PURE__ */ jsxDEV(Text, {
|
|
@@ -1372,6 +1728,13 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1372
1728
|
", (f) to toggle bg/fg, (b)old, (r)eset, ESC to go back"
|
|
1373
1729
|
]
|
|
1374
1730
|
}, undefined, true, undefined, this),
|
|
1731
|
+
/* @__PURE__ */ jsxDEV(Text, {
|
|
1732
|
+
dimColor: true,
|
|
1733
|
+
children: [
|
|
1734
|
+
"(s)how separators: ",
|
|
1735
|
+
showSeparators ? chalk2.green("ON") : chalk2.gray("OFF")
|
|
1736
|
+
]
|
|
1737
|
+
}, undefined, true, undefined, this),
|
|
1375
1738
|
selectedItem && /* @__PURE__ */ jsxDEV(Box, {
|
|
1376
1739
|
marginTop: 1,
|
|
1377
1740
|
children: /* @__PURE__ */ jsxDEV(Text, {
|
|
@@ -1384,7 +1747,7 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1384
1747
|
colorList.length,
|
|
1385
1748
|
"): ",
|
|
1386
1749
|
colorDisplay,
|
|
1387
|
-
selectedItem.bold &&
|
|
1750
|
+
selectedItem.bold && chalk2.bold(" [BOLD]")
|
|
1388
1751
|
]
|
|
1389
1752
|
}, undefined, true, undefined, this)
|
|
1390
1753
|
}, undefined, false, undefined, this),
|
|
@@ -1408,24 +1771,14 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
|
|
|
1408
1771
|
/* @__PURE__ */ jsxDEV(Text, {
|
|
1409
1772
|
dimColor: true,
|
|
1410
1773
|
wrap: "wrap",
|
|
1411
|
-
children: 'If colors appear incorrect in the VSCode integrated terminal, the "Terminal › Integrated: Minimum Contrast Ratio"'
|
|
1412
|
-
}, undefined, false, undefined, this),
|
|
1413
|
-
/* @__PURE__ */ jsxDEV(Text, {
|
|
1414
|
-
dimColor: true,
|
|
1415
|
-
wrap: "wrap",
|
|
1416
|
-
children: "(`terminal.integrated.minimumContrastRatio`) setting is forcing a minimum contrast between foreground and background colors."
|
|
1417
|
-
}, undefined, false, undefined, this),
|
|
1418
|
-
/* @__PURE__ */ jsxDEV(Text, {
|
|
1419
|
-
dimColor: true,
|
|
1420
|
-
wrap: "wrap",
|
|
1421
|
-
children: "You can adjust this setting to 1 to disable the contrast enforcement, or use a standalone terminal for accurate colors."
|
|
1774
|
+
children: 'If colors appear incorrect in the VSCode integrated terminal, the "Terminal › Integrated: Minimum Contrast Ratio" (`terminal.integrated.minimumContrastRatio`) setting is forcing a minimum contrast between foreground and background colors. You can adjust this setting to 1 to disable the contrast enforcement, or use a standalone terminal for accurate colors.'
|
|
1422
1775
|
}, undefined, false, undefined, this)
|
|
1423
1776
|
]
|
|
1424
1777
|
}, undefined, true, undefined, this)
|
|
1425
1778
|
]
|
|
1426
1779
|
}, undefined, true, undefined, this);
|
|
1427
1780
|
};
|
|
1428
|
-
var
|
|
1781
|
+
var TerminalWidthOptions = ({ settings, onUpdate, onBack }) => {
|
|
1429
1782
|
const [selectedOption, setSelectedOption] = useState(settings.flexMode || "full-minus-40");
|
|
1430
1783
|
const [compactThreshold, setCompactThreshold] = useState(settings.compactThreshold || 60);
|
|
1431
1784
|
const [editingThreshold, setEditingThreshold] = useState(false);
|
|
@@ -1455,10 +1808,10 @@ var FlexOptions = ({ settings, onUpdate, onBack }) => {
|
|
|
1455
1808
|
setThresholdInput(String(compactThreshold));
|
|
1456
1809
|
setEditingThreshold(false);
|
|
1457
1810
|
setValidationError(null);
|
|
1458
|
-
} else if (key.backspace
|
|
1811
|
+
} else if (key.backspace) {
|
|
1459
1812
|
setThresholdInput(thresholdInput.slice(0, -1));
|
|
1460
1813
|
setValidationError(null);
|
|
1461
|
-
} else if (input && /\d/.test(input)) {
|
|
1814
|
+
} else if (key.delete) {} else if (input && /\d/.test(input)) {
|
|
1462
1815
|
const newValue = thresholdInput + input;
|
|
1463
1816
|
if (newValue.length <= 2) {
|
|
1464
1817
|
setThresholdInput(newValue);
|
|
@@ -1481,7 +1834,7 @@ NOTE: If /ide integration is enabled, it's not recommended to use this mode as s
|
|
|
1481
1834
|
},
|
|
1482
1835
|
{
|
|
1483
1836
|
value: "full-minus-40",
|
|
1484
|
-
label: "Full width minus 40",
|
|
1837
|
+
label: "Full width minus 40 (default)",
|
|
1485
1838
|
description: "Leaves a gap to the right of the status line to accommodate the auto-compact message. This prevents wrapping but may leave unused space. This limitation exists because we cannot detect when the message will appear."
|
|
1486
1839
|
},
|
|
1487
1840
|
{
|
|
@@ -1516,11 +1869,16 @@ NOTE: If /ide integration is enabled, it's not recommended to use this mode as s
|
|
|
1516
1869
|
children: [
|
|
1517
1870
|
/* @__PURE__ */ jsxDEV(Text, {
|
|
1518
1871
|
bold: true,
|
|
1519
|
-
children: "
|
|
1872
|
+
children: "Terminal Width Options"
|
|
1873
|
+
}, undefined, false, undefined, this),
|
|
1874
|
+
/* @__PURE__ */ jsxDEV(Text, {
|
|
1875
|
+
color: "white",
|
|
1876
|
+
children: "These settings affect where long lines are truncated, and where right-alignment occurs when using flex separators"
|
|
1520
1877
|
}, undefined, false, undefined, this),
|
|
1521
1878
|
/* @__PURE__ */ jsxDEV(Text, {
|
|
1522
1879
|
dimColor: true,
|
|
1523
|
-
|
|
1880
|
+
wrap: "wrap",
|
|
1881
|
+
children: "These settings are necessary because claude code does not currently provide an available width variable for the statusline and features like IDE integration, auto-compaction notices, and rate limit messages can all cause the statusline to wrap if we do not truncate it"
|
|
1524
1882
|
}, undefined, false, undefined, this),
|
|
1525
1883
|
editingThreshold ? /* @__PURE__ */ jsxDEV(Box, {
|
|
1526
1884
|
marginTop: 1,
|
|
@@ -1652,9 +2010,9 @@ var GlobalOptionsMenu = ({ settings, onUpdate, onBack }) => {
|
|
|
1652
2010
|
} else if (key.escape) {
|
|
1653
2011
|
setPaddingInput(settings.defaultPadding || "");
|
|
1654
2012
|
setEditingPadding(false);
|
|
1655
|
-
} else if (key.backspace
|
|
2013
|
+
} else if (key.backspace) {
|
|
1656
2014
|
setPaddingInput(paddingInput.slice(0, -1));
|
|
1657
|
-
} else if (input) {
|
|
2015
|
+
} else if (key.delete) {} else if (input) {
|
|
1658
2016
|
setPaddingInput(paddingInput + input);
|
|
1659
2017
|
}
|
|
1660
2018
|
} else if (editingSeparator) {
|
|
@@ -1668,9 +2026,9 @@ var GlobalOptionsMenu = ({ settings, onUpdate, onBack }) => {
|
|
|
1668
2026
|
} else if (key.escape) {
|
|
1669
2027
|
setSeparatorInput(settings.defaultSeparator || "");
|
|
1670
2028
|
setEditingSeparator(false);
|
|
1671
|
-
} else if (key.backspace
|
|
2029
|
+
} else if (key.backspace) {
|
|
1672
2030
|
setSeparatorInput(separatorInput.slice(0, -1));
|
|
1673
|
-
} else if (input) {
|
|
2031
|
+
} else if (key.delete) {} else if (input) {
|
|
1674
2032
|
setSeparatorInput(separatorInput + input);
|
|
1675
2033
|
}
|
|
1676
2034
|
} else {
|
|
@@ -1855,7 +2213,7 @@ var GlobalOptionsMenu = ({ settings, onUpdate, onBack }) => {
|
|
|
1855
2213
|
}, undefined, false, undefined, this);
|
|
1856
2214
|
} else {
|
|
1857
2215
|
const bgColorName = bgColor.replace(/^bg/, "").toLowerCase();
|
|
1858
|
-
const bgFunc =
|
|
2216
|
+
const bgFunc = chalk2[bgColor];
|
|
1859
2217
|
const display = bgFunc ? bgFunc(` ${bgColorName} `) : bgColorName;
|
|
1860
2218
|
return /* @__PURE__ */ jsxDEV(Text, {
|
|
1861
2219
|
children: display
|
|
@@ -1881,7 +2239,7 @@ var GlobalOptionsMenu = ({ settings, onUpdate, onBack }) => {
|
|
|
1881
2239
|
children: "(none)"
|
|
1882
2240
|
}, undefined, false, undefined, this);
|
|
1883
2241
|
} else {
|
|
1884
|
-
const fgFunc =
|
|
2242
|
+
const fgFunc = chalk2[fgColor];
|
|
1885
2243
|
const display = fgFunc ? fgFunc(fgColor) : fgColor;
|
|
1886
2244
|
return /* @__PURE__ */ jsxDEV(Text, {
|
|
1887
2245
|
children: display
|
|
@@ -1936,17 +2294,7 @@ var GlobalOptionsMenu = ({ settings, onUpdate, onBack }) => {
|
|
|
1936
2294
|
/* @__PURE__ */ jsxDEV(Text, {
|
|
1937
2295
|
dimColor: true,
|
|
1938
2296
|
wrap: "wrap",
|
|
1939
|
-
children: 'If colors appear incorrect in the VSCode integrated terminal, the "Terminal › Integrated: Minimum Contrast Ratio"'
|
|
1940
|
-
}, undefined, false, undefined, this),
|
|
1941
|
-
/* @__PURE__ */ jsxDEV(Text, {
|
|
1942
|
-
dimColor: true,
|
|
1943
|
-
wrap: "wrap",
|
|
1944
|
-
children: "(`terminal.integrated.minimumContrastRatio`) setting is forcing a minimum contrast between foreground and background colors."
|
|
1945
|
-
}, undefined, false, undefined, this),
|
|
1946
|
-
/* @__PURE__ */ jsxDEV(Text, {
|
|
1947
|
-
dimColor: true,
|
|
1948
|
-
wrap: "wrap",
|
|
1949
|
-
children: "You can adjust this setting to 1 to disable the contrast enforcement, or use a standalone terminal for accurate colors."
|
|
2297
|
+
children: 'If colors appear incorrect in the VSCode integrated terminal, the "Terminal › Integrated: Minimum Contrast Ratio" (`terminal.integrated.minimumContrastRatio`) setting is forcing a minimum contrast between foreground and background colors. You can adjust this setting to 1 to disable the contrast enforcement, or use a standalone terminal for accurate colors.'
|
|
1950
2298
|
}, undefined, false, undefined, this)
|
|
1951
2299
|
]
|
|
1952
2300
|
}, undefined, true, undefined, this)
|
|
@@ -2051,8 +2399,8 @@ Continue?`;
|
|
|
2051
2399
|
case "colors":
|
|
2052
2400
|
setScreen("colors");
|
|
2053
2401
|
break;
|
|
2054
|
-
case "
|
|
2055
|
-
setScreen("
|
|
2402
|
+
case "terminalWidth":
|
|
2403
|
+
setScreen("terminalWidth");
|
|
2056
2404
|
break;
|
|
2057
2405
|
case "globalOptions":
|
|
2058
2406
|
setScreen("globalOptions");
|
|
@@ -2116,7 +2464,7 @@ Continue?`;
|
|
|
2116
2464
|
const menuMap = {
|
|
2117
2465
|
lines: 0,
|
|
2118
2466
|
colors: 1,
|
|
2119
|
-
|
|
2467
|
+
terminalWidth: 2,
|
|
2120
2468
|
globalOptions: 3,
|
|
2121
2469
|
install: 4
|
|
2122
2470
|
};
|
|
@@ -2173,7 +2521,7 @@ Continue?`;
|
|
|
2173
2521
|
setScreen("main");
|
|
2174
2522
|
}
|
|
2175
2523
|
}, undefined, false, undefined, this),
|
|
2176
|
-
screen === "
|
|
2524
|
+
screen === "terminalWidth" && /* @__PURE__ */ jsxDEV(TerminalWidthOptions, {
|
|
2177
2525
|
settings,
|
|
2178
2526
|
onUpdate: (updatedSettings) => {
|
|
2179
2527
|
setSettings(updatedSettings);
|
|
@@ -2212,29 +2560,6 @@ function runTUI() {
|
|
|
2212
2560
|
}
|
|
2213
2561
|
|
|
2214
2562
|
// src/ccstatusline.ts
|
|
2215
|
-
import * as fs4 from "fs";
|
|
2216
|
-
import { promisify as promisify3 } from "util";
|
|
2217
|
-
var readFile6 = fs4.promises?.readFile || promisify3(fs4.readFile);
|
|
2218
|
-
chalk2.level = 3;
|
|
2219
|
-
function applyColors2(text, foregroundColor, backgroundColor, bold) {
|
|
2220
|
-
let result = text;
|
|
2221
|
-
if (foregroundColor && foregroundColor !== "dim") {
|
|
2222
|
-
const fgFunc = chalk2[foregroundColor];
|
|
2223
|
-
if (fgFunc) {
|
|
2224
|
-
result = fgFunc(result);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
if (backgroundColor && backgroundColor !== "none") {
|
|
2228
|
-
const bgFunc = chalk2[backgroundColor];
|
|
2229
|
-
if (bgFunc) {
|
|
2230
|
-
result = bgFunc(result);
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
if (bold) {
|
|
2234
|
-
result = chalk2.bold(result);
|
|
2235
|
-
}
|
|
2236
|
-
return result;
|
|
2237
|
-
}
|
|
2238
2563
|
async function readStdin() {
|
|
2239
2564
|
if (process.stdin.isTTY) {
|
|
2240
2565
|
return null;
|
|
@@ -2257,548 +2582,28 @@ async function readStdin() {
|
|
|
2257
2582
|
return null;
|
|
2258
2583
|
}
|
|
2259
2584
|
}
|
|
2260
|
-
function
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
case "session-clock":
|
|
2269
|
-
return "yellow";
|
|
2270
|
-
case "version":
|
|
2271
|
-
return "green";
|
|
2272
|
-
case "tokens-input":
|
|
2273
|
-
return "blue";
|
|
2274
|
-
case "tokens-output":
|
|
2275
|
-
return "white";
|
|
2276
|
-
case "tokens-cached":
|
|
2277
|
-
return "cyan";
|
|
2278
|
-
case "tokens-total":
|
|
2279
|
-
return "cyan";
|
|
2280
|
-
case "context-length":
|
|
2281
|
-
return "gray";
|
|
2282
|
-
case "context-percentage":
|
|
2283
|
-
return "blue";
|
|
2284
|
-
case "context-percentage-usable":
|
|
2285
|
-
return "green";
|
|
2286
|
-
case "terminal-width":
|
|
2287
|
-
return "gray";
|
|
2288
|
-
case "custom-text":
|
|
2289
|
-
return "white";
|
|
2290
|
-
case "custom-command":
|
|
2291
|
-
return "white";
|
|
2292
|
-
case "separator":
|
|
2293
|
-
return "gray";
|
|
2294
|
-
default:
|
|
2295
|
-
return "white";
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
function getTerminalWidth() {
|
|
2299
|
-
try {
|
|
2300
|
-
const tty = execSync2("ps -o tty= -p $(ps -o ppid= -p $$)", {
|
|
2301
|
-
encoding: "utf8",
|
|
2302
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
2303
|
-
shell: "/bin/sh"
|
|
2304
|
-
}).trim();
|
|
2305
|
-
if (tty && tty !== "??" && tty !== "?") {
|
|
2306
|
-
const width = execSync2(`stty size < /dev/${tty} | awk '{print $2}'`, {
|
|
2307
|
-
encoding: "utf8",
|
|
2308
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
2309
|
-
shell: "/bin/sh"
|
|
2310
|
-
}).trim();
|
|
2311
|
-
const parsed = parseInt(width, 10);
|
|
2312
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
2313
|
-
return parsed;
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
} catch {}
|
|
2317
|
-
try {
|
|
2318
|
-
const width = execSync2("tput cols 2>/dev/null", {
|
|
2319
|
-
encoding: "utf8",
|
|
2320
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
2321
|
-
}).trim();
|
|
2322
|
-
const parsed = parseInt(width, 10);
|
|
2323
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
2324
|
-
return parsed;
|
|
2325
|
-
}
|
|
2326
|
-
} catch {}
|
|
2327
|
-
return null;
|
|
2328
|
-
}
|
|
2329
|
-
function getGitBranch() {
|
|
2330
|
-
try {
|
|
2331
|
-
const branch = execSync2("git branch --show-current 2>/dev/null", {
|
|
2332
|
-
encoding: "utf8",
|
|
2333
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
2334
|
-
}).trim();
|
|
2335
|
-
return branch || null;
|
|
2336
|
-
} catch {
|
|
2337
|
-
return null;
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
function getGitChanges() {
|
|
2341
|
-
try {
|
|
2342
|
-
let totalInsertions = 0;
|
|
2343
|
-
let totalDeletions = 0;
|
|
2344
|
-
const unstagedStat = execSync2("git diff --shortstat 2>/dev/null", {
|
|
2345
|
-
encoding: "utf8",
|
|
2346
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
2347
|
-
}).trim();
|
|
2348
|
-
const stagedStat = execSync2("git diff --cached --shortstat 2>/dev/null", {
|
|
2349
|
-
encoding: "utf8",
|
|
2350
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
2351
|
-
}).trim();
|
|
2352
|
-
if (unstagedStat) {
|
|
2353
|
-
const insertMatch = unstagedStat.match(/(\d+) insertion/);
|
|
2354
|
-
const deleteMatch = unstagedStat.match(/(\d+) deletion/);
|
|
2355
|
-
totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
|
|
2356
|
-
totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
|
|
2357
|
-
}
|
|
2358
|
-
if (stagedStat) {
|
|
2359
|
-
const insertMatch = stagedStat.match(/(\d+) insertion/);
|
|
2360
|
-
const deleteMatch = stagedStat.match(/(\d+) deletion/);
|
|
2361
|
-
totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
|
|
2362
|
-
totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
|
|
2363
|
-
}
|
|
2364
|
-
return { insertions: totalInsertions, deletions: totalDeletions };
|
|
2365
|
-
} catch {
|
|
2366
|
-
return null;
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
async function getSessionDuration(transcriptPath) {
|
|
2370
|
-
try {
|
|
2371
|
-
if (!fs4.existsSync(transcriptPath)) {
|
|
2372
|
-
return null;
|
|
2373
|
-
}
|
|
2374
|
-
const content = await readFile6(transcriptPath, "utf-8");
|
|
2375
|
-
const lines = content.trim().split(`
|
|
2376
|
-
`).filter((line) => line.trim());
|
|
2377
|
-
if (lines.length === 0) {
|
|
2378
|
-
return null;
|
|
2379
|
-
}
|
|
2380
|
-
let firstTimestamp = null;
|
|
2381
|
-
let lastTimestamp = null;
|
|
2382
|
-
for (const line of lines) {
|
|
2383
|
-
try {
|
|
2384
|
-
const data = JSON.parse(line);
|
|
2385
|
-
if (data.timestamp) {
|
|
2386
|
-
firstTimestamp = new Date(data.timestamp);
|
|
2387
|
-
break;
|
|
2388
|
-
}
|
|
2389
|
-
} catch {}
|
|
2390
|
-
}
|
|
2391
|
-
for (let i = lines.length - 1;i >= 0; i--) {
|
|
2392
|
-
try {
|
|
2393
|
-
const data = JSON.parse(lines[i]);
|
|
2394
|
-
if (data.timestamp) {
|
|
2395
|
-
lastTimestamp = new Date(data.timestamp);
|
|
2396
|
-
break;
|
|
2397
|
-
}
|
|
2398
|
-
} catch {}
|
|
2399
|
-
}
|
|
2400
|
-
if (!firstTimestamp || !lastTimestamp) {
|
|
2401
|
-
return null;
|
|
2402
|
-
}
|
|
2403
|
-
const durationMs = lastTimestamp.getTime() - firstTimestamp.getTime();
|
|
2404
|
-
const totalMinutes = Math.floor(durationMs / (1000 * 60));
|
|
2405
|
-
if (totalMinutes < 1) {
|
|
2406
|
-
return "<1m";
|
|
2407
|
-
}
|
|
2408
|
-
const hours = Math.floor(totalMinutes / 60);
|
|
2409
|
-
const minutes = totalMinutes % 60;
|
|
2410
|
-
if (hours === 0) {
|
|
2411
|
-
return `${minutes}m`;
|
|
2412
|
-
} else if (minutes === 0) {
|
|
2413
|
-
return `${hours}hr`;
|
|
2414
|
-
} else {
|
|
2415
|
-
return `${hours}hr ${minutes}m`;
|
|
2416
|
-
}
|
|
2417
|
-
} catch {
|
|
2418
|
-
return null;
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
async function getTokenMetrics(transcriptPath) {
|
|
2422
|
-
try {
|
|
2423
|
-
if (!fs4.existsSync(transcriptPath)) {
|
|
2424
|
-
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
2425
|
-
}
|
|
2426
|
-
const content = await readFile6(transcriptPath, "utf-8");
|
|
2427
|
-
const lines = content.trim().split(`
|
|
2428
|
-
`);
|
|
2429
|
-
let inputTokens = 0;
|
|
2430
|
-
let outputTokens = 0;
|
|
2431
|
-
let cachedTokens = 0;
|
|
2432
|
-
let contextLength = 0;
|
|
2433
|
-
let mostRecentMainChainEntry = null;
|
|
2434
|
-
let mostRecentTimestamp = null;
|
|
2435
|
-
for (const line of lines) {
|
|
2436
|
-
try {
|
|
2437
|
-
const data = JSON.parse(line);
|
|
2438
|
-
if (data.message?.usage) {
|
|
2439
|
-
inputTokens += data.message.usage.input_tokens || 0;
|
|
2440
|
-
outputTokens += data.message.usage.output_tokens || 0;
|
|
2441
|
-
cachedTokens += data.message.usage.cache_read_input_tokens || 0;
|
|
2442
|
-
cachedTokens += data.message.usage.cache_creation_input_tokens || 0;
|
|
2443
|
-
if (data.isSidechain !== true && data.timestamp) {
|
|
2444
|
-
const entryTime = new Date(data.timestamp);
|
|
2445
|
-
if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
|
|
2446
|
-
mostRecentTimestamp = entryTime;
|
|
2447
|
-
mostRecentMainChainEntry = data;
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
} catch {}
|
|
2452
|
-
}
|
|
2453
|
-
if (mostRecentMainChainEntry?.message?.usage) {
|
|
2454
|
-
const usage = mostRecentMainChainEntry.message.usage;
|
|
2455
|
-
contextLength = (usage.input_tokens || 0) + (usage.cache_read_input_tokens || 0) + (usage.cache_creation_input_tokens || 0);
|
|
2456
|
-
}
|
|
2457
|
-
const totalTokens = inputTokens + outputTokens + cachedTokens;
|
|
2458
|
-
return { inputTokens, outputTokens, cachedTokens, totalTokens, contextLength };
|
|
2459
|
-
} catch {
|
|
2460
|
-
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2463
|
-
function renderSingleLine2(items, settings, data, tokenMetrics, sessionDuration) {
|
|
2464
|
-
const applyColorsWithOverride = (text, foregroundColor, backgroundColor, bold) => {
|
|
2465
|
-
let fgColor = foregroundColor;
|
|
2466
|
-
if (settings.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
|
|
2467
|
-
fgColor = settings.overrideForegroundColor;
|
|
2468
|
-
}
|
|
2469
|
-
let bgColor = backgroundColor;
|
|
2470
|
-
if (settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none") {
|
|
2471
|
-
bgColor = settings.overrideBackgroundColor;
|
|
2472
|
-
}
|
|
2473
|
-
const shouldBold = settings.globalBold || bold;
|
|
2474
|
-
return applyColors2(text, fgColor, bgColor, shouldBold);
|
|
2475
|
-
};
|
|
2476
|
-
const detectedWidth = getTerminalWidth();
|
|
2477
|
-
let terminalWidth = null;
|
|
2478
|
-
if (detectedWidth) {
|
|
2479
|
-
const flexMode = settings.flexMode || "full-minus-40";
|
|
2480
|
-
if (flexMode === "full") {
|
|
2481
|
-
terminalWidth = detectedWidth - 4;
|
|
2482
|
-
} else if (flexMode === "full-minus-40") {
|
|
2483
|
-
terminalWidth = detectedWidth - 40;
|
|
2484
|
-
} else if (flexMode === "full-until-compact") {
|
|
2485
|
-
const threshold = settings.compactThreshold || 60;
|
|
2486
|
-
const contextPercentage = tokenMetrics ? Math.min(100, tokenMetrics.contextLength / 200000 * 100) : 0;
|
|
2487
|
-
if (contextPercentage >= threshold) {
|
|
2488
|
-
terminalWidth = detectedWidth - 40;
|
|
2489
|
-
} else {
|
|
2490
|
-
terminalWidth = detectedWidth - 4;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
const elements = [];
|
|
2495
|
-
let hasFlexSeparator = false;
|
|
2496
|
-
const formatTokens = (count) => {
|
|
2497
|
-
if (count >= 1e6)
|
|
2498
|
-
return `${(count / 1e6).toFixed(1)}M`;
|
|
2499
|
-
if (count >= 1000)
|
|
2500
|
-
return `${(count / 1000).toFixed(1)}k`;
|
|
2501
|
-
return count.toString();
|
|
2502
|
-
};
|
|
2503
|
-
for (const item of items) {
|
|
2504
|
-
switch (item.type) {
|
|
2505
|
-
case "model":
|
|
2506
|
-
if (data.model) {
|
|
2507
|
-
const text = item.rawValue ? data.model.display_name : `Model: ${data.model.display_name}`;
|
|
2508
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "model", item });
|
|
2509
|
-
}
|
|
2510
|
-
break;
|
|
2511
|
-
case "git-branch":
|
|
2512
|
-
const branch = getGitBranch();
|
|
2513
|
-
if (branch) {
|
|
2514
|
-
const text = item.rawValue ? branch : `⎇ ${branch}`;
|
|
2515
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "magenta", item.backgroundColor, item.bold), type: "git-branch", item });
|
|
2516
|
-
}
|
|
2517
|
-
break;
|
|
2518
|
-
case "git-changes":
|
|
2519
|
-
const changes = getGitChanges();
|
|
2520
|
-
if (changes !== null) {
|
|
2521
|
-
const changeStr = `(+${changes.insertions},-${changes.deletions})`;
|
|
2522
|
-
elements.push({ content: applyColorsWithOverride(changeStr, item.color || "yellow", item.backgroundColor, item.bold), type: "git-changes", item });
|
|
2523
|
-
}
|
|
2524
|
-
break;
|
|
2525
|
-
case "tokens-input":
|
|
2526
|
-
if (tokenMetrics) {
|
|
2527
|
-
const text = item.rawValue ? formatTokens(tokenMetrics.inputTokens) : `In: ${formatTokens(tokenMetrics.inputTokens)}`;
|
|
2528
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "blue", item.backgroundColor, item.bold), type: "tokens-input", item });
|
|
2529
|
-
}
|
|
2530
|
-
break;
|
|
2531
|
-
case "tokens-output":
|
|
2532
|
-
if (tokenMetrics) {
|
|
2533
|
-
const text = item.rawValue ? formatTokens(tokenMetrics.outputTokens) : `Out: ${formatTokens(tokenMetrics.outputTokens)}`;
|
|
2534
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "white", item.backgroundColor, item.bold), type: "tokens-output", item });
|
|
2535
|
-
}
|
|
2536
|
-
break;
|
|
2537
|
-
case "tokens-cached":
|
|
2538
|
-
if (tokenMetrics) {
|
|
2539
|
-
const text = item.rawValue ? formatTokens(tokenMetrics.cachedTokens) : `Cached: ${formatTokens(tokenMetrics.cachedTokens)}`;
|
|
2540
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-cached", item });
|
|
2541
|
-
}
|
|
2542
|
-
break;
|
|
2543
|
-
case "tokens-total":
|
|
2544
|
-
if (tokenMetrics) {
|
|
2545
|
-
const text = item.rawValue ? formatTokens(tokenMetrics.totalTokens) : `Total: ${formatTokens(tokenMetrics.totalTokens)}`;
|
|
2546
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "cyan", item.backgroundColor, item.bold), type: "tokens-total", item });
|
|
2547
|
-
}
|
|
2548
|
-
break;
|
|
2549
|
-
case "context-length":
|
|
2550
|
-
if (tokenMetrics) {
|
|
2551
|
-
const text = item.rawValue ? formatTokens(tokenMetrics.contextLength) : `Ctx: ${formatTokens(tokenMetrics.contextLength)}`;
|
|
2552
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "gray", item.backgroundColor, item.bold), type: "context-length", item });
|
|
2553
|
-
}
|
|
2554
|
-
break;
|
|
2555
|
-
case "context-percentage":
|
|
2556
|
-
if (tokenMetrics) {
|
|
2557
|
-
const percentage = Math.min(100, tokenMetrics.contextLength / 200000 * 100);
|
|
2558
|
-
const text = item.rawValue ? `${percentage.toFixed(1)}%` : `Ctx: ${percentage.toFixed(1)}%`;
|
|
2559
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "blue", item.backgroundColor, item.bold), type: "context-percentage", item });
|
|
2560
|
-
}
|
|
2561
|
-
break;
|
|
2562
|
-
case "context-percentage-usable":
|
|
2563
|
-
if (tokenMetrics) {
|
|
2564
|
-
const percentage = Math.min(100, tokenMetrics.contextLength / 160000 * 100);
|
|
2565
|
-
const text = item.rawValue ? `${percentage.toFixed(1)}%` : `Ctx(u): ${percentage.toFixed(1)}%`;
|
|
2566
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "green", item.backgroundColor, item.bold), type: "context-percentage-usable", item });
|
|
2567
|
-
}
|
|
2568
|
-
break;
|
|
2569
|
-
case "terminal-width":
|
|
2570
|
-
const width = terminalWidth || getTerminalWidth();
|
|
2571
|
-
if (width) {
|
|
2572
|
-
const text = item.rawValue ? `${width}` : `Term: ${width}`;
|
|
2573
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "gray", item.backgroundColor, item.bold), type: "terminal-width", item });
|
|
2574
|
-
}
|
|
2575
|
-
break;
|
|
2576
|
-
case "session-clock":
|
|
2577
|
-
if (sessionDuration) {
|
|
2578
|
-
const text = item.rawValue ? sessionDuration : `Session: ${sessionDuration}`;
|
|
2579
|
-
elements.push({ content: applyColorsWithOverride(text, item.color || "yellow", item.backgroundColor, item.bold), type: "session-clock", item });
|
|
2580
|
-
}
|
|
2581
|
-
break;
|
|
2582
|
-
case "version":
|
|
2583
|
-
const versionString = data.version || "Unknown";
|
|
2584
|
-
const versionText = item.rawValue ? versionString : `Version: ${versionString}`;
|
|
2585
|
-
elements.push({ content: applyColorsWithOverride(versionText, item.color || "green", item.backgroundColor, item.bold), type: "version", item });
|
|
2586
|
-
break;
|
|
2587
|
-
case "separator":
|
|
2588
|
-
const lastElement = elements[elements.length - 1];
|
|
2589
|
-
if (elements.length > 0 && lastElement && lastElement.type !== "separator") {
|
|
2590
|
-
const sepChar = item.character || "|";
|
|
2591
|
-
let sepText;
|
|
2592
|
-
if (sepChar === ",") {
|
|
2593
|
-
sepText = `${sepChar} `;
|
|
2594
|
-
} else if (sepChar === " ") {
|
|
2595
|
-
sepText = " ";
|
|
2596
|
-
} else {
|
|
2597
|
-
sepText = ` ${sepChar} `;
|
|
2598
|
-
}
|
|
2599
|
-
const sepContent = applyColorsWithOverride(sepText, item.color || "gray", item.backgroundColor, item.bold);
|
|
2600
|
-
elements.push({ content: sepContent, type: "separator", item });
|
|
2601
|
-
}
|
|
2602
|
-
break;
|
|
2603
|
-
case "flex-separator":
|
|
2604
|
-
elements.push({ content: "FLEX", type: "flex-separator", item });
|
|
2605
|
-
hasFlexSeparator = true;
|
|
2606
|
-
break;
|
|
2607
|
-
case "custom-text":
|
|
2608
|
-
const customText = item.customText || "";
|
|
2609
|
-
elements.push({ content: applyColorsWithOverride(customText, item.color || "white", item.backgroundColor, item.bold), type: "custom-text", item });
|
|
2610
|
-
break;
|
|
2611
|
-
case "custom-command":
|
|
2612
|
-
if (item.commandPath) {
|
|
2613
|
-
try {
|
|
2614
|
-
const timeout = item.timeout || 1000;
|
|
2615
|
-
const output = execSync2(item.commandPath, {
|
|
2616
|
-
encoding: "utf8",
|
|
2617
|
-
input: JSON.stringify(data),
|
|
2618
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
2619
|
-
timeout
|
|
2620
|
-
}).trim();
|
|
2621
|
-
if (output) {
|
|
2622
|
-
let finalOutput = output;
|
|
2623
|
-
if (item.maxWidth && item.maxWidth > 0) {
|
|
2624
|
-
const plainLength = output.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
2625
|
-
if (plainLength > item.maxWidth) {
|
|
2626
|
-
let truncated = "";
|
|
2627
|
-
let currentLength = 0;
|
|
2628
|
-
let inAnsiCode = false;
|
|
2629
|
-
let ansiBuffer = "";
|
|
2630
|
-
for (let i = 0;i < output.length; i++) {
|
|
2631
|
-
const char = output[i];
|
|
2632
|
-
if (char === "\x1B") {
|
|
2633
|
-
inAnsiCode = true;
|
|
2634
|
-
ansiBuffer = char;
|
|
2635
|
-
} else if (inAnsiCode) {
|
|
2636
|
-
ansiBuffer += char;
|
|
2637
|
-
if (char === "m") {
|
|
2638
|
-
truncated += ansiBuffer;
|
|
2639
|
-
inAnsiCode = false;
|
|
2640
|
-
ansiBuffer = "";
|
|
2641
|
-
}
|
|
2642
|
-
} else {
|
|
2643
|
-
if (currentLength < item.maxWidth) {
|
|
2644
|
-
truncated += char;
|
|
2645
|
-
currentLength++;
|
|
2646
|
-
} else {
|
|
2647
|
-
break;
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
finalOutput = truncated;
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
if (!item.preserveColors) {
|
|
2655
|
-
const stripped = finalOutput.replace(/\x1b\[[0-9;]*m/g, "");
|
|
2656
|
-
elements.push({ content: applyColorsWithOverride(stripped, item.color || "white", item.backgroundColor, item.bold), type: "custom-command", item });
|
|
2657
|
-
} else {
|
|
2658
|
-
elements.push({ content: finalOutput, type: "custom-command", item });
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
} catch {}
|
|
2662
|
-
}
|
|
2663
|
-
break;
|
|
2664
|
-
}
|
|
2665
|
-
}
|
|
2666
|
-
if (elements.length === 0)
|
|
2667
|
-
return "";
|
|
2668
|
-
while (elements.length > 0 && elements[elements.length - 1]?.type === "separator") {
|
|
2669
|
-
elements.pop();
|
|
2670
|
-
}
|
|
2671
|
-
const finalElements = [];
|
|
2672
|
-
const padding = settings.defaultPadding || "";
|
|
2673
|
-
const defaultSep = settings.defaultSeparator || "";
|
|
2674
|
-
elements.forEach((elem, index) => {
|
|
2675
|
-
if (defaultSep && index > 0) {
|
|
2676
|
-
if (settings.inheritSeparatorColors && index > 0) {
|
|
2677
|
-
const prevElem = elements[index - 1];
|
|
2678
|
-
if (prevElem && prevElem.item) {
|
|
2679
|
-
const itemColor = prevElem.item.color || getItemDefaultColor(prevElem.item.type);
|
|
2680
|
-
const coloredSep = applyColorsWithOverride(defaultSep, itemColor, prevElem.item.backgroundColor, prevElem.item.bold);
|
|
2681
|
-
finalElements.push(coloredSep);
|
|
2682
|
-
} else {
|
|
2683
|
-
finalElements.push(defaultSep);
|
|
2684
|
-
}
|
|
2685
|
-
} else if (settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none" || settings.overrideForegroundColor && settings.overrideForegroundColor !== "none") {
|
|
2686
|
-
const coloredSep = applyColorsWithOverride(defaultSep, undefined, undefined);
|
|
2687
|
-
finalElements.push(coloredSep);
|
|
2688
|
-
} else {
|
|
2689
|
-
finalElements.push(defaultSep);
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
if (elem.type === "separator" || elem.type === "flex-separator") {
|
|
2693
|
-
finalElements.push(elem.content);
|
|
2694
|
-
} else {
|
|
2695
|
-
const hasColorOverride = settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none" || settings.overrideForegroundColor && settings.overrideForegroundColor !== "none";
|
|
2696
|
-
if (padding && (elem.item?.backgroundColor || hasColorOverride)) {
|
|
2697
|
-
const paddedContent = applyColorsWithOverride(padding, undefined, elem.item?.backgroundColor) + elem.content + applyColorsWithOverride(padding, undefined, elem.item?.backgroundColor);
|
|
2698
|
-
finalElements.push(paddedContent);
|
|
2699
|
-
} else {
|
|
2700
|
-
finalElements.push(padding + elem.content + padding);
|
|
2701
|
-
}
|
|
2702
|
-
}
|
|
2703
|
-
});
|
|
2704
|
-
let statusLine = "";
|
|
2705
|
-
if (hasFlexSeparator && terminalWidth) {
|
|
2706
|
-
const parts = [[]];
|
|
2707
|
-
let currentPart = 0;
|
|
2708
|
-
for (let i = 0;i < finalElements.length; i++) {
|
|
2709
|
-
const elem = finalElements[i];
|
|
2710
|
-
if (elem === "FLEX" || elements[i] && elements[i]?.type === "flex-separator") {
|
|
2711
|
-
currentPart++;
|
|
2712
|
-
parts[currentPart] = [];
|
|
2713
|
-
} else {
|
|
2714
|
-
parts[currentPart]?.push(elem);
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
const partLengths = parts.map((part) => {
|
|
2718
|
-
const joined = part.join("");
|
|
2719
|
-
return joined.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
2720
|
-
});
|
|
2721
|
-
const totalContentLength = partLengths.reduce((sum, len) => sum + len, 0);
|
|
2722
|
-
const flexCount = parts.length - 1;
|
|
2723
|
-
const totalSpace = Math.max(0, terminalWidth - totalContentLength);
|
|
2724
|
-
const spacePerFlex = flexCount > 0 ? Math.floor(totalSpace / flexCount) : 0;
|
|
2725
|
-
const extraSpace = flexCount > 0 ? totalSpace % flexCount : 0;
|
|
2726
|
-
statusLine = "";
|
|
2727
|
-
for (let i = 0;i < parts.length; i++) {
|
|
2728
|
-
const part = parts[i];
|
|
2729
|
-
if (part) {
|
|
2730
|
-
statusLine += part.join("");
|
|
2731
|
-
}
|
|
2732
|
-
if (i < parts.length - 1) {
|
|
2733
|
-
const spaces = spacePerFlex + (i < extraSpace ? 1 : 0);
|
|
2734
|
-
statusLine += " ".repeat(spaces);
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2737
|
-
} else {
|
|
2738
|
-
if (hasFlexSeparator && !terminalWidth) {
|
|
2739
|
-
statusLine = finalElements.map((e) => e === "FLEX" ? chalk2.gray(" | ") : e).join("");
|
|
2740
|
-
} else {
|
|
2741
|
-
statusLine = finalElements.join("");
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
if (detectedWidth && detectedWidth > 0) {
|
|
2745
|
-
const plainLength = statusLine.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
2746
|
-
if (plainLength > detectedWidth) {
|
|
2747
|
-
let truncated = "";
|
|
2748
|
-
let currentLength = 0;
|
|
2749
|
-
let inAnsiCode = false;
|
|
2750
|
-
let ansiBuffer = "";
|
|
2751
|
-
const targetLength = detectedWidth - 7;
|
|
2752
|
-
for (let i = 0;i < statusLine.length; i++) {
|
|
2753
|
-
const char = statusLine[i];
|
|
2754
|
-
if (char === "\x1B") {
|
|
2755
|
-
inAnsiCode = true;
|
|
2756
|
-
ansiBuffer = char;
|
|
2757
|
-
} else if (inAnsiCode) {
|
|
2758
|
-
ansiBuffer += char;
|
|
2759
|
-
if (char === "m") {
|
|
2760
|
-
truncated += ansiBuffer;
|
|
2761
|
-
inAnsiCode = false;
|
|
2762
|
-
ansiBuffer = "";
|
|
2763
|
-
}
|
|
2764
|
-
} else {
|
|
2765
|
-
if (currentLength < targetLength) {
|
|
2766
|
-
truncated += char;
|
|
2767
|
-
currentLength++;
|
|
2768
|
-
} else {
|
|
2769
|
-
break;
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
statusLine = truncated + "...";
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
return statusLine;
|
|
2777
|
-
}
|
|
2778
|
-
async function renderStatusLine(data) {
|
|
2779
|
-
const settings = await loadSettings();
|
|
2780
|
-
let lines = [];
|
|
2781
|
-
if (settings.lines) {
|
|
2782
|
-
lines = settings.lines;
|
|
2783
|
-
} else if (settings.items) {
|
|
2784
|
-
lines = [settings.items];
|
|
2785
|
-
} else {
|
|
2786
|
-
lines = [[]];
|
|
2787
|
-
}
|
|
2788
|
-
const hasTokenItems = lines.some((line) => line.some((item) => ["tokens-input", "tokens-output", "tokens-cached", "tokens-total", "context-length", "context-percentage", "context-percentage-usable"].includes(item.type)));
|
|
2789
|
-
const hasSessionClock = lines.some((line) => line.some((item) => item.type === "session-clock"));
|
|
2790
|
-
let tokenMetrics = null;
|
|
2791
|
-
if (hasTokenItems && data.transcript_path) {
|
|
2792
|
-
tokenMetrics = await getTokenMetrics(data.transcript_path);
|
|
2585
|
+
async function renderMultipleLines(data) {
|
|
2586
|
+
const settings = await loadSettings();
|
|
2587
|
+
let lines = settings.lines || (settings.items ? [settings.items] : [[]]);
|
|
2588
|
+
const hasTokenItems = lines.some((line) => line.some((item) => ["tokens-input", "tokens-output", "tokens-cached", "tokens-total", "context-length", "context-percentage", "context-percentage-usable"].includes(item.type)));
|
|
2589
|
+
const hasSessionClock = lines.some((line) => line.some((item) => item.type === "session-clock"));
|
|
2590
|
+
let tokenMetrics = null;
|
|
2591
|
+
if (hasTokenItems && data.transcript_path) {
|
|
2592
|
+
tokenMetrics = await getTokenMetrics(data.transcript_path);
|
|
2793
2593
|
}
|
|
2794
2594
|
let sessionDuration = null;
|
|
2795
2595
|
if (hasSessionClock && data.transcript_path) {
|
|
2796
2596
|
sessionDuration = await getSessionDuration(data.transcript_path);
|
|
2797
2597
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2598
|
+
const context = {
|
|
2599
|
+
data,
|
|
2600
|
+
tokenMetrics,
|
|
2601
|
+
sessionDuration,
|
|
2602
|
+
isPreview: false
|
|
2603
|
+
};
|
|
2604
|
+
for (const lineItems of lines) {
|
|
2800
2605
|
if (lineItems && lineItems.length > 0) {
|
|
2801
|
-
const line =
|
|
2606
|
+
const line = renderStatusLine(lineItems, settings, context);
|
|
2802
2607
|
const outputLine = line.replace(/ /g, " ");
|
|
2803
2608
|
console.log(outputLine);
|
|
2804
2609
|
}
|
|
@@ -2810,7 +2615,7 @@ async function main() {
|
|
|
2810
2615
|
if (input && input.trim() !== "") {
|
|
2811
2616
|
try {
|
|
2812
2617
|
const data = JSON.parse(input);
|
|
2813
|
-
await
|
|
2618
|
+
await renderMultipleLines(data);
|
|
2814
2619
|
} catch (error) {
|
|
2815
2620
|
console.error("Error parsing JSON:", error);
|
|
2816
2621
|
process.exit(1);
|